/* * Copyright (C) 2005-2018 Team Kodi * This file is part of Kodi - https://kodi.tv * * SPDX-License-Identifier: GPL-2.0-or-later * See LICENSES/README.md for more information. */ /* * Most Codeparts are taken from the TuxBox Teletext plugin which is based * upon videotext-0.6.19991029 and written by Thomas Loewe (LazyT), * Roland Meier and DBLuelle. See http://www.tuxtxt.net/ for more information. * Many thanks to the TuxBox Teletext Team for this great work. */ #include "Teletext.h" #include "application/ApplicationComponents.h" #include "application/ApplicationPlayer.h" #include "filesystem/SpecialProtocol.h" #include "input/Key.h" #include "utils/log.h" #include "windowing/GraphicContext.h" #include using namespace std::chrono_literals; static inline void SDL_memset4(uint32_t* dst, uint32_t val, size_t len) { for (; len > 0; --len) *dst++ = val; } #define SDL_memcpy4(dst, src, len) memcpy(dst, src, (len) << 2) static const char *TeletextFont = "special://xbmc/media/Fonts/teletext.ttf"; /* spacing attributes */ #define alpha_black 0x00 #define alpha_red 0x01 #define alpha_green 0x02 #define alpha_yellow 0x03 #define alpha_blue 0x04 #define alpha_magenta 0x05 #define alpha_cyan 0x06 #define alpha_white 0x07 #define flash 0x08 #define steady 0x09 #define end_box 0x0A #define start_box 0x0B #define normal_size 0x0C #define double_height 0x0D #define double_width 0x0E #define double_size 0x0F #define mosaic_black 0x10 #define mosaic_red 0x11 #define mosaic_green 0x12 #define mosaic_yellow 0x13 #define mosaic_blue 0x14 #define mosaic_magenta 0x15 #define mosaic_cyan 0x16 #define mosaic_white 0x17 #define conceal 0x18 #define contiguous_mosaic 0x19 #define separated_mosaic 0x1A #define esc 0x1B #define black_background 0x1C #define new_background 0x1D #define hold_mosaic 0x1E #define release_mosaic 0x1F #define RowAddress2Row(row) ((row == 40) ? 24 : (row - 40)) // G2 Set as defined in ETS 300 706 const unsigned short int G2table[5][6*16] = { // Latin G2 Supplementary Set { 0x0020, 0x00A1, 0x00A2, 0x00A3, 0x0024, 0x00A5, 0x0023, 0x00A7, 0x00A4, 0x2018, 0x201C, 0x00AB, 0x2190, 0x2191, 0x2192, 0x2193, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00D7, 0x00B5, 0x00B6, 0x00B7, 0x00F7, 0x2019, 0x201D, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x0020, 0x0300, 0x0301, 0x02C6, 0x0303, 0x02C9, 0x02D8, 0x02D9, 0x00A8, 0x002E, 0x02DA, 0x00B8, 0x005F, 0x02DD, 0x02DB, 0x02C7, 0x2014, 0x00B9, 0x00AE, 0x00A9, 0x2122, 0x266A, 0x20AC, 0x2030, 0x03B1, 0x0020, 0x0020, 0x0020, 0x215B, 0x215C, 0x215D, 0x215E, 0x2126, 0x00C6, 0x00D0, 0x00AA, 0x0126, 0x0020, 0x0132, 0x013F, 0x0141, 0x00D8, 0x0152, 0x00BA, 0x00DE, 0x0166, 0x014A, 0x0149, 0x0138, 0x00E6, 0x0111, 0x00F0, 0x0127, 0x0131, 0x0133, 0x0140, 0x0142, 0x00F8, 0x0153, 0x00DF, 0x00FE, 0x0167, 0x014B, 0x25A0}, // Cyrillic G2 Supplementary Set { 0x0020, 0x00A1, 0x00A2, 0x00A3, 0x0024, 0x00A5, 0x0020, 0x00A7, 0x0020, 0x2018, 0x201C, 0x00AB, 0x2190, 0x2191, 0x2192, 0x2193, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00D7, 0x00B5, 0x00B6, 0x00B7, 0x00F7, 0x2019, 0x201D, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x0020, 0x0300, 0x0301, 0x02C6, 0x02DC, 0x02C9, 0x02D8, 0x02D9, 0x00A8, 0x002E, 0x02DA, 0x00B8, 0x005F, 0x02DD, 0x02DB, 0x02C7, 0x2014, 0x00B9, 0x00AE, 0x00A9, 0x2122, 0x266A, 0x20AC, 0x2030, 0x03B1, 0x0141, 0x0142, 0x00DF, 0x215B, 0x215C, 0x215D, 0x215E, 0x0044, 0x0045, 0x0046, 0x0047, 0x0049, 0x004A, 0x004B, 0x004C, 0x004E, 0x0051, 0x0052, 0x0053, 0x0055, 0x0056, 0x0057, 0x005A, 0x0064, 0x0065, 0x0066, 0x0067, 0x0069, 0x006A, 0x006B, 0x006C, 0x006E, 0x0071, 0x0072, 0x0073, 0x0075, 0x0076, 0x0077, 0x007A}, // Greek G2 Supplementary Set { 0x0020, 0x0061, 0x0062, 0x00A3, 0x0065, 0x0068, 0x0069, 0x00A7, 0x003A, 0x2018, 0x201C, 0x006B, 0x2190, 0x2191, 0x2192, 0x2193, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00D7, 0x006D, 0x006E, 0x0070, 0x00F7, 0x2019, 0x201D, 0x0074, 0x00BC, 0x00BD, 0x00BE, 0x0078, 0x0020, 0x0300, 0x0301, 0x02C6, 0x02DC, 0x02C9, 0x02D8, 0x02D9, 0x00A8, 0x002E, 0x02DA, 0x00B8, 0x005F, 0x02DD, 0x02DB, 0x02C7, 0x003F, 0x00B9, 0x00AE, 0x00A9, 0x2122, 0x266A, 0x20AC, 0x2030, 0x03B1, 0x038A, 0x038E, 0x038F, 0x215B, 0x215C, 0x215D, 0x215E, 0x0043, 0x0044, 0x0046, 0x0047, 0x004A, 0x004C, 0x0051, 0x0052, 0x0053, 0x0055, 0x0056, 0x0057, 0x0059, 0x005A, 0x0386, 0x0389, 0x0063, 0x0064, 0x0066, 0x0067, 0x006A, 0x006C, 0x0071, 0x0072, 0x0073, 0x0075, 0x0076, 0x0077, 0x0079, 0x007A, 0x0388, 0x25A0}, // Arabic G2 Set { 0x0020, 0x0639, 0xFEC9, 0xFE83, 0xFE85, 0xFE87, 0xFE8B, 0xFE89, 0xFB7C, 0xFB7D, 0xFB7A, 0xFB58, 0xFB59, 0xFB56, 0xFB6D, 0xFB8E, 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0xFECE, 0xFECD, 0xFEFC, 0xFEEC, 0xFEEA, 0xFEE9, 0x00E0, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00EB, 0x00EA, 0x00F9, 0x00EE, 0xFECA, 0x00E9, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00E2, 0x00F4, 0x00FB, 0x00E7, 0x25A0} }; //const (avoid warnings :<) TextPageAttr_t Text_AtrTable[] = { { TXT_ColorWhite , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_WB */ { TXT_ColorWhite , TXT_ColorBlack , C_G0P, 0, 0, 1 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_PassiveDefault */ { TXT_ColorWhite , TXT_ColorRed , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L250 */ { TXT_ColorBlack , TXT_ColorGreen , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L251 */ { TXT_ColorBlack , TXT_ColorYellow, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L252 */ { TXT_ColorWhite , TXT_ColorBlue , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L253 */ { TXT_ColorMagenta, TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU0 */ { TXT_ColorGreen , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU1 */ { TXT_ColorYellow , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU2 */ { TXT_ColorCyan , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU3 */ { TXT_ColorMenu2 , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG0 */ { TXT_ColorYellow , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG1 */ { TXT_ColorMenu2 , TXT_ColorTransp, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG2 */ { TXT_ColorWhite , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG3 */ { TXT_ColorMenu2 , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM0 */ { TXT_ColorYellow , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM1 */ { TXT_ColorMenu2 , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM2 */ { TXT_ColorWhite , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM3 */ { TXT_ColorMenu1 , TXT_ColorBlue , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENUHIL0 5a Z */ { TXT_ColorWhite , TXT_ColorBlue , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENUHIL1 58 X */ { TXT_ColorMenu2 , TXT_ColorTransp, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENUHIL2 9b õ */ { TXT_ColorMenu2 , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU0 ab ´ */ { TXT_ColorYellow , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU1 a4 § */ { TXT_ColorMenu2 , TXT_ColorTransp, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU2 9b õ */ { TXT_ColorMenu2 , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU3 cb À */ { TXT_ColorCyan , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU4 c7 « */ { TXT_ColorWhite , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU5 c8 » */ { TXT_ColorWhite , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU6 a8 ® */ { TXT_ColorYellow , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_CATCHMENU0 a4 § */ { TXT_ColorWhite , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f} /* ATR_CATCHMENU1 a8 ® */ }; /* shapes */ enum { S_END = 0, S_FHL, /* full horizontal line: y-offset */ S_FVL, /* full vertical line: x-offset */ S_BOX, /* rectangle: x-offset, y-offset, width, height */ S_TRA, /* trapez: x0, y0, l0, x1, y1, l1 */ S_BTR, /* trapez in bgcolor: x0, y0, l0, x1, y1, l1 */ S_INV, /* invert */ S_LNK, /* call other shape: shapenumber */ S_CHR, /* Character from freetype hibyte, lowbyte */ S_ADT, /* Character 2F alternating raster */ S_FLH, /* flip horizontal */ S_FLV /* flip vertical */ }; /* shape coordinates */ enum { S_W13 = 5, /* width*1/3 */ S_W12, /* width*1/2 */ S_W23, /* width*2/3 */ S_W11, /* width */ S_WM3, /* width-3 */ S_H13, /* height*1/3 */ S_H12, /* height*1/2 */ S_H23, /* height*2/3 */ S_H11, /* height */ S_NrShCoord }; /* G3 characters */ unsigned char aG3_20[] = { S_TRA, 0, S_H23, 1, 0, S_H11, S_W12, S_END }; unsigned char aG3_21[] = { S_TRA, 0, S_H23, 1, 0, S_H11, S_W11, S_END }; unsigned char aG3_22[] = { S_TRA, 0, S_H12, 1, 0, S_H11, S_W12, S_END }; unsigned char aG3_23[] = { S_TRA, 0, S_H12, 1, 0, S_H11, S_W11, S_END }; unsigned char aG3_24[] = { S_TRA, 0, 0, 1, 0, S_H11, S_W12, S_END }; unsigned char aG3_25[] = { S_TRA, 0, 0, 1, 0, S_H11, S_W11, S_END }; unsigned char aG3_26[] = { S_INV, S_LNK, 0x66, S_END }; unsigned char aG3_27[] = { S_INV, S_LNK, 0x67, S_END }; unsigned char aG3_28[] = { S_INV, S_LNK, 0x68, S_END }; unsigned char aG3_29[] = { S_INV, S_LNK, 0x69, S_END }; unsigned char aG3_2a[] = { S_INV, S_LNK, 0x6a, S_END }; unsigned char aG3_2b[] = { S_INV, S_LNK, 0x6b, S_END }; unsigned char aG3_2c[] = { S_INV, S_LNK, 0x6c, S_END }; unsigned char aG3_2d[] = { S_INV, S_LNK, 0x6d, S_END }; unsigned char aG3_2e[] = { S_BOX, 2, 0, 3, S_H11, S_END }; unsigned char aG3_2f[] = { S_ADT }; unsigned char aG3_30[] = { S_LNK, 0x20, S_FLH, S_END }; unsigned char aG3_31[] = { S_LNK, 0x21, S_FLH, S_END }; unsigned char aG3_32[] = { S_LNK, 0x22, S_FLH, S_END }; unsigned char aG3_33[] = { S_LNK, 0x23, S_FLH, S_END }; unsigned char aG3_34[] = { S_LNK, 0x24, S_FLH, S_END }; unsigned char aG3_35[] = { S_LNK, 0x25, S_FLH, S_END }; unsigned char aG3_36[] = { S_INV, S_LNK, 0x76, S_END }; unsigned char aG3_37[] = { S_INV, S_LNK, 0x77, S_END }; unsigned char aG3_38[] = { S_INV, S_LNK, 0x78, S_END }; unsigned char aG3_39[] = { S_INV, S_LNK, 0x79, S_END }; unsigned char aG3_3a[] = { S_INV, S_LNK, 0x7a, S_END }; unsigned char aG3_3b[] = { S_INV, S_LNK, 0x7b, S_END }; unsigned char aG3_3c[] = { S_INV, S_LNK, 0x7c, S_END }; unsigned char aG3_3d[] = { S_INV, S_LNK, 0x7d, S_END }; unsigned char aG3_3e[] = { S_LNK, 0x2e, S_FLH, S_END }; unsigned char aG3_3f[] = { S_BOX, 0, 0, S_W11, S_H11, S_END }; unsigned char aG3_40[] = { S_BOX, 0, S_H13, S_W11, S_H13, S_LNK, 0x7e, S_END }; unsigned char aG3_41[] = { S_BOX, 0, S_H13, S_W11, S_H13, S_LNK, 0x7e, S_FLV, S_END }; unsigned char aG3_42[] = { S_LNK, 0x50, S_BOX, S_W12, S_H13, S_W12, S_H13, S_END }; unsigned char aG3_43[] = { S_LNK, 0x50, S_BOX, 0, S_H13, S_W12, S_H13, S_END }; unsigned char aG3_44[] = { S_LNK, 0x48, S_FLV, S_LNK, 0x48, S_END }; unsigned char aG3_45[] = { S_LNK, 0x44, S_FLH, S_END }; unsigned char aG3_46[] = { S_LNK, 0x47, S_FLV, S_END }; unsigned char aG3_47[] = { S_LNK, 0x48, S_FLH, S_LNK, 0x48, S_END }; unsigned char aG3_48[] = { S_TRA, 0, 0, S_W23, 0, S_H23, 0, S_BTR, 0, 0, S_W13, 0, S_H13, 0, S_END }; unsigned char aG3_49[] = { S_LNK, 0x48, S_FLH, S_END }; unsigned char aG3_4a[] = { S_LNK, 0x48, S_FLV, S_END }; unsigned char aG3_4b[] = { S_LNK, 0x48, S_FLH, S_FLV, S_END }; unsigned char aG3_4c[] = { S_LNK, 0x50, S_BOX, 0, S_H13, S_W11, S_H13, S_END }; unsigned char aG3_4d[] = { S_CHR, 0x25, 0xE6 }; unsigned char aG3_4e[] = { S_CHR, 0x25, 0xCF }; unsigned char aG3_4f[] = { S_CHR, 0x25, 0xCB }; unsigned char aG3_50[] = { S_BOX, S_W12, 0, 2, S_H11, S_FLH, S_BOX, S_W12, 0, 2, S_H11,S_END }; unsigned char aG3_51[] = { S_BOX, 0, S_H12, S_W11, 2, S_FLV, S_BOX, 0, S_H12, S_W11, 2,S_END }; unsigned char aG3_52[] = { S_LNK, 0x55, S_FLH, S_FLV, S_END }; unsigned char aG3_53[] = { S_LNK, 0x55, S_FLV, S_END }; unsigned char aG3_54[] = { S_LNK, 0x55, S_FLH, S_END }; unsigned char aG3_55[] = { S_LNK, 0x7e, S_FLV, S_BOX, 0, S_H12, S_W12, 2, S_FLV, S_BOX, 0, S_H12, S_W12, 2, S_END }; unsigned char aG3_56[] = { S_LNK, 0x57, S_FLH, S_END}; unsigned char aG3_57[] = { S_LNK, 0x55, S_LNK, 0x50 , S_END}; unsigned char aG3_58[] = { S_LNK, 0x59, S_FLV, S_END}; unsigned char aG3_59[] = { S_LNK, 0x7e, S_LNK, 0x51 , S_END}; unsigned char aG3_5a[] = { S_LNK, 0x50, S_LNK, 0x51 , S_END}; unsigned char aG3_5b[] = { S_CHR, 0x21, 0x92}; unsigned char aG3_5c[] = { S_CHR, 0x21, 0x90}; unsigned char aG3_5d[] = { S_CHR, 0x21, 0x91}; unsigned char aG3_5e[] = { S_CHR, 0x21, 0x93}; unsigned char aG3_5f[] = { S_CHR, 0x00, 0x20}; unsigned char aG3_60[] = { S_INV, S_LNK, 0x20, S_END }; unsigned char aG3_61[] = { S_INV, S_LNK, 0x21, S_END }; unsigned char aG3_62[] = { S_INV, S_LNK, 0x22, S_END }; unsigned char aG3_63[] = { S_INV, S_LNK, 0x23, S_END }; unsigned char aG3_64[] = { S_INV, S_LNK, 0x24, S_END }; unsigned char aG3_65[] = { S_INV, S_LNK, 0x25, S_END }; unsigned char aG3_66[] = { S_LNK, 0x20, S_FLV, S_END }; unsigned char aG3_67[] = { S_LNK, 0x21, S_FLV, S_END }; unsigned char aG3_68[] = { S_LNK, 0x22, S_FLV, S_END }; unsigned char aG3_69[] = { S_LNK, 0x23, S_FLV, S_END }; unsigned char aG3_6a[] = { S_LNK, 0x24, S_FLV, S_END }; unsigned char aG3_6b[] = { S_BOX, 0, 0, S_W11, S_H13, S_TRA, 0, S_H13, S_W11, 0, S_H23, 1, S_END }; unsigned char aG3_6c[] = { S_TRA, 0, 0, 1, 0, S_H12, S_W12, S_FLV, S_TRA, 0, 0, 1, 0, S_H12, S_W12, S_BOX, 0, S_H12, S_W12,1, S_END }; unsigned char aG3_6d[] = { S_TRA, 0, 0, S_W12, S_W12, S_H12, 0, S_FLH, S_TRA, 0, 0, S_W12, S_W12, S_H12, 0, S_END }; unsigned char aG3_6e[] = { S_CHR, 0x00, 0x20}; unsigned char aG3_6f[] = { S_CHR, 0x00, 0x20}; unsigned char aG3_70[] = { S_INV, S_LNK, 0x30, S_END }; unsigned char aG3_71[] = { S_INV, S_LNK, 0x31, S_END }; unsigned char aG3_72[] = { S_INV, S_LNK, 0x32, S_END }; unsigned char aG3_73[] = { S_INV, S_LNK, 0x33, S_END }; unsigned char aG3_74[] = { S_INV, S_LNK, 0x34, S_END }; unsigned char aG3_75[] = { S_INV, S_LNK, 0x35, S_END }; unsigned char aG3_76[] = { S_LNK, 0x66, S_FLH, S_END }; unsigned char aG3_77[] = { S_LNK, 0x67, S_FLH, S_END }; unsigned char aG3_78[] = { S_LNK, 0x68, S_FLH, S_END }; unsigned char aG3_79[] = { S_LNK, 0x69, S_FLH, S_END }; unsigned char aG3_7a[] = { S_LNK, 0x6a, S_FLH, S_END }; unsigned char aG3_7b[] = { S_LNK, 0x6b, S_FLH, S_END }; unsigned char aG3_7c[] = { S_LNK, 0x6c, S_FLH, S_END }; unsigned char aG3_7d[] = { S_LNK, 0x6d, S_FLV, S_END }; unsigned char aG3_7e[] = { S_BOX, S_W12, 0, 2, S_H12, S_FLH, S_BOX, S_W12, 0, 2, S_H12, S_END };// help char, not printed directly (only by S_LNK) unsigned char *aShapes[] = { aG3_20, aG3_21, aG3_22, aG3_23, aG3_24, aG3_25, aG3_26, aG3_27, aG3_28, aG3_29, aG3_2a, aG3_2b, aG3_2c, aG3_2d, aG3_2e, aG3_2f, aG3_30, aG3_31, aG3_32, aG3_33, aG3_34, aG3_35, aG3_36, aG3_37, aG3_38, aG3_39, aG3_3a, aG3_3b, aG3_3c, aG3_3d, aG3_3e, aG3_3f, aG3_40, aG3_41, aG3_42, aG3_43, aG3_44, aG3_45, aG3_46, aG3_47, aG3_48, aG3_49, aG3_4a, aG3_4b, aG3_4c, aG3_4d, aG3_4e, aG3_4f, aG3_50, aG3_51, aG3_52, aG3_53, aG3_54, aG3_55, aG3_56, aG3_57, aG3_58, aG3_59, aG3_5a, aG3_5b, aG3_5c, aG3_5d, aG3_5e, aG3_5f, aG3_60, aG3_61, aG3_62, aG3_63, aG3_64, aG3_65, aG3_66, aG3_67, aG3_68, aG3_69, aG3_6a, aG3_6b, aG3_6c, aG3_6d, aG3_6e, aG3_6f, aG3_70, aG3_71, aG3_72, aG3_73, aG3_74, aG3_75, aG3_76, aG3_77, aG3_78, aG3_79, aG3_7a, aG3_7b, aG3_7c, aG3_7d, aG3_7e }; // G0 Table as defined in ETS 300 706 // cyrillic G0 Charset (0 = Serbian/Croatian, 1 = Russian/Bulgarian, 2 = Ukrainian) const unsigned short int G0table[6][6*16] = { // Cyrillic G0 Set - Option 1 - Serbian/Croatian { ' ', '!', '\"', '#', '$', '%', '&', '\'', '(' , ')' , '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', 0x0427, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0408, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x040C, 0x0420, 0x0421, 0x0422, 0x0423, 0x0412, 0x0403, 0x0409, 0x040A, 0x0417, 0x040B, 0x0416, 0x0402, 0x0428, 0x040F, 0x0447, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0458, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 0x045C, 0x0440, 0x0441, 0x0442, 0x0443, 0x0432, 0x0453, 0x0459, 0x045A, 0x0437, 0x045B, 0x0436, 0x0452, 0x0448, 0x25A0}, // Cyrillic G0 Set - Option 2 - Russian/Bulgarian { ' ', '!', '\"', '#', '$', '%', 0x044B, '\'', '(' , ')' , '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', 0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042C, 0x042A, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x042B, 0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044C, 0x044A, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x25A0}, // Cyrillic G0 Set - Option 3 - Ukrainian { ' ', '!', '\"', '#', '$', '%', 0x0457, '\'', '(' , ')' , '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', 0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042C, 0x0406, 0x0417, 0x0428, 0x0404, 0x0429, 0x0427, 0x0407, 0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044C, 0x0456, 0x0437, 0x0448, 0x0454, 0x0449, 0x0447, 0x25A0}, // Greek G0 Set { ' ', '!', '\"', '#', '$', '%', '&', '\'', '(' , ')' , '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', 0x00AB, '=', 0x00BB, '?', 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x0384, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF, 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x25A0}, // Hebrew G0 Set { ' ', '!', 0x05F2, 0x00A3, '$', '%', '&', '\'', '(' , ')' , '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0x2190, 0x00BD, 0x2192, 0x2191, '#', 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x20AA, 0x2551, 0x00BE, 0x00F7, 0x25A0}, // Arabic G0 Set - Thanks to Habib2006(fannansat) { ' ', '!', 0x05F2, 0x00A3, '$', 0x066A, 0xFEF0, 0xFEF2, 0xFD3F, 0xFD3E, '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', 0x061B, '>', '=', '<', 0x061F, 0xFE94, 0x0621, 0xFE92, 0x0628, 0xFE98, 0x062A, 0xFE8E, 0xFE8D, 0xFE91, 0xFE93, 0xFE97, 0xFE9B, 0xFE9F, 0xFEA3, 0xFEA7, 0xFEA9, 0x0630, 0xFEAD, 0xFEAF, 0xFEB3, 0xFEB7, 0xFEBB, 0xFEBF, 0xFEC1, 0xFEC5, 0xFECB, 0xFECF, 0xFE9C, 0xFEA0, 0xFEA4, 0xFEA8, 0x0023, 0x0640, 0xFED3, 0xFED7, 0xFEDB, 0xFEDF, 0xFEE3, 0xFEE7, 0xFEEB, 0xFEED, 0xFEEF, 0xFEF3, 0xFE99, 0xFE9D, 0xFEA1, 0xFEA5, 0xFEF4, 0xFEF0, 0xFECC, 0xFED0, 0xFED4, 0xFED1, 0xFED8, 0xFED5, 0xFED9, 0xFEE0, 0xFEDD, 0xFEE4, 0xFEE1, 0xFEE8, 0xFEE5, 0xFEFB, 0x25A0} }; const unsigned short int nationaltable23[14][2] = { { '#', 0x00A4 }, /* 0 */ { '#', 0x016F }, /* 1 CS/SK */ { 0x00A3, '$' }, /* 2 EN */ { '#', 0x00F5 }, /* 3 ET */ { 0x00E9, 0x0457 }, /* 4 FR */ { '#', '$' }, /* 5 DE */ { 0x00A3, '$' }, /* 6 IT */ { '#', '$' }, /* 7 LV/LT */ { '#', 0x0144 }, /* 8 PL */ { 0x00E7, '$' }, /* 9 PT/ES */ { '#', 0x00A4 }, /* A RO */ { '#', 0x00CB }, /* B SR/HR/SL */ { '#', 0x00A4 }, /* C SV/FI/HU */ { 0x20A4, 0x011F }, /* D TR */ }; const unsigned short int nationaltable40[14] = { '@', /* 0 */ 0x010D, /* 1 CS/SK */ '@', /* 2 EN */ 0x0161, /* 3 ET */ 0x00E0, /* 4 FR */ 0x00A7, /* 5 DE */ 0x00E9, /* 6 IT */ 0x0161, /* 7 LV/LT */ 0x0105, /* 8 PL */ 0x00A1, /* 9 PT/ES */ 0x0162, /* A RO */ 0x010C, /* B SR/HR/SL */ 0x00C9, /* C SV/FI/HU */ 0x0130, /* D TR */ }; const unsigned short int nationaltable5b[14][6] = { { '[', '\\', ']', '^', '_', '`' }, /* 0 */ { 0x0165, 0x017E, 0x00FD, 0x00ED, 0x0159, 0x00E9 }, /* 1 CS/SK */ { 0x2190, 0x00BD, 0x2192, 0x2191, '#', 0x00AD }, /* 2 EN */ { 0x00C4, 0x00D6, 0x017D, 0x00DC, 0x00D5, 0x0161 }, /* 3 ET */ { 0x0451, 0x00EA, 0x00F9, 0x00EE, '#', 0x00E8 }, /* 4 FR */ { 0x00C4, 0x00D6, 0x00DC, '^', '_', 0x00B0 }, /* 5 DE */ { 0x00B0, 0x00E7, 0x2192, 0x2191, '#', 0x00F9 }, /* 6 IT */ { 0x0117, 0x0119, 0x017D, 0x010D, 0x016B, 0x0161 }, /* 7 LV/LT */ { 0x017B, 0x015A, 0x0141, 0x0107, 0x00F3, 0x0119 }, /* 8 PL */ { 0x00E1, 0x00E9, 0x00ED, 0x00F3, 0x00FA, 0x00BF }, /* 9 PT/ES */ { 0x00C2, 0x015E, 0x01CD, 0x01CF, 0x0131, 0x0163 }, /* A RO */ { 0x0106, 0x017D, 0x00D0, 0x0160, 0x0451, 0x010D }, /* B SR/HR/SL */ { 0x00C4, 0x00D6, 0x00C5, 0x00DC, '_', 0x00E9 }, /* C SV/FI/HU */ { 0x015E, 0x00D6, 0x00C7, 0x00DC, 0x011E, 0x0131 }, /* D TR */ }; const unsigned short int nationaltable7b[14][4] = { { '{', '|', '}', '~' }, /* 0 */ { 0x00E1, 0x011B, 0x00FA, 0x0161 }, /* 1 CS/SK */ { 0x00BC, 0x2551, 0x00BE, 0x00F7 }, /* 2 EN */ { 0x00E4, 0x00F6, 0x017E, 0x00FC }, /* 3 ET */ { 0x00E2, 0x00F4, 0x00FB, 0x00E7 }, /* 4 FR */ { 0x00E4, 0x00F6, 0x00FC, 0x00DF }, /* 5 DE */ { 0x00E0, 0x00F3, 0x00E8, 0x00EC }, /* 6 IT */ { 0x0105, 0x0173, 0x017E, 0x012F }, /* 7 LV/LT */ { 0x017C, 0x015B, 0x0142, 0x017A }, /* 8 PL */ { 0x00FC, 0x00F1, 0x00E8, 0x00E0 }, /* 9 PT/ES */ { 0x00E2, 0x015F, 0x01CE, 0x00EE }, /* A RO */ { 0x0107, 0x017E, 0x0111, 0x0161 }, /* B SR/HR/SL */ { 0x00E4, 0x00F6, 0x00E5, 0x00FC }, /* C SV/FI/HU */ { 0x015F, 0x00F6, 0x00E7, 0x00FC }, /* D TR */ }; const unsigned short int arrowtable[] = { 8592, 8594, 8593, 8595, 'O', 'K', 8592, 8592 }; CTeletextDecoder::CTeletextDecoder() { memset(&m_RenderInfo, 0, sizeof(TextRenderInfo_t)); m_teletextFont = CSpecialProtocol::TranslatePath(TeletextFont); m_TextureBuffer = NULL; m_txtCache = NULL; m_Manager = NULL; m_Library = NULL; m_RenderInfo.ShowFlof = true; m_RenderInfo.Show39 = false; m_RenderInfo.Showl25 = true; m_RenderInfo.Prev_100 = 0x100; m_RenderInfo.Prev_10 = 0x100; m_RenderInfo.Next_100 = 0x100; m_RenderInfo.Next_10 = 0x100; m_RenderInfo.InputCounter = 2; unsigned short rd0[] = {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0x00<<8, 0x00<<8, 0x00<<8, 0, 0 }; unsigned short gn0[] = {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0x20<<8, 0x10<<8, 0x20<<8, 0, 0 }; unsigned short bl0[] = {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0x40<<8, 0x20<<8, 0x40<<8, 0, 0 }; unsigned short tr0[] = {0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF, 0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF, 0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF, 0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF, 0x0000 , 0x0000 , 0x0A0A , 0xFFFF, 0x3030 }; memcpy(m_RenderInfo.rd0,rd0,TXT_Color_SIZECOLTABLE*sizeof(unsigned short)); memcpy(m_RenderInfo.gn0,gn0,TXT_Color_SIZECOLTABLE*sizeof(unsigned short)); memcpy(m_RenderInfo.bl0,bl0,TXT_Color_SIZECOLTABLE*sizeof(unsigned short)); memcpy(m_RenderInfo.tr0,tr0,TXT_Color_SIZECOLTABLE*sizeof(unsigned short)); m_LastPage = 0; m_TempPage = 0; m_Ascender = 0; m_PCOldCol = 0; m_PCOldRow = 0; m_CatchedPage = 0; m_CatchCol = 0; m_CatchRow = 0; prevTimeSec = 0; prevHeaderPage = 0; m_updateTexture = false; m_YOffset = 0; } CTeletextDecoder::~CTeletextDecoder() = default; bool CTeletextDecoder::HandleAction(const CAction &action) { if (m_txtCache == NULL) { CLog::Log(LOGERROR, "CTeletextDecoder::HandleAction called without teletext cache"); return false; } std::unique_lock lock(m_txtCache->m_critSection); if (action.GetID() == ACTION_MOVE_UP) { if (m_RenderInfo.PageCatching) CatchNextPage(-1, -1); else GetNextPageOne(true); return true; } else if (action.GetID() == ACTION_MOVE_DOWN) { if (m_RenderInfo.PageCatching) CatchNextPage(1, 1); else GetNextPageOne(false); return true; } else if (action.GetID() == ACTION_MOVE_RIGHT) { if (m_RenderInfo.PageCatching) CatchNextPage(0, 1); else if (m_RenderInfo.Boxed) { m_RenderInfo.SubtitleDelay++; // display SubtitleDelay m_RenderInfo.PosY = 0; char ns[10]; SetPosX(1); sprintf(ns,"+%d ", m_RenderInfo.SubtitleDelay); RenderCharFB(ns[0], &Text_AtrTable[ATR_WB]); RenderCharFB(ns[1], &Text_AtrTable[ATR_WB]); RenderCharFB(ns[2], &Text_AtrTable[ATR_WB]); RenderCharFB(ns[4], &Text_AtrTable[ATR_WB]); } else { GetNextSubPage(1); } return true; } else if (action.GetID() == ACTION_MOVE_LEFT) { if (m_RenderInfo.PageCatching) CatchNextPage(0, -1); else if (m_RenderInfo.Boxed) { m_RenderInfo.SubtitleDelay--; // display subtitledelay m_RenderInfo.PosY = 0; char ns[10]; SetPosX(1); sprintf(ns,"+%d ", m_RenderInfo.SubtitleDelay); RenderCharFB(ns[0], &Text_AtrTable[ATR_WB]); RenderCharFB(ns[1], &Text_AtrTable[ATR_WB]); RenderCharFB(ns[2], &Text_AtrTable[ATR_WB]); RenderCharFB(ns[4], &Text_AtrTable[ATR_WB]); } else { GetNextSubPage(-1); } return true; } else if (action.GetID() >= REMOTE_0 && action.GetID() <= REMOTE_9) { PageInput(action.GetID() - REMOTE_0); return true; } else if (action.GetID() == KEY_UNICODE) { // input from the keyboard if (action.GetUnicode() >= 48 && action.GetUnicode() < 58) { PageInput(action.GetUnicode() - 48); return true; } return false; } else if (action.GetID() == ACTION_PAGE_UP) { SwitchZoomMode(); return true; } else if (action.GetID() == ACTION_PAGE_DOWN) { SwitchTranspMode(); return true; } else if (action.GetID() == ACTION_SELECT_ITEM) { if (m_txtCache->SubPageTable[m_txtCache->Page] == 0xFF) return false; if (!m_RenderInfo.PageCatching) StartPageCatching(); else StopPageCatching(); return true; } if (m_RenderInfo.PageCatching) { m_txtCache->PageUpdate = true; m_RenderInfo.PageCatching = false; return true; } if (action.GetID() == ACTION_SHOW_INFO) { SwitchHintMode(); return true; } else if (action.GetID() == ACTION_TELETEXT_RED) { ColorKey(m_RenderInfo.Prev_100); return true; } else if (action.GetID() == ACTION_TELETEXT_GREEN) { ColorKey(m_RenderInfo.Prev_10); return true; } else if (action.GetID() == ACTION_TELETEXT_YELLOW) { ColorKey(m_RenderInfo.Next_10); return true; } else if (action.GetID() == ACTION_TELETEXT_BLUE) { ColorKey(m_RenderInfo.Next_100); return true; } return false; } bool CTeletextDecoder::InitDecoder() { int error; auto& components = CServiceBroker::GetAppComponents(); const auto appPlayer = components.GetComponent(); m_txtCache = appPlayer->GetTeletextCache(); if (m_txtCache == nullptr) { CLog::Log(LOGERROR, "{}: called without teletext cache", __FUNCTION__); return false; } /* init fontlibrary */ if ((error = FT_Init_FreeType(&m_Library))) { CLog::Log(LOGERROR, "{}: ", __FUNCTION__, error); m_Library = NULL; return false; } if ((error = FTC_Manager_New(m_Library, 7, 2, 0, &MyFaceRequester, NULL, &m_Manager))) { FT_Done_FreeType(m_Library); m_Library = NULL; m_Manager = NULL; CLog::Log(LOGERROR, "{}: ", __FUNCTION__, error); return false; } if ((error = FTC_SBitCache_New(m_Manager, &m_Cache))) { FTC_Manager_Done(m_Manager); FT_Done_FreeType(m_Library); m_Manager = NULL; m_Library = NULL; CLog::Log(LOGERROR, "{}: ", __FUNCTION__, error); return false; } /* calculate font dimensions */ m_RenderInfo.Width = (int)(CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth()*CServiceBroker::GetWinSystem()->GetGfxContext().GetGUIScaleX()); m_RenderInfo.Height = (int)(CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight()*CServiceBroker::GetWinSystem()->GetGfxContext().GetGUIScaleY()); m_RenderInfo.FontHeight = m_RenderInfo.Height / 25; m_RenderInfo.FontWidth_Normal = m_RenderInfo.Width / (m_RenderInfo.Show39 ? 39 : 40); SetFontWidth(m_RenderInfo.FontWidth_Normal); for (int i = 0; i <= 10; i++) m_RenderInfo.axdrcs[i+12+1] = (m_RenderInfo.FontHeight * i + 6) / 10; /* center screen */ m_TypeTTF.face_id = (FTC_FaceID) const_cast(m_teletextFont.c_str()); m_TypeTTF.height = (FT_UShort) m_RenderInfo.FontHeight; m_TypeTTF.flags = FT_LOAD_MONOCHROME; if (FTC_Manager_LookupFace(m_Manager, m_TypeTTF.face_id, &m_Face)) { m_TypeTTF.face_id = (FTC_FaceID) const_cast(m_teletextFont.c_str()); if ((error = FTC_Manager_LookupFace(m_Manager, m_TypeTTF.face_id, &m_Face))) { CLog::Log(LOGERROR, "{}: ", __FUNCTION__, error); FTC_Manager_Done(m_Manager); FT_Done_FreeType(m_Library); m_Manager = NULL; m_Library = NULL; return false; } } m_Ascender = m_RenderInfo.FontHeight * m_Face->ascender / m_Face->units_per_EM; /* set variable screeninfo for double buffering */ m_YOffset = 0; m_TextureBuffer = new UTILS::COLOR::Color[4 * m_RenderInfo.Height * m_RenderInfo.Width]; ClearFB(GetColorRGB(TXT_ColorTransp)); ClearBB(GetColorRGB(TXT_ColorTransp)); /* initialize backbuffer */ /* set new colormap */ SetColors(DefaultColors, 0, TXT_Color_SIZECOLTABLE); for (int i = 0; i < 40 * 25; i++) { m_RenderInfo.PageChar[i] = ' '; m_RenderInfo.PageAtrb[i].fg = TXT_ColorTransp; m_RenderInfo.PageAtrb[i].bg = TXT_ColorTransp; m_RenderInfo.PageAtrb[i].charset = C_G0P; m_RenderInfo.PageAtrb[i].doubleh = 0; m_RenderInfo.PageAtrb[i].doublew = 0; m_RenderInfo.PageAtrb[i].IgnoreAtBlackBgSubst = 0; } m_RenderInfo.TranspMode = false; m_LastPage = 0x100; return true; } void CTeletextDecoder::EndDecoder() { /* clear SubtitleCache */ for (TextSubtitleCache_t*& subtitleCache : m_RenderInfo.SubtitleCache) { if (subtitleCache != NULL) { delete subtitleCache; subtitleCache = NULL; } } if (m_TextureBuffer) { delete[] m_TextureBuffer; m_TextureBuffer = NULL; } /* close freetype */ if (m_Manager) { FTC_Node_Unref(m_anode, m_Manager); FTC_Manager_Done(m_Manager); } if (m_Library) { FT_Done_FreeType(m_Library); } m_Manager = NULL; m_Library = NULL; if (!m_txtCache) { CLog::Log(LOGINFO, "{}: called without cache", __FUNCTION__); } else { std::unique_lock lock(m_txtCache->m_critSection); m_txtCache->PageUpdate = true; CLog::Log(LOGDEBUG, "Teletext: Rendering ended"); } } void CTeletextDecoder::PageInput(int Number) { std::unique_lock lock(m_txtCache->m_critSection); m_updateTexture = true; /* clear m_TempPage */ if (m_RenderInfo.InputCounter == 2) m_TempPage = 0; /* check for 0 & 9 on first position */ if (Number == 0 && m_RenderInfo.InputCounter == 2) { /* set page */ m_TempPage = m_LastPage; /* 0 toggles to last page as in program switching */ m_RenderInfo.InputCounter = -1; } else if (Number == 9 && m_RenderInfo.InputCounter == 2) { return; } /* show pageinput */ if (m_RenderInfo.ZoomMode == 2) { m_RenderInfo.ZoomMode = 1; CopyBB2FB(); } m_RenderInfo.PosY = 0; switch (m_RenderInfo.InputCounter) { case 2: SetPosX(1); RenderCharFB(Number | '0', &Text_AtrTable[ATR_WB]); RenderCharFB('-', &Text_AtrTable[ATR_WB]); RenderCharFB('-', &Text_AtrTable[ATR_WB]); break; case 1: SetPosX(2); RenderCharFB(Number | '0', &Text_AtrTable[ATR_WB]); break; case 0: SetPosX(3); RenderCharFB(Number | '0', &Text_AtrTable[ATR_WB]); break; } /* generate pagenumber */ m_TempPage |= Number << (m_RenderInfo.InputCounter*4); m_RenderInfo.InputCounter--; if (m_RenderInfo.InputCounter < 0) { /* disable SubPage zapping */ m_txtCache->ZapSubpageManual = false; /* reset input */ m_RenderInfo.InputCounter = 2; /* set new page */ m_LastPage = m_txtCache->Page; m_txtCache->Page = m_TempPage; m_RenderInfo.HintMode = false; /* check cache */ int subp = m_txtCache->SubPageTable[m_txtCache->Page]; if (subp != 0xFF) { m_txtCache->SubPage = subp; m_txtCache->PageUpdate = true; } else { m_txtCache->SubPage = 0; // RenderMessage(PageNotFound); } } } void CTeletextDecoder::GetNextPageOne(bool up) { std::unique_lock lock(m_txtCache->m_critSection); /* disable subpage zapping */ m_txtCache->ZapSubpageManual = false; /* abort pageinput */ m_RenderInfo.InputCounter = 2; /* find next cached page */ m_LastPage = m_txtCache->Page; int subp; do { if (up) CDVDTeletextTools::NextDec(&m_txtCache->Page); else CDVDTeletextTools::PrevDec(&m_txtCache->Page); subp = m_txtCache->SubPageTable[m_txtCache->Page]; } while (subp == 0xFF && m_txtCache->Page != m_LastPage); /* update Page */ if (m_txtCache->Page != m_LastPage) { if (m_RenderInfo.ZoomMode == 2) m_RenderInfo.ZoomMode = 1; m_txtCache->SubPage = subp; m_RenderInfo.HintMode = false; m_txtCache->PageUpdate = true; } } void CTeletextDecoder::GetNextSubPage(int offset) { std::unique_lock lock(m_txtCache->m_critSection); /* abort pageinput */ m_RenderInfo.InputCounter = 2; for (int loop = m_txtCache->SubPage + offset; loop != m_txtCache->SubPage; loop += offset) { if (loop < 0) loop = 0x79; else if (loop > 0x79) loop = 0; if (loop == m_txtCache->SubPage) break; if (m_txtCache->astCachetable[m_txtCache->Page][loop]) { /* enable manual SubPage zapping */ m_txtCache->ZapSubpageManual = true; /* update page */ if (m_RenderInfo.ZoomMode == 2) /* if zoomed to lower half */ m_RenderInfo.ZoomMode = 1; /* activate upper half */ m_txtCache->SubPage = loop; m_RenderInfo.HintMode = false; m_txtCache->PageUpdate = true; return; } } } void CTeletextDecoder::SwitchZoomMode() { std::unique_lock lock(m_txtCache->m_critSection); if (m_txtCache->SubPageTable[m_txtCache->Page] != 0xFF) { /* toggle mode */ m_RenderInfo.ZoomMode++; if (m_RenderInfo.ZoomMode == 3) m_RenderInfo.ZoomMode = 0; /* update page */ m_txtCache->PageUpdate = true; } } void CTeletextDecoder::SwitchTranspMode() { std::unique_lock lock(m_txtCache->m_critSection); /* toggle mode */ if (!m_RenderInfo.TranspMode) m_RenderInfo.TranspMode = true; else m_RenderInfo.TranspMode = false; /* backward to immediately switch to TV-screen */ /* set mode */ if (!m_RenderInfo.TranspMode) /* normal text-only */ { ClearBB(m_txtCache->FullScrColor); m_txtCache->PageUpdate = true; } else /* semi-transparent BG with FG text */ { ClearBB(TXT_ColorTransp); m_txtCache->PageUpdate = true; } } void CTeletextDecoder::SwitchHintMode() { std::unique_lock lock(m_txtCache->m_critSection); /* toggle mode */ m_RenderInfo.HintMode ^= true; if (!m_RenderInfo.HintMode) /* toggle evaluation of level 2.5 information by explicitly switching off HintMode */ { m_RenderInfo.Showl25 ^= true; } /* update page */ m_txtCache->PageUpdate = true; } void CTeletextDecoder::ColorKey(int target) { std::unique_lock lock(m_txtCache->m_critSection); if (!target) return; if (m_RenderInfo.ZoomMode == 2) m_RenderInfo.ZoomMode = 1; m_LastPage = m_txtCache->Page; m_txtCache->Page = target; m_txtCache->SubPage = m_txtCache->SubPageTable[m_txtCache->Page]; m_RenderInfo.InputCounter = 2; m_RenderInfo.HintMode = false; m_txtCache->PageUpdate = true; } void CTeletextDecoder::StartPageCatching() { m_RenderInfo.PageCatching = true; /* abort pageinput */ m_RenderInfo.InputCounter = 2; /* show info line */ m_RenderInfo.ZoomMode = 0; m_RenderInfo.PosX = 0; m_RenderInfo.PosY = 24*m_RenderInfo.FontHeight; /* check for pagenumber(s) */ m_CatchRow = 1; m_CatchCol = 0; m_CatchedPage = 0; m_PCOldRow = 0; m_PCOldCol = 0; /* no inverted page number to restore yet */ CatchNextPage(0, 1); if (!m_CatchedPage) { std::unique_lock lock(m_txtCache->m_critSection); m_RenderInfo.PageCatching = false; m_txtCache->PageUpdate = true; return; } } void CTeletextDecoder::StopPageCatching() { std::unique_lock lock(m_txtCache->m_critSection); /* set new page */ if (m_RenderInfo.ZoomMode == 2) m_RenderInfo.ZoomMode = 1; m_LastPage = m_txtCache->Page; m_txtCache->Page = m_CatchedPage; m_RenderInfo.HintMode = false; m_txtCache->PageUpdate = true; m_RenderInfo.PageCatching = false; int subp = m_txtCache->SubPageTable[m_txtCache->Page]; if (subp != 0xFF) m_txtCache->SubPage = subp; else m_txtCache->SubPage = 0; } void CTeletextDecoder::CatchNextPage(int firstlineinc, int inc) { int tmp_page, allowwrap = 1; /* allow first wrap around */ /* catch next page */ for(;;) { unsigned char *p = &(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol]); TextPageAttr_t a = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol]; if (!(a.charset == C_G1C || a.charset == C_G1S) && /* no mosaic */ (a.fg != a.bg) && /* not hidden */ (*p >= '1' && *p <= '8' && /* valid page number */ *(p+1) >= '0' && *(p+1) <= '9' && *(p+2) >= '0' && *(p+2) <= '9') && (m_CatchRow == 0 || (*(p-1) < '0' || *(p-1) > '9')) && /* non-numeric char before and behind */ (m_CatchRow == 37 || (*(p+3) < '0' || *(p+3) > '9'))) { tmp_page = ((*p - '0')<<8) | ((*(p+1) - '0')<<4) | (*(p+2) - '0'); #if 0 if (tmp_page != m_CatchedPage) /* confusing to skip identical page numbers - I want to reach what I aim to */ #endif { m_CatchedPage = tmp_page; RenderCatchedPage(); m_CatchCol += inc; /* FIXME: limit */ return; } } if (firstlineinc > 0) { m_CatchRow++; m_CatchCol = 0; firstlineinc = 0; } else if (firstlineinc < 0) { m_CatchRow--; m_CatchCol = 37; firstlineinc = 0; } else m_CatchCol += inc; if (m_CatchCol > 37) { m_CatchRow++; m_CatchCol = 0; } else if (m_CatchCol < 0) { m_CatchRow--; m_CatchCol = 37; } if (m_CatchRow > 23) { if (allowwrap) { allowwrap = 0; m_CatchRow = 1; m_CatchCol = 0; } else { return; } } else if (m_CatchRow < 1) { if (allowwrap) { allowwrap = 0; m_CatchRow = 23; m_CatchCol =37; } else { return; } } } } void CTeletextDecoder::RenderCatchedPage() { int zoom = 0; m_updateTexture = true; /* handle zoom */ if (m_RenderInfo.ZoomMode) zoom = 1<<10; if (m_PCOldRow || m_PCOldCol) /* not at first call */ { /* restore pagenumber */ SetPosX(m_PCOldCol); if (m_RenderInfo.ZoomMode == 2) m_RenderInfo.PosY = (m_PCOldRow-12)*m_RenderInfo.FontHeight*((zoom>>10)+1); else m_RenderInfo.PosY = m_PCOldRow*m_RenderInfo.FontHeight*((zoom>>10)+1); RenderCharFB(m_RenderInfo.PageChar[m_PCOldRow*40 + m_PCOldCol ], &m_RenderInfo.PageAtrb[m_PCOldRow*40 + m_PCOldCol ]); RenderCharFB(m_RenderInfo.PageChar[m_PCOldRow*40 + m_PCOldCol + 1], &m_RenderInfo.PageAtrb[m_PCOldRow*40 + m_PCOldCol + 1]); RenderCharFB(m_RenderInfo.PageChar[m_PCOldRow*40 + m_PCOldCol + 2], &m_RenderInfo.PageAtrb[m_PCOldRow*40 + m_PCOldCol + 2]); } m_PCOldRow = m_CatchRow; m_PCOldCol = m_CatchCol; /* mark pagenumber */ if (m_RenderInfo.ZoomMode == 1 && m_CatchRow > 11) { m_RenderInfo.ZoomMode = 2; CopyBB2FB(); } else if (m_RenderInfo.ZoomMode == 2 && m_CatchRow < 12) { m_RenderInfo.ZoomMode = 1; CopyBB2FB(); } SetPosX(m_CatchCol); if (m_RenderInfo.ZoomMode == 2) m_RenderInfo.PosY = (m_CatchRow-12)*m_RenderInfo.FontHeight*((zoom>>10)+1); else m_RenderInfo.PosY = m_CatchRow*m_RenderInfo.FontHeight*((zoom>>10)+1); TextPageAttr_t a0 = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol ]; TextPageAttr_t a1 = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol + 1]; TextPageAttr_t a2 = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol + 2]; int t; /* exchange colors */ t = a0.fg; a0.fg = a0.bg; a0.bg = t; t = a1.fg; a1.fg = a1.bg; a1.bg = t; t = a2.fg; a2.fg = a2.bg; a2.bg = t; RenderCharFB(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol ], &a0); RenderCharFB(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol + 1], &a1); RenderCharFB(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol + 2], &a2); } void CTeletextDecoder::RenderPage() { std::unique_lock lock(m_txtCache->m_critSection); int StartRow = 0; int national_subset_bak = m_txtCache->NationalSubset; if (m_txtCache->PageUpdate) m_updateTexture = true; /* update page or timestring */ if (m_txtCache->PageUpdate && m_txtCache->PageReceiving != m_txtCache->Page && m_RenderInfo.InputCounter == 2) { /* reset update flag */ m_txtCache->PageUpdate = false; if (m_RenderInfo.Boxed && m_RenderInfo.SubtitleDelay) { TextSubtitleCache_t* c = NULL; int j = -1; for (int i = 0; i < SUBTITLE_CACHESIZE; i++) { if (j == -1 && !m_RenderInfo.SubtitleCache[i]) j = i; if (m_RenderInfo.SubtitleCache[i] && !m_RenderInfo.SubtitleCache[i]->Valid) { c = m_RenderInfo.SubtitleCache[i]; break; } } if (c == NULL) { if (j == -1) // no more space in SubtitleCache return; c = new TextSubtitleCache_t; if (c == NULL) return; *c = {}; m_RenderInfo.SubtitleCache[j] = c; } c->Valid = true; c->Timestamp = std::chrono::steady_clock::now(); if (m_txtCache->SubPageTable[m_txtCache->Page] != 0xFF) { TextPageinfo_t * p = DecodePage(m_RenderInfo.Showl25, c->PageChar, c->PageAtrb, m_RenderInfo.HintMode, m_RenderInfo.ShowFlof); if (p) { m_RenderInfo.Boxed = p->boxed; } } m_RenderInfo.DelayStarted = true; return; } m_RenderInfo.DelayStarted = false; /* decode page */ if (m_txtCache->SubPageTable[m_txtCache->Page] != 0xFF) { TextPageinfo_t * p = DecodePage(m_RenderInfo.Showl25, m_RenderInfo.PageChar, m_RenderInfo.PageAtrb, m_RenderInfo.HintMode, m_RenderInfo.ShowFlof); if (p) { m_RenderInfo.PageInfo = p; m_RenderInfo.Boxed = p->boxed; } if (m_RenderInfo.Boxed || m_RenderInfo.TranspMode) FillBorder(GetColorRGB(TXT_ColorTransp)); else FillBorder(GetColorRGB((enumTeletextColor)m_txtCache->FullScrColor)); if (m_txtCache->ColorTable) /* as late as possible to shorten the time the old page is displayed with the new colors */ SetColors(m_txtCache->ColorTable, 16, 16); /* set colors for CLUTs 2+3 */ } else StartRow = 1; DoRenderPage(StartRow, national_subset_bak); } else { if (m_RenderInfo.DelayStarted) { auto now = std::chrono::steady_clock::now(); for (TextSubtitleCache_t* const subtitleCache : m_RenderInfo.SubtitleCache) { if (subtitleCache && subtitleCache->Valid && std::chrono::duration_cast(now - subtitleCache->Timestamp) .count() >= m_RenderInfo.SubtitleDelay) { memcpy(m_RenderInfo.PageChar, subtitleCache->PageChar, 40 * 25); memcpy(m_RenderInfo.PageAtrb, subtitleCache->PageAtrb, 40 * 25 * sizeof(TextPageAttr_t)); DoRenderPage(StartRow, national_subset_bak); subtitleCache->Valid = false; return; } } } if (m_RenderInfo.ZoomMode != 2) { m_RenderInfo.PosY = 0; if (m_txtCache->SubPageTable[m_txtCache->Page] == 0xff) { m_RenderInfo.PageAtrb[32].fg = TXT_ColorYellow; m_RenderInfo.PageAtrb[32].bg = TXT_ColorMenu1; int showpage = m_txtCache->PageReceiving; int showsubpage; // Verify that showpage is positive before any access to the array if (showpage >= 0 && (showsubpage = m_txtCache->SubPageTable[showpage]) != 0xff) { TextCachedPage_t *pCachedPage; pCachedPage = m_txtCache->astCachetable[showpage][showsubpage]; if (pCachedPage && IsDec(showpage)) { m_RenderInfo.PosX = 0; if (m_RenderInfo.InputCounter == 2) { if (m_txtCache->BTTok && !m_txtCache->BasicTop[m_txtCache->Page]) /* page non-existent according to TOP (continue search anyway) */ { m_RenderInfo.PageAtrb[0].fg = TXT_ColorWhite; m_RenderInfo.PageAtrb[0].bg = TXT_ColorRed; } else { m_RenderInfo.PageAtrb[0].fg = TXT_ColorYellow; m_RenderInfo.PageAtrb[0].bg = TXT_ColorMenu1; } CDVDTeletextTools::Hex2Str((char*)m_RenderInfo.PageChar+3, m_txtCache->Page); int col; for (col = m_RenderInfo.nofirst; col < 7; col++) // selected page { RenderCharFB(m_RenderInfo.PageChar[col], &m_RenderInfo.PageAtrb[0]); } RenderCharFB(m_RenderInfo.PageChar[col], &m_RenderInfo.PageAtrb[32]); } else SetPosX(8); memcpy(&m_RenderInfo.PageChar[8], pCachedPage->p0, 24); /* header line without timestring */ for (unsigned char i : pCachedPage->p0) { RenderCharFB(i, &m_RenderInfo.PageAtrb[32]); } /* Update on every Header number change */ if (pCachedPage->p0[2] != prevHeaderPage) { prevHeaderPage = pCachedPage->p0[2]; m_updateTexture = true; } } } } /* update timestring */ SetPosX(32); for (int i = 0; i < 8; i++) { if (!m_RenderInfo.PageAtrb[32+i].flashing) RenderCharFB(m_txtCache->TimeString[i], &m_RenderInfo.PageAtrb[32]); else { SetPosX(33+i); } } if (!IsSubtitlePage(m_txtCache->Page)) { /* Update on every changed second */ if (m_txtCache->TimeString[7] != prevTimeSec) { prevTimeSec = m_txtCache->TimeString[7]; m_updateTexture = true; } } else { m_updateTexture = true; } } DoFlashing(StartRow); m_txtCache->NationalSubset = national_subset_bak; } } bool CTeletextDecoder::IsSubtitlePage(int pageNumber) const { if (!m_txtCache) return false; std::unique_lock lock(m_txtCache->m_critSection); for (const auto subPage : m_txtCache->SubtitlePages) { if (subPage.page == pageNumber) return true; } return false; } void CTeletextDecoder::DoFlashing(int startrow) { std::unique_lock lock(m_txtCache->m_critSection); TextCachedPage_t* textCachepage = m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage]; // Verify that the page is not deleted by the other thread: CDVDTeletextData::ResetTeletextCache() if (!textCachepage || m_RenderInfo.PageInfo != &textCachepage->pageinfo) m_RenderInfo.PageInfo = nullptr; /* get national subset */ if (m_txtCache->NationalSubset <= NAT_MAX_FROM_HEADER && /* not for GR/RU as long as line28 is not evaluated */ m_RenderInfo.PageInfo && m_RenderInfo.PageInfo->nationalvalid) /* individual subset according to page header */ { m_txtCache->NationalSubset = CountryConversionTable[m_RenderInfo.PageInfo->national]; } /* Flashing */ TextPageAttr_t flashattr; char flashchar; std::chrono::milliseconds flashphase = std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()) % 1000; int srow = startrow; int erow = 24; int factor=1; switch (m_RenderInfo.ZoomMode) { case 1: erow = 12; factor=2;break; case 2: srow = 12; factor=2;break; } m_RenderInfo.PosY = startrow*m_RenderInfo.FontHeight*factor; for (int row = srow; row < erow; row++) { int index = row * 40; int dhset = 0; int incflash = 3; int decflash = 2; m_RenderInfo.PosX = 0; for (int col = m_RenderInfo.nofirst; col < 40; col++) { if (m_RenderInfo.PageAtrb[index + col].flashing && m_RenderInfo.PageChar[index + col] > 0x20 && m_RenderInfo.PageChar[index + col] != 0xff ) { SetPosX(col); flashchar = m_RenderInfo.PageChar[index + col]; bool doflash = false; memcpy(&flashattr, &m_RenderInfo.PageAtrb[index + col], sizeof(TextPageAttr_t)); switch (flashattr.flashing &0x1c) // Flash Rate { case 0x00 : // 1 Hz if (flashphase > 500ms) doflash = true; break; case 0x04 : // 2 Hz Phase 1 if (flashphase < 250ms) doflash = true; break; case 0x08 : // 2 Hz Phase 2 if (flashphase >= 250ms && flashphase < 500ms) doflash = true; break; case 0x0c : // 2 Hz Phase 3 if (flashphase >= 500ms && flashphase < 750ms) doflash = true; break; case 0x10 : // incremental flash incflash++; if (incflash>3) incflash = 1; switch (incflash) { case 1: if (flashphase < 250ms) doflash = true; break; case 2: if (flashphase >= 250ms && flashphase < 500ms) doflash = true; break; case 3: if (flashphase >= 500ms && flashphase < 750ms) doflash = true; break; } break; case 0x14 : // decremental flash decflash--; if (decflash<1) decflash = 3; switch (decflash) { case 1: if (flashphase < 250ms) doflash = true; break; case 2: if (flashphase >= 250ms && flashphase < 500ms) doflash = true; break; case 3: if (flashphase >= 500ms && flashphase < 750ms) doflash = true; break; } break; } switch (flashattr.flashing &0x03) // Flash Mode { case 0x01 : // normal Flashing if (doflash) flashattr.fg = flashattr.bg; break; case 0x02 : // inverted Flashing doflash = !doflash; if (doflash) flashattr.fg = flashattr.bg; break; case 0x03 : // color Flashing if (doflash) flashattr.fg = flashattr.fg + (flashattr.fg > 7 ? (-8) : 8); break; } RenderCharFB(flashchar, &flashattr); if (flashattr.doublew) col++; if (flashattr.doubleh) dhset = 1; m_updateTexture = true; } } if (dhset) { row++; m_RenderInfo.PosY += m_RenderInfo.FontHeight*factor; } m_RenderInfo.PosY += m_RenderInfo.FontHeight*factor; } } void CTeletextDecoder::DoRenderPage(int startrow, int national_subset_bak) { std::unique_lock lock(m_txtCache->m_critSection); /* display first column? */ m_RenderInfo.nofirst = m_RenderInfo.Show39; for (int row = 1; row < 24; row++) { int Byte = m_RenderInfo.PageChar[row*40]; if (Byte != ' ' && Byte != 0x00 && Byte != 0xFF && m_RenderInfo.PageAtrb[row*40].fg != m_RenderInfo.PageAtrb[row*40].bg) { m_RenderInfo.nofirst = 0; break; } } m_RenderInfo.FontWidth_Normal = m_RenderInfo.Width / (m_RenderInfo.nofirst ? 39 : 40); SetFontWidth(m_RenderInfo.FontWidth_Normal); if (m_RenderInfo.TranspMode || m_RenderInfo.Boxed) { FillBorder(GetColorRGB(TXT_ColorTransp));//ClearBB(transp); m_RenderInfo.ClearBBColor = TXT_ColorTransp; } /* get national subset */ if (m_txtCache->NationalSubset <= NAT_MAX_FROM_HEADER && /* not for GR/RU as long as line28 is not evaluated */ m_RenderInfo.PageInfo && m_RenderInfo.PageInfo->nationalvalid) /* individual subset according to page header */ { m_txtCache->NationalSubset = CountryConversionTable[m_RenderInfo.PageInfo->national]; } /* render page */ if (m_RenderInfo.PageInfo && (m_RenderInfo.PageInfo->function == FUNC_GDRCS || m_RenderInfo.PageInfo->function == FUNC_DRCS)) /* character definitions */ { #define DRCSROWS 8 #define DRCSCOLS (48/DRCSROWS) #define DRCSZOOMX 3 #define DRCSZOOMY 5 #define DRCSXSPC (12*DRCSZOOMX + 2) #define DRCSYSPC (10*DRCSZOOMY + 2) unsigned char ax[] = { /* array[0..12] of x-offsets, array[0..10] of y-offsets for each pixel */ DRCSZOOMX * 0, DRCSZOOMX * 1, DRCSZOOMX * 2, DRCSZOOMX * 3, DRCSZOOMX * 4, DRCSZOOMX * 5, DRCSZOOMX * 6, DRCSZOOMX * 7, DRCSZOOMX * 8, DRCSZOOMX * 9, DRCSZOOMX * 10, DRCSZOOMX * 11, DRCSZOOMX * 12, DRCSZOOMY * 0, DRCSZOOMY * 1, DRCSZOOMY * 2, DRCSZOOMY * 3, DRCSZOOMY * 4, DRCSZOOMY * 5, DRCSZOOMY * 6, DRCSZOOMY * 7, DRCSZOOMY * 8, DRCSZOOMY * 9, DRCSZOOMY * 10 }; ClearBB(TXT_ColorBlack); for (int col = 0; col < 24*40; col++) m_RenderInfo.PageAtrb[col] = Text_AtrTable[ATR_WB]; for (int row = 0; row < DRCSROWS; row++) { for (int col = 0; col < DRCSCOLS; col++) { RenderDRCS(m_RenderInfo.Width, m_RenderInfo.PageChar + 20 * (DRCSCOLS * row + col + 2), m_TextureBuffer + (m_RenderInfo.FontHeight + DRCSYSPC * row + m_RenderInfo.Height) * m_RenderInfo.Width + DRCSXSPC * col, ax, GetColorRGB(TXT_ColorWhite), GetColorRGB(TXT_ColorBlack)); } } memset(m_RenderInfo.PageChar + 40, 0xff, 24*40); /* don't render any char below row 0 */ } m_RenderInfo.PosY = startrow*m_RenderInfo.FontHeight; for (int row = startrow; row < 24; row++) { int index = row * 40; m_RenderInfo.PosX = 0; for (int col = m_RenderInfo.nofirst; col < 40; col++) { RenderCharBB(m_RenderInfo.PageChar[index + col], &m_RenderInfo.PageAtrb[index + col]); if (m_RenderInfo.PageAtrb[index + col].doubleh && m_RenderInfo.PageChar[index + col] != 0xff && row < 24-1) /* disable lower char in case of doubleh setting in l25 objects */ m_RenderInfo.PageChar[index + col + 40] = 0xff; if (m_RenderInfo.PageAtrb[index + col].doublew && col < 40-1) /* skip next column if double width */ { col++; if (m_RenderInfo.PageAtrb[index + col - 1].doubleh && m_RenderInfo.PageChar[index + col] != 0xff && row < 24-1) /* disable lower char in case of doubleh setting in l25 objects */ m_RenderInfo.PageChar[index + col + 40] = 0xff; } } m_RenderInfo.PosY += m_RenderInfo.FontHeight; } DoFlashing(startrow); /* update framebuffer */ CopyBB2FB(); m_txtCache->NationalSubset = national_subset_bak; } void CTeletextDecoder::Decode_BTT() { /* basic top table */ int current, b1, b2, b3, b4; unsigned char btt[23*40]; std::unique_lock lock(m_txtCache->m_critSection); if (m_txtCache->SubPageTable[0x1f0] == 0xff || 0 == m_txtCache->astCachetable[0x1f0][m_txtCache->SubPageTable[0x1f0]]) /* not yet received */ return; auto& components = CServiceBroker::GetAppComponents(); const auto appPlayer = components.GetComponent(); appPlayer->LoadPage(0x1f0, m_txtCache->SubPageTable[0x1f0], btt); if (btt[799] == ' ') /* not completely received or error */ return; current = 0x100; for (int i = 0; i < 800; i++) { b1 = btt[i]; if (b1 == ' ') b1 = 0; else { b1 = dehamming[b1]; if (b1 == 0xFF) /* hamming error in btt */ { btt[799] = ' '; /* mark btt as not received */ return; } } m_txtCache->BasicTop[current] = b1; CDVDTeletextTools::NextDec(¤t); } /* page linking table */ m_txtCache->ADIP_PgMax = -1; /* rebuild table of adip pages */ for (int i = 0; i < 10; i++) { b1 = dehamming[btt[800 + 8*i +0]]; if (b1 == 0xE) continue; /* unused */ else if (b1 == 0xF) break; /* end */ b4 = dehamming[btt[800 + 8*i +7]]; if (b4 != 2) /* only adip, ignore multipage (1) */ continue; b2 = dehamming[btt[800 + 8*i +1]]; b3 = dehamming[btt[800 + 8*i +2]]; if (b1 == 0xFF || b2 == 0xFF || b3 == 0xFF) { CLog::Log(LOGERROR, "CTeletextDecoder::Decode_BTT ", i); btt[799] = ' '; /* mark btt as not received */ return; } b1 = b1<<8 | b2<<4 | b3; /* page number */ m_txtCache->ADIP_Pg[++m_txtCache->ADIP_PgMax] = b1; } m_txtCache->BTTok = true; } void CTeletextDecoder::Decode_ADIP() /* additional information table */ { int i, p, j, b1, b2, b3, charfound; unsigned char padip[23*40]; std::unique_lock lock(m_txtCache->m_critSection); auto& components = CServiceBroker::GetAppComponents(); const auto appPlayer = components.GetComponent(); for (i = 0; i <= m_txtCache->ADIP_PgMax; i++) { p = m_txtCache->ADIP_Pg[i]; if (!p || m_txtCache->SubPageTable[p] == 0xff || 0 == m_txtCache->astCachetable[p][m_txtCache->SubPageTable[p]]) /* not cached (avoid segfault) */ continue; appPlayer->LoadPage(p, m_txtCache->SubPageTable[p], padip); for (j = 0; j < 44; j++) { b1 = dehamming[padip[20*j+0]]; if (b1 == 0xE) continue; /* unused */ if (b1 == 0xF) break; /* end */ b2 = dehamming[padip[20*j+1]]; b3 = dehamming[padip[20*j+2]]; if (b1 == 0xFF || b2 == 0xFF || b3 == 0xFF) { CLog::Log(LOGERROR, "CTeletextDecoder::Decode_BTT ", p, j, padip[20 * j + 0], padip[20 * j + 1], padip[20 * j + 2], b1, b2, b3); return; } if (b1>8 || b2>9 || b3>9) /* ignore entries with invalid or hex page numbers */ { continue; } b1 = b1<<8 | b2<<4 | b3; /* page number */ charfound = 0; /* flag: no printable char found */ for (b2 = 11; b2 >= 0; b2--) { b3 = deparity[padip[20*j + 8 + b2]]; if (b3 < ' ') b3 = ' '; if (b3 == ' ' && !charfound) m_txtCache->ADIPTable[b1][b2] = '\0'; else { m_txtCache->ADIPTable[b1][b2] = b3; charfound = 1; } } } /* next link j */ m_txtCache->ADIP_Pg[i] = 0; /* completely decoded: clear entry */ } /* next adip page i */ while ((m_txtCache->ADIP_PgMax >= 0) && !m_txtCache->ADIP_Pg[m_txtCache->ADIP_PgMax]) /* and shrink table */ m_txtCache->ADIP_PgMax--; } int CTeletextDecoder::TopText_GetNext(int startpage, int up, int findgroup) { int current, nextgrp, nextblk; int stoppage = (IsDec(startpage) ? startpage : startpage & 0xF00); // avoid endless loop in hexmode nextgrp = nextblk = 0; current = startpage; std::unique_lock lock(m_txtCache->m_critSection); do { if (up) CDVDTeletextTools::NextDec(¤t); else CDVDTeletextTools::PrevDec(¤t); if (!m_txtCache->BTTok || m_txtCache->BasicTop[current]) /* only if existent */ { if (findgroup) { if (m_txtCache->BasicTop[current] >= 6 && m_txtCache->BasicTop[current] <= 7) return current; if (!nextgrp && (current&0x00F) == 0) nextgrp = current; } if (m_txtCache->BasicTop[current] >= 2 && m_txtCache->BasicTop[current] <= 5) /* always find block */ return current; if (!nextblk && (current&0x0FF) == 0) nextblk = current; } } while (current != stoppage); if (nextgrp) return nextgrp; else if (nextblk) return nextblk; else return current; } void CTeletextDecoder::Showlink(int column, int linkpage) { unsigned char line[] = " >??? "; int oldfontwidth = m_RenderInfo.FontWidth; int yoffset; if (m_YOffset) yoffset = 0; else yoffset = m_RenderInfo.Height; int abx = ((m_RenderInfo.Width)%(40-m_RenderInfo.nofirst) == 0 ? m_RenderInfo.Width+1 : (m_RenderInfo.Width)/(((m_RenderInfo.Width)%(40-m_RenderInfo.nofirst)))+1);// distance between 'inserted' pixels int width = m_RenderInfo.Width /4; m_RenderInfo.PosY = 24*m_RenderInfo.FontHeight; if (m_RenderInfo.Boxed) { m_RenderInfo.PosX = column*width; FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY+yoffset, m_RenderInfo.Width, m_RenderInfo.FontHeight, GetColorRGB(TXT_ColorTransp)); return; } if (m_txtCache->ADIPTable[linkpage][0]) { m_RenderInfo.PosX = column*width; int l = strlen(m_txtCache->ADIPTable[linkpage]); if (l > 9) /* smaller font, if no space for one half space at front and end */ SetFontWidth(oldfontwidth * 10 / (l+1)); FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY+yoffset, width+(m_RenderInfo.Width%4), m_RenderInfo.FontHeight, GetColorRGB((enumTeletextColor)Text_AtrTable[ATR_L250 + column].bg)); m_RenderInfo.PosX += ((width) - (l*m_RenderInfo.FontWidth+l*m_RenderInfo.FontWidth/abx))/2; /* center */ for (char *p = m_txtCache->ADIPTable[linkpage]; *p; p++) RenderCharBB(*p, &Text_AtrTable[ATR_L250 + column]); SetFontWidth(oldfontwidth); } else /* display number */ { m_RenderInfo.PosX = column*width; FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY+yoffset, m_RenderInfo.Width-m_RenderInfo.PosX, m_RenderInfo.FontHeight, GetColorRGB((enumTeletextColor)Text_AtrTable[ATR_L250 + column].bg)); if (linkpage < m_txtCache->Page) { line[6] = '<'; CDVDTeletextTools::Hex2Str((char*)line + 5, linkpage); } else CDVDTeletextTools::Hex2Str((char*)line + 6, linkpage); for (unsigned char *p = line; p < line+9; p++) RenderCharBB(*p, &Text_AtrTable[ATR_L250 + column]); } } void CTeletextDecoder::CreateLine25() { std::unique_lock lock(m_txtCache->m_critSection); /* btt completely received and not yet decoded */ if (!m_txtCache->BTTok) Decode_BTT(); if (m_txtCache->ADIP_PgMax >= 0) Decode_ADIP(); if (!m_RenderInfo.ShowHex && m_RenderInfo.ShowFlof && (m_txtCache->FlofPages[m_txtCache->Page][0] || m_txtCache->FlofPages[m_txtCache->Page][1] || m_txtCache->FlofPages[m_txtCache->Page][2] || m_txtCache->FlofPages[m_txtCache->Page][3])) // FLOF-Navigation present { m_RenderInfo.Prev_100 = m_txtCache->FlofPages[m_txtCache->Page][0]; m_RenderInfo.Prev_10 = m_txtCache->FlofPages[m_txtCache->Page][1]; m_RenderInfo.Next_10 = m_txtCache->FlofPages[m_txtCache->Page][2]; m_RenderInfo.Next_100 = m_txtCache->FlofPages[m_txtCache->Page][3]; m_RenderInfo.PosY = 24*m_RenderInfo.FontHeight; m_RenderInfo.PosX = 0; for (int i=m_RenderInfo.nofirst; i<40; i++) RenderCharBB(m_RenderInfo.PageChar[24*40 + i], &m_RenderInfo.PageAtrb[24*40 + i]); } else { /* normal: blk-1, grp+1, grp+2, blk+1 */ /* hex: hex+1, blk-1, grp+1, blk+1 */ if (m_RenderInfo.ShowHex) { /* arguments: startpage, up, findgroup */ m_RenderInfo.Prev_100 = NextHex(m_txtCache->Page); m_RenderInfo.Prev_10 = TopText_GetNext(m_txtCache->Page, 0, 0); m_RenderInfo.Next_10 = TopText_GetNext(m_txtCache->Page, 1, 1); } else { m_RenderInfo.Prev_100 = TopText_GetNext(m_txtCache->Page, 0, 0); m_RenderInfo.Prev_10 = TopText_GetNext(m_txtCache->Page, 1, 1); m_RenderInfo.Next_10 = TopText_GetNext(m_RenderInfo.Prev_10, 1, 1); } m_RenderInfo.Next_100 = TopText_GetNext(m_RenderInfo.Next_10, 1, 0); Showlink(0, m_RenderInfo.Prev_100); Showlink(1, m_RenderInfo.Prev_10); Showlink(2, m_RenderInfo.Next_10); Showlink(3, m_RenderInfo.Next_100); } } void CTeletextDecoder::RenderCharFB(int Char, TextPageAttr_t *Attribute) { RenderCharIntern(&m_RenderInfo, Char, Attribute, m_RenderInfo.ZoomMode, m_YOffset); } void CTeletextDecoder::RenderCharBB(int Char, TextPageAttr_t *Attribute) { RenderCharIntern(&m_RenderInfo, Char, Attribute, 0, m_RenderInfo.Height-m_YOffset); } void CTeletextDecoder::CopyBB2FB() { UTILS::COLOR::Color *src, *dst, *topsrc; int screenwidth; UTILS::COLOR::Color fillcolor; std::unique_lock lock(m_txtCache->m_critSection); /* line 25 */ if (!m_RenderInfo.PageCatching) CreateLine25(); /* copy backbuffer to framebuffer */ if (!m_RenderInfo.ZoomMode) { if (m_YOffset) m_YOffset = 0; else m_YOffset = m_RenderInfo.Height; if (m_RenderInfo.ClearBBColor >= 0) { m_RenderInfo.ClearBBColor = -1; } return; } src = dst = topsrc = m_TextureBuffer + m_RenderInfo.Width; if (m_YOffset) { dst += m_RenderInfo.Width * m_RenderInfo.Height; } else { src += m_RenderInfo.Width * m_RenderInfo.Height; topsrc += m_RenderInfo.Width * m_RenderInfo.Height; } if (!m_RenderInfo.PageCatching) SDL_memcpy4(dst+(24*m_RenderInfo.FontHeight)*m_RenderInfo.Width, src + (24*m_RenderInfo.FontHeight)*m_RenderInfo.Width, m_RenderInfo.Width*m_RenderInfo.FontHeight); /* copy line25 in normal height */ if (m_RenderInfo.TranspMode) fillcolor = GetColorRGB(TXT_ColorTransp); else fillcolor = GetColorRGB((enumTeletextColor)m_txtCache->FullScrColor); if (m_RenderInfo.ZoomMode == 2) src += 12*m_RenderInfo.FontHeight*m_RenderInfo.Width; screenwidth = m_RenderInfo.Width; for (int i = 12*m_RenderInfo.FontHeight; i; i--) { SDL_memcpy4(dst, src, screenwidth); dst += m_RenderInfo.Width; SDL_memcpy4(dst, src, screenwidth); dst += m_RenderInfo.Width; src += m_RenderInfo.Width; } for (int i = m_RenderInfo.Height - 25*m_RenderInfo.FontHeight; i >= 0;i--) { SDL_memset4(dst + m_RenderInfo.Width*(m_RenderInfo.FontHeight+i), fillcolor, screenwidth); } } FT_Error CTeletextDecoder::MyFaceRequester(FTC_FaceID face_id, FT_Library library, FT_Pointer request_data, FT_Face *aface) { FT_Error result = FT_New_Face(library, (const char*)face_id, 0, aface); if (!result) CLog::Log(LOGINFO, "Teletext font {} loaded", (char*)face_id); else CLog::Log(LOGERROR, "Opening of Teletext font {} failed", (char*)face_id); return result; } void CTeletextDecoder::SetFontWidth(int newWidth) { if (m_RenderInfo.FontWidth != newWidth) { m_RenderInfo.FontWidth = newWidth; m_TypeTTF.width = (FT_UShort) m_RenderInfo.FontWidth; for (int i = 0; i <= 12; i++) m_RenderInfo.axdrcs[i] = (m_RenderInfo.FontWidth * i + 6) / 12; } } int CTeletextDecoder::GetCurFontWidth() { int mx = (m_RenderInfo.Width)%(40-m_RenderInfo.nofirst); // # of unused pixels int abx = (mx == 0 ? m_RenderInfo.Width+1 : (m_RenderInfo.Width)/(mx+1)); // distance between 'inserted' pixels int nx = abx+1-(m_RenderInfo.PosX % (abx+1)); // # of pixels to next insert return m_RenderInfo.FontWidth+(((m_RenderInfo.PosX+m_RenderInfo.FontWidth+1) <= m_RenderInfo.Width && nx <= m_RenderInfo.FontWidth+1) ? 1 : 0); } void CTeletextDecoder::SetPosX(int column) { m_RenderInfo.PosX = 0; for (int i = 0; i < column-m_RenderInfo.nofirst; i++) m_RenderInfo.PosX += GetCurFontWidth(); } void CTeletextDecoder::ClearBB(UTILS::COLOR::Color Color) { SDL_memset4(m_TextureBuffer + (m_RenderInfo.Height-m_YOffset)*m_RenderInfo.Width, Color, m_RenderInfo.Width*m_RenderInfo.Height); } void CTeletextDecoder::ClearFB(UTILS::COLOR::Color Color) { SDL_memset4(m_TextureBuffer + m_RenderInfo.Width*m_YOffset, Color, m_RenderInfo.Width*m_RenderInfo.Height); } void CTeletextDecoder::FillBorder(UTILS::COLOR::Color Color) { FillRect(m_TextureBuffer + (m_RenderInfo.Height-m_YOffset)*m_RenderInfo.Width, m_RenderInfo.Width, 0, 25*m_RenderInfo.FontHeight, m_RenderInfo.Width, m_RenderInfo.Height-(25*m_RenderInfo.FontHeight), Color); FillRect(m_TextureBuffer + m_RenderInfo.Width*m_YOffset, m_RenderInfo.Width, 0, 25*m_RenderInfo.FontHeight, m_RenderInfo.Width, m_RenderInfo.Height-(25*m_RenderInfo.FontHeight), Color); } void CTeletextDecoder::FillRect( UTILS::COLOR::Color* buffer, int xres, int x, int y, int w, int h, UTILS::COLOR::Color Color) { if (!buffer) return; UTILS::COLOR::Color* p = buffer + x + y * xres; if (w > 0) { for ( ; h > 0 ; h--) { SDL_memset4(p, Color, w); p += xres; } } } void CTeletextDecoder::DrawVLine( UTILS::COLOR::Color* lfb, int xres, int x, int y, int l, UTILS::COLOR::Color color) { if (!lfb) return; UTILS::COLOR::Color* p = lfb + x + y * xres; for ( ; l > 0 ; l--) { *p = color; p += xres; } } void CTeletextDecoder::DrawHLine( UTILS::COLOR::Color* lfb, int xres, int x, int y, int l, UTILS::COLOR::Color color) { if (!lfb) return; if (l > 0) SDL_memset4(lfb + x + y * xres, color, l); } void CTeletextDecoder::RenderDRCS( int xres, unsigned char* s, /* pointer to char data, parity undecoded */ UTILS::COLOR::Color* d, /* pointer to frame buffer of top left pixel */ unsigned char* ax, /* array[0..12] of x-offsets, array[0..10] of y-offsets for each pixel */ UTILS::COLOR::Color fgcolor, UTILS::COLOR::Color bgcolor) { if (d == NULL) return; unsigned char *ay = ax + 13; /* array[0..10] of y-offsets for each pixel */ for (int y = 0; y < 10; y++) /* 10*2 bytes a 6 pixels per char definition */ { unsigned char c1 = deparity[*s++]; unsigned char c2 = deparity[*s++]; int h = ay[y+1] - ay[y]; if (!h) continue; if (((c1 == ' ') && (*(s-2) != ' ')) || ((c2 == ' ') && (*(s-1) != ' '))) /* parity error: stop decoding FIXME */ return; for (int bit = 0x20, x = 0; bit; bit >>= 1, x++) /* bit mask (MSB left), column counter */ { UTILS::COLOR::Color f1 = (c1 & bit) ? fgcolor : bgcolor; UTILS::COLOR::Color f2 = (c2 & bit) ? fgcolor : bgcolor; for (int i = 0; i < h; i++) { if (ax[x+1] > ax[x]) SDL_memset4(d + ax[x], f1, ax[x+1] - ax[x]); if (ax[x+7] > ax[x+6]) SDL_memset4(d + ax[x+6], f2, ax[x+7] - ax[x+6]); /* 2nd byte 6 pixels to the right */ d += xres; } d -= h * xres; } d += h * xres; } } void CTeletextDecoder::FillRectMosaicSeparated(UTILS::COLOR::Color* lfb, int xres, int x, int y, int w, int h, UTILS::COLOR::Color fgcolor, UTILS::COLOR::Color bgcolor, int set) { if (!lfb) return; FillRect(lfb,xres,x, y, w, h, bgcolor); if (set) { FillRect(lfb,xres,x+1, y+1, w-2, h-2, fgcolor); } } void CTeletextDecoder::FillTrapez(UTILS::COLOR::Color* lfb, int xres, int x0, int y0, int l0, int xoffset1, int h, int l1, UTILS::COLOR::Color color) { UTILS::COLOR::Color* p = lfb + x0 + y0 * xres; int xoffset, l; for (int yoffset = 0; yoffset < h; yoffset++) { l = l0 + ((l1-l0) * yoffset + h/2) / h; xoffset = (xoffset1 * yoffset + h/2) / h; if (l > 0) SDL_memset4(p + xoffset, color, l); p += xres; } } void CTeletextDecoder::FlipHorz(UTILS::COLOR::Color* lfb, int xres, int x, int y, int w, int h) { UTILS::COLOR::Color buf[2048]; UTILS::COLOR::Color* p = lfb + x + y * xres; int w1,h1; for (h1 = 0 ; h1 < h ; h1++) { SDL_memcpy4(buf,p,w); for (w1 = 0 ; w1 < w ; w1++) { *(p+w1) = buf[w-(w1+1)]; } p += xres; } } void CTeletextDecoder::FlipVert(UTILS::COLOR::Color* lfb, int xres, int x, int y, int w, int h) { UTILS::COLOR::Color buf[2048]; UTILS::COLOR::Color *p = lfb + x + y * xres, *p1, *p2; int h1; for (h1 = 0 ; h1 < h/2 ; h1++) { p1 = (p+(h1*xres)); p2 = (p+(h-(h1+1))*xres); SDL_memcpy4(buf, p1, w); SDL_memcpy4(p1, p2, w); SDL_memcpy4(p2, buf, w); } } int CTeletextDecoder::ShapeCoord(int param, int curfontwidth, int curFontHeight) { switch (param) { case S_W13: return curfontwidth/3; case S_W12: return curfontwidth/2; case S_W23: return curfontwidth*2/3; case S_W11: return curfontwidth; case S_WM3: return curfontwidth-3; case S_H13: return curFontHeight/3; case S_H12: return curFontHeight/2; case S_H23: return curFontHeight*2/3; case S_H11: return curFontHeight; default: return param; } } void CTeletextDecoder::DrawShape(UTILS::COLOR::Color* lfb, int xres, int x, int y, int shapenumber, int curfontwidth, int FontHeight, int curFontHeight, UTILS::COLOR::Color fgcolor, UTILS::COLOR::Color bgcolor, bool clear) { if (!lfb || shapenumber < 0x20 || shapenumber > 0x7e || (shapenumber == 0x7e && clear)) return; unsigned char *p = aShapes[shapenumber - 0x20]; if (*p == S_INV) { int t = fgcolor; fgcolor = bgcolor; bgcolor = t; p++; } if (clear) FillRect(lfb, xres, x, y, curfontwidth, FontHeight, bgcolor); while (*p != S_END) { switch (*p++) { case S_FHL: { int offset = ShapeCoord(*p++, curfontwidth, curFontHeight); DrawHLine(lfb, xres, x, y + offset, curfontwidth, fgcolor); break; } case S_FVL: { int offset = ShapeCoord(*p++, curfontwidth, curFontHeight); DrawVLine(lfb,xres,x + offset, y, FontHeight, fgcolor); break; } case S_FLH: FlipHorz(lfb,xres,x,y,curfontwidth, FontHeight); break; case S_FLV: FlipVert(lfb,xres,x,y,curfontwidth, FontHeight); break; case S_BOX: { int xo = ShapeCoord(*p++, curfontwidth, curFontHeight); int yo = ShapeCoord(*p++, curfontwidth, curFontHeight); int w = ShapeCoord(*p++, curfontwidth, curFontHeight); int h = ShapeCoord(*p++, curfontwidth, curFontHeight); FillRect(lfb,xres,x + xo, y + yo, w, h, fgcolor); break; } case S_TRA: { int x0 = ShapeCoord(*p++, curfontwidth, curFontHeight); int y0 = ShapeCoord(*p++, curfontwidth, curFontHeight); int l0 = ShapeCoord(*p++, curfontwidth, curFontHeight); int x1 = ShapeCoord(*p++, curfontwidth, curFontHeight); int y1 = ShapeCoord(*p++, curfontwidth, curFontHeight); int l1 = ShapeCoord(*p++, curfontwidth, curFontHeight); FillTrapez(lfb, xres,x + x0, y + y0, l0, x1-x0, y1-y0, l1, fgcolor); break; } case S_BTR: { int x0 = ShapeCoord(*p++, curfontwidth, curFontHeight); int y0 = ShapeCoord(*p++, curfontwidth, curFontHeight); int l0 = ShapeCoord(*p++, curfontwidth, curFontHeight); int x1 = ShapeCoord(*p++, curfontwidth, curFontHeight); int y1 = ShapeCoord(*p++, curfontwidth, curFontHeight); int l1 = ShapeCoord(*p++, curfontwidth, curFontHeight); FillTrapez(lfb, xres, x + x0, y + y0, l0, x1-x0, y1-y0, l1, bgcolor); break; } case S_LNK: { DrawShape(lfb,xres,x, y, ShapeCoord(*p, curfontwidth, curFontHeight), curfontwidth, FontHeight, curFontHeight, fgcolor, bgcolor, false); break; } default: break; } } } void CTeletextDecoder::RenderCharIntern(TextRenderInfo_t* RenderInfo, int Char, TextPageAttr_t *Attribute, int zoom, int yoffset) { int Row, Pitch; int glyph; UTILS::COLOR::Color bgcolor, fgcolor; int factor, xfactor; unsigned char *sbitbuffer; std::unique_lock lock(m_txtCache->m_critSection); int national_subset_local = m_txtCache->NationalSubset; int curfontwidth = GetCurFontWidth(); int t = curfontwidth; m_RenderInfo.PosX += t; int curfontwidth2 = GetCurFontWidth(); m_RenderInfo.PosX -= t; int alphachar = RenderChar(m_TextureBuffer+(yoffset)*m_RenderInfo.Width, m_RenderInfo.Width, Char, &m_RenderInfo.PosX, m_RenderInfo.PosY, Attribute, zoom > 0, curfontwidth, curfontwidth2, m_RenderInfo.FontHeight, m_RenderInfo.TranspMode, m_RenderInfo.axdrcs, m_Ascender); if (alphachar <= 0) return; if (zoom && Attribute->doubleh) factor = 4; else if (zoom || Attribute->doubleh) factor = 2; else factor = 1; fgcolor = GetColorRGB((enumTeletextColor)Attribute->fg); if (m_RenderInfo.TranspMode && m_RenderInfo.PosY < 24*m_RenderInfo.FontHeight) { bgcolor = GetColorRGB(TXT_ColorTransp); } else { bgcolor = GetColorRGB((enumTeletextColor)Attribute->bg); } if (Attribute->doublew) { curfontwidth += curfontwidth2; xfactor = 2; } else xfactor = 1; // Check if the alphanumeric char has diacritical marks (or results from composing chars) or // on the other hand it is just a simple alphanumeric char if (!Attribute->diacrit) { Char = alphachar; } else { if ((national_subset_local == NAT_SC) || (national_subset_local == NAT_RB) || (national_subset_local == NAT_UA)) Char = G2table[1][0x20 + Attribute->diacrit]; else if (national_subset_local == NAT_GR) Char = G2table[2][0x20 + Attribute->diacrit]; else if (national_subset_local == NAT_HB) Char = G2table[3][0x20 + Attribute->diacrit]; else if (national_subset_local == NAT_AR) Char = G2table[4][0x20 + Attribute->diacrit]; else Char = G2table[0][0x20 + Attribute->diacrit]; // use harfbuzz to combine the diacritical mark with the alphanumeric char // fallback to the alphanumeric char if composition fails hb_unicode_funcs_t* ufuncs = hb_unicode_funcs_get_default(); hb_codepoint_t composedChar; const hb_bool_t isComposed = hb_unicode_compose(ufuncs, alphachar, Char, &composedChar); Char = isComposed ? composedChar : alphachar; } /* render char */ if (!(glyph = FT_Get_Char_Index(m_Face, Char))) { CLog::Log(LOGERROR, "{}: buffer; int backupTTFshiftY = m_RenderInfo.TTFShiftY; if (national_subset_local == NAT_AR) m_RenderInfo.TTFShiftY = backupTTFshiftY - 2; // for arabic TTF font should be shifted up slightly UTILS::COLOR::Color* p; int f; /* running counter for zoom factor */ int he = m_sBit->height; // sbit->height should not be altered, I guess Row = factor * (m_Ascender - m_sBit->top + m_RenderInfo.TTFShiftY); if (Row < 0) { sbitbuffer -= m_sBit->pitch*Row; he += Row; Row = 0; } else { FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY + yoffset, curfontwidth, Row, bgcolor); /* fill upper margin */ } if (m_Ascender - m_sBit->top + m_RenderInfo.TTFShiftY + he > m_RenderInfo.FontHeight) he = m_RenderInfo.FontHeight - m_Ascender + m_sBit->top - m_RenderInfo.TTFShiftY; /* limit char height to defined/calculated FontHeight */ if (he < 0) he = m_RenderInfo.FontHeight; p = m_TextureBuffer + m_RenderInfo.PosX + (yoffset + m_RenderInfo.PosY + Row) * m_RenderInfo.Width; /* running pointer into framebuffer */ for (Row = he; Row; Row--) /* row counts up, but down may be a little faster :) */ { int pixtodo = m_sBit->width; UTILS::COLOR::Color* pstart = p; for (int Bit = xfactor * (m_sBit->left + m_RenderInfo.TTFShiftX); Bit > 0; Bit--) /* fill left margin */ { for (f = factor-1; f >= 0; f--) *(p + f*m_RenderInfo.Width) = bgcolor; p++; } for (Pitch = m_sBit->pitch; Pitch; Pitch--) { for (int Bit = 0x80; Bit; Bit >>= 1) { UTILS::COLOR::Color color; if (--pixtodo < 0) break; if (*sbitbuffer & Bit) /* bit set -> foreground */ color = fgcolor; else /* bit not set -> background */ color = bgcolor; for (f = factor-1; f >= 0; f--) *(p + f*m_RenderInfo.Width) = color; p++; if (xfactor > 1) /* double width */ { for (f = factor-1; f >= 0; f--) *(p + f*m_RenderInfo.Width) = color; p++; } } sbitbuffer++; } for (int Bit = (curfontwidth - xfactor*(m_sBit->width + m_sBit->left + m_RenderInfo.TTFShiftX)); Bit > 0; Bit--) /* fill rest of char width */ { for (f = factor-1; f >= 0; f--) *(p + f*m_RenderInfo.Width) = bgcolor; p++; } p = pstart + factor*m_RenderInfo.Width; } Row = m_Ascender - m_sBit->top + he + m_RenderInfo.TTFShiftY; FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY + yoffset + Row * factor, curfontwidth, (m_RenderInfo.FontHeight - Row) * factor, bgcolor); /* fill lower margin */ if (Attribute->underline) FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY + yoffset + (m_RenderInfo.FontHeight-2)* factor, curfontwidth, 2*factor, fgcolor); /* underline char */ m_RenderInfo.PosX += curfontwidth; m_RenderInfo.TTFShiftY = backupTTFshiftY; // restore TTFShiftY } int CTeletextDecoder::RenderChar( UTILS::COLOR::Color* buffer, // pointer to render buffer, min. FontHeight*2*xres int xres, // length of 1 line in render buffer int Char, // character to render int* pPosX, // left border for rendering relative to *buffer, will be set to right border after rendering int PosY, // vertical position of char in *buffer TextPageAttr_t* Attribute, // Attributes of Char bool zoom, // 1= character will be rendered in double height int curfontwidth, // rendering width of character int curfontwidth2, // rendering width of next character (needed for doublewidth) int FontHeight, // height of character bool transpmode, // 1= transparent display unsigned char* axdrcs, // width and height of DRCS-chars int Ascender) // Ascender of font { UTILS::COLOR::Color bgcolor, fgcolor; int factor, xfactor; std::unique_lock lock(m_txtCache->m_critSection); int national_subset_local = m_txtCache->NationalSubset; int ymosaic[4]; ymosaic[0] = 0; /* y-offsets for 2*3 mosaic */ ymosaic[1] = (FontHeight + 1) / 3; ymosaic[2] = (FontHeight * 2 + 1) / 3; ymosaic[3] = FontHeight; if (Attribute->setX26) { national_subset_local = 0; // no national subset } // G0+G2 set designation if (Attribute->setG0G2 != 0x3f) { switch (Attribute->setG0G2) { case 0x20 : national_subset_local = NAT_SC; break; case 0x24 : national_subset_local = NAT_RB; break; case 0x25 : national_subset_local = NAT_UA; break; case 0x37: national_subset_local = NAT_GR; break; case 0x55: national_subset_local = NAT_HB; break; case 0x47: case 0x57: national_subset_local = NAT_AR; break; default: national_subset_local = CountryConversionTable[Attribute->setG0G2 & 0x07]; break; } } if (Attribute->charset == C_G0S) // use secondary charset national_subset_local = m_txtCache->NationalSubsetSecondary; if (zoom && Attribute->doubleh) factor = 4; else if (zoom || Attribute->doubleh) factor = 2; else factor = 1; if (Attribute->doublew) { curfontwidth += curfontwidth2; xfactor = 2; } else xfactor = 1; if (Char == 0xFF) /* skip doubleheight chars in lower line */ { *pPosX += curfontwidth; return -1; } /* get colors */ if (Attribute->inverted) { int t = Attribute->fg; Attribute->fg = Attribute->bg; Attribute->bg = t; } fgcolor = GetColorRGB((enumTeletextColor)Attribute->fg); if (transpmode == true && PosY < 24*FontHeight) { bgcolor = GetColorRGB(TXT_ColorTransp); } else { bgcolor = GetColorRGB((enumTeletextColor)Attribute->bg); } /* handle mosaic */ if ((Attribute->charset == C_G1C || Attribute->charset == C_G1S) && ((Char&0xA0) == 0x20)) { int w1 = (curfontwidth / 2 ) *xfactor; int w2 = (curfontwidth - w1) *xfactor; Char = (Char & 0x1f) | ((Char & 0x40) >> 1); if (Attribute->charset == C_G1S) /* separated mosaic */ { for (int y = 0; y < 3; y++) { FillRectMosaicSeparated(buffer, xres,*pPosX, PosY + ymosaic[y]*factor, w1, (ymosaic[y+1] - ymosaic[y])*factor, fgcolor, bgcolor, Char & 0x01); FillRectMosaicSeparated(buffer, xres,*pPosX + w1, PosY + ymosaic[y]*factor, w2, (ymosaic[y+1] - ymosaic[y])*factor, fgcolor, bgcolor, Char & 0x02); Char >>= 2; } } else { for (int y = 0; y < 3; y++) { FillRect(buffer, xres, *pPosX, PosY + ymosaic[y]*factor, w1, (ymosaic[y+1] - ymosaic[y])*factor, (Char & 0x01) ? fgcolor : bgcolor); FillRect(buffer, xres, *pPosX + w1, PosY + ymosaic[y]*factor, w2, (ymosaic[y+1] - ymosaic[y])*factor, (Char & 0x02) ? fgcolor : bgcolor); Char >>= 2; } } *pPosX += curfontwidth; return 0; } if (Attribute->charset == C_G3) { if (Char < 0x20 || Char > 0x7d) { Char = 0x20; } else { if (*aShapes[Char - 0x20] == S_CHR) { unsigned char *p = aShapes[Char - 0x20]; Char = (*(p+1) <<8) + (*(p+2)); } else if (*aShapes[Char - 0x20] == S_ADT) { if (buffer) { int x,y,f,c; UTILS::COLOR::Color* p = buffer + *pPosX + PosY * xres; for (y=0; ycharset >= C_OFFSET_DRCS) { TextCachedPage_t *pcache = m_txtCache->astCachetable[(Attribute->charset & 0x10) ? m_txtCache->drcs : m_txtCache->gdrcs][Attribute->charset & 0x0f]; if (pcache) { unsigned char drcs_data[23*40]; auto& components = CServiceBroker::GetAppComponents(); const auto appPlayer = components.GetComponent(); appPlayer->LoadPage((Attribute->charset & 0x10) ? m_txtCache->drcs : m_txtCache->gdrcs, Attribute->charset & 0x0f, drcs_data); unsigned char *p; if (Char < 23*2) p = drcs_data + 20*Char; else if (pcache->pageinfo.p24) p = pcache->pageinfo.p24 + 20*(Char - 23*2); else { FillRect(buffer, xres,*pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor); *pPosX += curfontwidth; return 0; } axdrcs[12] = curfontwidth; /* adjust last x-offset according to position, FIXME: double width */ RenderDRCS(xres, p, buffer + *pPosX + PosY * xres, axdrcs, fgcolor, bgcolor); } else { FillRect(buffer,xres,*pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor); } *pPosX += curfontwidth; return 0; } else if (Attribute->charset == C_G2 && Char >= 0x20 && Char <= 0x7F) { if ((national_subset_local == NAT_SC) || (national_subset_local == NAT_RB) || (national_subset_local == NAT_UA)) Char = G2table[1][Char-0x20]; else if (national_subset_local == NAT_GR) Char = G2table[2][Char-0x20]; else if (national_subset_local == NAT_AR) Char = G2table[3][Char-0x20]; else Char = G2table[0][Char-0x20]; //if (Char == 0x7F) //{ // FillRect(buffer,xres,*pPosX, PosY, curfontwidth, factor*Ascender, fgcolor); // FillRect(buffer,xres,*pPosX, PosY + factor*Ascender, curfontwidth, factor*(FontHeight-Ascender), bgcolor); // *pPosX += curfontwidth; // return 0; //} } else if (national_subset_local == NAT_SC && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for serbian/croatian */ Char = G0table[0][Char-0x20]; else if (national_subset_local == NAT_RB && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for russian/bulgarian */ Char = G0table[1][Char-0x20]; else if (national_subset_local == NAT_UA && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for ukrainian */ Char = G0table[2][Char-0x20]; else if (national_subset_local == NAT_GR && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for greek */ Char = G0table[3][Char-0x20]; else if (national_subset_local == NAT_HB && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for hebrew */ Char = G0table[4][Char-0x20]; else if (national_subset_local == NAT_AR && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for arabic */ Char = G0table[5][Char-0x20]; else { /* load char */ switch (Char) { case 0x00: case 0x20: FillRect(buffer, xres, *pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor); *pPosX += curfontwidth; return -3; case 0x23: case 0x24: Char = nationaltable23[national_subset_local][Char-0x23]; break; case 0x40: Char = nationaltable40[national_subset_local]; break; case 0x5B: case 0x5C: case 0x5D: case 0x5E: case 0x5F: case 0x60: Char = nationaltable5b[national_subset_local][Char-0x5B]; break; case 0x7B: case 0x7C: case 0x7D: case 0x7E: Char = nationaltable7b[national_subset_local][Char-0x7B]; break; case 0x7F: FillRect(buffer,xres,*pPosX, PosY , curfontwidth, factor*Ascender, fgcolor); FillRect(buffer,xres,*pPosX, PosY + factor*Ascender, curfontwidth, factor*(FontHeight-Ascender), bgcolor); *pPosX += curfontwidth; return 0; case 0xE0: /* |- */ DrawHLine(buffer,xres,*pPosX, PosY, curfontwidth, fgcolor); DrawVLine(buffer,xres,*pPosX, PosY +1, FontHeight -1, fgcolor); FillRect(buffer,xres,*pPosX +1, PosY +1, curfontwidth-1, FontHeight-1, bgcolor); *pPosX += curfontwidth; return 0; case 0xE1: /* - */ DrawHLine(buffer,xres,*pPosX, PosY, curfontwidth, fgcolor); FillRect(buffer,xres,*pPosX, PosY +1, curfontwidth, FontHeight-1, bgcolor); *pPosX += curfontwidth; return 0; case 0xE2: /* -| */ DrawHLine(buffer,xres,*pPosX, PosY, curfontwidth, fgcolor); DrawVLine(buffer,xres,*pPosX + curfontwidth -1, PosY +1, FontHeight -1, fgcolor); FillRect(buffer,xres,*pPosX, PosY +1, curfontwidth-1, FontHeight-1, bgcolor); *pPosX += curfontwidth; return 0; case 0xE3: /* | */ DrawVLine(buffer,xres,*pPosX, PosY, FontHeight, fgcolor); FillRect(buffer,xres,*pPosX +1, PosY, curfontwidth -1, FontHeight, bgcolor); *pPosX += curfontwidth; return 0; case 0xE4: /* | */ DrawVLine(buffer,xres,*pPosX + curfontwidth -1, PosY, FontHeight, fgcolor); FillRect(buffer,xres,*pPosX, PosY, curfontwidth -1, FontHeight, bgcolor); *pPosX += curfontwidth; return 0; case 0xE5: /* |_ */ DrawHLine(buffer,xres,*pPosX, PosY + FontHeight -1, curfontwidth, fgcolor); DrawVLine(buffer,xres,*pPosX, PosY, FontHeight -1, fgcolor); FillRect(buffer,xres,*pPosX +1, PosY, curfontwidth-1, FontHeight-1, bgcolor); *pPosX += curfontwidth; return 0; case 0xE6: /* _ */ DrawHLine(buffer,xres,*pPosX, PosY + FontHeight -1, curfontwidth, fgcolor); FillRect(buffer,xres,*pPosX, PosY, curfontwidth, FontHeight-1, bgcolor); *pPosX += curfontwidth; return 0; case 0xE7: /* _| */ DrawHLine(buffer,xres,*pPosX, PosY + FontHeight -1, curfontwidth, fgcolor); DrawVLine(buffer,xres,*pPosX + curfontwidth -1, PosY, FontHeight -1, fgcolor); FillRect(buffer,xres,*pPosX, PosY, curfontwidth-1, FontHeight-1, bgcolor); *pPosX += curfontwidth; return 0; case 0xE8: /* Ii */ FillRect(buffer,xres,*pPosX +1, PosY, curfontwidth -1, FontHeight, bgcolor); for (int Row=0; Row < curfontwidth/2; Row++) DrawVLine(buffer,xres,*pPosX + Row, PosY + Row, FontHeight - Row, fgcolor); *pPosX += curfontwidth; return 0; case 0xE9: /* II */ FillRect(buffer,xres,*pPosX, PosY, curfontwidth/2, FontHeight, fgcolor); FillRect(buffer,xres,*pPosX + curfontwidth/2, PosY, (curfontwidth+1)/2, FontHeight, bgcolor); *pPosX += curfontwidth; return 0; case 0xEA: /* ∞ */ FillRect(buffer,xres,*pPosX, PosY, curfontwidth, FontHeight, bgcolor); FillRect(buffer,xres,*pPosX, PosY, curfontwidth/2, curfontwidth/2, fgcolor); *pPosX += curfontwidth; return 0; case 0xEB: /* ¨ */ FillRect(buffer,xres,*pPosX, PosY +1, curfontwidth, FontHeight -1, bgcolor); for (int Row=0; Row < curfontwidth/2; Row++) DrawHLine(buffer,xres,*pPosX + Row, PosY + Row, curfontwidth - Row, fgcolor); *pPosX += curfontwidth; return 0; case 0xEC: /* -- */ FillRect(buffer, xres,*pPosX, PosY, curfontwidth, curfontwidth/2, fgcolor); FillRect(buffer, xres,*pPosX, PosY + curfontwidth/2, curfontwidth, FontHeight - curfontwidth/2, bgcolor); *pPosX += curfontwidth; return 0; case 0xED: case 0xEE: case 0xEF: case 0xF0: case 0xF1: case 0xF2: case 0xF3: case 0xF4: Char = arrowtable[Char - 0xED]; break; default: break; } } if (Char <= 0x20) { FillRect(buffer, xres, *pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor); *pPosX += curfontwidth; return -2; } return Char; // Char is an alphanumeric unicode character } TextPageinfo_t* CTeletextDecoder::DecodePage(bool showl25, // 1=decode Level2.5-graphics unsigned char* PageChar, // page buffer, min. 25*40 TextPageAttr_t *PageAtrb, // attribute buffer, min 25*40 bool HintMode, // 1=show hidden information bool showflof) // 1=decode FLOF-line { int col; int hold, dhset; int foreground, background, doubleheight, doublewidth, charset, previous_charset, mosaictype, IgnoreAtBlackBgSubst, concealed, flashmode, boxwin; unsigned char held_mosaic, *p; TextCachedPage_t *pCachedPage; std::unique_lock lock(m_txtCache->m_critSection); /* copy page to decode buffer */ if (m_txtCache->SubPageTable[m_txtCache->Page] == 0xff) /* not cached: do nothing */ return NULL; if (m_txtCache->ZapSubpageManual) pCachedPage = m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage]; else pCachedPage = m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPageTable[m_txtCache->Page]]; if (!pCachedPage) /* not cached: do nothing */ return nullptr; auto& components = CServiceBroker::GetAppComponents(); const auto appPlayer = components.GetComponent(); appPlayer->LoadPage(m_txtCache->Page, m_txtCache->SubPage, &PageChar[40]); memcpy(&PageChar[8], pCachedPage->p0, 24); /* header line without TimeString */ TextPageinfo_t* PageInfo = &(pCachedPage->pageinfo); if (PageInfo->p24) memcpy(&PageChar[24*40], PageInfo->p24, 40); /* line 25 for FLOF */ /* copy TimeString */ memcpy(&PageChar[32], &m_txtCache->TimeString, 8); bool boxed; /* check for newsflash & subtitle */ if (PageInfo->boxed && IsDec(m_txtCache->Page)) boxed = true; else boxed = false; /* modify header */ if (boxed) { memset(PageChar, ' ', 40); } else { memset(PageChar, ' ', 8); CDVDTeletextTools::Hex2Str((char*)PageChar+3, m_txtCache->Page); if (m_txtCache->SubPage) { *(PageChar+4) ='/'; *(PageChar+5) ='0'; CDVDTeletextTools::Hex2Str((char*)PageChar+6, m_txtCache->SubPage); } } if (!IsDec(m_txtCache->Page)) { TextPageAttr_t atr = { TXT_ColorWhite , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}; if (PageInfo->function == FUNC_MOT) /* magazine organization table */ { for (col = 0; col < 24*40; col++) PageAtrb[col] = atr; for (col = 40; col < 24*40; col++) PageChar[col] = number2char(PageChar[col]); return PageInfo; /* don't interpret irregular pages */ } else if (PageInfo->function == FUNC_GPOP || PageInfo->function == FUNC_POP) /* object definitions */ { for (int col = 0; col < 24*40; col++) PageAtrb[col] = atr; p = PageChar + 40; for (int row = 1; row < 12; row++) { *p++ = number2char(row); /* first column: number (0-9, A-..) */ for (int col = 1; col < 40; col += 3) { int d = CDVDTeletextTools::deh24(p); if (d < 0) { memcpy(p, "???", 3); p += 3; } else { *p++ = number2char((d >> 6) & 0x1f); /* mode */ *p++ = number2char(d & 0x3f); /* address */ *p++ = number2char((d >> 11) & 0x7f); /* data */ } } } return PageInfo; /* don't interpret irregular pages */ } else if (PageInfo->function == FUNC_GDRCS || PageInfo->function == FUNC_DRCS) /* character definitions */ { return PageInfo; /* don't interpret irregular pages */ } else { int h, parityerror = 0; for (int i = 0; i < 8; i++) PageAtrb[i] = atr; /* decode parity/hamming */ for (unsigned int i = 40; i < TELETEXT_PAGE_SIZE; i++) { PageAtrb[i] = atr; p = PageChar + i; h = dehamming[*p]; if (parityerror && h != 0xFF) /* if no regular page (after any parity error) */ CDVDTeletextTools::Hex2Str((char*)p, h); /* first try dehamming */ else { if (*p == ' ' || deparity[*p] != ' ') /* correct parity */ *p &= 127; else { parityerror = 1; if (h != 0xFF) /* first parity error: try dehamming */ CDVDTeletextTools::Hex2Str((char*)p, h); else *p = ' '; } } } if (parityerror) { return PageInfo; /* don't interpret irregular pages */ } } } int mosaic_pending,esc_pending; /* decode */ for (int row = 0; row < ((showflof && PageInfo->p24) ? 25 : 24); row++) { /* start-of-row default conditions */ foreground = TXT_ColorWhite; background = TXT_ColorBlack; doubleheight = 0; doublewidth = 0; charset = previous_charset = C_G0P; // remember charset for switching back after mosaic charset was used mosaictype = 0; concealed = 0; flashmode = 0; hold = 0; boxwin = 0; held_mosaic = ' '; dhset = 0; IgnoreAtBlackBgSubst = 0; mosaic_pending = esc_pending = 0; // we need to render at least one mosaic char if 'esc' is received immediately after mosaic charset switch on if (boxed && memchr(&PageChar[row*40], start_box, 40) == 0) { foreground = TXT_ColorTransp; background = TXT_ColorTransp; } for (int col = 0; col < 40; col++) { int index = row*40 + col; PageAtrb[index].fg = foreground; PageAtrb[index].bg = background; PageAtrb[index].charset = charset; PageAtrb[index].doubleh = doubleheight; PageAtrb[index].doublew = (col < 39 ? doublewidth : 0); PageAtrb[index].IgnoreAtBlackBgSubst = IgnoreAtBlackBgSubst; PageAtrb[index].concealed = concealed; PageAtrb[index].flashing = flashmode; PageAtrb[index].boxwin = boxwin; PageAtrb[index].inverted = 0; // only relevant for Level 2.5 PageAtrb[index].underline = 0; // only relevant for Level 2.5 PageAtrb[index].diacrit = 0; // only relevant for Level 2.5 PageAtrb[index].setX26 = 0; // only relevant for Level 2.5 PageAtrb[index].setG0G2 = 0x3f; // only relevant for Level 2.5 if (PageChar[index] < ' ') { if (esc_pending) { // mosaic char has been rendered and we can switch charsets charset = previous_charset; if (charset == C_G0P) charset = previous_charset = C_G0S; else if (charset == C_G0S) charset = previous_charset = C_G0P; esc_pending = 0; } switch (PageChar[index]) { case alpha_black: case alpha_red: case alpha_green: case alpha_yellow: case alpha_blue: case alpha_magenta: case alpha_cyan: case alpha_white: concealed = 0; foreground = PageChar[index] - alpha_black + TXT_ColorBlack; if (col == 0 && PageChar[index] == alpha_white) PageAtrb[index].fg = TXT_ColorBlack; // indicate level 1 color change on column 0; (hack) if ((charset!=C_G0P) && (charset!=C_G0S)) // we need to change charset to state it was before mosaic charset = previous_charset; break; case flash: flashmode = 1; break; case steady: flashmode = 0; PageAtrb[index].flashing = 0; break; case end_box: boxwin = 0; IgnoreAtBlackBgSubst = 0; break; case start_box: if (!boxwin) boxwin = 1; break; case normal_size: doubleheight = 0; doublewidth = 0; PageAtrb[index].doubleh = doubleheight; PageAtrb[index].doublew = doublewidth; break; case double_height: if (row < 23) { doubleheight = 1; dhset = 1; } doublewidth = 0; break; case double_width: if (col < 39) doublewidth = 1; doubleheight = 0; break; case double_size: if (row < 23) { doubleheight = 1; dhset = 1; } if (col < 39) doublewidth = 1; break; case mosaic_black: case mosaic_red: case mosaic_green: case mosaic_yellow: case mosaic_blue: case mosaic_magenta: case mosaic_cyan: case mosaic_white: concealed = 0; foreground = PageChar[index] - mosaic_black + TXT_ColorBlack; if ((charset==C_G0P) || (charset==C_G0S)) previous_charset=charset; charset = mosaictype ? C_G1S : C_G1C; mosaic_pending = 1; break; case conceal: PageAtrb[index].concealed = 1; concealed = 1; if (!HintMode) { foreground = background; PageAtrb[index].fg = foreground; } break; case contiguous_mosaic: mosaictype = 0; if (charset == C_G1S) { charset = C_G1C; PageAtrb[index].charset = charset; } break; case separated_mosaic: mosaictype = 1; if (charset == C_G1C) { charset = C_G1S; PageAtrb[index].charset = charset; } break; case esc: if (!mosaic_pending) { // if mosaic is pending we need to wait before mosaic arrives if ((charset != C_G0P) && (charset != C_G0S)) // we need to switch to charset which was active before mosaic charset = previous_charset; if (charset == C_G0P) charset = previous_charset = C_G0S; else if (charset == C_G0S) charset = previous_charset = C_G0P; } else esc_pending = 1; break; case black_background: background = TXT_ColorBlack; IgnoreAtBlackBgSubst = 0; PageAtrb[index].bg = background; PageAtrb[index].IgnoreAtBlackBgSubst = IgnoreAtBlackBgSubst; break; case new_background: background = foreground; if (background == TXT_ColorBlack) IgnoreAtBlackBgSubst = 1; else IgnoreAtBlackBgSubst = 0; PageAtrb[index].bg = background; PageAtrb[index].IgnoreAtBlackBgSubst = IgnoreAtBlackBgSubst; break; case hold_mosaic: hold = 1; break; case release_mosaic: hold = 2; break; } /* handle spacing attributes */ if (hold && (PageAtrb[index].charset == C_G1C || PageAtrb[index].charset == C_G1S)) PageChar[index] = held_mosaic; else PageChar[index] = ' '; if (hold == 2) hold = 0; } else /* char >= ' ' */ { mosaic_pending = 0; // charset will be switched next if esc_pending /* set new held-mosaic char */ if ((charset == C_G1C || charset == C_G1S) && ((PageChar[index]&0xA0) == 0x20)) held_mosaic = PageChar[index]; if (PageAtrb[index].doubleh) PageChar[index + 40] = 0xFF; } if (!(charset == C_G1C || charset == C_G1S)) held_mosaic = ' '; /* forget if outside mosaic */ } /* for col */ /* skip row if doubleheight */ if (row < 23 && dhset) { for (int col = 0; col < 40; col++) { int index = row*40 + col; PageAtrb[index+40].bg = PageAtrb[index].bg; PageAtrb[index+40].fg = TXT_ColorWhite; if (!PageAtrb[index].doubleh) PageChar[index+40] = ' '; PageAtrb[index+40].flashing = 0; PageAtrb[index+40].charset = C_G0P; PageAtrb[index+40].doubleh = 0; PageAtrb[index+40].doublew = 0; PageAtrb[index+40].IgnoreAtBlackBgSubst = 0; PageAtrb[index+40].concealed = 0; PageAtrb[index+40].flashing = 0; PageAtrb[index+40].boxwin = PageAtrb[index].boxwin; } row++; } } /* for row */ m_txtCache->FullScrColor = TXT_ColorBlack; if (showl25) Eval_l25(PageChar, PageAtrb, HintMode); /* handle Black Background Color Substitution and transparency (CLUT1#0) */ { int o = 0; char bitmask ; for (unsigned char row : m_txtCache->FullRowColor) { for (int c = 0; c < 40; c++) { bitmask = (PageAtrb[o].bg == 0x08 ? 0x08 : 0x00) | (row == 0x08 ? 0x04 : 0x00) | (PageAtrb[o].boxwin <<1) | (int)boxed; switch (bitmask) { case 0x08: case 0x0b: if (row == 0x08) PageAtrb[o].bg = m_txtCache->FullScrColor; else PageAtrb[o].bg = row; break; case 0x01: case 0x05: case 0x09: case 0x0a: case 0x0c: case 0x0d: case 0x0e: case 0x0f: PageAtrb[o].bg = TXT_ColorTransp; break; } bitmask = (PageAtrb[o].fg == 0x08 ? 0x08 : 0x00) | (row == 0x08 ? 0x04 : 0x00) | (PageAtrb[o].boxwin <<1) | (int)boxed; switch (bitmask) { case 0x08: case 0x0b: if (row == 0x08) PageAtrb[o].fg = m_txtCache->FullScrColor; else PageAtrb[o].fg = row; break; case 0x01: case 0x05: case 0x09: case 0x0a: case 0x0c: case 0x0d: case 0x0e: case 0x0f: PageAtrb[o].fg = TXT_ColorTransp; break; } o++; } } } return PageInfo; } void CTeletextDecoder::Eval_l25(unsigned char* PageChar, TextPageAttr_t *PageAtrb, bool HintMode) { std::unique_lock lock(m_txtCache->m_critSection); memset(m_txtCache->FullRowColor, 0, sizeof(m_txtCache->FullRowColor)); m_txtCache->FullScrColor = TXT_ColorBlack; m_txtCache->ColorTable = NULL; if (!m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage]) return; /* normal page */ if (IsDec(m_txtCache->Page)) { unsigned char APx0, APy0, APx, APy; TextPageinfo_t *pi = &(m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage]->pageinfo); TextCachedPage_t *pmot = m_txtCache->astCachetable[(m_txtCache->Page & 0xf00) | 0xfe][0]; int p26Received = 0; int BlackBgSubst = 0; int ColorTableRemapping = 0; m_txtCache->pop = m_txtCache->gpop = m_txtCache->drcs = m_txtCache->gdrcs = 0; if (pi->ext) { TextExtData_t *e = pi->ext; if (e->p26[0]) p26Received = 1; if (e->p27) { Textp27_t *p27 = e->p27; if (p27[0].l25) m_txtCache->gpop = p27[0].page; if (p27[1].l25) m_txtCache->pop = p27[1].page; if (p27[2].l25) m_txtCache->gdrcs = p27[2].page; if (p27[3].l25) m_txtCache->drcs = p27[3].page; } if (e->p28Received) { m_txtCache->ColorTable = e->bgr; BlackBgSubst = e->BlackBgSubst; ColorTableRemapping = e->ColorTableRemapping; memset(m_txtCache->FullRowColor, e->DefRowColor, sizeof(m_txtCache->FullRowColor)); m_txtCache->FullScrColor = e->DefScreenColor; m_txtCache->NationalSubset = SetNational(e->DefaultCharset); m_txtCache->NationalSubsetSecondary = SetNational(e->SecondCharset); } /* e->p28Received */ } if (!m_txtCache->ColorTable && m_txtCache->astP29[m_txtCache->Page >> 8]) { TextExtData_t *e = m_txtCache->astP29[m_txtCache->Page >> 8]; m_txtCache->ColorTable = e->bgr; BlackBgSubst = e->BlackBgSubst; ColorTableRemapping = e->ColorTableRemapping; memset(m_txtCache->FullRowColor, e->DefRowColor, sizeof(m_txtCache->FullRowColor)); m_txtCache->FullScrColor = e->DefScreenColor; m_txtCache->NationalSubset = SetNational(e->DefaultCharset); m_txtCache->NationalSubsetSecondary = SetNational(e->SecondCharset); } if (ColorTableRemapping) { for (int i = 0; i < 25*40; i++) { PageAtrb[i].fg += MapTblFG[ColorTableRemapping - 1]; if (!BlackBgSubst || PageAtrb[i].bg != TXT_ColorBlack || PageAtrb[i].IgnoreAtBlackBgSubst) PageAtrb[i].bg += MapTblBG[ColorTableRemapping - 1]; } } /* determine ?pop/?drcs from MOT */ if (pmot) { unsigned char pmot_data[23*40]; auto& components = CServiceBroker::GetAppComponents(); const auto appPlayer = components.GetComponent(); appPlayer->LoadPage((m_txtCache->Page & 0xf00) | 0xfe, 0, pmot_data); unsigned char *p = pmot_data; /* start of link data */ int o = 2 * (((m_txtCache->Page & 0xf0) >> 4) * 10 + (m_txtCache->Page & 0x0f)); /* offset of links for current page */ int opop = p[o] & 0x07; /* index of POP link */ int odrcs = p[o+1] & 0x07; /* index of DRCS link */ unsigned char obj[3*4*4]; /* types* objects * (triplet,packet,subp,high) */ unsigned char type,ct, tstart = 4*4; memset(obj,0,sizeof(obj)); if (p[o] & 0x08) /* GPOP data used */ { if (!m_txtCache->gpop || !(p[18*40] & 0x08)) /* no p27 data or higher prio of MOT link */ { m_txtCache->gpop = ((p[18*40] << 8) | (p[18*40+1] << 4) | p[18*40+2]) & 0x7ff; if ((m_txtCache->gpop & 0xff) == 0xff) m_txtCache->gpop = 0; else { if (m_txtCache->gpop < 0x100) m_txtCache->gpop += 0x800; if (!p26Received) { ct = 2; while (ct) { ct--; type = (p[18*40+5] >> 2*ct) & 0x03; if (type == 0) continue; obj[(type-1)*(tstart)+ct*4 ] = 3 * ((p[18*40+7+ct*2] >> 1) & 0x03) + type; //triplet obj[(type-1)*(tstart)+ct*4+1] = ((p[18*40+7+ct*2] & 0x08) >> 3) + 1 ; //packet obj[(type-1)*(tstart)+ct*4+2] = p[18*40+6+ct*2] & 0x0f ; //subp obj[(type-1)*(tstart)+ct*4+3] = p[18*40+7+ct*2] & 0x01 ; //high } } } } } if (opop) /* POP data used */ { opop = 18*40 + 10*opop; /* offset to POP link */ if (!m_txtCache->pop || !(p[opop] & 0x08)) /* no p27 data or higher prio of MOT link */ { m_txtCache->pop = ((p[opop] << 8) | (p[opop+1] << 4) | p[opop+2]) & 0x7ff; if ((m_txtCache->pop & 0xff) == 0xff) m_txtCache->pop = 0; else { if (m_txtCache->pop < 0x100) m_txtCache->pop += 0x800; if (!p26Received) { ct = 2; while (ct) { ct--; type = (p[opop+5] >> 2*ct) & 0x03; if (type == 0) continue; obj[(type-1)*(tstart)+(ct+2)*4 ] = 3 * ((p[opop+7+ct*2] >> 1) & 0x03) + type; //triplet obj[(type-1)*(tstart)+(ct+2)*4+1] = ((p[opop+7+ct*2] & 0x08) >> 3) + 1 ; //packet obj[(type-1)*(tstart)+(ct+2)*4+2] = p[opop+6+ct*2] ; //subp obj[(type-1)*(tstart)+(ct+2)*4+3] = p[opop+7+ct*2] & 0x01 ; //high } } } } } // eval default objects in correct order for (int i = 0; i < 12; i++) { if (obj[i*4] != 0) { APx0 = APy0 = APx = APy = m_txtCache->tAPx = m_txtCache->tAPy = 0; Eval_NumberedObject(i % 4 > 1 ? m_txtCache->pop : m_txtCache->gpop, obj[i*4+2], obj[i*4+1], obj[i*4], obj[i*4+3], &APx, &APy, &APx0, &APy0, PageChar, PageAtrb); } } if (p[o+1] & 0x08) /* GDRCS data used */ { if (!m_txtCache->gdrcs || !(p[20*40] & 0x08)) /* no p27 data or higher prio of MOT link */ { m_txtCache->gdrcs = ((p[20*40] << 8) | (p[20*40+1] << 4) | p[20*40+2]) & 0x7ff; if ((m_txtCache->gdrcs & 0xff) == 0xff) m_txtCache->gdrcs = 0; else if (m_txtCache->gdrcs < 0x100) m_txtCache->gdrcs += 0x800; } } if (odrcs) /* DRCS data used */ { odrcs = 20*40 + 4*odrcs; /* offset to DRCS link */ if (!m_txtCache->drcs || !(p[odrcs] & 0x08)) /* no p27 data or higher prio of MOT link */ { m_txtCache->drcs = ((p[odrcs] << 8) | (p[odrcs+1] << 4) | p[odrcs+2]) & 0x7ff; if ((m_txtCache->drcs & 0xff) == 0xff) m_txtCache->drcs = 0; else if (m_txtCache->drcs < 0x100) m_txtCache->drcs += 0x800; } } if (m_txtCache->astCachetable[m_txtCache->gpop][0]) m_txtCache->astCachetable[m_txtCache->gpop][0]->pageinfo.function = FUNC_GPOP; if (m_txtCache->astCachetable[m_txtCache->pop][0]) m_txtCache->astCachetable[m_txtCache->pop][0]->pageinfo.function = FUNC_POP; if (m_txtCache->astCachetable[m_txtCache->gdrcs][0]) m_txtCache->astCachetable[m_txtCache->gdrcs][0]->pageinfo.function = FUNC_GDRCS; if (m_txtCache->astCachetable[m_txtCache->drcs][0]) m_txtCache->astCachetable[m_txtCache->drcs][0]->pageinfo.function = FUNC_DRCS; } /* if mot */ /* evaluate local extension data from p26 */ if (p26Received) { APx0 = APy0 = APx = APy = m_txtCache->tAPx = m_txtCache->tAPy = 0; Eval_Object(13 * (23-2 + 2), m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage], &APx, &APy, &APx0, &APy0, OBJ_ACTIVE, &PageChar[40], PageChar, PageAtrb); /* 1st triplet p26/0 */ } { int o = 0; for (unsigned char row : m_txtCache->FullRowColor) { for (int c = 0; c < 40; c++) { if (BlackBgSubst && PageAtrb[o].bg == TXT_ColorBlack && !(PageAtrb[o].IgnoreAtBlackBgSubst)) { if (row == 0x08) PageAtrb[o].bg = m_txtCache->FullScrColor; else PageAtrb[o].bg = row; } o++; } } } if (!HintMode) { for (int i = 0; i < 25*40; i++) { if (PageAtrb[i].concealed) PageAtrb[i].fg = PageAtrb[i].bg; } } } /* is_dec(page) */ } /* dump interpreted object data to stdout */ /* in: 18 bit object data */ /* out: termination info, >0 if end of object */ void CTeletextDecoder::Eval_Object(int iONr, TextCachedPage_t *pstCachedPage, unsigned char *pAPx, unsigned char *pAPy, unsigned char *pAPx0, unsigned char *pAPy0, tObjType ObjType, unsigned char* pagedata, unsigned char* PageChar, TextPageAttr_t* PageAtrb) { int iOData; int iONr1 = iONr + 1; /* don't terminate after first triplet */ unsigned char drcssubp=0, gdrcssubp=0; signed char endcol = -1; /* last column to which to extend attribute changes */ TextPageAttr_t attrPassive = { TXT_ColorWhite , TXT_ColorBlack , C_G0P, 0, 0, 1 ,0, 0, 0, 0, 0, 0, 0, 0x3f}; /* current attribute for passive objects */ do { iOData = iTripletNumber2Data(iONr, pstCachedPage, pagedata); /* get triplet data, next triplet */ if (iOData < 0) /* invalid number, not cached, or hamming error: terminate */ break; if (endcol < 0) { if (ObjType == OBJ_ACTIVE) { endcol = 40; } else if (ObjType == OBJ_ADAPTIVE) /* search end of line */ { for (int i = iONr; i <= 506; i++) { int iTempOData = iTripletNumber2Data(i, pstCachedPage, pagedata); /* get triplet data, next triplet */ int iAddress = (iTempOData ) & 0x3f; int iMode = (iTempOData >> 6) & 0x1f; //int iData = (iTempOData >> 11) & 0x7f; if (iTempOData < 0 || /* invalid number, not cached, or hamming error: terminate */ (iAddress >= 40 /* new row: row address and */ && (iMode == 0x01 || /* Full Row Color or */ iMode == 0x04 || /* Set Active Position */ (iMode >= 0x15 && iMode <= 0x17) || /* Object Definition */ iMode == 0x17))) /* Object Termination */ break; if (iAddress < 40 && iMode != 0x06) endcol = iAddress; } } } iONr++; } while (0 == Eval_Triplet(iOData, pstCachedPage, pAPx, pAPy, pAPx0, pAPy0, &drcssubp, &gdrcssubp, &endcol, &attrPassive, pagedata, PageChar, PageAtrb) || iONr1 == iONr); /* repeat until termination reached */ } void CTeletextDecoder::Eval_NumberedObject(int p, int s, int packet, int triplet, int high, unsigned char *pAPx, unsigned char *pAPy, unsigned char *pAPx0, unsigned char *pAPy0, unsigned char* PageChar, TextPageAttr_t* PageAtrb) { std::unique_lock lock(m_txtCache->m_critSection); if (!packet || 0 == m_txtCache->astCachetable[p][s]) return; unsigned char pagedata[23*40]; auto& components = CServiceBroker::GetAppComponents(); const auto appPlayer = components.GetComponent(); appPlayer->LoadPage(p, s, pagedata); int idata = CDVDTeletextTools::deh24(pagedata + 40*(packet-1) + 1 + 3*triplet); int iONr; if (idata < 0) /* hamming error: ignore triplet */ return; if (high) iONr = idata >> 9; /* triplet number of odd object data */ else iONr = idata & 0x1ff; /* triplet number of even object data */ if (iONr <= 506) { Eval_Object(iONr, m_txtCache->astCachetable[p][s], pAPx, pAPy, pAPx0, pAPy0, (tObjType)(triplet % 3),pagedata, PageChar, PageAtrb); } } int CTeletextDecoder::Eval_Triplet(int iOData, TextCachedPage_t *pstCachedPage, unsigned char *pAPx, unsigned char *pAPy, unsigned char *pAPx0, unsigned char *pAPy0, unsigned char *drcssubp, unsigned char *gdrcssubp, signed char *endcol, TextPageAttr_t *attrPassive, unsigned char* pagedata, unsigned char* PageChar, TextPageAttr_t* PageAtrb) { int iAddress = (iOData ) & 0x3f; int iMode = (iOData >> 6) & 0x1f; int iData = (iOData >> 11) & 0x7f; std::unique_lock lock(m_txtCache->m_critSection); if (iAddress < 40) /* column addresses */ { int offset; /* offset to PageChar and PageAtrb */ if (iMode != 0x06) *pAPx = iAddress; /* new Active Column */ offset = (*pAPy0 + *pAPy) * 40 + *pAPx0 + *pAPx; /* offset to PageChar and PageAtrb */ switch (iMode) { case 0x00: if (0 == (iData>>5)) { int newcolor = iData & 0x1f; if (*endcol < 0) /* passive object */ attrPassive->fg = newcolor; else if (*endcol == 40) /* active object */ { TextPageAttr_t *p = &PageAtrb[offset]; int oldcolor = (p)->fg; /* current color (set-after) */ int c = *pAPx0 + *pAPx; /* current column absolute */ do { p->fg = newcolor; p++; c++; } while (c < 40 && p->fg == oldcolor); /* stop at change by level 1 page */ } else /* adaptive object */ { TextPageAttr_t *p = &PageAtrb[offset]; int c = *pAPx; /* current column relative to object origin */ do { p->fg = newcolor; p++; c++; } while (c <= *endcol); } } break; case 0x01: if (iData >= 0x20) { PageChar[offset] = iData; if (*endcol < 0) /* passive object */ { attrPassive->charset = C_G1C; /* FIXME: separated? */ PageAtrb[offset] = *attrPassive; } else if (PageAtrb[offset].charset != C_G1S) PageAtrb[offset].charset = C_G1C; /* FIXME: separated? */ } break; case 0x02: case 0x0b: PageChar[offset] = iData; if (*endcol < 0) /* passive object */ { attrPassive->charset = C_G3; PageAtrb[offset] = *attrPassive; } else PageAtrb[offset].charset = C_G3; break; case 0x03: if (0 == (iData>>5)) { int newcolor = iData & 0x1f; if (*endcol < 0) /* passive object */ attrPassive->bg = newcolor; else if (*endcol == 40) /* active object */ { TextPageAttr_t *p = &PageAtrb[offset]; int oldcolor = (p)->bg; /* current color (set-after) */ int c = *pAPx0 + *pAPx; /* current column absolute */ do { p->bg = newcolor; if (newcolor == TXT_ColorBlack) p->IgnoreAtBlackBgSubst = 1; p++; c++; } while (c < 40 && p->bg == oldcolor); /* stop at change by level 1 page */ } else /* adaptive object */ { TextPageAttr_t *p = &PageAtrb[offset]; int c = *pAPx; /* current column relative to object origin */ do { p->bg = newcolor; if (newcolor == TXT_ColorBlack) p->IgnoreAtBlackBgSubst = 1; p++; c++; } while (c <= *endcol); } } break; case 0x06: /* ignore */ break; case 0x07: if ((iData & 0x60) != 0) break; // reserved data field if (*endcol < 0) /* passive object */ { attrPassive->flashing=iData & 0x1f; PageAtrb[offset] = *attrPassive; } else PageAtrb[offset].flashing=iData & 0x1f; break; case 0x08: if (*endcol < 0) /* passive object */ { attrPassive->setG0G2=iData & 0x3f; PageAtrb[offset] = *attrPassive; } else PageAtrb[offset].setG0G2=iData & 0x3f; break; case 0x09: PageChar[offset] = iData; if (*endcol < 0) /* passive object */ { attrPassive->charset = C_G0P; /* FIXME: secondary? */ attrPassive->setX26 = 1; PageAtrb[offset] = *attrPassive; } else { PageAtrb[offset].charset = C_G0P; /* FIXME: secondary? */ PageAtrb[offset].setX26 = 1; } break; // case 0x0b: (see 0x02) case 0x0c: { int conc = (iData & 0x04); int inv = (iData & 0x10); int dw = (iData & 0x40 ?1:0); int dh = (iData & 0x01 ?1:0); int sep = (iData & 0x20); int bw = (iData & 0x02 ?1:0); if (*endcol < 0) /* passive object */ { if (conc) { attrPassive->concealed = 1; attrPassive->fg = attrPassive->bg; } attrPassive->inverted = (inv ? 1- attrPassive->inverted : 0); attrPassive->doubleh = dh; attrPassive->doublew = dw; attrPassive->boxwin = bw; if (bw) attrPassive->IgnoreAtBlackBgSubst = 0; if (sep) { if (attrPassive->charset == C_G1C) attrPassive->charset = C_G1S; else attrPassive->underline = 1; } else { if (attrPassive->charset == C_G1S) attrPassive->charset = C_G1C; else attrPassive->underline = 0; } } else { int c = *pAPx0 + (*endcol == 40 ? *pAPx : 0); /* current column */ int c1 = offset; TextPageAttr_t *p = &PageAtrb[offset]; do { p->inverted = (inv ? 1- p->inverted : 0); if (conc) { p->concealed = 1; p->fg = p->bg; } if (sep) { if (p->charset == C_G1C) p->charset = C_G1S; else p->underline = 1; } else { if (p->charset == C_G1S) p->charset = C_G1C; else p->underline = 0; } p->doublew = dw; p->doubleh = dh; p->boxwin = bw; if (bw) p->IgnoreAtBlackBgSubst = 0; p++; c++; c1++; } while (c < *endcol); } break; } case 0x0d: PageChar[offset] = iData & 0x3f; if (*endcol < 0) /* passive object */ { attrPassive->charset = C_OFFSET_DRCS + ((iData & 0x40) ? (0x10 + *drcssubp) : *gdrcssubp); PageAtrb[offset] = *attrPassive; } else PageAtrb[offset].charset = C_OFFSET_DRCS + ((iData & 0x40) ? (0x10 + *drcssubp) : *gdrcssubp); break; case 0x0f: PageChar[offset] = iData; if (*endcol < 0) /* passive object */ { attrPassive->charset = C_G2; PageAtrb[offset] = *attrPassive; } else PageAtrb[offset].charset = C_G2; break; default: if (iMode == 0x10 && iData == 0x2a) iData = '@'; if (iMode >= 0x10) { PageChar[offset] = iData; if (*endcol < 0) /* passive object */ { attrPassive->charset = C_G0P; attrPassive->diacrit = iMode & 0x0f; attrPassive->setX26 = 1; PageAtrb[offset] = *attrPassive; } else { PageAtrb[offset].charset = C_G0P; PageAtrb[offset].diacrit = iMode & 0x0f; PageAtrb[offset].setX26 = 1; } } break; /* unsupported or not yet implemented mode: ignore */ } /* switch (iMode) */ } else /* ================= (iAddress >= 40): row addresses ====================== */ { switch (iMode) { case 0x00: if (0 == (iData>>5)) { m_txtCache->FullScrColor = iData & 0x1f; } break; case 0x01: if (*endcol == 40) /* active object */ { *pAPy = RowAddress2Row(iAddress); /* new Active Row */ int color = iData & 0x1f; int row = *pAPy0 + *pAPy; int maxrow; if (row <= 24 && 0 == (iData>>5)) maxrow = row; else if (3 == (iData>>5)) maxrow = 24; else maxrow = -1; for (; row <= maxrow; row++) m_txtCache->FullRowColor[row] = color; *endcol = -1; } break; case 0x04: *pAPy = RowAddress2Row(iAddress); /* new Active Row */ if (iData < 40) *pAPx = iData; /* new Active Column */ *endcol = -1; /* FIXME: check if row changed? */ break; case 0x07: if (iAddress == 0x3f) { *pAPx = *pAPy = 0; /* new Active Position 0,0 */ if (*endcol == 40) /* active object */ { int color = iData & 0x1f; int row = *pAPy0; // + *pAPy; int maxrow; if (row <= 24 && 0 == (iData>>5)) maxrow = row; else if (3 == (iData>>5)) maxrow = 24; else maxrow = -1; for (; row <= maxrow; row++) m_txtCache->FullRowColor[row] = color; } *endcol = -1; } break; case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: /* ignore */ break; case 0x10: m_txtCache->tAPy = iAddress - 40; m_txtCache->tAPx = iData; break; case 0x11: case 0x12: case 0x13: if (iAddress & 0x10) /* POP or GPOP */ { unsigned char APx = 0, APy = 0; unsigned char APx0 = *pAPx0 + *pAPx + m_txtCache->tAPx, APy0 = *pAPy0 + *pAPy + m_txtCache->tAPy; int triplet = 3 * ((iData >> 5) & 0x03) + (iMode & 0x03); int packet = (iAddress & 0x03) + 1; int subp = iData & 0x0f; int high = (iData >> 4) & 0x01; if (APx0 < 40) /* not in side panel */ { Eval_NumberedObject((iAddress & 0x08) ? m_txtCache->gpop : m_txtCache->pop, subp, packet, triplet, high, &APx, &APy, &APx0, &APy0, PageChar,PageAtrb); } } else if (iAddress & 0x08) /* local: eval invoked object */ { unsigned char APx = 0, APy = 0; unsigned char APx0 = *pAPx0 + *pAPx + m_txtCache->tAPx, APy0 = *pAPy0 + *pAPy + m_txtCache->tAPy; int descode = ((iAddress & 0x01) << 3) | (iData >> 4); int triplet = iData & 0x0f; if (APx0 < 40) /* not in side panel */ { Eval_Object(13 * 23 + 13 * descode + triplet, pstCachedPage, &APx, &APy, &APx0, &APy0, (tObjType)(triplet % 3), pagedata, PageChar, PageAtrb); } } break; case 0x15: case 0x16: case 0x17: if (0 == (iAddress & 0x08)) /* Object Definition illegal or only level 3.5 */ break; /* ignore */ m_txtCache->tAPx = m_txtCache->tAPy = 0; *endcol = -1; return 0xFF; /* termination by object definition */ break; case 0x18: if (0 == (iData & 0x10)) /* DRCS Mode reserved or only level 3.5 */ break; /* ignore */ if (iData & 0x40) *drcssubp = iData & 0x0f; else *gdrcssubp = iData & 0x0f; break; case 0x1f: m_txtCache->tAPx = m_txtCache->tAPy = 0; *endcol = -1; return 0x80 | iData; /* explicit termination */ break; default: break; /* unsupported or not yet implemented mode: ignore */ } /* switch (iMode) */ } /* (iAddress >= 40): row addresses */ if (iAddress < 40 || iMode != 0x10) /* leave temp. AP-Offset unchanged only immediately after definition */ m_txtCache->tAPx = m_txtCache->tAPy = 0; return 0; /* normal exit, no termination */ } /* get object data */ /* in: absolute triplet number (0..506, start at packet 3 byte 1) */ /* in: pointer to cache struct of page data */ /* out: 18 bit triplet data, <0 if invalid number, not cached, or hamming error */ int CTeletextDecoder::iTripletNumber2Data(int iONr, TextCachedPage_t *pstCachedPage, unsigned char* pagedata) { if (iONr > 506 || 0 == pstCachedPage) return -1; unsigned char *p; int packet = (iONr / 13) + 3; int packetoffset = 3 * (iONr % 13); if (packet <= 23) p = pagedata + 40*(packet-1) + packetoffset + 1; else if (packet <= 25) { if (0 == pstCachedPage->pageinfo.p24) return -1; p = pstCachedPage->pageinfo.p24 + 40*(packet-24) + packetoffset + 1; } else { int descode = packet - 26; if (0 == pstCachedPage->pageinfo.ext) return -1; if (0 == pstCachedPage->pageinfo.ext->p26[descode]) return -1; p = pstCachedPage->pageinfo.ext->p26[descode] + packetoffset; /* first byte (=designation code) is not cached */ } return CDVDTeletextTools::deh24(p); } int CTeletextDecoder::SetNational(unsigned char sec) { std::unique_lock lock(m_txtCache->m_critSection); switch (sec) { case 0x08: return NAT_PL; //polish case 0x16: case 0x36: return NAT_TR; //turkish case 0x1d: return NAT_SR; //serbian, croatian, slovenian case 0x20: return NAT_SC; // serbian, croatian case 0x24: return NAT_RB; // russian, bulgarian case 0x25: return NAT_UA; // ukrainian case 0x22: return NAT_ET; // estonian case 0x23: return NAT_LV; // latvian, lithuanian case 0x37: return NAT_GR; // greek case 0x55: return NAT_HB; // hebrew case 0x47: case 0x57: return NAT_AR; // arabic } return CountryConversionTable[sec & 0x07]; } int CTeletextDecoder::NextHex(int i) /* return next existing non-decimal page number */ { int startpage = i; if (startpage < 0x100) startpage = 0x100; do { i++; if (i > 0x8FF) i = 0x100; if (i == startpage) break; } while ((m_txtCache->SubPageTable[i] == 0xFF) || IsDec(i)); return i; } void CTeletextDecoder::SetColors(const unsigned short *pcolormap, int offset, int number) { int j = offset; /* index in global color table */ for (int i = 0; i < number; i++) { int r = ((pcolormap[i] >> 8) & 0xf) << 4; int g = ((pcolormap[i] >> 4) & 0xf) << 4; int b = ((pcolormap[i]) & 0xf) << 4; if (m_RenderInfo.rd0[j] != r) { m_RenderInfo.rd0[j] = r; } if (m_RenderInfo.gn0[j] != g) { m_RenderInfo.gn0[j] = g; } if (m_RenderInfo.bl0[j] != b) { m_RenderInfo.bl0[j] = b; } j++; } } UTILS::COLOR::Color CTeletextDecoder::GetColorRGB(enumTeletextColor ttc) { switch (ttc) { case TXT_ColorBlack: return 0xFF000000; case TXT_ColorRed: return 0xFFFC1414; case TXT_ColorGreen: return 0xFF24FC24; case TXT_ColorYellow: return 0xFFFCC024; case TXT_ColorBlue: return 0xFF0000FC; case TXT_ColorMagenta: return 0xFFB000FC; case TXT_ColorCyan: return 0xFF00FCFC; case TXT_ColorWhite: return 0xFFFCFCFC; case TXT_ColorTransp: return 0x00000000; default: break; } /* Get colors for CLUTs 2+3 */ int index = (int)ttc; UTILS::COLOR::Color color = (m_RenderInfo.tr0[index] << 24) | (m_RenderInfo.bl0[index] << 16) | (m_RenderInfo.gn0[index] << 8) | m_RenderInfo.rd0[index]; return color; }