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