summaryrefslogtreecommitdiff
path: root/render.c
diff options
context:
space:
mode:
Diffstat (limited to 'render.c')
-rw-r--r--render.c423
1 files changed, 262 insertions, 161 deletions
diff --git a/render.c b/render.c
index 1c09863..65b0ce7 100644
--- a/render.c
+++ b/render.c
@@ -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,7 +37,10 @@ cText2SkinRender::cText2SkinRender(cText2SkinLoader *Loader, cxDisplay::eType Di
mStarted(),
mUpdateIn(0),
mNow(0),
- mBaseSize()
+ mFirst(true),
+ mBaseSize(),
+ mTabScale(1.0),
+ mTabScaleSet(false)
{
if (mDisplay == NULL) {
esyslog("ERROR: text2skin: display for %s missing", cxDisplay::GetType(Display).c_str());
@@ -127,17 +130,29 @@ cText2SkinRender::~cText2SkinRender()
void cText2SkinRender::Action(void)
{
+ bool to = true;
+ uint start_time = time_ms();
mActive = true;
UpdateLock();
mStarted.Broadcast();
while (mActive) {
- if (mUpdateIn) mDoUpdate.TimedWait(mDoUpdateMutex, mUpdateIn);
+ to = true;
+ start_time = mNow;
+
+ if (mUpdateIn) to = mDoUpdate.TimedWait(mDoUpdateMutex, mUpdateIn);
else mDoUpdate.Wait(mDoUpdateMutex);
if (!mActive) break; // fall out if thread to be stopped
-
- mUpdateIn = 0; // has to be re-set within Update();
mNow = time_ms();
+
+ if (mUpdateIn) {
+ if (!to || mNow >= start_time + mUpdateIn) {
+ SetDirty(cxRefresh::timeout);
+ mUpdateIn = 0; // has to be re-set within Update();
+ } else
+ mUpdateIn -= mNow - start_time;
+ }
+
Update();
}
UpdateUnlock();
@@ -145,203 +160,155 @@ void cText2SkinRender::Action(void)
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));
- //DShowBench("---\t", malen);
- //DStartBench(flushen);
+ mDirty = 0;
+ while (mDirtyItems.size() > 0)
+ mDirtyItems.pop_back();
+
Dbench(flush);
mScreen->Flush();
Ddiff("flush only", flush);
Ddiff("complete flush", update);
- //DShowBench("===\t", flushen);
- //DShowBench("=== ges\t", ges);
//printf("====\t%d\n", mDisplay->Objects());
}
-void cText2SkinRender::DrawObject(const cxObject *Object)
+void cText2SkinRender::DrawObject(cxObject *Object,
+ const txPoint &BaseOffset /*=txPoint(-1,-1)*/,
+ const txSize &BaseSize /*=txSize(-1,-1)*/,
+ const txSize &VirtSize /*=txSize(-1,-1)*/,
+ int ListItem /*=-1*/,
+ bool ForceUpdate /*=false*/)
{
- if (Object->Condition() != NULL && !Object->Condition()->Evaluate())
+ if (!Object->mRefresh.Dirty(mDirty, mUpdateIn, ForceUpdate, mNow) ||
+ (Object->Condition() != NULL && !Object->Condition()->Evaluate()))
return;
+ txPoint pos;
+ txSize size;
+
+ pos = Object->Pos(BaseOffset, BaseSize, VirtSize);
+
+ 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, VirtSize);
+
switch (Object->Type()) {
case cxObject::image:
- DrawImage(Object->Pos(), Object->Size(), Object->Bg(), Object->Fg(), Object->Mask(),
+ DrawImage(pos, size, Object->Bg(), Object->Fg(), Object->Mask(),
Object->Alpha(), Object->Colors(), Object->Path());
break;
case cxObject::text:
- DrawText(Object->Pos(), Object->Size(), Object->Fg(), Object->Text(), Object->Font(),
- Object->Align());
+ if (ListItem >= 0 && Object->Display()->Type() == cxDisplay::menu)
+ DrawItemText(Object, ListItem, pos, BaseSize);
+ else
+ DrawText(pos, size, Object->Fg(), Object->Bg(), Object->Text(), Object->Font(),
+ Object->Align());
break;
case cxObject::marquee:
- DrawMarquee(Object->Pos(), Object->Size(), Object->Fg(), Object->Text(), Object->Font(),
- Object->Align(), Object->Delay(), Object->Index());
+ if (ListItem >= 0 && Object->Display()->Type() == cxDisplay::menu)
+ DrawItemText(Object, ListItem, pos, BaseSize);
+ else
+ DrawMarquee(pos, size, Object->Fg(), Object->Bg(), Object->Text(), Object->Font(),
+ Object->Align(), Object->Delay(), Object->State());
break;
case cxObject::blink:
- DrawBlink(Object->Pos(), Object->Size(), Object->Fg(), Object->Bg(), Object->Text(),
- Object->Font(), Object->Align(), Object->Delay(), Object->Index());
+ if (ListItem >= 0 && Object->Display()->Type() == cxDisplay::menu)
+ DrawItemText(Object, ListItem, pos, BaseSize);
+ else
+ DrawBlink(pos, size, Object->Fg(), Object->Bg(), Object->Bl(), Object->Text(),
+ Object->Font(), Object->Align(), Object->Delay(),
+ Object->State());
break;
case cxObject::rectangle:
- DrawRectangle(Object->Pos(), Object->Size(), Object->Fg());
+ DrawRectangle(pos, size, Object->Fg());
break;
case cxObject::ellipse:
- DrawEllipse(Object->Pos(), Object->Size(), Object->Fg(), Object->Arc());
+ DrawEllipse(pos, size, Object->Fg(), Object->Arc());
break;
case cxObject::slope:
- DrawSlope(Object->Pos(), Object->Size(), Object->Fg(), Object->Arc());
+ DrawSlope(pos, size, Object->Fg(), Object->Arc());
break;
case cxObject::progress:
- DrawProgressbar(Object->Pos(), Object->Size(), Object->Current(), Object->Total(),
- Object->Bg(), Object->Fg(), Object->Keep(), Object->Mark(),
+ DrawProgressbar(pos, size, Object->Current(), Object->Total(),
+ Object->Bg(), Object->Fg(), Object->Keep(), Object->Mark(),
Object->Active(), GetMarks());
break;
case cxObject::scrolltext:
- DrawScrolltext(Object->Pos(), Object->Size(), Object->Fg(), Object->Text(), Object->Font(),
+ DrawScrolltext(pos, size, Object->Fg(), Object->Text(), Object->Font(),
Object->Align());
break;
case cxObject::scrollbar:
- DrawScrollbar(Object->Pos(), Object->Size(), Object->Bg(), Object->Fg());
+ DrawScrollbar(pos, size, Object->Bg(), Object->Fg());
case cxObject::block:
for (uint i = 0; i < Object->Objects(); ++i)
- DrawObject(Object->GetObject(i));
+ DrawObject(Object->GetObject(i), pos, size, Object->mVirtSize, ListItem,
+ ListItem >= 0 ? true : Object->mRefresh.Full());
break;
case cxObject::list: {
- const cxObject *item = Object->GetObject(0);
+ cxObject *item = Object->GetObject(0);
if (item && item->Type() == cxObject::item) {
- txSize areasize = Object->Size();
- uint itemheight = item->Size().h;
- uint maxitems = areasize.h / itemheight;
- uint yoffset = 0;
- bool initialEditableWidthSet = false;
-
+ txSize itemsize = item->Size(pos, size);
+ txPoint itempos = pos;
+ itemsize.w = size.w;
+ uint maxitems = size.h / itemsize.h;
mMenuScrollbar.maxItems = maxitems;
SetMaxItems(maxitems); //Dprintf("setmaxitems %d\n", maxitems);
- for (uint i = 0; i < maxitems; ++i, yoffset += itemheight) {
+ 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) {
+ if (!HasTabText(i, -1))
+ continue;
+
+ Dbench(item);
+ if (partial)
+ index = mDirtyItems[i];
+ else
+ index = i;
+
+ itempos.y = pos.y + index * itemsize.h;
for (uint j = 1; j < Object->Objects(); ++j) {
- const cxObject *o = Object->GetObject(j);
- int maxtabs = 1;
-
- if (o->Display()->Type() == cxDisplay::menu)
- maxtabs = cSkinDisplayMenu::MaxTabs;
-
- for (int t = -1; t < maxtabs; ++t) {
- if (!HasTabText(i, t))
- continue;
-
- int thistab = GetTab(t);
- int nexttab = GetTab(t + 1);
-
- cxObject obj(*o);
- obj.SetListIndex(i, t);
- if (obj.Condition() != NULL && !obj.Condition()->Evaluate())
- continue;
-
- obj.mPos1.x += Object->mPos1.x + (t >= 0 ? thistab : 0);
- obj.mPos1.y += Object->mPos1.y + yoffset;
-
- // get end position
- if (t >= 0 && nexttab > 0) {
- // there is a "next tab".. see if it contains text
- int n = t + 1;
- while (n < cSkinDisplayMenu::MaxTabs && !HasTabText(i, n))
- ++n;
- nexttab = GetTab(n);
- }
-
- // set initial EditableWidth
- // this is for plugins like 'extrecmenu' and 'rotor'
- if ((obj.Type() == cxObject::text || obj.Type() == cxObject::marquee || obj.Type() == cxObject::blink) && !initialEditableWidthSet) {
- initialEditableWidthSet = true;
- SetEditableWidth(obj.Size().w);
- }
-
- if (t >= 0 && nexttab > 0 && nexttab < obj.mPos1.x + obj.Size().w - 1)
- // there is a "next tab" with text
- obj.mPos2.x = Object->mPos1.x + o->mPos1.x + nexttab;
- else {
- // there is no "next tab", use the rightmost edge
- obj.mPos2.x += Object->mPos1.x;
- /* not used anymore due to change to fontOsd
- but could be usefull if someone uses a differnt font
-
- if ((obj.Type() == cxObject::text || obj.Type() == cxObject::marquee || obj.Type() == cxObject::blink) && t == 1) {
- // VDR assumes, that the font in the menu is fontOsd,
- // so the EditableWidth is not necessarily correct
- // for TTF
- const cFont *defFont = cFont::GetFont(fontOsd);
- const char *dummy = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ";
- int editableWidth = obj.Size().w;
- if (defFont != obj.Font())
- editableWidth = (int)(editableWidth * defFont->Width(dummy) / (1.1 * obj.Font()->Width(dummy)));
- SetEditableWidth(editableWidth);
- } */
- if ((obj.Type() == cxObject::text || obj.Type() == cxObject::marquee || obj.Type() == cxObject::blink) && t == 1)
- SetEditableWidth(obj.Size().w);
- }
-
- obj.mPos2.y += Object->mPos1.y + yoffset;
-
- std::string text = obj.Text();
- bool isprogress = false;
- if (text.length() > 5
- && text[0] == '[' && text[text.length() - 1] == ']') {
- const char *p = text.c_str() + 1;
- isprogress = true;
- for (; *p != ']'; ++p) {
- if (*p != ' ' && *p != '|') {
- isprogress = false;
- break;
- }
- }
- }
-
- if (isprogress) {
- //Dprintf("detected progress bar tab\n");
- if (obj.Condition() == NULL || obj.Condition()->Evaluate()) {
- int total = text.length() - 2;
- int current = 0;
- const char *p = text.c_str() + 1;
- while (*p == '|')
- (++current, ++p);
-
- txPoint pos = obj.Pos();
- txSize size = obj.Size();
-
- DrawRectangle(txPoint(pos.x, pos.y + 4),
- txSize(size.w, 2), obj.Fg());
- DrawRectangle(txPoint(pos.x, pos.y + 4),
- txSize(2, size.h - 8), obj.Fg());
- DrawRectangle(txPoint(pos.x, pos.y + size.h - 6),
- txSize(size.w, 2), obj.Fg());
- DrawRectangle(txPoint(pos.x + size.w - 2, pos.y + 4),
- txSize(2, size.h - 8), obj.Fg());
-
- pos.x += 4;
- pos.y += 8;
- size.w -= 8;
- size.h -= 16;
- DrawProgressbar(pos, size, current, total, obj.Bg(),
- obj.Fg(), NULL, NULL, NULL, NULL);
- }
- } else
- DrawObject(&obj);
- }
+ item = Object->GetObject(j);
+ item->SetListIndex(index, -1);
+ DrawObject(item, itempos, itemsize, Object->mVirtSize, index, true);
}
+ Ddiff("draw item", item);
}
}
}
@@ -354,6 +321,133 @@ void cText2SkinRender::DrawObject(const cxObject *Object)
}
}
+void cText2SkinRender::DrawItemText(cxObject *Object, int i, const txPoint &ListOffset, const txSize &ListSize)
+{
+ bool initialEditableWidthSet = false;
+ int maxtabs = cSkinDisplayMenu::MaxTabs;
+ txPoint Pos = ListOffset;
+ txSize BaseSize = Object->Size(ListOffset, ListSize);
+ txSize Size = BaseSize;
+
+ if (!mTabScaleSet) {
+ // VDR assumes, that the font in the menu is fontOsd,
+ // so the tab width is not necessarily correct
+ // for TTF
+ const cFont *defFont = cFont::GetFont(fontOsd);
+ const char *dummy = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ";
+ //if (defFont != Object->Font())
+ mTabScale = 1.08 * (float)Object->Font()->Width(dummy) / (float)defFont->Width(dummy);
+ mTabScaleSet = true;
+ }
+
+ // loop over tabs
+ for (int t = 0; t < maxtabs; ++t) {
+ if (!HasTabText(i, t))
+ continue;
+
+ int thistab = (int)(mTabScale * GetTab(t));
+ int nexttab = (int)(mTabScale * GetTab(t + 1));
+
+ Object->SetListIndex(i, t);
+ //if (Object.Condition() != NULL && !Object.Condition()->Evaluate())
+ // continue;
+
+ // set initial EditableWidth
+ // this is for plugins like 'extrecmenu' and 'rotor'
+ if (!initialEditableWidthSet) {
+ initialEditableWidthSet = true;
+ SetEditableWidth((int)(Size.w / mTabScale));
+ }
+
+ // Start position of the tab
+ Pos.x = ListOffset.x + (t >= 0 ? thistab : 0);
+
+ // get end position
+ if (t >= 0 && nexttab > 0) {
+ // there is a "next tab".. see if it contains text
+ int n = t + 1;
+ while (n < cSkinDisplayMenu::MaxTabs && !HasTabText(i, n))
+ ++n;
+ nexttab = (int)(mTabScale * GetTab(n));
+ }
+
+ if (t >= 0 && nexttab > 0 && nexttab < BaseSize.w - 1)
+ // there is a "next tab" with text
+ Size.w = nexttab - thistab;
+ else {
+ // there is no "next tab", use the rightmost edge
+ Size.w = BaseSize.w - thistab;
+ if (t == 1)
+ SetEditableWidth((int)(Size.w / mTabScale));
+ }
+
+ // Does the current tab contain a text-progress bar?
+ std::string text = Object->Text();
+ bool isprogress = false;
+ if (text.length() > 5 && text[0] == '[' && text[text.length() - 1] == ']') {
+ const char *p = text.c_str() + 1;
+ isprogress = true;
+ for (; *p != ']'; ++p) {
+ if (*p != ' ' && *p != '|') {
+ isprogress = false;
+ break;
+ }
+ }
+ }
+
+ if (isprogress) {
+ //Dprintf("detected progress bar tab\n");
+ int total = text.length() - 2;
+ int current = 0;
+ const char *p = text.c_str() + 1;
+ while (*p == '|')
+ (++current, ++p);
+
+ txPoint prog_pos = Pos;
+ txSize prog_size = Size;
+
+ DrawRectangle(txPoint(prog_pos.x, prog_pos.y + 4),
+ txSize(prog_size.w, 2), Object->Fg());
+ DrawRectangle(txPoint(prog_pos.x, prog_pos.y + 4),
+ txSize(2, prog_size.h - 8), Object->Fg());
+ DrawRectangle(txPoint(prog_pos.x, prog_pos.y + prog_size.h - 6),
+ txSize(prog_size.w, 2), Object->Fg());
+ DrawRectangle(txPoint(prog_pos.x + prog_size.w - 2, prog_pos.y + 4),
+ txSize(2, prog_size.h - 8), Object->Fg());
+
+ prog_pos.x += 4;
+ prog_pos.y += 8;
+ prog_size.w -= 8;
+ prog_size.h -= 16;
+ DrawProgressbar(prog_pos, prog_size, current, total, Object->Bg(),
+ Object->Fg(), NULL, NULL, NULL, NULL);
+ }
+ else {
+ switch (Object->Type()) {
+ case cxObject::text:
+ DrawText(Pos, Size, Object->Fg(), Object->Bg(), Object->Text(),
+ Object->Font(), Object->Align());
+ break;
+
+ case cxObject::marquee:
+ DrawMarquee(Pos, Size, Object->Fg(), Object->Bg(), Object->Text(),
+ Object->Font(), Object->Align(), Object->Delay(),
+ Object->State());
+ break;
+
+ case cxObject::blink:
+ DrawBlink(Pos, Size, Object->Fg(), Object->Bg(), Object->Bl(),
+ Object->Text(), Object->Font(), Object->Align(),
+ Object->Delay(), Object->State());
+ break;
+ default:
+ break;
+ }
+
+ }
+ }
+}
+
void cText2SkinRender::DrawImage(const txPoint &Pos, const txSize &Size, const tColor *Bg,
const tColor *Fg, const tColor *Mask, int Alpha, int Colors,
const std::string &Path)
@@ -370,23 +464,25 @@ void cText2SkinRender::DrawImage(const txPoint &Pos, const txSize &Size, const t
}
}
-void cText2SkinRender::DrawText(const txPoint &Pos, const txSize &Size, const tColor *Fg,
- const std::string &Text, const cFont *Font, int Align)
+void cText2SkinRender::DrawText(const txPoint &Pos, const txSize &Size, const tColor *Fg,
+ const tColor *Bg, const std::string &Text, const cFont *Font, int Align)
{
//Dprintf("trying to draw text %s to %d,%d size %d,%d, color %x\n", Text.c_str(), Pos.x, Pos.y,
// Size.w, Size.h, Fg ? *Fg : 0);
+ if (Bg)
+ mScreen->DrawRectangle(Pos.x, Pos.y, Pos.x + Size.w - 1, Pos.y + Size.h - 1, *Bg);
+
mScreen->DrawText(Pos.x, Pos.y, Text.c_str(), Fg ? *Fg : 0, 0, Font, Size.w, Size.h, Align);
}
-void cText2SkinRender::DrawMarquee(const txPoint &Pos, const txSize &Size, const tColor *Fg,
- const std::string &Text, const cFont *Font, int Align,
- uint Delay, uint Index)
+void cText2SkinRender::DrawMarquee(const txPoint &Pos, const txSize &Size, const tColor *Fg,
+ const tColor *Bg, const std::string &Text, const cFont *Font,
+ int Align, uint Delay, txState &state)
{
bool scrolling = Font->Width(Text.c_str()) > Size.w;
- tState &state = mStates[Index];
if (state.text != Text) {
- state = tState();
+ state = txState();
state.text = Text;
}
@@ -432,22 +528,23 @@ void cText2SkinRender::DrawMarquee(const txPoint &Pos, const txSize &Size, const
}
//Dprintf("drawMarquee text = %s, state.text = %s, offset = %d, index = %d, scrolling = %d, mUpdatteIn = %d, nexttime = %d, delay = %d\n",
// Text.c_str(), state.text.c_str(), state.offset, Index, scrolling, mUpdateIn, state.nexttime, Delay);
-
+
+ if (Bg)
+ mScreen->DrawRectangle(Pos.x, Pos.y, Pos.x + Size.w - 1, Pos.y + Size.h - 1, *Bg);
+
mScreen->DrawText(Pos.x, Pos.y, Text.c_str() + state.offset, Fg ? *Fg : 0, clrTransparent, Font,
Size.w, Size.h, Align);
}
-
-void cText2SkinRender::DrawBlink(const txPoint &Pos, const txSize &Size, const tColor *Fg,
- const tColor *Bg, const std::string &Text, const cFont *Font,
- int Align, uint Delay, uint Index)
+
+void cText2SkinRender::DrawBlink(const txPoint &Pos, const txSize &Size, const tColor *Fg,
+ const tColor *Bg, const tColor *Bl, const std::string &Text,
+ const cFont *Font, int Align, uint Delay, txState &state)
{
- tState &state = mStates[Index];
if (state.text != Text) {
- state = tState();
+ state = txState();
state.text = Text;
}
- Dprintf("drawBlink index = %d, state.text = %s, offset = %d\n", Index, state.text.c_str(),
- state.offset);
+ Dprintf("drawBlink state.text = %s, offset = %d\n", state.text.c_str(), state.offset);
if (state.nexttime == 0 || mNow >= state.nexttime) {
state.nexttime = mNow + Delay;
@@ -458,7 +555,11 @@ void cText2SkinRender::DrawBlink(const txPoint &Pos, const txSize &Size, const t
if (mUpdateIn == 0 || updatein < mUpdateIn)
mUpdateIn = updatein;
- const tColor *col = state.offset == 0 ? Fg : Bg;
+ const tColor *col = state.offset == 0 ? Fg : Bl;
+
+ if (Bg)
+ mScreen->DrawRectangle(Pos.x, Pos.y, Pos.x + Size.w - 1, Pos.y + Size.h - 1, *Bg);
+
if (col)
mScreen->DrawText(Pos.x, Pos.y, Text.c_str(), *col, clrTransparent, Font, Size.w, Size.h,
Align);