Skip to content

Commit 00848c3

Browse files
authored
Merge pull request #3742 from konero/fix-icon-rendering
Fix Icon Rendering for Hdpi
2 parents af5564a + 949afd1 commit 00848c3

File tree

3 files changed

+147
-32
lines changed

3 files changed

+147
-32
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Thumbs.db
1313

1414
# bundled thirdparty libraries
1515
/thirdparty/boost
16+
/thirdparty/libjpeg-turbo64
1617
/thirdparty/lzo
1718
/thirdparty/tiff-4.0.3
1819
/thirdparty/LibJPEG/jpeg-9

toonz/sources/include/toonzqt/gutil.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ QPixmap DVAPI recolorPixmap(
113113
QPixmap pixmap, QColor color = Preferences::instance()->getIconTheme()
114114
? Qt::black
115115
: Qt::white);
116+
QPixmap DVAPI compositePixmap(QPixmap pixmap, qreal opacity,
117+
int canvasWidth = 20, int canvasHeight = 20,
118+
int iconWidth = 16, int iconHeight = 16,
119+
int offset = 0);
116120
QIcon DVAPI createQIcon(const char *iconSVGName, bool useFullOpacity = false);
117121
QIcon DVAPI createQIconPNG(const char *iconPNGName);
118122
QIcon DVAPI createQIconOnOffPNG(const char *iconPNGName, bool withOver = true);

toonz/sources/toonzqt/gutil.cpp

Lines changed: 142 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -260,46 +260,143 @@ QPixmap recolorPixmap(QPixmap pixmap, QColor color) {
260260
.rgba();
261261
}
262262
}
263-
return pixmap = QPixmap::fromImage(img);
263+
pixmap = QPixmap::fromImage(img);
264+
return pixmap;
265+
}
266+
267+
//-----------------------------------------------------------------------------
268+
269+
QPixmap compositePixmap(QPixmap pixmap, qreal opacity, int canvasWidth,
270+
int canvasHeight, int iconWidth, int iconHeight,
271+
int offset) {
272+
/* Creates a composite pixmap from two pixmaps. The canvas controls the final
273+
* size, whereas pixmap is the image to be composited ontop. You can control
274+
* the position of pixmap by setting an offset (top-left), default is 0. */
275+
276+
static int devPixRatio = getDevPixRatio();
277+
278+
QPixmap canvas(canvasWidth, canvasHeight);
279+
canvas.fill(Qt::transparent); // set this to a color to debug
280+
QPixmap combined(canvasWidth, canvasHeight);
281+
combined.fill(Qt::transparent);
282+
if (!pixmap.isNull()) {
283+
QPainter painter;
284+
painter.begin(&combined);
285+
QRect canvasRect(0, 0, canvasWidth, canvasHeight);
286+
painter.drawPixmap(canvasRect, canvas);
287+
painter.setOpacity(opacity);
288+
QRect iconRect(offset, offset, iconWidth, iconHeight);
289+
painter.drawPixmap(iconRect, pixmap);
290+
painter.end();
291+
}
292+
293+
return combined;
264294
}
265295

266296
//-----------------------------------------------------------------------------
267297

268298
QIcon createQIcon(const char *iconSVGName, bool useFullOpacity) {
269-
QIcon normalIcon = QIcon::fromTheme(iconSVGName);
299+
static int devPixRatio = getDevPixRatio();
270300

271-
QSize iconSize(0, 0); // Get largest
272-
for (QList<QSize> sizes = normalIcon.availableSizes(); !sizes.isEmpty();
301+
// get icon size
302+
QIcon themeIcon = QIcon::fromTheme(iconSVGName);
303+
QSize iconSize(0, 0);
304+
for (QList<QSize> sizes = themeIcon.availableSizes(); !sizes.isEmpty();
273305
sizes.removeFirst())
274306
if (sizes.first().width() > iconSize.width()) iconSize = sizes.first();
275307

276-
const qreal offOpacity = 0.8;
277-
const qreal disabledOpacity = 0.15;
278-
QString overStr = QString(iconSVGName) + "_over";
279-
QString onStr = QString(iconSVGName) + "_on";
280-
QPixmap normalPm = recolorPixmap(normalIcon.pixmap(iconSize));
281-
QPixmap overPm = recolorPixmap(QIcon::fromTheme(overStr).pixmap(iconSize));
282-
QPixmap onPm = recolorPixmap(QIcon::fromTheme(onStr).pixmap(iconSize));
308+
QString overStr = QString(iconSVGName) + "_over";
309+
QString onStr = QString(iconSVGName) + "_on";
310+
QPixmap themeIconPixmap = recolorPixmap(themeIcon.pixmap(iconSize));
311+
QPixmap overPixmap =
312+
recolorPixmap(QIcon::fromTheme(overStr).pixmap(iconSize));
313+
QPixmap onPixmap = recolorPixmap(QIcon::fromTheme(onStr).pixmap(iconSize));
283314
QIcon icon;
284315

285-
// Off
286-
icon.addPixmap(useFullOpacity ? normalPm : setOpacity(normalPm, offOpacity),
287-
QIcon::Normal, QIcon::Off);
288-
icon.addPixmap(setOpacity(normalPm, disabledOpacity), QIcon::Disabled);
289-
290-
// Over
291-
icon.addPixmap(!overPm.isNull() ? overPm : normalPm, QIcon::Active);
316+
// build icon
317+
for (int devPixRatio = 1; devPixRatio <= 2; devPixRatio++) {
318+
int iconW = themeIconPixmap.width();
319+
int iconH = themeIconPixmap.height();
320+
int canvasW = iconW;
321+
int canvasH = iconH;
322+
int offset = 0;
323+
const qreal normalOpacity = useFullOpacity ? 1 : 0.8;
324+
const qreal disabledOpacity = 0.15;
325+
const qreal onOpacity = 1;
326+
327+
// off
328+
icon.addPixmap(compositePixmap(themeIconPixmap, normalOpacity, canvasW,
329+
canvasH, iconW, iconH),
330+
QIcon::Normal, QIcon::Off);
331+
icon.addPixmap(compositePixmap(themeIconPixmap, disabledOpacity, canvasW,
332+
canvasH, iconW, iconH),
333+
QIcon::Disabled);
334+
335+
// over
336+
icon.addPixmap(
337+
compositePixmap(!overPixmap.isNull() ? overPixmap : themeIconPixmap,
338+
onOpacity, canvasW, canvasH, iconW, iconH),
339+
QIcon::Active);
340+
341+
// on
342+
if (!onPixmap.isNull()) {
343+
icon.addPixmap(
344+
compositePixmap(onPixmap, onOpacity, canvasW, canvasH, iconW, iconH),
345+
QIcon::Normal, QIcon::On);
346+
icon.addPixmap(compositePixmap(onPixmap, normalOpacity, canvasW, canvasH,
347+
iconW, iconH),
348+
QIcon::Disabled, QIcon::On);
349+
} else {
350+
icon.addPixmap(compositePixmap(themeIconPixmap, onOpacity, canvasW,
351+
canvasH, iconW, iconH),
352+
QIcon::Normal, QIcon::On);
353+
icon.addPixmap(compositePixmap(themeIconPixmap, disabledOpacity, canvasW,
354+
canvasH, iconW, iconH),
355+
QIcon::Disabled, QIcon::On);
356+
}
292357

293-
// On
294-
if (!onPm.isNull()) {
295-
icon.addPixmap(onPm, QIcon::Normal, QIcon::On);
296-
icon.addPixmap(setOpacity(onPm, disabledOpacity), QIcon::Disabled,
297-
QIcon::On);
298-
} else {
299-
// If file doesn't exist, let's add an opaque normal pixmap
300-
icon.addPixmap(normalPm, QIcon::Normal, QIcon::On);
301-
icon.addPixmap(setOpacity(normalPm, disabledOpacity), QIcon::Disabled,
302-
QIcon::On);
358+
/* If size is 16x16 (suitable for menu) we composite it onto a separate
359+
* 20x20 pixmap so that it is compatible with toolbars, otherwise it will be
360+
* scaled up and blur. You need to add icons to all QIcon modes otherwise it
361+
* will use the original size, which is undesirable. This is equal to having
362+
* two sets loaded into the icon (16x16 and 20x20) and is dynamically used
363+
* depending on iconSize for toolbars.
364+
*/
365+
if (iconSize == (QSize(16, 16))) {
366+
canvasW = 20 * devPixRatio;
367+
canvasH = 20 * devPixRatio;
368+
offset = 2 * devPixRatio;
369+
370+
// off
371+
icon.addPixmap(compositePixmap(themeIconPixmap, normalOpacity, canvasW,
372+
canvasH, iconW, iconH, offset),
373+
QIcon::Normal, QIcon::Off);
374+
icon.addPixmap(compositePixmap(themeIconPixmap, disabledOpacity, canvasW,
375+
canvasH, iconW, iconH, offset),
376+
QIcon::Disabled);
377+
// over
378+
icon.addPixmap(
379+
compositePixmap(!overPixmap.isNull() ? overPixmap : themeIconPixmap,
380+
onOpacity, canvasW, canvasH, iconW, iconH, offset),
381+
QIcon::Active);
382+
383+
// on
384+
if (!onPixmap.isNull()) {
385+
icon.addPixmap(compositePixmap(onPixmap, onOpacity, canvasW, canvasH,
386+
iconW, iconH, offset),
387+
QIcon::Normal, QIcon::On);
388+
icon.addPixmap(compositePixmap(onPixmap, disabledOpacity, canvasW,
389+
canvasH, iconW, iconH, offset),
390+
QIcon::Disabled, QIcon::On);
391+
} else {
392+
icon.addPixmap(compositePixmap(themeIconPixmap, onOpacity, canvasW,
393+
canvasH, iconW, iconH, offset),
394+
QIcon::Normal, QIcon::On);
395+
icon.addPixmap(compositePixmap(themeIconPixmap, disabledOpacity,
396+
canvasW, canvasH, iconW, iconH, offset),
397+
QIcon::Disabled, QIcon::On);
398+
}
399+
}
303400
}
304401
return icon;
305402
}
@@ -340,8 +437,11 @@ QIcon createQIconOnOffPNG(const char *iconPNGName, bool withOver) {
340437
//-----------------------------------------------------------------------------
341438

342439
QIcon createTemporaryIconFromName(const char *commandName) {
343-
const int visibleIconSize = 20;
344-
const int menubarIconSize = 16;
440+
const int visibleIconSize = 20;
441+
const int menubarIconSize = 16;
442+
const qreal normalOpacity = 0.8;
443+
const qreal disabledOpacity = 0.15;
444+
const qreal onOpacity = 1;
345445
QString name(commandName);
346446
QList<QChar> iconChar;
347447

@@ -397,8 +497,18 @@ QIcon createTemporaryIconFromName(const char *commandName) {
397497

398498
icon.addPixmap(transparentPm);
399499
icon.addPixmap(transparentPm, QIcon::Disabled);
400-
icon.addPixmap(pixmap);
401-
icon.addPixmap(setOpacity(pixmap, 0.15), QIcon::Disabled);
500+
icon.addPixmap(compositePixmap(pixmap, normalOpacity, pxSize, pxSize,
501+
pixmap.width(), pixmap.height()),
502+
QIcon::Normal, QIcon::Off);
503+
icon.addPixmap(compositePixmap(pixmap, onOpacity, pxSize, pxSize,
504+
pixmap.width(), pixmap.height()),
505+
QIcon::Normal, QIcon::On);
506+
icon.addPixmap(compositePixmap(pixmap, onOpacity, pxSize, pxSize,
507+
pixmap.width(), pixmap.height()),
508+
QIcon::Active);
509+
icon.addPixmap(compositePixmap(pixmap, disabledOpacity, pxSize, pxSize,
510+
pixmap.width(), pixmap.height()),
511+
QIcon::Disabled);
402512
}
403513
return icon;
404514
}

0 commit comments

Comments
 (0)