summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsmlee <smlee@dev3>2016-03-17 10:33:22 (GMT)
committersmlee <smlee@dev3>2016-03-18 03:14:29 (GMT)
commit059fdda03072b1be0d67a525f20284df68a84a3c (patch)
treeaba33b94c87b1f0d25096b96195a1344fb8f4657
parent927892fd15874451b09b5d5c280d5f4b335ea383 (diff)
Support customized subtitles.
- font color, size, opacity, edge style, - background color, opacity, - subtitle position, align, etc. - add quick menu for subtitle
-rwxr-xr-xdata/menu.xml1
-rwxr-xr-xdata/setup.xml21
-rw-r--r--lib/base/nconfig.cpp22
-rw-r--r--lib/base/nconfig.h2
-rw-r--r--lib/dvb/subtitle.cpp77
-rw-r--r--lib/dvb/subtitle.h22
-rw-r--r--lib/gdi/font.cpp460
-rw-r--r--lib/gdi/font.h19
-rw-r--r--lib/gdi/gpixmap.h33
-rw-r--r--lib/gdi/grc.cpp16
-rw-r--r--lib/gdi/grc.h4
-rw-r--r--lib/gui/esubtitle.cpp498
-rw-r--r--lib/gui/esubtitle.h8
-rw-r--r--lib/python/Components/UsageConfig.py70
-rwxr-xr-xlib/python/Screens/AudioSelection.py120
-rwxr-xr-xlib/python/Screens/InfoBarGenerics.py5
-rwxr-xr-xlib/python/Screens/Setup.py6
-rwxr-xr-xlib/service/servicedvb.cpp45
-rwxr-xr-xlib/service/servicemp3.cpp31
19 files changed, 1270 insertions, 190 deletions
diff --git a/data/menu.xml b/data/menu.xml
index 03e582c..5c8cbb2 100755
--- a/data/menu.xml
+++ b/data/menu.xml
@@ -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>
diff --git a/data/setup.xml b/data/setup.xml
index 442fe13..6100565 100755
--- a/data/setup.xml
+++ b/data/setup.xml
@@ -62,6 +62,27 @@
<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>
diff --git a/lib/base/nconfig.cpp b/lib/base/nconfig.cpp
index 106558a..8207b71 100644
--- a/lib/base/nconfig.cpp
+++ b/lib/base/nconfig.cpp
@@ -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;
+}
diff --git a/lib/base/nconfig.h b/lib/base/nconfig.h
index 3667439..4674af6 100644
--- a/lib/base/nconfig.h
+++ b/lib/base/nconfig.h
@@ -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
};
diff --git a/lib/dvb/subtitle.cpp b/lib/dvb/subtitle.cpp
index 06bb266..78b1874 100644
--- a/lib/dvb/subtitle.cpp
+++ b/lib/dvb/subtitle.cpp
@@ -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);
}
diff --git a/lib/dvb/subtitle.h b/lib/dvb/subtitle.h
index c7a7e96..e34fd27 100644
--- a/lib/dvb/subtitle.h
+++ b/lib/dvb/subtitle.h
@@ -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;
diff --git a/lib/gdi/font.cpp b/lib/gdi/font.cpp
index 9e17547..3a32e2a 100644
--- a/lib/gdi/font.cpp
+++ b/lib/gdi/font.cpp
@@ -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();
}
diff --git a/lib/gdi/font.h b/lib/gdi/font.h
index 6b82183..a00a248 100644
--- a/lib/gdi/font.h
+++ b/lib/gdi/font.h
@@ -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;
diff --git a/lib/gdi/gpixmap.h b/lib/gdi/gpixmap.h
index 87fb502..202cd01 100644
--- a/lib/gdi/gpixmap.h
+++ b/lib/gdi/gpixmap.h
@@ -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
diff --git a/lib/gdi/grc.cpp b/lib/gdi/grc.cpp
index ce33171..ffb4c30 100644
--- a/lib/gdi/grc.cpp
+++ b/lib/gdi/grc.cpp
@@ -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;
}
diff --git a/lib/gdi/grc.h b/lib/gdi/grc.h
index 16f7db7..bcd2cf4 100644
--- a/lib/gdi/grc.h
+++ b/lib/gdi/grc.h
@@ -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));
diff --git a/lib/gui/esubtitle.cpp b/lib/gui/esubtitle.cpp
index 594ed8a..00bf12d 100644
--- a/lib/gui/esubtitle.cpp
+++ b/lib/gui/esubtitle.cpp
@@ -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)
diff --git a/lib/gui/esubtitle.h b/lib/gui/esubtitle.h
index cdad728..7f97a90 100644
--- a/lib/gui/esubtitle.h
+++ b/lib/gui/esubtitle.h
@@ -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;
diff --git a/lib/python/Components/UsageConfig.py b/lib/python/Components/UsageConfig.py
index b98be60..fc7ae83 100644
--- a/lib/python/Components/UsageConfig.py
+++ b/lib/python/Components/UsageConfig.py
@@ -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
diff --git a/lib/python/Screens/AudioSelection.py b/lib/python/Screens/AudioSelection.py
index b251ef7..537aab4 100755
--- a/lib/python/Screens/AudioSelection.py
+++ b/lib/python/Screens/AudioSelection.py
@@ -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()
diff --git a/lib/python/Screens/InfoBarGenerics.py b/lib/python/Screens/InfoBarGenerics.py
index c36eb9c..186b07a 100755
--- a/lib/python/Screens/InfoBarGenerics.py
+++ b/lib/python/Screens/InfoBarGenerics.py
@@ -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()
diff --git a/lib/python/Screens/Setup.py b/lib/python/Screens/Setup.py
index 2e83327..7e4f03a 100755
--- a/lib/python/Screens/Setup.py
+++ b/lib/python/Screens/Setup.py
@@ -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
diff --git a/lib/service/servicedvb.cpp b/lib/service/servicedvb.cpp
index 07827ec..3f580fe 100755
--- a/lib/service/servicedvb.cpp
+++ b/lib/service/servicedvb.cpp
@@ -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();
}
}
diff --git a/lib/service/servicemp3.cpp b/lib/service/servicemp3.cpp
index 0437f59..fc23d00 100755
--- a/lib/service/servicemp3.cpp
+++ b/lib/service/servicemp3.cpp
@@ -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;
}