MythTV  master
subtitlescreen.cpp
Go to the documentation of this file.
1 #include <algorithm>
2 
3 #include <QFontMetrics>
4 #include <QRegularExpression>
5 
10 #include "libmythui/mythuishape.h"
12 
14 
15 #define LOC QString("Subtitles: ")
16 #define LOC_WARN QString("Subtitles Warning: ")
17 
18 #ifdef DEBUG_SUBTITLES
19 static QString toString(MythVideoFrame const * const frame)
20 {
21  std::chrono::milliseconds time = frame->m_timecode;
22  return QString("%1(%2)")
23  .arg(MythDate::formatTime(time,"HH:mm:ss.zzz"))
24  .arg(time.count());
25 }
26 #endif
27 
28 // Class SubWrapper is used to attach caching structures to MythUIType
29 // objects in the SubtitleScreen's m_ChildrenList. If these objects
30 // inherit from SubWrapper, they can carry relevant information and an
31 // external caching object is not required.
32 //
33 // The following caching information is represented here:
34 // 1. Original m_Area, used for OptimiseDisplayedArea().
35 // 2. Which cc708 window it belongs to, used for Clear708Cache().
36 // 3. Subtitle expire time, primarily for AV subtitles.
38 {
39 protected:
40  SubWrapper(const MythRect &rect,
41  std::chrono::milliseconds expireTime,
42  int whichImageCache = -1) :
43  m_swOrigArea(rect),
44  m_swWhichImageCache(whichImageCache),
45  m_swExpireTime(expireTime)
46  {
47  }
48 public:
49  std::chrono::milliseconds GetExpireTime(void) const { return m_swExpireTime; }
50  MythRect GetOrigArea(void) const { return m_swOrigArea; }
51  int GetWhichImageCache(void) const { return m_swWhichImageCache; }
52 protected:
53  // Returns true if the object was deleted.
55  const int m_swWhichImageCache; // cc708 only; -1 = none
56  const std::chrono::milliseconds m_swExpireTime; // avsubs only; -1 = none
57 };
58 
60 {
61 public:
62  SubSimpleText(const QString &text, const MythFontProperties &font,
63  QRect rect, Qt::Alignment align,
64  MythUIType *parent, const QString &name,
65  int whichImageCache, std::chrono::milliseconds expireTime) :
66  MythUISimpleText(text, font, rect, align, parent, name),
67  SubWrapper(MythRect(rect), expireTime, whichImageCache) {}
68 };
69 
70 class SubShape : public MythUIShape, public SubWrapper
71 {
72 public:
73  SubShape(MythUIType *parent, const QString &name, const MythRect &area,
74  int whichImageCache, std::chrono::milliseconds expireTime) :
75  MythUIShape(parent, name),
76  SubWrapper(area, expireTime, whichImageCache) {}
77 };
78 
79 class SubImage : public MythUIImage, public SubWrapper
80 {
81 public:
82  SubImage(MythUIType *parent, const QString &name, const MythRect &area,
83  std::chrono::milliseconds expireTime) :
84  MythUIImage(parent, name),
85  SubWrapper(area, expireTime) {}
86  MythImage *GetImage(void) { return m_images[0]; }
87 };
88 
90 
91 // Class SubtitleFormat manages fonts and backgrounds for subtitles.
92 //
93 // Formatting is specified by the theme in the new file
94 // osd_subtitle.xml, in the osd_subtitle window. Subtitle types are
95 // text, teletext, 608, 708_0, 708_1, ..., 708_7. Each subtitle type
96 // has a fontdef component for the text and a shape component for the
97 // background. By default, the attributes of a subtitle type come
98 // from either the provider (e.g. font color, italics) or the system
99 // default (e.g. font family). These attributes can be overridden by
100 // the xml file.
101 //
102 // The fontdef name and the background shape name are both simply the
103 // name of the subtitle type. The fontdef and shape should ultimately
104 // inherit from the special value "provider", otherwise all provider
105 // values will be ignored.
106 //
107 // The following example forces .srt subtitles to be rendered in
108 // yellow text, FreeSans font, black shadow, with a translucent black
109 // background. The fontdef and shape names are "text" since the
110 // subtitles come from a .srt file. Note that in this example, color
111 // formatting controls in the .srt file will be ignored due to the
112 // explicit setting of yellow text.
113 //
114 // <?xml version="1.0" encoding="utf-8"?>
115 // <!DOCTYPE mythuitheme SYSTEM "http://www.mythtv.org/schema/mythuitheme.dtd">
116 // <mythuitheme>
117 // <window name="osd_subtitle">
118 // <fontdef name="text" face="FreeSans" from="provider">
119 // <color>#FFFF00</color>
120 // <shadowoffset>2,2</shadowoffset>
121 // <shadowcolor>#000000</shadowcolor>
122 // </fontdef>
123 // <shape name="text" from="provider">
124 // <fill color="#000000" alpha="100" />
125 // </shape>
126 // </window>
127 // </mythuitheme>
128 //
129 // All elements/attributes of fontdef and shape can be used. Note
130 // however that area and position are explicitly ignored, as these are
131 // dictated by the subtitle layout.
132 //
133 // This is implemented with almost no MythUI changes. Two copies of a
134 // "provider" object are created, with default/representative provider
135 // attributes. One copy is then "complemented" to have a different
136 // value for each attribute that a provider might change. The
137 // osd_subtitle.xml file is loaded twice, once with respect to each
138 // provider object, and the desired fontdef or shape is looked up.
139 // The two fontdefs or shapes are compared attribute by attribute, and
140 // each attribute that is different is an attribute that the provider
141 // may modify, whereas each identical attribute represents one that is
142 // fixed by the theme.
143 
145 {
146 public:
147  SubtitleFormat(void) = default;
148  ~SubtitleFormat(void);
149  MythFontProperties *GetFont(const QString &family,
150  const CC708CharacterAttribute &attr,
151  int pixelSize, int zoom, int stretch);
152  SubShape *GetBackground(MythUIType *parent, const QString &name,
153  const QString &family,
154  const CC708CharacterAttribute &attr,
155  const MythRect &area,
156  int whichImageCache,
157  std::chrono::milliseconds start,
158  std::chrono::milliseconds duration);
159  int GetBackgroundAlpha(const QString &family);
160  static QString MakePrefix(const QString &family,
161  const CC708CharacterAttribute &attr);
162 private:
163  void Load(const QString &family,
164  const CC708CharacterAttribute &attr);
165  bool IsUnlocked(const QString &prefix, const QString &property) const
166  {
167  return m_changeMap[prefix].contains(property);
168  }
169  static void CreateProviderDefault(const QString &family,
170  const CC708CharacterAttribute &attr,
171  MythUIType *parent,
172  bool isComplement,
173  MythFontProperties **font,
174  MythUIShape **bg);
175  static void Complement(MythFontProperties *font, MythUIShape *bg);
176  static QSet<QString> Diff(const QString &family,
177  const CC708CharacterAttribute &attr,
178  MythFontProperties *font1,
179  MythFontProperties *font2,
180  MythUIShape *bg1,
181  MythUIShape *bg2);
182 
183  QHash<QString, MythFontProperties *> m_fontMap;
184  QHash<QString, MythUIShape *> m_shapeMap;
185  QHash<QString, QSet<QString> > m_changeMap;
186  // The following m_*Map fields are the original values from the
187  // m_fontMap, which are preserved because the text font zoom
188  // factor affects the actual values stored in the font.
189  QHash<QString, int> m_pixelSizeMap;
190  QHash<QString, int> m_outlineSizeMap;
191  QHash<QString, QPoint> m_shadowOffsetMap;
192  QVector<MythUIType *> m_cleanup;
193 };
194 
195 static const QString kSubProvider("provider");
196 static const QString kSubFileName ("osd_subtitle.xml");
197 static const QString kSubWindowName("osd_subtitle");
198 static const QString kSubFamily608 ("608");
199 static const QString kSubFamily708 ("708");
200 static const QString kSubFamilyText ("text");
201 static const QString kSubFamilyAV ("AV");
202 static const QString kSubFamilyTeletext("teletext");
203 
204 static const QString kSubAttrItalics ("italics");
205 static const QString kSubAttrBold ("bold");
206 static const QString kSubAttrUnderline("underline");
207 static const QString kSubAttrPixelsize("pixelsize");
208 static const QString kSubAttrColor ("color");
209 static const QString kSubAttrBGfill ("bgfill");
210 static const QString kSubAttrShadow ("shadow"); // unused
211 static const QString kSubAttrShadowoffset("shadowoffset");
212 static const QString kSubAttrShadowcolor ("shadowcolor");
213 static const QString kSubAttrShadowalpha ("shadowalpha");
214 static const QString kSubAttrOutline ("outline"); // unused
215 static const QString kSubAttrOutlinecolor("outlinecolor");
216 static const QString kSubAttrOutlinesize ("outlinesize");
217 static const QString kSubAttrOutlinealpha("outlinealpha");
218 
219 static QString srtColorString(const QColor& color)
220 {
221  return QString("#%1%2%3")
222  .arg(color.red(), 2, 16, QLatin1Char('0'))
223  .arg(color.green(), 2, 16, QLatin1Char('0'))
224  .arg(color.blue(), 2, 16, QLatin1Char('0'));
225 }
226 
228 {
229  QString result;
230  result = QString("face=%1 pixelsize=%2 color=%3 "
231  "italics=%4 weight=%5 underline=%6")
232  .arg(f->GetFace()->family())
233  .arg(f->GetFace()->pixelSize())
234  .arg(srtColorString(f->color()))
235  .arg(static_cast<int>(f->GetFace()->italic()))
236  .arg(f->GetFace()->weight())
237  .arg(static_cast<int>(f->GetFace()->underline()));
238  QPoint offset;
239  QColor color;
240  int alpha = 0;
241  int size = 0;
242  f->GetShadow(offset, color, alpha);
243  result += QString(" shadow=%1 shadowoffset=%2 "
244  "shadowcolor=%3 shadowalpha=%4")
245  .arg(QString::number(static_cast<int>(f->hasShadow())),
246  QString("(%1,%2)").arg(offset.x()).arg(offset.y()),
247  srtColorString(color),
248  QString::number(alpha));
249  f->GetOutline(color, size, alpha);
250  result += QString(" outline=%1 outlinecolor=%2 "
251  "outlinesize=%3 outlinealpha=%4")
252  .arg(static_cast<int>(f->hasOutline()))
253  .arg(srtColorString(color))
254  .arg(size)
255  .arg(alpha);
256  return result;
257 }
258 
260 SubtitleFormat::GetFont(const QString &family,
261  const CC708CharacterAttribute &attr,
262  int pixelSize, int zoom, int stretch)
263 {
264  int origPixelSize = pixelSize;
265  float scale = zoom / 100.0;
266  if ((attr.m_penSize & 0x3) == k708AttrSizeSmall)
267  scale = scale * 32 / 42;
268  else if ((attr.m_penSize & 0x3) == k708AttrSizeLarge)
269  scale = scale * 42 / 32;
270 
271  QString prefix = MakePrefix(family, attr);
272  if (!m_fontMap.contains(prefix))
273  Load(family, attr);
275 
276  // Apply the scaling factor to pixelSize even if the theme
277  // explicitly sets pixelSize.
279  pixelSize = m_pixelSizeMap[prefix];
280  pixelSize *= scale;
281  result->GetFace()->setPixelSize(pixelSize);
282 
283  result->GetFace()->setStretch(stretch);
285  result->GetFace()->setItalic(attr.m_italics);
287  result->GetFace()->setUnderline(attr.m_underline);
289  result->GetFace()->setBold(attr.m_boldface);
291  result->SetColor(attr.GetFGColor());
292 
293  MythPoint offset;
294  QColor color;
295  int alpha = 0;
296  bool shadow = result->hasShadow();
297  result->GetShadow(offset, color, alpha);
299  color = attr.GetEdgeColor();
301  alpha = attr.GetFGAlpha();
303  {
304  int off = lroundf(scale * pixelSize / 20);
305  offset = QPoint(off, off);
307  {
308  shadow = true;
309  offset.setX(-off);
310  }
311  else if (attr.m_edgeType == k708AttrEdgeRightDropShadow)
312  {
313  shadow = true;
314  }
315  else
316  {
317  shadow = false;
318  }
319  }
320  else
321  {
322  offset = m_shadowOffsetMap[prefix];
323  offset.NormPoint();
324  offset.setX(lroundf(offset.x() * scale));
325  offset.setY(lroundf(offset.y() * scale));
326  }
327  result->SetShadow(shadow, offset, color, alpha);
328 
329  int off = 0;
330  bool outline = result->hasOutline();
331  result->GetOutline(color, off, alpha);
333  color = attr.GetEdgeColor();
335  alpha = attr.GetFGAlpha();
337  {
338  if (attr.m_edgeType == k708AttrEdgeUniform ||
339  attr.m_edgeType == k708AttrEdgeRaised ||
341  {
342  outline = true;
343  off = lroundf(scale * pixelSize / 20);
344  }
345  else
346  {
347  outline = false;
348  }
349  }
350  else
351  {
352  off = m_outlineSizeMap[prefix];
353  MythPoint point(off, off);
354  point.NormPoint();
355  off = lroundf(point.x() * scale);
356  }
357  result->SetOutline(outline, color, off, alpha);
358 
359  LOG(VB_VBI, LOG_DEBUG,
360  QString("GetFont(family=%1, prefix=%2, orig pixelSize=%3, "
361  "new pixelSize=%4 zoom=%5) = %6")
362  .arg(family, prefix).arg(origPixelSize).arg(pixelSize)
363  .arg(zoom).arg(fontToString(result)));
364  return result;
365 }
366 
368 {
369  // NOLINTNEXTLINE(modernize-loop-convert)
370  for (int i = 0; i < m_cleanup.size(); ++i)
371  {
372  m_cleanup[i]->DeleteAllChildren();
373  m_cleanup[i]->deleteLater();
374  m_cleanup[i] = nullptr; // just to be safe
375  }
376 }
377 
378 QString SubtitleFormat::MakePrefix(const QString &family,
379  const CC708CharacterAttribute &attr)
380 {
381  if (family == kSubFamily708)
382  return family + "_" + QString::number(attr.m_fontTag & 0x7);
383  return family;
384 }
385 
386 void SubtitleFormat::CreateProviderDefault(const QString &family,
387  const CC708CharacterAttribute &attr,
388  MythUIType *parent,
389  bool isComplement,
390  MythFontProperties **returnFont,
391  MythUIShape **returnBg)
392 {
393  auto *font = new MythFontProperties();
394  auto *bg = new MythUIShape(parent, kSubProvider);
395  if (family == kSubFamily608)
396  {
397  font->GetFace()->setFamily("FreeMono");
398  QBrush brush(Qt::black);
399  bg->SetFillBrush(brush);
400  bg->SetLinePen(QPen(brush, 0));
401  }
402  else if (family == kSubFamily708)
403  {
404  static const std::array<const std::string,8> s_cc708Fonts {
405  "FreeMono", // default
406  "FreeMono", // mono serif
407  "Droid Serif", // prop serif
408  "Droid Sans Mono", // mono sans
409  "Droid Sans", // prop sans
410  "Purisa", // casual
411  "TeX Gyre Chorus", // cursive
412  "Droid Serif" // small caps, QFont::SmallCaps will be applied
413  };
414  font->GetFace()->setFamily(QString::fromStdString(s_cc708Fonts[attr.m_fontTag & 0x7]));
415  }
416  else if (family == kSubFamilyText)
417  {
418  font->GetFace()->setFamily("Droid Sans");
419  QBrush brush(Qt::black);
420  bg->SetFillBrush(brush);
421  bg->SetLinePen(QPen(brush, 0));
422  }
423  else if (family == kSubFamilyTeletext)
424  {
425  font->GetFace()->setFamily("FreeMono");
426  // Set up a background with default alpha=100% so that the theme can
427  // override the background alpha.
428  QBrush brush(Qt::black);
429  bg->SetFillBrush(brush);
430  bg->SetLinePen(QPen(brush, 0));
431  }
432  font->GetFace()->setPixelSize(10);
433 
434  if (isComplement)
435  Complement(font, bg);
436  parent->AddFont(kSubProvider, font);
437 
438  *returnFont = font;
439  *returnBg = bg;
440 }
441 
442 static QColor differentColor(const QColor &color)
443 {
444  return color == Qt::white ? Qt::black : Qt::white;
445 }
446 
447 // Change everything (with respect to the == operator) that the
448 // provider might define or override.
450 {
451  QPoint offset;
452  QColor color;
453  int alpha = 0;
454  int size = 0;
455  QFont *face = font->GetFace();
456  face->setItalic(!face->italic());
457  face->setPixelSize(face->pixelSize() + 1);
458  face->setUnderline(!face->underline());
459 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
460  face->setWeight((face->weight() + 1) % 32);
461 #else
462  // qt6: Weight has become an enum. Leave it alone.
463 #endif
464  font->SetColor(differentColor(font->color()));
465 
466  font->GetShadow(offset, color, alpha);
467  offset.setX(offset.x() + 1);
468  font->SetShadow(!font->hasShadow(), offset, differentColor(color),
469  255 - alpha);
470 
471  font->GetOutline(color, size, alpha);
472  font->SetOutline(!font->hasOutline(), differentColor(color),
473  size + 1, 255 - alpha);
474 
475  bg->SetFillBrush(bg->m_fillBrush == Qt::NoBrush ?
476  Qt::SolidPattern : Qt::NoBrush);
477 }
478 
479 void SubtitleFormat::Load(const QString &family,
480  const CC708CharacterAttribute &attr)
481 {
482  // Widgets for the actual values
483  auto *baseParent = new MythUIType(nullptr, "base");
484  m_cleanup += baseParent;
485  MythFontProperties *providerBaseFont = nullptr;
486  MythUIShape *providerBaseShape = nullptr;
487  CreateProviderDefault(family, attr, baseParent, false,
488  &providerBaseFont, &providerBaseShape);
489 
490  // Widgets for the "negative" values
491  auto *negParent = new MythUIType(nullptr, "base");
492  m_cleanup += negParent;
493  MythFontProperties *negFont = nullptr;
494  MythUIShape *negBG = nullptr;
495  CreateProviderDefault(family, attr, negParent, true, &negFont, &negBG);
496 
497  bool posResult =
499  baseParent);
500  bool negResult =
502  negParent);
503  if (!posResult || !negResult)
504  LOG(VB_VBI, LOG_INFO,
505  QString("Couldn't load theme file %1").arg(kSubFileName));
506  QString prefix = MakePrefix(family, attr);
507  MythFontProperties *resultFont = baseParent->GetFont(prefix);
508  if (!resultFont)
509  resultFont = providerBaseFont;
510  auto *resultBG = dynamic_cast<MythUIShape *>(baseParent->GetChild(prefix));
511 
512  // The providerBaseShape object is not leaked here. It is added
513  // to a list of children in its base class constructor.
514  if (!resultBG)
515  resultBG = providerBaseShape;
516  // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
517  MythFontProperties *testFont = negParent->GetFont(prefix);
518  if (!testFont)
519  testFont = negFont;
520  auto *testBG = dynamic_cast<MythUIShape *>(negParent->GetChild(prefix));
521  if (!testBG)
522  testBG = negBG;
523  if (family == kSubFamily708 &&
524  (attr.m_fontTag & 0x7) == k708AttrFontSmallCaps)
525  resultFont->GetFace()->setCapitalization(QFont::SmallCaps);
526  m_fontMap[prefix] = resultFont;
527  m_shapeMap[prefix] = resultBG;
528  LOG(VB_VBI, LOG_DEBUG,
529  QString("providerBaseFont = %1").arg(fontToString(providerBaseFont)));
530  LOG(VB_VBI, LOG_DEBUG,
531  QString("negFont = %1").arg(fontToString(negFont)));
532  LOG(VB_VBI, LOG_DEBUG,
533  QString("resultFont = %1").arg(fontToString(resultFont)));
534  LOG(VB_VBI, LOG_DEBUG,
535  QString("testFont = %1").arg(fontToString(testFont)));
536  m_changeMap[prefix] = Diff(family, attr, resultFont, testFont,
537  resultBG, testBG);
538  QPoint offset;
539  QColor color;
540  int alpha = 0;
541  int size = 0;
542  resultFont->GetShadow(offset, color, alpha);
543  resultFont->GetOutline(color, size, alpha);
544  m_outlineSizeMap[prefix] = size;
545  m_shadowOffsetMap[prefix] = offset;
546  m_pixelSizeMap[prefix] = resultFont->GetFace()->pixelSize();
547 
548  delete negFont;
549 }
550 
551 QSet<QString> SubtitleFormat::Diff(const QString &family,
552  const CC708CharacterAttribute &attr,
553  MythFontProperties *font1,
554  MythFontProperties *font2,
555  MythUIShape *bg1,
556  MythUIShape *bg2)
557 {
558  bool is708 = (family == kSubFamily708);
559  QSet<QString> result;
560  QFont *face1 = font1->GetFace();
561  QFont *face2 = font2->GetFace();
562  if (face1->italic() != face2->italic())
563  result << kSubAttrItalics;
564  if (face1->weight() != face2->weight())
565  result << kSubAttrBold;
566  if (face1->underline() != face2->underline())
567  result << kSubAttrUnderline;
568  if (face1->pixelSize() != face2->pixelSize())
569  result << kSubAttrPixelsize;
570  if (font1->color() != font2->color())
571  result << kSubAttrColor;
572  if (is708 && font1->hasShadow() != font2->hasShadow())
573  {
574  result << kSubAttrShadow;
575  QPoint offset1;
576  QPoint offset2;
577  QColor color1;
578  QColor color2;
579  int alpha1 = 0;
580  int alpha2 = 0;
581  font1->GetShadow(offset1, color1, alpha1);
582  font2->GetShadow(offset2, color2, alpha2);
583  if (offset1 != offset2)
584  result << kSubAttrShadowoffset;
585  if (color1 != color2)
586  result << kSubAttrShadowcolor;
587  if (alpha1 != alpha2)
588  result << kSubAttrShadowalpha;
589  }
590  if (is708 && font1->hasOutline() != font2->hasOutline())
591  {
592  result << kSubAttrOutline;
593  QColor color1;
594  QColor color2;
595  int size1 = 0;
596  int size2 = 0;
597  int alpha1 = 0;
598  int alpha2 = 0;
599  font1->GetOutline(color1, size1, alpha1);
600  font2->GetOutline(color2, size2, alpha2);
601  if (color1 != color2)
602  result << kSubAttrOutlinecolor;
603  if (size1 != size2)
604  result << kSubAttrOutlinesize;
605  if (alpha1 != alpha2)
606  result << kSubAttrOutlinealpha;
607  }
608  if (bg1->m_fillBrush != bg2->m_fillBrush)
609  result << kSubAttrBGfill;
610 
611  QString values = "";
612  for (auto i = result.constBegin(); i != result.constEnd(); ++i)
613  values += " " + (*i);
614  LOG(VB_VBI, LOG_INFO,
615  QString("Subtitle family %1 allows provider to change:%2")
616  .arg(MakePrefix(family, attr), values));
617 
618  return result;
619 }
620 
621 SubShape *
622 SubtitleFormat::GetBackground(MythUIType *parent, const QString &name,
623  const QString &family,
624  const CC708CharacterAttribute &attr,
625  const MythRect &area,
626  int whichImageCache,
627  std::chrono::milliseconds start,
628  std::chrono::milliseconds duration)
629 {
630  QString prefix = MakePrefix(family, attr);
631  if (!m_shapeMap.contains(prefix))
632  Load(family, attr);
633  if (m_shapeMap[prefix]->GetAlpha() == 0)
634  return nullptr;
635  auto *result = new SubShape(parent, name, area, whichImageCache,
636  start + duration);
637  result->CopyFrom(m_shapeMap[prefix]);
638  if (family == kSubFamily708)
639  {
641  {
642  QBrush brush(attr.GetBGColor());
643  result->SetFillBrush(brush);
644  result->SetLinePen(QPen(brush, 0));
645  }
646  }
647  else if (family == kSubFamilyTeletext)
648  {
649  // add code here when teletextscreen.cpp is refactored
650  }
651  LOG(VB_VBI, LOG_DEBUG,
652  QString("GetBackground(prefix=%1) = "
653  "{type=%2 alpha=%3 brushstyle=%4 brushcolor=%5}")
654  .arg(prefix, result->m_type, QString::number(result->GetAlpha()),
655  QString::number(result->m_fillBrush.style()),
656  srtColorString(result->m_fillBrush.color())));
657  return result;
658 }
659 
660 int SubtitleFormat::GetBackgroundAlpha(const QString &family)
661 {
662  // This is a "temporary" hack for allowing teletextscreen.cpp to get the
663  // background alpha value from osd_subtitle.xml.
664  CC708CharacterAttribute attr(false, false, false, Qt::white);
665  SubShape *bgShape = GetBackground(nullptr, "dummyName", family, attr,
666  MythRect(), -1, 0ms, -1ms);
667  return bgShape->m_fillBrush.color().alpha();
668 }
669 
671 
672 QSize FormattedTextChunk::CalcSize(float layoutSpacing) const
673 {
674  return m_parent->CalcTextSize(m_text, m_format, layoutSpacing);
675 }
676 
677 void FormattedTextChunk::CalcPadding(bool isFirst, bool isLast,
678  int &left, int &right) const
679 {
680  m_parent->CalcPadding(m_format, isFirst, isLast, left, right);
681 }
682 
684 {
685  LOG(VB_VBI, LOG_INFO,
686  QString("Attempting to split chunk '%1'").arg(m_text));
687  int lastSpace = m_text.lastIndexOf(' ', -2); // -2 to ignore trailing space
688  if (lastSpace < 0)
689  {
690  LOG(VB_VBI, LOG_INFO,
691  QString("Failed to split chunk '%1'").arg(m_text));
692  return false;
693  }
694  newChunk.m_parent = m_parent;
695  newChunk.m_format = m_format;
696  newChunk.m_text = m_text.mid(lastSpace + 1).trimmed() + ' ';
697  m_text = m_text.left(lastSpace).trimmed();
698  LOG(VB_VBI, LOG_INFO,
699  QString("Split chunk into '%1' + '%2'").arg(m_text, newChunk.m_text));
700  return true;
701 }
702 
704 {
705  QString str("");
706  str += QString("fg=%1.%2 ")
708  .arg(m_format.GetFGAlpha());
709  str += QString("bg=%1.%2 ")
711  .arg(m_format.GetBGAlpha());
712  str += QString("edge=%1.%2 ")
714  .arg(m_format.m_edgeType);
715  str += QString("off=%1 pensize=%2 ")
716  .arg(m_format.m_offset)
717  .arg(m_format.m_penSize);
718  str += QString("it=%1 ul=%2 bf=%3 ")
719  .arg(static_cast<int>(m_format.m_italics))
720  .arg(static_cast<int>(m_format.m_underline))
721  .arg(static_cast<int>(m_format.m_boldface));
722  str += QString("font=%1 ").arg(m_format.m_fontTag);
723  str += QString(" text='%1'").arg(m_text);
724  return str;
725 }
726 
727 bool FormattedTextChunk::PreRender(bool isFirst, bool isLast,
728  int &x, int y, int height)
729 {
731  if (!m_textFont)
732  return false;
733  QFontMetrics font(*(m_textFont->GetFace()));
734  // If the chunk starts with whitespace, the leading whitespace
735  // ultimately gets lost due to the text.trimmed() operation in the
736  // MythUISimpleText constructor. To compensate, we manually
737  // indent the chunk accordingly.
738  int count = 0;
739  while (count < m_text.length() && m_text.at(count) == ' ')
740  {
741  ++count;
742  }
743  int x_adjust = count * font.horizontalAdvance(" ");
744  int leftPadding = 0;
745  int rightPadding = 0;
746  CalcPadding(isFirst, isLast, leftPadding, rightPadding);
747  // Account for extra padding before the first chunk.
748  if (isFirst)
749  x += leftPadding;
750  QSize chunk_sz = CalcSize();
751  QRect bgrect(x - leftPadding, y,
752  chunk_sz.width() + leftPadding + rightPadding,
753  height);
754  // Don't draw a background behind leading spaces.
755  if (isFirst)
756  bgrect.setLeft(bgrect.left() + x_adjust);
757  m_bgShapeName = QString("subbg%1x%2@%3,%4")
758  .arg(chunk_sz.width()).arg(height).arg(x).arg(y);
759  m_bgShapeRect = bgrect;
760  // Shift to the right to account for leading spaces that
761  // are removed by the MythUISimpleText constructor. Also
762  // add in padding at the end to avoid clipping.
763  m_textName = QString("subtxt%1x%2@%3,%4")
764  .arg(chunk_sz.width()).arg(height).arg(x).arg(y);
765  m_textRect = QRect(x + x_adjust, y,
766  chunk_sz.width() - x_adjust + rightPadding, height);
767 
768  x += chunk_sz.width();
769  return true;
770 }
771 
772 QSize FormattedTextLine::CalcSize(float layoutSpacing) const
773 {
774  int height = 0;
775  int width = 0;
776  int leftPadding = 0;
777  int rightPadding = 0;
778  QList<FormattedTextChunk>::const_iterator it;
779  bool isFirst = true;
780  for (it = chunks.constBegin(); it != chunks.constEnd(); ++it)
781  {
782  bool isLast = (it + 1 == chunks.constEnd());
783  QSize tmp = (*it).CalcSize(layoutSpacing);
784  height = std::max(height, tmp.height());
785  width += tmp.width();
786  (*it).CalcPadding(isFirst, isLast, leftPadding, rightPadding);
787  if (it == chunks.constBegin())
788  width += leftPadding;
789  isFirst = false;
790  }
791  return {width + rightPadding, height};
792 }
793 
794 // Normal font height is designed to be 1/20 of safe area height, with
795 // extra blank space between lines to make 17 lines within the safe
796 // area.
797 static const float LINE_SPACING = (20.0 / 17.0);
798 static const float PAD_WIDTH = 0.5;
799 static const float PAD_HEIGHT = 0.04;
800 
801 // Resolves any TBD x_indent and y_indent values in FormattedTextLine
802 // objects. Calculates m_bounds. Prunes most leading and all
803 // trailing whitespace from each line so that displaying with a black
804 // background doesn't look clumsy.
806 {
807  // Calculate dimensions of bounding rectangle
808  int anchor_width = 0;
809  int anchor_height = 0;
810  for (const auto & line : std::as_const(m_lines))
811  {
812  QSize sz = line.CalcSize(LINE_SPACING);
813  anchor_width = std::max(anchor_width, sz.width());
814  anchor_height += sz.height();
815  }
816 
817  // Adjust the anchor point according to actual width and height
818  int anchor_x = m_xAnchor;
819  int anchor_y = m_yAnchor;
820  if (m_xAnchorPoint == 1)
821  anchor_x -= anchor_width / 2;
822  else if (m_xAnchorPoint == 2)
823  anchor_x -= anchor_width;
824  if (m_yAnchorPoint == 1)
825  anchor_y -= anchor_height / 2;
826  else if (m_yAnchorPoint == 2)
827  anchor_y -= anchor_height;
828 
829  // Shift the anchor point back into the safe area if necessary/possible.
830  anchor_y = std::clamp(anchor_y, 0, m_safeArea.height() - anchor_height);
831  anchor_x = std::clamp(anchor_x, 0, m_safeArea.width() - anchor_width);
832 
833  m_bounds = QRect(anchor_x, anchor_y, anchor_width, anchor_height);
834 
835  // Fill in missing coordinates
836  int y = anchor_y;
837  // NOLINTNEXTLINE(modernize-loop-convert)
838  for (int i = 0; i < m_lines.size(); i++)
839  {
840  if (m_lines[i].m_xIndent < 0)
841  m_lines[i].m_xIndent = anchor_x;
842  if (m_lines[i].m_yIndent < 0)
843  m_lines[i].m_yIndent = y;
844  y += m_lines[i].CalcSize(LINE_SPACING).height();
845  // Prune leading all-whitespace chunks.
846  while (!m_lines[i].chunks.isEmpty() &&
847  m_lines[i].chunks.first().m_text.trimmed().isEmpty())
848  {
849  m_lines[i].m_xIndent +=
850  m_lines[i].chunks.first().CalcSize().width();
851  m_lines[i].chunks.removeFirst();
852  }
853  // Prune trailing all-whitespace chunks.
854  while (!m_lines[i].chunks.isEmpty() &&
855  m_lines[i].chunks.last().m_text.trimmed().isEmpty())
856  {
857  m_lines[i].chunks.removeLast();
858  }
859  // Trim trailing whitespace from last chunk. (Trimming
860  // leading whitespace from all chunks is handled in the Draw()
861  // routine.)
862  if (!m_lines[i].chunks.isEmpty())
863  {
864  QString *str = &m_lines[i].chunks.last().m_text;
865  int idx = str->length() - 1;
866  while (idx >= 0 && str->at(idx) == ' ')
867  --idx;
868  str->truncate(idx + 1);
869  }
870  }
871 }
872 
874 {
875  // NOLINTNEXTLINE(modernize-loop-convert)
876  for (int i = 0; i < m_lines.size(); i++)
877  {
878  int x = m_lines[i].m_xIndent;
879  int y = m_lines[i].m_yIndent;
880  int height = m_lines[i].CalcSize().height();
881  QList<FormattedTextChunk>::iterator chunk;
882  bool isFirst = true;
883  for (chunk = m_lines[i].chunks.begin();
884  chunk != m_lines[i].chunks.end();
885  ++chunk)
886  {
887  bool isLast = (chunk + 1 == m_lines[i].chunks.end());
888  (*chunk).PreRender(isFirst, isLast, x, y, height);
889  isFirst = false;
890  }
891  }
892 }
893 
894 // Returns true if anything new was drawn, false if not. The caller
895 // should call SubtitleScreen::OptimiseDisplayedArea() if true is
896 // returned.
898 {
899  QList<MythUIType *> textList;
900  QList<MythUIType *> shapeList;
901  for (auto & line : m_lines)
902  {
903  QList<FormattedTextChunk>::const_iterator chunk;
904  for (chunk = line.chunks.constBegin();
905  chunk != line.chunks.constEnd();
906  ++chunk)
907  {
908  MythFontProperties *mythfont =
909  m_subScreen->GetFont((*chunk).m_format);
910  if (!mythfont)
911  continue;
912  // Note: nullptr is passed as the parent argument to the
913  // MythUI constructors so that we can control the drawing
914  // order of the children. In particular, background
915  // shapes should be added/drawn first, and text drawn on
916  // top.
917  if ((*chunk).m_textRect.width() > 0) {
918  auto *text = new SubSimpleText((*chunk).m_text, *mythfont,
919  (*chunk).m_textRect,
920  Qt::AlignLeft|Qt::AlignTop,
921  /*m_subScreen*/nullptr,
922  (*chunk).m_textName, CacheNum(),
923  m_start + m_duration);
924  textList += text;
925  }
926  if ((*chunk).m_bgShapeRect.width() > 0) {
928  GetBackground(/*m_subScreen*/nullptr,
929  (*chunk).m_bgShapeName,
930  m_base, (*chunk).m_format,
931  MythRect((*chunk).m_bgShapeRect), CacheNum(),
933  if (bgshape)
934  {
935  bgshape->SetArea(MythRect((*chunk).m_bgShapeRect));
936  shapeList += bgshape;
937  }
938  }
939  }
940  }
941  while (!shapeList.isEmpty())
942  m_subScreen->AddChild(shapeList.takeFirst());
943  while (!textList.isEmpty())
944  m_subScreen->AddChild(textList.takeFirst());
945 }
946 
947 QStringList FormattedTextSubtitle::ToSRT(void) const
948 {
949  QStringList result;
950  for (const auto & ftl : std::as_const(m_lines))
951  {
952  QString line;
953  if (ftl.m_origX > 0)
954  line.fill(' ', ftl.m_origX);
955  QList<FormattedTextChunk>::const_iterator chunk;
956  for (chunk = ftl.chunks.constBegin();
957  chunk != ftl.chunks.constEnd();
958  ++chunk)
959  {
960  const QString &text = (*chunk).m_text;
961  const CC708CharacterAttribute &attr = (*chunk).m_format;
962  bool isBlank = !attr.m_underline && text.trimmed().isEmpty();
963  if (!isBlank)
964  {
965  if (attr.m_boldface)
966  line += "<b>";
967  if (attr.m_italics)
968  line += "<i>";
969  if (attr.m_underline)
970  line += "<u>";
971  if (attr.GetFGColor() != Qt::white)
972  line += QString("<font color=\"%1\">")
973  .arg(srtColorString(attr.GetFGColor()));
974  }
975  line += text;
976  if (!isBlank)
977  {
978  if (attr.GetFGColor() != Qt::white)
979  line += QString("</font>");
980  if (attr.m_underline)
981  line += "</u>";
982  if (attr.m_italics)
983  line += "</i>";
984  if (attr.m_boldface)
985  line += "</b>";
986  }
987  }
988  if (!line.trimmed().isEmpty())
989  result += line;
990  }
991  return result;
992 }
993 
994 void FormattedTextSubtitleSRT::Init(const QStringList &subs)
995 {
996  // Does a simplistic parsing of HTML tags from the strings.
997  // Nesting is not implemented. Stray whitespace may cause
998  // legitimate tags to be ignored. All other HTML tags are
999  // stripped and ignored.
1000  //
1001  // <i> - enable italics
1002  // </i> - disable italics
1003  // <b> - enable boldface
1004  // </b> - disable boldface
1005  // <u> - enable underline
1006  // </u> - disable underline
1007  // <font color="#xxyyzz"> - change font color
1008  // </font> - reset font color to white
1009 
1010  int pixelSize = m_safeArea.height() / 20;
1011  if (m_subScreen)
1012  m_subScreen->SetFontSize(pixelSize);
1013  m_xAnchorPoint = 1; // center
1014  m_yAnchorPoint = 2; // bottom
1015  m_xAnchor = m_safeArea.width() / 2;
1016  m_yAnchor = m_safeArea.height();
1017 
1018  bool isItalic = false;
1019  bool isBold = false;
1020  bool isUnderline = false;
1021  QColor color(Qt::white);
1022  static const QRegularExpression htmlTag {
1023  "</?.+>", QRegularExpression::InvertedGreedinessOption };
1024  QString htmlPrefix("<font color=\"");
1025  QString htmlSuffix("\">");
1026  for (const QString& subtitle : std::as_const(subs))
1027  {
1028  FormattedTextLine line;
1029  QString text(subtitle);
1030  while (!text.isEmpty())
1031  {
1032  auto match = htmlTag.match(text);
1033  int pos = match.capturedStart();
1034  if (pos != 0) // don't add a zero-length string
1035  {
1036  CC708CharacterAttribute attr(isItalic, isBold, isUnderline,
1037  color);
1038  FormattedTextChunk chunk(text.left(pos), attr, m_subScreen);
1039  line.chunks += chunk;
1040  text = (pos < 0 ? "" : text.mid(pos));
1041  LOG(VB_VBI, LOG_INFO, QString("Adding SRT chunk: %1")
1042  .arg(chunk.ToLogString()));
1043  }
1044  if (pos >= 0)
1045  {
1046  int htmlLen = match.capturedLength();
1047  QString html = text.left(htmlLen).toLower();
1048  text = text.mid(htmlLen);
1049  if (html == "<i>")
1050  isItalic = true;
1051  else if (html == "</i>")
1052  isItalic = false;
1053  else if (html.startsWith(htmlPrefix) &&
1054  html.endsWith(htmlSuffix))
1055  {
1056  int colorLen = html.length() -
1057  (htmlPrefix.length() + htmlSuffix.length());
1058  QString colorString(
1059  html.mid(htmlPrefix.length(), colorLen));
1060  QColor newColor(colorString);
1061  if (newColor.isValid())
1062  {
1063  color = newColor;
1064  }
1065  else
1066  {
1067  LOG(VB_VBI, LOG_INFO,
1068  QString("Ignoring invalid SRT color specification: "
1069  "'%1'").arg(colorString));
1070  }
1071  }
1072  else if (html == "</font>")
1073  {
1074  color = Qt::white;
1075  }
1076  else if (html == "<b>")
1077  {
1078  isBold = true;
1079  }
1080  else if (html == "</b>")
1081  {
1082  isBold = false;
1083  }
1084  else if (html == "<u>")
1085  {
1086  isUnderline = true;
1087  }
1088  else if (html == "</u>")
1089  {
1090  isUnderline = false;
1091  }
1092  else
1093  {
1094  LOG(VB_VBI, LOG_INFO,
1095  QString("Ignoring unknown SRT formatting: '%1'")
1096  .arg(html));
1097  }
1098 
1099  LOG(VB_VBI, LOG_INFO,
1100  QString("SRT formatting change '%1', "
1101  "new ital=%2 bold=%3 uline=%4 color=%5)")
1102  .arg(html).arg(isItalic).arg(isBold).arg(isUnderline)
1103  .arg(srtColorString(color)));
1104  }
1105  }
1106  m_lines += line;
1107  }
1108 }
1109 
1111 {
1112  int maxWidth = m_safeArea.width();
1113  for (int i = 0; i < m_lines.size(); i++)
1114  {
1115  int width = m_lines[i].CalcSize().width();
1116  // Move entire chunks to the next line as necessary. Leave at
1117  // least one chunk on the current line.
1118  while (width > maxWidth && m_lines[i].chunks.size() > 1)
1119  {
1120  width -= m_lines[i].chunks.back().CalcSize().width();
1121  // Make sure there's a next line to wrap into.
1122  if (m_lines.size() == i + 1)
1123  m_lines += FormattedTextLine(m_lines[i].m_xIndent,
1124  m_lines[i].m_yIndent);
1125  m_lines[i+1].chunks.prepend(m_lines[i].chunks.takeLast());
1126  LOG(VB_VBI, LOG_INFO,
1127  QString("Wrapping chunk to next line: '%1'")
1128  .arg(m_lines[i+1].chunks[0].m_text));
1129  }
1130  // Split the last chunk until width is small enough or until
1131  // no more splits are possible.
1132  bool isSplitPossible = true;
1133  while (width > maxWidth && isSplitPossible)
1134  {
1135  FormattedTextChunk newChunk;
1136  isSplitPossible = m_lines[i].chunks.back().Split(newChunk);
1137  if (isSplitPossible)
1138  {
1139  // Make sure there's a next line to split into.
1140  if (m_lines.size() == i + 1)
1141  m_lines += FormattedTextLine(m_lines[i].m_xIndent,
1142  m_lines[i].m_yIndent);
1143  m_lines[i+1].chunks.prepend(newChunk);
1144  width = m_lines[i].CalcSize().width();
1145  }
1146  }
1147  }
1148 }
1149 
1155 static QString extract_cc608(QString &text, int &color,
1156  bool &isItalic, bool &isUnderline)
1157 {
1158  QString result;
1159 
1160  // Handle an initial control sequence.
1161  if (text.length() >= 1 && text[0] >= QChar(0x7000))
1162  {
1163  int op = text[0].unicode() - 0x7000;
1164  isUnderline = ((op & 0x1) != 0);
1165  switch (op & ~1)
1166  {
1167  case 0x0e:
1168  // color unchanged
1169  isItalic = true;
1170  break;
1171  case 0x1e:
1172  color = op >> 1;
1173  isItalic = true;
1174  break;
1175  case 0x20:
1176  // color unchanged
1177  // italics unchanged
1178  break;
1179  default:
1180  color = (op & 0xf) >> 1;
1181  isItalic = false;
1182  break;
1183  }
1184  text = text.mid(1);
1185  }
1186 
1187  // Copy the string into the result, up to the next control
1188  // character.
1189  static const QRegularExpression kControlCharsRE { "[\\x{7000}-\\x{7fff}]" };
1190  int nextControl = text.indexOf(kControlCharsRE);
1191  if (nextControl < 0)
1192  {
1193  result = text;
1194  text.clear();
1195  }
1196  else
1197  {
1198  result = text.left(nextControl);
1199  text = text.mid(nextControl);
1200  }
1201 
1202  return result;
1203 }
1204 
1205 // Adjusts the Y coordinates to avoid overlap, which could happen as a
1206 // result of a large text zoom factor. Then, if the total height
1207 // exceeds the safe area, compresses each piece of vertical blank
1208 // space proportionally to make it fit.
1210 {
1211  int totalHeight = 0;
1212  int totalSpace = 0;
1213  int firstY = 0;
1214  int prevY = 0;
1215  QVector<int> heights(m_lines.size());
1216  QVector<int> spaceBefore(m_lines.size());
1217  // Calculate totalHeight and totalSpace
1218  for (int i = 0; i < m_lines.size(); i++)
1219  {
1220  m_lines[i].m_yIndent = std::max(m_lines[i].m_yIndent, prevY); // avoid overlap
1221  int y = m_lines[i].m_yIndent;
1222  if (i == 0)
1223  firstY = prevY = y;
1224  int height = m_lines[i].CalcSize().height();
1225  heights[i] = height;
1226  spaceBefore[i] = y - prevY;
1227  totalSpace += (y - prevY);
1228  prevY = y + height;
1229  totalHeight += height;
1230  }
1231  int safeHeight = m_safeArea.height();
1232  int overage = std::min(totalHeight - safeHeight, totalSpace);
1233 
1234  // Recalculate Y coordinates, applying the shrink factor to space
1235  // between each line.
1236  if (overage > 0 && totalSpace > 0)
1237  {
1238  float shrink = (totalSpace - overage) / (float)totalSpace;
1239  prevY = firstY;
1240  for (int i = 0; i < m_lines.size(); i++)
1241  {
1242  m_lines[i].m_yIndent = prevY + spaceBefore[i] * shrink;
1243  prevY = m_lines[i].m_yIndent + heights[i];
1244  }
1245  }
1246 
1247  // Shift Y coordinates back up into the safe area.
1248  int shift = std::min(firstY, std::max(0, prevY - safeHeight));
1249  // NOLINTNEXTLINE(modernize-loop-convert)
1250  for (int i = 0; i < m_lines.size(); i++)
1251  m_lines[i].m_yIndent -= shift;
1252 
1254 }
1255 
1256 void FormattedTextSubtitle608::Init(const std::vector<CC608Text*> &buffers)
1257 {
1258  static const std::array<const QColor,8> kClr
1259  {
1260  Qt::white, Qt::green, Qt::blue, Qt::cyan,
1261  Qt::red, Qt::yellow, Qt::magenta, Qt::white,
1262  };
1263 
1264  if (buffers.empty())
1265  return;
1266  auto i = buffers.cbegin();
1267  int xscale = 36;
1268  int yscale = 17;
1269  int pixelSize = m_safeArea.height() / (yscale * LINE_SPACING);
1270  int fontwidth = 0;
1271  int xmid = 0;
1272  int zoom = 100;
1273  if (m_subScreen)
1274  {
1275  zoom = m_subScreen->GetZoom();
1276  m_subScreen->SetFontSize(pixelSize);
1277  CC708CharacterAttribute def_attr(false, false, false, kClr[0]);
1278  QFont *font = m_subScreen->GetFont(def_attr)->GetFace();
1279  QFontMetrics fm(*font);
1280  fontwidth = fm.averageCharWidth();
1281  xmid = m_safeArea.width() / 2;
1282  // Disable centering for zoom factor >= 100%
1283  if (zoom >= 100)
1284  xscale = m_safeArea.width() / fontwidth;
1285  }
1286 
1287  for (; i != buffers.cend(); ++i)
1288  {
1289  CC608Text *cc = (*i);
1290  int color = 0;
1291  bool isItalic = false;
1292  bool isUnderline = false;
1293  const bool isBold = false;
1294  QString text(cc->m_text);
1295 
1296  int orig_x = cc->m_x;
1297  // position as if we use a fixed size font
1298  // - font size already has zoom factor applied
1299 
1300  int x = 0;
1301  if (xmid)
1302  {
1303  // center horizontally
1304  x = xmid + (orig_x - xscale / 2) * fontwidth;
1305  }
1306  else
1307  {
1308  // fallback
1309  x = (orig_x + 3) * m_safeArea.width() / xscale;
1310  }
1311 
1312  int orig_y = cc->m_y;
1313  int y = 0;
1314  if (orig_y < yscale / 2)
1315  {
1316  // top half -- anchor up
1317  y = (orig_y * m_safeArea.height() * zoom / (yscale * 100));
1318  }
1319  else
1320  {
1321  // bottom half -- anchor down
1322  y = m_safeArea.height() -
1323  ((yscale - orig_y - 0.5) * m_safeArea.height() * zoom /
1324  (yscale * 100));
1325  }
1326 
1327  FormattedTextLine line(x, y, orig_x, orig_y);
1328  while (!text.isNull())
1329  {
1330  QString captionText =
1331  extract_cc608(text, color, isItalic, isUnderline);
1332  CC708CharacterAttribute attr(isItalic, isBold, isUnderline,
1333  kClr[std::clamp(color, 0, 7)]);
1334  FormattedTextChunk chunk(captionText, attr, m_subScreen);
1335  line.chunks += chunk;
1336  LOG(VB_VBI, LOG_INFO,
1337  QString("Adding cc608 chunk (%1,%2): %3")
1338  .arg(cc->m_x).arg(cc->m_y).arg(chunk.ToLogString()));
1339  }
1340  m_lines += line;
1341  }
1342 }
1343 
1345 {
1346  // Draw the window background after calculating bounding rectangle
1347  // in Layout()
1348  if (m_bgFillAlpha) // TODO border?
1349  {
1350  QBrush fill(m_bgFillColor, Qt::SolidPattern);
1351  auto *shape = new SubShape(m_subScreen, QString("cc708bg%1").arg(m_num),
1353  shape->SetFillBrush(fill);
1354  shape->SetArea(MythRect(m_bounds));
1355  }
1356  // The background is drawn first so that it appears behind
1357  // everything else.
1359 }
1360 
1362  const std::vector<CC708String*> &list,
1363  float aspect)
1364 {
1365  LOG(VB_VBI, LOG_DEBUG,LOC +
1366  QString("Display Win %1, Anchor_id %2, x_anch %3, y_anch %4, "
1367  "relative %5")
1368  .arg(m_num).arg(win.m_anchor_point).arg(win.m_anchor_horizontal)
1369  .arg(win.m_anchor_vertical).arg(win.m_relative_pos));
1370  int pixelSize = m_safeArea.height() / 20;
1371  if (m_subScreen)
1372  m_subScreen->SetFontSize(pixelSize);
1373 
1374  float xrange { 160.0F };
1375  if (win.m_relative_pos)
1376  xrange = 100.0F;
1377  else if (aspect > 1.4F)
1378  xrange = 210.0F;
1379  float yrange = win.m_relative_pos ? 100.0F : 75.0F;
1380  float xmult = (float)m_safeArea.width() / xrange;
1381  float ymult = (float)m_safeArea.height() / yrange;
1382  uint anchor_x = (uint)(xmult * (float)win.m_anchor_horizontal);
1383  uint anchor_y = (uint)(ymult * (float)win.m_anchor_vertical);
1384  m_xAnchorPoint = win.m_anchor_point % 3;
1385  m_yAnchorPoint = win.m_anchor_point / 3;
1386  m_xAnchor = anchor_x;
1387  m_yAnchor = anchor_y;
1388 
1389  for (auto *str708 : list)
1390  {
1391  if (str708->m_y >= (uint)m_lines.size())
1392  m_lines.resize(str708->m_y + 1);
1393  FormattedTextChunk chunk(str708->m_str, str708->m_attr, m_subScreen);
1394  m_lines[str708->m_y].chunks += chunk;
1395  LOG(VB_VBI, LOG_INFO, QString("Adding cc708 chunk: win %1 row %2: %3")
1396  .arg(m_num).arg(str708->m_y).arg(chunk.ToLogString()));
1397  }
1398 }
1399 
1401 
1403  const QString &Name, int FontStretch)
1404  : MythScreenType(static_cast<MythScreenType*>(nullptr), Name),
1405  m_player(Player),
1406  m_fontStretch(FontStretch),
1407  m_format(new SubtitleFormat)
1408 {
1409  m_painter = Painter;
1410 
1411  connect(m_player, &MythPlayer::SeekingDone, this, [&]()
1412  {
1413  LOG(VB_PLAYBACK, LOG_INFO, LOC + "SeekingDone signal received.");
1414  m_atEnd = false;
1415  });
1416 }
1417 
1419 {
1421  delete m_format;
1422 #ifdef USING_LIBASS
1424 #endif
1425 }
1426 
1427 void SubtitleScreen::EnableSubtitles(int type, bool forced_only)
1428 {
1429  int prevType = m_subtitleType;
1430  m_subtitleType = type;
1431 
1432  if (forced_only)
1433  {
1434  if (prevType == kDisplayNone)
1435  {
1437  SetVisible(true);
1438  SetArea(MythRect());
1439  }
1440  }
1441  else
1442  {
1443  if (m_subreader)
1444  {
1448  }
1449  if (m_cc608reader)
1451  if (m_cc708reader)
1455  SetArea(MythRect());
1456  }
1457  if (!forced_only || m_family.isEmpty()) {
1458  switch (m_subtitleType)
1459  {
1460  case kDisplayTextSubtitle:
1463  m_textFontZoom = gCoreContext->GetNumSetting("OSDCC708TextZoom", 100);
1464  break;
1465  case kDisplayCC608:
1467  m_textFontZoom = gCoreContext->GetNumSetting("OSDCC708TextZoom", 100);
1468  break;
1469  case kDisplayCC708:
1471  m_textFontZoom = gCoreContext->GetNumSetting("OSDCC708TextZoom", 100);
1472  break;
1473  case kDisplayAVSubtitle:
1475  m_textFontZoom = gCoreContext->GetNumSetting("OSDAVSubZoom", 100);
1476  break;
1477  }
1478  }
1483 }
1484 
1486 {
1488  return;
1490  SetVisible(false);
1491  SetArea(MythRect());
1492 }
1493 
1495 {
1498 #ifdef USING_LIBASS
1499  if (m_assTrack)
1500  ass_flush_events(m_assTrack);
1501 #endif
1502 }
1503 
1505 {
1511  m_cc608reader->ClearBuffers(true, true);
1514 }
1515 
1517 {
1520 }
1521 
1522 void SubtitleScreen::DisplayDVDButton(AVSubtitle* dvdButton, QRect &buttonPos)
1523 {
1524  if (!dvdButton || !m_player)
1525  return;
1526 
1528  if (!vo)
1529  return;
1530 
1533 
1534  float tmp = 0.0;
1535  QRect dummy;
1536  vo->GetOSDBounds(dummy, m_safeArea, tmp, tmp, tmp);
1537 
1538  AVSubtitleRect *hl_button = dvdButton->rects[0];
1539  uint h = hl_button->h;
1540  uint w = hl_button->w;
1541  QRect rect = QRect(hl_button->x, hl_button->y, w, h);
1542  QImage bg_image(hl_button->data[0], w, h, w, QImage::Format_Indexed8);
1543  auto *bgpalette = (uint32_t *)(hl_button->data[1]);
1544 
1545  QVector<uint32_t> bg_palette(4);
1546  for (int i = 0; i < 4; i++)
1547  bg_palette[i] = bgpalette[i];
1548  bg_image.setColorTable(bg_palette);
1549 
1550  // copy button region of background image
1551  const QRect fg_rect(buttonPos.translated(-hl_button->x, -hl_button->y));
1552  QImage fg_image = bg_image.copy(fg_rect);
1553  QVector<uint32_t> fg_palette(4);
1554  auto *fgpalette = (uint32_t *)(dvdButton->rects[1]->data[1]);
1555  if (fgpalette)
1556  {
1557  for (int i = 0; i < 4; i++)
1558  fg_palette[i] = fgpalette[i];
1559  fg_image.setColorTable(fg_palette);
1560  }
1561 
1562  bg_image = bg_image.convertToFormat(QImage::Format_ARGB32);
1563  fg_image = fg_image.convertToFormat(QImage::Format_ARGB32);
1564 
1565  // set pixel of highlight area to highlight color
1566  for (int x=fg_rect.x(); x < fg_rect.x()+fg_rect.width(); ++x)
1567  {
1568  if ((x < 0) || (x > hl_button->w))
1569  continue;
1570  for (int y=fg_rect.y(); y < fg_rect.y()+fg_rect.height(); ++y)
1571  {
1572  if ((y < 0) || (y > hl_button->h))
1573  continue;
1574  bg_image.setPixel(x, y, fg_image.pixel(x-fg_rect.x(),y-fg_rect.y()));
1575  }
1576  }
1577 
1578  AddScaledImage(bg_image, rect);
1579 }
1580 
1581 void SubtitleScreen::SetZoom(int percent)
1582 {
1583  m_textFontZoom = percent;
1584  if (m_family == kSubFamilyAV)
1585  gCoreContext->SaveSetting("OSDAVSubZoom", percent);
1586  else
1587  gCoreContext->SaveSetting("OSDCC708TextZoom", percent);
1588 }
1589 
1591 {
1592  return m_textFontZoom;
1593 }
1594 
1595 void SubtitleScreen::SetDelay(std::chrono::milliseconds ms)
1596 {
1597  m_textFontDelayMs = ms;
1598 }
1599 
1600 std::chrono::milliseconds SubtitleScreen::GetDelay(void) const
1601 {
1602  return m_textFontDelayMs;
1603 }
1604 
1606 {
1607  QList<MythUIType *> list = m_childrenList;
1608  QList<MythUIType *>::iterator it;
1609  for (it = list.begin(); it != list.end(); ++it)
1610  {
1611  MythUIType *child = *it;
1612  auto *wrapper = dynamic_cast<SubWrapper *>(child);
1613  if (wrapper)
1614  {
1615  int whichImageCache = wrapper->GetWhichImageCache();
1616  if (whichImageCache != -1 && (mask & (1UL << whichImageCache)))
1617  {
1619  DeleteChild(child);
1620  }
1621  }
1622  }
1623 }
1624 
1625 // SetElementAdded() should be called after a new element is added to
1626 // the subtitle screen.
1628 {
1629  m_refreshModified = true;
1630 }
1631 
1632 // SetElementResized() should be called after a subtitle screen
1633 // element's size is changed.
1635 {
1636  SetElementAdded();
1637 }
1638 
1639 // SetElementAdded() should be called *before* an element is deleted
1640 // from the subtitle screen.
1642 {
1643  if (!m_refreshDeleted)
1644  SetRedraw();
1645  m_refreshDeleted = true;
1646 }
1647 
1648 // The QFontMetrics class does not account for the MythFontProperties
1649 // shadow and offset properties. This method calculates the
1650 // additional padding to the right and below that is needed for proper
1651 // bounding box computation.
1653 {
1654  QColor color;
1655  int alpha = 0;
1656  int outlineSize = 0;
1657  int shadowWidth = 0;
1658  int shadowHeight = 0;
1659  if (mythfont->hasOutline())
1660  {
1661  mythfont->GetOutline(color, outlineSize, alpha);
1662  MythPoint outline(outlineSize, outlineSize);
1663  outline.NormPoint();
1664  outlineSize = outline.x();
1665  }
1666  if (mythfont->hasShadow())
1667  {
1668  MythPoint shadowOffset;
1669  mythfont->GetShadow(shadowOffset, color, alpha);
1670  shadowOffset.NormPoint();
1671  shadowWidth = abs(shadowOffset.x());
1672  shadowHeight = abs(shadowOffset.y());
1673  // Shadow and outline overlap, so don't just add them.
1674  shadowWidth = std::max(shadowWidth, outlineSize);
1675  shadowHeight = std::max(shadowHeight, outlineSize);
1676  }
1677  return {shadowWidth + outlineSize, shadowHeight + outlineSize};
1678 }
1679 
1680 QSize SubtitleScreen::CalcTextSize(const QString &text,
1681  const CC708CharacterAttribute &format,
1682  float layoutSpacing) const
1683 {
1684  MythFontProperties *mythfont = GetFont(format);
1685  QFont *font = mythfont->GetFace();
1686  QFontMetrics fm(*font);
1687  int width = fm.horizontalAdvance(text);
1688  int height = fm.height() * (1 + PAD_HEIGHT);
1689  if (layoutSpacing > 0 && !text.trimmed().isEmpty())
1690  height = std::max(height, (int)(font->pixelSize() * layoutSpacing));
1691  height += CalcShadowOffsetPadding(mythfont).height();
1692  return {width, height};
1693 }
1694 
1695 // Padding calculation is different depending on whether the padding
1696 // is on the left side or the right side of the text. Padding on the
1697 // right needs to add the shadow and offset padding.
1699  bool isFirst, bool isLast,
1700  int &left, int &right) const
1701 {
1702  MythFontProperties *mythfont = GetFont(format);
1703  QFont *font = mythfont->GetFace();
1704  QFontMetrics fm(*font);
1705  int basicPadding = fm.maxWidth() * PAD_WIDTH;
1706  left = isFirst ? basicPadding : 0;
1707  right = CalcShadowOffsetPadding(mythfont).width() +
1708  (isLast ? basicPadding : 0);
1709 }
1710 
1713 {
1714  return m_format->GetFont(m_family, attr, m_fontSize,
1716 }
1717 
1719 {
1720  SubtitleFormat format;
1721  CC708CharacterAttribute attr(false, false, false, Qt::white);
1722  MythFontProperties *mythfont =
1723  format.GetFont(kSubFamilyTeletext, attr,
1724  /*pixelsize*/20, /*zoom*/100, /*stretch*/100);
1725  return mythfont->face().family();
1726 }
1727 
1729 {
1730  SubtitleFormat format;
1731  return format.GetBackgroundAlpha(kSubFamilyTeletext);
1732 }
1733 
1735 {
1736  if (!m_player)
1737  return false;
1738 
1742  if (!m_subreader)
1743  LOG(VB_GENERAL, LOG_WARNING, LOC + "Failed to get subtitle reader.");
1744  if (!m_cc608reader)
1745  LOG(VB_GENERAL, LOG_WARNING, LOC + "Failed to get CEA-608 reader.");
1746  if (!m_cc708reader)
1747  LOG(VB_GENERAL, LOG_WARNING, LOC + "Failed to get CEA-708 reader.");
1748 
1749  return true;
1750 }
1751 
1753 {
1754  QList<MythUIType *>::iterator it;
1755  QList<MythUIType *>::iterator itNext;
1756 
1757  MythVideoOutput *videoOut = m_player->GetVideoOutput();
1758  MythVideoFrame *currentFrame = videoOut ? videoOut->GetLastShownFrame() : nullptr;
1759  std::chrono::milliseconds now =
1760  currentFrame ? currentFrame->m_timecode : std::chrono::milliseconds::max();
1761  bool needRescale = (m_textFontZoom != m_textFontZoomPrev);
1762 
1763  for (it = m_childrenList.begin(); it != m_childrenList.end(); it = itNext)
1764  {
1765  itNext = it + 1;
1766  MythUIType *child = *it;
1767  auto *wrapper = dynamic_cast<SubWrapper *>(child);
1768  if (!wrapper)
1769  continue;
1770 
1771  // Expire the subtitle object if needed.
1772  std::chrono::milliseconds expireTime = wrapper->GetExpireTime();
1773  if (expireTime > 0ms && expireTime < now)
1774  {
1775  DeleteChild(child);
1777  continue;
1778  }
1779 
1780  // Rescale the AV subtitle image if the zoom changed.
1781  if (expireTime > 0ms && needRescale)
1782  {
1783  auto *image = dynamic_cast<SubImage *>(child);
1784  if (image)
1785  {
1786  double factor = m_textFontZoom / (double)m_textFontZoomPrev;
1787  QSize size = image->GetImage()->size();
1788  size *= factor;
1789  image->GetImage()->Resize(size);
1791  }
1792  }
1793  }
1794 
1795  DisplayAVSubtitles(); // allow forced subtitles to work
1796 
1799  else if (kDisplayCC708 == m_subtitleType)
1803  while (!m_qInited.isEmpty())
1804  {
1805  FormattedTextSubtitle *fsub = m_qInited.takeFirst();
1806  fsub->WrapLongLines();
1807  fsub->Layout();
1808  fsub->PreRender();
1809  fsub->Draw();
1810  delete fsub;
1811  SetElementAdded();
1812  }
1813 
1821 }
1822 
1824 {
1825  m_refreshModified = false;
1826  m_refreshDeleted = false;
1827 }
1828 
1830 {
1831  if (!m_refreshModified)
1832  return;
1833 
1834  QRegion visible;
1835  QListIterator<MythUIType *> i(m_childrenList);
1836  while (i.hasNext())
1837  {
1838  MythUIType *img = i.next();
1839  auto *wrapper = dynamic_cast<SubWrapper *>(img);
1840  if (wrapper && img->IsVisible())
1841  visible = visible.united(wrapper->GetOrigArea());
1842  }
1843 
1844  if (visible.isEmpty())
1845  return;
1846 
1847  QRect bounding = visible.boundingRect();
1848  bounding = bounding.translated(m_safeArea.topLeft());
1849  bounding = m_safeArea.intersected(bounding);
1850  int left = m_safeArea.left() - bounding.left();
1851  int top = m_safeArea.top() - bounding.top();
1852  SetArea(MythRect(bounding));
1853 
1854  i.toFront();
1855  while (i.hasNext())
1856  {
1857  MythUIType *img = i.next();
1858  auto *wrapper = dynamic_cast<SubWrapper *>(img);
1859  if (wrapper && img->IsVisible())
1860  img->SetArea(MythRect(wrapper->GetOrigArea().translated(left, top)));
1861  }
1862 }
1863 
1865 {
1866  if (!m_player || !m_subreader)
1867  return;
1868 
1870  QMutexLocker lock(&(subs->m_lock));
1871  if (subs->m_buffers.empty()
1874  return;
1875 
1876  MythVideoOutput *videoOut = m_player->GetVideoOutput();
1877  MythVideoFrame *currentFrame = videoOut ? videoOut->GetLastShownFrame() : nullptr;
1878  int ret {0};
1879 
1880  if (!currentFrame || !videoOut)
1881  return;
1882 
1883  if (subs->m_buffers.empty() && (m_subreader->GetParser() != nullptr))
1884  {
1885  if (subs->m_needSync)
1886  {
1887  // Seeking on a subtitle stream is a pain. The internals
1888  // of ff_subtitles_queue_seek() calls search_sub_ts(),
1889  // which always returns the subtitle that starts on or
1890  // before the current timestamp.
1891  //
1892  // If avformat_seek_file() has been asked to search
1893  // forward, then the subsequent range check will always
1894  // fail because avformat_seek_file() has set the minimum
1895  // timestamp to the requested timestamp. This call only
1896  // succeeds if the timestamp matches to the millisecond.
1897  //
1898  // If avformat_seek_file() has been asked to search
1899  // backward then a subtitle will be returned, but because
1900  // that subtitle is in the past the code below this
1901  // comment will always consume that subtitle, resulting in
1902  // a new seek every time this function is called.
1903  //
1904  // The solution seems to be to seek backwards so that we
1905  // get the subtitle that should have most recently been
1906  // displayed, then skip that subtitle to get the one that
1907  // should be displayed next.
1908  lock.unlock();
1909  m_subreader->SeekFrame(currentFrame->m_timecode.count()*1000,
1910  AVSEEK_FLAG_BACKWARD);
1911  ret = m_subreader->ReadNextSubtitle();
1912  if (ret < 0)
1913  {
1914  m_atEnd = true;
1915 #ifdef DEBUG_SUBTITLES
1916  if (VERBOSE_LEVEL_CHECK(VB_PLAYBACK, LOG_DEBUG))
1917  {
1918  LOG(VB_PLAYBACK, LOG_DEBUG,
1919  LOC + QString("time %1, no subtitle available after seek")
1920  .arg(toString(currentFrame)));
1921  }
1922 #endif
1923  }
1924  lock.relock();
1925  subs->m_needSync = false;
1926 
1927  // extra check to avoid segfault
1928  if (subs->m_buffers.empty())
1929  return;
1930  AVSubtitle subtitle = subs->m_buffers.front();
1931  if (subtitle.end_display_time < currentFrame->m_timecode.count())
1932  {
1933  subs->m_buffers.pop_front();
1934 #ifdef DEBUG_SUBTITLES
1935  if (VERBOSE_LEVEL_CHECK(VB_PLAYBACK, LOG_DEBUG))
1936  {
1937  LOG(VB_PLAYBACK, LOG_DEBUG,
1938  LOC + QString("time %1, drop %2")
1939  .arg(toString(currentFrame), toString(subtitle)));
1940  }
1941 #endif
1942  }
1943  }
1944 
1945  // Always add one subtitle.
1946  lock.unlock();
1947  if (!m_atEnd)
1948  {
1949  ret = m_subreader->ReadNextSubtitle();
1950  if (ret < 0)
1951  {
1952  m_atEnd = true;
1953 #ifdef DEBUG_SUBTITLES
1954  if (VERBOSE_LEVEL_CHECK(VB_PLAYBACK, LOG_DEBUG))
1955  {
1956  LOG(VB_PLAYBACK, LOG_DEBUG,
1957  LOC + QString("time %1, no subtitle available")
1958  .arg(toString(currentFrame)));
1959  }
1960 #endif
1961  }
1962  }
1963  lock.relock();
1964  }
1965 
1966  float tmp = 0.0;
1967  QRect dummy;
1968  videoOut->GetOSDBounds(dummy, m_safeArea, tmp, tmp, tmp);
1969 
1970  [[maybe_unused]] bool assForceNext {false};
1971  while (!subs->m_buffers.empty())
1972  {
1973  AVSubtitle subtitle = subs->m_buffers.front();
1974  if (subtitle.start_display_time > currentFrame->m_timecode.count())
1975  {
1976 #ifdef DEBUG_SUBTITLES
1977  if (VERBOSE_LEVEL_CHECK(VB_PLAYBACK, LOG_DEBUG))
1978  {
1979  LOG(VB_PLAYBACK, LOG_DEBUG,
1980  LOC + QString("time %1, next %2")
1981  .arg(toString(currentFrame), toString(subtitle)));
1982  }
1983 #endif
1984  break;
1985  }
1986 
1987  // If this is the most recently displayed subtitle and a
1988  // backward jump means that it needs to be displayed again,
1989  // the call to ass_render_frame will say there is no work to
1990  // be done. Force RenderAssTrack to display the subtitle
1991  // anyway.
1992  assForceNext = true;
1993 
1994  auto displayfor = std::chrono::milliseconds(subtitle.end_display_time -
1995  subtitle.start_display_time);
1996 #ifdef DEBUG_SUBTITLES
1997  if (VERBOSE_LEVEL_CHECK(VB_PLAYBACK, LOG_DEBUG))
1998  {
1999  LOG(VB_PLAYBACK, LOG_DEBUG,
2000  LOC + QString("time %1, show %2")
2001  .arg(toString(currentFrame), toString(subtitle)));
2002  }
2003 #endif
2004  if (displayfor == 0ms)
2005  displayfor = 60s;
2006  displayfor = (displayfor < 50ms) ? 50ms : displayfor;
2007  std::chrono::milliseconds late = currentFrame->m_timecode -
2008  std::chrono::milliseconds(subtitle.start_display_time);
2009 
2011  subs->m_buffers.pop_front();
2012  for (std::size_t i = 0; i < subtitle.num_rects; ++i)
2013  {
2014  AVSubtitleRect* rect = subtitle.rects[i];
2015 
2016  bool displaysub = true;
2017  if (!subs->m_buffers.empty() &&
2018  subs->m_buffers.front().end_display_time <
2019  currentFrame->m_timecode.count())
2020  {
2021  displaysub = false;
2022  }
2023 
2024  if (displaysub && rect->type == SUBTITLE_BITMAP)
2025  {
2026  QRect display(rect->display_x, rect->display_y,
2027  rect->display_w, rect->display_h);
2028 
2029  // XSUB and some DVD/DVB subs are based on the original video
2030  // size before the video was converted. We need to guess the
2031  // original size and allow for the difference
2032 
2033  int right = rect->x + rect->w;
2034  int bottom = rect->y + rect->h;
2035  if (subs->m_fixPosition || (currentFrame->m_height < bottom) ||
2036  (currentFrame->m_width < right) ||
2037  !display.width() || !display.height())
2038  {
2039  int sd_height = 576;
2040  if ((m_player->GetFrameRate() > 26.0F ||
2041  m_player->GetFrameRate() < 24.0F) && bottom <= 480)
2042  sd_height = 480;
2043 
2044  int height { 1080 };
2045  if ((currentFrame->m_height <= sd_height) &&
2046  (bottom <= sd_height))
2047  height = sd_height;
2048  else if ((currentFrame->m_height <= 720) && bottom <= 720)
2049  height = 720;
2050 
2051  int width { 1920 };
2052  if ((currentFrame->m_width <= 720) && (right <= 720))
2053  width = 720;
2054  else if ((currentFrame->m_width <= 1280) &&
2055  (right <= 1280))
2056  width = 1280;
2057  display = QRect(0, 0, width, height);
2058  }
2059 
2060  // split into upper/lower to allow zooming
2061  QRect bbox;
2062  int uh = (display.height() / 2) - rect->y;
2063  std::chrono::milliseconds displayuntil = currentFrame->m_timecode + displayfor;
2064  if (uh > 0)
2065  {
2066  bbox = QRect(0, 0, rect->w, uh);
2067  uh = DisplayScaledAVSubtitles(rect, bbox, true, display,
2068  rect->flags & AV_SUBTITLE_FLAG_FORCED,
2069  QString("avsub%1t").arg(i),
2070  displayuntil, late);
2071  }
2072  else
2073  {
2074  uh = 0;
2075  }
2076  int lh = rect->h - uh;
2077  if (lh > 0)
2078  {
2079  bbox = QRect(0, uh, rect->w, lh);
2080  DisplayScaledAVSubtitles(rect, bbox, false, display,
2081  rect->flags & AV_SUBTITLE_FLAG_FORCED,
2082  QString("avsub%1b").arg(i),
2083  displayuntil, late);
2084  }
2085  }
2086 #ifdef USING_LIBASS
2087  else if (displaysub && rect->type == SUBTITLE_ASS)
2088  {
2090  AddAssEvent(rect->ass, subtitle.start_display_time, subtitle.end_display_time);
2091  }
2092 #endif
2093  }
2095  }
2096 #ifdef USING_LIBASS
2097  RenderAssTrack(currentFrame->m_timecode, assForceNext);
2098 #endif
2099 }
2100 
2101 int SubtitleScreen::DisplayScaledAVSubtitles(const AVSubtitleRect *rect,
2102  QRect &bbox, bool top,
2103  QRect &display, int forced,
2104  const QString& imagename,
2105  std::chrono::milliseconds displayuntil,
2106  std::chrono::milliseconds late)
2107 {
2108  // split image vertically if it spans middle of display
2109  // - split point is empty line nearest the middle
2110  // crop image to reduce scaling time
2111  bool prev_empty = false;
2112 
2113  // initialize to opposite edges
2114  int xmin = bbox.right();
2115  int xmax = bbox.left();
2116  int ymin = bbox.bottom();
2117  int ymax = bbox.top();
2118  int ylast = bbox.top();
2119  int ysplit = bbox.bottom();
2120 
2121  // find bounds of active image
2122  for (int y = bbox.top(); y <= bbox.bottom(); ++y)
2123  {
2124  if (y >= rect->h)
2125  {
2126  // end of image
2127  if (!prev_empty)
2128  ylast = y;
2129  break;
2130  }
2131 
2132  bool empty = true;
2133  for (int x = bbox.left(); x <= bbox.right(); ++x)
2134  {
2135  const uint8_t color =
2136  rect->data[0][(y * rect->linesize[0]) + x];
2137  const uint32_t pixel = *((uint32_t *)rect->data[1] + color);
2138  if (pixel & 0xff000000)
2139  {
2140  empty = false;
2141  xmin = std::min(x, xmin);
2142  xmax = std::max(x, xmax);
2143  }
2144  }
2145 
2146  if (!empty)
2147  {
2148  ymin = std::min(y, ymin);
2149  ymax = std::max(y, ymax);
2150  }
2151  else if (!prev_empty)
2152  {
2153  // remember uppermost empty line
2154  ylast = y;
2155  }
2156  prev_empty = empty;
2157  }
2158 
2159  if (ymax <= ymin)
2160  return 0;
2161 
2162  if (top)
2163  {
2164  if (ylast < ymin)
2165  // no empty lines
2166  return 0;
2167 
2168  if (ymax == bbox.bottom())
2169  {
2170  ymax = ylast;
2171  ysplit = ylast;
2172  }
2173  }
2174 
2175  // set new bounds
2176  bbox.setLeft(xmin);
2177  bbox.setRight(xmax);
2178  bbox.setTop(ymin);
2179  bbox.setBottom(ymax);
2180 
2181  // copy active region
2182  // AVSubtitleRect's image data's not guaranteed to be 4 byte
2183  // aligned.
2184 
2185  QRect orig_rect(bbox.left(), bbox.top(), bbox.width(), bbox.height());
2186 
2187  QImage qImage(bbox.width(), bbox.height(), QImage::Format_ARGB32);
2188  for (int y = 0; y < bbox.height(); ++y)
2189  {
2190  int ysrc = y + bbox.top();
2191  for (int x = 0; x < bbox.width(); ++x)
2192  {
2193  int xsrc = x + bbox.left();
2194  const uint8_t color =
2195  rect->data[0][(ysrc * rect->linesize[0]) + xsrc];
2196  const uint32_t pixel = *((uint32_t *)rect->data[1] + color);
2197  qImage.setPixel(x, y, pixel);
2198  }
2199  }
2200 
2201  // translate to absolute coordinates
2202  bbox.translate(rect->x, rect->y);
2203 
2204  // scale and move according to zoom factor
2205  bbox.setWidth(bbox.width() * m_textFontZoom / 100);
2206  bbox.setHeight(bbox.height() * m_textFontZoom / 100);
2207 
2208  MythVideoOutput *videoOut = m_player->GetVideoOutput();
2209  QRect scaled = videoOut->GetImageRect(bbox, &display);
2210 
2211  if (scaled.size() != orig_rect.size())
2212  qImage = qImage.scaled(scaled.width(), scaled.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
2213 
2214  int hsize = m_safeArea.width();
2215  int vsize = m_safeArea.height();
2216 
2217  scaled.moveLeft(((100 - m_textFontZoom) * hsize / 2 + m_textFontZoom * scaled.left()) / 100);
2218  if (top)
2219  {
2220  // anchor up
2221  scaled.moveTop(scaled.top() * m_textFontZoom / 100);
2222  }
2223  else
2224  {
2225  // anchor down
2226  scaled.moveTop(((100 - m_textFontZoom) * vsize + m_textFontZoom * scaled.top()) / 100);
2227  }
2228 
2229  MythImage* image = m_painter->GetFormatImage();
2230  SubImage *uiimage = nullptr;
2231 
2232  if (image)
2233  {
2234  image->Assign(qImage);
2235  uiimage = new SubImage(this, imagename, MythRect(scaled), displayuntil);
2236  if (uiimage)
2237  {
2238  uiimage->SetImage(image);
2239  uiimage->SetArea(MythRect(scaled));
2240  SetElementAdded();
2241  }
2242  image->DecrRef();
2243  image = nullptr;
2244  }
2245 
2246  if (uiimage)
2247  {
2248  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Display %1AV sub until %2ms")
2249  .arg(forced ? "FORCED " : "").arg(displayuntil.count()));
2250  if (late > 50ms)
2251  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("AV Sub was %1ms late").arg(late.count()));
2252  }
2253 
2254  return (ysplit + 1);
2255 }
2256 
2258 {
2259  if (!m_player || !m_subreader)
2260  return;
2261 
2262  std::chrono::milliseconds duration = 0ms;
2263  QStringList subs = m_subreader->GetRawTextSubtitles(duration);
2264  if (subs.empty())
2265  return;
2266 
2268  if (!vo)
2269  return;
2270 
2271  MythVideoFrame *currentFrame = vo->GetLastShownFrame();
2272  if (!currentFrame)
2273  return;
2274 
2275  m_safeArea = vo->GetSafeRect();
2276 
2277  // delete old subs that may still be on screen
2280  DrawTextSubtitles(subs, currentFrame->m_timecode, duration);
2281 }
2282 
2283 void SubtitleScreen::DrawTextSubtitles(const QStringList &subs,
2284  std::chrono::milliseconds start,
2285  std::chrono::milliseconds duration)
2286 {
2287  auto *fsub = new FormattedTextSubtitleSRT(m_family, m_safeArea, start,
2288  duration, this, subs);
2289  m_qInited.append(fsub);
2290 }
2291 
2293 {
2294  if (!m_cc608reader)
2295  return;
2296 
2297  bool changed = (m_textFontZoom != m_textFontZoomPrev);
2298 
2299  if (!m_player || !m_player->GetVideoOutput())
2300  return;
2302 
2303  CC608Buffer* textlist = m_cc608reader->GetOutputText(changed);
2304  if (!changed)
2305  return;
2306 
2309 
2310  if (!textlist)
2311  return;
2312 
2313  QMutexLocker locker(&textlist->m_lock);
2314 
2315  if (textlist->m_buffers.empty())
2316  return;
2317 
2318  auto *fsub = new FormattedTextSubtitle608(textlist->m_buffers, m_family,
2319  m_safeArea, this/*, m_textFontZoom*/);
2320  m_qInited.append(fsub);
2321 }
2322 
2324 {
2326  return;
2327 
2328  CC708Service *cc708service = m_cc708reader->GetCurrentService();
2329  float video_aspect = m_player->GetVideoAspect();
2330  QRect oldsafe = m_safeArea;
2332  bool changed = (oldsafe != m_safeArea || m_textFontZoom != m_textFontZoomPrev);
2333  if (changed)
2334  {
2335  for (auto & window : cc708service->m_windows)
2336  window.SetChanged();
2337  }
2338 
2339  uint64_t clearMask = 0;
2340  QList<FormattedTextSubtitle *> addList;
2341  for (int i = 0; i < k708MaxWindows; i++)
2342  {
2343  CC708Window &win = cc708service->m_windows[i];
2344  if (win.GetExists() && win.GetVisible() && !win.GetChanged())
2345  continue;
2346  if (!win.GetChanged())
2347  continue;
2348 
2349  clearMask |= (1UL << i);
2350  win.ResetChanged();
2351  if (!win.GetExists() || !win.GetVisible())
2352  continue;
2353 
2354  QMutexLocker locker(&win.m_lock);
2355  std::vector<CC708String*> list = win.GetStrings();
2356  if (!list.empty())
2357  {
2358  auto *fsub = new FormattedTextSubtitle708(win, i, list, m_family,
2359  m_safeArea, this, video_aspect);
2360  addList.append(fsub);
2361  }
2363  }
2364  if (clearMask)
2365  {
2366  Clear708Cache(clearMask);
2367  }
2368  if (!addList.empty())
2369  m_qInited.append(addList);
2370 }
2371 
2372 void SubtitleScreen::AddScaledImage(QImage &img, QRect &pos)
2373 {
2375  if (!vo)
2376  return;
2377 
2378  QRect scaled = vo->GetImageRect(pos);
2379  if (scaled.size() != pos.size())
2380  {
2381  img = img.scaled(scaled.width(), scaled.height(),
2382  Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
2383  }
2384 
2385  MythImage* image = m_painter->GetFormatImage();
2386  if (image)
2387  {
2388  image->Assign(img);
2389  MythUIImage *uiimage = new SubImage(this, "dvd_button", MythRect(scaled), 0ms);
2390  if (uiimage)
2391  {
2392  uiimage->SetImage(image);
2393  uiimage->SetArea(MythRect(scaled));
2394  SetElementAdded();
2395  }
2396  image->DecrRef();
2397  }
2398 }
2399 
2400 #ifdef USING_LIBASS
2401 static void myth_libass_log(int level, const char *fmt, va_list vl, void */*ctx*/)
2402 {
2403  uint64_t verbose_mask = VB_GENERAL;
2404  LogLevel_t verbose_level = LOG_INFO;
2405 
2406  switch (level)
2407  {
2408  case 0: //MSGL_FATAL
2409  verbose_level = LOG_EMERG;
2410  break;
2411  case 1: //MSGL_ERR
2412  verbose_level = LOG_ERR;
2413  break;
2414  case 2: //MSGL_WARN
2415  verbose_level = LOG_WARNING;
2416  break;
2417  case 4: //MSGL_INFO
2418  verbose_level = LOG_INFO;
2419  break;
2420  case 6: //MSGL_V
2421  case 7: //MSGL_DBG2
2422  verbose_level = LOG_DEBUG;
2423  break;
2424  default:
2425  return;
2426  }
2427 
2428  if (!VERBOSE_LEVEL_CHECK(verbose_mask, verbose_level))
2429  return;
2430 
2431  static QMutex s_stringLock;
2432  s_stringLock.lock();
2433 
2434  QString str = QString::vasprintf(fmt, vl);
2435  LOG(verbose_mask, verbose_level, QString("libass: %1").arg(str));
2436  s_stringLock.unlock();
2437 }
2438 
2440 {
2441  if (m_assLibrary && m_assRenderer)
2442  return true;
2443 
2444  if (!m_assLibrary)
2445  {
2446  m_assLibrary = ass_library_init();
2447  if (!m_assLibrary)
2448  return false;
2449 
2450  ass_set_message_cb(m_assLibrary, myth_libass_log, nullptr);
2451  ass_set_extract_fonts(m_assLibrary, static_cast<int>(true));
2452  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Initialised libass object.");
2453  }
2454 
2455  LoadAssFonts();
2456 
2457  if (!m_assRenderer)
2458  {
2459  m_assRenderer = ass_renderer_init(m_assLibrary);
2460  if (!m_assRenderer)
2461  return false;
2462 
2463 #ifdef Q_OS_ANDROID
2464  // fontconfig doesn't yet work for us on Android. For the
2465  // time being, more explicitly set a font we know should
2466  // exist. This was adapted from VLC master as of 2019-01-21.
2467  const char *psz_font = "/system/fonts/DroidSans.ttf";
2468  const char *psz_font_family = "Droid Sans";
2469 #else
2470  const char *psz_font = nullptr;
2471  const char *psz_font_family = "sans-serif";
2472 #endif
2473  ass_set_fonts(m_assRenderer, psz_font, psz_font_family, 1, nullptr, 1);
2474  ass_set_hinting(m_assRenderer, ASS_HINTING_LIGHT);
2475  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Initialised libass renderer.");
2476  }
2477 
2478  return true;
2479 }
2480 
2482 {
2483  if (!m_assLibrary || !m_player)
2484  return;
2485 
2487  if (m_assFontCount == count)
2488  return;
2489 
2490  ass_clear_fonts(m_assLibrary);
2491  m_assFontCount = 0;
2492 
2493  // TODO these need checking and/or reinitialising after a stream change
2494  for (uint i = 0; i < count; ++i)
2495  {
2496  QByteArray filename;
2497  QByteArray font;
2499  ass_add_font(m_assLibrary, filename.data(), font.data(), font.size());
2500  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Retrieved font '%1'")
2501  .arg(filename.constData()));
2502  m_assFontCount++;
2503  }
2504 }
2505 
2507 {
2508  CleanupAssTrack();
2509 
2510  if (m_assRenderer)
2511  ass_renderer_done(m_assRenderer);
2512  m_assRenderer = nullptr;
2513 
2514  if (m_assLibrary)
2515  {
2516  ass_clear_fonts(m_assLibrary);
2517  m_assFontCount = 0;
2518  ass_library_done(m_assLibrary);
2519  }
2520  m_assLibrary = nullptr;
2521 }
2522 
2524 {
2525  if (!InitialiseAssLibrary() || !m_player)
2526  return;
2527 
2528  if (tracknum == m_assTrackNum && m_assTrack)
2529  return;
2530 
2531  LoadAssFonts();
2532  CleanupAssTrack();
2533  m_assTrack = ass_new_track(m_assLibrary);
2534  m_assTrackNum = tracknum;
2535 
2536  QByteArray header = m_player->GetDecoder()->GetSubHeader(tracknum);
2537  if (header.isNull())
2538  {
2540  if (parser)
2541  header = parser->GetSubHeader();
2542  }
2543  if (!header.isNull())
2544  ass_process_codec_private(m_assTrack, header.data(), header.size());
2545 
2548 }
2549 
2551 {
2552  if (m_assTrack)
2553  ass_free_track(m_assTrack);
2554  m_assTrack = nullptr;
2555 }
2556 
2557 void SubtitleScreen::AddAssEvent(char *event, uint32_t starttime, uint32_t endtime)
2558 {
2559  if (m_assTrack && event)
2560  ass_process_chunk(m_assTrack, event, strlen(event), starttime, endtime-starttime);
2561 }
2562 
2564 {
2565  ass_set_frame_size(m_assRenderer, m_safeArea.width(), m_safeArea.height());
2566  ass_set_margins(m_assRenderer, 0, 0, 0, 0);
2567  ass_set_use_margins(m_assRenderer, static_cast<int>(true));
2568  ass_set_font_scale(m_assRenderer, 1.0);
2569 }
2570 
2571 void SubtitleScreen::RenderAssTrack(std::chrono::milliseconds timecode, bool force)
2572 {
2573  if (!m_player || !m_assRenderer || !m_assTrack)
2574  return;
2575 
2577  if (!vo )
2578  return;
2579 
2580  QRect oldscreen = m_safeArea;
2582  if (oldscreen != m_safeArea)
2584 
2585  int changed = 0;
2586  ASS_Image *images = ass_render_frame(m_assRenderer, m_assTrack, timecode.count(), &changed);
2587  if (!changed && !force)
2588  return;
2589 
2590  int count = 0;
2593  while (images)
2594  {
2595  if (images->w == 0 || images->h == 0)
2596  {
2597  images = images->next;
2598  continue;
2599  }
2600 
2601  uint8_t alpha = images->color & 0xFF;
2602  uint8_t blue = images->color >> 8 & 0xFF;
2603  uint8_t green = images->color >> 16 & 0xFF;
2604  uint8_t red = images->color >> 24 & 0xFF;
2605 
2606  if (alpha == 255)
2607  {
2608  images = images->next;
2609  continue;
2610  }
2611 
2612  QSize img_size(images->w, images->h);
2613  QRect img_rect(images->dst_x,images->dst_y,
2614  images->w, images->h);
2615  QImage qImage(img_size, QImage::Format_ARGB32);
2616  qImage.fill(0x00000000);
2617 
2618  unsigned char *src = images->bitmap;
2619  for (int y = 0; y < images->h; ++y)
2620  {
2621  for (int x = 0; x < images->w; ++x)
2622  {
2623  uint8_t value = src[x];
2624  if (value)
2625  {
2626  uint32_t pixel = (value * (255 - alpha) / 255 << 24) |
2627  (red << 16) | (green << 8) | blue;
2628  qImage.setPixel(x, y, pixel);
2629  }
2630  }
2631  src += images->stride;
2632  }
2633 
2634  MythImage* image = m_painter->GetFormatImage();
2635  SubImage *uiimage = nullptr;
2636 
2637  if (image)
2638  {
2639  image->Assign(qImage);
2640  QString name = QString("asssub%1").arg(count);
2641  uiimage = new SubImage(this, name, MythRect(img_rect), 0ms);
2642  if (uiimage)
2643  {
2644  uiimage->SetImage(image);
2645  uiimage->SetArea(MythRect(img_rect));
2646  SetElementAdded();
2647  }
2648  image->DecrRef();
2649  }
2650 
2651  images = images->next;
2652  count++;
2653  }
2654 }
2655 #endif // USING_LIBASS
SubtitleScreen::DisplayRawTextSubtitles
void DisplayRawTextSubtitles(void)
Definition: subtitlescreen.cpp:2257
kSubAttrOutlinealpha
static const QString kSubAttrOutlinealpha("outlinealpha")
FormattedTextSubtitle
Definition: subtitlescreen.h:75
CC608Buffer::m_lock
QMutex m_lock
Definition: cc608reader.h:49
MythVideoOutput::GetOSDBounds
virtual void GetOSDBounds(QRect &Total, QRect &Visible, float &VisibleAspect, float &FontScaling, float ThemeAspect) const
Definition: mythvideoout.cpp:207
kTrackTypeAttachment
@ kTrackTypeAttachment
Definition: decoderbase.h:37
SubImage::SubImage
SubImage(MythUIType *parent, const QString &name, const MythRect &area, std::chrono::milliseconds expireTime)
Definition: subtitlescreen.cpp:82
FormattedTextSubtitle::CacheNum
virtual int CacheNum(void) const
Definition: subtitlescreen.h:96
kSubAttrShadowcolor
static const QString kSubAttrShadowcolor("shadowcolor")
k708AttrEdgeDepressed
const uint k708AttrEdgeDepressed
Definition: cc708window.cpp:95
FormattedTextSubtitle608::Init
void Init(const std::vector< CC608Text * > &buffers)
Definition: subtitlescreen.cpp:1256
FormattedTextSubtitle::Layout
virtual void Layout(void)
Definition: subtitlescreen.cpp:805
CC608Text
Definition: cc608reader.h:17
SubtitleFormat::GetBackgroundAlpha
int GetBackgroundAlpha(const QString &family)
Definition: subtitlescreen.cpp:660
LINE_SPACING
static const float LINE_SPACING
Definition: subtitlescreen.cpp:797
differentColor
static QColor differentColor(const QColor &color)
Definition: subtitlescreen.cpp:442
SubtitleFormat::IsUnlocked
bool IsUnlocked(const QString &prefix, const QString &property) const
Definition: subtitlescreen.cpp:165
MythDate::toString
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:84
SubtitleScreen::m_textFontZoom
int m_textFontZoom
Definition: subtitlescreen.h:246
SubtitleScreen::m_cc708reader
CC708Reader * m_cc708reader
Definition: subtitlescreen.h:242
DecoderBase::GetTrack
int GetTrack(uint Type)
Definition: decoderbase.cpp:957
SubtitleScreen::Pulse
void Pulse(void) override
Pulse is called 70 times a second to trigger a single frame of an animation.
Definition: subtitlescreen.cpp:1752
CC708CharacterAttribute::GetEdgeColor
QColor GetEdgeColor(void) const
Definition: cc708window.h:129
SubtitleScreen::m_textFontMinDurationMs
std::chrono::milliseconds m_textFontMinDurationMs
Definition: subtitlescreen.h:250
MythPlayer::GetVideoOutput
MythVideoOutput * GetVideoOutput(void)
Definition: mythplayer.h:166
SubWrapper
Definition: subtitlescreen.cpp:37
TextSubtitleParser
Definition: textsubtitleparser.h:86
SubtitleFormat::m_shapeMap
QHash< QString, MythUIShape * > m_shapeMap
Definition: subtitlescreen.cpp:184
MythUIImage
Image widget, displays a single image or multiple images in sequence.
Definition: mythuiimage.h:97
MythVideoOutput
Definition: mythvideoout.h:35
FormattedTextSubtitle::m_base
QString m_base
Definition: subtitlescreen.h:100
MythUIType::DeleteChild
void DeleteChild(const QString &name)
Delete a named child of this UIType.
Definition: mythuitype.cpp:153
FormattedTextSubtitleSRT::Init
void Init(const QStringList &subs)
Definition: subtitlescreen.cpp:994
CC708Service::m_windows
std::array< CC708Window, k708MaxWindows > m_windows
Definition: cc708window.h:320
kTrackTypeSubtitle
@ kTrackTypeSubtitle
Definition: decoderbase.h:31
SubtitleScreen::m_assLibrary
ASS_Library * m_assLibrary
Definition: subtitlescreen.h:273
FormattedTextChunk::Split
bool Split(FormattedTextChunk &newChunk)
Definition: subtitlescreen.cpp:683
FormattedTextLine
Definition: subtitlescreen.h:60
SubSimpleText::SubSimpleText
SubSimpleText(const QString &text, const MythFontProperties &font, QRect rect, Qt::Alignment align, MythUIType *parent, const QString &name, int whichImageCache, std::chrono::milliseconds expireTime)
Definition: subtitlescreen.cpp:62
MythUIImage::m_images
QHash< int, MythImage * > m_images
Definition: mythuiimage.h:170
CC708CharacterAttribute::m_edgeType
uint m_edgeType
Definition: cc708window.h:90
CC708Window::m_lock
QRecursiveMutex m_lock
Definition: cc708window.h:310
SubtitleScreen::GetDelay
std::chrono::milliseconds GetDelay(void) const
Definition: subtitlescreen.cpp:1600
SubtitleScreen::GetTeletextBackgroundAlpha
static int GetTeletextBackgroundAlpha(void)
Definition: subtitlescreen.cpp:1728
kSubAttrOutlinecolor
static const QString kSubAttrOutlinecolor("outlinecolor")
SubtitleScreen::InitialiseAssLibrary
bool InitialiseAssLibrary(void)
Definition: subtitlescreen.cpp:2439
CC708Reader::ClearBuffers
void ClearBuffers(void)
Definition: cc708reader.cpp:41
kDisplayCC608
@ kDisplayCC608
Definition: videoouttypes.h:16
CC708CharacterAttribute::m_penSize
uint m_penSize
Definition: cc708window.h:86
kDisplayCC708
@ kDisplayCC708
Definition: videoouttypes.h:17
CC708Window
Definition: cc708window.h:194
SubtitleScreen::AddScaledImage
void AddScaledImage(QImage &img, QRect &pos)
Definition: subtitlescreen.cpp:2372
kSubAttrUnderline
static const QString kSubAttrUnderline("underline")
FormattedTextSubtitle::m_duration
std::chrono::milliseconds m_duration
Definition: subtitlescreen.h:104
MythPainter::GetFormatImage
MythImage * GetFormatImage()
Returns a blank reference counted image in the format required for the Draw functions for this painte...
Definition: mythpainter.cpp:525
CC708CharacterAttribute::m_fontTag
uint m_fontTag
Definition: cc708window.h:89
CC708Reader::GetCurrentService
CC708Service * GetCurrentService(void)
Definition: cc708reader.h:23
CC708Reader::SetEnabled
void SetEnabled(bool enable)
Definition: cc708reader.h:24
SubWrapper::m_swOrigArea
const MythRect m_swOrigArea
Definition: subtitlescreen.cpp:54
kSubWindowName
static const QString kSubWindowName("osd_subtitle")
MythFontProperties::GetFace
QFont * GetFace(void)
Definition: mythfontproperties.h:18
mythuisimpletext.h
FormattedTextSubtitle708::m_num
int m_num
Definition: subtitlescreen.h:171
cc
Definition: cc.h:9
DecoderBase::GetAttachmentData
virtual void GetAttachmentData(uint, QByteArray &, QByteArray &)
Definition: decoderbase.h:251
CC608Buffer::m_buffers
std::vector< CC608Text * > m_buffers
Definition: cc608reader.h:50
kSubAttrShadowoffset
static const QString kSubAttrShadowoffset("shadowoffset")
SubtitleScreen::m_assRenderer
ASS_Renderer * m_assRenderer
Definition: subtitlescreen.h:274
SubtitleScreen::DisplayCC708Subtitles
void DisplayCC708Subtitles(void)
Definition: subtitlescreen.cpp:2323
VERBOSE_LEVEL_CHECK
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
Definition: mythlogging.h:29
FormattedTextSubtitleSRT
Definition: subtitlescreen.h:113
SubWrapper::m_swExpireTime
const std::chrono::milliseconds m_swExpireTime
Definition: subtitlescreen.cpp:56
SubtitleScreen::DisplayScaledAVSubtitles
int DisplayScaledAVSubtitles(const AVSubtitleRect *rect, QRect &bbox, bool top, QRect &display, int forced, const QString &imagename, std::chrono::milliseconds displayuntil, std::chrono::milliseconds late)
Definition: subtitlescreen.cpp:2101
FormattedTextSubtitle::m_yAnchor
int m_yAnchor
Definition: subtitlescreen.h:109
MythFontProperties::face
QFont face(void) const
Definition: mythfontproperties.cpp:40
MythDate::formatTime
QString formatTime(std::chrono::milliseconds msecs, QString fmt)
Format a milliseconds time value.
Definition: mythdate.cpp:233
SubtitleScreen::Create
bool Create(void) override
Definition: subtitlescreen.cpp:1734
CC708CharacterAttribute
Definition: cc708window.h:83
kSubProvider
static const QString kSubProvider("provider")
MythUIType::AddFont
bool AddFont(const QString &text, MythFontProperties *fontProp)
Definition: mythuitype.cpp:1326
MythVideoFrame::m_width
int m_width
Definition: mythframe.h:121
x3
static int x3
Definition: mythsocket.cpp:52
MythUIType::SetArea
virtual void SetArea(const MythRect &rect)
Definition: mythuitype.cpp:610
MythFontProperties::hasShadow
bool hasShadow(void) const
Definition: mythfontproperties.h:29
MythPlayer::GetFrameRate
float GetFrameRate(void) const
Definition: mythplayer.h:134
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
SubtitleFormat::MakePrefix
static QString MakePrefix(const QString &family, const CC708CharacterAttribute &attr)
Definition: subtitlescreen.cpp:378
kSubFileName
static const QString kSubFileName("osd_subtitle.xml")
SubtitleFormat::~SubtitleFormat
~SubtitleFormat(void)
Definition: subtitlescreen.cpp:367
MythScreenType
Screen in which all other widgets are contained and rendered.
Definition: mythscreentype.h:45
FormattedTextSubtitle708::m_bgFillAlpha
uint m_bgFillAlpha
Definition: subtitlescreen.h:172
SubtitleScreen::DisplayAVSubtitles
void DisplayAVSubtitles(void)
Definition: subtitlescreen.cpp:1864
SubtitleScreen::m_assTrack
ASS_Track * m_assTrack
Definition: subtitlescreen.h:276
SubtitleFormat::GetBackground
SubShape * GetBackground(MythUIType *parent, const QString &name, const QString &family, const CC708CharacterAttribute &attr, const MythRect &area, int whichImageCache, std::chrono::milliseconds start, std::chrono::milliseconds duration)
Definition: subtitlescreen.cpp:622
SubtitleScreen::CleanupAssLibrary
void CleanupAssLibrary(void)
Definition: subtitlescreen.cpp:2506
MythFontProperties::SetColor
void SetColor(const QColor &color)
Definition: mythfontproperties.cpp:49
MythPlayer::GetCC708Reader
virtual CC708Reader * GetCC708Reader(uint=0)
Definition: mythplayer.h:195
CC708Window::m_anchor_vertical
uint m_anchor_vertical
Definition: cc708window.h:252
pixel
static guint32 * pixel
--------------------------------------------------—**
Definition: goom_core.cpp:24
build_compdb.parser
parser
Definition: build_compdb.py:7
FormattedTextSubtitle::m_xAnchor
int m_xAnchor
Definition: subtitlescreen.h:108
SubtitleScreen::m_refreshModified
bool m_refreshModified
Definition: subtitlescreen.h:254
MythPlayer
Definition: mythplayer.h:83
SubtitleReader::EnableAVSubtitles
void EnableAVSubtitles(bool enable)
Definition: subtitlereader.cpp:33
FormattedTextChunk::m_bgShapeRect
QRect m_bgShapeRect
Definition: subtitlescreen.h:54
FormattedTextLine::CalcSize
QSize CalcSize(float layoutSpacing=0.0F) const
Definition: subtitlescreen.cpp:772
force
bool force
Definition: mythcommflag.cpp:70
LOC
#define LOC
Definition: subtitlescreen.cpp:15
MythFontProperties::SetShadow
void SetShadow(bool on, QPoint offset, const QColor &color, int alpha)
Definition: mythfontproperties.cpp:55
SubtitleScreen::m_textFontDelayMs
std::chrono::milliseconds m_textFontDelayMs
Definition: subtitlescreen.h:248
MythRect
Wrapper around QRect allowing us to handle percentage and other relative values for areas in mythui.
Definition: mythrect.h:17
MythUIType::Pulse
virtual void Pulse(void)
Pulse is called 70 times a second to trigger a single frame of an animation.
Definition: mythuitype.cpp:456
k708AttrEdgeUniform
const uint k708AttrEdgeUniform
Definition: cc708window.cpp:96
MythVideoBounds::GetDisplayVideoRect
QRect GetDisplayVideoRect(void) const
Definition: mythvideobounds.h:77
kSubAttrShadowalpha
static const QString kSubAttrShadowalpha("shadowalpha")
FormattedTextSubtitle708
Definition: subtitlescreen.h:147
hardwareprofile.distros.mythtv_data.data_mythtv.prefix
string prefix
Definition: data_mythtv.py:40
mythuiimage.h
FormattedTextSubtitle608
Definition: subtitlescreen.h:131
SubtitleFormat::m_shadowOffsetMap
QHash< QString, QPoint > m_shadowOffsetMap
Definition: subtitlescreen.cpp:191
MythUIType::DeleteAllChildren
void DeleteAllChildren(void)
Delete all child widgets.
Definition: mythuitype.cpp:222
FormattedTextSubtitle::m_safeArea
const QRect m_safeArea
Definition: subtitlescreen.h:102
SubtitleScreen::GetZoom
int GetZoom(void) const
Definition: subtitlescreen.cpp:1590
SubtitleScreen::m_fontStretch
int m_fontStretch
Definition: subtitlescreen.h:257
PAD_WIDTH
static const float PAD_WIDTH
Definition: subtitlescreen.cpp:798
SubtitleScreen::RenderAssTrack
void RenderAssTrack(std::chrono::milliseconds timecode, bool force)
Definition: subtitlescreen.cpp:2571
SubtitleFormat::m_changeMap
QHash< QString, QSet< QString > > m_changeMap
Definition: subtitlescreen.cpp:185
SubtitleScreen::m_family
QString m_family
Definition: subtitlescreen.h:258
MythUIShape::SetFillBrush
void SetFillBrush(QBrush fill)
Definition: mythuishape.cpp:42
tmp
static guint32 * tmp
Definition: goom_core.cpp:26
CC608Reader::GetOutputText
CC608Buffer * GetOutputText(bool &changed, int &streamIdx)
Definition: cc608reader.cpp:52
MythFontProperties::hasOutline
bool hasOutline(void) const
Definition: mythfontproperties.h:32
SubWrapper::GetExpireTime
std::chrono::milliseconds GetExpireTime(void) const
Definition: subtitlescreen.cpp:49
SubtitleScreen::m_refreshDeleted
bool m_refreshDeleted
Definition: subtitlescreen.h:255
SubtitleReader::ClearAVSubtitles
void ClearAVSubtitles(void)
Definition: subtitlereader.cpp:144
MythUIType::AddChild
void AddChild(MythUIType *child)
Add a child UIType.
Definition: mythuitype.cpp:89
SubtitleScreen::~SubtitleScreen
~SubtitleScreen() override
Definition: subtitlescreen.cpp:1418
SubImage::GetImage
MythImage * GetImage(void)
Definition: subtitlescreen.cpp:86
SubtitleFormat::SubtitleFormat
SubtitleFormat(void)=default
CC708Window::GetVisible
bool GetVisible(void) const
Definition: cc708window.h:288
SubtitleFormat::Diff
static QSet< QString > Diff(const QString &family, const CC708CharacterAttribute &attr, MythFontProperties *font1, MythFontProperties *font2, MythUIShape *bg1, MythUIShape *bg2)
Definition: subtitlescreen.cpp:551
CC708CharacterAttribute::m_boldface
bool m_boldface
Definition: cc708window.h:93
SubtitleScreen::DisplayCC608Subtitles
void DisplayCC608Subtitles(void)
Definition: subtitlescreen.cpp:2292
SubtitleScreen::m_textFontDelayMsPrev
std::chrono::milliseconds m_textFontDelayMsPrev
Definition: subtitlescreen.h:249
DecoderBase::GetTrackCount
virtual uint GetTrackCount(uint Type)
Definition: decoderbase.cpp:909
CC708Window::m_relative_pos
uint m_relative_pos
Definition: cc708window.h:251
kDisplayRawTextSubtitle
@ kDisplayRawTextSubtitle
Definition: videoouttypes.h:20
FormattedTextChunk::CalcPadding
void CalcPadding(bool isFirst, bool isLast, int &left, int &right) const
Definition: subtitlescreen.cpp:677
srtColorString
static QString srtColorString(const QColor &color)
Definition: subtitlescreen.cpp:219
mythfontproperties.h
FormattedTextChunk
Definition: subtitlescreen.h:32
mythlogging.h
kSubFamilyText
static const QString kSubFamilyText("text")
subtitlescreen.h
CC708CharacterAttribute::GetFGColor
QColor GetFGColor(void) const
Definition: cc708window.h:116
SubtitleReader::ReadNextSubtitle
int ReadNextSubtitle(void)
Definition: subtitlereader.cpp:171
SubtitleScreen::LoadAssFonts
void LoadAssFonts(void)
Definition: subtitlescreen.cpp:2481
SubtitleScreen::ResetElementState
void ResetElementState(void)
Definition: subtitlescreen.cpp:1823
SubtitleScreen::CalcTextSize
QSize CalcTextSize(const QString &text, const CC708CharacterAttribute &format, float layoutSpacing) const
Definition: subtitlescreen.cpp:1680
kSubFamilyTeletext
static const QString kSubFamilyTeletext("teletext")
MythVideoFrame::m_timecode
std::chrono::milliseconds m_timecode
Definition: mythframe.h:131
SubtitleScreen::SetDelay
void SetDelay(std::chrono::milliseconds ms)
Definition: subtitlescreen.cpp:1595
SubtitleScreen::GetTeletextFontName
static QString GetTeletextFontName(void)
Definition: subtitlescreen.cpp:1718
SubtitleReader::EnableTextSubtitles
void EnableTextSubtitles(bool enable)
Definition: subtitlereader.cpp:38
SubtitleScreen::Clear708Cache
void Clear708Cache(uint64_t mask)
Definition: subtitlescreen.cpp:1605
CC708Service
Definition: cc708window.h:313
SubtitleScreen::EnableSubtitles
void EnableSubtitles(int type, bool forced_only=false)
Definition: subtitlescreen.cpp:1427
SubtitleScreen::m_qInited
QList< FormattedTextSubtitle * > m_qInited
Definition: subtitlescreen.h:260
MythFontProperties
Definition: mythfontproperties.h:13
CC608Buffer
Definition: cc608reader.h:36
kDisplayAVSubtitle
@ kDisplayAVSubtitle
Definition: videoouttypes.h:15
CC708CharacterAttribute::GetFGAlpha
uint GetFGAlpha(void) const
Definition: cc708window.h:131
k708AttrEdgeLeftDropShadow
const uint k708AttrEdgeLeftDropShadow
Definition: cc708window.cpp:97
SubShape::SubShape
SubShape(MythUIType *parent, const QString &name, const MythRect &area, int whichImageCache, std::chrono::milliseconds expireTime)
Definition: subtitlescreen.cpp:73
kSubAttrShadow
static const QString kSubAttrShadow("shadow")
FormattedTextSubtitle::m_xAnchorPoint
int m_xAnchorPoint
Definition: subtitlescreen.h:106
MythPlayer::GetCC608Reader
virtual CC608Reader * GetCC608Reader(uint=0)
Definition: mythplayer.h:196
SubtitleScreen::m_fontSize
int m_fontSize
Definition: subtitlescreen.h:245
FormattedTextSubtitleSRT::WrapLongLines
void WrapLongLines(void) override
Definition: subtitlescreen.cpp:1110
SubtitleScreen::DrawTextSubtitles
void DrawTextSubtitles(const QStringList &subs, std::chrono::milliseconds start, std::chrono::milliseconds duration)
Definition: subtitlescreen.cpp:2283
FormattedTextChunk::m_parent
const SubtitleScreen * m_parent
Definition: subtitlescreen.h:50
PAD_HEIGHT
static const float PAD_HEIGHT
Definition: subtitlescreen.cpp:799
AVSubtitles
Definition: subtitlereader.h:22
MythVideoOutput::GetImageRect
QRect GetImageRect(QRect Rect, QRect *DisplayRect=nullptr)
translates caption/dvd button rectangle into 'screen' space
Definition: mythvideoout.cpp:322
MythPoint::setY
void setY(const QString &sY)
Definition: mythrect.cpp:540
SubtitleReader::SeekFrame
void SeekFrame(int64_t ts, int flags)
Definition: subtitlereader.cpp:48
MythFontProperties::color
QColor color(void) const
Definition: mythfontproperties.h:26
MythImage::DecrRef
int DecrRef(void) override
Decrements reference count and deletes on 0.
Definition: mythimage.cpp:52
SubtitleReader::ClearRawTextSubtitles
void ClearRawTextSubtitles(void)
Definition: subtitlereader.cpp:207
SubtitleScreen::CleanupAssTrack
void CleanupAssTrack(void)
Definition: subtitlescreen.cpp:2550
SubtitleScreen::ClearDisplayedSubtitles
void ClearDisplayedSubtitles(void)
Definition: subtitlescreen.cpp:1516
SubtitleScreen::ResizeAssRenderer
void ResizeAssRenderer(void)
Definition: subtitlescreen.cpp:2563
MythUIType::m_childrenList
QList< MythUIType * > m_childrenList
Definition: mythuitype.h:253
SubtitleReader::FreeAVSubtitle
static void FreeAVSubtitle(AVSubtitle &sub)
Definition: subtitlereader.cpp:155
MythFontProperties::GetOutline
void GetOutline(QColor &color, int &size, int &alpha) const
Definition: mythfontproperties.cpp:82
SubtitleScreen::m_textFontDurationExtensionMs
std::chrono::milliseconds m_textFontDurationExtensionMs
Definition: subtitlescreen.h:252
SubtitleFormat::m_pixelSizeMap
QHash< QString, int > m_pixelSizeMap
Definition: subtitlescreen.cpp:189
SubtitleScreen::DisplayDVDButton
void DisplayDVDButton(AVSubtitle *dvdButton, QRect &buttonPos)
Definition: subtitlescreen.cpp:1522
SubtitleFormat::Load
void Load(const QString &family, const CC708CharacterAttribute &attr)
Definition: subtitlescreen.cpp:479
FormattedTextChunk::m_textFont
MythFontProperties * m_textFont
Definition: subtitlescreen.h:55
SubtitleFormat::CreateProviderDefault
static void CreateProviderDefault(const QString &family, const CC708CharacterAttribute &attr, MythUIType *parent, bool isComplement, MythFontProperties **font, MythUIShape **bg)
Definition: subtitlescreen.cpp:386
SubtitleFormat::GetFont
MythFontProperties * GetFont(const QString &family, const CC708CharacterAttribute &attr, int pixelSize, int zoom, int stretch)
Definition: subtitlescreen.cpp:260
mythpainter.h
FormattedTextSubtitle608::Layout
void Layout(void) override
Definition: subtitlescreen.cpp:1209
SubShape
Definition: subtitlescreen.cpp:70
SubtitleReader::EnableRawTextSubtitles
void EnableRawTextSubtitles(bool enable)
Definition: subtitlereader.cpp:43
MythPoint::NormPoint
void NormPoint(void)
Definition: mythrect.cpp:471
FormattedTextChunk::m_textRect
QRect m_textRect
Definition: subtitlescreen.h:57
kSubFamilyAV
static const QString kSubFamilyAV("AV")
uint
unsigned int uint
Definition: compat.h:81
SubtitleScreen::m_assTrackNum
int m_assTrackNum
Definition: subtitlescreen.h:275
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:55
clamp
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:204
SubtitleScreen::m_textFontZoomPrev
int m_textFontZoomPrev
Definition: subtitlescreen.h:247
SubtitleScreen::DisableForcedSubtitles
void DisableForcedSubtitles(void)
Definition: subtitlescreen.cpp:1485
SubtitleScreen::SetZoom
void SetZoom(int percent)
Definition: subtitlescreen.cpp:1581
FormattedTextSubtitle708::Draw
void Draw(void) override
Definition: subtitlescreen.cpp:1344
k708AttrSizeLarge
const uint k708AttrSizeLarge
Definition: cc708window.cpp:78
CC708CharacterAttribute::GetBGColor
QColor GetBGColor(void) const
Definition: cc708window.h:123
CC708CharacterAttribute::GetBGAlpha
uint GetBGAlpha(void) const
Definition: cc708window.h:138
SubtitleReader::GetRawTextSubtitles
QStringList GetRawTextSubtitles(std::chrono::milliseconds &duration)
Definition: subtitlereader.cpp:184
myth_libass_log
static void myth_libass_log(int level, const char *fmt, va_list vl, void *)
Definition: subtitlescreen.cpp:2401
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:916
SubtitleScreen::SetElementDeleted
void SetElementDeleted(void)
Definition: subtitlescreen.cpp:1641
MythFontProperties::GetShadow
void GetShadow(QPoint &offset, QColor &color, int &alpha) const
Definition: mythfontproperties.cpp:75
MythPlayer::GetDecoder
DecoderBase * GetDecoder(void)
Returns the stream decoder currently in use.
Definition: mythplayer.h:186
kSubAttrBGfill
static const QString kSubAttrBGfill("bgfill")
FormattedTextChunk::m_textName
QString m_textName
Definition: subtitlescreen.h:56
FormattedTextChunk::m_text
QString m_text
Definition: subtitlescreen.h:48
DecoderBase::GetSubHeader
virtual QByteArray GetSubHeader(uint)
Definition: decoderbase.h:250
MythPlayer::GetSubReader
virtual SubtitleReader * GetSubReader(uint=0)
Definition: mythplayer.h:197
kSubAttrItalics
static const QString kSubAttrItalics("italics")
AVSubtitles::m_needSync
bool m_needSync
Definition: subtitlereader.h:29
MythUIType
The base class on which all widgets and screens are based.
Definition: mythuitype.h:85
kSubAttrBold
static const QString kSubAttrBold("bold")
MythUIShape
A widget for rendering primitive shapes and lines.
Definition: mythuishape.h:21
FormattedTextSubtitle::m_lines
QVector< FormattedTextLine > m_lines
Definition: subtitlescreen.h:101
k708MaxWindows
const int k708MaxWindows
Definition: cc708window.h:72
SubtitleReader::GetAVSubtitles
AVSubtitles * GetAVSubtitles(void)
Definition: subtitlereader.h:57
SubtitleFormat::m_cleanup
QVector< MythUIType * > m_cleanup
Definition: subtitlescreen.cpp:192
k708AttrSizeSmall
const uint k708AttrSizeSmall
Definition: cc708window.cpp:76
buffers
Definition: freesurround.cpp:51
prevY
unsigned int prevY
Definition: filters.cpp:111
CalcShadowOffsetPadding
static QSize CalcShadowOffsetPadding(MythFontProperties *mythfont)
Definition: subtitlescreen.cpp:1652
SubtitleScreen::AddAssEvent
void AddAssEvent(char *event, uint32_t starttime, uint32_t endtime)
Definition: subtitlescreen.cpp:2557
FormattedTextSubtitle708::m_bgFillColor
QColor m_bgFillColor
Definition: subtitlescreen.h:173
kSubAttrPixelsize
static const QString kSubAttrPixelsize("pixelsize")
k708AttrEdgeRightDropShadow
const uint k708AttrEdgeRightDropShadow
Definition: cc708window.cpp:98
MythFontProperties::SetOutline
void SetOutline(bool on, const QColor &color, int size, int alpha)
Definition: mythfontproperties.cpp:65
FormattedTextChunk::CalcSize
QSize CalcSize(float layoutSpacing=0.0F) const
Definition: subtitlescreen.cpp:672
k708AttrEdgeRaised
const uint k708AttrEdgeRaised
Definition: cc708window.cpp:94
CC708Window::GetChanged
bool GetChanged(void) const
Definition: cc708window.h:289
SubtitleFormat::m_outlineSizeMap
QHash< QString, int > m_outlineSizeMap
Definition: subtitlescreen.cpp:190
AVSubtitles::m_buffers
MythDeque< AVSubtitle > m_buffers
Definition: subtitlereader.h:26
FormattedTextChunk::ToLogString
QString ToLogString(void) const
Definition: subtitlescreen.cpp:703
SubtitleScreen::ClearNonDisplayedSubtitles
void ClearNonDisplayedSubtitles(void)
Definition: subtitlescreen.cpp:1504
SubtitleScreen::InitialiseAssTrack
void InitialiseAssTrack(int tracknum)
Definition: subtitlescreen.cpp:2523
XMLParseBase::LoadWindowFromXML
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
Definition: xmlparsebase.cpp:687
MythPainter
Definition: mythpainter.h:34
SubtitleScreen::GetSubtitleFormat
class SubtitleFormat * GetSubtitleFormat(void)
Definition: subtitlescreen.h:197
MythImage
Definition: mythimage.h:36
fontToString
static QString fontToString(MythFontProperties *f)
Definition: subtitlescreen.cpp:227
SubtitleScreen::m_format
class SubtitleFormat * m_format
Definition: subtitlescreen.h:261
SubtitleFormat::Complement
static void Complement(MythFontProperties *font, MythUIShape *bg)
Definition: subtitlescreen.cpp:449
Name
Definition: channelsettings.cpp:71
SubtitleFormat::m_fontMap
QHash< QString, MythFontProperties * > m_fontMap
Definition: subtitlescreen.cpp:183
anonymous_namespace{BlankFrameDetector.cpp}::isBlank
bool isBlank(unsigned char median, float stddev, unsigned char maxmedian, float maxstddev)
Definition: BlankFrameDetector.cpp:25
AVSubtitles::m_fixPosition
bool m_fixPosition
Definition: subtitlereader.h:28
kDisplayNone
@ kDisplayNone
Definition: videoouttypes.h:12
SubtitleReader::GetParser
TextSubtitleParser * GetParser(void)
Definition: subtitlereader.h:64
kSubAttrOutline
static const QString kSubAttrOutline("outline")
SubtitleScreen::m_atEnd
bool m_atEnd
Definition: subtitlescreen.h:256
FormattedTextSubtitle708::Init
void Init(const CC708Window &win, const std::vector< CC708String * > &list, float aspect)
Definition: subtitlescreen.cpp:1361
SubtitleScreen::m_assFontCount
uint m_assFontCount
Definition: subtitlescreen.h:277
MythUIShape::m_fillBrush
QBrush m_fillBrush
Definition: mythuishape.h:42
SubtitleScreen::SetElementResized
void SetElementResized(void)
Definition: subtitlescreen.cpp:1634
MythPlayer::GetVideoAspect
float GetVideoAspect(void) const
Definition: mythplayer.h:133
FormattedTextSubtitle::PreRender
virtual void PreRender(void)
Definition: subtitlescreen.cpp:873
FormattedTextChunk::m_bgShapeName
QString m_bgShapeName
Definition: subtitlescreen.h:53
FormattedTextChunk::m_format
CC708CharacterAttribute m_format
Definition: subtitlescreen.h:49
CC708Window::GetStrings
std::vector< CC708String * > GetStrings(void) const
Definition: cc708window.cpp:284
CC708Window::DisposeStrings
static void DisposeStrings(std::vector< CC708String * > &strings)
Definition: cc708window.cpp:378
SubtitleScreen::OptimiseDisplayedArea
void OptimiseDisplayedArea(void)
Definition: subtitlescreen.cpp:1829
MythVideoOutput::GetSafeRect
QRect GetSafeRect()
Returns a QRect describing an area of the screen on which it is 'safe' to render the On Screen Displa...
Definition: mythvideoout.cpp:379
AVSubtitles::m_lock
QMutex m_lock
Definition: subtitlereader.h:27
SubtitleScreen::SetElementAdded
void SetElementAdded(void)
Definition: subtitlescreen.cpp:1627
MythUIType::SetVisible
virtual void SetVisible(bool visible)
Definition: mythuitype.cpp:1105
SubWrapper::GetOrigArea
MythRect GetOrigArea(void) const
Definition: subtitlescreen.cpp:50
MythUIType::m_painter
MythPainter * m_painter
Definition: mythuitype.h:295
CC708CharacterAttribute::m_offset
uint m_offset
Definition: cc708window.h:87
SubSimpleText
Definition: subtitlescreen.cpp:59
FormattedTextSubtitle::m_subScreen
SubtitleScreen * m_subScreen
Definition: subtitlescreen.h:105
CC708Window::ResetChanged
void ResetChanged(void)
Definition: cc708window.h:306
FormattedTextSubtitle::ToSRT
QStringList ToSRT(void) const
Definition: subtitlescreen.cpp:947
SubtitleScreen::m_subtitleType
int m_subtitleType
Definition: subtitlescreen.h:244
MythVideoFrame::m_height
int m_height
Definition: mythframe.h:122
SubtitleScreen::SubtitleScreen
SubtitleScreen(MythPlayer *Player, MythPainter *Painter, const QString &Name, int FontStretch)
Definition: subtitlescreen.cpp:1402
MythUIImage::SetImage
void SetImage(MythImage *img)
Should not be used unless absolutely necessary since it bypasses the image caching and threaded loade...
Definition: mythuiimage.cpp:749
SubtitleScreen::m_textFontMinDurationMsPrev
std::chrono::milliseconds m_textFontMinDurationMsPrev
Definition: subtitlescreen.h:251
FormattedTextSubtitle::m_yAnchorPoint
int m_yAnchorPoint
Definition: subtitlescreen.h:107
kSubAttrColor
static const QString kSubAttrColor("color")
k708AttrFontSmallCaps
const uint k708AttrFontSmallCaps
Definition: cc708window.cpp:91
FormattedTextSubtitle::m_start
std::chrono::milliseconds m_start
Definition: subtitlescreen.h:103
kDisplayTextSubtitle
@ kDisplayTextSubtitle
Definition: videoouttypes.h:18
SubtitleScreen::m_safeArea
QRect m_safeArea
Definition: subtitlescreen.h:243
CC608Reader::ClearBuffers
void ClearBuffers(bool input, bool output, int outputStreamIdx=-1)
Definition: cc608reader.cpp:448
SubtitleScreen::m_cc608reader
CC608Reader * m_cc608reader
Definition: subtitlescreen.h:241
CC708Window::m_anchor_point
uint m_anchor_point
Definition: cc708window.h:250
CC708CharacterAttribute::m_italics
bool m_italics
Definition: cc708window.h:92
CC608Reader::SetEnabled
void SetEnabled(bool enable)
Definition: cc608reader.h:85
SubtitleScreen::m_subreader
SubtitleReader * m_subreader
Definition: subtitlescreen.h:240
Player
Definition: zmliveplayer.h:34
MythPoint::setX
void setX(const QString &sX)
Definition: mythrect.cpp:530
MythVideoFrame
Definition: mythframe.h:88
MythUISimpleText
Simplified text widget, displays a text string.
Definition: mythuisimpletext.h:22
MythCoreContext::SaveSetting
void SaveSetting(const QString &key, int newValue)
Definition: mythcorecontext.cpp:885
MythImage::Assign
void Assign(const QImage &img)
Definition: mythimage.cpp:77
FormattedTextChunk::PreRender
bool PreRender(bool isFirst, bool isLast, int &x, int y, int height)
Definition: subtitlescreen.cpp:727
SubtitleFormat
Definition: subtitlescreen.cpp:144
SubWrapper::m_swWhichImageCache
const int m_swWhichImageCache
Definition: subtitlescreen.cpp:55
SubtitleScreen::m_player
MythPlayer * m_player
Definition: subtitlescreen.h:239
MythVideoOutput::GetLastShownFrame
virtual MythVideoFrame * GetLastShownFrame()
Returns frame from the head of the ready to be displayed queue, if StartDisplayingFrame has been call...
Definition: mythvideoout.cpp:316
build_compdb.filename
filename
Definition: build_compdb.py:21
SubWrapper::GetWhichImageCache
int GetWhichImageCache(void) const
Definition: subtitlescreen.cpp:51
SubtitleScreen::CalcPadding
void CalcPadding(const CC708CharacterAttribute &format, bool isFirst, bool isLast, int &left, int &right) const
Definition: subtitlescreen.cpp:1698
MythUIType::SetRedraw
void SetRedraw(void)
Definition: mythuitype.cpp:313
SubWrapper::SubWrapper
SubWrapper(const MythRect &rect, std::chrono::milliseconds expireTime, int whichImageCache=-1)
Definition: subtitlescreen.cpp:40
kSubFamily608
static const QString kSubFamily608("608")
MythUIType::IsVisible
bool IsVisible(bool recurse=false) const
Definition: mythuitype.cpp:903
SubtitleScreen::SetFontSize
void SetFontSize(int pixelSize)
Definition: subtitlescreen.h:210
SubtitleScreen::GetFont
MythFontProperties * GetFont(const CC708CharacterAttribute &attr) const
Definition: subtitlescreen.cpp:1712
kSubFamily708
static const QString kSubFamily708("708")
FormattedTextLine::chunks
QList< FormattedTextChunk > chunks
Definition: subtitlescreen.h:68
SubImage
Definition: subtitlescreen.cpp:79
MythPlayer::SeekingDone
void SeekingDone()
CC708Window::m_anchor_horizontal
uint m_anchor_horizontal
Definition: cc708window.h:253
kSubAttrOutlinesize
static const QString kSubAttrOutlinesize("outlinesize")
mythuishape.h
FormattedTextSubtitle::Draw
virtual void Draw(void)
Definition: subtitlescreen.cpp:897
FormattedTextSubtitle::m_bounds
QRect m_bounds
Definition: subtitlescreen.h:110
FormattedTextSubtitle::WrapLongLines
virtual void WrapLongLines(void)
Definition: subtitlescreen.h:91
SubtitleScreen::m_textFontDurationExtensionMsPrev
std::chrono::milliseconds m_textFontDurationExtensionMsPrev
Definition: subtitlescreen.h:253
SubtitleScreen::ClearAllSubtitles
void ClearAllSubtitles(void)
Definition: subtitlescreen.cpp:1494
CC708CharacterAttribute::m_underline
bool m_underline
Definition: cc708window.h:91
extract_cc608
static QString extract_cc608(QString &text, int &color, bool &isItalic, bool &isUnderline)
Extract everything from the text buffer up until the next format control character.
Definition: subtitlescreen.cpp:1155
MythPoint
Wrapper around QPoint allowing us to handle percentage and other relative values for positioning in m...
Definition: mythrect.h:88
CC708Window::GetExists
bool GetExists(void) const
Definition: cc708window.h:287