summaryrefslogtreecommitdiffstats
path: root/xbmc/video/Teletext.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/video/Teletext.cpp')
-rw-r--r--xbmc/video/Teletext.cpp4163
1 files changed, 4163 insertions, 0 deletions
diff --git a/xbmc/video/Teletext.cpp b/xbmc/video/Teletext.cpp
new file mode 100644
index 0000000..3cbff16
--- /dev/null
+++ b/xbmc/video/Teletext.cpp
@@ -0,0 +1,4163 @@
+/*
+ * 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 <harfbuzz/hb-ft.h>
+
+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<CCriticalSection> 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<CApplicationPlayer>();
+ 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, "{}: <FT_Init_FreeType: {:#2X}>", __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, "{}: <FTC_Manager_New: {:#2X}>", __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, "{}: <FTC_SBit_Cache_New: {:#2X}>", __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<char*>(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<char*>(m_teletextFont.c_str());
+ if ((error = FTC_Manager_LookupFace(m_Manager, m_TypeTTF.face_id, &m_Face)))
+ {
+ CLog::Log(LOGERROR, "{}: <FTC_Manager_Lookup_Face failed with Errorcode {:#2X}>",
+ __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<CCriticalSection> lock(m_txtCache->m_critSection);
+ m_txtCache->PageUpdate = true;
+ CLog::Log(LOGDEBUG, "Teletext: Rendering ended");
+ }
+}
+
+void CTeletextDecoder::PageInput(int Number)
+{
+ std::unique_lock<CCriticalSection> 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<CCriticalSection> 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<CCriticalSection> 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<CCriticalSection> 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<CCriticalSection> 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<CCriticalSection> 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<CCriticalSection> 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<CCriticalSection> lock(m_txtCache->m_critSection);
+
+ m_RenderInfo.PageCatching = false;
+ m_txtCache->PageUpdate = true;
+ return;
+ }
+}
+
+void CTeletextDecoder::StopPageCatching()
+{
+ std::unique_lock<CCriticalSection> 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<CCriticalSection> 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<std::chrono::seconds>(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<CCriticalSection> 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<CCriticalSection> 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::milliseconds>(
+ 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<CCriticalSection> 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<CCriticalSection> 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<CApplicationPlayer>();
+
+ 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(&current);
+ }
+ /* 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 <Biterror in btt/plt index {}>", 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<CCriticalSection> lock(m_txtCache->m_critSection);
+
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ 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 <Biterror in ait {:03x} {} {:02x} {:02x} {:02x} "
+ "{:02x} {:02x} {:02x}>",
+ 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<CCriticalSection> lock(m_txtCache->m_critSection);
+
+ do {
+ if (up)
+ CDVDTeletextTools::NextDec(&current);
+ else
+ CDVDTeletextTools::PrevDec(&current);
+
+ 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<CCriticalSection> 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<CCriticalSection> 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<CCriticalSection> 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, "{}: <FT_Get_Char_Index for Char {:x} \"{}\" failed", __FUNCTION__,
+ alphachar, alphachar);
+
+ FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY + yoffset, curfontwidth, factor*m_RenderInfo.FontHeight, bgcolor);
+ m_RenderInfo.PosX += curfontwidth;
+ return;
+ }
+
+ if (FTC_SBitCache_Lookup(m_Cache, &m_TypeTTF, glyph, &m_sBit, &m_anode) != 0)
+ {
+ FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY + yoffset, curfontwidth, m_RenderInfo.FontHeight, bgcolor);
+ m_RenderInfo.PosX += curfontwidth;
+ return;
+ }
+
+ sbitbuffer = m_sBit->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<CCriticalSection> 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; y<FontHeight;y++)
+ {
+ for (f=0; f<factor; f++)
+ {
+ for (x=0; x<curfontwidth*xfactor;x++)
+ {
+ c = (y&4 ? (x/3)&1 :((x+3)/3)&1);
+ *(p+x) = (c ? fgcolor : bgcolor);
+ }
+ p += xres;
+ }
+ }
+ }
+ *pPosX += curfontwidth;
+ return 0;
+ }
+ else
+ {
+ DrawShape(buffer, xres,*pPosX, PosY, Char, curfontwidth, FontHeight, factor*FontHeight, fgcolor, bgcolor, true);
+ *pPosX += curfontwidth;
+ return 0;
+ }
+ }
+ }
+ else if (Attribute->charset >= 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<CApplicationPlayer>();
+ 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<CCriticalSection> 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<CApplicationPlayer>();
+ 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<CCriticalSection> 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<CApplicationPlayer>();
+ 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<CCriticalSection> 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<CApplicationPlayer>();
+ 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<CCriticalSection> 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<CCriticalSection> 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;
+}
+