1 #include <QFontMetrics>
12 #define LOC QString("Subtitles: ")
13 #define LOC_WARN QString("Subtitles Warning: ")
28 std::chrono::milliseconds expireTime,
29 int whichImageCache = -1) :
50 QRect rect, Qt::Alignment align,
52 int whichImageCache, std::chrono::milliseconds expireTime) :
61 int whichImageCache, std::chrono::milliseconds expireTime) :
63 SubWrapper(area, expireTime, whichImageCache) {}
70 std::chrono::milliseconds expireTime) :
138 int pixelSize,
int zoom,
int stretch);
140 const QString &family,
144 std::chrono::milliseconds start,
145 std::chrono::milliseconds duration);
147 static QString
MakePrefix(
const QString &family,
150 void Load(
const QString &family,
163 static QSet<QString>
Diff(
const QString &family,
208 return QString(
"#%1%2%3")
209 .arg(color.red(), 2, 16, QLatin1Char(
'0'))
210 .arg(color.green(), 2, 16, QLatin1Char(
'0'))
211 .arg(color.blue(), 2, 16, QLatin1Char(
'0'));
217 result = QString(
"face=%1 pixelsize=%2 color=%3 "
218 "italics=%4 weight=%5 underline=%6")
220 .arg(f->
GetFace()->pixelSize())
222 .arg(
static_cast<int>(f->
GetFace()->italic()))
224 .arg(
static_cast<int>(f->
GetFace()->underline()));
230 result += QString(
" shadow=%1 shadowoffset=%2 "
231 "shadowcolor=%3 shadowalpha=%4")
232 .arg(QString::number(
static_cast<int>(f->
hasShadow())),
233 QString(
"(%1,%2)").arg(offset.x()).arg(offset.y()),
235 QString::number(alpha));
237 result += QString(
" outline=%1 outlinecolor=%2 "
238 "outlinesize=%3 outlinealpha=%4")
249 int pixelSize,
int zoom,
int stretch)
251 int origPixelSize = pixelSize;
252 float scale = zoom / 100.0;
254 scale = scale * 32 / 42;
256 scale = scale * 42 / 32;
268 result->
GetFace()->setPixelSize(pixelSize);
270 result->
GetFace()->setStretch(stretch);
291 int off = lroundf(scale * pixelSize / 20);
292 offset = QPoint(off, off);
307 offset.
setX(lroundf(offset.x() * scale));
308 offset.
setY(lroundf(offset.y() * scale));
310 result->
SetShadow(shadow, offset, color, alpha);
326 off = lroundf(scale * pixelSize / 20);
336 off = lroundf(point.x() * scale);
338 result->
SetOutline(outline, color, off, alpha);
340 LOG(VB_VBI, LOG_DEBUG,
341 QString(
"GetFont(family=%1, prefix=%2, orig pixelSize=%3, "
342 "new pixelSize=%4 zoom=%5) = %6")
343 .arg(family,
prefix).arg(origPixelSize).arg(pixelSize)
351 for (
int i = 0; i <
m_cleanup.size(); ++i)
363 return family +
"_" + QString::number(attr.
m_fontTag & 0x7);
378 font->GetFace()->setFamily(
"FreeMono");
379 QBrush brush(Qt::black);
380 bg->SetFillBrush(brush);
381 bg->SetLinePen(QPen(brush, 0));
385 static const std::array<const std::string,8> s_cc708Fonts {
395 font->GetFace()->setFamily(QString::fromStdString(s_cc708Fonts[attr.
m_fontTag & 0x7]));
399 font->GetFace()->setFamily(
"Droid Sans");
400 QBrush brush(Qt::black);
401 bg->SetFillBrush(brush);
402 bg->SetLinePen(QPen(brush, 0));
406 font->GetFace()->setFamily(
"FreeMono");
409 QBrush brush(Qt::black);
410 bg->SetFillBrush(brush);
411 bg->SetLinePen(QPen(brush, 0));
413 font->GetFace()->setPixelSize(10);
425 return color == Qt::white ? Qt::black : Qt::white;
437 face->setItalic(!face->italic());
438 face->setPixelSize(face->pixelSize() + 1);
439 face->setUnderline(!face->underline());
440 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
441 face->setWeight((face->weight() + 1) % 32);
448 offset.setX(offset.x() + 1);
454 size + 1, 255 - alpha);
457 Qt::SolidPattern : Qt::NoBrush);
464 auto *baseParent =
new MythUIType(
nullptr,
"base");
469 &providerBaseFont, &providerBaseShape);
472 auto *negParent =
new MythUIType(
nullptr,
"base");
484 if (!posResult || !negResult)
485 LOG(VB_VBI, LOG_INFO,
486 QString(
"Couldn't load theme file %1").arg(
kSubFileName));
490 resultFont = providerBaseFont;
496 resultBG = providerBaseShape;
506 resultFont->
GetFace()->setCapitalization(QFont::SmallCaps);
509 LOG(VB_VBI, LOG_DEBUG,
510 QString(
"providerBaseFont = %1").arg(
fontToString(providerBaseFont)));
511 LOG(VB_VBI, LOG_DEBUG,
513 LOG(VB_VBI, LOG_DEBUG,
514 QString(
"resultFont = %1").arg(
fontToString(resultFont)));
515 LOG(VB_VBI, LOG_DEBUG,
523 resultFont->
GetShadow(offset, color, alpha);
540 QSet<QString> result;
541 QFont *face1 = font1->
GetFace();
542 QFont *face2 = font2->
GetFace();
543 if (face1->italic() != face2->italic())
545 if (face1->weight() != face2->weight())
547 if (face1->underline() != face2->underline())
549 if (face1->pixelSize() != face2->pixelSize())
562 font1->
GetShadow(offset1, color1, alpha1);
563 font2->
GetShadow(offset2, color2, alpha2);
564 if (offset1 != offset2)
566 if (color1 != color2)
568 if (alpha1 != alpha2)
582 if (color1 != color2)
586 if (alpha1 != alpha2)
593 for (
auto i = result.constBegin(); i != result.constEnd(); ++i)
594 values +=
" " + (*i);
595 LOG(VB_VBI, LOG_INFO,
596 QString(
"Subtitle family %1 allows provider to change:%2")
604 const QString &family,
608 std::chrono::milliseconds start,
609 std::chrono::milliseconds duration)
616 auto *result =
new SubShape(parent, name, area, whichImageCache,
624 result->SetFillBrush(brush);
625 result->SetLinePen(QPen(brush, 0));
632 LOG(VB_VBI, LOG_DEBUG,
633 QString(
"GetBackground(prefix=%1) = "
634 "{type=%2 alpha=%3 brushstyle=%4 brushcolor=%5}")
635 .arg(
prefix, result->m_type, QString::number(result->GetAlpha()),
636 QString::number(result->m_fillBrush.style()),
659 int &left,
int &right)
const
666 LOG(VB_VBI, LOG_INFO,
667 QString(
"Attempting to split chunk '%1'").arg(
m_text));
668 int lastSpace =
m_text.lastIndexOf(
' ', -2);
671 LOG(VB_VBI, LOG_INFO,
672 QString(
"Failed to split chunk '%1'").arg(
m_text));
677 newChunk.
m_text =
m_text.mid(lastSpace + 1).trimmed() +
' ';
679 LOG(VB_VBI, LOG_INFO,
680 QString(
"Split chunk into '%1' + '%2'").arg(
m_text, newChunk.
m_text));
687 str += QString(
"fg=%1.%2 ")
690 str += QString(
"bg=%1.%2 ")
693 str += QString(
"edge=%1.%2 ")
696 str += QString(
"off=%1 pensize=%2 ")
699 str += QString(
"it=%1 ul=%2 bf=%3 ")
704 str += QString(
" text='%1'").arg(
m_text);
709 int &x,
int y,
int height)
720 while (count <
m_text.length() &&
m_text.at(count) ==
' ')
724 int x_adjust = count * font.horizontalAdvance(
" ");
726 int rightPadding = 0;
727 CalcPadding(isFirst, isLast, leftPadding, rightPadding);
732 QRect bgrect(x - leftPadding, y,
733 chunk_sz.width() + leftPadding + rightPadding,
737 bgrect.setLeft(bgrect.left() + x_adjust);
739 .arg(chunk_sz.width()).arg(height).arg(x).arg(y);
745 .arg(chunk_sz.width()).arg(height).arg(x).arg(y);
747 chunk_sz.width() - x_adjust + rightPadding, height);
749 x += chunk_sz.width();
758 int rightPadding = 0;
759 QList<FormattedTextChunk>::const_iterator it;
761 for (it =
chunks.constBegin(); it !=
chunks.constEnd(); ++it)
763 bool isLast = (it + 1 ==
chunks.constEnd());
764 QSize
tmp = (*it).CalcSize(layoutSpacing);
765 height = std::max(height,
tmp.height());
766 width +=
tmp.width();
767 (*it).CalcPadding(isFirst, isLast, leftPadding, rightPadding);
768 if (it ==
chunks.constBegin())
769 width += leftPadding;
772 return {width + rightPadding, height};
789 int anchor_width = 0;
790 int anchor_height = 0;
791 for (
const auto & line : qAsConst(
m_lines))
794 anchor_width = std::max(anchor_width, sz.width());
795 anchor_height += sz.height();
802 anchor_x -= anchor_width / 2;
804 anchor_x -= anchor_width;
806 anchor_y -= anchor_height / 2;
808 anchor_y -= anchor_height;
811 anchor_y = std::max(0, std::min(anchor_y,
m_safeArea.height() - anchor_height));
812 anchor_x = std::max(0, std::min(anchor_x,
m_safeArea.width() - anchor_width));
814 m_bounds = QRect(anchor_x, anchor_y, anchor_width, anchor_height);
819 for (
int i = 0; i <
m_lines.size(); i++)
822 m_lines[i].m_xIndent = anchor_x;
827 while (!
m_lines[i].chunks.isEmpty() &&
828 m_lines[i].chunks.first().m_text.trimmed().isEmpty())
831 m_lines[i].chunks.first().CalcSize().width();
832 m_lines[i].chunks.removeFirst();
835 while (!
m_lines[i].chunks.isEmpty() &&
836 m_lines[i].chunks.last().m_text.trimmed().isEmpty())
838 m_lines[i].chunks.removeLast();
843 if (!
m_lines[i].chunks.isEmpty())
845 QString *str = &
m_lines[i].chunks.last().m_text;
846 int idx = str->length() - 1;
847 while (idx >= 0 && str->at(idx) ==
' ')
849 str->truncate(idx + 1);
857 for (
int i = 0; i <
m_lines.size(); i++)
861 int height =
m_lines[i].CalcSize().height();
862 QList<FormattedTextChunk>::iterator chunk;
864 for (chunk =
m_lines[i].chunks.begin();
865 chunk !=
m_lines[i].chunks.end();
868 bool isLast = (chunk + 1 ==
m_lines[i].chunks.end());
869 (*chunk).PreRender(isFirst, isLast, x, y, height);
880 QList<MythUIType *> textList;
881 QList<MythUIType *> shapeList;
884 QList<FormattedTextChunk>::const_iterator chunk;
885 for (chunk = line.chunks.constBegin();
886 chunk != line.chunks.constEnd();
898 if ((*chunk).m_textRect.width() > 0) {
901 Qt::AlignLeft|Qt::AlignTop,
907 if ((*chunk).m_bgShapeRect.width() > 0) {
909 GetBackground(
nullptr,
910 (*chunk).m_bgShapeName,
911 m_base, (*chunk).m_format,
917 shapeList += bgshape;
922 while (!shapeList.isEmpty())
924 while (!textList.isEmpty())
931 for (
const auto & ftl : qAsConst(
m_lines))
935 line.fill(
' ', ftl.m_origX);
936 QList<FormattedTextChunk>::const_iterator chunk;
937 for (chunk = ftl.chunks.constBegin();
938 chunk != ftl.chunks.constEnd();
941 const QString &text = (*chunk).m_text;
943 bool isBlank = !attr.
m_underline && text.trimmed().isEmpty();
953 line += QString(
"<font color=\"%1\">")
960 line += QString(
"</font>");
969 if (!line.trimmed().isEmpty())
999 bool isItalic =
false;
1000 bool isBold =
false;
1001 bool isUnderline =
false;
1002 QColor color(Qt::white);
1003 static const QRegularExpression htmlTag {
1004 "</?.+>", QRegularExpression::InvertedGreedinessOption };
1005 QString htmlPrefix(
"<font color=\"");
1006 QString htmlSuffix(
"\">");
1007 for (
const QString& subtitle : qAsConst(subs))
1010 QString text(subtitle);
1011 while (!text.isEmpty())
1013 auto match = htmlTag.match(text);
1014 int pos = match.capturedStart();
1021 text = (pos < 0 ?
"" : text.mid(pos));
1022 LOG(VB_VBI, LOG_INFO, QString(
"Adding SRT chunk: %1")
1027 int htmlLen = match.capturedLength();
1028 QString html = text.left(htmlLen).toLower();
1029 text = text.mid(htmlLen);
1032 else if (html ==
"</i>")
1034 else if (html.startsWith(htmlPrefix) &&
1035 html.endsWith(htmlSuffix))
1037 int colorLen = html.length() -
1038 (htmlPrefix.length() + htmlSuffix.length());
1039 QString colorString(
1040 html.mid(htmlPrefix.length(), colorLen));
1041 QColor newColor(colorString);
1042 if (newColor.isValid())
1048 LOG(VB_VBI, LOG_INFO,
1049 QString(
"Ignoring invalid SRT color specification: "
1050 "'%1'").arg(colorString));
1053 else if (html ==
"</font>")
1055 else if (html ==
"<b>")
1057 else if (html ==
"</b>")
1059 else if (html ==
"<u>")
1061 else if (html ==
"</u>")
1062 isUnderline =
false;
1065 LOG(VB_VBI, LOG_INFO,
1066 QString(
"Ignoring unknown SRT formatting: '%1'")
1070 LOG(VB_VBI, LOG_INFO,
1071 QString(
"SRT formatting change '%1', "
1072 "new ital=%2 bold=%3 uline=%4 color=%5)")
1073 .arg(html).arg(isItalic).arg(isBold).arg(isUnderline)
1084 for (
int i = 0; i <
m_lines.size(); i++)
1086 int width =
m_lines[i].CalcSize().width();
1089 while (width > maxWidth &&
m_lines[i].chunks.size() > 1)
1091 width -=
m_lines[i].chunks.back().CalcSize().width();
1097 LOG(VB_VBI, LOG_INFO,
1098 QString(
"Wrapping chunk to next line: '%1'")
1099 .arg(
m_lines[i+1].chunks[0].m_text));
1103 bool isSplitPossible =
true;
1104 while (width > maxWidth && isSplitPossible)
1107 isSplitPossible =
m_lines[i].chunks.back().Split(newChunk);
1108 if (isSplitPossible)
1114 m_lines[i+1].chunks.prepend(newChunk);
1115 width =
m_lines[i].CalcSize().width();
1127 bool &isItalic,
bool &isUnderline)
1132 if (text.length() >= 1 && text[0] >= QChar(0x7000))
1134 int op = text[0].unicode() - 0x7000;
1135 isUnderline = ((op & 0x1) != 0);
1151 color = (op & 0xf) >> 1;
1160 static const QRegularExpression kControlCharsRE {
"[\\x{7000}-\\x{7fff}]" };
1161 int nextControl = text.indexOf(kControlCharsRE);
1162 if (nextControl < 0)
1169 result = text.left(nextControl);
1170 text = text.mid(nextControl);
1182 int totalHeight = 0;
1186 QVector<int> heights(
m_lines.size());
1187 QVector<int> spaceBefore(
m_lines.size());
1189 for (
int i = 0; i <
m_lines.size(); i++)
1195 int height =
m_lines[i].CalcSize().height();
1196 heights[i] = height;
1197 spaceBefore[i] = y -
prevY;
1198 totalSpace += (y -
prevY);
1200 totalHeight += height;
1203 int overage = std::min(totalHeight - safeHeight, totalSpace);
1207 if (overage > 0 && totalSpace > 0)
1209 float shrink = (totalSpace - overage) / (
float)totalSpace;
1211 for (
int i = 0; i <
m_lines.size(); i++)
1213 m_lines[i].m_yIndent =
prevY + spaceBefore[i] * shrink;
1219 int shift = std::min(firstY, std::max(0,
prevY - safeHeight));
1221 for (
int i = 0; i <
m_lines.size(); i++)
1222 m_lines[i].m_yIndent -= shift;
1229 static const std::array<const QColor,8> kClr
1231 Qt::white, Qt::green, Qt::blue, Qt::cyan,
1232 Qt::red, Qt::yellow, Qt::magenta, Qt::white,
1250 QFontMetrics fm(*font);
1251 fontwidth = fm.averageCharWidth();
1258 for (; i !=
buffers.cend(); ++i)
1262 bool isItalic =
false;
1263 bool isUnderline =
false;
1264 const bool isBold =
false;
1265 QString text(
cc->m_text);
1267 int orig_x =
cc->m_x;
1275 x = xmid + (orig_x - xscale / 2) * fontwidth;
1280 x = (orig_x + 3) *
m_safeArea.width() / xscale;
1283 int orig_y =
cc->m_y;
1285 if (orig_y < yscale / 2)
1288 y = (orig_y *
m_safeArea.height() * zoom / (yscale * 100));
1294 ((yscale - orig_y - 0.5) *
m_safeArea.height() * zoom /
1299 while (!text.isNull())
1301 QString captionText =
1304 kClr[std::min(std::max(0, color), 7)]);
1307 LOG(VB_VBI, LOG_INFO,
1308 QString(
"Adding cc608 chunk (%1,%2): %3")
1324 shape->SetFillBrush(fill);
1333 const std::vector<CC708String*> &list,
1336 LOG(VB_VBI, LOG_DEBUG,
LOC +
1337 QString(
"Display Win %1, Anchor_id %2, x_anch %3, y_anch %4, "
1346 (aspect > 1.4F) ? 210.0F : 160.0F;
1348 float xmult = (float)
m_safeArea.width() / xrange;
1349 float ymult = (float)
m_safeArea.height() / yrange;
1357 for (
auto *str708 : list)
1360 m_lines.resize(str708->m_y + 1);
1362 m_lines[str708->m_y].chunks += chunk;
1363 LOG(VB_VBI, LOG_INFO, QString(
"Adding cc708 chunk: win %1 row %2: %3")
1371 const QString &
Name,
int FontStretch)
1374 m_fontStretch(FontStretch),
1419 if (!forced_only ||
m_family.isEmpty()) {
1500 AVSubtitleRect *hl_button = dvdButton->rects[0];
1501 uint h = hl_button->h;
1502 uint w = hl_button->w;
1503 QRect rect = QRect(hl_button->x, hl_button->y, w, h);
1504 QImage bg_image(hl_button->data[0], w, h, w, QImage::Format_Indexed8);
1505 auto *bgpalette = (uint32_t *)(hl_button->data[1]);
1507 QVector<uint32_t> bg_palette(4);
1508 for (
int i = 0; i < 4; i++)
1509 bg_palette[i] = bgpalette[i];
1510 bg_image.setColorTable(bg_palette);
1513 const QRect fg_rect(buttonPos.translated(-hl_button->x, -hl_button->y));
1514 QImage fg_image = bg_image.copy(fg_rect);
1515 QVector<uint32_t> fg_palette(4);
1516 auto *fgpalette = (uint32_t *)(dvdButton->rects[1]->data[1]);
1519 for (
int i = 0; i < 4; i++)
1520 fg_palette[i] = fgpalette[i];
1521 fg_image.setColorTable(fg_palette);
1524 bg_image = bg_image.convertToFormat(QImage::Format_ARGB32);
1525 fg_image = fg_image.convertToFormat(QImage::Format_ARGB32);
1528 for (
int x=fg_rect.x(); x < fg_rect.x()+fg_rect.width(); ++x)
1530 if ((x < 0) || (x > hl_button->w))
1532 for (
int y=fg_rect.y(); y < fg_rect.y()+fg_rect.height(); ++y)
1534 if ((y < 0) || (y > hl_button->h))
1536 bg_image.setPixel(x, y, fg_image.pixel(x-fg_rect.x(),y-fg_rect.y()));
1570 QList<MythUIType *>::iterator it;
1571 for (it = list.begin(); it != list.end(); ++it)
1574 auto *wrapper =
dynamic_cast<SubWrapper *
>(child);
1578 if (whichImageCache != -1 && (mask & (1UL << whichImageCache)))
1618 int outlineSize = 0;
1619 int shadowWidth = 0;
1620 int shadowHeight = 0;
1623 mythfont->
GetOutline(color, outlineSize, alpha);
1624 MythPoint outline(outlineSize, outlineSize);
1626 outlineSize = outline.x();
1631 mythfont->
GetShadow(shadowOffset, color, alpha);
1633 shadowWidth = abs(shadowOffset.x());
1634 shadowHeight = abs(shadowOffset.y());
1636 shadowWidth = std::max(shadowWidth, outlineSize);
1637 shadowHeight = std::max(shadowHeight, outlineSize);
1639 return {shadowWidth + outlineSize, shadowHeight + outlineSize};
1644 float layoutSpacing)
const
1647 QFont *font = mythfont->
GetFace();
1648 QFontMetrics fm(*font);
1649 int width = fm.horizontalAdvance(text);
1651 if (layoutSpacing > 0 && !text.trimmed().isEmpty())
1652 height = std::max(height, (
int)(font->pixelSize() * layoutSpacing));
1654 return {width, height};
1661 bool isFirst,
bool isLast,
1662 int &left,
int &right)
const
1665 QFont *font = mythfont->
GetFace();
1666 QFontMetrics fm(*font);
1667 int basicPadding = fm.maxWidth() *
PAD_WIDTH;
1668 left = isFirst ? basicPadding : 0;
1670 (isLast ? basicPadding : 0);
1687 return mythfont->
face().family();
1705 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Failed to get subtitle reader.");
1707 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Failed to get CEA-608 reader.");
1709 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Failed to get CEA-708 reader.");
1716 QList<MythUIType *>::iterator it;
1717 QList<MythUIType *>::iterator itNext;
1721 std::chrono::milliseconds now =
1722 currentFrame ? currentFrame->
m_timecode : std::chrono::milliseconds::max();
1729 auto *wrapper =
dynamic_cast<SubWrapper *
>(child);
1734 std::chrono::milliseconds expireTime = wrapper->
GetExpireTime();
1735 if (expireTime > 0ms && expireTime < now)
1743 if (expireTime > 0ms && needRescale)
1745 auto *image =
dynamic_cast<SubImage *
>(child);
1749 QSize size = image->GetImage()->size();
1751 image->GetImage()->Resize(size);
1801 auto *wrapper =
dynamic_cast<SubWrapper *
>(img);
1803 visible = visible.united(wrapper->GetOrigArea());
1806 if (visible.isEmpty())
1809 QRect bounding = visible.boundingRect();
1810 bounding = bounding.translated(
m_safeArea.topLeft());
1812 int left =
m_safeArea.left() - bounding.left();
1820 auto *wrapper =
dynamic_cast<SubWrapper *
>(img);
1832 QMutexLocker lock(&(subs->
m_lock));
1839 if (!currentFrame || !videoOut)
1848 AVSubtitle subtitle = subs->
m_buffers.front();
1849 if (subtitle.start_display_time > currentFrame->
m_timecode.count())
1852 auto displayfor = std::chrono::milliseconds(subtitle.end_display_time -
1853 subtitle.start_display_time);
1854 if (displayfor == 0ms)
1856 displayfor = (displayfor < 50ms) ? 50ms : displayfor;
1857 std::chrono::milliseconds late = currentFrame->
m_timecode -
1858 std::chrono::milliseconds(subtitle.start_display_time);
1862 for (std::size_t i = 0; i < subtitle.num_rects; ++i)
1864 AVSubtitleRect* rect = subtitle.rects[i];
1866 bool displaysub =
true;
1868 subs->
m_buffers.front().end_display_time <
1874 if (displaysub && rect->type == SUBTITLE_BITMAP)
1876 QRect display(rect->display_x, rect->display_y,
1877 rect->display_w, rect->display_h);
1883 int right = rect->x + rect->w;
1884 int bottom = rect->y + rect->h;
1886 (currentFrame->
m_width < right) ||
1887 !display.width() || !display.height())
1889 int sd_height = 576;
1893 int height = ((currentFrame->
m_height <= sd_height) &&
1894 (bottom <= sd_height)) ? sd_height :
1895 ((currentFrame->
m_height <= 720) && bottom <= 720)
1897 int width = ((currentFrame->
m_width <= 720) &&
1898 (right <= 720)) ? 720 :
1899 ((currentFrame->
m_width <= 1280) &&
1900 (right <= 1280)) ? 1280 : 1920;
1901 display = QRect(0, 0, width, height);
1906 int uh = display.height() / 2 - rect->y;
1907 std::chrono::milliseconds displayuntil = currentFrame->
m_timecode + displayfor;
1910 bbox = QRect(0, 0, rect->w, uh);
1912 rect->flags & AV_SUBTITLE_FLAG_FORCED,
1913 QString(
"avsub%1t").arg(i),
1914 displayuntil, late);
1918 int lh = rect->h - uh;
1921 bbox = QRect(0, uh, rect->w, lh);
1923 rect->flags & AV_SUBTITLE_FLAG_FORCED,
1924 QString(
"avsub%1b").arg(i),
1925 displayuntil, late);
1929 else if (displaysub && rect->type == SUBTITLE_ASS)
1932 AddAssEvent(rect->ass, subtitle.start_display_time, subtitle.end_display_time);
1944 QRect &bbox,
bool top,
1945 QRect &display,
int forced,
1946 const QString& imagename,
1947 std::chrono::milliseconds displayuntil,
1948 std::chrono::milliseconds late)
1953 bool prev_empty =
false;
1956 int xmin = bbox.right();
1957 int xmax = bbox.left();
1958 int ymin = bbox.bottom();
1959 int ymax = bbox.top();
1960 int ylast = bbox.top();
1961 int ysplit = bbox.bottom();
1964 for (
int y = bbox.top(); y <= bbox.bottom(); ++y)
1975 for (
int x = bbox.left(); x <= bbox.right(); ++x)
1977 const uint8_t color =
1978 rect->data[0][y * rect->linesize[0] + x];
1979 const uint32_t
pixel = *((uint32_t *)rect->data[1] + color);
1980 if (
pixel & 0xff000000)
1997 else if (!prev_empty)
2014 if (ymax == bbox.bottom())
2023 bbox.setRight(xmax);
2025 bbox.setBottom(ymax);
2031 QRect orig_rect(bbox.left(), bbox.top(), bbox.width(), bbox.height());
2033 QImage qImage(bbox.width(), bbox.height(), QImage::Format_ARGB32);
2034 for (
int y = 0; y < bbox.height(); ++y)
2036 int ysrc = y + bbox.top();
2037 for (
int x = 0; x < bbox.width(); ++x)
2039 int xsrc = x + bbox.left();
2040 const uint8_t color =
2041 rect->data[0][ysrc * rect->linesize[0] + xsrc];
2042 const uint32_t
pixel = *((uint32_t *)rect->data[1] + color);
2043 qImage.setPixel(x, y,
pixel);
2048 bbox.translate(rect->x, rect->y);
2057 if (scaled.size() != orig_rect.size())
2058 qImage = qImage.scaled(scaled.width(), scaled.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
2094 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Display %1AV sub until %2ms")
2095 .arg(forced ?
"FORCED " :
"").arg(displayuntil.count()));
2097 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"AV Sub was %1ms late").arg(late.count()));
2100 return (ysplit + 1);
2108 std::chrono::milliseconds duration = 0ms;
2130 std::chrono::milliseconds start,
2131 std::chrono::milliseconds duration)
2134 duration,
this, subs);
2159 QMutexLocker locker(&textlist->
m_lock);
2181 for (
auto & window : cc708service->
m_windows)
2182 window.SetChanged();
2185 uint64_t clearMask = 0;
2186 QList<FormattedTextSubtitle *> addList;
2195 clearMask |= (1UL << i);
2200 QMutexLocker locker(&win.
m_lock);
2201 std::vector<CC708String*> list = win.
GetStrings();
2206 addList.append(fsub);
2214 if (!addList.empty())
2225 if (scaled.size() != pos.size())
2227 img = img.scaled(scaled.width(), scaled.height(),
2228 Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
2249 uint64_t verbose_mask = VB_GENERAL;
2250 LogLevel_t verbose_level = LOG_INFO;
2255 verbose_level = LOG_EMERG;
2258 verbose_level = LOG_ERR;
2261 verbose_level = LOG_WARNING;
2264 verbose_level = LOG_INFO;
2268 verbose_level = LOG_DEBUG;
2277 static QMutex s_stringLock;
2278 s_stringLock.lock();
2280 QString str = QString::vasprintf(fmt, vl);
2281 LOG(verbose_mask, verbose_level, QString(
"libass: %1").arg(str));
2282 s_stringLock.unlock();
2297 ass_set_extract_fonts(
m_assLibrary,
static_cast<int>(
true));
2298 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Initialised libass object.");
2313 const char *psz_font =
"/system/fonts/DroidSans.ttf";
2314 const char *psz_font_family =
"Droid Sans";
2316 const char *psz_font =
nullptr;
2317 const char *psz_font_family =
"sans-serif";
2319 ass_set_fonts(
m_assRenderer, psz_font, psz_font_family, 1,
nullptr, 1);
2321 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Initialised libass renderer.");
2340 for (
uint i = 0; i < count; ++i)
2346 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Retrieved font '%1'")
2383 if (header.isNull())
2387 header =
parser->GetSubHeader();
2389 if (!header.isNull())
2390 ass_process_codec_private(
m_assTrack, header.data(), header.size());
2406 ass_process_chunk(
m_assTrack, event, strlen(event), starttime, endtime-starttime);
2441 if (images->w == 0 || images->h == 0)
2443 images = images->next;
2447 uint8_t alpha = images->color & 0xFF;
2448 uint8_t blue = images->color >> 8 & 0xFF;
2449 uint8_t green = images->color >> 16 & 0xFF;
2450 uint8_t red = images->color >> 24 & 0xFF;
2454 images = images->next;
2458 QSize img_size(images->w, images->h);
2459 QRect img_rect(images->dst_x,images->dst_y,
2460 images->w, images->h);
2461 QImage qImage(img_size, QImage::Format_ARGB32);
2462 qImage.fill(0x00000000);
2464 unsigned char *src = images->bitmap;
2465 for (
int y = 0; y < images->h; ++y)
2467 for (
int x = 0; x < images->w; ++x)
2469 uint8_t value = src[x];
2472 uint32_t
pixel = (value * (255 - alpha) / 255 << 24) |
2473 (red << 16) | (green << 8) | blue;
2474 qImage.setPixel(x, y,
pixel);
2477 src += images->stride;
2486 QString name = QString(
"asssub%1").arg(count);
2497 images = images->next;
2501 #endif // USING_LIBASS