Fix Hdpi Icon Scaling

Menu icons (16x16) were difficult to repurpose for toolbars because they would resize to fit 20x20, this change composites them onto a 20x20 pixmap so this no longer happens, this seems to make the whole process more stable
This commit is contained in:
Kite 2021-03-02 09:14:26 +00:00 committed by manongjohn
parent 8280906d92
commit 9fded30646
3 changed files with 129 additions and 27 deletions

1
.gitignore vendored
View file

@ -13,6 +13,7 @@ Thumbs.db
# bundled thirdparty libraries # bundled thirdparty libraries
/thirdparty/boost /thirdparty/boost
/thirdparty/libjpeg-turbo64
/thirdparty/lzo /thirdparty/lzo
/thirdparty/tiff-4.0.3 /thirdparty/tiff-4.0.3
/thirdparty/LibJPEG/jpeg-9 /thirdparty/LibJPEG/jpeg-9

View file

@ -113,6 +113,10 @@ QPixmap DVAPI recolorPixmap(
QPixmap pixmap, QColor color = Preferences::instance()->getIconTheme() QPixmap pixmap, QColor color = Preferences::instance()->getIconTheme()
? Qt::black ? Qt::black
: Qt::white); : Qt::white);
QPixmap DVAPI compositePixmap(QPixmap pixmap, qreal opacity,
int canvasWidth = 20, int canvasHeight = 20,
int iconWidth = 16, int iconHeight = 16,
int offset = 0);
QIcon DVAPI createQIcon(const char *iconSVGName, bool useFullOpacity = false); QIcon DVAPI createQIcon(const char *iconSVGName, bool useFullOpacity = false);
QIcon DVAPI createQIconPNG(const char *iconPNGName); QIcon DVAPI createQIconPNG(const char *iconPNGName);
QIcon DVAPI createQIconOnOffPNG(const char *iconPNGName, bool withOver = true); QIcon DVAPI createQIconOnOffPNG(const char *iconPNGName, bool withOver = true);

View file

@ -260,46 +260,143 @@ QPixmap recolorPixmap(QPixmap pixmap, QColor color) {
.rgba(); .rgba();
} }
} }
return pixmap = QPixmap::fromImage(img); pixmap = QPixmap::fromImage(img);
return pixmap;
}
//-----------------------------------------------------------------------------
QPixmap compositePixmap(QPixmap pixmap, qreal opacity, int canvasWidth,
int canvasHeight, int iconWidth, int iconHeight,
int offset) {
/* Creates a composite pixmap from two pixmaps. The canvas controls the final
* size, whereas pixmap is the image to be composited ontop. You can control
* the position of pixmap by setting an offset (top-left), default is 0. */
static int devPixRatio = getDevPixRatio();
QPixmap canvas(canvasWidth, canvasHeight);
canvas.fill(Qt::transparent); // set this to a color to debug
QPixmap combined(canvasWidth, canvasHeight);
combined.fill(Qt::transparent);
if (!pixmap.isNull()) {
QPainter painter;
painter.begin(&combined);
QRect canvasRect(0, 0, canvasWidth, canvasHeight);
painter.drawPixmap(canvasRect, canvas);
painter.setOpacity(opacity);
QRect iconRect(offset, offset, iconWidth, iconHeight);
painter.drawPixmap(iconRect, pixmap);
painter.end();
}
return combined;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
QIcon createQIcon(const char *iconSVGName, bool useFullOpacity) { QIcon createQIcon(const char *iconSVGName, bool useFullOpacity) {
QIcon normalIcon = QIcon::fromTheme(iconSVGName); static int devPixRatio = getDevPixRatio();
QSize iconSize(0, 0); // Get largest // get icon size
for (QList<QSize> sizes = normalIcon.availableSizes(); !sizes.isEmpty(); QIcon themeIcon = QIcon::fromTheme(iconSVGName);
QSize iconSize(0, 0);
for (QList<QSize> sizes = themeIcon.availableSizes(); !sizes.isEmpty();
sizes.removeFirst()) sizes.removeFirst())
if (sizes.first().width() > iconSize.width()) iconSize = sizes.first(); if (sizes.first().width() > iconSize.width()) iconSize = sizes.first();
const qreal offOpacity = 0.8; QString overStr = QString(iconSVGName) + "_over";
const qreal disabledOpacity = 0.15; QString onStr = QString(iconSVGName) + "_on";
QString overStr = QString(iconSVGName) + "_over"; QPixmap themeIconPixmap = recolorPixmap(themeIcon.pixmap(iconSize));
QString onStr = QString(iconSVGName) + "_on"; QPixmap overPixmap =
QPixmap normalPm = recolorPixmap(normalIcon.pixmap(iconSize)); recolorPixmap(QIcon::fromTheme(overStr).pixmap(iconSize));
QPixmap overPm = recolorPixmap(QIcon::fromTheme(overStr).pixmap(iconSize)); QPixmap onPixmap = recolorPixmap(QIcon::fromTheme(onStr).pixmap(iconSize));
QPixmap onPm = recolorPixmap(QIcon::fromTheme(onStr).pixmap(iconSize));
QIcon icon; QIcon icon;
// Off // build icon
icon.addPixmap(useFullOpacity ? normalPm : setOpacity(normalPm, offOpacity), for (int devPixRatio = 1; devPixRatio <= 2; devPixRatio++) {
QIcon::Normal, QIcon::Off); int iconW = themeIconPixmap.width();
icon.addPixmap(setOpacity(normalPm, disabledOpacity), QIcon::Disabled); int iconH = themeIconPixmap.height();
int canvasW = iconW;
int canvasH = iconH;
int offset = 0;
const qreal normalOpacity = useFullOpacity ? 1 : 0.8;
const qreal disabledOpacity = 0.15;
const qreal onOpacity = 1;
// Over // off
icon.addPixmap(!overPm.isNull() ? overPm : normalPm, QIcon::Active); icon.addPixmap(compositePixmap(themeIconPixmap, normalOpacity, canvasW,
canvasH, iconW, iconH),
QIcon::Normal, QIcon::Off);
icon.addPixmap(compositePixmap(themeIconPixmap, disabledOpacity, canvasW,
canvasH, iconW, iconH),
QIcon::Disabled);
// On // over
if (!onPm.isNull()) { icon.addPixmap(
icon.addPixmap(onPm, QIcon::Normal, QIcon::On); compositePixmap(!overPixmap.isNull() ? overPixmap : themeIconPixmap,
icon.addPixmap(setOpacity(onPm, disabledOpacity), QIcon::Disabled, onOpacity, canvasW, canvasH, iconW, iconH),
QIcon::On); QIcon::Active);
} else {
// If file doesn't exist, let's add an opaque normal pixmap // on
icon.addPixmap(normalPm, QIcon::Normal, QIcon::On); if (!onPixmap.isNull()) {
icon.addPixmap(setOpacity(normalPm, disabledOpacity), QIcon::Disabled, icon.addPixmap(
QIcon::On); compositePixmap(onPixmap, onOpacity, canvasW, canvasH, iconW, iconH),
QIcon::Normal, QIcon::On);
icon.addPixmap(compositePixmap(onPixmap, normalOpacity, canvasW, canvasH,
iconW, iconH),
QIcon::Disabled, QIcon::On);
} else {
icon.addPixmap(compositePixmap(themeIconPixmap, onOpacity, canvasW,
canvasH, iconW, iconH),
QIcon::Normal, QIcon::On);
icon.addPixmap(compositePixmap(themeIconPixmap, disabledOpacity, canvasW,
canvasH, iconW, iconH),
QIcon::Disabled, QIcon::On);
}
/* If size is 16x16 (suitable for menu) we composite it onto a separate
* 20x20 pixmap so that it is compatible with toolbars, otherwise it will be
* scaled up and blur. You need to add icons to all QIcon modes otherwise it
* will use the original size, which is undesirable. This is equal to having
* two sets loaded into the icon (16x16 and 20x20) and is dynamically used
* depending on iconSize for toolbars.
*/
if (iconSize == (QSize(16, 16))) {
canvasW = 20 * devPixRatio;
canvasH = 20 * devPixRatio;
offset = 2 * devPixRatio;
// off
icon.addPixmap(compositePixmap(themeIconPixmap, normalOpacity, canvasW,
canvasH, iconW, iconH, offset),
QIcon::Normal, QIcon::Off);
icon.addPixmap(compositePixmap(themeIconPixmap, disabledOpacity, canvasW,
canvasH, iconW, iconH, offset),
QIcon::Disabled);
// over
icon.addPixmap(
compositePixmap(!overPixmap.isNull() ? overPixmap : themeIconPixmap,
onOpacity, canvasW, canvasH, iconW, iconH, offset),
QIcon::Active);
// on
if (!onPixmap.isNull()) {
icon.addPixmap(compositePixmap(onPixmap, onOpacity, canvasW, canvasH,
iconW, iconH, offset),
QIcon::Normal, QIcon::On);
icon.addPixmap(compositePixmap(onPixmap, disabledOpacity, canvasW,
canvasH, iconW, iconH, offset),
QIcon::Disabled, QIcon::On);
} else {
icon.addPixmap(compositePixmap(themeIconPixmap, onOpacity, canvasW,
canvasH, iconW, iconH, offset),
QIcon::Normal, QIcon::On);
icon.addPixmap(compositePixmap(themeIconPixmap, disabledOpacity,
canvasW, canvasH, iconW, iconH, offset),
QIcon::Disabled, QIcon::On);
}
}
} }
return icon; return icon;
} }