From d7fdce5b042918c88f5950a867a6da8d77a3c9f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Mon, 1 May 2017 21:48:35 +0200 Subject: [PATCH 1/9] clarify variable usage in PixmapSource --- src/skin/pixmapsource.cpp | 12 ++++++------ src/skin/pixmapsource.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/skin/pixmapsource.cpp b/src/skin/pixmapsource.cpp index 8f159530a946..2328da27832e 100644 --- a/src/skin/pixmapsource.cpp +++ b/src/skin/pixmapsource.cpp @@ -11,7 +11,7 @@ PixmapSource::PixmapSource(const QString& filepath) { } QByteArray PixmapSource::getData() const { - return m_baData; + return m_svgSourceData; } QString PixmapSource::getPath() const { @@ -19,7 +19,7 @@ QString PixmapSource::getPath() const { } void PixmapSource::setPath(const QString& newPath) { - m_baData.truncate(0); + m_svgSourceData.clear(); m_path = newPath; if (m_path.endsWith(".svg", Qt::CaseInsensitive)) { m_eType = SVG; @@ -29,7 +29,7 @@ void PixmapSource::setPath(const QString& newPath) { } bool PixmapSource::isEmpty() const { - return m_path.isEmpty() && m_baData.isEmpty() ; + return m_path.isEmpty() && m_svgSourceData.isEmpty() ; } bool PixmapSource::isSVG() const { @@ -41,16 +41,16 @@ bool PixmapSource::isBitmap() const { } void PixmapSource::setSVG(const QByteArray& content) { - m_baData = content; + m_svgSourceData = content; m_eType = SVG; } QString PixmapSource::getId() const { quint16 checksum; - if (m_baData.isEmpty()) { + if (m_svgSourceData.isEmpty()) { checksum = qChecksum(m_path.toAscii().constData(), m_path.length()); } else { - checksum = qChecksum(m_baData.constData(), m_baData.length()); + checksum = qChecksum(m_svgSourceData.constData(), m_svgSourceData.length()); } return m_path + QString::number(checksum); } diff --git a/src/skin/pixmapsource.h b/src/skin/pixmapsource.h index 6c28df78ab3a..afb5611f6334 100644 --- a/src/skin/pixmapsource.h +++ b/src/skin/pixmapsource.h @@ -27,7 +27,7 @@ class PixmapSource final { }; QString m_path; - QByteArray m_baData; + QByteArray m_svgSourceData; enum Type m_eType; }; From 89e3df208ec947d9a50ae300afee4f1fdd3eb945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Mon, 1 May 2017 22:09:03 +0200 Subject: [PATCH 2/9] inline PixmapSource::setPath --- src/skin/pixmapsource.cpp | 23 +++++++++-------------- src/skin/pixmapsource.h | 1 - src/skin/skincontext.cpp | 15 +++++++-------- src/widget/wpixmapstore.cpp | 2 +- 4 files changed, 17 insertions(+), 24 deletions(-) diff --git a/src/skin/pixmapsource.cpp b/src/skin/pixmapsource.cpp index 2328da27832e..b4898c039d31 100644 --- a/src/skin/pixmapsource.cpp +++ b/src/skin/pixmapsource.cpp @@ -2,12 +2,17 @@ #include "skin/pixmapsource.h" -PixmapSource::PixmapSource(): - m_eType(SVG) { +PixmapSource::PixmapSource() + : m_eType(SVG) { } -PixmapSource::PixmapSource(const QString& filepath) { - setPath(filepath); +PixmapSource::PixmapSource(const QString& filepath) + : m_path(filepath) { + if (m_path.endsWith(".svg", Qt::CaseInsensitive)) { + m_eType = SVG; + } else { + m_eType = BITMAP; + } } QByteArray PixmapSource::getData() const { @@ -18,16 +23,6 @@ QString PixmapSource::getPath() const { return m_path; } -void PixmapSource::setPath(const QString& newPath) { - m_svgSourceData.clear(); - m_path = newPath; - if (m_path.endsWith(".svg", Qt::CaseInsensitive)) { - m_eType = SVG; - } else { - m_eType = BITMAP; - } -} - bool PixmapSource::isEmpty() const { return m_path.isEmpty() && m_svgSourceData.isEmpty() ; } diff --git a/src/skin/pixmapsource.h b/src/skin/pixmapsource.h index afb5611f6334..78b60fb92bf1 100644 --- a/src/skin/pixmapsource.h +++ b/src/skin/pixmapsource.h @@ -15,7 +15,6 @@ class PixmapSource final { bool isSVG() const; bool isBitmap() const; void setSVG(const QByteArray& content); - void setPath(const QString& newPath); QString getPath() const; QByteArray getData() const; QString getId() const; diff --git a/src/skin/skincontext.cpp b/src/skin/skincontext.cpp index 7e91f9474766..a076807d3dc6 100644 --- a/src/skin/skincontext.cpp +++ b/src/skin/skincontext.cpp @@ -167,8 +167,6 @@ QString SkinContext::nodeToString(const QDomNode& node) const { } PixmapSource SkinContext::getPixmapSource(const QDomNode& pixmapNode) const { - PixmapSource source; - const SvgParser svgParser(*this); if (!pixmapNode.isNull()) { @@ -177,14 +175,15 @@ PixmapSource SkinContext::getPixmapSource(const QDomNode& pixmapNode) const { // inline svg const QByteArray rslt = svgParser.saveToQByteArray( svgParser.parseSvgTree(svgNode, m_xmlPath)); + PixmapSource source; source.setSVG(rslt); } else { // filename. - source = getPixmapSourceInner(nodeToString(pixmapNode), svgParser); + return getPixmapSourceInner(nodeToString(pixmapNode), svgParser); } } - return source; + return PixmapSource(); } PixmapSource SkinContext::getPixmapSource(const QString& filename) const { @@ -210,17 +209,17 @@ QDomElement SkinContext::loadSvg(const QString& filename) const { PixmapSource SkinContext::getPixmapSourceInner(const QString& filename, const SvgParser& svgParser) const { - PixmapSource source; if (!filename.isEmpty()) { - source.setPath(getSkinPath(filename)); + PixmapSource source(getSkinPath(filename)); if (source.isSVG()) { QDomElement svgElement = loadSvg(filename); const QByteArray rslt = svgParser.saveToQByteArray( - svgParser.parseSvgTree(svgElement, filename)); + svgParser.parseSvgTree(svgElement, filename)); source.setSVG(rslt); } + return source; } - return source; + return PixmapSource(); } /** diff --git a/src/widget/wpixmapstore.cpp b/src/widget/wpixmapstore.cpp index ca49ea0bafa1..e6c8c097b9ed 100644 --- a/src/widget/wpixmapstore.cpp +++ b/src/widget/wpixmapstore.cpp @@ -304,7 +304,7 @@ QString Paintable::getAltFileName(const QString& fileName) { PaintablePointer WPixmapStore::getPaintable(PixmapSource source, Paintable::DrawMode mode, double scaleFactor) { - QString key = source.getId() + QString::number(scaleFactor); + QString key = source.getId() + QString::number(mode) + QString::number(scaleFactor); // See if we have a cached value for the pixmap. PaintablePointer pPaintable = m_paintableCache.value( From 8838c1a21d35c139b39eef976fdc74a767c61b4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Tue, 2 May 2017 22:28:46 +0200 Subject: [PATCH 3/9] Extracted Paintable class to its own file --- build/depends.py | 1 + src/widget/paintable.cpp | 279 ++++++++++++++++++++++++++++++++++ src/widget/paintable.h | 68 +++++++++ src/widget/wpixmapstore.cpp | 288 ------------------------------------ src/widget/wpixmapstore.h | 50 +------ 5 files changed, 349 insertions(+), 337 deletions(-) create mode 100644 src/widget/paintable.cpp create mode 100644 src/widget/paintable.h diff --git a/build/depends.py b/build/depends.py index 4fc24c183e1b..852fd43ee624 100644 --- a/build/depends.py +++ b/build/depends.py @@ -845,6 +845,7 @@ def sources(self, build): "widget/wskincolor.cpp", "widget/wsearchlineedit.cpp", "widget/wpixmapstore.cpp", + "widget/paintable.cpp", "widget/wimagestore.cpp", "widget/hexspinbox.cpp", "widget/wtrackproperty.cpp", diff --git a/src/widget/paintable.cpp b/src/widget/paintable.cpp new file mode 100644 index 000000000000..fb7bc5d02749 --- /dev/null +++ b/src/widget/paintable.cpp @@ -0,0 +1,279 @@ + +#include "widget/wpixmapstore.h" + +#include +#include +#include + +#include "util/math.h" +#include "skin/imgloader.h" + +// static +Paintable::DrawMode Paintable::DrawModeFromString(const QString& str) { + if (str.compare("FIXED", Qt::CaseInsensitive) == 0) { + return FIXED; + } else if (str.compare("STRETCH", Qt::CaseInsensitive) == 0) { + return STRETCH; + } else if (str.compare("STRETCH_ASPECT", Qt::CaseInsensitive) == 0) { + return STRETCH_ASPECT; + } else if (str.compare("TILE", Qt::CaseInsensitive) == 0) { + return TILE; + } + + // Fall back on the implicit default from before Mixxx supported draw modes. + qWarning() << "Unknown DrawMode string in DrawModeFromString:" + << str << "using FIXED"; + return FIXED; +} + +// static +QString Paintable::DrawModeToString(DrawMode mode) { + switch (mode) { + case FIXED: + return "FIXED"; + case STRETCH: + return "STRETCH"; + case STRETCH_ASPECT: + return "STRETCH_ASPECT"; + case TILE: + return "TILE"; + } + // Fall back on the implicit default from before Mixxx supported draw modes. + qWarning() << "Unknown DrawMode in DrawModeToString " << mode + << "using FIXED"; + return "FIXED"; +} + +Paintable::Paintable(QImage* pImage, DrawMode mode) + : m_drawMode(mode) { +#if QT_VERSION >= 0x040700 + m_pPixmap.reset(new QPixmap()); + m_pPixmap->convertFromImage(*pImage); +#else + m_pPixmap.reset(new QPixmap(QPixmap::fromImage(*pImage))); +#endif + delete pImage; +} + +Paintable::Paintable(const PixmapSource& source, DrawMode mode) + : m_drawMode(mode) { + if (source.isSVG()) { + QScopedPointer pSvgRenderer(new QSvgRenderer()); + if (source.getData().isEmpty()) { + pSvgRenderer->load(source.getPath()); + } else { + pSvgRenderer->load(source.getData()); + } + + if (mode == TILE) { + // The SVG renderer doesn't directly support tiling, so we render + // it to a pixmap which will then get tiled. + QImage copy_buffer(pSvgRenderer->defaultSize(), QImage::Format_ARGB32); + copy_buffer.fill(0x00000000); // Transparent black. + m_pPixmap.reset(new QPixmap(pSvgRenderer->defaultSize())); + QPainter painter(©_buffer); + pSvgRenderer->render(&painter); + m_pPixmap->convertFromImage(copy_buffer); + } else { + m_pSvg.reset(pSvgRenderer.take()); + } + } else { + auto pPixmap = new QPixmap(); + if (!source.getData().isEmpty()) { + pPixmap->loadFromData(source.getData()); + } else { + pPixmap->load(source.getPath()); + } + m_pPixmap.reset(pPixmap); + } +} + +bool Paintable::isNull() const { + if (!m_pPixmap.isNull()) { + return m_pPixmap->isNull(); + } else if (!m_pSvg.isNull()) { + return !m_pSvg->isValid(); + } + return false; +} + +QSize Paintable::size() const { + if (!m_pPixmap.isNull()) { + return m_pPixmap->size(); + } else if (!m_pSvg.isNull()) { + return m_pSvg->defaultSize(); + } + return QSize(); +} + +int Paintable::width() const { + if (!m_pPixmap.isNull()) { + return m_pPixmap->width(); + } else if (!m_pSvg.isNull()) { + QSize size = m_pSvg->defaultSize(); + return size.width(); + } + return 0; +} + +int Paintable::height() const { + if (!m_pPixmap.isNull()) { + return m_pPixmap->height(); + } else if (!m_pSvg.isNull()) { + QSize size = m_pSvg->defaultSize(); + return size.height(); + } + return 0; +} + +QRectF Paintable::rect() const { + if (!m_pPixmap.isNull()) { + return m_pPixmap->rect(); + } else if (!m_pSvg.isNull()) { + return QRectF(QPointF(0, 0), m_pSvg->defaultSize()); + } + return QRectF(); +} + +void Paintable::draw(const QRectF& targetRect, QPainter* pPainter) { + // The sourceRect is implicitly the entire Paintable. + draw(targetRect, pPainter, rect()); +} + +void Paintable::draw(int x, int y, QPainter* pPainter) { + QRectF sourceRect(rect()); + QRectF targetRect(QPointF(x, y), sourceRect.size()); + draw(targetRect, pPainter, sourceRect); +} + +void Paintable::draw(const QPointF& point, QPainter* pPainter, const QRectF& sourceRect) { + return draw(QRectF(point, sourceRect.size()), pPainter, sourceRect); +} + +void Paintable::draw(const QRectF& targetRect, QPainter* pPainter, + const QRectF& sourceRect) { + if (!targetRect.isValid() || !sourceRect.isValid() || isNull()) { + return; + } + + if (m_drawMode == FIXED) { + // Only render the minimum overlapping rectangle between the source + // and target. + QSizeF fixedSize(math_min(sourceRect.width(), targetRect.width()), + math_min(sourceRect.height(), targetRect.height())); + QRectF adjustedTarget(targetRect.topLeft(), fixedSize); + QRectF adjustedSource(sourceRect.topLeft(), fixedSize); + return drawInternal(adjustedTarget, pPainter, adjustedSource); + } else if (m_drawMode == STRETCH_ASPECT) { + qreal sx = targetRect.width() / sourceRect.width(); + qreal sy = targetRect.height() / sourceRect.height(); + + // Adjust the scale so that the scaling in both axes is equal. + if (sx != sy) { + qreal scale = math_min(sx, sy); + QRectF adjustedTarget(targetRect.x(), + targetRect.y(), + scale * sourceRect.width(), + scale * sourceRect.height()); + return drawInternal(adjustedTarget, pPainter, sourceRect); + } else { + return drawInternal(targetRect, pPainter, sourceRect); + } + } else if (m_drawMode == STRETCH) { + return drawInternal(targetRect, pPainter, sourceRect); + } else if (m_drawMode == TILE) { + return drawInternal(targetRect, pPainter, sourceRect); + } +} + +void Paintable::drawCentered(const QRectF& targetRect, QPainter* pPainter, + const QRectF& sourceRect) { + if (m_drawMode == FIXED) { + // Only render the minimum overlapping rectangle between the source + // and target. + QSizeF fixedSize(math_min(sourceRect.width(), targetRect.width()), + math_min(sourceRect.height(), targetRect.height())); + + QRectF adjustedSource(sourceRect.topLeft(), fixedSize); + QRectF adjustedTarget(QPointF(-adjustedSource.width() / 2.0, + -adjustedSource.height() / 2.0), + fixedSize); + return drawInternal(adjustedTarget, pPainter, adjustedSource); + } else if (m_drawMode == STRETCH_ASPECT) { + qreal sx = targetRect.width() / sourceRect.width(); + qreal sy = targetRect.height() / sourceRect.height(); + + // Adjust the scale so that the scaling in both axes is equal. + if (sx != sy) { + qreal scale = math_min(sx, sy); + qreal scaledWidth = scale * sourceRect.width(); + qreal scaledHeight = scale * sourceRect.height(); + QRectF adjustedTarget(-scaledWidth / 2.0, -scaledHeight / 2.0, + scaledWidth, scaledHeight); + return drawInternal(adjustedTarget, pPainter, sourceRect); + } else { + return drawInternal(targetRect, pPainter, sourceRect); + } + } else if (m_drawMode == STRETCH) { + return drawInternal(targetRect, pPainter, sourceRect); + } else if (m_drawMode == TILE) { + // TODO(XXX): What's the right behavior here? Draw the first tile at the + // center point and then tile all around it based on that? + return drawInternal(targetRect, pPainter, sourceRect); + } +} + +void Paintable::drawInternal(const QRectF& targetRect, QPainter* pPainter, + const QRectF& sourceRect) { + // qDebug() << "Paintable::drawInternal" << DrawModeToString(m_draw_mode) + // << targetRect << sourceRect; + if (m_pPixmap) { + if (m_drawMode == TILE) { + // TODO(rryan): Using a source rectangle doesn't make much sense + // with tiling. Ignore the source rect and tile our natural size + // across the target rect. What's the right general behavior here? + // NOTE(rryan): We round our target/source rectangles to the nearest + // pixel for raster images. + pPainter->drawTiledPixmap(targetRect.toRect(), *m_pPixmap, QPoint(0,0)); + } else { + // NOTE(rryan): We round our target/source rectangles to the nearest + // pixel for raster images. + pPainter->drawPixmap(targetRect.toRect(), *m_pPixmap, + sourceRect.toRect()); + } + } else if (m_pSvg) { + if (m_drawMode == TILE) { + qWarning() << "Tiled SVG should have been rendered to pixmap!"; + } else { + // NOTE(rryan): QSvgRenderer render does not clip for us -- it + // applies a world transformation using viewBox and renders the + // entire SVG to the painter. We save/restore the QPainter in case + // there is an existing clip region (I don't know of any Mixxx code + // that uses one but we may in the future). + pPainter->save(); + pPainter->setClipping(true); + pPainter->setClipRect(targetRect); + m_pSvg->setViewBox(sourceRect); + m_pSvg->render(pPainter, targetRect); + pPainter->restore(); + } + } +} + +// static +QString Paintable::getAltFileName(const QString& fileName) { + // Detect if the alternate image file exists and, if it does, + // return its path instead + QStringList temp = fileName.split('.'); + if (temp.length() != 2) { + return fileName; + } + + QString newFileName = temp[0] + QString::fromAscii("@2x.") + temp[1]; + QFile file(newFileName); + if (QFileInfo(file).exists()) { + return newFileName; + } else { + return fileName; + } +} diff --git a/src/widget/paintable.h b/src/widget/paintable.h new file mode 100644 index 000000000000..ba02839d846c --- /dev/null +++ b/src/widget/paintable.h @@ -0,0 +1,68 @@ + +#ifndef PAINTABLE_H +#define PAINTABLE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "skin/imgsource.h" +#include "skin/pixmapsource.h" + +// Wrapper around QImage and QSvgRenderer to support rendering SVG images in +// high fidelity. +class Paintable { + public: + enum DrawMode { + // Draw the image in its native dimensions with no stretching or tiling. + FIXED, + // Stretch the image. + STRETCH, + // Stretch the image maintaining its aspect ratio. + STRETCH_ASPECT, + // Tile the image. + TILE + }; + + // Takes ownership of QImage. + Paintable(QImage* pImage, DrawMode mode); + Paintable(const PixmapSource& source, DrawMode mode); + + QSize size() const; + int width() const; + int height() const; + QRectF rect() const; + DrawMode drawMode() const { + return m_drawMode; + } + + void draw(int x, int y, QPainter* pPainter); + void draw(const QPointF& point, QPainter* pPainter, + const QRectF& sourceRect); + void draw(const QRectF& targetRect, QPainter* pPainter); + void draw(const QRectF& targetRect, QPainter* pPainter, + const QRectF& sourceRect); + void drawCentered(const QRectF& targetRect, QPainter* pPainter, + const QRectF& sourceRect); + bool isNull() const; + + static DrawMode DrawModeFromString(const QString& str); + static QString DrawModeToString(DrawMode mode); + static QString getAltFileName(const QString& fileName); + + private: + void drawInternal(const QRectF& targetRect, QPainter* pPainter, + const QRectF& sourceRect); + + QScopedPointer m_pPixmap; + QScopedPointer m_pSvg; + DrawMode m_drawMode; +}; + +#endif // PAINTABLE diff --git a/src/widget/wpixmapstore.cpp b/src/widget/wpixmapstore.cpp index e6c8c097b9ed..51c526f97cb6 100644 --- a/src/widget/wpixmapstore.cpp +++ b/src/widget/wpixmapstore.cpp @@ -1,20 +1,3 @@ -/*************************************************************************** - wpixmapstore.cpp - description - ------------------- - begin : Mon Jul 28 2003 - copyright : (C) 2003 by Tue & Ken Haste Andersen - email : haste@diku.dk -***************************************************************************/ - -/*************************************************************************** -* * -* This program is free software; you can redistribute it and/or modify * -* it under the terms of the GNU General Public License as published by * -* the Free Software Foundation; either version 2 of the License, or * -* (at your option) any later version. * -* * -***************************************************************************/ - #include "widget/wpixmapstore.h" #include @@ -29,277 +12,6 @@ QHash WPixmapStore::m_paintableCache; QSharedPointer WPixmapStore::m_loader = QSharedPointer(new ImgLoader()); -// static -Paintable::DrawMode Paintable::DrawModeFromString(const QString& str) { - if (str.compare("FIXED", Qt::CaseInsensitive) == 0) { - return FIXED; - } else if (str.compare("STRETCH", Qt::CaseInsensitive) == 0) { - return STRETCH; - } else if (str.compare("STRETCH_ASPECT", Qt::CaseInsensitive) == 0) { - return STRETCH_ASPECT; - } else if (str.compare("TILE", Qt::CaseInsensitive) == 0) { - return TILE; - } - - // Fall back on the implicit default from before Mixxx supported draw modes. - qWarning() << "Unknown DrawMode string in DrawModeFromString:" - << str << "using FIXED"; - return FIXED; -} - -// static -QString Paintable::DrawModeToString(DrawMode mode) { - switch (mode) { - case FIXED: - return "FIXED"; - case STRETCH: - return "STRETCH"; - case STRETCH_ASPECT: - return "STRETCH_ASPECT"; - case TILE: - return "TILE"; - } - // Fall back on the implicit default from before Mixxx supported draw modes. - qWarning() << "Unknown DrawMode in DrawModeToString " << mode - << "using FIXED"; - return "FIXED"; -} - -Paintable::Paintable(QImage* pImage, DrawMode mode) - : m_draw_mode(mode) { -#if QT_VERSION >= 0x040700 - m_pPixmap.reset(new QPixmap()); - m_pPixmap->convertFromImage(*pImage); -#else - m_pPixmap.reset(new QPixmap(QPixmap::fromImage(*pImage))); -#endif - delete pImage; -} - -Paintable::Paintable(const PixmapSource& source, DrawMode mode) - : m_draw_mode(mode) { - if (source.isSVG()) { - QScopedPointer pSvgRenderer(new QSvgRenderer()); - if (source.getData().isEmpty()) { - pSvgRenderer->load(source.getPath()); - } else { - pSvgRenderer->load(source.getData()); - } - - if (mode == TILE) { - // The SVG renderer doesn't directly support tiling, so we render - // it to a pixmap which will then get tiled. - QImage copy_buffer(pSvgRenderer->defaultSize(), QImage::Format_ARGB32); - copy_buffer.fill(0x00000000); // Transparent black. - m_pPixmap.reset(new QPixmap(pSvgRenderer->defaultSize())); - QPainter painter(©_buffer); - pSvgRenderer->render(&painter); - m_pPixmap->convertFromImage(copy_buffer); - } else { - m_pSvg.reset(pSvgRenderer.take()); - } - } else { - auto pPixmap = new QPixmap(); - if (!source.getData().isEmpty()) { - pPixmap->loadFromData(source.getData()); - } else { - pPixmap->load(source.getPath()); - } - m_pPixmap.reset(pPixmap); - } -} - -bool Paintable::isNull() const { - if (!m_pPixmap.isNull()) { - return m_pPixmap->isNull(); - } else if (!m_pSvg.isNull()) { - return !m_pSvg->isValid(); - } - return false; -} - -QSize Paintable::size() const { - if (!m_pPixmap.isNull()) { - return m_pPixmap->size(); - } else if (!m_pSvg.isNull()) { - return m_pSvg->defaultSize(); - } - return QSize(); -} - -int Paintable::width() const { - if (!m_pPixmap.isNull()) { - return m_pPixmap->width(); - } else if (!m_pSvg.isNull()) { - QSize size = m_pSvg->defaultSize(); - return size.width(); - } - return 0; -} - -int Paintable::height() const { - if (!m_pPixmap.isNull()) { - return m_pPixmap->height(); - } else if (!m_pSvg.isNull()) { - QSize size = m_pSvg->defaultSize(); - return size.height(); - } - return 0; -} - -QRectF Paintable::rect() const { - if (!m_pPixmap.isNull()) { - return m_pPixmap->rect(); - } else if (!m_pSvg.isNull()) { - return QRectF(QPointF(0, 0), m_pSvg->defaultSize()); - } - return QRectF(); -} - -void Paintable::draw(const QRectF& targetRect, QPainter* pPainter) { - // The sourceRect is implicitly the entire Paintable. - draw(targetRect, pPainter, rect()); -} - -void Paintable::draw(int x, int y, QPainter* pPainter) { - QRectF sourceRect(rect()); - QRectF targetRect(QPointF(x, y), sourceRect.size()); - draw(targetRect, pPainter, sourceRect); -} - -void Paintable::draw(const QPointF& point, QPainter* pPainter, const QRectF& sourceRect) { - return draw(QRectF(point, sourceRect.size()), pPainter, sourceRect); -} - -void Paintable::draw(const QRectF& targetRect, QPainter* pPainter, - const QRectF& sourceRect) { - if (!targetRect.isValid() || !sourceRect.isValid() || isNull()) { - return; - } - - if (m_draw_mode == FIXED) { - // Only render the minimum overlapping rectangle between the source - // and target. - QSizeF fixedSize(math_min(sourceRect.width(), targetRect.width()), - math_min(sourceRect.height(), targetRect.height())); - QRectF adjustedTarget(targetRect.topLeft(), fixedSize); - QRectF adjustedSource(sourceRect.topLeft(), fixedSize); - return drawInternal(adjustedTarget, pPainter, adjustedSource); - } else if (m_draw_mode == STRETCH_ASPECT) { - qreal sx = targetRect.width() / sourceRect.width(); - qreal sy = targetRect.height() / sourceRect.height(); - - // Adjust the scale so that the scaling in both axes is equal. - if (sx != sy) { - qreal scale = math_min(sx, sy); - QRectF adjustedTarget(targetRect.x(), - targetRect.y(), - scale * sourceRect.width(), - scale * sourceRect.height()); - return drawInternal(adjustedTarget, pPainter, sourceRect); - } else { - return drawInternal(targetRect, pPainter, sourceRect); - } - } else if (m_draw_mode == STRETCH) { - return drawInternal(targetRect, pPainter, sourceRect); - } else if (m_draw_mode == TILE) { - return drawInternal(targetRect, pPainter, sourceRect); - } -} - -void Paintable::drawCentered(const QRectF& targetRect, QPainter* pPainter, - const QRectF& sourceRect) { - if (m_draw_mode == FIXED) { - // Only render the minimum overlapping rectangle between the source - // and target. - QSizeF fixedSize(math_min(sourceRect.width(), targetRect.width()), - math_min(sourceRect.height(), targetRect.height())); - - QRectF adjustedSource(sourceRect.topLeft(), fixedSize); - QRectF adjustedTarget(QPointF(-adjustedSource.width() / 2.0, - -adjustedSource.height() / 2.0), - fixedSize); - return drawInternal(adjustedTarget, pPainter, adjustedSource); - } else if (m_draw_mode == STRETCH_ASPECT) { - qreal sx = targetRect.width() / sourceRect.width(); - qreal sy = targetRect.height() / sourceRect.height(); - - // Adjust the scale so that the scaling in both axes is equal. - if (sx != sy) { - qreal scale = math_min(sx, sy); - qreal scaledWidth = scale * sourceRect.width(); - qreal scaledHeight = scale * sourceRect.height(); - QRectF adjustedTarget(-scaledWidth / 2.0, -scaledHeight / 2.0, - scaledWidth, scaledHeight); - return drawInternal(adjustedTarget, pPainter, sourceRect); - } else { - return drawInternal(targetRect, pPainter, sourceRect); - } - } else if (m_draw_mode == STRETCH) { - return drawInternal(targetRect, pPainter, sourceRect); - } else if (m_draw_mode == TILE) { - // TODO(XXX): What's the right behavior here? Draw the first tile at the - // center point and then tile all around it based on that? - return drawInternal(targetRect, pPainter, sourceRect); - } -} - -void Paintable::drawInternal(const QRectF& targetRect, QPainter* pPainter, - const QRectF& sourceRect) { - // qDebug() << "Paintable::drawInternal" << DrawModeToString(m_draw_mode) - // << targetRect << sourceRect; - if (m_pPixmap) { - if (m_draw_mode == TILE) { - // TODO(rryan): Using a source rectangle doesn't make much sense - // with tiling. Ignore the source rect and tile our natural size - // across the target rect. What's the right general behavior here? - // NOTE(rryan): We round our target/source rectangles to the nearest - // pixel for raster images. - pPainter->drawTiledPixmap(targetRect.toRect(), *m_pPixmap, QPoint(0,0)); - } else { - // NOTE(rryan): We round our target/source rectangles to the nearest - // pixel for raster images. - pPainter->drawPixmap(targetRect.toRect(), *m_pPixmap, - sourceRect.toRect()); - } - } else if (m_pSvg) { - if (m_draw_mode == TILE) { - qWarning() << "Tiled SVG should have been rendered to pixmap!"; - } else { - // NOTE(rryan): QSvgRenderer render does not clip for us -- it - // applies a world transformation using viewBox and renders the - // entire SVG to the painter. We save/restore the QPainter in case - // there is an existing clip region (I don't know of any Mixxx code - // that uses one but we may in the future). - pPainter->save(); - pPainter->setClipping(true); - pPainter->setClipRect(targetRect); - m_pSvg->setViewBox(sourceRect); - m_pSvg->render(pPainter, targetRect); - pPainter->restore(); - } - } -} - -// static -QString Paintable::getAltFileName(const QString& fileName) { - // Detect if the alternate image file exists and, if it does, - // return its path instead - QStringList temp = fileName.split('.'); - if (temp.length() != 2) { - return fileName; - } - - QString newFileName = temp[0] + QString::fromAscii("@2x.") + temp[1]; - QFile file(newFileName); - if (QFileInfo(file).exists()) { - return newFileName; - } else { - return fileName; - } -} - - // static PaintablePointer WPixmapStore::getPaintable(PixmapSource source, Paintable::DrawMode mode, diff --git a/src/widget/wpixmapstore.h b/src/widget/wpixmapstore.h index a0256ed525ec..e23c25f94b90 100644 --- a/src/widget/wpixmapstore.h +++ b/src/widget/wpixmapstore.h @@ -30,56 +30,8 @@ #include "skin/imgsource.h" #include "skin/pixmapsource.h" +#include "widget/paintable.h" -// Wrapper around QImage and QSvgRenderer to support rendering SVG images in -// high fidelity. -class Paintable { - public: - enum DrawMode { - // Draw the image in its native dimensions with no stretching or tiling. - FIXED, - // Stretch the image. - STRETCH, - // Stretch the image maintaining its aspect ratio. - STRETCH_ASPECT, - // Tile the image. - TILE - }; - - // Takes ownership of QImage. - Paintable(QImage* pImage, DrawMode mode); - Paintable(const PixmapSource& source, DrawMode mode); - - QSize size() const; - int width() const; - int height() const; - QRectF rect() const; - DrawMode drawMode() const { - return m_draw_mode; - } - - void draw(int x, int y, QPainter* pPainter); - void draw(const QPointF& point, QPainter* pPainter, - const QRectF& sourceRect); - void draw(const QRectF& targetRect, QPainter* pPainter); - void draw(const QRectF& targetRect, QPainter* pPainter, - const QRectF& sourceRect); - void drawCentered(const QRectF& targetRect, QPainter* pPainter, - const QRectF& sourceRect); - bool isNull() const; - - static DrawMode DrawModeFromString(const QString& str); - static QString DrawModeToString(DrawMode mode); - static QString getAltFileName(const QString& fileName); - - private: - void drawInternal(const QRectF& targetRect, QPainter* pPainter, - const QRectF& sourceRect); - - QScopedPointer m_pPixmap; - QScopedPointer m_pSvg; - DrawMode m_draw_mode; -}; typedef QSharedPointer PaintablePointer; typedef QWeakPointer WeakPaintablePointer; From 57ac6c7450f821f0deab25d7411a601960bbf7a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Tue, 2 May 2017 22:40:32 +0200 Subject: [PATCH 4/9] Don't parse SVG Files in SkinContext. It is done later anyway --- src/skin/skincontext.cpp | 17 ++++------------- src/skin/skincontext.h | 3 +-- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/skin/skincontext.cpp b/src/skin/skincontext.cpp index a076807d3dc6..7bcc86843100 100644 --- a/src/skin/skincontext.cpp +++ b/src/skin/skincontext.cpp @@ -179,7 +179,7 @@ PixmapSource SkinContext::getPixmapSource(const QDomNode& pixmapNode) const { source.setSVG(rslt); } else { // filename. - return getPixmapSourceInner(nodeToString(pixmapNode), svgParser); + return getPixmapSourceInner(nodeToString(pixmapNode)); } } @@ -187,8 +187,7 @@ PixmapSource SkinContext::getPixmapSource(const QDomNode& pixmapNode) const { } PixmapSource SkinContext::getPixmapSource(const QString& filename) const { - const SvgParser svgParser(*this); - return getPixmapSourceInner(filename, svgParser); + return getPixmapSourceInner(filename); } QDomElement SkinContext::loadSvg(const QString& filename) const { @@ -207,17 +206,9 @@ QDomElement SkinContext::loadSvg(const QString& filename) const { return cachedSvg; } -PixmapSource SkinContext::getPixmapSourceInner(const QString& filename, - const SvgParser& svgParser) const { +PixmapSource SkinContext::getPixmapSourceInner(const QString& filename) const { if (!filename.isEmpty()) { - PixmapSource source(getSkinPath(filename)); - if (source.isSVG()) { - QDomElement svgElement = loadSvg(filename); - const QByteArray rslt = svgParser.saveToQByteArray( - svgParser.parseSvgTree(svgElement, filename)); - source.setSVG(rslt); - } - return source; + return PixmapSource(getSkinPath(filename)); } return PixmapSource(); } diff --git a/src/skin/skincontext.h b/src/skin/skincontext.h index 5e2d75098be4..c95c6b3507c9 100644 --- a/src/skin/skincontext.h +++ b/src/skin/skincontext.h @@ -252,8 +252,7 @@ class SkinContext { } private: - PixmapSource getPixmapSourceInner(const QString& filename, - const SvgParser& svgParser) const; + PixmapSource getPixmapSourceInner(const QString& filename) const; QDomElement loadSvg(const QString& filename) const; From 128d02993d8c1e1110837d5f672861e937219775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Tue, 2 May 2017 23:03:28 +0200 Subject: [PATCH 5/9] Unify Paintable constructor --- src/widget/paintable.cpp | 20 ++++++-------------- src/widget/paintable.h | 5 ++--- src/widget/wpixmapstore.cpp | 21 +-------------------- 3 files changed, 9 insertions(+), 37 deletions(-) diff --git a/src/widget/paintable.cpp b/src/widget/paintable.cpp index fb7bc5d02749..3650f373feb9 100644 --- a/src/widget/paintable.cpp +++ b/src/widget/paintable.cpp @@ -44,20 +44,12 @@ QString Paintable::DrawModeToString(DrawMode mode) { return "FIXED"; } -Paintable::Paintable(QImage* pImage, DrawMode mode) - : m_drawMode(mode) { -#if QT_VERSION >= 0x040700 - m_pPixmap.reset(new QPixmap()); - m_pPixmap->convertFromImage(*pImage); -#else - m_pPixmap.reset(new QPixmap(QPixmap::fromImage(*pImage))); -#endif - delete pImage; -} - -Paintable::Paintable(const PixmapSource& source, DrawMode mode) - : m_drawMode(mode) { - if (source.isSVG()) { +Paintable::Paintable(const PixmapSource& source, DrawMode mode, double scaleFactor) + : m_drawMode(mode), + m_source(source) { + if (mode == Paintable::FIXED || mode == Paintable::TILE || !source.isSVG()) { + m_pPixmap.reset(WPixmapStore::getPixmapNoCache(source.getPath(), scaleFactor)); + } else if (source.isSVG()) { QScopedPointer pSvgRenderer(new QSvgRenderer()); if (source.getData().isEmpty()) { pSvgRenderer->load(source.getPath()); diff --git a/src/widget/paintable.h b/src/widget/paintable.h index ba02839d846c..7dcc2568da20 100644 --- a/src/widget/paintable.h +++ b/src/widget/paintable.h @@ -30,9 +30,7 @@ class Paintable { TILE }; - // Takes ownership of QImage. - Paintable(QImage* pImage, DrawMode mode); - Paintable(const PixmapSource& source, DrawMode mode); + Paintable(const PixmapSource& source, DrawMode mode, double scaleFactor); QSize size() const; int width() const; @@ -63,6 +61,7 @@ class Paintable { QScopedPointer m_pPixmap; QScopedPointer m_pSvg; DrawMode m_drawMode; + PixmapSource m_source; }; #endif // PAINTABLE diff --git a/src/widget/wpixmapstore.cpp b/src/widget/wpixmapstore.cpp index 51c526f97cb6..5170a864de2b 100644 --- a/src/widget/wpixmapstore.cpp +++ b/src/widget/wpixmapstore.cpp @@ -26,26 +26,7 @@ PaintablePointer WPixmapStore::getPaintable(PixmapSource source, return pPaintable; } - // Otherwise, construct it with the pixmap loader. - //qDebug() << "WPixmapStore Loading pixmap from file" << source.getPath(); - - if (mode == Paintable::FIXED || mode == Paintable::TILE || !source.isSVG()) { - QImage* pImage = m_loader->getImage(source.getPath(), scaleFactor); - pPaintable = PaintablePointer(new Paintable(pImage, mode)); - } else { - pPaintable = PaintablePointer(new Paintable(source, mode)); - } - - if (pPaintable->isNull()) { - // Only log if it looks like the user tried to specify a - // pixmap. Otherwise we probably just have a widget that is calling - // getPaintable without checking that the skinner actually wanted one. - if (!source.isEmpty()) { - qDebug() << "WPixmapStore couldn't load:" << source.getPath() - << pPaintable.isNull(); - } - return PaintablePointer(); - } + pPaintable = PaintablePointer(new Paintable(source, mode, scaleFactor)); m_paintableCache.insert(key, pPaintable); return pPaintable; From 89db72aaa1b964b1d3d796030847eff2bcb457a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Tue, 2 May 2017 23:10:11 +0200 Subject: [PATCH 6/9] Remove unreachable code --- src/widget/paintable.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/widget/paintable.cpp b/src/widget/paintable.cpp index 3650f373feb9..4a404933ff95 100644 --- a/src/widget/paintable.cpp +++ b/src/widget/paintable.cpp @@ -69,14 +69,6 @@ Paintable::Paintable(const PixmapSource& source, DrawMode mode, double scaleFact } else { m_pSvg.reset(pSvgRenderer.take()); } - } else { - auto pPixmap = new QPixmap(); - if (!source.getData().isEmpty()) { - pPixmap->loadFromData(source.getData()); - } else { - pPixmap->load(source.getPath()); - } - m_pPixmap.reset(pPixmap); } } From e746cd438288f86eadea41f573aec629d50bdfe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Thu, 4 May 2017 22:26:08 +0200 Subject: [PATCH 7/9] Correct collers of inline SVGs as well --- src/widget/paintable.cpp | 30 ++++++++++++------------------ src/widget/wpixmapstore.cpp | 5 +++++ src/widget/wpixmapstore.h | 1 + 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/widget/paintable.cpp b/src/widget/paintable.cpp index 4a404933ff95..fdcd7dc564fa 100644 --- a/src/widget/paintable.cpp +++ b/src/widget/paintable.cpp @@ -47,38 +47,32 @@ QString Paintable::DrawModeToString(DrawMode mode) { Paintable::Paintable(const PixmapSource& source, DrawMode mode, double scaleFactor) : m_drawMode(mode), m_source(source) { - if (mode == Paintable::FIXED || mode == Paintable::TILE || !source.isSVG()) { + if (!source.isSVG()) { m_pPixmap.reset(WPixmapStore::getPixmapNoCache(source.getPath(), scaleFactor)); - } else if (source.isSVG()) { - QScopedPointer pSvgRenderer(new QSvgRenderer()); + } else { + m_pSvg.reset(new QSvgRenderer()); if (source.getData().isEmpty()) { - pSvgRenderer->load(source.getPath()); + m_pSvg->load(source.getPath()); } else { - pSvgRenderer->load(source.getData()); + m_pSvg->load(source.getData()); } - - if (mode == TILE) { + if (mode == TILE || mode == Paintable::FIXED) { // The SVG renderer doesn't directly support tiling, so we render // it to a pixmap which will then get tiled. - QImage copy_buffer(pSvgRenderer->defaultSize(), QImage::Format_ARGB32); + QImage copy_buffer(m_pSvg->defaultSize() * scaleFactor, QImage::Format_ARGB32); copy_buffer.fill(0x00000000); // Transparent black. - m_pPixmap.reset(new QPixmap(pSvgRenderer->defaultSize())); QPainter painter(©_buffer); - pSvgRenderer->render(&painter); + m_pSvg->render(&painter); + WPixmapStore::correctImageColors(©_buffer); + + m_pPixmap.reset(new QPixmap(copy_buffer.size())); m_pPixmap->convertFromImage(copy_buffer); - } else { - m_pSvg.reset(pSvgRenderer.take()); } } } bool Paintable::isNull() const { - if (!m_pPixmap.isNull()) { - return m_pPixmap->isNull(); - } else if (!m_pSvg.isNull()) { - return !m_pSvg->isValid(); - } - return false; + return m_source.isEmpty(); } QSize Paintable::size() const { diff --git a/src/widget/wpixmapstore.cpp b/src/widget/wpixmapstore.cpp index 5170a864de2b..59cb9186455c 100644 --- a/src/widget/wpixmapstore.cpp +++ b/src/widget/wpixmapstore.cpp @@ -48,6 +48,11 @@ QPixmap* WPixmapStore::getPixmapNoCache( return pPixmap; } +// static +void WPixmapStore::correctImageColors(QImage* p) { + m_loader->correctImageColors(p); +} + void WPixmapStore::setLoader(QSharedPointer ld) { m_loader = ld; diff --git a/src/widget/wpixmapstore.h b/src/widget/wpixmapstore.h index e23c25f94b90..0a97f1f89251 100644 --- a/src/widget/wpixmapstore.h +++ b/src/widget/wpixmapstore.h @@ -44,6 +44,7 @@ class WPixmapStore { double scaleFactor); static QPixmap* getPixmapNoCache(const QString& fileName, double scaleFactor); static void setLoader(QSharedPointer ld); + static void correctImageColors(QImage* p); private: static QHash m_paintableCache; From c13c9eda152a95b420504709cbfe7cc9a52aabef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sat, 6 May 2017 01:00:12 +0200 Subject: [PATCH 8/9] Render an intermediate pixmap for stretched images as well xwhen we have a colour schema --- src/skin/imgsource.h | 6 +++++- src/widget/paintable.cpp | 6 +----- src/widget/paintable.h | 2 -- src/widget/wimagestore.cpp | 5 +++++ src/widget/wimagestore.h | 1 + src/widget/wpixmapstore.cpp | 4 ++++ src/widget/wpixmapstore.h | 1 + 7 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/skin/imgsource.h b/src/skin/imgsource.h index 824de028fa8b..9e26cce47f50 100644 --- a/src/skin/imgsource.h +++ b/src/skin/imgsource.h @@ -30,6 +30,7 @@ class ImgSource { virtual QImage* getImage(const QString& fileName, double scaleFactor) const = 0; virtual QColor getCorrectColor(const QColor& c) const { return c; } virtual void correctImageColors(QImage* p) const { (void)p; }; + virtual bool willCorrectColors() const { return false; }; }; class ImgProcessor : public ImgSource { @@ -41,7 +42,8 @@ class ImgProcessor : public ImgSource { QColor getCorrectColor(const QColor& c) const override { return doColorCorrection(m_parent->getCorrectColor(c)); } - void correctImageColors(QImage* p) const override { (void)p; }; + void correctImageColors(QImage* p) const override { (void)p; } + bool willCorrectColors() const override { return false; } protected: ImgSource* m_parent; @@ -131,6 +133,8 @@ class ImgColorProcessor : public ImgProcessor { } } } + + bool willCorrectColors() const override { return true; } }; #endif diff --git a/src/widget/paintable.cpp b/src/widget/paintable.cpp index fdcd7dc564fa..6ea7d3d6359f 100644 --- a/src/widget/paintable.cpp +++ b/src/widget/paintable.cpp @@ -56,7 +56,7 @@ Paintable::Paintable(const PixmapSource& source, DrawMode mode, double scaleFact } else { m_pSvg->load(source.getData()); } - if (mode == TILE || mode == Paintable::FIXED) { + if (mode == TILE || mode == Paintable::FIXED || WPixmapStore::willCorrectColors()) { // The SVG renderer doesn't directly support tiling, so we render // it to a pixmap which will then get tiled. QImage copy_buffer(m_pSvg->defaultSize() * scaleFactor, QImage::Format_ARGB32); @@ -124,10 +124,6 @@ void Paintable::draw(int x, int y, QPainter* pPainter) { draw(targetRect, pPainter, sourceRect); } -void Paintable::draw(const QPointF& point, QPainter* pPainter, const QRectF& sourceRect) { - return draw(QRectF(point, sourceRect.size()), pPainter, sourceRect); -} - void Paintable::draw(const QRectF& targetRect, QPainter* pPainter, const QRectF& sourceRect) { if (!targetRect.isValid() || !sourceRect.isValid() || isNull()) { diff --git a/src/widget/paintable.h b/src/widget/paintable.h index 7dcc2568da20..af36b2b38479 100644 --- a/src/widget/paintable.h +++ b/src/widget/paintable.h @@ -41,8 +41,6 @@ class Paintable { } void draw(int x, int y, QPainter* pPainter); - void draw(const QPointF& point, QPainter* pPainter, - const QRectF& sourceRect); void draw(const QRectF& targetRect, QPainter* pPainter); void draw(const QRectF& targetRect, QPainter* pPainter, const QRectF& sourceRect); diff --git a/src/widget/wimagestore.cpp b/src/widget/wimagestore.cpp index e1d8cc81b654..65f19f860eae 100644 --- a/src/widget/wimagestore.cpp +++ b/src/widget/wimagestore.cpp @@ -108,6 +108,11 @@ void WImageStore::correctImageColors(QImage* p) { m_loader->correctImageColors(p); } +// static +bool WImageStore::willCorrectColors() { + return m_loader->willCorrectColors(); +}; + // static void WImageStore::setLoader(QSharedPointer ld) { m_loader = ld; diff --git a/src/widget/wimagestore.h b/src/widget/wimagestore.h index 127c8dfc9a18..b44e0869ee43 100644 --- a/src/widget/wimagestore.h +++ b/src/widget/wimagestore.h @@ -20,6 +20,7 @@ class WImageStore { static void setLoader(QSharedPointer ld); // For external owned images like software generated ones. static void correctImageColors(QImage* p); + static bool willCorrectColors(); private: struct ImageInfoType { diff --git a/src/widget/wpixmapstore.cpp b/src/widget/wpixmapstore.cpp index 59cb9186455c..b2648337c099 100644 --- a/src/widget/wpixmapstore.cpp +++ b/src/widget/wpixmapstore.cpp @@ -53,6 +53,10 @@ void WPixmapStore::correctImageColors(QImage* p) { m_loader->correctImageColors(p); } +bool WPixmapStore::willCorrectColors() { + return m_loader->willCorrectColors(); +}; + void WPixmapStore::setLoader(QSharedPointer ld) { m_loader = ld; diff --git a/src/widget/wpixmapstore.h b/src/widget/wpixmapstore.h index 0a97f1f89251..ec501617c6c6 100644 --- a/src/widget/wpixmapstore.h +++ b/src/widget/wpixmapstore.h @@ -45,6 +45,7 @@ class WPixmapStore { static QPixmap* getPixmapNoCache(const QString& fileName, double scaleFactor); static void setLoader(QSharedPointer ld); static void correctImageColors(QImage* p); + static bool willCorrectColors(); private: static QHash m_paintableCache; From 20703b3ee1a13bd9f06415ac52950ac1495da110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Fri, 2 Jun 2017 23:26:04 +0200 Subject: [PATCH 9/9] Special case the code for Apples Retina scaling --- src/widget/paintable.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/widget/paintable.cpp b/src/widget/paintable.cpp index 6ea7d3d6359f..e43cbb877e43 100644 --- a/src/widget/paintable.cpp +++ b/src/widget/paintable.cpp @@ -56,7 +56,16 @@ Paintable::Paintable(const PixmapSource& source, DrawMode mode, double scaleFact } else { m_pSvg->load(source.getData()); } +#ifdef __APPLE__ + // Apple does Retina scaling behind the sceens, so we also pass a + // Paintable::FIXED image. On the other targets, it is better to + // cache the pixmap. We do not do this for TILE and color schemas. + // which can result in a correct but possibly blurry picture at a + // Retina display. This can be fixed when switching to QT5 + if (mode == TILE || WPixmapStore::willCorrectColors()) { +#else if (mode == TILE || mode == Paintable::FIXED || WPixmapStore::willCorrectColors()) { +#endif // The SVG renderer doesn't directly support tiling, so we render // it to a pixmap which will then get tiled. QImage copy_buffer(m_pSvg->defaultSize() * scaleFactor, QImage::Format_ARGB32);