Apple icon engine: reverse implementation to avoid pixmaps

Instead of implementing scaledPixmap() to rasterize the
vector image we get from App/UIKit, and calling that from
paint, implement paint() to draw the vector image directly
through the painter, and use that in scaledPixmap.

Change-Id: I2c62826f29406543bc8d8c7fa71199e91586d83b
Reviewed-by: Amr Elsayed <amr.elsayed@qt.io>
Reviewed-by: Doris Verria <doris.verria@qt.io>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
(cherry picked from commit 9d8c5bc7186b0553c47ddabc9ad6c9d509b35f81)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Volker Hilsheimer 2024-03-22 15:59:36 +01:00 committed by Qt Cherry-pick Bot
parent c198f7124c
commit 2a1bdbf730

View File

@ -395,21 +395,32 @@ auto *configuredImage(const UIImage *image, const QColor &color)
#endif #endif
} }
namespace {
template <typename Image>
QPixmap imageToPixmap(const Image *image, QSizeF renderSize)
{
if constexpr (std::is_same_v<Image, NSImage>)
return qt_mac_toQPixmap(image, renderSize.toSize());
else
return QPixmap::fromImage(qt_mac_toQImage(image, renderSize.toSize()));
}
}
QPixmap QAppleIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale) QPixmap QAppleIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale)
{ {
const quint64 cacheKey = calculateCacheKey(mode, state); const quint64 cacheKey = calculateCacheKey(mode, state);
if (cacheKey != m_cacheKey || m_pixmap.size() != size || m_pixmap.devicePixelRatio() != scale) { if (cacheKey != m_cacheKey || m_pixmap.size() != size || m_pixmap.devicePixelRatio() != scale) {
const QSize paintSize = actualSize(size, mode, state);
const QSize paintOffset = paintSize != size
? (QSizeF(size - paintSize) * 0.5).toSize()
: QSize();
m_pixmap = QPixmap(size * scale);
m_pixmap.setDevicePixelRatio(scale);
m_pixmap.fill(Qt::transparent);
QPainter painter(&m_pixmap);
paint(&painter, QRect(paintOffset.width(), paintOffset.height(),
paintSize.width(), paintSize.height()), mode, state);
m_cacheKey = cacheKey;
}
return m_pixmap;
}
void QAppleIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state)
{
Q_UNUSED(state);
QColor color; QColor color;
const QPalette palette; const QPalette palette;
switch (mode) { switch (mode) {
@ -428,36 +439,26 @@ QPixmap QAppleIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIco
} }
const auto *image = configuredImage(m_image, color); const auto *image = configuredImage(m_image, color);
// The size we want might have a different aspect ratio than the icon we have. QMacCGContext ctx(painter);
// So ask for a pixmap with the same aspect ratio as the icon, constrained to the
// size we want, and then center that within a pixmap of the requested size.
const QSize requestedSize = size * scale;
const QSizeF renderSize = actualSize(requestedSize, mode, state);
QPixmap iconPixmap = imageToPixmap(image, renderSize);
iconPixmap.setDevicePixelRatio(scale);
if (renderSize != requestedSize) { #if defined(Q_OS_MACOS)
m_pixmap = QPixmap(requestedSize); NSGraphicsContext *gc = [NSGraphicsContext graphicsContextWithCGContext:ctx flipped:YES];
m_pixmap.fill(Qt::transparent); [NSGraphicsContext saveGraphicsState];
m_pixmap.setDevicePixelRatio(scale); [NSGraphicsContext setCurrentContext:gc];
QPainter painter(&m_pixmap); const NSSize pixmapSize = NSMakeSize(rect.width(), rect.height());
const QSize offset = ((m_pixmap.deviceIndependentSize() [image setSize:pixmapSize];
- iconPixmap.deviceIndependentSize()) / 2).toSize(); const NSRect sourceRect = NSMakeRect(0, 0, pixmapSize.width, pixmapSize.height);
painter.drawPixmap(offset.width(), offset.height(), iconPixmap); const NSRect iconRect = NSMakeRect(rect.x(), rect.y(), pixmapSize.width, pixmapSize.height);
} else {
m_pixmap = iconPixmap;
}
m_cacheKey = cacheKey;
}
return m_pixmap;
}
void QAppleIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) [image drawInRect:iconRect fromRect:sourceRect operation:NSCompositingOperationSourceOver fraction:1.0 respectFlipped:YES hints:nil];
{ [NSGraphicsContext restoreGraphicsState];
const qreal scale = painter->device()->devicePixelRatio(); #elif defined(Q_OS_IOS)
// TODO: render the image directly if we don't have the pixmap yet and paint on an image UIGraphicsPushContext(ctx);
painter->drawPixmap(rect, scaledPixmap(rect.size(), mode, state, scale)); const CGRect cgrect = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
[image drawInRect:cgrect];
UIGraphicsPopContext();
#endif
} }
QT_END_NAMESPACE QT_END_NAMESPACE