diff options
-rw-r--r-- | HISTORY | 7 | ||||
-rw-r--r-- | display.c | 66 | ||||
-rw-r--r-- | render.c | 68 | ||||
-rw-r--r-- | render.h | 18 | ||||
-rw-r--r-- | screen.c | 13 | ||||
-rw-r--r-- | screen.h | 3 | ||||
-rw-r--r-- | xml/display.c | 3 | ||||
-rw-r--r-- | xml/display.h | 1 | ||||
-rw-r--r-- | xml/object.c | 114 | ||||
-rw-r--r-- | xml/object.h | 35 | ||||
-rw-r--r-- | xml/parser.c | 23 |
11 files changed, 298 insertions, 53 deletions
@@ -1,6 +1,13 @@ VDR Plugin 'text2skin' Revision History --------------------------------------- +2007-05-06: Version 1.1-cvs_ext-0.10c (text2skin-1.1-cvs_ext-0.10c.diff) + +- selective update of changed objects + refresh can be controlled for individual objects by the attributes + "refresh" and "changed" + default behaviour is to redraw everything (compatible with old skins) + 2007-05-06: Version 1.1-cvs_ext-0.10b (text2skin-1.1-cvs_ext-0.10b.diff) - increase skin file version to 1.1 @@ -51,7 +51,7 @@ void cText2SkinDisplayChannel::SetChannel(const cChannel *Channel, int Number) if (mChannel != Channel || mNumber != Number) { mChannel = Channel; mNumber = Number; - SetDirty(); + SetDirty(cxRefresh::update); } UpdateUnlock(); } @@ -67,7 +67,7 @@ void cText2SkinDisplayChannel::SetEvents(const cEvent *Present, const cEvent *Fo if (mPresent != Present || mFollowing != Following) { mPresent = Present; mFollowing = Following; - SetDirty(); + SetDirty(cxRefresh::update); } UpdateUnlock(); } @@ -84,7 +84,7 @@ void cText2SkinDisplayChannel::SetMessage(eMessageType Type, const char *Text) if (mType != Type || mText != Text) { mType = Type; mText = Text; - SetDirty(); + SetDirty(cxRefresh::all); } UpdateUnlock(); } @@ -108,7 +108,7 @@ void cText2SkinDisplayChannel::SetButtons(const char *Red, const char *Green, co mButtonGreen = Green; mButtonYellow = Yellow; mButtonBlue = Blue; - SetDirty(); + SetDirty(cxRefresh::update); } UpdateUnlock(); } @@ -372,7 +372,7 @@ void cText2SkinDisplayVolume::SetVolume(int Current, int Total, bool Mute) mCurrent = Current; mTotal = Total; mMute = Mute; - SetDirty(); + SetDirty(cxRefresh::update); } UpdateUnlock(); } @@ -422,7 +422,7 @@ void cText2SkinDisplayReplay::SetTitle(const char *Title) if (Title == NULL) Title = ""; if (mTitle != Title) { mTitle = Title; - SetDirty(); + SetDirty(cxRefresh::update); } UpdateUnlock(); } @@ -436,7 +436,7 @@ void cText2SkinDisplayReplay::SetMode(bool Play, bool Forward, int Speed) mPlay = Play; mForward = Forward; mSpeed = Speed; - SetDirty(); + SetDirty(cxRefresh::update); } UpdateUnlock(); } @@ -447,7 +447,7 @@ void cText2SkinDisplayReplay::SetProgress(int Current, int Total) if (mCurrent != Current || mTotal != Total) { mCurrent = Current; mTotal = Total; - // SetDirty(); TODO: let this cause a display update every frame? + // SetDirty(cxRefresh::update); TODO: let this cause a display update every frame? } UpdateUnlock(); } @@ -456,7 +456,7 @@ void cText2SkinDisplayReplay::SetMarks(const cMarks *Marks) { UpdateLock(); mMarks = Marks; - SetDirty(); + SetDirty(cxRefresh::update); UpdateUnlock(); } @@ -466,7 +466,7 @@ void cText2SkinDisplayReplay::SetCurrent(const char *Current) if (Current == NULL) Current = ""; if (mPosition != Current) { mPosition = Current; - SetDirty(); + SetDirty(cxRefresh::update); } UpdateUnlock(); } @@ -477,7 +477,7 @@ void cText2SkinDisplayReplay::SetTotal(const char *Total) if (Total == NULL) Total = ""; if (mDuration != Total) { mDuration = Total; - SetDirty(); + SetDirty(cxRefresh::update); } UpdateUnlock(); } @@ -488,7 +488,7 @@ void cText2SkinDisplayReplay::SetJump(const char *Jump) if (Jump == NULL) Jump = ""; if (mPrompt != Jump) { mPrompt = Jump; - SetDirty(); + SetDirty(cxRefresh::update); } UpdateUnlock(); } @@ -500,7 +500,7 @@ void cText2SkinDisplayReplay::SetMessage(eMessageType Type, const char *Text) if (mType != Type || mText != Text) { mType = Type; mText = Text; - SetDirty(); + SetDirty(cxRefresh::all); } UpdateUnlock(); } @@ -519,7 +519,7 @@ void cText2SkinDisplayReplay::SetButtons(const char *Red, const char *Green, con mButtonGreen = Green; mButtonYellow = Yellow; mButtonBlue = Blue; - SetDirty(); + SetDirty(cxRefresh::update); } UpdateUnlock(); } @@ -677,7 +677,7 @@ void cText2SkinDisplayMessage::SetMessage(eMessageType Type, const char *Text) if (mType != Type || mText != Text) { mType = Type; mText = Text; - SetDirty(); + SetDirty(cxRefresh::all); } UpdateUnlock(); } @@ -778,7 +778,7 @@ void cText2SkinDisplayMenu::Clear(void) ExtRecordingDescription = ""; mText = ""; cText2SkinRender::Clear(); - SetDirty(); + SetDirty(cxRefresh::all); UpdateUnlock(); } @@ -797,7 +797,7 @@ void cText2SkinDisplayMenu::SetTitle(const char *Title) mUpdate.events = true; mUpdate.resetMarquee = true; mTitle = Title; - SetDirty(); + SetDirty(cxRefresh::update); } UpdateUnlock(); } @@ -821,7 +821,7 @@ void cText2SkinDisplayMenu::SetButtons(const char *Red, const char *Green, const mButtonGreen = Green; mButtonYellow = Yellow; mButtonBlue = Blue; - SetDirty(); + SetDirty(cxRefresh::update); } UpdateUnlock(); } @@ -839,7 +839,7 @@ void cText2SkinDisplayMenu::SetMessage(eMessageType Type, const char *Text) if (mMessageType != Type || mMessageText != Text) { mMessageType = Type; mMessageText = Text; - SetDirty(); + SetDirty(cxRefresh::all); } UpdateUnlock(); } @@ -865,16 +865,22 @@ void cText2SkinDisplayMenu::SetItem(const char *Text, int Index, bool Current, b if (mItems.size() <= (uint)Index) { mItems.push_back(item); - SetDirty(); + mDirtyItems.push_back( Index ); + SetDirty(cxRefresh::list); } else if (mItems[Index] != item) { mItems[Index] = item; - SetDirty(); + // refresh only the changed items + mDirtyItems.push_back( Index ); + SetDirty(cxRefresh::list); } if (Current && mCurrentItem != (uint)Index) { + // refresh only the changed items + mDirtyItems.push_back( mCurrentItem ); + mDirtyItems.push_back( Index ); + SetDirty(cxRefresh::list); mCurrentItem = Index; - SetDirty(); } if (Current) mRender->mMenuScrollbar.currentOnScreen = (uint)Index; @@ -893,7 +899,7 @@ void cText2SkinDisplayMenu::SetEvent(const cEvent *Event) mEvent = Event; ExtPresentDescription = ""; if (mEvent != NULL) - SetDirty(); + SetDirty(cxRefresh::update); } UpdateUnlock(); } @@ -911,7 +917,7 @@ void cText2SkinDisplayMenu::SetRecording(const cRecording *Recording) mRecording = Recording; ExtRecordingDescription = ""; if (mRecording != NULL) - SetDirty(); + SetDirty(cxRefresh::update); } UpdateUnlock(); } @@ -927,7 +933,7 @@ void cText2SkinDisplayMenu::SetText(const char *Text, bool FixedFont) if (Text == NULL) Text = ""; if (mText != Text) { mText = Text; - SetDirty(); + SetDirty(cxRefresh::update); } UpdateUnlock(); } @@ -953,7 +959,7 @@ void cText2SkinDisplayMenu::Scroll(bool Up, bool Page) UpdateLock(); cText2SkinRender::Scroll(Up, Page); - SetDirty(); + SetDirty(cxRefresh::scroll); UpdateUnlock(); } @@ -1421,9 +1427,13 @@ void cText2SkinDisplayTracks::SetTrack(int Index, const char * const *Tracks) UpdateLock(); Dprintf("SetTrack: %d (%s here, %s in array)\n", Index, Tracks[Index], mItems[Index].c_str()); if (mCurrentItem != (uint)Index) { + // refresh only the changed items + mDirtyItems.push_back( mCurrentItem ); + mDirtyItems.push_back( Index ); mCurrentItem = Index; - SetDirty(); + SetDirty(cxRefresh::list); } + UpdateUnlock(); } @@ -1432,7 +1442,7 @@ void cText2SkinDisplayTracks::SetAudioChannel(int AudioChannel) UpdateLock(); if (mAudioChannel != AudioChannel) { mAudioChannel = AudioChannel; - SetDirty(); + SetDirty(cxRefresh::update); } UpdateUnlock(); } @@ -29,7 +29,7 @@ cText2SkinRender::cText2SkinRender(cText2SkinLoader *Loader, cxDisplay::eType Di mScreen(NULL), mScroller(NULL), mBasePath(BasePath), - mDirty(true), + mDirty(cxRefresh::all), mFallback(NULL), mActive(false), mDoUpdate(), @@ -37,6 +37,7 @@ cText2SkinRender::cText2SkinRender(cText2SkinLoader *Loader, cxDisplay::eType Di mStarted(), mUpdateIn(0), mNow(0), + mFirst(true), mBaseSize(), mTabScale(1.0), mTabScaleSet(false) @@ -129,12 +130,16 @@ cText2SkinRender::~cText2SkinRender() void cText2SkinRender::Action(void) { + bool to = true; mActive = true; UpdateLock(); mStarted.Broadcast(); while (mActive) { - if (mUpdateIn) mDoUpdate.TimedWait(mDoUpdateMutex, mUpdateIn); - else mDoUpdate.Wait(mDoUpdateMutex); + to = true; + if (mUpdateIn) to=mDoUpdate.TimedWait(mDoUpdateMutex, mUpdateIn); + else mDoUpdate.Wait(mDoUpdateMutex); + + if(!to) SetDirty(cxRefresh::timeout); if (!mActive) break; // fall out if thread to be stopped @@ -150,10 +155,24 @@ void cText2SkinRender::Update(void) //DStartBench(malen); //DStartBench(ges); Dbench(update); +#ifdef BENCH + fprintf( stderr, "mDirty = 0x%04x\n", mDirty ); +#endif + if( mFirst ) { + mDirty = (1<<cxRefresh::all); + mFirst = false; + } else if( mDirty & (1<<cxRefresh::all) ) { + // we need a complete redraw anyway, that is enough + mDirty = 1 << cxRefresh::all; + } for (uint i = 0; i < mDisplay->Objects(); ++i) DrawObject(mDisplay->GetObject(i)); + mDirty = 0; + while( mDirtyItems.size() > 0 ) + mDirtyItems.pop_back(); + //DShowBench("---\t", malen); //DStartBench(flushen); Dbench(flush); @@ -168,9 +187,11 @@ void cText2SkinRender::Update(void) void cText2SkinRender::DrawObject( cxObject *Object, const txPoint &BaseOffset /*=txPoint(-1,-1)*/, const txSize &BaseSize /*=txPoint(-1,-1)*/, - int ListItem /*=-1*/ ) + int ListItem /*=-1*/, + bool ForceUpdate /*=false*/) { - if (Object->Condition() != NULL && !Object->Condition()->Evaluate()) + if( !Object->mRefresh.Dirty(mDirty, ForceUpdate) || + (Object->Condition()!=NULL && !Object->Condition()->Evaluate())) return; txPoint pos; @@ -178,13 +199,13 @@ void cText2SkinRender::DrawObject( cxObject *Object, pos = Object->Pos(BaseOffset, BaseSize); - if( ListItem >= 0 && !mSkin->Version().Require(1,1) ) { - // Object is part of al list - // Calculate offset of list item relative to the list offset - size = Object->Size(); - } else { - size = Object->Size(BaseOffset, BaseSize); - } + if( ListItem >= 0 && !mSkin->Version().Require(1,1) ) { + // Object is part of al list + // Calculate offset of list item relative to the list offset + size = Object->Size(); + } else { + size = Object->Size(BaseOffset, BaseSize); + } switch (Object->Type()) { @@ -246,7 +267,8 @@ void cText2SkinRender::DrawObject( cxObject *Object, case cxObject::block: for (uint i = 0; i < Object->Objects(); ++i) - DrawObject(Object->GetObject(i), pos, size, ListItem ); + DrawObject(Object->GetObject(i), pos, size, ListItem, + ListItem >= 0 ? true : Object->mRefresh.Full()); break; case cxObject::list:{ @@ -259,6 +281,14 @@ void cText2SkinRender::DrawObject( cxObject *Object, mMenuScrollbar.maxItems = maxitems; SetMaxItems(maxitems); //Dprintf("setmaxitems %d\n", maxitems); uint index = 0; + bool partial = false; + + // is only a partial update needed? + if( !Object->mRefresh.Full() && + !(Object->mRefresh.Type() & mDirty & ~(1<<cxRefresh::list)) ) { + maxitems = mDirtyItems.size(); + partial = true; + } // draw list items for (uint i = 0; i < maxitems; ++i) { @@ -266,13 +296,21 @@ void cText2SkinRender::DrawObject( cxObject *Object, continue; Dbench(item); - index = i; + if( partial ) + index = mDirtyItems[i]; + else + index = i; itempos.y = pos.y + index * itemsize.h; for (uint j = 1; j < Object->Objects(); ++j) { item = Object->GetObject(j); + // exclude items with only "list" update set from + // complete redraw + if( !partial && !(item->mRefresh.Type() & + ~(1<<cxRefresh::list)) ) + continue; item->SetListIndex( index, -1 ); - DrawObject( item, itempos, itemsize, index ); + DrawObject( item, itempos, itemsize, index, true); } Ddiff( "draw item", item ); } @@ -48,7 +48,8 @@ private: tTokenCache mTokenCache; std::string mBasePath; - bool mDirty; + uint mDirty; // bit mask of required updates - set by SetDirty() + std::vector<int> mDirtyItems; uint mMaxItems; cSkin *mFallback; @@ -60,6 +61,7 @@ private: cCondVar mStarted; uint mUpdateIn; uint mNow; // timestamp to calculate update timings + bool mFirst; // First drawing of the display -> draw everything // coordinate transformation txSize mBaseSize; @@ -90,7 +92,7 @@ protected: // Drawing operations void DrawObject(cxObject *Object, const txPoint &BaseOffset=txPoint(-1,-1), const txSize &BaseSize=txSize(-1,-1), - int ListItem=-1 ); + int ListItem=-1, bool ForceUpdate=false); void DrawItemText(cxObject *o, int i, const txPoint &ListOffset, const txSize &ListSize); void DrawBackground(const txPoint &Pos, const txSize &Size, const tColor *Bg, const tColor *Fg, @@ -130,7 +132,8 @@ protected: // functions for display renderer to control behaviour void Flush(bool Force = false); - void SetDirty(void) { mDirty = true; } + void SetDirty( cxRefresh::eRefreshType type=cxRefresh::all) { + mDirty |= 1<<type; } void Scroll(bool Up, bool Page) { if (mScroller != NULL) mScroller->Scroll(Up, Page); } void Clear(void) { DELETENULL(mScroller); } cSkin *Fallback(void) const { return mFallback; } @@ -179,14 +182,17 @@ public: inline void cText2SkinRender::Flush(bool Force) { - if (mDirty || Force) { + if( Force ) { + // do a full redraw + mDirty = (1 << cxRefresh::all); + } + + if (mDirty>0) { mTokenCache.clear(); UpdateLock(); mDoUpdate.Broadcast(); UpdateUnlock(); - - mDirty = false; } } @@ -67,7 +67,10 @@ void cText2SkinScreen::DrawBitmap(int x, int y, const cBitmap &Bitmap, const tCo DrawBitmapOverlay(*mRegions[i], x, y, (cBitmap&)Bitmap, ColorMask); //mRegions[i]->DrawBitmap(x, y, Bitmap); #else - mOsd->DrawBitmap(x, y, Bitmap, ColorFg, ColorBg); + // mOsd->DrawBitmap(x, y, Bitmap, ColorFg, ColorBg); + cBitmap *bm = NULL; + for (int i = 0; (bm=mOsd->GetBitmap(i)) != NULL; ++i) + DrawBitmapOverlay(*bm, x, y, (cBitmap&)Bitmap, ColorMask); #endif } @@ -122,6 +125,14 @@ void cText2SkinScreen::Flush(void) mOsd->DrawBitmap(mRegions[i]->X0(), mRegions[i]->Y0(), *mRegions[i]); #endif } +#ifdef BENCH + int x1=0,y1=0,x2=0,y2=0; + cBitmap *bm; + for(int j=0; (bm=mOsd->GetBitmap(j)) != NULL; j++ ) + if(bm->Dirty(x1,y1,x2,y2)) + fprintf(stderr, "Flush dirty screen area %2i: x1=%3i x2=%3i y1=%3i y2=%3i\n", + j,x1,x2,y1,y2 ); +#endif if (!mOffScreen) mOsd->Flush(); } @@ -8,7 +8,8 @@ #include "common.h" #include <vdr/osd.h> -#undef DIRECTBLIT +// #undef DIRECTBLIT +#define DIRECTBLIT class cText2SkinScreen { /* Skin Editor */ diff --git a/xml/display.c b/xml/display.c index a8268c4..adf500b 100644 --- a/xml/display.c +++ b/xml/display.c @@ -11,7 +11,8 @@ static const std::string DisplayNames[] = cxDisplay::cxDisplay(cxSkin *parent): mSkin(parent), mType((eType)__COUNT_DISPLAY__), - mNumWindows(0) + mNumWindows(0), + mRefreshDefault(NULL) { } diff --git a/xml/display.h b/xml/display.h index a9b2080..af1f590 100644 --- a/xml/display.h +++ b/xml/display.h @@ -35,6 +35,7 @@ private: int mNumWindows; int mNumMarquees; cxObjects mObjects; + cxRefresh mRefreshDefault; public: cxDisplay(cxSkin *Parent); diff --git a/xml/object.c b/xml/object.c index 81dc854..587eab5 100644 --- a/xml/object.c +++ b/xml/object.c @@ -30,6 +30,7 @@ cxObject::cxObject(cxDisplay *Parent): mFontWidth(0), mDelay(150), mIndex(0), + mRefresh(this), mObjects(NULL) { } @@ -59,6 +60,7 @@ cxObject::cxObject(const cxObject &Src): mFontSize(Src.mFontSize), mFontWidth(Src.mFontWidth), mDelay(Src.mDelay), + mRefresh(Src.mRefresh), mObjects(NULL) { if (Src.mCondition) @@ -223,3 +225,115 @@ cxObjects::~cxObjects() delete operator[](i); } + + + + + +cxRefresh::cxRefresh( cxObject *Object ): + mRefreshType(0xFF), + mText(NULL), + mChanged(NULL), + mObject(Object), + mForce(true), + mFull(true) +{ +} + +cxRefresh::~cxRefresh() +{ + delete mText; +} + +bool cxRefresh::Dirty(uint dirty, bool force) +{ + bool need_changed = !mForce && !force && !(mRefreshType & dirty & ~(1<<update)); + + if( !(mRefreshType & dirty) ) + return false; + + if( mChanged == NULL && need_changed ) + return false; + else if( mChanged == NULL ) + return true; + + mEval = mChanged->Evaluate(); + + if( mEval == mLastEval && need_changed ) { + return false; + } else { + mLastEval = mEval; + } + + return true; +} + + + + + +bool cxRefresh::Parse(const std::string &Text) +{ + uint refresh=0; + bool force=false, full=false; + + if( Text.find("all") != std::string::npos ) + refresh |= (1<<all); + + if( Text.find("timeout") != std::string::npos ) + refresh |= (1<<timeout); + + if( Text.find("update") != std::string::npos ) + refresh |= (1<<update); + + //if( Text.find("message") =! std::string::npos ) + // refresh |= (1<<list); + + if( Text.find("list") != std::string::npos ) + refresh |= (1<<list); + + if( Text.find("scroll") != std::string::npos ) + refresh |= (1<<scroll); + + if( Text.find("allways") != std::string::npos ) + refresh |= 0xFF; + + if( Text.find("full") != std::string::npos ) + full = true; + + if( Text.find("force") != std::string::npos ) + force = true; + + if( refresh == 0) + return false; + + mForce = force; + mFull = full; + mRefreshType = refresh; + + return true; +} + +bool cxRefresh::ParseChanged(const std::string &Text) +{ + if( mObject == NULL ) + return false; + + if(mText == NULL) + mText = new cxString(mObject, false); + + if ( mText->Parse(Text) ) { + mChanged = mText; + return true; + } + + return false; +} + +cxRefresh &cxRefresh::operator=(const cxRefresh &a) +{ + mRefreshType = a.mRefreshType; + mForce = a.mForce; + mFull = a.mFull; + return *this; +} diff --git a/xml/object.h b/xml/object.h index 603019f..7a37b5c 100644 --- a/xml/object.h +++ b/xml/object.h @@ -33,6 +33,40 @@ struct txWindow { pos1(_x1, _y2), pos2(_x2, _y2), bpp(_bpp) {} }; +class cxObject; + +class cxRefresh { + friend bool xEndElem(const std::string &name); + +public: + enum eRefreshType { + all, // complete redraw of the screen + timeout, // redraw due to a timeout + //message, // a message was set or removed + update, // update of the osd elements + scroll, // a scroll event + list, // list items or the current item have changed + }; + + cxRefresh(cxObject *Object); + ~cxRefresh(); + bool Dirty(uint dirty, bool force=false); + bool Full(void) const { return mFull; } + uint Type(void) const { return mRefreshType; } + bool Parse(const std::string &Text); + bool ParseChanged(const std::string &Text); + cxRefresh &cxRefresh::operator=(const cxRefresh &b); + +private: + uint mRefreshType; + cxType mLastEval; + cxType mEval; + cxString *mText; + cxString *mChanged; + cxObject *mObject; + bool mForce, mFull; +}; + class cxObjects; class cxObject { @@ -88,6 +122,7 @@ private: int mFontWidth; uint mDelay; uint mIndex; + cxRefresh mRefresh; cxObjects *mObjects; // used for block objects such as <list> public: diff --git a/xml/parser.c b/xml/parser.c index 0045af1..1e3ed73 100644 --- a/xml/parser.c +++ b/xml/parser.c @@ -102,6 +102,7 @@ bool xStartElem(const std::string &name, std::map<std::string,std::string> &attr if (name == "display") { display = new cxDisplay(skin); ATTRIB_MAN_FUNC ("id", display->ParseType); + ATTRIB_OPT_FUNC ("refresh", display->mRefreshDefault.Parse); } else TAG_ERR_REMAIN("skin"); @@ -129,12 +130,18 @@ bool xStartElem(const std::string &name, std::map<std::string,std::string> &attr else { object = new cxObject(display); if (object->ParseType(name)) { + if(parents.size() > 0) + object->mRefresh = parents.back()->mRefresh; + else + object->mRefresh = display->mRefreshDefault; + ATTRIB_OPT_NUMBER("x1", object->mPos1.x); ATTRIB_OPT_NUMBER("y1", object->mPos1.y); ATTRIB_OPT_NUMBER("x2", object->mPos2.x); ATTRIB_OPT_NUMBER("y2", object->mPos2.y); ATTRIB_OPT_FUNC ("condition", object->ParseCondition); - + ATTRIB_OPT_FUNC ("refresh", object->mRefresh.Parse); + ATTRIB_OPT_FUNC ("changed", object->mRefresh.ParseChanged); if (name == "image") { ATTRIB_OPT_NUMBER("x", object->mPos1.x); ATTRIB_OPT_NUMBER("y", object->mPos1.y); @@ -256,6 +263,20 @@ bool xEndElem(const std::string &name) { } } + if (object->mRefresh.mChanged == NULL) { + switch (object->mType) { + case cxObject::text: + case cxObject::marquee: + case cxObject::blink: + case cxObject::scrolltext: + object->mRefresh.mChanged = &object->mText; + break; + + default: + break; + } + } + object->mIndex = oindex++; if (parents.size() > 0) { Dprintf("pushing to parent\n"); |