00001 #include <stdint.h>
00002
00003
00004 #include <QRect>
00005 #include <QPainter>
00006
00007
00008 #include "mythlogging.h"
00009
00010
00011 #include "mythfontproperties.h"
00012 #include "mythimage.h"
00013
00014
00015 #include "mythpainter.h"
00016
00017 MythPainter::MythPainter()
00018 : m_Parent(0), m_HardwareCacheSize(0), m_SoftwareCacheSize(0),
00019 m_showBorders(false), m_showNames(false)
00020 {
00021 SetMaximumCacheSizes(96, 96);
00022 }
00023
00024 MythPainter::~MythPainter(void)
00025 {
00026 QMutexLocker locker(&m_allocationLock);
00027 if (m_allocatedImages.isEmpty())
00028 return;
00029
00030 LOG(VB_GENERAL, LOG_WARNING,
00031 QString("MythPainter: %1 images not yet de-allocated.")
00032 .arg(m_allocatedImages.size()));
00033 while (!m_allocatedImages.isEmpty())
00034 m_allocatedImages.takeLast()->SetParent(NULL);
00035 }
00036
00037 void MythPainter::SetClipRect(const QRect &)
00038 {
00039 }
00040
00041 void MythPainter::SetClipRegion(const QRegion &)
00042 {
00043 }
00044
00045 void MythPainter::Clear(QPaintDevice *device, const QRegion ®ion)
00046 {
00047 }
00048
00049 void MythPainter::DrawImage(int x, int y, MythImage *im, int alpha)
00050 {
00051 if (!im)
00052 {
00053 LOG(VB_GENERAL, LOG_ERR,
00054 "Null image pointer passed to MythPainter::DrawImage()");
00055 return;
00056 }
00057 QRect dest = QRect(x, y, im->width(), im->height());
00058 QRect src = im->rect();
00059 DrawImage(dest, im, src, alpha);
00060 }
00061
00062 void MythPainter::DrawImage(const QPoint &topLeft, MythImage *im, int alpha)
00063 {
00064 DrawImage(topLeft.x(), topLeft.y(), im, alpha);
00065 }
00066
00067 void MythPainter::DrawText(const QRect &r, const QString &msg,
00068 int flags, const MythFontProperties &font,
00069 int alpha, const QRect &boundRect)
00070 {
00071 MythImage *im = GetImageFromString(msg, flags, r, font);
00072 if (!im)
00073 return;
00074
00075 QRect destRect(boundRect);
00076 QRect srcRect(0,0,r.width(),r.height());
00077 if (!boundRect.isEmpty() && boundRect != r)
00078 {
00079 int x = 0;
00080 int y = 0;
00081 int width = boundRect.width();
00082 int height = boundRect.height();
00083
00084 if (boundRect.x() > r.x())
00085 {
00086 x = boundRect.x()-r.x();
00087 }
00088 else if (r.x() > boundRect.x())
00089 {
00090 destRect.setX(r.x());
00091 width = (boundRect.x() + boundRect.width()) - r.x();
00092 }
00093
00094 if (boundRect.y() > r.y())
00095 {
00096 y = boundRect.y()-r.y();
00097 }
00098 else if (r.y() > boundRect.y())
00099 {
00100 destRect.setY(r.y());
00101 height = (boundRect.y() + boundRect.height()) - r.y();
00102 }
00103
00104 if (width <= 0 || height <= 0)
00105 return;
00106
00107 srcRect.setRect(x,y,width,height);
00108 }
00109
00110 DrawImage(destRect, im, srcRect, alpha);
00111 }
00112
00113 void MythPainter::DrawTextLayout(const QRect & canvasRect,
00114 const LayoutVector & layouts,
00115 const FormatVector & formats,
00116 const MythFontProperties & font, int alpha,
00117 const QRect & destRect)
00118 {
00119 if (canvasRect.isNull())
00120 return;
00121
00122 QRect canvas(canvasRect);
00123 QRect dest(destRect);
00124
00125 MythImage *im = GetImageFromTextLayout(layouts, formats, font,
00126 canvas, dest);
00127 if (!im)
00128 {
00129 LOG(VB_GENERAL, LOG_ERR, QString("MythPainter::DrawTextLayout: "
00130 "Unable to create image."));
00131 return;
00132 }
00133 if (im->isNull())
00134 {
00135 LOG(VB_GENERAL, LOG_DEBUG, QString("MythPainter::DrawTextLayout: "
00136 "Rendered image is null."));
00137 return;
00138 }
00139
00140 QRect srcRect(0, 0, dest.width(), dest.height());
00141 DrawImage(dest, im, srcRect, alpha);
00142 }
00143
00144 void MythPainter::DrawRect(const QRect &area, const QBrush &fillBrush,
00145 const QPen &linePen, int alpha)
00146 {
00147 MythImage *im = GetImageFromRect(area, 0, 0, fillBrush, linePen);
00148 if (im)
00149 DrawImage(area.x(), area.y(), im, alpha);
00150 }
00151
00152 void MythPainter::DrawRoundRect(const QRect &area, int cornerRadius,
00153 const QBrush &fillBrush, const QPen &linePen,
00154 int alpha)
00155 {
00156 MythImage *im = GetImageFromRect(area, cornerRadius, 0, fillBrush, linePen);
00157 if (im)
00158 DrawImage(area.x(), area.y(), im, alpha);
00159 }
00160
00161 void MythPainter::DrawEllipse(const QRect &area, const QBrush &fillBrush,
00162 const QPen &linePen, int alpha)
00163 {
00164 MythImage *im = GetImageFromRect(area, 0, 1, fillBrush, linePen);
00165 if (im)
00166 DrawImage(area.x(), area.y(), im, alpha);
00167 }
00168
00169 void MythPainter::PushTransformation(const UIEffects &zoom, QPointF center)
00170 {
00171 (void)zoom;
00172 (void)center;
00173 }
00174
00175 void MythPainter::DrawTextPriv(MythImage *im, const QString &msg, int flags,
00176 const QRect &r, const MythFontProperties &font)
00177 {
00178 if (!im)
00179 return;
00180
00181 QPoint drawOffset;
00182 font.GetOffset(drawOffset);
00183
00184 QImage pm(r.size(), QImage::Format_ARGB32);
00185 QColor fillcolor = font.color();
00186 if (font.hasOutline())
00187 {
00188 QColor outlineColor;
00189 int outlineSize, outlineAlpha;
00190
00191 font.GetOutline(outlineColor, outlineSize, outlineAlpha);
00192
00193 fillcolor = outlineColor;
00194 }
00195 fillcolor.setAlpha(0);
00196 pm.fill(fillcolor.rgba());
00197
00198 QPainter tmp(&pm);
00199 QFont tmpfont = font.face();
00200 tmpfont.setStyleStrategy(QFont::OpenGLCompatible);
00201 tmp.setFont(tmpfont);
00202
00203 if (font.hasShadow())
00204 {
00205 QPoint shadowOffset;
00206 QColor shadowColor;
00207 int shadowAlpha;
00208
00209 font.GetShadow(shadowOffset, shadowColor, shadowAlpha);
00210
00211 QRect a = QRect(0, 0, r.width(), r.height());
00212 a.translate(shadowOffset.x() + drawOffset.x(),
00213 shadowOffset.y() + drawOffset.y());
00214
00215 shadowColor.setAlpha(shadowAlpha);
00216 tmp.setPen(shadowColor);
00217 tmp.drawText(a, flags, msg);
00218 }
00219
00220 if (font.hasOutline())
00221 {
00222 QColor outlineColor;
00223 int outlineSize, outlineAlpha;
00224
00225 font.GetOutline(outlineColor, outlineSize, outlineAlpha);
00226
00227
00228 int outalpha = 16;
00229
00230 QRect a = QRect(0, 0, r.width(), r.height());
00231 a.translate(-outlineSize + drawOffset.x(),
00232 -outlineSize + drawOffset.y());
00233
00234 outlineColor.setAlpha(outalpha);
00235 tmp.setPen(outlineColor);
00236 tmp.drawText(a, flags, msg);
00237
00238 for (int i = (0 - outlineSize + 1); i <= outlineSize; i++)
00239 {
00240 a.translate(1, 0);
00241 tmp.drawText(a, flags, msg);
00242 }
00243
00244 for (int i = (0 - outlineSize + 1); i <= outlineSize; i++)
00245 {
00246 a.translate(0, 1);
00247 tmp.drawText(a, flags, msg);
00248 }
00249
00250 for (int i = (0 - outlineSize + 1); i <= outlineSize; i++)
00251 {
00252 a.translate(-1, 0);
00253 tmp.drawText(a, flags, msg);
00254 }
00255
00256 for (int i = (0 - outlineSize + 1); i <= outlineSize; i++)
00257 {
00258 a.translate(0, -1);
00259 tmp.drawText(a, flags, msg);
00260 }
00261 }
00262
00263 tmp.setPen(QPen(font.GetBrush(), 0));
00264 tmp.drawText(drawOffset.x(), drawOffset.y(), r.width(), r.height(),
00265 flags, msg);
00266 tmp.end();
00267 im->Assign(pm);
00268 }
00269
00270 void MythPainter::DrawRectPriv(MythImage *im, const QRect &area, int radius,
00271 int ellipse,
00272 const QBrush &fillBrush, const QPen &linePen)
00273 {
00274 if (!im)
00275 return;
00276
00277 QImage image(QSize(area.width(), area.height()), QImage::Format_ARGB32);
00278 image.fill(0x00000000);
00279 QPainter painter(&image);
00280 painter.setRenderHint(QPainter::Antialiasing);
00281 painter.setPen(linePen);
00282 painter.setBrush(fillBrush);
00283
00284 if ((area.width() / 2) < radius)
00285 radius = area.width() / 2;
00286
00287 if ((area.height() / 2) < radius)
00288 radius = area.height() / 2;
00289
00290 int lineWidth = linePen.width();
00291 QRect r(lineWidth, lineWidth,
00292 area.width() - (lineWidth * 2), area.height() - (lineWidth * 2));
00293
00294 if (ellipse)
00295 painter.drawEllipse(r);
00296 else if (radius == 0)
00297 painter.drawRect(r);
00298 else
00299 painter.drawRoundedRect(r, (qreal)radius, qreal(radius));
00300
00301 painter.end();
00302 im->Assign(image);
00303 }
00304
00305 MythImage *MythPainter::GetImageFromString(const QString &msg,
00306 int flags, const QRect &r,
00307 const MythFontProperties &font)
00308 {
00309 QString incoming = font.GetHash() + QString::number(r.width()) +
00310 QString::number(r.height()) +
00311 QString::number(flags) +
00312 QString::number(font.color().rgba()) + msg;
00313
00314 if (m_StringToImageMap.contains(incoming))
00315 {
00316 m_StringExpireList.remove(incoming);
00317 m_StringExpireList.push_back(incoming);
00318 return m_StringToImageMap[incoming];
00319 }
00320
00321 MythImage *im = GetFormatImage();
00322 if (im)
00323 {
00324 DrawTextPriv(im, msg, flags, r, font);
00325 m_SoftwareCacheSize += im->bytesPerLine() * im->height();
00326 m_StringToImageMap[incoming] = im;
00327 m_StringExpireList.push_back(incoming);
00328 ExpireImages(m_MaxSoftwareCacheSize);
00329 }
00330 return im;
00331 }
00332
00333 MythImage *MythPainter::GetImageFromTextLayout(const LayoutVector &layouts,
00334 const FormatVector &formats,
00335 const MythFontProperties &font,
00336 QRect &canvas, QRect &dest)
00337 {
00338 LayoutVector::const_iterator Ipara;
00339
00340 QString incoming = QString::number(canvas.x()) +
00341 QString::number(canvas.y()) +
00342 QString::number(canvas.width()) +
00343 QString::number(canvas.height()) +
00344 QString::number(dest.width()) +
00345 QString::number(dest.height()) +
00346 font.GetHash();
00347
00348 for (Ipara = layouts.begin(); Ipara != layouts.end(); ++Ipara)
00349 incoming += (*Ipara)->text();
00350
00351 if (m_StringToImageMap.contains(incoming))
00352 {
00353 m_StringExpireList.remove(incoming);
00354 m_StringExpireList.push_back(incoming);
00355 return m_StringToImageMap[incoming];
00356 }
00357
00358 MythImage *im = GetFormatImage();
00359 if (im)
00360 {
00361 QImage pm(canvas.size(), QImage::Format_ARGB32_Premultiplied);
00362 pm.fill(0);
00363
00364 QPainter painter(&pm);
00365 if (!painter.isActive())
00366 {
00367 LOG(VB_GENERAL, LOG_ERR, "MythPainter::GetImageFromTextLayout: "
00368 "Invalid canvas.");
00369 return im;
00370 }
00371
00372 QRect clip;
00373 clip.setSize(canvas.size());
00374
00375 QFont tmpfont = font.face();
00376 tmpfont.setStyleStrategy(QFont::OpenGLCompatible);
00377 painter.setFont(tmpfont);
00378 painter.setRenderHint(QPainter::Antialiasing);
00379
00380 if (font.hasShadow())
00381 {
00382 QRect shadowRect;
00383 QPoint shadowOffset;
00384 QColor shadowColor;
00385 int shadowAlpha;
00386
00387 font.GetShadow(shadowOffset, shadowColor, shadowAlpha);
00388 shadowColor.setAlpha(shadowAlpha);
00389
00390 MythPoint shadow(shadowOffset);
00391 shadow.NormPoint();
00392
00393 shadowRect = canvas;
00394 shadowRect.translate(shadow.x(), shadow.y());
00395
00396 painter.setPen(shadowColor);
00397 for (Ipara = layouts.begin(); Ipara != layouts.end(); ++Ipara)
00398 (*Ipara)->draw(&painter, shadowRect.topLeft(), formats, clip);
00399 }
00400
00401 painter.setPen(QPen(font.GetBrush(), 0));
00402 for (Ipara = layouts.begin(); Ipara != layouts.end(); ++Ipara)
00403 (*Ipara)->draw(&painter, canvas.topLeft(), formats, clip);
00404
00405 painter.end();
00406
00407 pm.setOffset(canvas.topLeft());
00408 im->Assign(pm.copy(0, 0, dest.width(), dest.height()));
00409
00410 m_SoftwareCacheSize += im->bytesPerLine() * im->height();
00411 m_StringToImageMap[incoming] = im;
00412 m_StringExpireList.push_back(incoming);
00413 ExpireImages(m_MaxSoftwareCacheSize);
00414 }
00415 return im;
00416 }
00417
00418 MythImage* MythPainter::GetImageFromRect(const QRect &area, int radius,
00419 int ellipse,
00420 const QBrush &fillBrush,
00421 const QPen &linePen)
00422 {
00423 if (area.width() <= 0 || area.height() <= 0)
00424 return NULL;
00425
00426 uint64_t hash1 = ((0xfff & (uint64_t)area.width())) +
00427 ((0xfff & (uint64_t)area.height()) << 12) +
00428 ((0xff & (uint64_t)fillBrush.style()) << 24) +
00429 ((0xff & (uint64_t)linePen.width()) << 32) +
00430 ((0xff & (uint64_t)radius) << 40) +
00431 ((0xff & (uint64_t)linePen.style()) << 48) +
00432 ((0xff & (uint64_t)ellipse) << 56);
00433 uint64_t hash2 = ((0xffffffff & (uint64_t)linePen.color().rgba())) +
00434 ((0xffffffff & (uint64_t)fillBrush.color().rgba()) << 32);
00435
00436 QString incoming("R");
00437 if (fillBrush.style() == Qt::LinearGradientPattern && fillBrush.gradient())
00438 {
00439 const QLinearGradient *gradient = static_cast<const QLinearGradient*>(fillBrush.gradient());
00440 if (gradient)
00441 {
00442 incoming = QString::number(
00443 ((0xfff & (uint64_t)gradient->start().x())) +
00444 ((0xfff & (uint64_t)gradient->start().y()) << 12) +
00445 ((0xfff & (uint64_t)gradient->finalStop().x()) << 24) +
00446 ((0xfff & (uint64_t)gradient->finalStop().y()) << 36));
00447 QGradientStops stops = gradient->stops();
00448 for (int i = 0; i < stops.size(); i++)
00449 {
00450 incoming += QString::number(
00451 ((0xfff * (uint64_t)(stops[i].first * 100))) +
00452 ((uint64_t)stops[i].second.rgba() << 12));
00453 }
00454 }
00455 }
00456
00457 incoming += QString::number(hash1) + QString::number(hash2);
00458
00459 if (m_StringToImageMap.contains(incoming))
00460 {
00461 m_StringExpireList.remove(incoming);
00462 m_StringExpireList.push_back(incoming);
00463 return m_StringToImageMap[incoming];
00464 }
00465
00466 MythImage *im = GetFormatImage();
00467 if (im)
00468 {
00469 DrawRectPriv(im, area, radius, ellipse, fillBrush, linePen);
00470 m_SoftwareCacheSize += (im->bytesPerLine() * im->height());
00471 m_StringToImageMap[incoming] = im;
00472 m_StringExpireList.push_back(incoming);
00473 ExpireImages(m_MaxSoftwareCacheSize);
00474 }
00475 return im;
00476 }
00477
00478 MythImage *MythPainter::GetFormatImage()
00479 {
00480 m_allocationLock.lock();
00481 MythImage *result = GetFormatImagePriv();
00482 m_allocatedImages.append(result);
00483 m_allocationLock.unlock();
00484 return result;
00485 }
00486
00487 void MythPainter::DeleteFormatImage(MythImage *im)
00488 {
00489 m_allocationLock.lock();
00490 DeleteFormatImagePriv(im);
00491
00492 while (m_allocatedImages.contains(im))
00493 m_allocatedImages.removeOne(im);
00494 m_allocationLock.unlock();
00495 }
00496
00497 void MythPainter::CheckFormatImage(MythImage *im)
00498 {
00499 if (im && !im->GetParent())
00500 {
00501 m_allocationLock.lock();
00502 m_allocatedImages.append(im);
00503 im->SetParent(this);
00504 m_allocationLock.unlock();
00505 }
00506 }
00507
00508 void MythPainter::ExpireImages(int max)
00509 {
00510 if (m_StringExpireList.size() < 1)
00511 return;
00512
00513 while (m_SoftwareCacheSize > max)
00514 {
00515 QString oldmsg = m_StringExpireList.front();
00516 m_StringExpireList.pop_front();
00517
00518 MythImage *oldim = NULL;
00519 if (m_StringToImageMap.contains(oldmsg))
00520 oldim = m_StringToImageMap[oldmsg];
00521
00522 m_StringToImageMap.remove(oldmsg);
00523
00524 if (oldim)
00525 {
00526 m_SoftwareCacheSize -= oldim->bytesPerLine() * oldim->height();
00527 oldim->DownRef();
00528 }
00529 }
00530 }
00531
00532
00533 void MythPainter::SetMaximumCacheSizes(int hardware, int software)
00534 {
00535 m_MaxHardwareCacheSize = 1024 * 1024 * hardware;
00536 m_MaxSoftwareCacheSize = 1024 * 1024 * software;
00537 LOG(VB_GUI, LOG_INFO,
00538 QString("MythPainter cache sizes: Hardware %1Mb, Software %2Mb")
00539 .arg(hardware).arg(software));
00540 }