MythTV master
teletextscreen.cpp
Go to the documentation of this file.
1#include <algorithm>
2
3#include <QFontMetrics>
4#include <QPainter>
5
14
17#include "vbilut.h"
18
19#define LOC QString("TeletextScreen: ")
20
21const QColor TeletextScreen::kColorBlack = QColor( 0, 0, 0,255);
22const QColor TeletextScreen::kColorRed = QColor(255, 0, 0,255);
23const QColor TeletextScreen::kColorGreen = QColor( 0,255, 0,255);
24const QColor TeletextScreen::kColorYellow = QColor(255,255, 0,255);
25const QColor TeletextScreen::kColorBlue = QColor( 0, 0,255,255);
26const QColor TeletextScreen::kColorMagenta = QColor(255, 0,255,255);
27const QColor TeletextScreen::kColorCyan = QColor( 0,255,255,255);
28const QColor TeletextScreen::kColorWhite = QColor(255,255,255,255);
31
34
35static QChar cvt_char(char ch, int lang)
36{
37 for (int j = 0; j < 14; j++)
38 {
39 int c = ch & 0x7F;
40 if (c == lang_chars[0][j])
41 ch = lang_chars[lang + 1][j];
42 }
43 return QLatin1Char(ch);
44}
45
46TeletextScreen::TeletextScreen(MythPlayer* Player, MythPainter *Painter, const QString& Name, int FontStretch)
47 : MythScreenType(static_cast<MythScreenType*>(nullptr), Name),
48 m_player(Player),
49 m_fontStretch(FontStretch)
50{
51 m_painter = Painter;
52}
53
55{
57}
58
60{
61 if (m_player)
63 return m_player && m_teletextReader;
64}
65
67{
69 for (const auto & img : std::as_const(m_rowImages))
70 delete img;
71 m_rowImages.clear();
72 SetRedraw();
73}
74
75QImage* TeletextScreen::GetRowImage(int row, QRect &rect)
76{
77 int y = row & ~1;
78 rect.translate(0, -(y * m_rowHeight));
79 if (!m_rowImages.contains(y))
80 {
81 auto* img = new QImage(m_safeArea.width(), m_rowHeight * 2,
82 QImage::Format_ARGB32);
83 if (img)
84 {
85 img->fill(0);
86 m_rowImages.insert(y, img);
87 }
88 else
89 {
90 return nullptr;
91 }
92 }
93 return m_rowImages.value(y);
94}
95
97{
98 QHashIterator<int, QImage*> it(m_rowImages);
99 while (it.hasNext())
100 {
101 it.next();
103 if (!image || !it.value())
104 continue;
105
106 int row = it.key();
107 image->Assign(*(it.value()));
108 auto *uiimage = new MythUIImage(this, QString("ttrow%1").arg(row));
109 if (uiimage)
110 {
111 uiimage->SetImage(image);
112 uiimage->SetArea(MythRect(0, row * m_rowHeight, m_safeArea.width(), m_rowHeight * 2));
113 }
114 image->DecrRef();
115 }
116
117 QRegion visible;
118 QListIterator<MythUIType *> i(m_childrenList);
119 while (i.hasNext())
120 {
121 MythUIType *img = i.next();
122 visible = visible.united(img->GetArea());
123 }
124
125 if (visible.isEmpty())
126 return;
127
128 QRect bounding = visible.boundingRect();
129 bounding = bounding.translated(m_safeArea.topLeft());
130 bounding = m_safeArea.intersected(bounding);
131 int left = m_safeArea.left() - bounding.left();
132 int top = m_safeArea.top() - bounding.top();
133 SetArea(MythRect(bounding));
134
135 i.toFront();;
136 while (i.hasNext())
137 {
138 MythUIType *img = i.next();
139 img->SetArea(MythRect(img->GetArea().translated(left, top)));
140 }
141}
142
144{
145 if (!InitialiseFont() || !m_displaying)
146 return;
147
149 {
150 static const float kTextPadding = 0.96F;
151 QRect oldsafe = m_safeArea;
153 m_colWidth = (int)((float)m_safeArea.width() / (float)kTeletextColumns);
154 m_rowHeight = (int)((float)m_safeArea.height() / (float)kTeletextRows);
155
156 if (oldsafe != m_safeArea)
157 {
159
160 int max_width = (int)((float)m_colWidth * kTextPadding);
161 m_fontHeight = (int)((float)m_rowHeight * kTextPadding);
162 max_width = std::min(max_width, m_colWidth - 2);
163 m_fontHeight = std::min(m_fontHeight, m_rowHeight - 2);
164 gTTFont->GetFace()->setPixelSize(m_fontHeight);
165
166 m_fontStretch = 200;
167 bool ok = false;
168 while (!ok && m_fontStretch > 50)
169 {
170 gTTFont->GetFace()->setStretch(m_fontStretch);
171 QFontMetrics font(*(gTTFont->GetFace()));
172 if (font.averageCharWidth() <= max_width || m_fontStretch < 50)
173 ok = true;
174 else
175 m_fontStretch -= 10;
176 }
177 }
178 }
179 else
180 {
181 return;
182 }
183
185 return;
186
187 ClearScreen();
188
190
191 if (!ttpage)
192 {
193 // no page selected so show the header and a list of available pages
194 DrawHeader({}, 0);
197 return;
198 }
199
201
202 int a = 0;
203 if ((ttpage->subtitle) ||
205 {
206 a = 1; // when showing subtitles we don't want to see the teletext
207 // header line, so we skip that line...
210 }
211 else
212 {
217 }
218
219 for (int y = kTeletextRows - a; y >= 2; y--)
220 DrawLine(ttpage->data[y-1], y, ttpage->lang);
221
224}
225
226bool TeletextScreen::KeyPress(const QString& Key, bool& Exit)
227{
229 return m_teletextReader->KeyPress(Key, Exit);
230 return false;
231}
232
233void TeletextScreen::SetPage(int page, int subpage)
234{
236 m_teletextReader->SetPage(page, subpage);
237}
238
240{
241 m_displaying = display;
242 if (!m_displaying)
243 ClearScreen();
244}
245
247{
250}
251
252void TeletextScreen::DrawHeader(const tt_line_array& page, int lang)
253{
254 if (!m_displaying)
255 return;
256
257 if (!page.empty())
258 DrawLine(page, 1, lang);
259
260 DrawStatus();
261}
262
263static QColor ttcolortoqcolor(int ttcolor)
264{
265 QColor color;
266
267 switch (ttcolor & ~kTTColorTransparent)
268 {
269 case kTTColorBlack: color = TeletextScreen::kColorBlack; break;
270 case kTTColorRed: color = TeletextScreen::kColorRed; break;
271 case kTTColorGreen: color = TeletextScreen::kColorGreen; break;
272 case kTTColorYellow: color = TeletextScreen::kColorYellow; break;
273 case kTTColorBlue: color = TeletextScreen::kColorBlue; break;
275 case kTTColorCyan: color = TeletextScreen::kColorCyan; break;
276 case kTTColorWhite: color = TeletextScreen::kColorWhite; break;
277 }
278
279 return color;
280}
281
282static QString TTColorToString(int ttcolor)
283{
284 switch (ttcolor & ~kTTColorTransparent)
285 {
286 case kTTColorBlack: return "Black";
287 case kTTColorRed: return "Red";
288 case kTTColorGreen: return "Green";
289 case kTTColorYellow: return "Yellow";
290 case kTTColorBlue: return "Blue";
291 case kTTColorMagenta: return "Magenta";
292 case kTTColorCyan: return "Cyan";
293 case kTTColorWhite: return "White";
294 default: return "Unknown";
295 }
296}
297
299{
300 LOG(VB_VBI, LOG_DEBUG, QString("SetForegroundColor(%1)")
301 .arg(TTColorToString(ttcolor)));
302
304}
305
307{
308 LOG(VB_VBI, LOG_DEBUG, QString("SetBackgroundColor(%1)")
309 .arg(TTColorToString(ttcolor)));
310
311 m_bgColor = ttcolortoqcolor(ttcolor);
312 m_bgColor.setAlpha((ttcolor & kTTColorTransparent) ?
313 0x00 : gTTBackgroundAlpha);
314}
315
316void TeletextScreen::DrawLine(const tt_line_array& page, uint row, int lang)
317{
318 unsigned char last_ch = ' ';
319
320 uint fgcolor = kTTColorWhite;
321 uint bgcolor = kTTColorBlack;
322
324 {
325 bgcolor = kTTColorTransparent;
326
327 bool isBlank = true;
328 for (uint i = (row == 1 ? 8 : 0); i < (uint) kTeletextColumns; i++)
329 {
330 unsigned char ch = page[i] & 0x7F;
331 if (ch != ' ')
332 {
333 isBlank = false;
334 break;
335 }
336 }
337
338 if (isBlank)
339 return;
340 }
341
342 SetForegroundColor(fgcolor);
343 SetBackgroundColor(bgcolor);
344
345 bool mosaic = false;
346 [[maybe_unused]] bool seperation = false;
347 bool conceal = false;
348 [[maybe_unused]] bool flash = false;
349 bool doubleheight = false;
350 [[maybe_unused]] bool blink = false;
351 bool hold = false;
352 bool endbox = false;
353 bool startbox = false;
354 bool withinbox = false;
355 uint flof_link_count = 0;
356 uint old_bgcolor = bgcolor;
357
358 if (row == 1)
359 {
360 for (uint x = 0; x < 8; x++)
361 DrawBackground(x, 1);
362 }
363
364 for (uint x = (row == 1 ? 8 : 0); x < (uint)kTeletextColumns; ++x)
365 {
366 if (startbox)
367 {
368 old_bgcolor = bgcolor;
369 if (kTTColorTransparent & bgcolor)
370 bgcolor = bgcolor & ~kTTColorTransparent;
371 startbox = false;
372 withinbox = true;
373 }
374
375 if (endbox)
376 {
377 bgcolor = old_bgcolor;
378 endbox = false;
379 withinbox = false;
380 }
381
382 SetForegroundColor(fgcolor);
383 SetBackgroundColor(bgcolor);
384
385 unsigned char ch = page[x] & 0x7F;
386 bool reserved = false;
387 switch (ch)
388 {
389 case 0x00: case 0x01: case 0x02: case 0x03:
390 case 0x04: case 0x05: case 0x06: case 0x07: // alpha + foreground color
391 fgcolor = ch & 7;
392 mosaic = false;
393 conceal = false;
394 // increment FLOF/FastText count if menu item detected
395 flof_link_count += (row == 25) ? 1 : 0;
396 break;
397 case 0x08: // flash
398 // XXX
399 break;
400 case 0x09: // steady
401 flash = false;
402 break;
403 case 0x0a: // end box
404 endbox = true;
405 break;
406 case 0x0b: // start box
407 if (x < kTeletextColumns - 1 && ((page[x + 1] & 0x7F) == 0x0b))
408 startbox = true;
409 break;
410 case 0x0c: // normal height
411 doubleheight = false;
412 break;
413 case 0x0d: // double height
414 doubleheight = (row < (kTeletextRows-1)) && (x < (kTeletextColumns - 1));
415 break;
416
417 case 0x10: case 0x11: case 0x12: case 0x13:
418 case 0x14: case 0x15: case 0x16: case 0x17: // graphics + foreground color
419 fgcolor = ch & 7;
420 mosaic = true;
421 conceal = false;
422 break;
423 case 0x18: // conceal display
424 conceal = true;
425 break;
426 case 0x19: // contiguous graphics
427 seperation = false;
428 break;
429 case 0x1a: // separate graphics
430 seperation = true;
431 break;
432 case 0x1c: // black background
433 bgcolor = kTTColorBlack;
434 break;
435 case 0x1d: // new background
436 bgcolor = fgcolor;
437 break;
438 case 0x1e: // hold graphics
439 hold = true;
440 break;
441 case 0x1f: // release graphics
442 hold = false;
443 break;
444 case 0x0e: // SO (reserved, double width)
445 case 0x0f: // SI (reserved, double size)
446 case 0x1b: // ESC (reserved)
447 reserved = true;
448 break;
449
450 default:
451 if ((ch >= 0x80) && (ch <=0x9f)) // these aren't used
452 ch = ' '; // BAD_CHAR;
453 else
454 {
455 if (conceal && !m_teletextReader->RevealHidden())
456 ch = ' ';
457 }
458 break;
459 }
460
461 // Extra processing for control characters
462 if (ch < 0x20)
463 ch = (hold && mosaic && !reserved) ? last_ch : ' ';
464
465 // Hide FastText/FLOF menu characters if not available
466 if (flof_link_count && (flof_link_count <= 6))
467 {
469
470 if (ttpage)
471 {
472 bool has_flof = ttpage->floflink[flof_link_count - 1] != 0;
473 ch = (has_flof) ? ch : ' ';
474 }
475 }
476
477 uint newfgcolor = fgcolor;
478 uint newbgcolor = bgcolor;
479
480 SetForegroundColor(newfgcolor);
481 SetBackgroundColor(newbgcolor);
482 if ((row != 0) || (x > 7))
483 {
486
487 if (withinbox || !m_teletextReader->IsSubtitle())
488 {
489 DrawBackground(x, row);
490 if (doubleheight && row < (uint)kTeletextRows)
491 DrawBackground(x, row + 1);
492
493 if ((mosaic) && (ch < 0x40 || ch > 0x5F))
494 {
495 SetBackgroundColor(newfgcolor);
496 DrawMosaic(x, row, ch, doubleheight);
497 }
498 else
499 {
500 QChar c2 = cvt_char(ch, lang);
501 DrawCharacter(x, row, c2, doubleheight);
502 }
503 }
504 }
505 }
506}
507
508void TeletextScreen::DrawCharacter(int x, int y, QChar ch, bool doubleheight)
509{
510 QString line = ch;
511 if (line == " ")
512 return;
513
514 int row = y;
515 x *= m_colWidth;
516 y *= m_rowHeight;
517 int height = m_rowHeight * (doubleheight ? 2 : 1);
518 QRect rect(x, y, m_colWidth, height);
519
520 if (doubleheight)
521 {
522 gTTFont->GetFace()->setPixelSize(m_fontHeight * 2);
523 gTTFont->GetFace()->setStretch(m_fontStretch / 2);
524 }
525
526 QImage* image = GetRowImage(row, rect);
527 if (image)
528 {
529 QPainter painter(image);
530 painter.setFont(gTTFont->face());
531 painter.setPen(gTTFont->color());
532 painter.drawText(rect, Qt::AlignCenter, line);
533 painter.end();
534 }
535
536 if (row & 1)
537 {
538 row++;
539 rect = QRect(x, y + m_rowHeight, m_colWidth, height);
540 rect.translate(0, -m_rowHeight);
541 image = GetRowImage(row, rect);
542 if (image)
543 {
544 QPainter painter(image);
545 painter.setFont(gTTFont->face());
546 painter.setPen(gTTFont->color());
547 painter.drawText(rect, Qt::AlignCenter, line);
548 painter.end();
549 }
550 }
551
552 if (doubleheight)
553 {
554 gTTFont->GetFace()->setPixelSize(m_fontHeight);
555 gTTFont->GetFace()->setStretch(m_fontStretch);
556 }
557}
558
560{
561 int row = y;
562 x *= m_colWidth;
563 y *= m_rowHeight;
564 DrawRect(row, QRect(x, y, m_colWidth, m_rowHeight));
565}
566
567void TeletextScreen::DrawRect(int row, QRect rect)
568{
569 QImage* image = GetRowImage(row, rect);
570 if (!image)
571 return;
572
573 QBrush bgfill = QBrush(m_bgColor, Qt::SolidPattern);
574 QPainter painter(image);
575 painter.setBrush(bgfill);
576 painter.setPen(QPen(Qt::NoPen));
577 painter.drawRect(rect);
578 painter.end();
579}
580
581void TeletextScreen::DrawMosaic(int x, int y, int code, bool doubleheight)
582{
583 int row = y;
584 x *= m_colWidth;
585 y *= m_rowHeight;
586
587 int dx = (int)round((double)m_colWidth / 2) + 1;
588 int dy = (int)round((double)m_rowHeight / 3) + 1;
589 dy = (doubleheight) ? (2 * dy) : dy;
590
591 if (code & 0x10)
592 DrawRect(row, QRect(x, y + (2*dy), dx, dy));
593 if (code & 0x40)
594 DrawRect(row, QRect(x + dx, y + (2*dy), dx, dy));
595 if (code & 0x01)
596 DrawRect(row, QRect(x, y, dx, dy));
597 if (code & 0x02)
598 DrawRect(row, QRect(x + dx, y, dx, dy));
599 if (code & 0x04)
600 DrawRect(row, QRect(x, y + dy, dx, dy));
601 if (code & 0x08)
602 DrawRect(row, QRect(x + dx, y + dy, dx, dy));
603}
604
606{
609
611 for (int i = 0; i < 40; ++i)
612 DrawBackground(i, 0);
613
614 DrawCharacter(1, 0, 'P', false);
615 DrawCharacter(2, 0, QChar(m_teletextReader->GetPageInput(0)), false);
616 DrawCharacter(3, 0, QChar(m_teletextReader->GetPageInput(1)), false);
617 DrawCharacter(4, 0, QChar(m_teletextReader->GetPageInput(2)), false);
618
620
621 if (!ttpage)
622 {
623 QString str = QObject::tr("Page Not Available",
624 "Requested Teletext page not available");
625 for (int i = 0; (i < 30) && i < str.length(); i++)
626 DrawCharacter(i+10, 0, str[i], false);
627
628 return;
629 }
630
631 QString str = m_teletextReader->GetPage();
632 if (str.isEmpty())
633 return;
634
636 for (int x = 0; x < 11; x++)
637 {
640 else
642
643 DrawBackground((x * 3) + 7, 0);
644
645 if (str[x * 3] == '*')
646 {
647 str[x * 3] = ' ';
649 }
650
651 DrawBackground((x * 3) + 8, 0);
652 DrawBackground((x * 3) + 9, 0);
653
654 DrawCharacter((x * 3) + 7, 0, str[x * 3], false);
655 DrawCharacter((x * 3) + 8, 0, str[(x * 3) + 1], false);
656 DrawCharacter((x * 3) + 9, 0, str[(x * 3) + 2], false);
657 }
658}
659
661{
662 static bool s_initialised = false;
663 //QString font = gCoreContext->GetSetting("DefaultSubtitleFont", "FreeMono");
664 if (s_initialised)
665 {
666 return true;
667#if 0
668 if (gTTFont->face().family() == font)
669 return true;
670 delete gTTFont;
671#endif // 0
672 }
673
674 auto *mythfont = new MythFontProperties();
676 if (mythfont)
677 {
678 QFont newfont(font);
679 mythfont->SetFace(newfont);
680 gTTFont = mythfont;
681 }
682 else
683 {
684 return false;
685 }
686
688
689 s_initialised = true;
690 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Loaded main subtitle font '%1'")
691 .arg(font));
692 return true;
693}
QColor color(void) const
QFont face(void) const
void SetColor(const QColor &color)
void Assign(const QImage &img)
Definition: mythimage.cpp:77
int DecrRef(void) override
Decrements reference count and deletes on 0.
Definition: mythimage.cpp:52
MythImage * GetFormatImage()
Returns a blank reference counted image in the format required for the Draw functions for this painte...
MythVideoOutput * GetVideoOutput(void)
Definition: mythplayer.h:164
virtual TeletextReader * GetTeletextReader(uint=0)
Definition: mythplayer.h:196
Wrapper around QRect allowing us to handle percentage and other relative values for areas in mythui.
Definition: mythrect.h:18
Screen in which all other widgets are contained and rendered.
Image widget, displays a single image or multiple images in sequence.
Definition: mythuiimage.h:98
The base class on which all widgets and screens are based.
Definition: mythuitype.h:86
virtual void SetArea(const MythRect &rect)
Definition: mythuitype.cpp:610
void SetRedraw(void)
Definition: mythuitype.cpp:313
virtual MythRect GetArea(void) const
If the object has a minimum area defined, return it, other wise return the default area.
Definition: mythuitype.cpp:885
void DeleteAllChildren(void)
Delete all child widgets.
Definition: mythuitype.cpp:222
MythPainter * m_painter
Definition: mythuitype.h:298
QList< MythUIType * > m_childrenList
Definition: mythuitype.h:255
QRect GetSafeRect()
Returns a QRect describing an area of the screen on which it is 'safe' to render the On Screen Displa...
static int GetTeletextBackgroundAlpha(void)
static QString GetTeletextFontName(void)
void SetShowHeader(bool show)
bool IsSubtitle(void) const
bool RevealHidden(void) const
void SetHeaderChanged(bool changed)
TeletextSubPage * FindSubPage(void)
void SetPage(int page, int subpage)
tt_line_array GetHeader(void)
void SetSubPage(int subpage)
bool PageChanged(void) const
bool KeyPress(const QString &Key, bool &Exit)
int GetPageInput(uint num) const
QString GetPage(void)
void SetIsSubtitle(bool sub)
void SetPageChanged(bool changed)
bool IsTransparent(void) const
MythPlayer * m_player
void DrawLine(const tt_line_array &page, uint row, int lang)
static void SetForegroundColor(int color)
static const QColor kColorRed
static const QColor kColorMagenta
static const QColor kColorGreen
void SetPage(int page, int subpage)
static bool InitialiseFont()
bool KeyPress(const QString &Key, bool &Exit)
static const QColor kColorBlack
static const int kTeletextRows
void DrawHeader(const tt_line_array &page, int lang)
~TeletextScreen() override
TeletextScreen(MythPlayer *Player, MythPainter *Painter, const QString &Name, int FontStretch)
void Pulse() override
Pulse is called 70 times a second to trigger a single frame of an animation.
bool Create() override
void DrawRect(int row, QRect rect)
void OptimiseDisplayedArea()
QHash< int, QImage * > m_rowImages
void DrawMosaic(int x, int y, int code, bool doubleheight)
void SetDisplaying(bool display)
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
void DrawBackground(int x, int y)
void DrawCharacter(int x, int y, QChar ch, bool doubleheight=false)
void SetBackgroundColor(int color)
TeletextReader * m_teletextReader
static const QColor kColorYellow
static const QColor kColorWhite
static const QColor kColorCyan
static const int kTeletextColumns
QImage * GetRowImage(int row, QRect &rect)
static const QColor kColorBlue
int subpagenum
the wanted subpage
int flags
misc flags
int lang
language code
std::array< int, 6 > floflink
FastText links (FLOF)
bool subtitle
page is subtitle page
std::array< tt_line_array, 25 > data
page data
unsigned int uint
Definition: freesurround.h:24
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
bool isBlank(unsigned char median, float stddev, unsigned char maxmedian, float maxstddev)
@ kTTColorRed
@ kTTColorWhite
@ kTTColorGreen
@ kTTColorYellow
@ kTTColorMagenta
@ kTTColorBlue
@ kTTColorTransparent
@ kTTColorBlack
@ kTTColorCyan
static constexpr uint8_t TP_NEWSFLASH
static constexpr uint8_t TP_SUBTITLE
std::array< uint8_t, 40 > tt_line_array
static constexpr uint8_t TP_SUPPRESS_HEADER
#define LOC
static QString TTColorToString(int ttcolor)
static MythFontProperties * gTTFont
static QChar cvt_char(char ch, int lang)
static QColor ttcolortoqcolor(int ttcolor)
static int gTTBackgroundAlpha
const std::array< const std::array< const uint8_t, 16 >, 1+8+8 > lang_chars
Definition: vbilut.cpp:4