MythTV  0.26-pre
mythpainter.cpp
Go to the documentation of this file.
00001 #include <stdint.h>
00002 
00003 // QT headers
00004 #include <QRect>
00005 #include <QPainter>
00006 
00007 // libmythbase headers
00008 #include "mythlogging.h"
00009 
00010 // libmythui headers
00011 #include "mythfontproperties.h"
00012 #include "mythimage.h"
00013 
00014 // Own header
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 &region)
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         /* FIXME: use outlineAlpha */
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(); // scale it to screen resolution
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 // the following assume graphics hardware operates natively at 32bpp
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends