2007-10-12 14:52:30 +02:00
/*
* dvbsubtitle . c : DVB subtitles
*
* See the main source file ' vdr . c ' for copyright information and
* how to reach the author .
*
2012-03-11 13:34:12 +01:00
* Original author : Marco Schluessler < marco @ lordzodiac . de >
2013-08-30 12:00:40 +02:00
* With some input from the " subtitles plugin " by Pekka Virtanen < pekka . virtanen @ sci . fi >
2007-10-12 14:52:30 +02:00
*
2022-12-06 16:57:01 +01:00
* $ Id : dvbsubtitle . c 5.2 2022 / 12 / 06 16 : 57 : 01 kls Exp $
2007-10-12 14:52:30 +02:00
*/
# include "dvbsubtitle.h"
2011-03-27 15:12:20 +02:00
# define __STDC_FORMAT_MACROS // Required for format specifiers
# include <inttypes.h>
2007-10-12 14:52:30 +02:00
# include "device.h"
2011-09-18 11:36:38 +02:00
# include "libsi/si.h"
2007-10-12 14:52:30 +02:00
2011-09-18 11:36:38 +02:00
# define PAGE_COMPOSITION_SEGMENT 0x10
# define REGION_COMPOSITION_SEGMENT 0x11
# define CLUT_DEFINITION_SEGMENT 0x12
# define OBJECT_DATA_SEGMENT 0x13
# define DISPLAY_DEFINITION_SEGMENT 0x14
# define DISPARITY_SIGNALING_SEGMENT 0x15 // DVB BlueBook A156
# define END_OF_DISPLAY_SET_SEGMENT 0x80
# define STUFFING_SEGMENT 0xFF
2007-10-12 14:52:30 +02:00
2015-01-14 10:39:55 +01:00
# define PGS_PALETTE_SEGMENT 0x14
# define PGS_OBJECT_SEGMENT 0x15
# define PGS_PRESENTATION_SEGMENT 0x16
# define PGS_WINDOW_SEGMENT 0x17
# define PGS_DISPLAY_SEGMENT 0x80
2013-09-06 12:37:27 +02:00
// Set these to 'true' for debug output, which is written into the file dbg-log.htm
// in the current working directory. The HTML file shows the actual bitmaps (dbg-nnn.jpg)
// used to display the subtitles.
static bool DebugNormal = false ; // shows pages, regions and objects
static bool DebugVerbose = false ; // shows everything
static bool DebugDisplay = DebugVerbose | | DebugNormal ;
static bool DebugPages = DebugVerbose | | DebugNormal ;
static bool DebugRegions = DebugVerbose | | DebugNormal ;
static bool DebugObjects = DebugVerbose | | DebugNormal ;
static bool DebugConverter = DebugVerbose ;
static bool DebugSegments = DebugVerbose ;
static bool DebugPixel = DebugVerbose ;
static bool DebugCluts = DebugVerbose ;
static bool DebugOutput = DebugVerbose ;
# define dbgdisplay(a...) if (DebugDisplay) SD.WriteHtml(a)
# define dbgpages(a...) if (DebugPages) SD.WriteHtml(a)
# define dbgregions(a...) if (DebugRegions) SD.WriteHtml(a)
# define dbgobjects(a...) if (DebugObjects) SD.WriteHtml(a)
# define dbgconverter(a...) if (DebugConverter) SD.WriteHtml(a)
# define dbgsegments(a...) if (DebugSegments) SD.WriteHtml(a)
# define dbgpixel(a...) if (DebugPixel) SD.WriteHtml(a)
# define dbgcluts(a...) if (DebugCluts) SD.WriteHtml(a)
# define dbgoutput(a...) if (DebugOutput) SD.WriteHtml(a)
# define DBGMAXBITMAPS 100 // debug output will be stopped after this many bitmaps
# define DBGBITMAPWIDTH 400
2015-01-09 12:03:31 +01:00
# define FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY // some don't properly handle version numbers, which renders them useless because subtitles are not displayed
2013-09-06 12:37:27 +02:00
// --- cSubtitleDebug --------------------------------------------------------
class cSubtitleDebug {
private :
cMutex mutex ;
int imgCnt ;
int64_t firstPts ;
bool newFile ;
double factor ;
public :
cSubtitleDebug ( void ) { Reset ( ) ; }
void Reset ( void ) ;
bool Active ( void ) { return imgCnt < DBGMAXBITMAPS ; }
int64_t FirstPts ( void ) { return firstPts ; }
void SetFirstPts ( int64_t FirstPts ) { if ( firstPts < 0 ) firstPts = FirstPts ; }
void SetFactor ( double Factor ) { factor = Factor ; }
cString WriteJpeg ( const cBitmap * Bitmap , int MaxX = 0 , int MaxY = 0 ) ;
void WriteHtml ( const char * Format , . . . ) ;
} ;
void cSubtitleDebug : : Reset ( void )
{
imgCnt = 0 ;
firstPts = - 1 ;
newFile = true ;
factor = 1.0 ;
}
cString cSubtitleDebug : : WriteJpeg ( const cBitmap * Bitmap , int MaxX , int MaxY )
{
if ( ! Active ( ) )
return NULL ;
cMutexLock MutexLock ( & mutex ) ;
cBitmap * Scaled = Bitmap - > Scaled ( factor , factor , true ) ;
int w = MaxX ? int ( round ( MaxX * factor ) ) : Scaled - > Width ( ) ;
int h = MaxY ? int ( round ( MaxY * factor ) ) : Scaled - > Height ( ) ;
uchar mem [ w * h * 3 ] ;
for ( int x = 0 ; x < w ; x + + ) {
for ( int y = 0 ; y < h ; y + + ) {
tColor c = Scaled - > GetColor ( x , y ) ;
int o = ( y * w + x ) * 3 ;
mem [ o + + ] = ( c & 0x00FF0000 ) > > 16 ;
mem [ o + + ] = ( c & 0x0000FF00 ) > > 8 ;
mem [ o ] = ( c & 0x000000FF ) ;
}
}
delete Scaled ;
int Size = 0 ;
uchar * Jpeg = RgbToJpeg ( mem , w , h , Size ) ;
cString ImgName = cString : : sprintf ( " dbg-%03d.jpg " , imgCnt + + ) ;
int f = open ( ImgName , O_WRONLY | O_CREAT , DEFFILEMODE ) ;
2013-09-07 10:39:46 +02:00
if ( f > = 0 ) {
if ( write ( f , Jpeg , Size ) < 0 )
LOG_ERROR_STR ( * ImgName ) ;
close ( f ) ;
}
2013-09-06 12:37:27 +02:00
free ( Jpeg ) ;
return ImgName ;
}
void cSubtitleDebug : : WriteHtml ( const char * Format , . . . )
{
if ( ! Active ( ) )
return ;
cMutexLock MutexLock ( & mutex ) ;
2013-09-07 10:39:46 +02:00
if ( FILE * f = fopen ( " dbg-log.htm " , newFile ? " w " : " a " ) ) {
va_list ap ;
va_start ( ap , Format ) ;
vfprintf ( f , Format , ap ) ;
va_end ( ap ) ;
fclose ( f ) ;
newFile = false ;
}
2013-09-06 12:37:27 +02:00
}
static cSubtitleDebug SD ;
2007-10-12 14:52:30 +02:00
// --- cSubtitleClut ---------------------------------------------------------
class cSubtitleClut : public cListObject {
private :
int clutId ;
2013-09-06 12:37:27 +02:00
int clutVersionNumber ;
2007-10-12 14:52:30 +02:00
cPalette palette2 ;
cPalette palette4 ;
cPalette palette8 ;
2013-09-06 12:37:27 +02:00
tColor yuv2rgb ( int Y , int Cb , int Cr ) ;
void SetColor ( int Bpp , int Index , tColor Color ) ;
2007-10-12 14:52:30 +02:00
public :
cSubtitleClut ( int ClutId ) ;
2013-09-06 12:37:27 +02:00
void Parse ( cBitStream & bs ) ;
2015-01-14 10:39:55 +01:00
void ParsePgs ( cBitStream & bs ) ;
2007-10-12 14:52:30 +02:00
int ClutId ( void ) { return clutId ; }
2013-09-06 12:37:27 +02:00
int ClutVersionNumber ( void ) { return clutVersionNumber ; }
2007-10-12 14:52:30 +02:00
const cPalette * GetPalette ( int Bpp ) ;
} ;
cSubtitleClut : : cSubtitleClut ( int ClutId )
: palette2 ( 2 )
, palette4 ( 4 )
, palette8 ( 8 )
{
2011-09-18 11:36:38 +02:00
int a = 0 , r = 0 , g = 0 , b = 0 ;
2007-10-12 14:52:30 +02:00
clutId = ClutId ;
2013-09-06 12:37:27 +02:00
clutVersionNumber = - 1 ;
2011-09-18 11:36:38 +02:00
// ETSI EN 300 743 10.3: 4-entry CLUT default contents
palette2 . SetColor ( 0 , ArgbToColor ( 0 , 0 , 0 , 0 ) ) ;
palette2 . SetColor ( 1 , ArgbToColor ( 255 , 255 , 255 , 255 ) ) ;
palette2 . SetColor ( 2 , ArgbToColor ( 255 , 0 , 0 , 0 ) ) ;
palette2 . SetColor ( 3 , ArgbToColor ( 255 , 127 , 127 , 127 ) ) ;
// ETSI EN 300 743 10.2: 16-entry CLUT default contents
palette4 . SetColor ( 0 , ArgbToColor ( 0 , 0 , 0 , 0 ) ) ;
for ( int i = 1 ; i < 16 ; + + i ) {
if ( i < 8 ) {
r = ( i & 1 ) ? 255 : 0 ;
g = ( i & 2 ) ? 255 : 0 ;
b = ( i & 4 ) ? 255 : 0 ;
}
else {
r = ( i & 1 ) ? 127 : 0 ;
g = ( i & 2 ) ? 127 : 0 ;
b = ( i & 4 ) ? 127 : 0 ;
}
palette4 . SetColor ( i , ArgbToColor ( 255 , r , g , b ) ) ;
}
// ETSI EN 300 743 10.1: 256-entry CLUT default contents
palette8 . SetColor ( 0 , ArgbToColor ( 0 , 0 , 0 , 0 ) ) ;
for ( int i = 1 ; i < 256 ; + + i ) {
if ( i < 8 ) {
r = ( i & 1 ) ? 255 : 0 ;
g = ( i & 2 ) ? 255 : 0 ;
b = ( i & 4 ) ? 255 : 0 ;
a = 63 ;
}
else {
switch ( i & 0x88 ) {
case 0x00 :
r = ( ( i & 1 ) ? 85 : 0 ) + ( ( i & 0x10 ) ? 170 : 0 ) ;
g = ( ( i & 2 ) ? 85 : 0 ) + ( ( i & 0x20 ) ? 170 : 0 ) ;
b = ( ( i & 4 ) ? 85 : 0 ) + ( ( i & 0x40 ) ? 170 : 0 ) ;
a = 255 ;
break ;
case 0x08 :
r = ( ( i & 1 ) ? 85 : 0 ) + ( ( i & 0x10 ) ? 170 : 0 ) ;
g = ( ( i & 2 ) ? 85 : 0 ) + ( ( i & 0x20 ) ? 170 : 0 ) ;
b = ( ( i & 4 ) ? 85 : 0 ) + ( ( i & 0x40 ) ? 170 : 0 ) ;
a = 127 ;
break ;
case 0x80 :
r = 127 + ( ( i & 1 ) ? 43 : 0 ) + ( ( i & 0x10 ) ? 85 : 0 ) ;
g = 127 + ( ( i & 2 ) ? 43 : 0 ) + ( ( i & 0x20 ) ? 85 : 0 ) ;
b = 127 + ( ( i & 4 ) ? 43 : 0 ) + ( ( i & 0x40 ) ? 85 : 0 ) ;
a = 255 ;
break ;
case 0x88 :
r = ( ( i & 1 ) ? 43 : 0 ) + ( ( i & 0x10 ) ? 85 : 0 ) ;
g = ( ( i & 2 ) ? 43 : 0 ) + ( ( i & 0x20 ) ? 85 : 0 ) ;
b = ( ( i & 4 ) ? 43 : 0 ) + ( ( i & 0x40 ) ? 85 : 0 ) ;
a = 255 ;
break ;
}
}
palette8 . SetColor ( i , ArgbToColor ( a , r , g , b ) ) ;
}
2007-10-12 14:52:30 +02:00
}
2013-09-06 12:37:27 +02:00
void cSubtitleClut : : Parse ( cBitStream & bs )
{
int Version = bs . GetBits ( 4 ) ;
2015-01-09 12:03:31 +01:00
# ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
2013-09-06 12:37:27 +02:00
if ( clutVersionNumber = = Version )
return ; // no update
2015-01-09 12:03:31 +01:00
# endif
2013-09-06 12:37:27 +02:00
clutVersionNumber = Version ;
bs . SkipBits ( 4 ) ; // reserved
dbgcluts ( " <b>clut</b> id %d version %d<br> \n " , clutId , clutVersionNumber ) ;
while ( ! bs . IsEOF ( ) ) {
uchar clutEntryId = bs . GetBits ( 8 ) ;
bool entryClut2Flag = bs . GetBit ( ) ;
bool entryClut4Flag = bs . GetBit ( ) ;
bool entryClut8Flag = bs . GetBit ( ) ;
bs . SkipBits ( 4 ) ; // reserved
uchar yval ;
uchar crval ;
uchar cbval ;
uchar tval ;
if ( bs . GetBit ( ) ) { // full_range_flag
yval = bs . GetBits ( 8 ) ;
crval = bs . GetBits ( 8 ) ;
cbval = bs . GetBits ( 8 ) ;
tval = bs . GetBits ( 8 ) ;
}
else {
yval = bs . GetBits ( 6 ) < < 2 ;
crval = bs . GetBits ( 4 ) < < 4 ;
cbval = bs . GetBits ( 4 ) < < 4 ;
tval = bs . GetBits ( 2 ) < < 6 ;
}
tColor value = 0 ;
if ( yval ) {
value = yuv2rgb ( yval , cbval , crval ) ;
value | = ( ( 10 - ( clutEntryId ? Setup . SubtitleFgTransparency : Setup . SubtitleBgTransparency ) ) * ( 255 - tval ) / 10 ) < < 24 ;
}
dbgcluts ( " %2d %d %d %d %08X<br> \n " , clutEntryId , entryClut2Flag ? 2 : 0 , entryClut4Flag ? 4 : 0 , entryClut8Flag ? 8 : 0 , value ) ;
if ( entryClut2Flag )
SetColor ( 2 , clutEntryId , value ) ;
if ( entryClut4Flag )
SetColor ( 4 , clutEntryId , value ) ;
if ( entryClut8Flag )
SetColor ( 8 , clutEntryId , value ) ;
}
}
2015-01-14 10:39:55 +01:00
void cSubtitleClut : : ParsePgs ( cBitStream & bs )
{
int Version = bs . GetBits ( 8 ) ;
if ( clutVersionNumber = = Version )
return ; // no update
clutVersionNumber = Version ;
dbgcluts ( " <b>clut</b> id %d version %d<br> \n " , clutId , clutVersionNumber ) ;
for ( int i = 0 ; i < 256 ; + + i )
SetColor ( 8 , i , ArgbToColor ( 0 , 0 , 0 , 0 ) ) ;
while ( ! bs . IsEOF ( ) ) {
uchar clutEntryId = bs . GetBits ( 8 ) ;
uchar yval = bs . GetBits ( 8 ) ;
uchar crval = bs . GetBits ( 8 ) ;
uchar cbval = bs . GetBits ( 8 ) ;
uchar tval = bs . GetBits ( 8 ) ;
tColor value = 0 ;
if ( yval ) {
value = yuv2rgb ( yval , cbval , crval ) ;
value | = ( ( 10 - ( clutEntryId ? Setup . SubtitleFgTransparency : Setup . SubtitleBgTransparency ) ) * tval / 10 ) < < 24 ;
}
dbgcluts ( " %2d %08X<br> \n " , clutEntryId , value ) ;
SetColor ( 8 , clutEntryId , value ) ;
}
}
2013-09-06 12:37:27 +02:00
tColor cSubtitleClut : : yuv2rgb ( int Y , int Cb , int Cr )
{
int Ey , Epb , Epr ;
int Eg , Eb , Er ;
Ey = ( Y - 16 ) ;
Epb = ( Cb - 128 ) ;
Epr = ( Cr - 128 ) ;
/* ITU-R 709 */
Er = constrain ( ( 298 * Ey + 460 * Epr ) / 256 , 0 , 255 ) ;
Eg = constrain ( ( 298 * Ey - 55 * Epb - 137 * Epr ) / 256 , 0 , 255 ) ;
Eb = constrain ( ( 298 * Ey + 543 * Epb ) / 256 , 0 , 255 ) ;
return ( Er < < 16 ) | ( Eg < < 8 ) | Eb ;
}
2007-10-12 14:52:30 +02:00
void cSubtitleClut : : SetColor ( int Bpp , int Index , tColor Color )
{
switch ( Bpp ) {
case 2 : palette2 . SetColor ( Index , Color ) ; break ;
case 4 : palette4 . SetColor ( Index , Color ) ; break ;
case 8 : palette8 . SetColor ( Index , Color ) ; break ;
default : esyslog ( " ERROR: wrong Bpp in cSubtitleClut::SetColor(%d, %d, %08X) " , Bpp , Index , Color ) ;
}
}
const cPalette * cSubtitleClut : : GetPalette ( int Bpp )
{
switch ( Bpp ) {
case 2 : return & palette2 ;
case 4 : return & palette4 ;
case 8 : return & palette8 ;
default : esyslog ( " ERROR: wrong Bpp in cSubtitleClut::GetPalette(%d) " , Bpp ) ;
}
return & palette8 ;
}
// --- cSubtitleObject -------------------------------------------------------
class cSubtitleObject : public cListObject {
private :
int objectId ;
2013-09-06 12:37:27 +02:00
int objectVersionNumber ;
int objectCodingMethod ;
2007-10-12 14:52:30 +02:00
bool nonModifyingColorFlag ;
2013-09-06 12:37:27 +02:00
int topLength ;
int botLength ;
2015-01-14 10:39:55 +01:00
int topIndex ;
2013-09-06 12:37:27 +02:00
uchar * topData ;
uchar * botData ;
char * txtData ;
int lineHeight ;
void DrawLine ( cBitmap * Bitmap , int x , int y , tIndex Index , int Length ) ;
bool Decode2BppCodeString ( cBitmap * Bitmap , int px , int py , cBitStream * bs , int & x , int y , const uint8_t * MapTable ) ;
bool Decode4BppCodeString ( cBitmap * Bitmap , int px , int py , cBitStream * bs , int & x , int y , const uint8_t * MapTable ) ;
bool Decode8BppCodeString ( cBitmap * Bitmap , int px , int py , cBitStream * bs , int & x , int y ) ;
2015-01-14 10:39:55 +01:00
bool DecodePgsCodeString ( cBitmap * Bitmap , int px , int py , cBitStream * bs , int & x , int y ) ;
2013-09-06 12:37:27 +02:00
void DecodeSubBlock ( cBitmap * Bitmap , int px , int py , const uchar * Data , int Length , bool Even ) ;
void DecodeCharacterString ( const uchar * Data , int NumberOfCodes ) ;
2007-10-12 14:52:30 +02:00
public :
2013-09-06 12:37:27 +02:00
cSubtitleObject ( int ObjectId ) ;
~ cSubtitleObject ( ) ;
void Parse ( cBitStream & bs ) ;
2015-01-14 10:39:55 +01:00
void ParsePgs ( cBitStream & bs ) ;
2007-10-12 14:52:30 +02:00
int ObjectId ( void ) { return objectId ; }
2013-09-06 12:37:27 +02:00
int ObjectVersionNumber ( void ) { return objectVersionNumber ; }
int ObjectCodingMethod ( void ) { return objectCodingMethod ; }
2007-10-12 14:52:30 +02:00
bool NonModifyingColorFlag ( void ) { return nonModifyingColorFlag ; }
2013-09-06 12:37:27 +02:00
void Render ( cBitmap * Bitmap , int px , int py , tIndex IndexFg , tIndex IndexBg ) ;
2007-10-12 14:52:30 +02:00
} ;
2013-09-06 12:37:27 +02:00
cSubtitleObject : : cSubtitleObject ( int ObjectId )
2007-10-12 14:52:30 +02:00
{
objectId = ObjectId ;
2013-09-06 12:37:27 +02:00
objectVersionNumber = - 1 ;
objectCodingMethod = - 1 ;
2007-10-12 14:52:30 +02:00
nonModifyingColorFlag = false ;
2013-09-06 12:37:27 +02:00
topLength = 0 ;
botLength = 0 ;
2015-01-14 10:39:55 +01:00
topIndex = 0 ;
2013-09-06 12:37:27 +02:00
topData = NULL ;
botData = NULL ;
txtData = NULL ;
lineHeight = 26 ; // configurable subtitling font size?
}
cSubtitleObject : : ~ cSubtitleObject ( )
{
free ( topData ) ;
free ( botData ) ;
free ( txtData ) ;
}
void cSubtitleObject : : Parse ( cBitStream & bs )
{
int Version = bs . GetBits ( 4 ) ;
2015-01-09 12:03:31 +01:00
# ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
2013-09-06 12:37:27 +02:00
if ( objectVersionNumber = = Version )
return ; // no update
2015-01-09 12:03:31 +01:00
# endif
2013-09-06 12:37:27 +02:00
objectVersionNumber = Version ;
objectCodingMethod = bs . GetBits ( 2 ) ;
nonModifyingColorFlag = bs . GetBit ( ) ;
bs . SkipBit ( ) ; // reserved
dbgobjects ( " <b>object</b> id %d version %d method %d modify %d " , objectId , objectVersionNumber , objectCodingMethod , nonModifyingColorFlag ) ; // no "<br>\n" here, DecodeCharacterString() may add data
if ( objectCodingMethod = = 0 ) { // coding of pixels
topLength = bs . GetBits ( 16 ) ;
botLength = bs . GetBits ( 16 ) ;
free ( topData ) ;
if ( ( topData = MALLOC ( uchar , topLength ) ) ! = NULL )
memcpy ( topData , bs . GetData ( ) , topLength ) ;
else
topLength = 0 ;
free ( botData ) ;
if ( ( botData = MALLOC ( uchar , botLength ) ) ! = NULL )
memcpy ( botData , bs . GetData ( ) + topLength , botLength ) ;
else
botLength = 0 ;
bs . WordAlign ( ) ;
}
else if ( objectCodingMethod = = 1 ) { // coded as a string of characters
int numberOfCodes = bs . GetBits ( 8 ) ;
DecodeCharacterString ( bs . GetData ( ) , numberOfCodes ) ;
}
dbgobjects ( " <br> \n " ) ;
if ( DebugObjects ) {
// We can't get the actual clut here, so we use a default one. This may lead to
// funny colors, but we just want to get a rough idea of what's in the object, anyway.
cSubtitleClut Clut ( 0 ) ;
cBitmap b ( 1920 , 1080 , 8 ) ;
b . Replace ( * Clut . GetPalette ( b . Bpp ( ) ) ) ;
b . Clean ( ) ;
Render ( & b , 0 , 0 , 0 , 1 ) ;
int x1 , y1 , x2 , y2 ;
if ( b . Dirty ( x1 , y1 , x2 , y2 ) ) {
cString ImgName = SD . WriteJpeg ( & b , x2 , y2 ) ;
dbgobjects ( " <img src= \" %s \" ><br> \n " , * ImgName ) ;
}
}
2011-09-18 11:36:38 +02:00
}
2015-01-14 10:39:55 +01:00
void cSubtitleObject : : ParsePgs ( cBitStream & bs )
{
int Version = bs . GetBits ( 8 ) ;
if ( objectVersionNumber = = Version )
return ; // no update
objectVersionNumber = Version ;
objectCodingMethod = 0 ;
int sequenceDescriptor = bs . GetBits ( 8 ) ;
if ( ! ( sequenceDescriptor & 0x80 ) & & topData ! = NULL ) {
memcpy ( topData + topIndex , bs . GetData ( ) , ( bs . Length ( ) - bs . Index ( ) ) / 8 ) ;
topIndex + = ( bs . Length ( ) - bs . Index ( ) ) / 8 ;
return ;
}
topLength = bs . GetBits ( 24 ) - 4 + 1 ; // exclude width / height, add sub block type
bs . SkipBits ( 32 ) ;
if ( ( topData = MALLOC ( uchar , topLength ) ) ! = NULL ) {
topData [ topIndex + + ] = 0xFF ; // PGS end of line
memcpy ( topData + 1 , bs . GetData ( ) , ( bs . Length ( ) - bs . Index ( ) ) / 8 ) ;
topIndex + = ( bs . Length ( ) - bs . Index ( ) ) / 8 + 1 ;
}
dbgobjects ( " <b>object</b> id %d version %d method %d modify %d " , objectId , objectVersionNumber , objectCodingMethod , nonModifyingColorFlag ) ;
}
2011-09-18 11:36:38 +02:00
void cSubtitleObject : : DecodeCharacterString ( const uchar * Data , int NumberOfCodes )
{
2013-08-30 12:00:40 +02:00
// "ETSI EN 300 743 V1.3.1 (2006-11)", chapter 7.2.5 "Object data segment" specifies
// character_code to be a 16-bit index number into the character table identified
// in the subtitle_descriptor. However, the "subtitling_descriptor" <sic> according to
// "ETSI EN 300 468 V1.13.1 (2012-04)" doesn't contain a "character table identifier".
// It only contains a three letter language code, without any specification as to how
// this is related to a specific character table.
// Apparently the first "code" in textual subtitles contains the character table
// identifier, and all codes are 8-bit only. So let's first make Data a string of
// 8-bit characters:
2011-09-18 11:36:38 +02:00
if ( NumberOfCodes > 0 ) {
2013-08-30 12:00:40 +02:00
char txt [ NumberOfCodes + 1 ] ;
for ( int i = 0 ; i < NumberOfCodes ; i + + )
txt [ i ] = Data [ i * 2 + 1 ] ;
txt [ NumberOfCodes ] = 0 ;
const uchar * from = ( uchar * ) txt ;
int len = NumberOfCodes ;
2020-05-15 12:32:51 +02:00
const char * CharacterTable = SI : : getCharacterTable ( from , len ) ;
dbgobjects ( " table %s raw '%s' " , CharacterTable , from ) ;
2013-08-30 12:00:40 +02:00
cCharSetConv conv ( CharacterTable , cCharSetConv : : SystemCharacterTable ( ) ) ;
const char * s = conv . Convert ( ( const char * ) from ) ;
2013-09-06 12:37:27 +02:00
dbgobjects ( " conv '%s' " , s ) ;
free ( txtData ) ;
txtData = strdup ( s ) ;
2011-09-18 11:36:38 +02:00
}
2007-10-12 14:52:30 +02:00
}
2013-09-06 12:37:27 +02:00
void cSubtitleObject : : DecodeSubBlock ( cBitmap * Bitmap , int px , int py , const uchar * Data , int Length , bool Even )
2007-10-12 14:52:30 +02:00
{
int x = 0 ;
int y = Even ? 0 : 1 ;
2011-09-18 11:36:38 +02:00
uint8_t map2to4 [ 4 ] = { 0x00 , 0x07 , 0x08 , 0x0F } ;
uint8_t map2to8 [ 4 ] = { 0x00 , 0x77 , 0x88 , 0xFF } ;
uint8_t map4to8 [ 16 ] = { 0x00 , 0x11 , 0x22 , 0x33 , 0x44 , 0x55 , 0x66 , 0x77 , 0x88 , 0x99 , 0xAA , 0xBB , 0xCC , 0xDD , 0xEE , 0xFF } ;
const uint8_t * mapTable = NULL ;
cBitStream bs ( Data , Length * 8 ) ;
while ( ! bs . IsEOF ( ) ) {
switch ( bs . GetBits ( 8 ) ) {
case 0x10 :
2013-09-06 12:37:27 +02:00
dbgpixel ( " 2-bit / pixel code string<br> \n " ) ;
switch ( Bitmap - > Bpp ( ) ) {
2011-09-18 11:36:38 +02:00
case 8 : mapTable = map2to8 ; break ;
case 4 : mapTable = map2to4 ; break ;
default : mapTable = NULL ; break ;
}
2013-09-06 12:37:27 +02:00
while ( Decode2BppCodeString ( Bitmap , px , py , & bs , x , y , mapTable ) & & ! bs . IsEOF ( ) )
2011-09-18 11:36:38 +02:00
;
bs . ByteAlign ( ) ;
break ;
case 0x11 :
2013-09-06 12:37:27 +02:00
dbgpixel ( " 4-bit / pixel code string<br> \n " ) ;
switch ( Bitmap - > Bpp ( ) ) {
2011-09-18 11:36:38 +02:00
case 8 : mapTable = map4to8 ; break ;
default : mapTable = NULL ; break ;
}
2013-09-06 12:37:27 +02:00
while ( Decode4BppCodeString ( Bitmap , px , py , & bs , x , y , mapTable ) & & ! bs . IsEOF ( ) )
2011-09-18 11:36:38 +02:00
;
bs . ByteAlign ( ) ;
break ;
case 0x12 :
2013-09-06 12:37:27 +02:00
dbgpixel ( " 8-bit / pixel code string<br> \n " ) ;
while ( Decode8BppCodeString ( Bitmap , px , py , & bs , x , y ) & & ! bs . IsEOF ( ) )
2011-09-18 11:36:38 +02:00
;
break ;
case 0x20 :
2013-09-06 12:37:27 +02:00
dbgpixel ( " sub block 2 to 4 map<br> \n " ) ;
for ( int i = 0 ; i < 4 ; + + i )
map2to4 [ i ] = bs . GetBits ( 4 ) ;
2011-09-18 11:36:38 +02:00
break ;
case 0x21 :
2013-09-06 12:37:27 +02:00
dbgpixel ( " sub block 2 to 8 map<br> \n " ) ;
2011-09-18 11:36:38 +02:00
for ( int i = 0 ; i < 4 ; + + i )
map2to8 [ i ] = bs . GetBits ( 8 ) ;
break ;
case 0x22 :
2013-09-06 12:37:27 +02:00
dbgpixel ( " sub block 4 to 8 map<br> \n " ) ;
2011-09-18 11:36:38 +02:00
for ( int i = 0 ; i < 16 ; + + i )
map4to8 [ i ] = bs . GetBits ( 8 ) ;
break ;
case 0xF0 :
2013-09-06 12:37:27 +02:00
dbgpixel ( " end of object line<br> \n " ) ;
2011-09-18 11:36:38 +02:00
x = 0 ;
y + = 2 ;
break ;
2015-01-14 10:39:55 +01:00
case 0xFF :
dbgpixel ( " PGS code string, including EOLs<br> \n " ) ;
while ( DecodePgsCodeString ( Bitmap , px , py , & bs , x , y ) & & ! bs . IsEOF ( ) ) {
x = 0 ;
y + + ;
}
break ;
2013-09-06 12:37:27 +02:00
default : dbgpixel ( " unknown sub block %s %d<br> \n " , __FUNCTION__ , __LINE__ ) ;
2011-09-18 11:36:38 +02:00
}
2007-10-12 14:52:30 +02:00
}
}
2013-09-06 12:37:27 +02:00
void cSubtitleObject : : DrawLine ( cBitmap * Bitmap , int x , int y , tIndex Index , int Length )
2007-10-12 14:52:30 +02:00
{
if ( nonModifyingColorFlag & & Index = = 1 )
return ;
for ( int pos = x ; pos < x + Length ; pos + + )
2013-09-06 12:37:27 +02:00
Bitmap - > SetIndex ( pos , y , Index ) ;
2007-10-12 14:52:30 +02:00
}
2013-09-06 12:37:27 +02:00
bool cSubtitleObject : : Decode2BppCodeString ( cBitmap * Bitmap , int px , int py , cBitStream * bs , int & x , int y , const uint8_t * MapTable )
2007-10-12 14:52:30 +02:00
{
int rl = 0 ;
int color = 0 ;
2011-09-18 11:36:38 +02:00
uchar code = bs - > GetBits ( 2 ) ;
2007-10-12 14:52:30 +02:00
if ( code ) {
color = code ;
rl = 1 ;
}
2011-09-18 11:36:38 +02:00
else if ( bs - > GetBit ( ) ) { // switch_1
rl = bs - > GetBits ( 3 ) + 3 ;
color = bs - > GetBits ( 2 ) ;
}
else if ( bs - > GetBit ( ) ) // switch_2
rl = 1 ; //color 0
2007-10-12 14:52:30 +02:00
else {
2011-09-18 11:36:38 +02:00
switch ( bs - > GetBits ( 2 ) ) { // switch_3
case 0 :
return false ;
case 1 :
rl = 2 ; //color 0
break ;
case 2 :
rl = bs - > GetBits ( 4 ) + 12 ;
color = bs - > GetBits ( 2 ) ;
break ;
case 3 :
rl = bs - > GetBits ( 8 ) + 29 ;
color = bs - > GetBits ( 2 ) ;
break ;
default : ;
}
2007-10-12 14:52:30 +02:00
}
2011-09-18 11:36:38 +02:00
if ( MapTable )
color = MapTable [ color ] ;
2013-09-06 12:37:27 +02:00
DrawLine ( Bitmap , px + x , py + y , color , rl ) ;
2007-10-12 14:52:30 +02:00
x + = rl ;
return true ;
}
2013-09-06 12:37:27 +02:00
bool cSubtitleObject : : Decode4BppCodeString ( cBitmap * Bitmap , int px , int py , cBitStream * bs , int & x , int y , const uint8_t * MapTable )
2007-10-12 14:52:30 +02:00
{
int rl = 0 ;
int color = 0 ;
2011-09-18 11:36:38 +02:00
uchar code = bs - > GetBits ( 4 ) ;
2007-10-12 14:52:30 +02:00
if ( code ) {
color = code ;
rl = 1 ;
}
2011-09-18 11:36:38 +02:00
else if ( bs - > GetBit ( ) = = 0 ) { // switch_1
code = bs - > GetBits ( 3 ) ;
if ( code )
rl = code + 2 ; //color 0
else
return false ;
}
else if ( bs - > GetBit ( ) = = 0 ) { // switch_2
rl = bs - > GetBits ( 2 ) + 4 ;
color = bs - > GetBits ( 4 ) ;
}
2007-10-12 14:52:30 +02:00
else {
2011-09-18 11:36:38 +02:00
switch ( bs - > GetBits ( 2 ) ) { // switch_3
case 0 : // color 0
rl = 1 ;
break ;
case 1 : // color 0
rl = 2 ;
break ;
case 2 :
rl = bs - > GetBits ( 4 ) + 9 ;
color = bs - > GetBits ( 4 ) ;
break ;
case 3 :
rl = bs - > GetBits ( 8 ) + 25 ;
color = bs - > GetBits ( 4 ) ;
break ;
}
2007-10-12 14:52:30 +02:00
}
2011-09-18 11:36:38 +02:00
if ( MapTable )
color = MapTable [ color ] ;
2013-09-06 12:37:27 +02:00
DrawLine ( Bitmap , px + x , py + y , color , rl ) ;
2007-10-12 14:52:30 +02:00
x + = rl ;
return true ;
}
2013-09-06 12:37:27 +02:00
bool cSubtitleObject : : Decode8BppCodeString ( cBitmap * Bitmap , int px , int py , cBitStream * bs , int & x , int y )
2007-10-12 14:52:30 +02:00
{
int rl = 0 ;
int color = 0 ;
2011-09-18 11:36:38 +02:00
uchar code = bs - > GetBits ( 8 ) ;
2007-10-12 14:52:30 +02:00
if ( code ) {
color = code ;
rl = 1 ;
}
2011-09-18 11:36:38 +02:00
else if ( bs - > GetBit ( ) ) {
rl = bs - > GetBits ( 7 ) ;
color = bs - > GetBits ( 8 ) ;
}
2007-10-12 14:52:30 +02:00
else {
2011-09-18 11:36:38 +02:00
code = bs - > GetBits ( 7 ) ;
if ( code )
rl = code ; // color 0
else
return false ;
2007-10-12 14:52:30 +02:00
}
2013-09-06 12:37:27 +02:00
DrawLine ( Bitmap , px + x , py + y , color , rl ) ;
2007-10-12 14:52:30 +02:00
x + = rl ;
return true ;
}
2015-01-14 10:39:55 +01:00
bool cSubtitleObject : : DecodePgsCodeString ( cBitmap * Bitmap , int px , int py , cBitStream * bs , int & x , int y )
{
while ( ! bs - > IsEOF ( ) ) {
int color = bs - > GetBits ( 8 ) ;
int rl = 1 ;
if ( ! color ) {
int flags = bs - > GetBits ( 8 ) ;
rl = flags & 0x3f ;
if ( flags & 0x40 )
rl = ( rl < < 8 ) + bs - > GetBits ( 8 ) ;
color = flags & 0x80 ? bs - > GetBits ( 8 ) : 0 ;
}
if ( rl > 0 ) {
DrawLine ( Bitmap , px + x , py + y , color , rl ) ;
x + = rl ;
}
else if ( ! rl )
return true ;
}
return false ;
}
2013-09-06 12:37:27 +02:00
void cSubtitleObject : : Render ( cBitmap * Bitmap , int px , int py , tIndex IndexFg , tIndex IndexBg )
{
if ( objectCodingMethod = = 0 ) { // coding of pixels
DecodeSubBlock ( Bitmap , px , py , topData , topLength , true ) ;
if ( botLength )
DecodeSubBlock ( Bitmap , px , py , botData , botLength , false ) ;
else
DecodeSubBlock ( Bitmap , px , py , topData , topLength , false ) ;
}
else if ( objectCodingMethod = = 1 ) { // coded as a string of characters
if ( txtData ) {
//TODO couldn't we draw the text directly into Bitmap?
cFont * font = cFont : : CreateFont ( Setup . FontOsd , Setup . FontOsdSize ) ;
cBitmap tmp ( font - > Width ( txtData ) , font - > Height ( ) , Bitmap - > Bpp ( ) ) ;
double factor = ( double ) lineHeight / font - > Height ( ) ;
tmp . DrawText ( 0 , 0 , txtData , Bitmap - > Color ( IndexFg ) , Bitmap - > Color ( IndexBg ) , font ) ;
cBitmap * scaled = tmp . Scaled ( factor , factor , true ) ;
Bitmap - > DrawBitmap ( px , py , * scaled ) ;
delete scaled ;
delete font ;
}
}
}
// --- cSubtitleObjects ------------------------------------------------------
class cSubtitleObjects : public cList < cSubtitleObject > {
public :
cSubtitleObject * GetObjectById ( int ObjectId , bool New = false ) ;
} ;
cSubtitleObject * cSubtitleObjects : : GetObjectById ( int ObjectId , bool New )
{
for ( cSubtitleObject * so = First ( ) ; so ; so = Next ( so ) ) {
if ( so - > ObjectId ( ) = = ObjectId )
return so ;
}
if ( ! New )
return NULL ;
cSubtitleObject * Object = new cSubtitleObject ( ObjectId ) ;
Add ( Object ) ;
return Object ;
}
// --- cSubtitleObjectRef ----------------------------------------------------
class cSubtitleObjectRef : public cListObject {
2015-01-14 10:39:55 +01:00
protected :
2013-09-06 12:37:27 +02:00
int objectId ;
int objectType ;
int objectProviderFlag ;
int objectHorizontalPosition ;
int objectVerticalPosition ;
int foregroundPixelCode ;
int backgroundPixelCode ;
public :
2015-01-14 10:39:55 +01:00
cSubtitleObjectRef ( void ) ;
2013-09-06 12:37:27 +02:00
cSubtitleObjectRef ( cBitStream & bs ) ;
int ObjectId ( void ) { return objectId ; }
int ObjectType ( void ) { return objectType ; }
int ObjectProviderFlag ( void ) { return objectProviderFlag ; }
int ObjectHorizontalPosition ( void ) { return objectHorizontalPosition ; }
int ObjectVerticalPosition ( void ) { return objectVerticalPosition ; }
int ForegroundPixelCode ( void ) { return foregroundPixelCode ; }
int BackgroundPixelCode ( void ) { return backgroundPixelCode ; }
} ;
2015-01-14 10:39:55 +01:00
cSubtitleObjectRef : : cSubtitleObjectRef ( void )
{
objectId = 0 ;
objectType = 0 ;
objectProviderFlag = 0 ;
objectHorizontalPosition = 0 ;
objectVerticalPosition = 0 ;
foregroundPixelCode = 0 ;
backgroundPixelCode = 0 ;
}
2013-09-06 12:37:27 +02:00
cSubtitleObjectRef : : cSubtitleObjectRef ( cBitStream & bs )
{
objectId = bs . GetBits ( 16 ) ;
objectType = bs . GetBits ( 2 ) ;
objectProviderFlag = bs . GetBits ( 2 ) ;
objectHorizontalPosition = bs . GetBits ( 12 ) ;
bs . SkipBits ( 4 ) ; // reserved
objectVerticalPosition = bs . GetBits ( 12 ) ;
if ( objectType = = 0x01 | | objectType = = 0x02 ) {
foregroundPixelCode = bs . GetBits ( 8 ) ;
backgroundPixelCode = bs . GetBits ( 8 ) ;
}
else {
foregroundPixelCode = 0 ;
backgroundPixelCode = 0 ;
}
dbgregions ( " <b>objectref</b> id %d type %d flag %d x %d y %d fg %d bg %d<br> \n " , objectId , objectType , objectProviderFlag , objectHorizontalPosition , objectVerticalPosition , foregroundPixelCode , backgroundPixelCode ) ;
}
2015-01-14 10:39:55 +01:00
// --- cSubtitleObjectRefPgs - PGS variant of cSubtitleObjectRef -------------
class cSubtitleObjectRefPgs : public cSubtitleObjectRef {
private :
int windowId ;
int compositionFlag ;
int cropX ;
int cropY ;
int cropW ;
int cropH ;
public :
cSubtitleObjectRefPgs ( cBitStream & bs ) ;
} ;
cSubtitleObjectRefPgs : : cSubtitleObjectRefPgs ( cBitStream & bs )
: cSubtitleObjectRef ( )
{
objectId = bs . GetBits ( 16 ) ;
windowId = bs . GetBits ( 8 ) ;
compositionFlag = bs . GetBits ( 8 ) ;
bs . SkipBits ( 32 ) ; // skip absolute position, object is aligned to region
if ( ( compositionFlag & 0x80 ) ! = 0 ) {
cropX = bs . GetBits ( 16 ) ;
cropY = bs . GetBits ( 16 ) ;
cropW = bs . GetBits ( 16 ) ;
cropH = bs . GetBits ( 16 ) ;
}
else
cropX = cropY = cropW = cropH = 0 ;
dbgregions ( " <b>objectrefPgs</b> id %d flag %d x %d y %d cropX %d cropY %d cropW %d cropH %d<br> \n " , objectId , compositionFlag , objectHorizontalPosition , objectVerticalPosition , cropX , cropY , cropW , cropH ) ;
}
2007-10-12 14:52:30 +02:00
// --- cSubtitleRegion -------------------------------------------------------
2013-09-06 12:37:27 +02:00
class cSubtitleRegion : public cListObject {
2007-10-12 14:52:30 +02:00
private :
int regionId ;
2013-09-06 12:37:27 +02:00
int regionVersionNumber ;
bool regionFillFlag ;
int regionWidth ;
int regionHeight ;
int regionLevelOfCompatibility ;
int regionDepth ;
2007-10-12 14:52:30 +02:00
int clutId ;
2013-09-06 12:37:27 +02:00
int region8bitPixelCode ;
int region4bitPixelCode ;
int region2bitPixelCode ;
cList < cSubtitleObjectRef > objectRefs ;
2007-10-12 14:52:30 +02:00
public :
cSubtitleRegion ( int RegionId ) ;
2013-09-06 12:37:27 +02:00
void Parse ( cBitStream & bs ) ;
2015-01-14 10:39:55 +01:00
void ParsePgs ( cBitStream & bs ) ;
void SetDimensions ( int Width , int Height ) ;
2007-10-12 14:52:30 +02:00
int RegionId ( void ) { return regionId ; }
2013-09-06 12:37:27 +02:00
int RegionVersionNumber ( void ) { return regionVersionNumber ; }
bool RegionFillFlag ( void ) { return regionFillFlag ; }
int RegionWidth ( void ) { return regionWidth ; }
int RegionHeight ( void ) { return regionHeight ; }
int RegionLevelOfCompatibility ( void ) { return regionLevelOfCompatibility ; }
int RegionDepth ( void ) { return regionDepth ; }
2007-10-12 14:52:30 +02:00
int ClutId ( void ) { return clutId ; }
2013-09-06 12:37:27 +02:00
void Render ( cBitmap * Bitmap , cSubtitleObjects * Objects ) ;
2007-10-12 14:52:30 +02:00
} ;
cSubtitleRegion : : cSubtitleRegion ( int RegionId )
{
regionId = RegionId ;
2013-09-06 12:37:27 +02:00
regionVersionNumber = - 1 ;
regionFillFlag = false ;
regionWidth = 0 ;
regionHeight = 0 ;
regionLevelOfCompatibility = 0 ;
regionDepth = 0 ;
2007-10-12 14:52:30 +02:00
clutId = - 1 ;
2013-09-06 12:37:27 +02:00
region8bitPixelCode = 0 ;
region4bitPixelCode = 0 ;
region2bitPixelCode = 0 ;
2007-10-12 14:52:30 +02:00
}
2013-09-06 12:37:27 +02:00
void cSubtitleRegion : : Parse ( cBitStream & bs )
2007-10-12 14:52:30 +02:00
{
2013-09-06 12:37:27 +02:00
int Version = bs . GetBits ( 4 ) ;
2015-01-09 12:03:31 +01:00
# ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
2013-09-06 12:37:27 +02:00
if ( regionVersionNumber = = Version )
return ; // no update
2015-01-09 12:03:31 +01:00
# endif
2013-09-06 12:37:27 +02:00
regionVersionNumber = Version ;
regionFillFlag = bs . GetBit ( ) ;
bs . SkipBits ( 3 ) ; // reserved
regionWidth = bs . GetBits ( 16 ) ;
regionHeight = bs . GetBits ( 16 ) ;
regionLevelOfCompatibility = 1 < < bs . GetBits ( 3 ) ; // stored as "number of bits per pixel"
regionDepth = 1 < < bs . GetBits ( 3 ) ; // stored as "number of bits per pixel"
bs . SkipBits ( 2 ) ; // reserved
clutId = bs . GetBits ( 8 ) ;
region8bitPixelCode = bs . GetBits ( 8 ) ;
region4bitPixelCode = bs . GetBits ( 4 ) ;
region2bitPixelCode = bs . GetBits ( 2 ) ;
bs . SkipBits ( 2 ) ; // reserved
dbgregions ( " <b>region</b> id %d version %d fill %d width %d height %d level %d depth %d clutId %d<br> \n " , regionId , regionVersionNumber , regionFillFlag , regionWidth , regionHeight , regionLevelOfCompatibility , regionDepth , clutId ) ;
// no objectRefs.Clear() here!
while ( ! bs . IsEOF ( ) )
objectRefs . Add ( new cSubtitleObjectRef ( bs ) ) ;
2007-10-12 14:52:30 +02:00
}
2015-01-14 10:39:55 +01:00
void cSubtitleRegion : : ParsePgs ( cBitStream & bs )
{
regionDepth = 8 ;
bs . SkipBits ( 8 ) ; // skip palette update flag
clutId = bs . GetBits ( 8 ) ;
dbgregions ( " <b>region</b> id %d version %d clutId %d<br> \n " , regionId , regionVersionNumber , clutId ) ;
int objects = bs . GetBits ( 8 ) ;
while ( objects - - )
objectRefs . Add ( new cSubtitleObjectRefPgs ( bs ) ) ;
}
void cSubtitleRegion : : SetDimensions ( int Width , int Height )
{
regionWidth = Width ;
regionHeight = Height ;
dbgregions ( " <b>region</b> id %d width %d height %d<br> \n " , regionId , regionWidth , regionHeight ) ;
}
2013-09-06 12:37:27 +02:00
void cSubtitleRegion : : Render ( cBitmap * Bitmap , cSubtitleObjects * Objects )
2007-10-12 14:52:30 +02:00
{
2013-09-06 12:37:27 +02:00
if ( regionFillFlag ) {
switch ( Bitmap - > Bpp ( ) ) {
case 2 : Bitmap - > Fill ( region2bitPixelCode ) ; break ;
case 4 : Bitmap - > Fill ( region4bitPixelCode ) ; break ;
case 8 : Bitmap - > Fill ( region8bitPixelCode ) ; break ;
default : dbgregions ( " unknown bpp %d (%s %d)<br> \n " , Bitmap - > Bpp ( ) , __FUNCTION__ , __LINE__ ) ;
}
2007-10-12 14:52:30 +02:00
}
2013-09-06 12:37:27 +02:00
for ( cSubtitleObjectRef * sor = objectRefs . First ( ) ; sor ; sor = objectRefs . Next ( sor ) ) {
if ( cSubtitleObject * so = Objects - > GetObjectById ( sor - > ObjectId ( ) ) ) {
so - > Render ( Bitmap , sor - > ObjectHorizontalPosition ( ) , sor - > ObjectVerticalPosition ( ) , sor - > ForegroundPixelCode ( ) , sor - > BackgroundPixelCode ( ) ) ;
2011-09-18 11:36:38 +02:00
}
}
}
2013-09-06 12:37:27 +02:00
// --- cSubtitleRegionRef ----------------------------------------------------
2007-10-12 14:52:30 +02:00
2013-09-06 12:37:27 +02:00
class cSubtitleRegionRef : public cListObject {
private :
int regionId ;
int regionHorizontalAddress ;
int regionVerticalAddress ;
public :
2015-01-14 10:39:55 +01:00
cSubtitleRegionRef ( int id , int x , int y ) ;
2013-09-06 12:37:27 +02:00
cSubtitleRegionRef ( cBitStream & bs ) ;
int RegionId ( void ) { return regionId ; }
int RegionHorizontalAddress ( void ) { return regionHorizontalAddress ; }
int RegionVerticalAddress ( void ) { return regionVerticalAddress ; }
} ;
2015-01-14 10:39:55 +01:00
cSubtitleRegionRef : : cSubtitleRegionRef ( int id , int x , int y )
{
regionId = id ;
regionHorizontalAddress = x ;
regionVerticalAddress = y ;
dbgpages ( " <b>regionref</b> id %d tx %d y %d<br> \n " , regionId , regionHorizontalAddress , regionVerticalAddress ) ;
}
2013-09-06 12:37:27 +02:00
cSubtitleRegionRef : : cSubtitleRegionRef ( cBitStream & bs )
2007-10-12 14:52:30 +02:00
{
2013-09-06 12:37:27 +02:00
regionId = bs . GetBits ( 8 ) ;
bs . SkipBits ( 8 ) ; // reserved
regionHorizontalAddress = bs . GetBits ( 16 ) ;
regionVerticalAddress = bs . GetBits ( 16 ) ;
dbgpages ( " <b>regionref</b> id %d tx %d y %d<br> \n " , regionId , regionHorizontalAddress , regionVerticalAddress ) ;
2007-10-12 14:52:30 +02:00
}
// --- cDvbSubtitlePage ------------------------------------------------------
class cDvbSubtitlePage : public cListObject {
private :
int pageId ;
2013-09-06 12:37:27 +02:00
int pageTimeout ;
int pageVersionNumber ;
int pageState ;
2007-10-12 14:52:30 +02:00
int64_t pts ;
2013-08-30 12:00:40 +02:00
bool pending ;
2013-09-06 12:37:27 +02:00
cSubtitleObjects objects ;
2007-10-12 14:52:30 +02:00
cList < cSubtitleClut > cluts ;
cList < cSubtitleRegion > regions ;
2013-09-06 12:37:27 +02:00
cList < cSubtitleRegionRef > regionRefs ;
public :
2007-10-12 14:52:30 +02:00
cDvbSubtitlePage ( int PageId ) ;
2013-09-06 12:37:27 +02:00
void Parse ( int64_t Pts , cBitStream & bs ) ;
2015-01-14 10:39:55 +01:00
void ParsePgs ( int64_t Pts , cBitStream & bs ) ;
2007-10-12 14:52:30 +02:00
int PageId ( void ) { return pageId ; }
2013-09-06 12:37:27 +02:00
int PageTimeout ( void ) { return pageTimeout ; }
int PageVersionNumber ( void ) { return pageVersionNumber ; }
int PageState ( void ) { return pageState ; }
2007-10-12 14:52:30 +02:00
int64_t Pts ( void ) const { return pts ; }
2013-08-30 12:00:40 +02:00
bool Pending ( void ) { return pending ; }
2013-09-06 12:37:27 +02:00
cSubtitleObjects * Objects ( void ) { return & objects ; }
2021-03-17 15:24:34 +01:00
tArea * GetAreas ( int & NumAreas ) ;
tArea CombineAreas ( int NumAreas , const tArea * Areas ) ;
tArea ScaleArea ( const tArea & Area , double FactorX , double FactorY ) ;
2013-09-06 12:37:27 +02:00
cSubtitleObject * GetObjectById ( int ObjectId , bool New = false ) ;
cSubtitleClut * GetClutById ( int ClutId , bool New = false ) ;
cSubtitleRegion * GetRegionById ( int RegionId , bool New = false ) ;
cSubtitleRegionRef * GetRegionRefByIndex ( int RegionRefIndex ) { return regionRefs . Get ( RegionRefIndex ) ; }
2015-01-14 10:39:55 +01:00
void AddRegionRef ( cSubtitleRegionRef * rf ) { regionRefs . Add ( rf ) ; }
2013-08-30 12:00:40 +02:00
void SetPending ( bool Pending ) { pending = Pending ; }
2007-10-12 14:52:30 +02:00
} ;
cDvbSubtitlePage : : cDvbSubtitlePage ( int PageId )
{
pageId = PageId ;
2013-09-06 12:37:27 +02:00
pageTimeout = 0 ;
pageVersionNumber = - 1 ;
pageState = - 1 ;
2013-08-30 12:00:40 +02:00
pts = - 1 ;
pending = false ;
2007-10-12 14:52:30 +02:00
}
2013-09-06 12:37:27 +02:00
void cDvbSubtitlePage : : Parse ( int64_t Pts , cBitStream & bs )
2007-10-12 14:52:30 +02:00
{
2013-09-06 12:37:27 +02:00
if ( Pts > = 0 )
pts = Pts ;
pageTimeout = bs . GetBits ( 8 ) ;
int Version = bs . GetBits ( 4 ) ;
2015-01-09 12:03:31 +01:00
# ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
2013-09-06 12:37:27 +02:00
if ( pageVersionNumber = = Version )
return ; // no update
2015-01-09 12:03:31 +01:00
# endif
2013-09-06 12:37:27 +02:00
pageVersionNumber = Version ;
pageState = bs . GetBits ( 2 ) ;
switch ( pageState ) {
case 0 : // normal case - page update
break ;
case 1 : // acquisition point - page refresh
regions . Clear ( ) ;
objects . Clear ( ) ;
break ;
case 2 : // mode change - new page
regions . Clear ( ) ;
cluts . Clear ( ) ;
objects . Clear ( ) ;
break ;
case 3 : // reserved
break ;
default : dbgpages ( " unknown page state: %d<br> \n " , pageState ) ;
}
bs . SkipBits ( 2 ) ; // reserved
2015-01-20 14:56:18 +01:00
dbgpages ( " <hr> \n <b>page</b> id %d version %d pts % " PRId64 " timeout %d state %d<br> \n " , pageId , pageVersionNumber , pts , pageTimeout , pageState ) ;
2013-09-06 12:37:27 +02:00
regionRefs . Clear ( ) ;
while ( ! bs . IsEOF ( ) )
regionRefs . Add ( new cSubtitleRegionRef ( bs ) ) ;
pending = true ;
2007-10-12 14:52:30 +02:00
}
2015-01-14 10:39:55 +01:00
void cDvbSubtitlePage : : ParsePgs ( int64_t Pts , cBitStream & bs )
{
if ( Pts > = 0 )
pts = Pts ;
pageTimeout = 60000 ;
int Version = bs . GetBits ( 16 ) ;
if ( pageVersionNumber = = Version )
return ;
pageVersionNumber = Version ;
pageState = bs . GetBits ( 2 ) ;
switch ( pageState ) {
case 0 : // normal case - page update
regions . Clear ( ) ;
break ;
case 1 : // acquisition point - page refresh
case 2 : // epoch start - new page
case 3 : // epoch continue - new page
regions . Clear ( ) ;
cluts . Clear ( ) ;
objects . Clear ( ) ;
break ;
default : dbgpages ( " unknown page state: %d<br> \n " , pageState ) ;
}
bs . SkipBits ( 6 ) ;
2015-01-20 14:56:18 +01:00
dbgpages ( " <hr> \n <b>page</b> id %d version %d pts % " PRId64 " timeout %d state %d<br> \n " , pageId , pageVersionNumber , pts , pageTimeout , pageState ) ;
2015-01-14 10:39:55 +01:00
regionRefs . Clear ( ) ;
pending = true ;
}
2021-03-17 15:24:34 +01:00
tArea * cDvbSubtitlePage : : GetAreas ( int & NumAreas )
2007-10-12 14:52:30 +02:00
{
if ( regions . Count ( ) > 0 ) {
2013-09-06 12:37:27 +02:00
NumAreas = regionRefs . Count ( ) ;
tArea * Areas = new tArea [ NumAreas ] ;
2007-10-12 14:52:30 +02:00
tArea * a = Areas ;
2013-09-06 12:37:27 +02:00
for ( cSubtitleRegionRef * srr = regionRefs . First ( ) ; srr ; srr = regionRefs . Next ( srr ) ) {
if ( cSubtitleRegion * sr = GetRegionById ( srr - > RegionId ( ) ) ) {
2021-03-17 15:24:34 +01:00
a - > x1 = srr - > RegionHorizontalAddress ( ) ;
a - > y1 = srr - > RegionVerticalAddress ( ) ;
a - > x2 = srr - > RegionHorizontalAddress ( ) + sr - > RegionWidth ( ) - 1 ;
a - > y2 = srr - > RegionVerticalAddress ( ) + sr - > RegionHeight ( ) - 1 ;
2013-09-06 12:37:27 +02:00
a - > bpp = sr - > RegionDepth ( ) ;
}
else
a - > x1 = a - > y1 = a - > x2 = a - > y2 = a - > bpp = 0 ;
2007-10-12 14:52:30 +02:00
a + + ;
}
return Areas ;
}
2013-09-06 12:37:27 +02:00
NumAreas = 0 ;
2007-10-12 14:52:30 +02:00
return NULL ;
}
2021-03-17 15:24:34 +01:00
tArea cDvbSubtitlePage : : CombineAreas ( int NumAreas , const tArea * Areas )
{
tArea a ;
a . x1 = INT_MAX ;
a . x2 = INT_MIN ;
a . y1 = INT_MAX ;
a . y2 = INT_MIN ;
a . bpp = 1 ;
for ( int i = 0 ; i < NumAreas ; i + + ) {
a . x1 = min ( a . x1 , Areas [ i ] . x1 ) ;
a . x2 = max ( a . x2 , Areas [ i ] . x2 ) ;
a . y1 = min ( a . y1 , Areas [ i ] . y1 ) ;
a . y2 = max ( a . y2 , Areas [ i ] . y2 ) ;
a . bpp = max ( a . bpp , Areas [ i ] . bpp ) ;
}
return a ;
}
tArea cDvbSubtitlePage : : ScaleArea ( const tArea & Area , double FactorX , double FactorY )
{
tArea a ;
a . x1 = int ( round ( FactorX * Area . x1 ) ) ;
a . x2 = int ( round ( FactorX * Area . x2 ) - 1 ) ;
a . y1 = int ( round ( FactorY * Area . y1 ) ) ;
a . y2 = int ( round ( FactorY * Area . y2 ) - 1 ) ;
a . bpp = Area . bpp ;
while ( ( a . Width ( ) & 3 ) ! = 0 )
a . x2 + + ; // aligns width to a multiple of 4, so 2, 4 and 8 bpp will work
return a ;
}
2007-10-12 14:52:30 +02:00
cSubtitleClut * cDvbSubtitlePage : : GetClutById ( int ClutId , bool New )
{
for ( cSubtitleClut * sc = cluts . First ( ) ; sc ; sc = cluts . Next ( sc ) ) {
if ( sc - > ClutId ( ) = = ClutId )
2013-09-06 12:37:27 +02:00
return sc ;
2007-10-12 14:52:30 +02:00
}
2013-09-06 12:37:27 +02:00
if ( ! New )
return NULL ;
cSubtitleClut * Clut = new cSubtitleClut ( ClutId ) ;
cluts . Add ( Clut ) ;
return Clut ;
2007-10-12 14:52:30 +02:00
}
cSubtitleRegion * cDvbSubtitlePage : : GetRegionById ( int RegionId , bool New )
{
for ( cSubtitleRegion * sr = regions . First ( ) ; sr ; sr = regions . Next ( sr ) ) {
if ( sr - > RegionId ( ) = = RegionId )
2013-09-06 12:37:27 +02:00
return sr ;
2007-10-12 14:52:30 +02:00
}
2013-09-06 12:37:27 +02:00
if ( ! New )
return NULL ;
cSubtitleRegion * Region = new cSubtitleRegion ( RegionId ) ;
regions . Add ( Region ) ;
return Region ;
2007-10-12 14:52:30 +02:00
}
2013-09-06 12:37:27 +02:00
cSubtitleObject * cDvbSubtitlePage : : GetObjectById ( int ObjectId , bool New )
2007-10-12 14:52:30 +02:00
{
2013-09-06 12:37:27 +02:00
return objects . GetObjectById ( ObjectId , New ) ;
2007-10-12 14:52:30 +02:00
}
// --- cDvbSubtitleAssembler -------------------------------------------------
class cDvbSubtitleAssembler {
private :
uchar * data ;
int length ;
int pos ;
int size ;
bool Realloc ( int Size ) ;
public :
cDvbSubtitleAssembler ( void ) ;
virtual ~ cDvbSubtitleAssembler ( ) ;
void Reset ( void ) ;
unsigned char * Get ( int & Length ) ;
void Put ( const uchar * Data , int Length ) ;
} ;
cDvbSubtitleAssembler : : cDvbSubtitleAssembler ( void )
{
data = NULL ;
size = 0 ;
Reset ( ) ;
}
cDvbSubtitleAssembler : : ~ cDvbSubtitleAssembler ( )
{
free ( data ) ;
}
void cDvbSubtitleAssembler : : Reset ( void )
{
length = 0 ;
pos = 0 ;
}
bool cDvbSubtitleAssembler : : Realloc ( int Size )
{
if ( Size > size ) {
2011-02-25 15:25:42 +01:00
Size = max ( Size , 2048 ) ;
if ( uchar * NewBuffer = ( uchar * ) realloc ( data , Size ) ) {
size = Size ;
data = NewBuffer ;
}
else {
2007-10-12 14:52:30 +02:00
esyslog ( " ERROR: can't allocate memory for subtitle assembler " ) ;
length = 0 ;
size = 0 ;
2011-02-25 15:25:42 +01:00
free ( data ) ;
data = NULL ;
2007-10-12 14:52:30 +02:00
return false ;
}
}
return true ;
}
unsigned char * cDvbSubtitleAssembler : : Get ( int & Length )
{
if ( length > pos + 5 ) {
2008-08-15 14:49:34 +02:00
Length = ( data [ pos + 4 ] < < 8 ) + data [ pos + 5 ] + 6 ;
if ( length > = pos + Length ) {
unsigned char * result = data + pos ;
pos + = Length ;
return result ;
}
2007-10-12 14:52:30 +02:00
}
return NULL ;
}
void cDvbSubtitleAssembler : : Put ( const uchar * Data , int Length )
{
if ( Length & & Realloc ( length + Length ) ) {
memcpy ( data + length , Data , Length ) ;
length + = Length ;
}
}
// --- cDvbSubtitleBitmaps ---------------------------------------------------
class cDvbSubtitleBitmaps : public cListObject {
private :
2013-08-30 12:00:40 +02:00
int state ;
2007-10-12 14:52:30 +02:00
int64_t pts ;
int timeout ;
tArea * areas ;
int numAreas ;
2021-03-17 15:24:34 +01:00
tArea areaCombined ;
tArea areaOsd ;
2011-03-20 15:19:28 +01:00
double osdFactorX ;
double osdFactorY ;
2007-10-12 14:52:30 +02:00
cVector < cBitmap * > bitmaps ;
public :
2021-03-17 15:24:34 +01:00
cDvbSubtitleBitmaps ( int State , int64_t Pts , int Timeout , tArea * Areas , int NumAreas , double OsdFactorX , double OsdFactorY , tArea & AreaCombined , tArea & AreaOsd ) ;
2007-10-12 14:52:30 +02:00
~ cDvbSubtitleBitmaps ( ) ;
2013-08-30 12:00:40 +02:00
int State ( void ) { return state ; }
2007-10-12 14:52:30 +02:00
int64_t Pts ( void ) { return pts ; }
int Timeout ( void ) { return timeout ; }
void AddBitmap ( cBitmap * Bitmap ) ;
2013-09-06 12:37:27 +02:00
bool HasBitmaps ( void ) { return bitmaps . Size ( ) ; }
2007-10-12 14:52:30 +02:00
void Draw ( cOsd * Osd ) ;
2013-09-06 12:37:27 +02:00
void DbgDump ( int WindowWidth , int WindowHeight ) ;
2007-10-12 14:52:30 +02:00
} ;
2021-03-17 15:24:34 +01:00
cDvbSubtitleBitmaps : : cDvbSubtitleBitmaps ( int State , int64_t Pts , int Timeout , tArea * Areas , int NumAreas , double OsdFactorX , double OsdFactorY , tArea & AreaCombined , tArea & AreaOsd )
2007-10-12 14:52:30 +02:00
{
2013-08-30 12:00:40 +02:00
state = State ;
2007-10-12 14:52:30 +02:00
pts = Pts ;
timeout = Timeout ;
areas = Areas ;
numAreas = NumAreas ;
2021-03-17 15:24:34 +01:00
areaCombined = AreaCombined ;
areaOsd = AreaOsd ;
2011-03-20 15:19:28 +01:00
osdFactorX = OsdFactorX ;
osdFactorY = OsdFactorY ;
2007-10-12 14:52:30 +02:00
}
cDvbSubtitleBitmaps : : ~ cDvbSubtitleBitmaps ( )
{
delete [ ] areas ;
for ( int i = 0 ; i < bitmaps . Size ( ) ; i + + )
delete bitmaps [ i ] ;
}
void cDvbSubtitleBitmaps : : AddBitmap ( cBitmap * Bitmap )
{
bitmaps . Append ( Bitmap ) ;
}
void cDvbSubtitleBitmaps : : Draw ( cOsd * Osd )
{
2011-04-17 14:40:58 +02:00
bool Scale = ! ( DoubleEqual ( osdFactorX , 1.0 ) & & DoubleEqual ( osdFactorY , 1.0 ) ) ;
2021-03-17 15:24:34 +01:00
bool AntiAlias = Setup . AntiAlias ;
2011-04-17 14:40:58 +02:00
if ( Scale & & osdFactorX > 1.0 | | osdFactorY > 1.0 ) {
// Upscaling requires 8bpp:
2021-03-17 15:24:34 +01:00
int Bpp = areaOsd . bpp ;
areaOsd . bpp = 8 ;
if ( Osd - > CanHandleAreas ( & areaOsd , 1 ) ! = oeOk ) {
areaOsd . bpp = Bpp ;
2011-04-17 14:40:58 +02:00
AntiAlias = false ;
}
}
2021-03-17 15:24:34 +01:00
if ( State ( ) = = 0 | | Osd - > SetAreas ( & areaOsd , 1 ) = = oeOk ) {
cBitmap combined ( areaCombined . Width ( ) , areaCombined . Height ( ) , areaCombined . bpp ) ;
combined . SetOffset ( areaCombined . x1 , areaCombined . y1 ) ;
2011-03-12 16:08:08 +01:00
for ( int i = 0 ; i < bitmaps . Size ( ) ; i + + ) {
2021-03-17 15:24:34 +01:00
// merge bitmaps into combined
2011-03-12 16:08:08 +01:00
cBitmap * b = bitmaps [ i ] ;
2021-03-17 15:24:34 +01:00
combined . DrawBitmap ( b - > X0 ( ) , b - > Y0 ( ) , * b ) ;
2011-03-12 16:08:08 +01:00
}
2021-03-17 15:24:34 +01:00
Osd - > DrawScaledBitmap ( int ( round ( combined . X0 ( ) * osdFactorX ) ) , int ( round ( combined . Y0 ( ) * osdFactorY ) ) , combined , osdFactorX , osdFactorY , AntiAlias ) ;
2007-10-12 14:52:30 +02:00
Osd - > Flush ( ) ;
}
}
2013-09-06 12:37:27 +02:00
void cDvbSubtitleBitmaps : : DbgDump ( int WindowWidth , int WindowHeight )
2013-08-30 12:00:40 +02:00
{
2013-09-06 12:37:27 +02:00
if ( ! SD . Active ( ) )
2013-08-30 12:00:40 +02:00
return ;
2013-09-06 12:37:27 +02:00
SD . SetFirstPts ( Pts ( ) ) ;
double STC = double ( cDevice : : PrimaryDevice ( ) - > GetSTC ( ) - SD . FirstPts ( ) ) / 90000 ;
double Start = double ( Pts ( ) - SD . FirstPts ( ) ) / 90000 ;
double Duration = Timeout ( ) ;
2013-08-30 12:00:40 +02:00
double End = Start + Duration ;
2013-09-06 12:37:27 +02:00
cBitmap Bitmap ( WindowWidth , WindowHeight , 8 ) ;
# define DBGBACKGROUND 0xA0A0A0
Bitmap . DrawRectangle ( 0 , 0 , WindowWidth - 1 , WindowHeight - 1 , DBGBACKGROUND ) ;
for ( int i = 0 ; i < bitmaps . Size ( ) ; i + + ) {
cBitmap * b = bitmaps [ i ] ;
Bitmap . DrawBitmap ( b - > X0 ( ) , b - > Y0 ( ) , * b ) ;
}
cString ImgName = SD . WriteJpeg ( & Bitmap ) ;
2013-08-30 12:00:40 +02:00
# define BORDER //" border=1"
2013-09-06 12:37:27 +02:00
SD . WriteHtml ( " <p>%s<br> " , State ( ) = = 0 ? " page update " : State ( ) = = 1 ? " page refresh " : State ( ) = = 2 ? " new page " : " ??? " ) ;
SD . WriteHtml ( " <table " BORDER " ><tr><td> " ) ;
SD . WriteHtml ( " %.2f " , STC ) ;
SD . WriteHtml ( " </td><td> " ) ;
SD . WriteHtml ( " <img src= \" %s \" > " , * ImgName ) ;
SD . WriteHtml ( " </td><td style= \" height:100%% \" ><table " BORDER " style= \" height:100%% \" > " ) ;
SD . WriteHtml ( " <tr><td valign=top><b>%.2f</b></td></tr> " , Start ) ;
SD . WriteHtml ( " <tr><td valign=middle>%.2f</td></tr> " , Duration ) ;
SD . WriteHtml ( " <tr><td valign=bottom>%.2f</td></tr> " , End ) ;
SD . WriteHtml ( " </table></td> " ) ;
SD . WriteHtml ( " </tr></table> \n " ) ;
2013-08-30 12:00:40 +02:00
}
2007-10-12 14:52:30 +02:00
// --- cDvbSubtitleConverter -------------------------------------------------
int cDvbSubtitleConverter : : setupLevel = 0 ;
cDvbSubtitleConverter : : cDvbSubtitleConverter ( void )
2015-01-14 11:39:55 +01:00
: cThread ( " subtitle converter " )
2007-10-12 14:52:30 +02:00
{
dvbSubtitleAssembler = new cDvbSubtitleAssembler ;
osd = NULL ;
2010-02-07 12:08:13 +01:00
frozen = false ;
2010-08-29 14:10:47 +02:00
ddsVersionNumber = - 1 ;
2011-03-12 13:09:30 +01:00
displayWidth = windowWidth = 720 ;
displayHeight = windowHeight = 576 ;
windowHorizontalOffset = 0 ;
windowVerticalOffset = 0 ;
2007-10-12 14:52:30 +02:00
pages = new cList < cDvbSubtitlePage > ;
bitmaps = new cList < cDvbSubtitleBitmaps > ;
2013-09-06 12:37:27 +02:00
SD . Reset ( ) ;
2007-10-12 14:52:30 +02:00
Start ( ) ;
}
cDvbSubtitleConverter : : ~ cDvbSubtitleConverter ( )
{
Cancel ( 3 ) ;
delete dvbSubtitleAssembler ;
delete osd ;
delete bitmaps ;
delete pages ;
}
void cDvbSubtitleConverter : : SetupChanged ( void )
{
setupLevel + + ;
}
void cDvbSubtitleConverter : : Reset ( void )
{
2013-09-06 12:37:27 +02:00
dbgconverter ( " converter reset -----------------------<br> \n " ) ;
2007-10-12 14:52:30 +02:00
dvbSubtitleAssembler - > Reset ( ) ;
Lock ( ) ;
pages - > Clear ( ) ;
bitmaps - > Clear ( ) ;
DELETENULL ( osd ) ;
2010-02-07 12:08:13 +01:00
frozen = false ;
2010-08-29 14:10:47 +02:00
ddsVersionNumber = - 1 ;
2011-03-12 13:09:30 +01:00
displayWidth = windowWidth = 720 ;
displayHeight = windowHeight = 576 ;
windowHorizontalOffset = 0 ;
windowVerticalOffset = 0 ;
2007-10-12 14:52:30 +02:00
Unlock ( ) ;
}
2008-08-15 14:49:34 +02:00
int cDvbSubtitleConverter : : ConvertFragments ( const uchar * Data , int Length )
2007-10-12 14:52:30 +02:00
{
if ( Data & & Length > 8 ) {
2008-08-15 14:49:34 +02:00
int PayloadOffset = PesPayloadOffset ( Data ) ;
2007-10-12 14:52:30 +02:00
int SubstreamHeaderLength = 4 ;
bool ResetSubtitleAssembler = Data [ PayloadOffset + 3 ] = = 0x00 ;
// Compatibility mode for old subtitles plugin:
2007-11-03 14:39:25 +01:00
if ( ( Data [ 7 ] & 0x01 ) & & ( Data [ PayloadOffset - 3 ] & 0x81 ) = = 0x01 & & Data [ PayloadOffset - 2 ] = = 0x81 ) {
2007-10-12 14:52:30 +02:00
PayloadOffset - - ;
SubstreamHeaderLength = 1 ;
ResetSubtitleAssembler = Data [ 8 ] > = 5 ;
}
if ( Length > PayloadOffset + SubstreamHeaderLength ) {
2013-08-30 12:00:40 +02:00
int64_t pts = PesHasPts ( Data ) ? PesGetPts ( Data ) : - 1 ;
if ( pts > = 0 )
2015-01-20 14:56:18 +01:00
dbgconverter ( " converter PTS: % " PRId64 " <br> \n " , pts ) ;
2007-10-12 14:52:30 +02:00
const uchar * data = Data + PayloadOffset + SubstreamHeaderLength ; // skip substream header
int length = Length - PayloadOffset - SubstreamHeaderLength ; // skip substream header
if ( ResetSubtitleAssembler )
dvbSubtitleAssembler - > Reset ( ) ;
if ( length > 3 ) {
if ( data [ 0 ] = = 0x20 & & data [ 1 ] = = 0x00 & & data [ 2 ] = = 0x0F )
dvbSubtitleAssembler - > Put ( data + 2 , length - 2 ) ;
else
dvbSubtitleAssembler - > Put ( data , length ) ;
int Count ;
while ( true ) {
unsigned char * b = dvbSubtitleAssembler - > Get ( Count ) ;
if ( b & & b [ 0 ] = = 0x0F ) {
if ( ExtractSegment ( b , Count , pts ) = = - 1 )
break ;
}
else
break ;
}
}
}
2007-11-03 14:39:25 +01:00
return Length ;
2007-10-12 14:52:30 +02:00
}
return 0 ;
}
2008-08-15 14:49:34 +02:00
int cDvbSubtitleConverter : : Convert ( const uchar * Data , int Length )
{
if ( Data & & Length > 8 ) {
int PayloadOffset = PesPayloadOffset ( Data ) ;
if ( Length > PayloadOffset ) {
2013-08-30 12:00:40 +02:00
int64_t pts = PesHasPts ( Data ) ? PesGetPts ( Data ) : - 1 ;
if ( pts > = 0 )
2015-01-20 14:56:18 +01:00
dbgconverter ( " converter PTS: % " PRId64 " <br> \n " , pts ) ;
2008-08-15 14:49:34 +02:00
const uchar * data = Data + PayloadOffset ;
int length = Length - PayloadOffset ;
2015-01-14 10:39:55 +01:00
if ( length > 0 ) {
if ( length > 2 & & data [ 0 ] = = 0x20 & & data [ 1 ] = = 0x00 & & data [ 2 ] = = 0x0F ) {
2008-08-15 14:49:34 +02:00
data + = 2 ;
length - = 2 ;
}
const uchar * b = data ;
while ( length > 0 ) {
2015-01-14 10:39:55 +01:00
if ( b [ 0 ] = = STUFFING_SEGMENT )
break ;
int n ;
if ( b [ 0 ] = = 0x0F )
n = ExtractSegment ( b , length , pts ) ;
2008-08-15 14:49:34 +02:00
else
2015-01-14 10:39:55 +01:00
n = ExtractPgsSegment ( b , length , pts ) ;
if ( n < 0 )
2008-08-15 14:49:34 +02:00
break ;
2015-01-14 10:39:55 +01:00
b + = n ;
length - = n ;
2008-08-15 14:49:34 +02:00
}
}
}
return Length ;
}
return 0 ;
}
2010-10-24 12:29:04 +02:00
# define LimitTo32Bit(n) ((n) & 0x00000000FFFFFFFFL)
2007-10-12 14:52:30 +02:00
void cDvbSubtitleConverter : : Action ( void )
{
int LastSetupLevel = setupLevel ;
cTimeMs Timeout ;
while ( Running ( ) ) {
int WaitMs = 100 ;
2010-02-07 12:08:13 +01:00
if ( ! frozen ) {
2011-08-13 13:34:32 +02:00
LOCK_THREAD ;
2010-02-07 12:08:13 +01:00
if ( osd ) {
int NewSetupLevel = setupLevel ;
if ( Timeout . TimedOut ( ) | | LastSetupLevel ! = NewSetupLevel ) {
2013-09-06 12:37:27 +02:00
dbgoutput ( " closing osd<br> \n " ) ;
2010-02-07 12:08:13 +01:00
DELETENULL ( osd ) ;
}
LastSetupLevel = NewSetupLevel ;
2007-10-12 14:52:30 +02:00
}
2013-08-30 12:00:40 +02:00
for ( cDvbSubtitleBitmaps * sb = bitmaps - > First ( ) ; sb ; sb = bitmaps - > Next ( sb ) ) {
// Calculate the Delta between the STC (the current timestamp of the video)
// and the bitmap's PTS (the timestamp when the bitmap shall be presented).
// A negative Delta means that the bitmap will be presented in the future:
int64_t STC = cDevice : : PrimaryDevice ( ) - > GetSTC ( ) ;
int64_t Delta = LimitTo32Bit ( STC ) - LimitTo32Bit ( sb - > Pts ( ) ) ; // some devices only deliver 32 bits
if ( Delta > ( int64_t ( 1 ) < < 31 ) )
Delta - = ( int64_t ( 1 ) < < 32 ) ;
else if ( Delta < - ( ( int64_t ( 1 ) < < 31 ) - 1 ) )
Delta + = ( int64_t ( 1 ) < < 32 ) ;
Delta / = 90 ; // STC and PTS are in 1/90000s
if ( Delta > = 0 ) { // found a bitmap that shall be displayed...
if ( Delta < sb - > Timeout ( ) * 1000 ) { // ...and has not timed out yet
2013-09-06 12:37:27 +02:00
if ( ! sb - > HasBitmaps ( ) ) {
Timeout . Set ( ) ;
WaitMs = 0 ;
}
else if ( AssertOsd ( ) ) {
dbgoutput ( " showing bitmap #%d of %d<br> \n " , sb - > Index ( ) + 1 , bitmaps - > Count ( ) ) ;
2013-08-30 12:00:40 +02:00
sb - > Draw ( osd ) ;
Timeout . Set ( sb - > Timeout ( ) * 1000 ) ;
2015-01-20 14:56:18 +01:00
dbgconverter ( " PTS: % " PRId64 " STC: % " PRId64 " (% " PRId64 " ) timeout: %d<br> \n " , sb - > Pts ( ) , STC , Delta , sb - > Timeout ( ) ) ;
2013-08-30 12:00:40 +02:00
}
}
else
WaitMs = 0 ; // bitmap already timed out, so try next one immediately
2013-09-06 12:37:27 +02:00
dbgoutput ( " deleting bitmap #%d of %d<br> \n " , sb - > Index ( ) + 1 , bitmaps - > Count ( ) ) ;
2013-08-30 12:00:40 +02:00
bitmaps - > Del ( sb ) ;
break ;
}
}
2007-10-12 14:52:30 +02:00
}
cCondWait : : SleepMs ( WaitMs ) ;
}
}
2011-03-12 16:08:08 +01:00
void cDvbSubtitleConverter : : SetOsdData ( void )
{
2011-03-20 15:19:28 +01:00
int OsdWidth , OsdHeight ;
2011-03-12 16:08:08 +01:00
double OsdAspect ;
2011-03-20 15:19:28 +01:00
int VideoWidth , VideoHeight ;
double VideoAspect ;
2011-03-12 16:08:08 +01:00
cDevice : : PrimaryDevice ( ) - > GetOsdSize ( OsdWidth , OsdHeight , OsdAspect ) ;
2011-03-20 15:19:28 +01:00
cDevice : : PrimaryDevice ( ) - > GetVideoSize ( VideoWidth , VideoHeight , VideoAspect ) ;
2012-02-25 14:47:44 +01:00
if ( OsdWidth = = displayWidth & & OsdHeight = = displayHeight ) {
2011-03-27 14:12:58 +02:00
osdFactorX = osdFactorY = 1.0 ;
osdDeltaX = osdDeltaY = 0 ;
}
else {
2015-03-25 12:39:01 +01:00
osdFactorX = osdFactorY = min ( double ( OsdWidth ) / displayWidth , double ( OsdHeight ) / displayHeight ) ;
2011-03-27 14:12:58 +02:00
osdDeltaX = ( OsdWidth - displayWidth * osdFactorX ) / 2 ;
osdDeltaY = ( OsdHeight - displayHeight * osdFactorY ) / 2 ;
}
2011-03-12 16:08:08 +01:00
}
2007-10-12 14:52:30 +02:00
bool cDvbSubtitleConverter : : AssertOsd ( void )
{
2011-08-13 13:34:32 +02:00
LOCK_THREAD ;
2012-02-24 11:27:04 +01:00
if ( ! osd ) {
SetOsdData ( ) ;
osd = cOsdProvider : : NewOsd ( int ( round ( osdFactorX * windowHorizontalOffset + osdDeltaX ) ) , int ( round ( osdFactorY * windowVerticalOffset + osdDeltaY ) ) + Setup . SubtitleOffset , OSD_LEVEL_SUBTITLES ) ;
}
return osd ! = NULL ;
2007-10-12 14:52:30 +02:00
}
2013-09-06 12:37:27 +02:00
cDvbSubtitlePage * cDvbSubtitleConverter : : GetPageById ( int PageId , bool New )
{
for ( cDvbSubtitlePage * sp = pages - > First ( ) ; sp ; sp = pages - > Next ( sp ) ) {
if ( sp - > PageId ( ) = = PageId )
return sp ;
}
if ( ! New )
return NULL ;
cDvbSubtitlePage * Page = new cDvbSubtitlePage ( PageId ) ;
pages - > Add ( Page ) ;
return Page ;
}
2007-10-12 14:52:30 +02:00
int cDvbSubtitleConverter : : ExtractSegment ( const uchar * Data , int Length , int64_t Pts )
{
2011-09-18 11:36:38 +02:00
cBitStream bs ( Data , Length * 8 ) ;
if ( Length > 5 & & bs . GetBits ( 8 ) = = 0x0F ) { // sync byte
int segmentType = bs . GetBits ( 8 ) ;
if ( segmentType = = STUFFING_SEGMENT )
return - 1 ;
2013-09-06 12:37:27 +02:00
LOCK_THREAD ;
cDvbSubtitlePage * page = GetPageById ( bs . GetBits ( 16 ) , true ) ;
2011-09-18 11:36:38 +02:00
int segmentLength = bs . GetBits ( 16 ) ;
if ( ! bs . SetLength ( bs . Index ( ) + segmentLength * 8 ) )
2007-10-12 14:52:30 +02:00
return - 1 ;
switch ( segmentType ) {
case PAGE_COMPOSITION_SEGMENT : {
2013-08-30 12:00:40 +02:00
if ( page - > Pending ( ) ) {
2013-09-06 12:37:27 +02:00
dbgsegments ( " END_OF_DISPLAY_SET_SEGMENT (simulated)<br> \n " ) ;
2013-08-30 12:00:40 +02:00
FinishPage ( page ) ;
}
2013-09-06 12:37:27 +02:00
dbgsegments ( " PAGE_COMPOSITION_SEGMENT<br> \n " ) ;
page - > Parse ( Pts , bs ) ;
SD . SetFactor ( double ( DBGBITMAPWIDTH ) / windowWidth ) ;
2007-10-12 14:52:30 +02:00
break ;
}
case REGION_COMPOSITION_SEGMENT : {
2013-09-06 12:37:27 +02:00
dbgsegments ( " REGION_COMPOSITION_SEGMENT<br> \n " ) ;
cSubtitleRegion * region = page - > GetRegionById ( bs . GetBits ( 8 ) , true ) ;
region - > Parse ( bs ) ;
2007-10-12 14:52:30 +02:00
break ;
}
case CLUT_DEFINITION_SEGMENT : {
2013-09-06 12:37:27 +02:00
dbgsegments ( " CLUT_DEFINITION_SEGMENT<br> \n " ) ;
2012-02-22 11:25:57 +01:00
cSubtitleClut * clut = page - > GetClutById ( bs . GetBits ( 8 ) , true ) ;
2013-09-06 12:37:27 +02:00
clut - > Parse ( bs ) ;
2007-10-12 14:52:30 +02:00
break ;
}
case OBJECT_DATA_SEGMENT : {
2013-09-06 12:37:27 +02:00
dbgsegments ( " OBJECT_DATA_SEGMENT<br> \n " ) ;
cSubtitleObject * object = page - > GetObjectById ( bs . GetBits ( 16 ) , true ) ;
object - > Parse ( bs ) ;
2007-10-12 14:52:30 +02:00
break ;
}
2010-05-13 14:51:48 +02:00
case DISPLAY_DEFINITION_SEGMENT : {
2013-09-06 12:37:27 +02:00
dbgsegments ( " DISPLAY_DEFINITION_SEGMENT<br> \n " ) ;
2011-09-18 11:36:38 +02:00
int version = bs . GetBits ( 4 ) ;
2015-01-09 12:03:31 +01:00
# ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
if ( version = = ddsVersionNumber )
break ; // no update
# endif
bool displayWindowFlag = bs . GetBit ( ) ;
windowHorizontalOffset = 0 ;
windowVerticalOffset = 0 ;
bs . SkipBits ( 3 ) ; // reserved
displayWidth = windowWidth = bs . GetBits ( 16 ) + 1 ;
displayHeight = windowHeight = bs . GetBits ( 16 ) + 1 ;
if ( displayWindowFlag ) {
windowHorizontalOffset = bs . GetBits ( 16 ) ; // displayWindowHorizontalPositionMinimum
windowWidth = bs . GetBits ( 16 ) - windowHorizontalOffset + 1 ; // displayWindowHorizontalPositionMaximum
windowVerticalOffset = bs . GetBits ( 16 ) ; // displayWindowVerticalPositionMinimum
windowHeight = bs . GetBits ( 16 ) - windowVerticalOffset + 1 ; // displayWindowVerticalPositionMaximum
2010-05-13 14:51:48 +02:00
}
2015-01-09 12:03:31 +01:00
SetOsdData ( ) ;
ddsVersionNumber = version ;
dbgdisplay ( " <b>display</b> version %d flag %d width %d height %d ofshor %d ofsver %d<br> \n " , ddsVersionNumber , displayWindowFlag , windowWidth , windowHeight , windowHorizontalOffset , windowVerticalOffset ) ;
2010-05-13 14:51:48 +02:00
break ;
}
2011-09-18 11:36:38 +02:00
case DISPARITY_SIGNALING_SEGMENT : {
2013-09-06 12:37:27 +02:00
dbgsegments ( " DISPARITY_SIGNALING_SEGMENT<br> \n " ) ;
2011-09-18 11:36:38 +02:00
bs . SkipBits ( 4 ) ; // dss_version_number
bool disparity_shift_update_sequence_page_flag = bs . GetBit ( ) ;
bs . SkipBits ( 3 ) ; // reserved
bs . SkipBits ( 8 ) ; // page_default_disparity_shift
if ( disparity_shift_update_sequence_page_flag ) {
bs . SkipBits ( 8 ) ; // disparity_shift_update_sequence_length
bs . SkipBits ( 24 ) ; // interval_duration[23..0]
int division_period_count = bs . GetBits ( 8 ) ;
for ( int i = 0 ; i < division_period_count ; + + i ) {
bs . SkipBits ( 8 ) ; // interval_count
bs . SkipBits ( 8 ) ; // disparity_shift_update_integer_part
}
}
while ( ! bs . IsEOF ( ) ) {
bs . SkipBits ( 8 ) ; // region_id
bool disparity_shift_update_sequence_region_flag = bs . GetBit ( ) ;
bs . SkipBits ( 5 ) ; // reserved
int number_of_subregions_minus_1 = bs . GetBits ( 2 ) ;
for ( int i = 0 ; i < = number_of_subregions_minus_1 ; + + i ) {
if ( number_of_subregions_minus_1 > 0 ) {
bs . SkipBits ( 16 ) ; // subregion_horizontal_position
bs . SkipBits ( 16 ) ; // subregion_width
}
bs . SkipBits ( 8 ) ; // subregion_disparity_shift_integer_part
bs . SkipBits ( 4 ) ; // subregion_disparity_shift_fractional_part
bs . SkipBits ( 4 ) ; // reserved
if ( disparity_shift_update_sequence_region_flag ) {
bs . SkipBits ( 8 ) ; // disparity_shift_update_sequence_length
bs . SkipBits ( 24 ) ; // interval_duration[23..0]
int division_period_count = bs . GetBits ( 8 ) ;
for ( int i = 0 ; i < division_period_count ; + + i ) {
bs . SkipBits ( 8 ) ; // interval_count
bs . SkipBits ( 8 ) ; // disparity_shift_update_integer_part
}
}
}
}
break ;
}
2007-10-12 14:52:30 +02:00
case END_OF_DISPLAY_SET_SEGMENT : {
2013-09-06 12:37:27 +02:00
dbgsegments ( " END_OF_DISPLAY_SET_SEGMENT<br> \n " ) ;
2007-10-12 14:52:30 +02:00
FinishPage ( page ) ;
2013-08-30 12:00:40 +02:00
page - > SetPending ( false ) ;
2007-11-03 14:39:25 +01:00
break ;
2011-09-18 11:36:38 +02:00
}
2007-10-12 14:52:30 +02:00
default :
2013-09-06 12:37:27 +02:00
dbgsegments ( " *** unknown segment type: %02X<br> \n " , segmentType ) ;
2007-10-12 14:52:30 +02:00
}
2011-09-18 11:36:38 +02:00
return bs . Length ( ) / 8 ;
2007-10-12 14:52:30 +02:00
}
return - 1 ;
}
2015-01-14 10:39:55 +01:00
int cDvbSubtitleConverter : : ExtractPgsSegment ( const uchar * Data , int Length , int64_t Pts )
{
cBitStream bs ( Data , Length * 8 ) ;
if ( Length > = 3 ) {
int segmentType = bs . GetBits ( 8 ) ;
int segmentLength = bs . GetBits ( 16 ) ;
if ( ! bs . SetLength ( bs . Index ( ) + segmentLength * 8 ) )
return - 1 ;
LOCK_THREAD ;
cDvbSubtitlePage * page = GetPageById ( 0 , true ) ;
switch ( segmentType ) {
case PGS_PRESENTATION_SEGMENT : {
if ( page - > Pending ( ) ) {
dbgsegments ( " PGS_DISPLAY_SEGMENT (simulated)<br> \n " ) ;
FinishPage ( page ) ;
}
dbgsegments ( " PGS_PRESENTATION_SEGMENT<br> \n " ) ;
displayWidth = windowWidth = bs . GetBits ( 16 ) ;
displayHeight = windowHeight = bs . GetBits ( 16 ) ;
bs . SkipBits ( 8 ) ;
page - > ParsePgs ( Pts , bs ) ;
SD . SetFactor ( double ( DBGBITMAPWIDTH ) / windowWidth ) ;
cSubtitleRegion * region = page - > GetRegionById ( 0 , true ) ;
region - > ParsePgs ( bs ) ;
break ;
}
case PGS_WINDOW_SEGMENT : {
bs . SkipBits ( 16 ) ;
int regionHorizontalAddress = bs . GetBits ( 16 ) ;
int regionVerticalAddress = bs . GetBits ( 16 ) ;
int regionWidth = bs . GetBits ( 16 ) ;
int regionHeight = bs . GetBits ( 16 ) ;
cSubtitleRegion * region = page - > GetRegionById ( 0 , true ) ;
region - > SetDimensions ( regionWidth , regionHeight ) ;
page - > AddRegionRef ( new cSubtitleRegionRef ( 0 , regionHorizontalAddress , regionVerticalAddress ) ) ;
dbgsegments ( " PGS_WINDOW_SEGMENT<br> \n " ) ;
break ;
}
case PGS_PALETTE_SEGMENT : {
dbgsegments ( " PGS_PALETTE_SEGMENT<br> \n " ) ;
cSubtitleClut * clut = page - > GetClutById ( bs . GetBits ( 8 ) , true ) ;
clut - > ParsePgs ( bs ) ;
break ;
}
case PGS_OBJECT_SEGMENT : {
dbgsegments ( " PGS_OBJECT_SEGMENT<br> \n " ) ;
cSubtitleObject * object = page - > GetObjectById ( bs . GetBits ( 16 ) , true ) ;
object - > ParsePgs ( bs ) ;
break ;
}
case PGS_DISPLAY_SEGMENT : {
dbgsegments ( " PGS_DISPLAY_SEGMENT<br> \n " ) ;
FinishPage ( page ) ;
page - > SetPending ( false ) ;
break ;
}
default :
dbgsegments ( " *** unknown segment type: %02X<br> \n " , segmentType ) ;
return - 1 ;
}
return bs . Length ( ) / 8 ;
}
return - 1 ;
}
2007-10-12 14:52:30 +02:00
void cDvbSubtitleConverter : : FinishPage ( cDvbSubtitlePage * Page )
{
if ( ! AssertOsd ( ) )
return ;
2013-09-06 12:37:27 +02:00
int NumAreas ;
2021-03-17 15:24:34 +01:00
tArea * Areas = Page - > GetAreas ( NumAreas ) ;
2022-12-06 16:57:01 +01:00
if ( ! Areas )
return ;
2021-03-17 15:24:34 +01:00
tArea AreaCombined = Page - > CombineAreas ( NumAreas , Areas ) ;
tArea AreaOsd = Page - > ScaleArea ( AreaCombined , osdFactorX , osdFactorY ) ;
2007-10-12 14:52:30 +02:00
int Bpp = 8 ;
bool Reduced = false ;
2022-12-06 16:57:01 +01:00
if ( osd ) {
2021-03-17 15:24:34 +01:00
while ( osd - > CanHandleAreas ( & AreaOsd , 1 ) ! = oeOk ) {
dbgoutput ( " CanHandleAreas: %d<br> \n " , osd - > CanHandleAreas ( & AreaOsd , 1 ) ) ;
int HalfBpp = Bpp / 2 ;
if ( HalfBpp > = 2 ) {
if ( AreaOsd . bpp > = Bpp ) {
AreaOsd . bpp = HalfBpp ;
2007-10-12 14:52:30 +02:00
Reduced = true ;
}
2021-03-17 15:24:34 +01:00
Bpp = HalfBpp ;
}
else
return ; // unable to draw bitmaps
2007-10-12 14:52:30 +02:00
}
2021-03-17 15:24:34 +01:00
}
cDvbSubtitleBitmaps * Bitmaps = new cDvbSubtitleBitmaps ( Page - > PageState ( ) , Page - > Pts ( ) , Page - > PageTimeout ( ) , Areas , NumAreas , osdFactorX , osdFactorY , AreaCombined , AreaOsd ) ;
2012-03-16 12:01:31 +01:00
bitmaps - > Add ( Bitmaps ) ;
2013-02-17 13:19:36 +01:00
for ( int i = 0 ; i < NumAreas ; i + + ) {
2013-09-06 12:37:27 +02:00
if ( cSubtitleRegionRef * srr = Page - > GetRegionRefByIndex ( i ) ) {
if ( cSubtitleRegion * sr = Page - > GetRegionById ( srr - > RegionId ( ) ) ) {
if ( cSubtitleClut * clut = Page - > GetClutById ( sr - > ClutId ( ) ) ) {
cBitmap * bm = new cBitmap ( sr - > RegionWidth ( ) , sr - > RegionHeight ( ) , sr - > RegionDepth ( ) ) ;
bm - > Replace ( * clut - > GetPalette ( sr - > RegionDepth ( ) ) ) ;
sr - > Render ( bm , Page - > Objects ( ) ) ;
if ( Reduced ) {
if ( sr - > RegionDepth ( ) ! = Areas [ i ] . bpp ) {
if ( sr - > RegionLevelOfCompatibility ( ) < = Areas [ i ] . bpp ) {
//TODO this is untested - didn't have any such subtitle stream
cSubtitleClut * Clut = Page - > GetClutById ( sr - > ClutId ( ) ) ;
dbgregions ( " reduce region %d bpp %d level %d area bpp %d<br> \n " , sr - > RegionId ( ) , sr - > RegionDepth ( ) , sr - > RegionLevelOfCompatibility ( ) , Areas [ i ] . bpp ) ;
bm - > ReduceBpp ( * Clut - > GetPalette ( sr - > RegionDepth ( ) ) ) ;
}
else {
dbgregions ( " condense region %d bpp %d level %d area bpp %d<br> \n " , sr - > RegionId ( ) , sr - > RegionDepth ( ) , sr - > RegionLevelOfCompatibility ( ) , Areas [ i ] . bpp ) ;
bm - > ShrinkBpp ( Areas [ i ] . bpp ) ;
}
}
2007-10-12 14:52:30 +02:00
}
2013-09-06 12:37:27 +02:00
bm - > SetOffset ( srr - > RegionHorizontalAddress ( ) , srr - > RegionVerticalAddress ( ) ) ;
Bitmaps - > AddBitmap ( bm ) ;
2007-10-12 14:52:30 +02:00
}
}
}
}
2013-09-06 12:37:27 +02:00
if ( DebugPages )
Bitmaps - > DbgDump ( windowWidth , windowHeight ) ;
2007-10-12 14:52:30 +02:00
}