From 143341499d54689c49588f98a1b729b1adb25a39 Mon Sep 17 00:00:00 2001 From: Huevos Date: Sat, 21 Sep 2024 17:44:01 +0200 Subject: [PATCH] Text blending (#4069) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Added] text blending functionality * most of the code is taken from OpenATV. * Credits to @jblayel e.g. * [Updated] Adjust addons to respect new text blending possibilities * [font.cpp] attempt to fix build error... PLi compiler is old and not compatible with "register" keyword. Please revert when the compiler is updated. error: ISO C++17 does not allow ‘register’ storage class specifier [-Werror=register] --------- Co-authored-by: Dimitar Tsenev --- lib/gdi/font.cpp | 155 +++++++++++------- lib/gdi/font.h | 7 +- lib/gdi/grc.cpp | 3 + lib/gui/elabel.cpp | 13 ++ lib/gui/elabel.h | 2 + lib/gui/elistboxcontent.cpp | 17 +- .../Components/Addons/ColorButtonsSequence.py | 3 +- lib/python/Components/Addons/MainMenu.py | 4 +- lib/python/Components/Addons/ScreenHeader.py | 4 +- .../Converter/TemplatedMultiContent.py | 2 +- 10 files changed, 139 insertions(+), 71 deletions(-) diff --git a/lib/gdi/font.cpp b/lib/gdi/font.cpp index d8ff6a8cbc0..614b74d16f4 100644 --- a/lib/gdi/font.cpp +++ b/lib/gdi/font.cpp @@ -915,7 +915,7 @@ nprint: isprintable=0; return 0; } -void eTextPara::blit(gDC &dc, const ePoint &offset, const gRGB &background, const gRGB &foreground, bool border) +void eTextPara::blit(gDC &dc, const ePoint &offset, const gRGB &cbackground, const gRGB &foreground, bool border) { if (glyphs.empty()) return; @@ -939,6 +939,7 @@ void eTextPara::blit(gDC &dc, const ePoint &offset, const gRGB &background, cons dc.getPixmap(target); gUnmanagedSurface *surface = target->surface; gRGB currentforeground = foreground; + const gRGB background = (m_blend && surface->bpp == 32) ? gRGB(currentforeground.r, currentforeground.g, currentforeground.b, 200) : cbackground; int opcode = -1; @@ -995,15 +996,16 @@ void eTextPara::blit(gDC &dc, const ePoint &offset, const gRGB &background, cons opcode=0; } else opcode=1; - } else if (surface->bpp == 32) + } + else if (surface->bpp == 32) { - opcode=3; + opcode = (m_blend) ? 4 : 3; for (int i=0; i<16; ++i) { + unsigned char da = background.a, dr = background.r, dg = background.g, db = background.b; #define BLEND(y, x, a) (y + (((x-y) * a)>>8)) - unsigned char da = background.a, dr = background.r, dg = background.g, db = background.b; int sa = i * 16; if (sa < 256) { @@ -1018,7 +1020,8 @@ void eTextPara::blit(gDC &dc, const ePoint &offset, const gRGB &background, cons } for (int i=0; i<16; ++i) lookup32_invert[i]=lookup32_normal[i^0xF]; - } else if (surface->bpp == 16) + } + else if (surface->bpp == 16) { opcode=2; for (int i = 0; i != 16; ++i) @@ -1042,7 +1045,8 @@ void eTextPara::blit(gDC &dc, const ePoint &offset, const gRGB &background, cons } for (int i=0; i<16; ++i) lookup16_invert[i]=lookup16_normal[i^0xF]; - } else + } + else { eWarning("[eTextPara] Can't render to %dbpp!", surface->bpp); return; @@ -1056,7 +1060,8 @@ void eTextPara::blit(gDC &dc, const ePoint &offset, const gRGB &background, cons lookup8 = lookup8_normal; lookup16 = lookup16_normal; lookup32 = lookup32_normal; - } else + } + else { lookup8 = lookup8_invert; lookup16 = lookup16_invert; @@ -1128,77 +1133,113 @@ void eTextPara::blit(gDC &dc, const ePoint &offset, const gRGB &background, cons { case 0: // 4bit lookup to 8bit { - int extra_buffer_stride = buffer_stride - sx; - __u8 *td=d; - for (int ay = 0; ay < sy; ay++) - { - int ax; - - for (ax=0; ax>4; - if(b) - *td=lookup8[b]; - ++td; + int ax; + + for (ax=0; ax>4; + if(b) + *td=lookup8[b]; + ++td; + } + s += extra_source_stride; + td += extra_buffer_stride; } - s += extra_source_stride; - td += extra_buffer_stride; - } } break; case 1: // 8bit direct { - int extra_buffer_stride = buffer_stride - sx; - __u8 *td=d; - for (int ay = 0; ay < sy; ay++) - { - int ax; - for (ax=0; ax> 1) - sx; - __u16 *td = (__u16*)d; - for (int ay = 0; ay != sy; ay++) - { - int ax; - for (ax = 0; ax != sx; ax++) + int extra_buffer_stride = (buffer_stride >> 1) - sx; + __u16 *td = (__u16*)d; + for (int ay = 0; ay != sy; ay++) { - int b = (*s++) >> 4; - if (b) - *td = lookup16[b]; - ++td; + int ax; + for (ax = 0; ax != sx; ax++) + { + int b = (*s++) >> 4; + if (b) + *td = lookup16[b]; + ++td; + } + s += extra_source_stride; + td += extra_buffer_stride; } - s += extra_source_stride; - td += extra_buffer_stride; - } } break; case 3: // 32bit { - int extra_buffer_stride = (buffer_stride >> 2) - sx; - __u32 *td=(__u32*)d; - for (int ay = 0; ay < sy; ay++) - { - int ax; - for (ax=0; ax> 2) - sx; + __u32 *td=(__u32*)d; + for (int ay = 0; ay < sy; ay++) { - int b=(*s++)>>4; - if(b) - *td=lookup32[b]; - ++td; + int ax; + for (ax=0; ax>4; + if(b) + *td=lookup32[b]; + ++td; + } + s += extra_source_stride; + td += extra_buffer_stride; } - s += extra_source_stride; - td += extra_buffer_stride; } + break; + case 4: // 32-bit blend + { + int extra_buffer_stride = (buffer_stride >> 2) - sx; + __u32 *td = (__u32 *)d; + for (int ay = 0; ay < sy; ay++) + { + int ax; + for (ax = 0; ax < sx; ax++) + { + int b = (*s++) >> 4; + if (b) + { + // unsigned char frame_a = (*td) >> 24 & 0xFF; + unsigned char frame_r = (*td) >> 16 & 0xFF; + unsigned char frame_g = (*td) >> 8 & 0xFF; + unsigned char frame_b = (*td) & 0xFF; + + unsigned char da = lookup32[b] >> 24 & 0xFF; + unsigned char dr = lookup32[b] >> 16 & 0xFF; + unsigned char dg = lookup32[b] >> 8 & 0xFF; + unsigned char db = lookup32[b] & 0xFF; + +#define BLEND(y, x, a) (y + (((x-y) * a)>>8)) + frame_r = BLEND(frame_r, dr, da) & 0xFF; + frame_g = BLEND(frame_g, dg, da) & 0xFF; + frame_b = BLEND(frame_b, db, da) & 0xFF; +#undef BLEND + *td = ((currentforeground.a ^ 0xFF) << 24) | (frame_r << 16) | (frame_g << 8) | frame_b; + } + ++td; + } + s += extra_source_stride; + td += extra_buffer_stride; + } } break; } diff --git a/lib/gdi/font.h b/lib/gdi/font.h index de31cdcba84..5c83ce9e34a 100644 --- a/lib/gdi/font.h +++ b/lib/gdi/font.h @@ -145,6 +145,7 @@ class eTextPara: public iObject int bboxValid; eRect boundBox; bool doTopBottomReordering; + bool m_blend; int appendGlyph(Font *current_font, FT_Face current_face, FT_UInt glyphIndex, int flags, int rflags, int border, bool last, bool activate_newcolor, unsigned long newcolor); @@ -156,7 +157,7 @@ class eTextPara: public iObject : current_font(0), replacement_font(0), fallback_font(0), current_face(0), replacement_face(0), fallback_face(0), area(area), cursor(start), maximum(0, 0), left(start.x()), charCount(0), totalheight(0), - bboxValid(0), doTopBottomReordering(false) + bboxValid(0), doTopBottomReordering(false), m_blend(false) { } virtual ~eTextPara(); @@ -171,7 +172,9 @@ class eTextPara: public iObject void clear(); - void blit(gDC &dc, const ePoint &offset, const gRGB &background, const gRGB &foreground, bool border = false); + void setBlend(bool blend) { m_blend = blend; } + + void blit(gDC &dc, const ePoint &offset, const gRGB &cbackground, const gRGB &foreground, bool border = false); enum { diff --git a/lib/gdi/grc.cpp b/lib/gdi/grc.cpp index c51a43bcb9f..e515113cfb1 100644 --- a/lib/gdi/grc.cpp +++ b/lib/gdi/grc.cpp @@ -900,6 +900,9 @@ void gDC::exec(const gOpcode *o) int correction = o->parm.renderText->area.height() - bbox.height() - 2; offset += ePoint(0, correction); } + + para->setBlend(flags & gPainter::RT_BLEND); + if (o->parm.renderText->border) { para->blit(*this, offset, m_background_color_rgb, o->parm.renderText->bordercolor, true); diff --git a/lib/gui/elabel.cpp b/lib/gui/elabel.cpp index b7d6bcde1f8..d393aa67d04 100644 --- a/lib/gui/elabel.cpp +++ b/lib/gui/elabel.cpp @@ -110,6 +110,9 @@ int eLabel::event(int event, void *data, void *data2) if (!m_nowrap) flags |= gPainter::RT_WRAP; + if (m_blend) + flags |= gPainter::RT_BLEND; + /* if we don't have shadow, m_shadow_offset will be 0,0 */ painter.renderText(eRect(-m_shadow_offset.x(), -m_shadow_offset.y(), size().width(), size().height()), m_text, flags, m_border_color, m_border_size); @@ -222,6 +225,16 @@ void eLabel::setNoWrap(int nowrap) } } +void eLabel::setAlphatest(int alphatest) +{ + bool blend = (alphatest > 0); // blend if BT_ALPHATEST or BT_ALPHABLEND + if (m_blend != blend) + { + m_blend = blend; + invalidate(); + } +} + void eLabel::clearForegroundColor() { if (m_have_foreground_color) diff --git a/lib/gui/elabel.h b/lib/gui/elabel.h index e4231c8b412..f78ea6a9918 100644 --- a/lib/gui/elabel.h +++ b/lib/gui/elabel.h @@ -32,6 +32,7 @@ class eLabel: public eWidget void setBorderColor(const gRGB &col); void setBorderWidth(int size); void setNoWrap(int nowrap); + void setAlphatest(int alphatest); void clearForegroundColor(); int getNoWrap() { return m_nowrap; } @@ -49,6 +50,7 @@ class eLabel: public eWidget ePoint m_shadow_offset; int m_border_size; int m_nowrap; + bool m_blend = false; enum eLabelEvent { diff --git a/lib/gui/elistboxcontent.cpp b/lib/gui/elistboxcontent.cpp index 6c37e21e7d7..7b20080a348 100644 --- a/lib/gui/elistboxcontent.cpp +++ b/lib/gui/elistboxcontent.cpp @@ -189,6 +189,7 @@ void eListboxPythonStringContent::paint(gPainter &painter, eWindowStyle &style, int border_size = 0; int radius = 0; int edges = 0; + bool alphablendtext = true; /* get local listbox style, if present */ if (m_listbox) @@ -309,6 +310,8 @@ void eListboxPythonStringContent::paint(gPainter &painter, eWindowStyle &style, painter.setForegroundColor(gRGB(0x808080)); int flags = 0; + if (alphablendtext) + flags |= gPainter::RT_BLEND; if (local_style) { style_text_offset = local_style->m_text_offset; @@ -420,6 +423,7 @@ void eListboxPythonConfigContent::paint(gPainter &painter, eWindowStyle &style, int border_size = 0; int radius = 0; int edges = 0; + bool alphablendtext = true; painter.clip(itemrect); style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal); @@ -498,6 +502,7 @@ void eListboxPythonConfigContent::paint(gPainter &painter, eWindowStyle &style, if (m_list && cursorValid) { + int alphablendflag = (alphablendtext) ? gPainter::RT_BLEND : 0; /* get current list item */ ePyObject item = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference! ePyObject text, value; @@ -533,7 +538,7 @@ void eListboxPythonConfigContent::paint(gPainter &painter, eWindowStyle &style, //eDebug("[CONFIGCONTENT] Go to step 1 fill line thick: %d; at pos x: %d, y: %d, w: %d, h: %d", m_sepline_thickness, offset.x() + 15, offset.y() + (m_itemsize.height() - m_sepline_thickness)/2, m_itemsize.width() - 30, m_sepline_thickness); painter.fill(eRect(offset.x()+15, offset.y() + (m_itemsize.height() - m_sepline_thickness)/2, m_itemsize.width() - 30, m_sepline_thickness)); } else { - painter.renderText(eRect(ePoint(offset.x()+15, offset.y()), m_itemsize), string, gPainter::RT_HALIGN_LEFT | gPainter::RT_VALIGN_CENTER, border_color, border_size); + painter.renderText(eRect(ePoint(offset.x()+15, offset.y()), m_itemsize), string, alphablendflag | gPainter::RT_HALIGN_LEFT | gPainter::RT_VALIGN_CENTER, border_color, border_size); } Py_XDECREF(text); if (!sep) { @@ -563,7 +568,7 @@ void eListboxPythonConfigContent::paint(gPainter &painter, eWindowStyle &style, /* check if this is really a tuple */ if (value && PyTuple_Check(value)) { - /* convert type to string */ + /* convert type to string */ ePyObject type = PyTuple_GET_ITEM(value, 0); const char *atype = (type && PyUnicode_Check(type)) ? PyUnicode_AsUTF8(type) : 0; @@ -575,9 +580,9 @@ void eListboxPythonConfigContent::paint(gPainter &painter, eWindowStyle &style, const char *value = (pvalue && PyUnicode_Check(pvalue)) ? PyUnicode_AsUTF8(pvalue) : ""; painter.setFont(fnt2); if (value_alignment_left) - painter.renderText(eRect(ePoint(offset.x()-15, offset.y()), m_itemsize), value, gPainter::RT_HALIGN_LEFT | gPainter::RT_VALIGN_CENTER, border_color, border_size); + painter.renderText(eRect(ePoint(offset.x()-15, offset.y()), m_itemsize), value, alphablendflag | gPainter::RT_HALIGN_LEFT | gPainter::RT_VALIGN_CENTER, border_color, border_size); else - painter.renderText(eRect(ePoint(offset.x()-15, offset.y()), m_itemsize), value, gPainter::RT_HALIGN_RIGHT| gPainter::RT_VALIGN_CENTER, border_color, border_size); + painter.renderText(eRect(ePoint(offset.x()-15, offset.y()), m_itemsize), value, alphablendflag | gPainter::RT_HALIGN_RIGHT| gPainter::RT_VALIGN_CENTER, border_color, border_size); /* pvalue is borrowed */ } else if (!strcmp(atype, "slider")) @@ -709,9 +714,9 @@ void eListboxPythonConfigContent::paint(gPainter &painter, eWindowStyle &style, const char *value = (ppixmap && PyUnicode_Check(ppixmap)) ? PyUnicode_AsUTF8(ppixmap) : ""; painter.setFont(fnt2); if (value_alignment_left) - painter.renderText(eRect(ePoint(offset.x()-15, offset.y()), m_itemsize), value, gPainter::RT_HALIGN_LEFT | gPainter::RT_VALIGN_CENTER, border_color, border_size); + painter.renderText(eRect(ePoint(offset.x()-15, offset.y()), m_itemsize), value, alphablendflag | gPainter::RT_HALIGN_LEFT | gPainter::RT_VALIGN_CENTER, border_color, border_size); else - painter.renderText(eRect(ePoint(offset.x()-15, offset.y()), m_itemsize), value, gPainter::RT_HALIGN_RIGHT| gPainter::RT_VALIGN_CENTER, border_color, border_size); + painter.renderText(eRect(ePoint(offset.x()-15, offset.y()), m_itemsize), value, alphablendflag | gPainter::RT_HALIGN_RIGHT| gPainter::RT_VALIGN_CENTER, border_color, border_size); } else { diff --git a/lib/python/Components/Addons/ColorButtonsSequence.py b/lib/python/Components/Addons/ColorButtonsSequence.py index 7606844a2e2..e61d770cf17 100644 --- a/lib/python/Components/Addons/ColorButtonsSequence.py +++ b/lib/python/Components/Addons/ColorButtonsSequence.py @@ -1,6 +1,6 @@ from Components.Addons.GUIAddon import GUIAddon -from enigma import eListbox, eListboxPythonMultiContent, BT_ALIGN_CENTER, RT_VALIGN_CENTER, RT_HALIGN_LEFT, RT_HALIGN_CENTER, BT_SCALE, eSize, getDesktop, gFont +from enigma import eListbox, eListboxPythonMultiContent, BT_ALIGN_CENTER, RT_VALIGN_CENTER, RT_HALIGN_LEFT, RT_HALIGN_CENTER, RT_BLEND, BT_SCALE, eSize, getDesktop, gFont from skin import parseScale, parseColor, parseFont, applySkinFactor @@ -109,6 +109,7 @@ def buildEntry(self, sequence): backColor = buttonBgColor if self.renderType == "ImageTextOver": + textFlags = textFlags | RT_BLEND if x in self.pixmaps: pic = LoadPixmap(resolveFilename(SCOPE_GUISKIN, self.pixmaps[x])) if pic: diff --git a/lib/python/Components/Addons/MainMenu.py b/lib/python/Components/Addons/MainMenu.py index e173ad2ae9e..dc398b159c7 100644 --- a/lib/python/Components/Addons/MainMenu.py +++ b/lib/python/Components/Addons/MainMenu.py @@ -1,6 +1,6 @@ from Components.Addons.GUIAddon import GUIAddon -from enigma import eListbox, eListboxPythonMultiContent, gFont, RT_HALIGN_LEFT, RT_VALIGN_CENTER, getDesktop, eSize +from enigma import eListbox, eListboxPythonMultiContent, gFont, RT_BLEND, RT_HALIGN_LEFT, RT_VALIGN_CENTER, getDesktop, eSize from skin import applySkinFactor, parseFont, parseColor, parseScale @@ -64,7 +64,7 @@ def buildEntry(self, *args): res.append(MultiContentEntryText( pos=(xPos, 0), size=(textWidth, self.itemHeight), - font=0, flags=RT_HALIGN_LEFT | RT_VALIGN_CENTER, + font=0, flags=RT_BLEND | RT_HALIGN_LEFT | RT_VALIGN_CENTER, text=item_text, color=self.foregroundColor, color_sel=self.foregroundColorSelected, backcolor=None, backcolor_sel=None)) diff --git a/lib/python/Components/Addons/ScreenHeader.py b/lib/python/Components/Addons/ScreenHeader.py index 0a686ecf027..d6b793c0c01 100644 --- a/lib/python/Components/Addons/ScreenHeader.py +++ b/lib/python/Components/Addons/ScreenHeader.py @@ -1,6 +1,6 @@ from Components.Addons.GUIAddon import GUIAddon -from enigma import eListbox, eListboxPythonMultiContent, gFont, RT_HALIGN_LEFT, RT_VALIGN_CENTER +from enigma import eListbox, eListboxPythonMultiContent, gFont, RT_BLEND, RT_HALIGN_LEFT, RT_VALIGN_CENTER from skin import applySkinFactor, parseFont, parseColor @@ -86,7 +86,7 @@ def buildEntry(self, sequence): res.append(MultiContentEntryText( pos=(xPos, yPos), size=(self.instance.size().width() - xPos, itemHeight), - font=fontIndex, flags=RT_HALIGN_LEFT | RT_VALIGN_CENTER, + font=fontIndex, flags=RT_BLEND | RT_HALIGN_LEFT | RT_VALIGN_CENTER, text=x.text, color=foreColor, color_sel=foreColor, backcolor=self.backgroundColor, backcolor_sel=self.backgroundColor)) diff --git a/lib/python/Components/Converter/TemplatedMultiContent.py b/lib/python/Components/Converter/TemplatedMultiContent.py index 8e06ab04c8e..85257846326 100644 --- a/lib/python/Components/Converter/TemplatedMultiContent.py +++ b/lib/python/Components/Converter/TemplatedMultiContent.py @@ -8,7 +8,7 @@ class TemplatedMultiContent(StringList): def __init__(self, args): StringList.__init__(self, args) - from enigma import BT_SCALE, RT_HALIGN_CENTER, RT_HALIGN_LEFT, RT_HALIGN_RIGHT, RT_VALIGN_BOTTOM, RT_VALIGN_CENTER, RT_VALIGN_TOP, RT_WRAP, eListboxPythonMultiContent, gFont + from enigma import BT_SCALE, RT_BLEND, RT_HALIGN_CENTER, RT_HALIGN_LEFT, RT_HALIGN_RIGHT, RT_VALIGN_BOTTOM, RT_VALIGN_CENTER, RT_VALIGN_TOP, RT_WRAP, eListboxPythonMultiContent, gFont from skin import parseFont, getSkinFactor from Components.MultiContent import MultiContentEntryPixmap, MultiContentEntryPixmapAlphaBlend, MultiContentEntryPixmapAlphaTest, MultiContentEntryProgress, MultiContentEntryProgressPixmap, MultiContentEntryText, MultiContentTemplateColor f = getSkinFactor()