Support customized subtitles.
authorsmlee <smlee@dev3>
Thu, 17 Mar 2016 10:33:22 +0000 (19:33 +0900)
committersmlee <smlee@dev3>
Fri, 18 Mar 2016 03:14:29 +0000 (12:14 +0900)
 - font color, size, opacity, edge style,
 - background color, opacity,
 - subtitle position, align, etc.
 - add quick menu for subtitle

19 files changed:
data/menu.xml
data/setup.xml
lib/base/nconfig.cpp
lib/base/nconfig.h
lib/dvb/subtitle.cpp
lib/dvb/subtitle.h
lib/gdi/font.cpp
lib/gdi/font.h
lib/gdi/gpixmap.h
lib/gdi/grc.cpp
lib/gdi/grc.h
lib/gui/esubtitle.cpp
lib/gui/esubtitle.h
lib/python/Components/UsageConfig.py
lib/python/Screens/AudioSelection.py
lib/python/Screens/InfoBarGenerics.py
lib/python/Screens/Setup.py
lib/service/servicedvb.cpp
lib/service/servicemp3.cpp

index 03e582c..5c8cbb2 100755 (executable)
@@ -50,6 +50,7 @@
                                <item level="0" text="Language" entryID="language_setup"><screen module="LanguageSelection" /></item>
                                <item level="0" entryID="usage_setup"><setup id="usage" /></item>
                                <item level="0" entryID="timezone_setup"><setup id="timezone"/></item>
+                               <item level="2" entryID="subtitle_setup"><setup id="subtitlesetup"/></item>
                                <item level="0" entryID="av_setup"><setup id="avsetup"/></item>
                                <!--<item level="0" text="Video Setup" entryID="video_setup"><screen module="VideoSetup" /></item>-->
                                <item level="1" entryID="rfmod_setup" requires="RfModulator"><setup id="RFmod"/></item>
index 442fe13..6100565 100755 (executable)
                        <item level="2" text="Limited character set for recording filenames">config.recording.ascii_filenames</item>
                        <item level="2" text="Composition of the recording filenames">config.recording.filename_composition</item>
                </setup>
+               <setup key="subtitlesetup" title="Subtitle settings">
+                       <item level="2" text="Subtitle font color" description="Configure the color of the subtitles.">config.subtitles.subtitle_fontcolor</item>
+                       <item level="2" text="Subtitle font size" description="Configure the font size of the subtitles.">config.subtitles.subtitle_fontsize</item>
+                       <item level="2" text="Background color" description="Configure the color of the background of subtitles.">config.subtitles.subtitle_bgcolor</item>
+                       <item level="2" text="Background transparency" description="Configure the transparency of the background of subtitles.">config.subtitles.subtitle_bgopacity</item>
+                       <item level="2" text="Character edge style" description="Configure the character style of the subtitles.">config.subtitles.subtitle_edgestyle</item>
+                       <item level="2" text="Character style level" description="Configure the level of the character styles. The character style makes the subtitles easier to read on a light background.">config.subtitles.subtitle_edgestyle_level</item>
+                       <item level="2" text="Subtitle text transparency" description="Configure the transparency of the subtitles.">config.subtitles.subtitle_opacity</item>
+                       <item level="2" text="Use original position" description="When enabled, teletext subtitles will be displayed at their original position.">config.subtitles.subtitle_original_position</item>
+                       <item level="2" text="Subtitle alignment" description="Configure the horizontal alignment of the subtitles.">config.subtitles.subtitle_alignment</item>
+                       <item level="2" text="Subtitle position" description="Configure the vertical position of the subtitles, measured from the bottom of the screen.">config.subtitles.subtitle_position</item>
+                       <item level="2" text="Center DVB subtitles" description="When enabled, graphical DVB subtitles will be centered horizontally.">config.subtitles.dvb_subtitles_centered</item>
+                       <item level="2" text="Subtitle delay when timing lacks" description="Configure the subtitle delay when timing information is not available.">config.subtitles.subtitle_noPTSrecordingdelay</item>
+                       <item level="2" text="Subtitle delay when timing is bad" description="Configure an additional delay to improve subtitle synchronisation.">config.subtitles.subtitle_bad_timing_delay</item>
+                       <item level="2" text="External subtitle switch fonts" description="Configure if the subtitle should switch between normal, italic, bold and bolditalic">config.subtitles.pango_subtitle_fontswitch</item>
+                       <item level="2" text="External subtitle dialog colorisation" description="Replace `- ` in dialogs with colored text per speaker (like teletext subtitles for the hearing impaired)">config.subtitles.colourise_dialogs</item>
+                       <item level="2" text="Rewrap external subtitles" description="When enabled, reformat subtitles to match the width of the screen.">config.subtitles.subtitle_rewrap</item>
+                       <item level="2" text="Delay for external subtitles" description="Configure an additional delay to improve external subtitle synchronisation.">config.subtitles.pango_subtitles_delay</item>
+                       <item level="2" text="Set fps for external subtitles" description="Can be used for different fps between external subtitles and video.">config.subtitles.pango_subtitles_fps</item>
+                       <item level="2" text="Automatically turn on external subtitles" description="When enabled, external subtitles will be always turned on for playback movie.">config.subtitles.pango_autoturnon</item>
+               </setup>
                <setup key="harddisk" title="Harddisk setup" >
                        <item level="0" text="Harddisk standby after">config.usage.hdd_standby</item>
                </setup>
index 106558a..8207b71 100644 (file)
@@ -33,3 +33,25 @@ RESULT ePythonConfigQuery::getConfigValue(const char *key, std::string &value)
        }
        return -1;
 }
+
+int ePythonConfigQuery::getConfigIntValue(const char *key, int defaultvalue)
+{
+       int result = defaultvalue;
+       std::string value;
+
+       if (!getConfigValue(key, value))
+               result = (value != "") ? atoi(value.c_str()) : result;
+
+       return result;
+}
+
+bool ePythonConfigQuery::getConfigBoolValue(const char *key, bool defaultvalue)
+{
+       bool result = defaultvalue;
+       std::string value;
+
+       if (!getConfigValue(key, value))
+               result = (value == "True" || value == "true");
+
+       return result;
+}
index 3667439..4674af6 100644 (file)
@@ -12,6 +12,8 @@ public:
        static void setQueryFunc(SWIG_PYOBJECT(ePyObject) func);
 #ifndef SWIG
        static RESULT getConfigValue(const char *key, std::string &value);
+       static int getConfigIntValue(const char *key, int defaultvalue = 0);
+       static bool getConfigBoolValue(const char *key, bool defaultvalue = false);
 #endif
 };
 
index 06bb266..78b1874 100644 (file)
@@ -9,6 +9,7 @@
 #include <lib/dvb/subtitle.h>
 #include <lib/base/smartptr.h>
 #include <lib/base/eerror.h>
+#include <lib/base/nconfig.h>
 #include <lib/gdi/gpixmap.h>
 
 void bitstream_init(bitstream *bit, const void *buffer, int size)
@@ -56,7 +57,8 @@ static int extract_pts(pts_t &pts, __u8 *pkt)
 
 void eDVBSubtitleParser::subtitle_process_line(subtitle_region *region, subtitle_region_object *object, int line, __u8 *data, int len)
 {
-       int x = object->object_horizontal_position;
+       bool subcentered = ePythonConfigQuery::getConfigBoolValue("config.subtitles.dvb_subtitles_centered");
+       int x = subcentered ? (region->width - len) /2 : object->object_horizontal_position;
        int y = object->object_vertical_position + line;
        if (x + len > region->width)
        {
@@ -72,6 +74,14 @@ void eDVBSubtitleParser::subtitle_process_line(subtitle_region *region, subtitle
        }
 //     eDebug("inserting %d bytes (into region %d)", len, region->region_id);
 //     eDebug("put data to buffer %p", &(*region->buffer));
+       if (subcentered && region->region_id && line < 3)
+       {
+               for (int i = 0; i < len; i++)
+                       if(data[i] <= 8)
+                       {
+                               data[i] = 0;
+                       }
+       }
        memcpy((__u8*)region->buffer->surface->data + region->buffer->surface->stride * y + x, data, len);
 }
 
@@ -1173,6 +1183,8 @@ void eDVBSubtitleParser::subtitle_redraw(int page_id)
                                        break;
                        }
 
+                       int bgopacity = ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_bgopacity");
+                       int fontcolor = ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_fontcolor");
                        for (int i=0; i<clut_size; ++i)
                        {
                                if (entries && entries[i].valid)
@@ -1185,10 +1197,71 @@ void eDVBSubtitleParser::subtitle_redraw(int page_id)
                                                y -= 16;
                                                cr -= 128;
                                                cb -= 128;
+
+                                               if (fontcolor == FONTCOLOR_WHITE || fontcolor == FONTCOLOR_BLACK)
+                                               {
+                                                       cr = 0;
+                                                       cb = 0;
+                                               }
+
                                                palette[i].r = MAX(MIN(((298 * y            + 460 * cr) / 256), 255), 0);
                                                palette[i].g = MAX(MIN(((298 * y -  55 * cb - 137 * cr) / 256), 255), 0);
                                                palette[i].b = MAX(MIN(((298 * y + 543 * cb           ) / 256), 255), 0);
-                                               palette[i].a = (entries[i].T) & 0xFF;
+                                               switch (fontcolor)
+                                               {
+                                                       case FONTCOLOR_YELLOW:
+                                                               palette[i].b = 0;
+                                                               break;
+                                                       case FONTCOLOR_GREEN:
+                                                               palette[i].r =
+                                                               palette[i].b = 0;
+                                                               break;
+                                                       case FONTCOLOR_CYAN:
+                                                               palette[i].r = 0;
+                                                               break;
+                                                       case FONTCOLOR_BLUE:
+                                                               palette[i].r =
+                                                               palette[i].g = 0;
+                                                               break;
+                                                       case FONTCOLOR_MAGNETA:
+                                                               palette[i].g = 0;
+                                                               break;
+                                                       case FONTCOLOR_RED:
+                                                               palette[i].g =
+                                                               palette[i].b = 0;
+                                                               break;
+                                                       case FONTCOLOR_BLACK:
+                                                               palette[i].r = 255 - palette[i].r;
+                                                               palette[i].g = 255 - palette[i].g;
+                                                               palette[i].b = 255 - palette[i].b;
+                                                               break;
+
+                                                       default:
+                                                               break;
+                                               }
+                                               if (bgopacity)
+                                               {
+                                                       if (palette[i].r || palette[i].g || palette[i].b)
+                                                               palette[i].a = (entries[i].T) & 0xFF;
+                                                       else
+                                                               palette[i].a = bgopacity;
+                                               }
+                                               else
+                                                       palette[i].a = 0x1;
+
+                                               if (fontcolor == FONTCOLOR_BLACK)
+                                               {
+                                                       if (bgopacity)
+                                                       {
+                                                               if (255 - palette[i].r || 255 - palette[i].g || 255 - palette[i].b)
+                                                                       palette[i].a = (entries[i].T) & 0xFF;
+                                                               else
+                                                                       palette[i].a = bgopacity;
+                                                       }
+                                                       else
+                                                               palette[i].a = 0x1;
+                                               }
+
 //                                             eDebug("override clut entry %d RGBA %02x%02x%02x%02x", i,
 //                                                     palette[i].r, palette[i].g, palette[i].b, palette[i].a);
                                        }
index c7a7e96..e34fd27 100644 (file)
@@ -8,6 +8,28 @@
 
 typedef unsigned char __u8;
 
+enum {
+       FONTCOLOR_WHITE = 1,
+       FONTCOLOR_YELLOW,
+       FONTCOLOR_GREEN,
+       FONTCOLOR_CYAN,
+       FONTCOLOR_BLUE,
+       FONTCOLOR_MAGNETA,
+       FONTCOLOR_RED,
+       FONTCOLOR_BLACK,
+};
+
+enum {
+       BGCOLOR_BLACK = 0,
+       BGCOLOR_RED,
+       BGCOLOR_MAGNETA,
+       BGCOLOR_BLUE,
+       BGCOLOR_CYAN,
+       BGCOLOR_GREEN,
+       BGCOLOR_YELLOW,
+       BGCOLOR_WHITE,
+};
+
 struct subtitle_clut_entry
 {
        __u8 Y, Cr, Cb, T;
index 9e17547..3a32e2a 100644 (file)
@@ -118,6 +118,32 @@ FT_Error fontRenderClass::getGlyphBitmap(FTC_Image_Desc *font, FT_ULong glyph_in
        return res;
 }
 
+FT_Error fontRenderClass::getGlyphImage(FTC_Image_Desc *font, FT_ULong glyph_index, FT_Glyph *glyph, FT_Glyph *borderglyph, int bordersize)
+{
+       FT_Glyph image;
+       FT_Error err = FTC_ImageCache_Lookup(imageCache, font, glyph_index, &image, NULL);
+       if (err) return err;
+
+       if (glyph)
+       {
+               err = FT_Glyph_Copy(image, glyph);
+               if (err) return err;
+       }
+
+       if (borderglyph && bordersize)
+       {
+               err = FT_Glyph_Copy(image, borderglyph);
+               if (err) return err;
+               if (bordersize != strokerRadius)
+               {
+                       strokerRadius = bordersize;
+                       FT_Stroker_Set(stroker, strokerRadius, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
+               }
+               err = FT_Glyph_Stroke(borderglyph, stroker, 1);
+       }
+       return err;
+}
+
 std::string fontRenderClass::AddFont(const std::string &filename, const std::string &name, int scale)
 {
        eDebugNoNewLine("[FONT] adding font %s...", filename.c_str());
@@ -185,7 +211,12 @@ fontRenderClass::fontRenderClass(): fb(fbClass::getInstance())
                {
                        eDebug("[FONT] initializing font cache imagecache failed!");
                }
+               if (FT_Stroker_New(library, &stroker))
+               {
+                       eDebug("[FONT] initializing font stroker failed!");
+               }
        }
+       strokerRadius = -1;
        return;
 }
 
@@ -267,21 +298,79 @@ FT_Error Font::getGlyphBitmap(FT_ULong glyph_index, FTC_SBit *sbit)
        return renderer->getGlyphBitmap(&font, glyph_index, sbit);
 }
 
+FT_Error Font::getGlyphImage(FT_ULong glyph_index, FT_Glyph *glyph, FT_Glyph *borderglyph, int bordersize)
+{
+       return renderer->getGlyphImage(&font, glyph_index, glyph, borderglyph, bordersize);
+}
+
 Font::~Font()
 {
 }
 
 DEFINE_REF(eTextPara);
 
-int eTextPara::appendGlyph(Font *current_font, FT_Face current_face, FT_UInt glyphIndex, int flags, int rflags)
+int eTextPara::appendGlyph(Font *current_font, FT_Face current_face, FT_UInt glyphIndex, int flags, int rflags, int border, bool activate_newcolor, unsigned long newcolor)
 {
-       FTC_SBit glyph;
-       if (current_font->getGlyphBitmap(glyphIndex, &glyph))
-               return 1;
+       int xadvance, left, top, height;
+       pGlyph ng;
+       int xborder = 0;
+
+       if (border)
+       {
+               /* TODO: scale border radius with current_font scaling */
+               if (current_font->getGlyphImage(glyphIndex, &ng.image, &ng.borderimage, 64 * border))
+                       return 1;
+               if (ng.image && ng.image->format != FT_GLYPH_FORMAT_BITMAP)
+               {
+                       FT_Glyph_To_Bitmap(&ng.image, FT_RENDER_MODE_NORMAL, NULL, 1);
+                       if (ng.image->format != FT_GLYPH_FORMAT_BITMAP) return 1;
+               }
+               if (ng.borderimage && ng.borderimage->format != FT_GLYPH_FORMAT_BITMAP)
+               {
+                       FT_Glyph_To_Bitmap(&ng.borderimage, FT_RENDER_MODE_NORMAL, NULL, 1);
+                       if (ng.borderimage->format != FT_GLYPH_FORMAT_BITMAP) return 1;
+               }
+               FT_BitmapGlyph glyph = NULL;
+               if (ng.borderimage)
+               {
+                       xadvance = ng.borderimage->advance.x;
+                       if (!previous)
+                       {
+                               /* Move the first character, to make sure the border does not get cut off by the boundingbox (xborder is in pixel units, so just divide the width difference by two)  */
+                               xborder = (((FT_BitmapGlyph)ng.borderimage)->bitmap.width - ((FT_BitmapGlyph)ng.image)->bitmap.width) / 2;
+                       }
+                       glyph = (FT_BitmapGlyph)ng.borderimage;
+               }
+               else if (ng.image)
+               {
+                       xadvance = ng.image->advance.x;
+                       glyph = (FT_BitmapGlyph)ng.image;
+               }
+               else
+               {
+                       return 1;
+               }
+               xadvance >>= 16;
+
+               left = glyph->left;
+               top = glyph->top;
+               height = glyph->bitmap.rows;
+       }
+       else
+       {
+               FTC_SBit glyph;
+               if (current_font->getGlyphBitmap(glyphIndex, &glyph))
+                       return 1;
+
+               xadvance = glyph->xadvance;
+               left = glyph->left;
+               top = glyph->top;
+               height = glyph->height;
+       }
 
        int nx=cursor.x();
 
-       nx+=glyph->xadvance;
+       nx+=xadvance;
 
        if (
                        (rflags&RS_WRAP) && 
@@ -338,8 +427,7 @@ int eTextPara::appendGlyph(Font *current_font, FT_Face current_face, FT_UInt gly
                }
        }
 
-       int xadvance=glyph->xadvance, kern=0;
-
+       int kern=0;
        if (previous && use_kerning)
        {
                FT_Vector delta;
@@ -347,21 +435,26 @@ int eTextPara::appendGlyph(Font *current_font, FT_Face current_face, FT_UInt gly
                kern=delta.x>>6;
        }
 
-       pGlyph ng;
-       ng.bbox.setLeft( ((flags&GS_ISFIRST)|(cursor.x()-1))+glyph->left );
-       ng.bbox.setTop( cursor.y() - glyph->top );
-       ng.bbox.setWidth( glyph->width );
-       ng.bbox.setHeight( glyph->height );
+       ng.bbox.setLeft( ((flags&GS_ISFIRST)|(cursor.x()-1))+left );
+       ng.bbox.setTop( cursor.y() - top );
+       ng.bbox.setHeight( height );
 
-       xadvance += kern;
+       xadvance += kern + xborder;
        ng.bbox.setWidth(xadvance);
 
-       ng.x = cursor.x()+kern;
+       ng.x = cursor.x()+kern+xborder;
        ng.y = cursor.y();
        ng.w = xadvance;
        ng.font = current_font;
        ng.glyph_index = glyphIndex;
        ng.flags = flags;
+
+       if (activate_newcolor)
+       {
+               ng.flags |= GS_COLORCHANGE;
+               ng.newcolor = newcolor;
+       }
+
        glyphs.push_back(ng);
        ++charCount;
 
@@ -483,7 +576,7 @@ void eTextPara::setFont(Font *fnt, Font *replacement)
 void
 shape (std::vector<unsigned long> &string, const std::vector<unsigned long> &text);
 
-int eTextPara::renderString(const char *string, int rflags)
+int eTextPara::renderString(const char *string, int rflags, int border)
 {
        singleLock s(ftlock);
        
@@ -572,7 +665,9 @@ int eTextPara::renderString(const char *string, int rflags)
        uc_visual.assign(target, target+size);
 
        glyphs.reserve(size);
-       
+
+       unsigned long newcolor = 0;
+       bool activate_newcolor = false;
        int nextflags = 0;
        
        for (std::vector<unsigned long>::const_iterator i(uc_visual.begin());
@@ -601,6 +696,24 @@ int eTextPara::renderString(const char *string, int rflags)
                                        case 'r':
                                                i++;
                                                goto nprint;
+                                       case 'c':
+                                       {
+                                               char color[8];
+                                               int codeidx;
+                                               for (codeidx = 0; codeidx < 8; codeidx++)
+                                               {
+                                                       if ((i + 2 + codeidx) == uc_visual.end()) break;
+                                                       color[codeidx] = (char)((*(i + 2 + codeidx)) & 0xff);
+                                               }
+                                               if (codeidx == 8)
+                                               {
+                                                       newcolor = gRGB(color).argb();
+                                                       activate_newcolor = true;
+                                                       isprintable = 0;
+                                                       i += 1 + codeidx;
+                                               }
+                                               break;
+                                       }
                                        default:
                                        ;
                                }
@@ -656,9 +769,11 @@ nprint:    isprintable=0;
                                if (!index)
                                        eDebug("unicode U+%4lx not present", chr);
                                else
-                                       appendGlyph(replacement_font, replacement_face, index, flags, rflags);
+                                       appendGlyph(replacement_font, replacement_face, index, flags, rflags, border, activate_newcolor, newcolor);
                        } else
-                               appendGlyph(current_font, current_face, index, flags, rflags);
+                               appendGlyph(current_font, current_face, index, flags, rflags, border, activate_newcolor, newcolor);
+
+                       activate_newcolor = false;
                }
        }
        bboxValid=false;
@@ -679,8 +794,10 @@ nprint:    isprintable=0;
        return 0;
 }
 
-void eTextPara::blit(gDC &dc, const ePoint &offset, const gRGB &background, const gRGB &foreground)
+void eTextPara::blit(gDC &dc, const ePoint &offset, const gRGB &background, const gRGB &foreground, bool border)
 {
+       if (glyphs.empty()) return;
+
        singleLock s(ftlock);
        
        if (!current_font)
@@ -700,8 +817,9 @@ void eTextPara::blit(gDC &dc, const ePoint &offset, const gRGB &background, cons
        ePtr<gPixmap> target;
        dc.getPixmap(target);
        gSurface *surface = target->surface;
+       gRGB currentforeground = foreground;
 
-       register int opcode;
+       register int opcode = -1;
 
        gColor *lookup8, lookup8_invert[16];
        gColor *lookup8_normal=0;
@@ -709,115 +827,152 @@ void eTextPara::blit(gDC &dc, const ePoint &offset, const gRGB &background, cons
        __u16 lookup16_normal[16], lookup16_invert[16], *lookup16;
        __u32 lookup32_normal[16], lookup32_invert[16], *lookup32;
 
-       if (surface->bpp == 8)
-       {
-               if (surface->clut.data)
-               {
-                       lookup8_normal=getColor(surface->clut, background, foreground).lookup;
-                       
-                       int i;
-                       for (i=0; i<16; ++i)
-                               lookup8_invert[i] = lookup8_normal[i^0xF];
-                       
-                       opcode=0;
-               } else
-                       opcode=1;
-       } else if (surface->bpp == 16)
-       {
-               opcode=2;
-               for (int i=0; i<16; ++i)
-               {
-#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)
-                       {
-                               dr = BLEND(background.r, foreground.r, sa) & 0xFF;
-                               dg = BLEND(background.g, foreground.g, sa) & 0xFF;
-                               db = BLEND(background.b, foreground.b, sa) & 0xFF;
-                       }
-#undef BLEND
-#if BYTE_ORDER == LITTLE_ENDIAN
-                       lookup16_normal[i] = bswap_16(((db >> 3) << 11) | ((dg >> 2) << 5) | (dr >> 3));
-#else
-                       lookup16_normal[i] = ((db >> 3) << 11) | ((dg >> 2) << 5) | (dr >> 3);
-#endif
-                       da ^= 0xFF;
-               }
-               for (int i=0; i<16; ++i)
-                       lookup16_invert[i]=lookup16_normal[i^0xF];
-       } else if (surface->bpp == 32)
-       {
-               opcode=3;
-               for (int i=0; i<16; ++i)
-               {
-#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)
-                       {
-                               da = BLEND(background.a, foreground.a, sa) & 0xFF;
-                               dr = BLEND(background.r, foreground.r, sa) & 0xFF;
-                               dg = BLEND(background.g, foreground.g, sa) & 0xFF;
-                               db = BLEND(background.b, foreground.b, sa) & 0xFF;
-                       }
-#undef BLEND
-                       da ^= 0xFF;
-                       lookup32_normal[i]=db | (dg << 8) | (dr << 16) | (da << 24);;
-               }
-               for (int i=0; i<16; ++i)
-                       lookup32_invert[i]=lookup32_normal[i^0xF];
-       } else
-       {
-               eWarning("can't render to %dbpp", surface->bpp);
-               return;
-       }
-
        gRegion area(eRect(0, 0, surface->x, surface->y));
        gRegion clip = dc.getClip() & area;
 
        int buffer_stride=surface->stride;
 
-       for (unsigned int c = 0; c < clip.rects.size(); ++c)
+       bool setcolor = true;
+       std::list<int>::reverse_iterator line_offs_it(lineOffsets.rbegin());
+       std::list<int>::iterator line_chars_it(lineChars.begin());
+       int line_offs=0;
+       int line_chars=0;
+       for (glyphString::iterator i(glyphs.begin()); i != glyphs.end(); ++i, --line_chars)
        {
-               std::list<int>::reverse_iterator line_offs_it(lineOffsets.rbegin());
-               std::list<int>::iterator line_chars_it(lineChars.begin());
-               int line_offs=0;
-               int line_chars=0;
-               for (glyphString::iterator i(glyphs.begin()); i != glyphs.end(); ++i, --line_chars)
+               while(!line_chars)
+               {
+                       line_offs = *(line_offs_it++);
+                       line_chars = *(line_chars_it++);
+               }
+               if (i->flags & GS_COLORCHANGE)
                {
-                       while(!line_chars)
+                       /* don't do colorchanges in borders */
+                       if (!border)
                        {
-                               line_offs = *(line_offs_it++);
-                               line_chars = *(line_chars_it++);
+                               currentforeground = i->newcolor;
+                               setcolor = true;
                        }
+               }
+               if (setcolor)
+               {
+                       setcolor = false;
+                       if (surface->bpp == 8)
+                       {
+                               if (surface->clut.data)
+                               {
+                                       lookup8_normal=getColor(surface->clut, background, currentforeground).lookup;
 
-                       if (i->flags & GS_SOFTHYPHEN)
-                               continue;
+                                       int i;
+                                       for (i=0; i<16; ++i)
+                                               lookup8_invert[i] = lookup8_normal[i^0xF];
 
-                       if (!(i->flags & GS_INVERT))
+                                       opcode=0;
+                               } else
+                                       opcode=1;
+                       } else if (surface->bpp == 16)
                        {
-                               lookup8 = lookup8_normal;
-                               lookup16 = lookup16_normal;
-                               lookup32 = lookup32_normal;
+                               opcode=2;
+                               for (int i = 0; i != 16; ++i)
+                               {
+#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)
+                                       {
+                                               dr = BLEND(background.r, foreground.r, sa) & 0xFF;
+                                               dg = BLEND(background.g, foreground.g, sa) & 0xFF;
+                                               db = BLEND(background.b, foreground.b, sa) & 0xFF;
+                                       }
+#undef BLEND
+#if BYTE_ORDER == LITTLE_ENDIAN
+                                       lookup16_normal[i] = bswap_16(((db >> 3) << 11) | ((dg >> 2) << 5) | (dr >> 3));
+#else
+                                       lookup16_normal[i] = ((db >> 3) << 11) | ((dg >> 2) << 5) | (dr >> 3);
+#endif
+                                       da ^= 0xFF;
+                               }
+                               for (int i=0; i<16; ++i)
+                                       lookup16_invert[i]=lookup16_normal[i^0xF];
+                       } else if (surface->bpp == 32)
+                       {
+                               opcode=3;
+                               for (int i=0; i<16; ++i)
+                               {
+#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)
+                                       {
+                                               da = BLEND(background.a, currentforeground.a, sa) & 0xFF;
+                                               dr = BLEND(background.r, currentforeground.r, sa) & 0xFF;
+                                               dg = BLEND(background.g, currentforeground.g, sa) & 0xFF;
+                                               db = BLEND(background.b, currentforeground.b, sa) & 0xFF;
+                                       }
+#undef BLEND
+                                       da ^= 0xFF;
+                                       lookup32_normal[i]=db | (dg << 8) | (dr << 16) | (da << 24);;
+                               }
+                               for (int i=0; i<16; ++i)
+                                       lookup32_invert[i]=lookup32_normal[i^0xF];
                        } else
                        {
-                               lookup8 = lookup8_invert;
-                               lookup16 = lookup16_invert;
-                               lookup32 = lookup32_invert;
+                               eWarning("can't render to %dbpp", surface->bpp);
+                               return;
                        }
+               }
+               if (i->flags & GS_SOFTHYPHEN)
+                       continue;
+
+               if (!(i->flags & GS_INVERT))
+               {
+                       lookup8 = lookup8_normal;
+                       lookup16 = lookup16_normal;
+                       lookup32 = lookup32_normal;
+               } else
+               {
+                       lookup8 = lookup8_invert;
+                       lookup16 = lookup16_invert;
+                       lookup32 = lookup32_invert;
+               }
 
+               int rxbase, rybase;
+               __u8 *dbase;
+               __u8 *sbase;
+               int sxbase;
+               int sybase;
+               int pitch;
+               if (i->image)
+               {
+                       FT_BitmapGlyph glyph = border ? (FT_BitmapGlyph)i->borderimage : (FT_BitmapGlyph)i->image;
+                       if (!glyph->bitmap.buffer) continue;
+                       rxbase = i->x + glyph->left + offset.x();
+                       rybase = (doTopBottomReordering ? line_offs : i->y) - glyph->top + offset.y();
+                       sbase = glyph->bitmap.buffer;
+                       sxbase = glyph->bitmap.width;
+                       sybase = glyph->bitmap.rows;
+                       pitch = glyph->bitmap.pitch;
+               }
+               else
+               {
                        static FTC_SBit glyph_bitmap;
                        if (fontRenderClass::instance->getGlyphBitmap(&i->font->font, i->glyph_index, &glyph_bitmap))
                                continue;
-                       int rx=i->x+glyph_bitmap->left + offset.x();
-                       int ry=(doTopBottomReordering ? line_offs : i->y) - glyph_bitmap->top + offset.y();
-
-                       __u8 *d=(__u8*)(surface->data)+buffer_stride*ry+rx*surface->bypp;
-                       __u8 *s=glyph_bitmap->buffer;
-                       register int sx=glyph_bitmap->width;
-                       int sy=glyph_bitmap->height;
+                       rxbase=i->x+glyph_bitmap->left + offset.x();
+                       rybase=(doTopBottomReordering ? line_offs : i->y) - glyph_bitmap->top + offset.y();
+                       sbase=glyph_bitmap->buffer;
+                       sxbase=glyph_bitmap->width;
+                       sybase=glyph_bitmap->height;
+                       pitch = glyph_bitmap->pitch;
+               }
+               dbase = (__u8*)(surface->data)+buffer_stride*rybase+rxbase*surface->bypp;
+               for (unsigned int c = 0; c < clip.rects.size(); ++c)
+               {
+                       int rx = rxbase, ry = rybase;
+                       __u8 *d = dbase;
+                       __u8 *s = sbase;
+                       register int sx = sxbase;
+                       int sy = sybase;
                        if ((sy+ry) >= clip.rects[c].bottom())
                                sy=clip.rects[c].bottom()-ry;
                        if ((sx+rx) >= clip.rects[c].right())
@@ -833,78 +988,90 @@ void eTextPara::blit(gDC &dc, const ePoint &offset, const gRGB &background, cons
                        if (ry < clip.rects[c].top())
                        {
                                int diff=clip.rects[c].top()-ry;
-                               s+=diff*glyph_bitmap->pitch;
+                               s+=diff*pitch;
                                sy-=diff;
                                ry+=diff;
                                d+=diff*buffer_stride;
                        }
-                       if (sx>0)
+                       if ((sx>0) && (sy>0))
                        {
-                               switch(opcode) {
+                               int extra_source_stride = pitch - sx;
+                               switch (opcode)
+                               {
                                case 0: // 4bit lookup to 8bit
-                                       for (int ay=0; ay<sy; ay++)
                                        {
-                                               register __u8 *td=d;
+                                       register int extra_buffer_stride = buffer_stride - sx;
+                                       register __u8 *td=d;
+                                       for (int ay = 0; ay < sy; ay++)
+                                       {
                                                register int ax;
                                                for (ax=0; ax<sx; ax++)
                                                {
                                                        register int b=(*s++)>>4;
                                                        if(b)
-                                                               *td++=lookup8[b];
-                                                       else
-                                                               td++;
+                                                               *td=lookup8[b];
+                                                       ++td;
                                                }
-                                               s+=glyph_bitmap->pitch-sx;
-                                               d+=buffer_stride;
+                                               s += extra_source_stride;
+                                               td += extra_buffer_stride;
+                                       }
                                        }
                                        break;
                                case 1: // 8bit direct
-                                       for (int ay=0; ay<sy; ay++)
                                        {
-                                               register __u8 *td=d;
+                                       register int extra_buffer_stride = buffer_stride - sx;
+                                       register __u8 *td=d;
+                                       for (int ay = 0; ay < sy; ay++)
+                                       {
                                                register int ax;
                                                for (ax=0; ax<sx; ax++)
                                                {
                                                        register int b=*s++;
                                                        *td++^=b;
                                                }
-                                               s+=glyph_bitmap->pitch-sx;
-                                               d+=buffer_stride;
+                                               s += extra_source_stride;
+                                               td += extra_buffer_stride;
+                                       }
                                        }
                                        break;
                                case 2: // 16bit
-                                       for (int ay=0; ay<sy; ay++)
                                        {
-                                               register __u16 *td=(__u16*)d;
-                                               register int ax;
-                                               for (ax=0; ax<sx; ax++)
-                                               {
-                                                       register int b=(*s++)>>4;
-                                                       if(b)
-                                                               *td++=lookup16[b];
-                                                       else
-                                                               td++;
-                                               }
-                                               s+=glyph_bitmap->pitch-sx;
-                                               d+=buffer_stride;
+                                       int extra_buffer_stride = (buffer_stride >> 1) - sx;
+                                       register __u16 *td = (__u16*)d;
+                                       for (int ay = 0; ay != sy; ay++)
+                                       {
+                                                       register int ax;
+                                                       for (ax = 0; ax != sx; ax++)
+                                                       {
+                                                               register int b = (*s++) >> 4;
+                                                               if (b)
+                                                                       *td = lookup16[b];
+                                                               ++td;
+                                                       }
+                                                       s += extra_source_stride;
+                                                       td += extra_buffer_stride;
+                                       }
                                        }
                                        break;
                                case 3: // 32bit
-                                       for (int ay=0; ay<sy; ay++)
                                        {
-                                               register __u32 *td=(__u32*)d;
+                                       register int extra_buffer_stride = (buffer_stride >> 2) - sx;
+                                       register __u32 *td=(__u32*)d;
+                                       for (int ay = 0; ay < sy; ay++)
+                                       {
                                                register int ax;
                                                for (ax=0; ax<sx; ax++)
                                                {
                                                        register int b=(*s++)>>4;
                                                        if(b)
-                                                               *td++=lookup32[b];
-                                                       else
-                                                               td++;
+                                                               *td=lookup32[b];
+                                                       ++td;
                                                }
-                                               s+=glyph_bitmap->pitch-sx;
-                                               d+=buffer_stride;
+                                               s += extra_source_stride;
+                                               td += extra_buffer_stride;
                                        }
+                                       }
+                                       break;
                                default:
                                        break;
                                }
@@ -1002,6 +1169,11 @@ void eTextPara::clear()
        current_font = 0;
        replacement_font = 0;
 
+       for (unsigned int i = 0; i < glyphs.size(); i++)
+       {
+               if (glyphs[i].image) FT_Done_Glyph(glyphs[i].image);
+               if (glyphs[i].borderimage) FT_Done_Glyph(glyphs[i].borderimage);
+       }
        glyphs.clear();
 }
 
index 6b82183..a00a248 100644 (file)
@@ -8,6 +8,7 @@
 #include FT_CACHE_H
 #include FT_CACHE_IMAGE_H
 #include FT_CACHE_SMALL_BITMAPS_H
+#include FT_STROKER_H
 typedef FTC_ImageCache FTC_Image_Cache;
 typedef FTC_ImageTypeRec FTC_Image_Desc;
 typedef FTC_SBitCache FTC_SBit_Cache;
@@ -48,9 +49,12 @@ class fontRenderClass
        FTC_Manager                     cacheManager;                           /* the cache manager                                                     */
        FTC_Image_Cache imageCache;                                     /* the glyph image cache                                         */
        FTC_SBit_Cache   sbitsCache;                            /* the glyph small bitmaps cache         */
+       FT_Stroker stroker;
+       int strokerRadius;
 
        FTC_FaceID getFaceID(const std::string &face);
        FT_Error getGlyphBitmap(FTC_Image_Desc *font, FT_ULong glyph_index, FTC_SBit *sbit);
+       FT_Error getGlyphImage(FTC_Image_Desc *font, FT_ULong glyph_index, FT_Glyph *glyph, FT_Glyph *borderglyph, int bordersize);
        static fontRenderClass *instance;
 #else
        fontRenderClass();
@@ -81,15 +85,23 @@ public:
 #define GS_INVERT   8
 #define GS_SOFTHYPHEN 16
 #define GS_HYPHEN   32
+#define GS_COLORCHANGE 64
 #define GS_CANBREAK (GS_ISSPACE|GS_SOFTHYPHEN|GS_HYPHEN)
 
 struct pGlyph
 {
        int x, y, w;
+       unsigned long newcolor;
        ePtr<Font> font;
        FT_ULong glyph_index;
        int flags;
        eRect bbox;
+       FT_Glyph image, borderimage;
+       pGlyph()
+       {
+               image = NULL;
+               borderimage = NULL;
+       }
 };
 
 typedef std::vector<pGlyph> glyphString;
@@ -117,7 +129,7 @@ class eTextPara: public iObject
        int charCount;
        bool doTopBottomReordering;
 
-       int appendGlyph(Font *current_font, FT_Face current_face, FT_UInt glyphIndex, int flags, int rflags);
+       int appendGlyph(Font *current_font, FT_Face current_face, FT_UInt glyphIndex, int flags, int rflags, int border, bool activate_newcolor, unsigned long newcolor);
        void newLine(int flags);
        void setFont(Font *font, Font *replacement_font);
        eRect boundBox;
@@ -137,11 +149,11 @@ public:
        static void forceReplacementGlyph(int unicode) { forced_replaces.insert(unicode); }
 
        void setFont(const gFont *font);
-       int renderString(const char *string, int flags=0);
+       int renderString(const char *string, int flags=0, int border=0);
 
 
 
-       void blit(gDC &dc, const ePoint &offset, const gRGB &background, const gRGB &foreground);
+       void blit(gDC &dc, const ePoint &offset, const gRGB &background, const gRGB &foreground, bool border = false);
 
        enum
        {
@@ -193,6 +205,7 @@ public:
        FTC_Image_Desc font;
        fontRenderClass *renderer;
        FT_Error getGlyphBitmap(FT_ULong glyph_index, FTC_SBit *sbit);
+       FT_Error getGlyphImage(FT_ULong glyph_index, FT_Glyph *glyph, FT_Glyph *borderglyph, int bordersize);
        FT_Face face;
        FT_Size size;
        
index 87fb502..202cd01 100644 (file)
@@ -18,6 +18,23 @@ struct gRGB
        gRGB(unsigned long val): b(val&0xFF), g((val>>8)&0xFF), r((val>>16)&0xFF), a((val>>24)&0xFF)            // ARGB
        {
        }
+       gRGB(const char *colorstring)
+       {
+               unsigned long val = 0;
+               if (colorstring)
+               {
+                       for (int i = 0; i < 8; i++)
+                       {
+                               if (i) val <<= 4;
+                               if (!colorstring[i]) break;
+                               val |= (colorstring[i]) & 0x0f;
+                       }
+               }
+               b = val&0xFF;
+               g = (val>>8)&0xFF;
+               r = (val>>16)&0xFF;
+               a = (val>>24)&0xFF;
+       }
        gRGB(): b(0), g(0), r(0), a(0)
        {
        }
@@ -56,6 +73,22 @@ struct gRGB
        {
                return (b == c.b) && (g == c.g) && (r == c.r) && (a == c.a);
        }
+       bool operator != (const gRGB &c) const
+       {
+               return (b != c.b) || (g != c.g) || (r != c.r) || (a != c.a);
+       }
+       operator const std::string () const
+       {
+               unsigned long val = (a<<24)|(r<<16)|(g<<8)|b;
+               std::string escapecolor = "\\c";
+               escapecolor.resize(10);
+               for (int i = 9; i >= 2; i--)
+               {
+                       escapecolor[i] = 0x40 | (val & 0xf);
+                       val >>= 4;
+               }
+               return escapecolor;
+       }
 };
 
 #ifndef SWIG
index ce33171..ffb4c30 100644 (file)
@@ -331,7 +331,7 @@ void gPainter::setFont(gFont *font)
        m_rc->submit(o);
 }
 
-void gPainter::renderText(const eRect &pos, const std::string &string, int flags)
+void gPainter::renderText(const eRect &pos, const std::string &string, int flags, gRGB bordercolor, int border)
 {
        if ( m_dc->islocked() )
                return;
@@ -342,6 +342,8 @@ void gPainter::renderText(const eRect &pos, const std::string &string, int flags
        o.parm.renderText->area = pos;
        o.parm.renderText->text = string.empty()?0:strdup(string.c_str());
        o.parm.renderText->flags = flags;
+       o.parm.renderText->border = border;
+       o.parm.renderText->bordercolor = bordercolor;
        m_rc->submit(o);
 }
 
@@ -699,7 +701,7 @@ void gDC::exec(const gOpcode *o)
                int flags = o->parm.renderText->flags;
                ASSERT(m_current_font);
                para->setFont(m_current_font);
-               para->renderString(o->parm.renderText->text, (flags & gPainter::RT_WRAP) ? RS_WRAP : 0);
+               para->renderString(o->parm.renderText->text, (flags & gPainter::RT_WRAP) ? RS_WRAP : 0, o->parm.renderText->border);
                if (o->parm.renderText->text)
                        free(o->parm.renderText->text);
                if (flags & gPainter::RT_HALIGN_RIGHT)
@@ -718,8 +720,14 @@ void gDC::exec(const gOpcode *o)
                        int correction = vcentered_top - bbox.top();
                        offset += ePoint(0, correction);
                }
-               
-               para->blit(*this, offset, m_background_color_rgb, m_foreground_color_rgb);
+
+               if (o->parm.renderText->border)
+               {
+                       para->blit(*this, offset, m_background_color_rgb, o->parm.renderText->bordercolor, true);
+                       para->blit(*this, offset, o->parm.renderText->bordercolor, m_foreground_color_rgb);
+               }
+               else
+                       para->blit(*this, offset, m_background_color_rgb, m_foreground_color_rgb);
                delete o->parm.renderText;
                break;
        }
index 16f7db7..bcd2cf4 100644 (file)
@@ -89,6 +89,8 @@ struct gOpcode
                        eRect area;
                        char *text;
                        int flags;
+                       int border;
+                       gRGB bordercolor;
                } *renderText;
 
                struct prenderPara
@@ -243,7 +245,7 @@ public:
                
                RT_WRAP = 32
        };
-       void renderText(const eRect &position, const std::string &string, int flags=0);
+       void renderText(const eRect &position, const std::string &string, int flags=0, gRGB bordercolor=gRGB(), int border=0);
        
        void renderPara(eTextPara *para, ePoint offset=ePoint(0, 0));
 
index 594ed8a..00bf12d 100644 (file)
@@ -1,6 +1,9 @@
 #include <lib/gui/esubtitle.h>
+#include <lib/gui/ewidgetdesktop.h>
 #include <lib/gdi/grc.h>
+#include <lib/gdi/font.h>
 #include <lib/base/estring.h>
+#include <lib/base/nconfig.h>
 
        /*
                ok, here's much room for improvements.
@@ -25,12 +28,14 @@ eSubtitleWidget::eSubtitleWidget(eWidget *parent)
 #define startX 50
 void eSubtitleWidget::setPage(const eDVBTeletextSubtitlePage &p)
 {
+       eDVBTeletextSubtitlePage newpage = p;
        m_page = p;
+       m_page.clear();
        m_page_ok = 1;
        invalidate(m_visible_region);  // invalidate old visible regions
        m_visible_region.rects.clear();
 
-       int elements = m_page.m_elements.size();
+       unsigned int elements = newpage.m_elements.size();
        if (elements)
        {
                int startY = elements > 1
@@ -39,11 +44,78 @@ void eSubtitleWidget::setPage(const eDVBTeletextSubtitlePage &p)
                int width = size().width() - startX * 2;
                int height = size().height() - startY;
                int size_per_element = height / (elements ? elements : 1);
+               bool original_position = ePythonConfigQuery::getConfigBoolValue("config.subtitles.subtitle_original_position");
+               gRGB color;
+               bool original_colors = false;
+               switch (ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_fontcolor", 0))
+               {
+                       default:
+                       case 0: /* use original teletext colors */
+                               color = newpage.m_elements[0].m_color;
+                               original_colors = true;
+                               break;
+                       case FONTCOLOR_WHITE:
+                               color = gRGB(255, 255, 255);
+                               break;
+                       case FONTCOLOR_YELLOW:
+                               color = gRGB(255, 255, 0);
+                               break;
+                       case FONTCOLOR_GREEN:
+                               color = gRGB(0, 255, 0);
+                               break;
+                       case FONTCOLOR_CYAN:
+                               color = gRGB(0, 255, 255);
+                               break;
+                       case FONTCOLOR_BLUE:
+                               color = gRGB(0, 0, 255);
+                               break;
+                       case FONTCOLOR_MAGNETA:
+                               color = gRGB(255, 0, 255);
+                               break;
+                       case FONTCOLOR_RED:
+                               color = gRGB(255, 0, 0);
+                               break;
+                       case FONTCOLOR_BLACK:
+                               color = gRGB(0, 0, 0);
+                               break;
+               }
+               color.a = ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_opacity");
+
+               int line = newpage.m_elements[0].m_source_line;
+               int currentelement = 0;
+               m_page.m_elements.push_back(eDVBTeletextSubtitlePageElement(color, "", line));
                for (int i=0; i<elements; ++i)
                {
+                       if (!m_page.m_elements[currentelement].m_text.empty())
+                               m_page.m_elements[currentelement].m_text += " ";
+                       if (original_colors && color != newpage.m_elements[i].m_color)
+                       {
+                               color = newpage.m_elements[i].m_color;
+                               m_page.m_elements[currentelement].m_text += (std::string)color;
+                       }
+                       if (line != newpage.m_elements[i].m_source_line)
+                       {
+                               line = newpage.m_elements[i].m_source_line;
+                               m_page.m_elements.push_back(eDVBTeletextSubtitlePageElement(color, "", line));
+                               currentelement++;
+                       }
+                       m_page.m_elements[currentelement].m_text += newpage.m_elements[i].m_text;
+               }
+               for (int i=0; i<m_page.m_elements.size(); i++)
+               {
                        eRect &area = m_page.m_elements[i].m_area;
                        area.setLeft(startX);
-                       area.setTop(size_per_element * i + startY);
+                       if (!original_position)
+                       {
+                               int lowerborder = ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_position", 50);
+                               if (lowerborder == 0)
+                                       lowerborder -= 100 * getDesktop(0)->size().height()/720;
+                               else if (lowerborder == 50)
+                                       lowerborder -= 50 * getDesktop(0)->size().height()/720;
+                               area.setTop(size_per_element * i + startY - lowerborder);
+                       }
+                       else
+                               area.setTop(size_per_element * i + startY);
                        area.setWidth(width);
                        area.setHeight(size_per_element);
                        m_visible_region.rects.push_back(area);
@@ -59,8 +131,20 @@ void eSubtitleWidget::setPage(const eDVBSubtitlePage &p)
        m_dvb_page = p;
        invalidate(m_visible_region);  // invalidate old visible regions
        m_visible_region.rects.clear();
+       int line = 0;
+       bool original_position = ePythonConfigQuery::getConfigBoolValue("config.subtitles.subtitle_original_position");
        for (std::list<eDVBSubtitleRegion>::iterator it(m_dvb_page.m_regions.begin()); it != m_dvb_page.m_regions.end(); ++it)
        {
+               if (!original_position)
+               {
+                       int lines = m_dvb_page.m_regions.size();
+                       int lowerborder = ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_position", -1);
+                       if (lowerborder >= 0)
+                       {
+                               it->m_position = ePoint(it->m_position.x(), p.m_display_size.height() - (lines - line) * it->m_pixmap->size().height() - lowerborder);
+                       }
+                       line++;
+               }
                eDebug("add %d %d %d %d", it->m_position.x(), it->m_position.y(), it->m_pixmap->size().width(), it->m_pixmap->size().height());
                eDebug("disp width %d, disp height %d", p.m_display_size.width(), p.m_display_size.height());
                eRect r = eRect(it->m_position, it->m_pixmap->size());
@@ -79,7 +163,55 @@ void eSubtitleWidget::setPage(const ePangoSubtitlePage &p)
        invalidate(m_visible_region);  // invalidate old visible regions
        m_visible_region.rects.clear();
 
+       bool rewrap_enabled = ePythonConfigQuery::getConfigBoolValue("config.subtitles.subtitle_rewrap");
+       bool colourise_dialogs_enabled = ePythonConfigQuery::getConfigBoolValue("config.subtitles.colourise_dialogs");
+       bool original_position = ePythonConfigQuery::getConfigBoolValue("config.subtitles.subtitle_original_position");
+
        int elements = m_pango_page.m_elements.size();
+
+       if(rewrap_enabled | colourise_dialogs_enabled)
+       {
+               size_t ix, colourise_dialogs_current = 0;
+               std::vector<std::string> colourise_dialogs_colours;
+               std::string replacement;
+               std::string alignmentValue;
+               ePythonConfigQuery::getConfigValue("config.subtitles.subtitle_alignment", alignmentValue);
+               bool alignment_center = (alignmentValue == "center");
+
+               if(colourise_dialogs_enabled)
+               {
+                       colourise_dialogs_colours.push_back((std::string)gRGB(0xff, 0xff, 0x00));       // yellow
+                       colourise_dialogs_colours.push_back((std::string)gRGB(0x00, 0xff, 0xff));       // cyan
+                       colourise_dialogs_colours.push_back((std::string)gRGB(0xff, 0x00, 0xff));       // magenta
+                       colourise_dialogs_colours.push_back((std::string)gRGB(0x00, 0xff, 0x00));       // green
+                       colourise_dialogs_colours.push_back((std::string)gRGB(0xff, 0xaa, 0xaa));       // light red
+                       colourise_dialogs_colours.push_back((std::string)gRGB(0xaa, 0xaa, 0xff));       // light blue
+               }
+
+               for (int i=0; i<elements; ++i)
+               {
+                       std::string& line = m_pango_page.m_elements[i].m_pango_line;
+
+                       for (ix = 0; ix < line.length(); ix++)
+                       {
+                               if(rewrap_enabled && !line.compare(ix, 1, "\n"))
+                                       line.replace(ix, 1, " ");
+
+                               if(colourise_dialogs_enabled && !line.compare(ix, 2, "- "))
+                               {
+                                       /* workaround for rendering fault when colouring is enabled, rewrap is off and alignment is center */
+                                       replacement = std::string((!rewrap_enabled && alignment_center) ? "  " : "") + colourise_dialogs_colours.at(colourise_dialogs_current);
+
+                                       line.replace(ix, 2, replacement);
+                                       colourise_dialogs_current++;
+
+                                       if(colourise_dialogs_current >= colourise_dialogs_colours.size())
+                                               colourise_dialogs_current = 0;
+                               }
+                       }
+               }
+       }
+
        if (elements)
        {
                int startY = elements > 1
@@ -92,7 +224,17 @@ void eSubtitleWidget::setPage(const ePangoSubtitlePage &p)
                {
                        eRect &area = m_pango_page.m_elements[i].m_area;
                        area.setLeft(startX);
-                       area.setTop(size_per_element * i + startY);
+                       if (!original_position)
+                       {
+                               int lowerborder = ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_position", 50);
+                               if (lowerborder == 0)
+                                       lowerborder -= 100 * getDesktop(0)->size().height()/720;
+                               else if (lowerborder == 50)
+                                       lowerborder -= 50 * getDesktop(0)->size().height()/720;
+                               area.setTop(size_per_element * i + startY - lowerborder);
+                       }
+                       else
+                               area.setTop(size_per_element * i + startY);
                        area.setWidth(width);
                        area.setHeight(size_per_element);
                        m_visible_region.rects.push_back(area);
@@ -140,6 +282,22 @@ int eSubtitleWidget::event(int event, void *data, void *data2)
                getStyle(style);
                eWidget::event(event, data, data2);
 
+               std::string alignmentValue;
+               int rt_halignment_flag;
+               ePythonConfigQuery::getConfigValue("config.subtitles.subtitle_alignment", alignmentValue);
+               if (alignmentValue == "right")
+                       rt_halignment_flag = gPainter::RT_HALIGN_RIGHT;
+               else if (alignmentValue == "left")
+                       rt_halignment_flag = gPainter::RT_HALIGN_LEFT;
+               else
+                       rt_halignment_flag = gPainter::RT_HALIGN_CENTER;
+
+               int fontsize = ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_fontsize", 34) * getDesktop(0)->size().width()/1280;
+               int edgestyle = ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_edgestyle");
+               int borderwidth = (edgestyle == FONTSTYLE_UNIFORM) ? ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_edgestyle_level") : 0;
+               gRGB bordercolor = gRGB();
+               bool original_colors = false;
+
                if (m_pixmap)
                {
                        eRect r = m_pixmap_dest;
@@ -148,20 +306,135 @@ int eSubtitleWidget::event(int event, void *data, void *data2)
                } else if (m_page_ok)
                {
                        int elements = m_page.m_elements.size();
+
+                       subtitleStyles[Subtitle_TTX].font->pointSize = fontsize;
+
                        painter.setFont(subtitleStyles[Subtitle_TTX].font);
                        for (int i=0; i<elements; ++i)
                        {
                                eDVBTeletextSubtitlePageElement &element = m_page.m_elements[i];
                                eRect &area = element.m_area;
-                               eRect shadow = area;
-                               shadow.moveBy(subtitleStyles[Subtitle_TTX].shadow_offset);
-                               painter.setForegroundColor(subtitleStyles[Subtitle_TTX].shadow_color);
-                               painter.renderText(shadow, element.m_text, gPainter::RT_WRAP|gPainter::RT_VALIGN_CENTER|gPainter::RT_HALIGN_CENTER);
+                               gRGB fontcolor = (!subtitleStyles[Subtitle_TTX].have_foreground_color) ? element.m_color : subtitleStyles[Subtitle_TTX].foreground_color;
+                               int bg_r, bg_g, bg_b, bg_a;
+                               if (ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_bgopacity") < 0xFF)
+                               {
+                                       unsigned int padding = 10;
+                                       eTextPara *para = new eTextPara(area);
+                                       para->setFont(subtitleStyles[Subtitle_TTX].font);
+                                       para->renderString(element.m_text.c_str(), RS_WRAP);
+
+                                       eRect bgbox = para->getBoundBox();
+                                       int bgboxWidth = bgbox.width();
+                                       int bgboxHeight = bgbox.height();
+                                       if (alignmentValue == "left")
+                                               bgbox.setLeft(area.left() - padding - borderwidth);
+                                       else if (alignmentValue == "right")
+                                               bgbox.setLeft(area.left() + area.width() - bgboxWidth - padding - borderwidth);
+                                       else
+                                               bgbox.setLeft(area.left() + area.width() / 2 - bgboxWidth / 2 - padding - borderwidth);
+                                       bgbox.setTop(area.top() + area.height() / 2 - bgboxHeight / 2 - padding * 2 - borderwidth);
+                                       bgbox.setWidth(bgboxWidth + padding * 2 + borderwidth * 2);
+                                       bgbox.setHeight(bgboxHeight + padding * 3 + borderwidth * 2);
+
+                                       switch (ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_bgcolor", 0))
+                                       {
+                                               case BGCOLOR_WHITE:
+                                                       bg_r = 255;
+                                                       bg_g = 255;
+                                                       bg_b = 255;
+                                                       break;
+                                               case BGCOLOR_YELLOW:
+                                                       bg_r = 255;
+                                                       bg_g = 255;
+                                                       bg_b = 0;
+                                                       break;
+                                               case BGCOLOR_GREEN:
+                                                       bg_r = 0;
+                                                       bg_g = 255;
+                                                       bg_b = 0;
+                                                       break;
+                                               case BGCOLOR_CYAN:
+                                                       bg_r = 0;
+                                                       bg_g = 255;
+                                                       bg_b = 255;
+                                                       break;
+                                               case BGCOLOR_BLUE:
+                                                       bg_r = 0;
+                                                       bg_g = 0;
+                                                       bg_b = 255;
+                                                       break;
+                                               case BGCOLOR_MAGNETA:
+                                                       bg_r = 255;
+                                                       bg_g = 0;
+                                                       bg_b = 255;
+                                                       break;
+                                               case BGCOLOR_RED:
+                                                       bg_r = 255;
+                                                       bg_g = 0;
+                                                       bg_b = 0;
+                                                       break;
+                                               case BGCOLOR_BLACK:
+                                               default:
+                                                       bg_r = 0;
+                                                       bg_g = 0;
+                                                       bg_b = 0;
+                                                       break;
+                                       }
+                                       bg_a = ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_bgopacity", 0);
+
+                                       painter.setForegroundColor(gRGB(bg_r, bg_g, bg_b, bg_a));
+                                       painter.fill(bgbox);
+                               }
+
+                               int offset = ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_edgestyle_level", 3);
+                               switch(edgestyle)
+                               {
+                                       default:
+                                       case FONTSTYLE_NONE:
+                                               offset = 0;
+                                               borderwidth = 0;
+                                               break;
+                                       case FONTSTYLE_RAISED:
+                                       {
+                                               eRect shadow = area;
+                                               ePoint shadow_offset = ePoint(-offset, -offset);
+                                               shadow.moveBy(shadow_offset);
+                                               painter.setForegroundColor(subtitleStyles[Subtitle_TTX].shadow_color);
+                                               painter.renderText(shadow, element.m_text, gPainter::RT_WRAP|gPainter::RT_VALIGN_CENTER|rt_halignment_flag);
+                                       }
+                                               break;
+                                       case FONTSTYLE_DEPRESSED:
+                                       {
+                                               eRect shadow = area;
+                                               ePoint shadow_offset = ePoint(offset, offset);
+                                               shadow.moveBy(shadow_offset);
+                                               painter.setForegroundColor(subtitleStyles[Subtitle_TTX].shadow_color);
+                                               painter.renderText(shadow, element.m_text, gPainter::RT_WRAP|gPainter::RT_VALIGN_CENTER|rt_halignment_flag);
+                                       }
+                                               break;
+                                       case FONTSTYLE_UNIFORM:
+                                       {
+                                               if (borderwidth > 0)
+                                               {
+                                                       if (fontcolor.r == 0 && fontcolor.g == 0 && fontcolor.b == 0)
+                                                       {
+                                                               gRGB tmp_border_white = gRGB(255,255,255);
+                                                               bordercolor = tmp_border_white;
+                                                       }
+                                                       else if (bg_r == 0 && bg_g == 0 && bg_b == 0)
+                                                       {
+                                                               borderwidth = 0;
+                                                       }
+                                               }
+                                       }
+                                               break;
+                               }
+
                                if ( !subtitleStyles[Subtitle_TTX].have_foreground_color )
                                        painter.setForegroundColor(element.m_color);
                                else
                                        painter.setForegroundColor(subtitleStyles[Subtitle_TTX].foreground_color);
-                               painter.renderText(area, element.m_text, gPainter::RT_WRAP|gPainter::RT_VALIGN_CENTER|gPainter::RT_HALIGN_CENTER);
+                               painter.renderText(area, element.m_text, gPainter::RT_WRAP|gPainter::RT_VALIGN_CENTER|rt_halignment_flag, bordercolor, borderwidth);
                        }
                }
                else if (m_pango_page_ok)
@@ -174,34 +447,209 @@ int eSubtitleWidget::event(int event, void *data, void *data2)
                                face = Subtitle_Regular;
                                ePangoSubtitlePageElement &element = m_pango_page.m_elements[i];
                                std::string text = element.m_pango_line;
-                               std::string::size_type loc = text.find("<", 0 );
-                               if ( loc != std::string::npos )
+                               text = replace_all(text, "&apos;", "'");
+                               text = replace_all(text, "&quot;", "\"");
+                               text = replace_all(text, "&amp;", "&");
+                               text = replace_all(text, "&lt;", "<");
+                               text = replace_all(text, "&gt;", ">");
+
+                               std::string shadow_text = text;
+                               if (edgestyle == FONTSTYLE_RAISED || edgestyle == FONTSTYLE_DEPRESSED)
                                {
-                                       switch (char(text.at(1)))
-                                       {
-                                       case 'i':
+                                       shadow_text = replace_all(shadow_text, "</u>", "");
+                                       shadow_text = replace_all(shadow_text, "</i>", "");
+                                       shadow_text = replace_all(shadow_text, "</b>", "");
+                                       shadow_text = replace_all(shadow_text, "<u>", "");
+                                       shadow_text = replace_all(shadow_text, "<i>", "");
+                                       shadow_text = replace_all(shadow_text, "<b>", "");
+                               }
+
+                               if (ePythonConfigQuery::getConfigBoolValue("config.subtitles.pango_subtitle_fontswitch"))
+                               {
+                                       if (text.find("<i>") != std::string::npos || text.find("</i>") != std::string::npos)
                                                face = Subtitle_Italic;
-                                               break;
-                                       case 'b':
+                                       else if (text.find("<b>") != std::string::npos || text.find("</b>") != std::string::npos)
                                                face = Subtitle_Bold;
-                                               break;
+                               }
+                               int subtitleColors = ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_fontcolor", 1);
+                               if (!subtitleColors)
+                                       {
+                                               text = replace_all(text, "<i>", gRGB(255,255,0));
+                                               text = replace_all(text, "<b>", gRGB(0,255,255));
+                                               text = replace_all(text, "<u>", (std::string) gRGB(0,255,0));
+                                               text = replace_all(text, "</i>", (std::string) gRGB(255,255,255));
+                                               text = replace_all(text, "</b>", (std::string) gRGB(255,255,255));
+                                               text = replace_all(text, "</u>", (std::string) gRGB(255,255,255));
                                        }
-                                       text = text.substr(3, text.length()-7);
+                               else
+                               {
+                                       text = replace_all(text, "</u>", "");
+                                       text = replace_all(text, "</i>", "");
+                                       text = replace_all(text, "</b>", "");
+                                       text = replace_all(text, "<u>", "");
+                                       text = replace_all(text, "<i>", "");
+                                       text = replace_all(text, "<b>", "");
                                }
-                               text = replace_all(text, "&apos;", "'");
-                               text = replace_all(text, "&quot;", "\"");
-                               text = replace_all(text, "&amp;", "&");
+
+                               gRGB fontcolor = (!subtitleStyles[face].have_foreground_color) ? element.m_color : subtitleStyles[face].foreground_color;
+                               switch (subtitleColors)
+                               {
+                                       default:
+                                       case 0: /* use original colors */
+                                               original_colors = true;
+                                               break;
+                                       case FONTCOLOR_WHITE:
+                                               fontcolor = gRGB(255, 255, 255);
+                                               break;
+                                       case FONTCOLOR_YELLOW:
+                                               fontcolor = gRGB(255, 255, 0);
+                                               break;
+                                       case FONTCOLOR_GREEN:
+                                               fontcolor = gRGB(0, 255, 0);
+                                               break;
+                                       case FONTCOLOR_CYAN:
+                                               fontcolor = gRGB(0, 255, 255);
+                                               break;
+                                       case FONTCOLOR_BLUE:
+                                               fontcolor = gRGB(0, 0, 255);
+                                               break;
+                                       case FONTCOLOR_MAGNETA:
+                                               fontcolor = gRGB(255, 0, 255);
+                                               break;
+                                       case FONTCOLOR_RED:
+                                               fontcolor = gRGB(255, 0, 0);
+                                               break;
+                                       case FONTCOLOR_BLACK:
+                                               fontcolor = gRGB(0, 0, 0);
+                                               break;
+                               }
+                               fontcolor.a = ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_opacity");
+                               if (!original_colors)
+                                       text = (std::string)fontcolor + text;
+
+                               subtitleStyles[face].font->pointSize = fontsize;
                                painter.setFont(subtitleStyles[face].font);
                                eRect &area = element.m_area;
-                               eRect shadow = area;
-                               shadow.moveBy(subtitleStyles[face].shadow_offset);
-                               painter.setForegroundColor(subtitleStyles[face].shadow_color);
-                               painter.renderText(shadow, text, gPainter::RT_WRAP|gPainter::RT_VALIGN_CENTER|gPainter::RT_HALIGN_CENTER);
+                               int bg_r, bg_g, bg_b, bg_a;
+                               if (ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_bgopacity") < 0xFF)
+                               {
+                                       unsigned int padding = 10;
+                                       eTextPara *para = new eTextPara(area);
+                                       para->setFont(subtitleStyles[face].font);
+                                       para->renderString(text.c_str(), RS_WRAP);
+
+                                       eRect bgbox = para->getBoundBox();
+                                       int bgboxWidth = bgbox.width();
+                                       int bgboxHeight = bgbox.height();
+                                       if (alignmentValue == "left")
+                                               bgbox.setLeft(area.left() - padding - borderwidth);
+                                       else if (alignmentValue == "right")
+                                               bgbox.setLeft(area.left() + area.width() - bgboxWidth - padding - borderwidth);
+                                       else
+                                               bgbox.setLeft(area.left() + area.width() / 2 - bgboxWidth / 2 - padding - borderwidth);
+                                       bgbox.setTop(area.top() + area.height() / 2 - bgboxHeight / 2 - padding * 2 - borderwidth);
+                                       bgbox.setWidth(bgboxWidth + padding * 2 + borderwidth * 2);
+                                       bgbox.setHeight(bgboxHeight + padding * 3 + borderwidth * 2);
+
+                                       switch (ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_bgcolor", 0))
+                                       {
+                                               case BGCOLOR_WHITE:
+                                                       bg_r = 255;
+                                                       bg_g = 255;
+                                                       bg_b = 255;
+                                                       break;
+                                               case BGCOLOR_YELLOW:
+                                                       bg_r = 255;
+                                                       bg_g = 255;
+                                                       bg_b = 0;
+                                                       break;
+                                               case BGCOLOR_GREEN:
+                                                       bg_r = 0;
+                                                       bg_g = 255;
+                                                       bg_b = 0;
+                                                       break;
+                                               case BGCOLOR_CYAN:
+                                                       bg_r = 0;
+                                                       bg_g = 255;
+                                                       bg_b = 255;
+                                                       break;
+                                               case BGCOLOR_BLUE:
+                                                       bg_r = 0;
+                                                       bg_g = 0;
+                                                       bg_b = 255;
+                                                       break;
+                                               case BGCOLOR_MAGNETA:
+                                                       bg_r = 255;
+                                                       bg_g = 0;
+                                                       bg_b = 255;
+                                                       break;
+                                               case BGCOLOR_RED:
+                                                       bg_r = 255;
+                                                       bg_g = 0;
+                                                       bg_b = 0;
+                                                       break;
+                                               case BGCOLOR_BLACK:
+                                               default:
+                                                       bg_r = 0;
+                                                       bg_g = 0;
+                                                       bg_b = 0;
+                                                       break;
+                                       }
+                                       bg_a = ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_bgopacity", 0);
+
+                                       painter.setForegroundColor(gRGB(bg_r, bg_g, bg_b, bg_a));
+                                       painter.fill(bgbox);
+                               }
+
+                               int offset = ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_edgestyle_level", 3);
+                               switch(edgestyle)
+                               {
+                                       default:
+                                       case FONTSTYLE_NONE:
+                                               offset = 0;
+                                               borderwidth = 0;
+                                               break;
+                                       case FONTSTYLE_RAISED:
+                                       {
+                                               eRect shadow = area;
+                                               ePoint shadow_offset = ePoint(-offset, -offset);
+                                               shadow.moveBy(shadow_offset);
+                                               painter.setForegroundColor(subtitleStyles[face].shadow_color);
+                                               painter.renderText(shadow, shadow_text, gPainter::RT_WRAP|gPainter::RT_VALIGN_CENTER|rt_halignment_flag);
+                                       }
+                                               break;
+                                       case FONTSTYLE_DEPRESSED:
+                                       {
+                                               eRect shadow = area;
+                                               ePoint shadow_offset = ePoint(offset, offset);
+                                               shadow.moveBy(shadow_offset);
+                                               painter.setForegroundColor(subtitleStyles[face].shadow_color);
+                                               painter.renderText(shadow, shadow_text, gPainter::RT_WRAP|gPainter::RT_VALIGN_CENTER|rt_halignment_flag);
+                                       }
+                                               break;
+                                       case FONTSTYLE_UNIFORM:
+                                       {
+                                               if (borderwidth > 0)
+                                               {
+                                                       if (fontcolor.r == 0 && fontcolor.g == 0 && fontcolor.b == 0)
+                                                       {
+                                                               gRGB tmp_border_white = gRGB(255,255,255);
+                                                               bordercolor = tmp_border_white;
+                                                       }
+                                                       else if (bg_r == 0 && bg_g == 0 && bg_b == 0)
+                                                       {
+                                                               borderwidth = 0;
+                                                       }
+                                               }
+                                       }
+                                               break;
+                               }
+
                                if ( !subtitleStyles[face].have_foreground_color && element.m_have_color )
                                        painter.setForegroundColor(element.m_color);
                                else
                                        painter.setForegroundColor(subtitleStyles[face].foreground_color);
-                               painter.renderText(area, text, gPainter::RT_WRAP|gPainter::RT_VALIGN_CENTER|gPainter::RT_HALIGN_CENTER);
+                               painter.renderText(area, text, gPainter::RT_WRAP|gPainter::RT_VALIGN_CENTER|rt_halignment_flag, bordercolor, borderwidth);
                        }
                }
                else if (m_dvb_page_ok)
index cdad728..7f97a90 100644 (file)
@@ -5,6 +5,14 @@
 #include <lib/dvb/teletext.h>
 #include <lib/dvb/subtitle.h>
 
+enum
+{
+       FONTSTYLE_NONE = 0,
+       FONTSTYLE_RAISED,
+       FONTSTYLE_DEPRESSED,
+       FONTSTYLE_UNIFORM,
+};
+
 struct ePangoSubtitlePageElement
 {
        gRGB m_color;
index b98be60..fc7ae83 100644 (file)
@@ -142,6 +142,76 @@ def InitUsageConfig():
 
        config.seek.speeds_backward.addNotifier(updateEnterBackward, immediate_feedback = False)
 
+       config.subtitles = ConfigSubsection()
+       config.subtitles.subtitle_fontcolor = ConfigSelection(default = "0", choices = [
+               ("0", _("default")),
+               ("1", _("white")),
+               ("2", _("yellow")),
+               ("3", _("green")),
+               ("4", _("cyan")),
+               ("5", _("blue")),
+               ("6", _("magneta")),
+               ("7", _("red")),
+               ("8", _("black")) ])
+       config.subtitles.subtitle_fontsize  = ConfigSelection(choices = ["%d" % x for x in range(16,101) if not x % 2], default = "20")
+       config.subtitles.subtitle_bgcolor = ConfigSelection(default = "0", choices = [
+               ("0", _("black")),
+               ("1", _("red")),
+               ("2", _("magneta")),
+               ("3", _("blue")),
+               ("4", _("cyan")),
+               ("5", _("green")),
+               ("6", _("yellow")),
+               ("7", _("white"))])
+       config.subtitles.subtitle_bgopacity = ConfigSelection(default = "225", choices = [
+               ("0", _("No transparency")),
+               ("25", "10%"),
+               ("50", "20%"),
+               ("75", "30%"),
+               ("100", "40%"),
+               ("125", "50%"),
+               ("150", "60%"),
+               ("175", "70%"),
+               ("200", "80%"),
+               ("225", "90%"),
+               ("255", _("Full transparency"))])
+       config.subtitles.subtitle_edgestyle = ConfigSelection(default = "2", choices = [
+               ("0", "None"),
+               ("1", "Raised"),
+               ("2", "Depressed"),
+               ("3", "Uniform")])
+       config.subtitles.subtitle_edgestyle_level = ConfigSelection(choices = ["0", "1", "2", "3", "4", "5"], default = "3")
+       config.subtitles.subtitle_opacity = ConfigSelection(default = "0", choices = [
+               ("0", _("No transparency")),
+               ("75", "25%"),
+               ("150", "50%")])
+       config.subtitles.subtitle_original_position = ConfigYesNo(default = True)
+       config.subtitles.subtitle_alignment = ConfigSelection(choices = [("left", _("left")), ("center", _("center")), ("right", _("right"))], default = "center")
+       config.subtitles.subtitle_position = ConfigSelection( choices = ["0", "50", "100", "150", "200", "250", "300", "350", "400", "450", "500", "550", "600"], default = "50")
+
+       config.subtitles.dvb_subtitles_centered = ConfigYesNo(default = False)
+
+       subtitle_delay_choicelist = []
+       for i in range(-900000, 1845000, 45000):
+               if i == 0:
+                       subtitle_delay_choicelist.append(("0", _("No delay")))
+               else:
+                       subtitle_delay_choicelist.append((str(i), "%2.1f sec" % (i / 90000.)))
+       config.subtitles.subtitle_noPTSrecordingdelay = ConfigSelection(default = "315000", choices = subtitle_delay_choicelist)
+       config.subtitles.subtitle_bad_timing_delay = ConfigSelection(default = "0", choices = subtitle_delay_choicelist)
+       config.subtitles.subtitle_rewrap = ConfigYesNo(default = False)
+       config.subtitles.colourise_dialogs = ConfigYesNo(default = False)
+       config.subtitles.pango_subtitle_fontswitch = ConfigYesNo(default = True)
+       config.subtitles.pango_subtitles_delay = ConfigSelection(default = "0", choices = subtitle_delay_choicelist)
+       config.subtitles.pango_subtitles_fps = ConfigSelection(default = "1", choices = [
+               ("1", _("Original")),
+               ("23976", _("23.976")),
+               ("24000", _("24")),
+               ("25000", _("25")),
+               ("29970", _("29.97")),
+               ("30000", _("30"))])
+       config.subtitles.pango_autoturnon = ConfigYesNo(default = True)
+
 def updateChoices(sel, choices):
        if choices:
                defval = None
index b251ef7..537aab4 100755 (executable)
@@ -1,15 +1,17 @@
 from Screen import Screen
+from Screens.Setup import getConfigMenuItem
 from Components.ServiceEventTracker import ServiceEventTracker
 from Components.ActionMap import NumberActionMap
 from Components.ConfigList import ConfigListScreen
 from Components.ChoiceList import ChoiceList, ChoiceEntryComponent
 from Components.config import config, ConfigSubsection, getConfigListEntry, ConfigNothing, ConfigSelection, ConfigOnOff
+from Components.Label import Label
 from Components.MultiContent import MultiContentEntryText
 from Components.Sources.List import List
 from Components.Sources.Boolean import Boolean
 from Components.SystemInfo import SystemInfo
 
-from enigma import iPlayableService
+from enigma import iPlayableService, eTimer, eSize
 
 from Tools.ISO639 import LanguageCodes
 from Tools.BoundFunction import boundFunction
@@ -129,9 +131,13 @@ class AudioSelection(Screen, ConfigListScreen):
                elif self.settings.menupage.getValue() == PAGE_SUBTITLES:
                        self.setTitle(_("Subtitle selection"))
                        conflist.append(('',))
-                       conflist.append(('',))
                        self["key_red"].setBoolean(False)
-                       self["key_green"].setBoolean(False)
+                       if self.infobar.selected_subtitle:
+                               conflist.append(getConfigListEntry(_("Subtitle Quickmenu"), ConfigNothing()))
+                               self["key_green"].setBoolean(True)
+                       else:
+                               conflist.append(('',))
+                               self["key_green"].setBoolean(False)
 
                        if self.subtitlesEnabled():
                                sel = self.infobar.selected_subtitle
@@ -249,7 +255,10 @@ class AudioSelection(Screen, ConfigListScreen):
        def keyRight(self, config = False):
                if config or self.focus == FOCUS_CONFIG:
                        if self["config"].getCurrentIndex() < 3:
-                               ConfigListScreen.keyRight(self)
+                               if self["config"].getCurrentIndex() == 1 and self.settings.menupage.getValue() == PAGE_SUBTITLES and self.infobar.selected_subtitle:
+                                       self.session.open(QuickSubtitlesConfigMenu, self.infobar)
+                               else:
+                                       ConfigListScreen.keyRight(self)
                        elif hasattr(self, "plugincallfunc"):
                                self.plugincallfunc()
                if self.focus == FOCUS_STREAMS and self["streams"].count() and config == False:
@@ -329,3 +338,106 @@ class SubtitleSelection(AudioSelection):
        def __init__(self, session, infobar=None):
                AudioSelection.__init__(self, session, infobar, page=PAGE_SUBTITLES)
                self.skinName = ["AudioSelection"]
+
+class QuickSubtitlesConfigMenu(ConfigListScreen, Screen):
+       skin = """
+       <screen position="50,80" size="480,425" title="Subtitle settings">
+               <widget name="config" position="5,5" size="470,395" scrollbarMode="showOnDemand" />
+               <widget name="videofps" position="5,405" size="470,25" zPosition="10" font="Regular;20" backgroundColor="#25062748" transparent="1" />
+       </screen>"""
+
+       def __init__(self, session, infobar):
+               Screen.__init__(self, session)
+               self.skin = QuickSubtitlesConfigMenu.skin
+               self.infobar = infobar or self.session.infobar
+
+               self.wait = eTimer()
+               self.wait.timeout.get().append(self.resyncSubtitles)
+
+               self["videofps"] = Label("")
+
+               sub = self.infobar.selected_subtitle
+               if sub[0] == 0:  # dvb
+                       menu = [
+                               getConfigMenuItem("config.subtitles.subtitle_fontcolor"),
+                               getConfigMenuItem("config.subtitles.dvb_subtitles_centered"),
+                               getConfigMenuItem("config.subtitles.subtitle_bgopacity"),
+                               getConfigMenuItem("config.subtitles.subtitle_original_position"),
+                               getConfigMenuItem("config.subtitles.subtitle_position"),
+                               getConfigMenuItem("config.subtitles.subtitle_bad_timing_delay"),
+                               getConfigMenuItem("config.subtitles.subtitle_noPTSrecordingdelay"),
+                       ]
+               elif sub[0] == 1: # teletext
+                       menu = [
+                               getConfigMenuItem("config.subtitles.subtitle_fontcolor"),
+                               getConfigMenuItem("config.subtitles.subtitle_fontsize"),
+                               getConfigMenuItem("config.subtitles.subtitle_opacity"),
+                               getConfigMenuItem("config.subtitles.subtitle_bgcolor"),
+                               getConfigMenuItem("config.subtitles.subtitle_bgopacity"),
+                               getConfigMenuItem("config.subtitles.subtitle_edgestyle"),
+                               getConfigMenuItem("config.subtitles.subtitle_edgestyle_level"),
+                               getConfigMenuItem("config.subtitles.subtitle_original_position"),
+                               getConfigMenuItem("config.subtitles.subtitle_alignment"),
+                               getConfigMenuItem("config.subtitles.subtitle_position"),
+                               getConfigMenuItem("config.subtitles.subtitle_bad_timing_delay"),
+                               getConfigMenuItem("config.subtitles.subtitle_noPTSrecordingdelay"),
+                       ]
+               else:           # pango
+                       menu = [
+                               getConfigMenuItem("config.subtitles.subtitle_fontcolor"),
+                               getConfigMenuItem("config.subtitles.subtitle_fontsize"),
+                               getConfigMenuItem("config.subtitles.subtitle_opacity"),
+                               getConfigMenuItem("config.subtitles.subtitle_bgcolor"),
+                               getConfigMenuItem("config.subtitles.subtitle_bgopacity"),
+                               getConfigMenuItem("config.subtitles.subtitle_edgestyle"),
+                               getConfigMenuItem("config.subtitles.subtitle_edgestyle_level"),
+                               getConfigMenuItem("config.subtitles.subtitle_original_position"),
+                               getConfigMenuItem("config.subtitles.subtitle_alignment"),
+                               getConfigMenuItem("config.subtitles.subtitle_position"),
+                               getConfigMenuItem("config.subtitles.pango_subtitle_fontswitch"),
+                               getConfigMenuItem("config.subtitles.colourise_dialogs"),
+                               getConfigMenuItem("config.subtitles.subtitle_rewrap"),
+                               getConfigMenuItem("config.subtitles.pango_subtitles_delay"),
+                               getConfigMenuItem("config.subtitles.pango_subtitles_fps"),
+                       ]
+                       self["videofps"].setText(_("Video: %s fps") % (self.getFps().rstrip(".000")))
+
+               ConfigListScreen.__init__(self, menu, self.session, on_change = self.changedEntry)
+
+               self["actions"] = NumberActionMap(["SetupActions"],
+               {
+                       "cancel": self.cancel,
+                       "ok": self.ok,
+               },-2)
+
+               self.onLayoutFinish.append(self.layoutFinished)
+
+       def layoutFinished(self):
+               if not self["videofps"].text:
+                       self.instance.resize(eSize(self.instance.size().width(), self["config"].l.getItemSize().height()*len(self["config"].getList()) + 10))
+
+       def changedEntry(self):
+               if self["config"].getCurrent() in [getConfigMenuItem("config.subtitles.pango_subtitles_delay"),getConfigMenuItem("config.subtitles.pango_subtitles_fps")]:
+                       self.wait.start(500, True)
+
+       def resyncSubtitles(self):
+               self.infobar.setSeekState(self.infobar.SEEK_STATE_PAUSE)
+               self.infobar.setSeekState(self.infobar.SEEK_STATE_PLAY)
+
+       def getFps(self):
+               from enigma import iServiceInformation
+               service = self.session.nav.getCurrentService()
+               info = service and service.info()
+               if not info:
+                       return ""
+               fps = info.getInfo(iServiceInformation.sFrameRate)
+               if fps > 0:
+                       return "%6.3f" % (fps/1000.)
+               return ""
+
+       def cancel(self):
+               self.close()
+
+       def ok(self):
+               config.subtitles.save()
+               self.close()
index c36eb9c..186b07a 100755 (executable)
@@ -2254,8 +2254,7 @@ class InfoBarSubtitleSupport(object):
                        self.__selected_subtitle = None
 
        def __updatedInfo(self):
-               if not self.cached_subtitle_checked:
-                       self.cached_subtitle_checked = True
+               if not self.__selected_subtitle:
                        subtitle = self.getCurrentServiceSubtitle()
                        self.setSelectedSubtitle(subtitle and subtitle.getCachedSubtitle())
                        if self.__selected_subtitle:
@@ -2276,7 +2275,7 @@ class InfoBarSubtitleSupport(object):
                else:
                        if subtitle:
                                subtitle.disableSubtitles(self.subtitle_window.instance)
-                       self.__selected_subtitle = False
+                       self.__selected_subtitle = None
                        self.__subtitles_enabled = False
                        self.subtitle_window.hide()
 
index 2e83327..7e4f03a 100755 (executable)
@@ -19,6 +19,12 @@ except:
 setupdom = xml.etree.cElementTree.parse(setupfile)
 setupfile.close()
 
+def getConfigMenuItem(configElement):
+       for item in setupdom.getroot().findall('./setup/item/.'):
+               if item.text == configElement:
+                       return _(item.attrib["text"]), eval(configElement)
+       return "", None
+
 class SetupError(Exception):
     def __init__(self, message):
         self.msg = message
index 07827ec..3f580fe 100755 (executable)
@@ -3051,11 +3051,30 @@ void eDVBServicePlay::newSubtitlePage(const eDVBTeletextSubtitlePage &page)
 {
        if (m_subtitle_widget)
        {
+               int subtitledelay = 0;
                pts_t pos = 0;
                if (m_decoder)
                        m_decoder->getPTS(0, pos);
+               if (m_is_pvr || m_timeshift_enabled)
+               {
+                       eDebug("Subtitle in recording/timeshift");
+                       subtitledelay = ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_noPTSrecordingdelay", 315000);
+               }
+               else
+               {
+                       /* check the setting for subtitle delay in live playback, either with pos, or without pos */
+                       subtitledelay = ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_bad_timing_delay", 0);
+               }
+
                eDebug("got new subtitle page %lld %lld %d", pos, page.m_pts, page.m_have_pts);
-               m_subtitle_pages.push_back(page);
+               eDVBTeletextSubtitlePage tmppage = page;
+               tmppage.m_have_pts = true;
+
+               if (abs(tmppage.m_pts - pos) > 20*90000)
+                       tmppage.m_pts = pos; // fix abnormal pos diffs
+
+               tmppage.m_pts += subtitledelay;
+               m_subtitle_pages.push_back(tmppage);
                checkSubtitleTiming();
        }
 }
@@ -3139,7 +3158,29 @@ void eDVBServicePlay::newDVBSubtitlePage(const eDVBSubtitlePage &p)
                if (m_decoder)
                        m_decoder->getPTS(0, pos);
                eDebug("got new subtitle page %lld %lld", pos, p.m_show_time);
-               m_dvb_subtitle_pages.push_back(p);
+               if ( abs(pos-p.m_show_time)>1800000 && (m_is_pvr || m_timeshift_enabled))
+               {
+                       eDebug("[eDVBServicePlay] Subtitle without PTS and recording");
+                       int subtitledelay = ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_noPTSrecordingdelay", 315000);
+
+                       eDVBSubtitlePage tmppage;
+                       tmppage = p;
+                       tmppage.m_show_time = pos + subtitledelay;
+                       m_dvb_subtitle_pages.push_back(tmppage);
+               }
+               else
+               {
+                       int subtitledelay = ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_bad_timing_delay", 0);
+                       if (subtitledelay != 0)
+                       {
+                               eDVBSubtitlePage tmppage;
+                               tmppage = p;
+                               tmppage.m_show_time += subtitledelay;
+                               m_dvb_subtitle_pages.push_back(tmppage);
+                       }
+                       else
+                               m_dvb_subtitle_pages.push_back(p);
+               }
                checkSubtitleTiming();
        }
 }
index 0437f59..fc23d00 100755 (executable)
@@ -1593,7 +1593,7 @@ void eServiceMP3::gstBusCall(GstBus *bus, GstMessage *msg)
                                m_subtitleStreams.push_back(subs);
                                g_free (g_lang);
                        }
-                       m_event((iPlayableService*)this, evUpdatedEventInfo);
+                       m_event((iPlayableService*)this, evUpdatedInfo);
 
                        if ( m_errorInfo.missing_codec != "" )
                        {
@@ -1913,6 +1913,13 @@ void eServiceMP3::pullSubtitle(GstBuffer *buffer)
                {
                        if ( subType < stVOB )
                        {
+                               int delay = ePythonConfigQuery::getConfigIntValue("config.subtitles.pango_subtitles_delay");
+                               int subtitle_fps = ePythonConfigQuery::getConfigIntValue("config.subtitles.pango_subtitles_fps");
+
+                               double convert_fps = 1.0;
+                               if (subtitle_fps > 1 && m_framerate > 0)
+                                       convert_fps = subtitle_fps / (double)m_framerate;
+
                                unsigned char line[len+1];
                                SubtitlePage page;
 #if GST_VERSION_MAJOR < 1
@@ -1927,7 +1934,7 @@ void eServiceMP3::pullSubtitle(GstBuffer *buffer)
                                gRGB rgbcol(0xD0,0xD0,0xD0);
                                page.type = SubtitlePage::Pango;
                                page.pango_page.m_elements.push_back(ePangoSubtitlePageElement(rgbcol, (const char*)line));
-                               page.pango_page.m_show_pts = buf_pos / 11111LL;
+                               page.pango_page.m_show_pts = buf_pos / 11111LL + convert_fps + delay;
                                page.pango_page.m_timeout = duration_ns / 1000000;
                                m_subtitle_pages.push_back(page);
                                m_subtitle_sync_timer->start(1, true);
@@ -2110,6 +2117,26 @@ RESULT eServiceMP3::disableSubtitles(eWidget *parent)
 PyObject *eServiceMP3::getCachedSubtitle()
 {
 //     eDebug("eServiceMP3::getCachedSubtitle");
+       bool autoturnon = ePythonConfigQuery::getConfigBoolValue("config.subtitles.pango_autoturnon", true);
+       if (!autoturnon)
+               Py_RETURN_NONE;
+
+       if (!m_subtitleStreams.empty())
+       {
+               int index = 0;
+               if (m_currentSubtitleStream >= 0 && m_currentSubtitleStream < (int)m_subtitleStreams.size())
+               {
+                       index = m_currentSubtitleStream;
+               }
+               ePyObject tuple = PyTuple_New(5);
+               PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(2));
+               PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(index));
+               PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(int(m_subtitleStreams[index].type)));
+               PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(0));
+               PyTuple_SET_ITEM(tuple, 4, PyString_FromString(m_subtitleStreams[index].language_code.c_str()));
+               return tuple;
+       }
+
        Py_RETURN_NONE;
 }