Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scaled svg cache #13679

Merged
merged 4 commits into from
Oct 19, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 62 additions & 48 deletions src/widget/paintable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,29 +69,7 @@ Paintable::Paintable(const PixmapSource& source, DrawMode mode, double scaleFact
} else {
return;
}
#ifdef __APPLE__
// Apple does Retina scaling behind the scenes, 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(pSvg->defaultSize() * scaleFactor, QImage::Format_ARGB32);
copy_buffer.fill(0x00000000); // Transparent black.
QPainter painter(&copy_buffer);
pSvg->render(&painter);
WPixmapStore::correctImageColors(&copy_buffer);

m_pPixmap.reset(new QPixmap(copy_buffer.size()));
m_pPixmap->convertFromImage(copy_buffer);
} else {
m_pSvg = std::move(pSvg);
}
m_pSvg = std::move(pSvg);
}
}

Expand All @@ -100,39 +78,43 @@ bool Paintable::isNull() const {
}

QSize Paintable::size() const {
if (m_pSvg) {
return m_pSvg->defaultSize();
}
if (m_pPixmap) {
return m_pPixmap->size();
} else if (m_pSvg) {
return m_pSvg->defaultSize();
}
return QSize();
}

int Paintable::width() const {
if (m_pPixmap) {
return m_pPixmap->width();
} else if (m_pSvg) {
if (m_pSvg) {
QSize size = m_pSvg->defaultSize();
return size.width();
}
if (m_pPixmap) {
return m_pPixmap->width();
}
return 0;
}

int Paintable::height() const {
if (m_pPixmap) {
return m_pPixmap->height();
} else if (m_pSvg) {
if (m_pSvg) {
QSize size = m_pSvg->defaultSize();
return size.height();
}
if (m_pPixmap) {
return m_pPixmap->height();
}
return 0;
}

QRectF Paintable::rect() const {
if (m_pSvg) {
return QRectF(QPointF(0, 0), m_pSvg->defaultSize());
}
if (m_pPixmap) {
return m_pPixmap->rect();
} else if (m_pSvg) {
return QRectF(QPointF(0, 0), m_pSvg->defaultSize());
}
return QRectF();
}
Expand Down Expand Up @@ -247,6 +229,44 @@ void Paintable::drawInternal(const QRectF& targetRect, QPainter* pPainter,
const QRectF& sourceRect) {
// qDebug() << "Paintable::drawInternal" << DrawModeToString(m_drawMode)
// << targetRect << sourceRect;
if (m_pSvg) {
if (m_drawMode == TILE) {
if (!m_pPixmap) {
// qDebug() << "Paintable cache miss";
qreal devicePixelRatio = pPainter->device()->devicePixelRatio();
m_pPixmap = std::make_unique<QPixmap>(m_pSvg->defaultSize());
m_pPixmap->setDevicePixelRatio(devicePixelRatio);
m_pPixmap->fill(Qt::transparent);
{ // QPainter Scope
auto pixmapPainter = QPainter(m_pPixmap.get());
m_pSvg->render(&pixmapPainter);
}
mayCorrectColors();
}
// The SVG renderer doesn't directly support tiling, so we render
// it to a pixmap which will then get tiled.
pPainter->drawTiledPixmap(targetRect, *m_pPixmap);
} else {
if (!m_pPixmap ||
m_pPixmap->size() != targetRect.size().toSize() ||
m_lastSourceRect != sourceRect) {
// qDebug() << "Paintable cache miss";
qreal devicePixelRatio = pPainter->device()->devicePixelRatio();
m_pPixmap = std::make_unique<QPixmap>(targetRect.size().toSize());
m_pPixmap->setDevicePixelRatio(devicePixelRatio);
m_pPixmap->fill(Qt::transparent);
{ // QPainter Scope
auto pixmapPainter = QPainter(m_pPixmap.get());
m_pSvg->setViewBox(sourceRect);
m_pSvg->render(&pixmapPainter);
}
mayCorrectColors();
m_lastSourceRect = sourceRect;
}
pPainter->drawPixmap(targetRect.topLeft(), *m_pPixmap);
}
return;
}
if (m_pPixmap) {
// Note: Qt rounds the target rect to device pixels internally
// using roundInDeviceCoordinates()
Expand All @@ -263,21 +283,7 @@ void Paintable::drawInternal(const QRectF& targetRect, QPainter* pPainter,
pPainter->drawPixmap(targetRect, *m_pPixmap, sourceRect);
}
}
} 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).
PainterScope PainterScope(pPainter);
pPainter->setClipping(true);
pPainter->setClipRect(targetRect);
m_pSvg->setViewBox(sourceRect);
m_pSvg->render(pPainter, targetRect);
}
return;
}
}

Expand All @@ -298,3 +304,11 @@ QString Paintable::getAltFileName(const QString& fileName) {
return fileName;
}
}

void Paintable::mayCorrectColors() {
if (WPixmapStore::willCorrectColors()) {
QImage image = m_pPixmap->toImage();
WPixmapStore::correctImageColors(&image);
m_pPixmap->convertFromImage(image);
}
}
2 changes: 2 additions & 0 deletions src/widget/paintable.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ class Paintable {
private:
void drawInternal(const QRectF& targetRect, QPainter* pPainter,
const QRectF& sourceRect);
void mayCorrectColors();

std::unique_ptr<QPixmap> m_pPixmap;
std::unique_ptr<QSvgRenderer> m_pSvg;
DrawMode m_drawMode;
QRectF m_lastSourceRect;
};
Loading