|
MythTV
0.26-pre
|
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 ®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 /* 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 }
1.7.6.1