File indexing completed on 2024-12-22 04:40:20
0001 /* 0002 SPDX-FileCopyrightText: 2010-2022 Mladen Milinkovic <max@smoothware.net> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "subtitletextoverlay.h" 0008 0009 #include "core/subtitleline.h" 0010 0011 #include <QAbstractTextDocumentLayout> 0012 #include <QPainter> 0013 #include <QTextCharFormat> 0014 #include <QTextLayout> 0015 0016 #include "scconfig.h" 0017 0018 using namespace SubtitleComposer; 0019 0020 SubtitleTextOverlay::SubtitleTextOverlay() 0021 : m_invertPixels(false) 0022 { 0023 m_font.setStyleStrategy(QFont::PreferAntialias); 0024 m_font.setPixelSize(SCConfig::fontSize()); 0025 } 0026 0027 QTextLayout ** 0028 SubtitleTextOverlay::drawDocPrepare(QPainter *painter) 0029 { 0030 QTextLayout **layoutData = new QTextLayout*[2 * m_doc->blockCount() + 1]; 0031 QTextLayout **res = layoutData; 0032 0033 const QFontMetrics &fontMetrics = painter->fontMetrics(); 0034 0035 QTextOption layoutTextOption; 0036 const int imgWidth = m_renderScale > 1.f ? float(m_image.width()) / m_renderScale : m_image.width(); 0037 int lineWidth; 0038 if(m_pos) { 0039 layoutTextOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); 0040 if(m_pos->hAlign == SubtitleRect::START) 0041 layoutTextOption.setAlignment(Qt::AlignLeft); 0042 else if(m_pos->hAlign == SubtitleRect::END) 0043 layoutTextOption.setAlignment(Qt::AlignRight); 0044 else 0045 layoutTextOption.setAlignment(Qt::AlignHCenter); 0046 lineWidth = (m_pos->right - m_pos->left) * imgWidth / 100; 0047 } else { 0048 layoutTextOption.setWrapMode(QTextOption::NoWrap); 0049 layoutTextOption.setAlignment(Qt::AlignHCenter); 0050 lineWidth = imgWidth; 0051 } 0052 0053 qreal height = 0., heightOutline = 0.; 0054 qreal maxLineWidth = 0; 0055 0056 RichDocumentLayout *docLayout = m_doc->documentLayout(); 0057 for(QTextBlock bi = m_doc->begin(); bi != m_doc->end(); bi = bi.next()) { 0058 const QString &text = bi.text(); 0059 QVector<QTextLayout::FormatRange> fmtRanges = docLayout->applyCSS(bi.textFormats()); 0060 0061 QTextLayout *tlNormal = new QTextLayout(text, m_font, painter->device()); 0062 *layoutData++ = tlNormal; 0063 tlNormal->setCacheEnabled(true); 0064 tlNormal->setTextOption(layoutTextOption); 0065 tlNormal->setFormats(fmtRanges); 0066 0067 tlNormal->beginLayout(); 0068 for(;;) { 0069 QTextLine line = tlNormal->createLine(); 0070 if(!line.isValid()) 0071 break; 0072 line.setLineWidth(lineWidth); 0073 height += fontMetrics.leading(); 0074 line.setPosition(QPointF(0., height)); 0075 height += line.height(); 0076 maxLineWidth = qMax(maxLineWidth, line.naturalTextWidth()); 0077 } 0078 tlNormal->endLayout(); 0079 0080 if(m_textOutline.width()) { 0081 QTextLayout *tlOutline = new QTextLayout(text, m_font, painter->device()); 0082 *layoutData++ = tlOutline; 0083 tlOutline->setCacheEnabled(true); 0084 tlOutline->setTextOption(layoutTextOption); 0085 for(QTextLayout::FormatRange &r: fmtRanges) 0086 r.format.setTextOutline(m_textOutline); 0087 tlOutline->setFormats(fmtRanges); 0088 0089 tlOutline->beginLayout(); 0090 for(;;) { 0091 QTextLine line = tlOutline->createLine(); 0092 if(!line.isValid()) 0093 break; 0094 line.setLineWidth(lineWidth); 0095 heightOutline += fontMetrics.leading(); 0096 line.setPosition(QPointF(0., heightOutline)); 0097 heightOutline += line.height(); 0098 maxLineWidth = qMax(maxLineWidth, line.naturalTextWidth()); 0099 } 0100 tlOutline->endLayout(); 0101 } else { 0102 *layoutData++ = nullptr; 0103 } 0104 } 0105 0106 *layoutData = nullptr; 0107 0108 m_textSize = QSize(maxLineWidth, qMax(height, heightOutline)); 0109 0110 return res; 0111 } 0112 0113 void 0114 SubtitleTextOverlay::drawDoc() 0115 { 0116 QPainter painter(&m_image); 0117 painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform, true); 0118 painter.setFont(m_font); 0119 painter.setPen(m_textColor); 0120 0121 QTextLayout **d = drawDocPrepare(&painter); 0122 0123 const float imgWidth = m_renderScale > 1.f ? float(m_image.width()) / m_renderScale : m_image.width(); 0124 const float imgHeight = (m_renderScale > 1.f ? float(m_image.height()) / m_renderScale : m_image.height()) - m_bottomPadding; 0125 QPointF drawPos; 0126 if(m_pos) { 0127 drawPos.setX(m_pos->left * imgWidth / 100.); 0128 if(m_pos->vAlign == SubtitleRect::TOP) 0129 drawPos.setY(m_pos->top * imgHeight / 100.); 0130 else 0131 drawPos.setY(m_pos->bottom * imgHeight / 100. - m_textSize.height()); 0132 } else { 0133 drawPos.setY(imgHeight - m_textSize.height()); 0134 } 0135 0136 for(int i = 0; d[i]; i += 2) { 0137 if(d[i + 1]) { 0138 d[i + 1]->draw(&painter, drawPos); 0139 delete d[i + 1]; 0140 } 0141 d[i]->draw(&painter, drawPos); 0142 delete d[i]; 0143 } 0144 delete[] d; 0145 0146 painter.end(); 0147 } 0148 0149 void 0150 SubtitleTextOverlay::drawImage() 0151 { 0152 m_image.fill(Qt::transparent); 0153 if(m_doc) 0154 drawDoc(); 0155 m_dirty = false; 0156 } 0157 0158 const QImage & 0159 SubtitleTextOverlay::image() 0160 { 0161 if(m_dirty) 0162 drawImage(); 0163 0164 return m_image; 0165 } 0166 0167 void 0168 SubtitleTextOverlay::invertPixels(bool invert) 0169 { 0170 if(m_invertPixels == invert) 0171 return; 0172 m_invertPixels = invert; 0173 setTextColor(m_textColor); 0174 setOutlineColor(m_textOutline.color()); 0175 } 0176 0177 const QSize & 0178 SubtitleTextOverlay::textSize() 0179 { 0180 if(m_dirty) 0181 drawImage(); 0182 0183 return m_textSize; 0184 } 0185 0186 void 0187 SubtitleTextOverlay::setImageSize(int width, int height) 0188 { 0189 if(m_image.width() == width && m_image.height() == height) 0190 return; 0191 0192 m_image = QImage(width, height, QImage::Format_ARGB32); 0193 setDirty(); 0194 } 0195 0196 void 0197 SubtitleTextOverlay::setDirty() 0198 { 0199 m_dirty = true; 0200 emit repaintNeeded(); 0201 } 0202 0203 void 0204 SubtitleTextOverlay::setText(const QString &text) 0205 { 0206 if(!m_text) { 0207 m_text = new RichDocument(this); 0208 } else if(m_text->toHtml() == text) { 0209 return; 0210 } 0211 m_text->setHtml(text, true); 0212 setDoc(m_text); 0213 setDirty(); 0214 } 0215 0216 void 0217 SubtitleTextOverlay::setDoc(const RichDocument *doc) 0218 { 0219 if(m_doc == doc) 0220 return; 0221 if(m_doc) { 0222 disconnect(m_doc, nullptr, this, nullptr); 0223 disconnect(m_doc->stylesheet(), nullptr, this, nullptr); 0224 } 0225 m_doc = doc; 0226 if(m_doc) { 0227 connect(m_doc, &RichDocument::contentsChanged, this, &SubtitleTextOverlay::setDirty); 0228 connect(m_doc->stylesheet(), &RichCSS::changed, this, &SubtitleTextOverlay::setDirty); 0229 } 0230 setDirty(); 0231 } 0232 0233 void 0234 SubtitleTextOverlay::setDocRect(const SubtitleRect *pos) 0235 { 0236 if(m_pos == pos) 0237 return; 0238 m_pos = pos; 0239 setDirty(); 0240 } 0241 0242 void 0243 SubtitleTextOverlay::setRenderScale(double scale) 0244 { 0245 if(m_renderScale == scale) 0246 return; 0247 m_renderScale = scale; 0248 setDirty(); 0249 } 0250 0251 void 0252 SubtitleTextOverlay::setBottomPadding(int padding) 0253 { 0254 if(m_bottomPadding == padding) 0255 return; 0256 m_bottomPadding = padding; 0257 setDirty(); 0258 } 0259 0260 void 0261 SubtitleTextOverlay::setFontFamily(const QString &family) 0262 { 0263 if(m_font.family() == family) 0264 return; 0265 m_font.setFamily(family); 0266 setDirty(); 0267 } 0268 0269 void 0270 SubtitleTextOverlay::setFontSize(int fontSize) 0271 { 0272 if(fontSize == m_font.pixelSize()) 0273 return; 0274 m_font.setPixelSize(fontSize); 0275 setDirty(); 0276 } 0277 0278 void 0279 SubtitleTextOverlay::setTextColor(QColor color) 0280 { 0281 if(m_invertPixels) 0282 color = QColor(color.blue(), color.green(), color.red(), color.alpha()); 0283 if(m_textColor == color) 0284 return; 0285 m_textColor = color; 0286 setDirty(); 0287 } 0288 0289 void 0290 SubtitleTextOverlay::setOutlineColor(QColor color) 0291 { 0292 if(m_invertPixels) 0293 color = QColor(color.blue(), color.green(), color.red(), color.alpha()); 0294 if(m_textOutline.color() == color) 0295 return; 0296 m_textOutline.setColor(color); 0297 setDirty(); 0298 } 0299 0300 void 0301 SubtitleTextOverlay::setOutlineWidth(int width) 0302 { 0303 if(m_textOutline.width() == width) 0304 return; 0305 m_textOutline.setWidth(width); 0306 setDirty(); 0307 } 0308