Qt: HiDPI (retina) support for splash screen

- remove splash screen images (reduce binary size)
- dynamicly draw splash screen with available icon.
- remove testnet icon
- dynamicly colorize testnet icon
This commit is contained in:
Jonas Schnelli 2014-11-06 16:28:29 +01:00 committed by Wladimir J. van der Laan
parent f3af0c898f
commit 54f2571a00
12 changed files with 122 additions and 36 deletions

View file

@ -49,8 +49,7 @@ Jonas Schnelli
src/qt/res/icons/tx_output.png, src/qt/res/icons/bitcoin.icns, src/qt/res/icons/tx_output.png, src/qt/res/icons/bitcoin.icns,
src/qt/res/src/bitcoin.svg, src/qt/res/src/bitcoin.ico, src/qt/res/src/bitcoin.svg, src/qt/res/src/bitcoin.ico,
src/qt/res/src/bitcoin.png, src/qt/res/src/bitcoin_testnet.png, src/qt/res/src/bitcoin.png, src/qt/res/src/bitcoin_testnet.png,
docs/bitcoin_logo_doxygen.png, src/qt/res/images/splash.png, docs/bitcoin_logo_doxygen.png, src/qt/res/src/tx*.svg,
src/qt/res/images/splash_testnet.png, src/qt/res/src/tx*.svg,
src/qt/res/src/connect*.svg, src/qt/res/src/clock*.svg, src/qt/res/src/connect*.svg, src/qt/res/src/clock*.svg,
src/qt/res/src/mine.svg, src/qt/res/src/qt.svg, src/qt/res/src/mine.svg, src/qt/res/src/qt.svg,
src/qt/res/src/verify.svg, src/qt/res/src/verify.svg,

View file

@ -216,8 +216,6 @@ RES_ICONS = \
qt/res/icons/about_qt.png \ qt/res/icons/about_qt.png \
qt/res/icons/bitcoin.ico \ qt/res/icons/bitcoin.ico \
qt/res/icons/bitcoin.png \ qt/res/icons/bitcoin.png \
qt/res/icons/bitcoin_testnet.ico \
qt/res/icons/bitcoin_testnet.png \
qt/res/icons/clock1.png \ qt/res/icons/clock1.png \
qt/res/icons/clock2.png \ qt/res/icons/clock2.png \
qt/res/icons/clock3.png \ qt/res/icons/clock3.png \
@ -315,9 +313,7 @@ BITCOIN_QT_CPP += \
endif endif
RES_IMAGES = \ RES_IMAGES = \
qt/res/images/about.png \ qt/res/images/about.png
qt/res/images/splash.png \
qt/res/images/splash_testnet.png
RES_MOVIES = $(wildcard qt/res/movies/spinner-*.png) RES_MOVIES = $(wildcard qt/res/movies/spinner-*.png)
@ -371,7 +367,7 @@ qt_bitcoin_qt_LIBTOOLFLAGS = --tag CXX
#locale/foo.ts -> locale/foo.qm #locale/foo.ts -> locale/foo.qm
QT_QM=$(QT_TS:.ts=.qm) QT_QM=$(QT_TS:.ts=.qm)
.SECONDARY: $(QT_QM) SECONDARY: $(QT_QM)
qt/bitcoinstrings.cpp: $(libbitcoin_server_a_SOURCES) $(libbitcoin_wallet_a_SOURCES) qt/bitcoinstrings.cpp: $(libbitcoin_server_a_SOURCES) $(libbitcoin_wallet_a_SOURCES)
@test -n $(XGETTEXT) || echo "xgettext is required for updating translations" @test -n $(XGETTEXT) || echo "xgettext is required for updating translations"

View file

@ -25,7 +25,6 @@
<file alias="editpaste">res/icons/editpaste.png</file> <file alias="editpaste">res/icons/editpaste.png</file>
<file alias="editcopy">res/icons/editcopy.png</file> <file alias="editcopy">res/icons/editcopy.png</file>
<file alias="add">res/icons/add.png</file> <file alias="add">res/icons/add.png</file>
<file alias="bitcoin_testnet">res/icons/bitcoin_testnet.png</file>
<file alias="edit">res/icons/edit.png</file> <file alias="edit">res/icons/edit.png</file>
<file alias="history">res/icons/history.png</file> <file alias="history">res/icons/history.png</file>
<file alias="overview">res/icons/overview.png</file> <file alias="overview">res/icons/overview.png</file>
@ -52,8 +51,6 @@
</qresource> </qresource>
<qresource prefix="/images"> <qresource prefix="/images">
<file alias="about">res/images/about.png</file> <file alias="about">res/images/about.png</file>
<file alias="splash">res/images/splash.png</file>
<file alias="splash_testnet">res/images/splash_testnet.png</file>
</qresource> </qresource>
<qresource prefix="/movies"> <qresource prefix="/movies">
<file alias="spinner-000">res/movies/spinner-000.png</file> <file alias="spinner-000">res/movies/spinner-000.png</file>

View file

@ -11,22 +11,22 @@
static const struct { static const struct {
const char *networkId; const char *networkId;
const char *appName; const char *appName;
const char *appIcon; const int iconColorHueShift;
const int iconColorSaturationReduction;
const char *titleAddText; const char *titleAddText;
const char *splashImage;
} network_styles[] = { } network_styles[] = {
{"main", QAPP_APP_NAME_DEFAULT, ":/icons/bitcoin", "", ":/images/splash"}, {"main", QAPP_APP_NAME_DEFAULT, 0, 0, ""},
{"test", QAPP_APP_NAME_TESTNET, ":/icons/bitcoin_testnet", QT_TRANSLATE_NOOP("SplashScreen", "[testnet]"), ":/images/splash_testnet"}, {"test", QAPP_APP_NAME_TESTNET, 70, 30, QT_TRANSLATE_NOOP("SplashScreen", "[testnet]")},
{"regtest", QAPP_APP_NAME_TESTNET, ":/icons/bitcoin_testnet", "[regtest]", ":/images/splash_testnet"} {"regtest", QAPP_APP_NAME_TESTNET, 70, 30, "[regtest]"}
}; };
static const unsigned network_styles_count = sizeof(network_styles)/sizeof(*network_styles); static const unsigned network_styles_count = sizeof(network_styles)/sizeof(*network_styles);
// titleAddText needs to be const char* for tr() // titleAddText needs to be const char* for tr()
NetworkStyle::NetworkStyle(const QString &appName, const QString &appIcon, const char *titleAddText, const QString &splashImage): NetworkStyle::NetworkStyle(const QString &appName, const int iconColorHueShift, const int iconColorSaturationReduction, const char *titleAddText):
appName(appName), appName(appName),
appIcon(appIcon), iconColorHueShift(iconColorHueShift),
titleAddText(qApp->translate("SplashScreen", titleAddText)), iconColorSaturationReduction(iconColorSaturationReduction),
splashImage(splashImage) titleAddText(qApp->translate("SplashScreen", titleAddText))
{ {
} }
@ -38,10 +38,78 @@ const NetworkStyle *NetworkStyle::instantiate(const QString &networkId)
{ {
return new NetworkStyle( return new NetworkStyle(
network_styles[x].appName, network_styles[x].appName,
network_styles[x].appIcon, network_styles[x].iconColorHueShift,
network_styles[x].titleAddText, network_styles[x].iconColorSaturationReduction,
network_styles[x].splashImage); network_styles[x].titleAddText);
} }
} }
return 0; return 0;
} }
QIcon NetworkStyle::getAppIcon() const
{
return getAppIcon(QSize(256,256));
}
QIcon NetworkStyle::getAppIcon(const QSize size) const
{
// load pixmap
QPixmap pixmap(":/icons/bitcoin");
if(pixmap.size().width() != size.width() && pixmap.size().height() != size.height())
{
QPixmap scaledPixmap = pixmap.scaled(size, Qt::KeepAspectRatio);
if(!scaledPixmap.isNull())
{
pixmap = scaledPixmap;
}
}
if(iconColorHueShift != 0 && iconColorSaturationReduction != 0)
{
// copy the pixmap because on linux the original pixmap will be affected
pixmap = pixmap.copy();
// generate QImage from QPixmap
QImage img = pixmap.toImage();
int h,s,l,a;
// traverse though lines
for(int y=0;y<img.height();y++)
{
QRgb *scL = reinterpret_cast< QRgb *>( img.scanLine( y ) );
// loop through pixels
for(int x=0;x<img.width();x++)
{
// preserve alpha because QColor::getHsl doesen't return the alpha value
a = qAlpha(scL[x]);
QColor col(scL[x]);
// get hue value
col.getHsl(&h,&s,&l);
// rotate color on RGB color circle
// 70° should end up with the typical "testnet" green
h+=iconColorHueShift;
// change saturation value
if(s>iconColorSaturationReduction)
{
s -= iconColorSaturationReduction;
}
col.setHsl(h,s,l,a);
// set the pixel
scL[x] = col.rgba();
}
}
//convert back to QPixmap
pixmap.convertFromImage(img);
}
QIcon icon(pixmap);
return icon;
}

View file

@ -17,17 +17,17 @@ public:
static const NetworkStyle *instantiate(const QString &networkId); static const NetworkStyle *instantiate(const QString &networkId);
const QString &getAppName() const { return appName; } const QString &getAppName() const { return appName; }
const QIcon &getAppIcon() const { return appIcon; }
const QString &getTitleAddText() const { return titleAddText; } const QString &getTitleAddText() const { return titleAddText; }
const QPixmap &getSplashImage() const { return splashImage; }
QIcon getAppIcon() const;
QIcon getAppIcon(const QSize size) const;
private: private:
NetworkStyle(const QString &appName, const QString &appIcon, const char *titleAddText, const QString &splashImage); NetworkStyle(const QString &appName, const int iconColorHueShift, const int iconColorSaturationReduction, const char *titleAddText);
QString appName; QString appName;
QIcon appIcon; int iconColorHueShift;
int iconColorSaturationReduction;
QString titleAddText; QString titleAddText;
QPixmap splashImage;
}; };
#endif // BITCOIN_QT_NETWORKSTYLE_H #endif // BITCOIN_QT_NETWORKSTYLE_H

View file

@ -1,5 +1,4 @@
IDI_ICON1 ICON DISCARDABLE "icons/bitcoin.ico" IDI_ICON1 ICON DISCARDABLE "icons/bitcoin.ico"
IDI_ICON2 ICON DISCARDABLE "icons/bitcoin_testnet.ico"
#include <windows.h> // needed for VERSIONINFO #include <windows.h> // needed for VERSIONINFO
#include "../../clientversion.h" // holds the needed client version information #include "../../clientversion.h" // holds the needed client version information

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 342 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

View file

@ -19,6 +19,7 @@
#include <QCloseEvent> #include <QCloseEvent>
#include <QDesktopWidget> #include <QDesktopWidget>
#include <QPainter> #include <QPainter>
#include <QRadialGradient>
SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle) : SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle) :
QWidget(0, f), curAlignment(0) QWidget(0, f), curAlignment(0)
@ -30,6 +31,10 @@ SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle)
int titleCopyrightVSpace = 40; int titleCopyrightVSpace = 40;
float fontFactor = 1.0; float fontFactor = 1.0;
float devicePixelRatio = 1.0;
#if QT_VERSION > 0x050100
devicePixelRatio = ((QGuiApplication*)QCoreApplication::instance())->devicePixelRatio();
#endif
// define text to place // define text to place
QString titleText = tr("Bitcoin Core"); QString titleText = tr("Bitcoin Core");
@ -39,12 +44,34 @@ SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle)
QString font = "Arial"; QString font = "Arial";
// load the bitmap for writing some text over it // create a bitmap according to device pixelratio
pixmap = networkStyle->getSplashImage(); QSize splashSize(480*devicePixelRatio,320*devicePixelRatio);
pixmap = QPixmap(splashSize);
#if QT_VERSION > 0x050100
// change to HiDPI if it makes sense
pixmap.setDevicePixelRatio(devicePixelRatio);
#endif
QPainter pixPaint(&pixmap); QPainter pixPaint(&pixmap);
pixPaint.setPen(QColor(100,100,100)); pixPaint.setPen(QColor(100,100,100));
// draw a slighly radial gradient
QRadialGradient gradient(QPoint(0,0), splashSize.width()/devicePixelRatio);
gradient.setColorAt(0, Qt::white);
gradient.setColorAt(1, QColor(247,247,247));
QRect rGradient(QPoint(0,0), splashSize);
pixPaint.fillRect(rGradient, gradient);
// draw the bitcoin icon, expected size of PNG: 1024x1024
QRect rectIcon(QPoint(-150,-122), QSize(430,430));
const QSize requiredSize(1024,1024);
QIcon appIcon = networkStyle->getAppIcon(requiredSize);
QPixmap icon(appIcon.pixmap(requiredSize));
pixPaint.drawPixmap(rectIcon, icon);
// check font size and drawing with // check font size and drawing with
pixPaint.setFont(QFont(font, 33*fontFactor)); pixPaint.setFont(QFont(font, 33*fontFactor));
QFontMetrics fm = pixPaint.fontMetrics(); QFontMetrics fm = pixPaint.fontMetrics();
@ -57,7 +84,7 @@ SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle)
pixPaint.setFont(QFont(font, 33*fontFactor)); pixPaint.setFont(QFont(font, 33*fontFactor));
fm = pixPaint.fontMetrics(); fm = pixPaint.fontMetrics();
titleTextWidth = fm.width(titleText); titleTextWidth = fm.width(titleText);
pixPaint.drawText(pixmap.width()-titleTextWidth-paddingRight,paddingTop,titleText); pixPaint.drawText(pixmap.width()/devicePixelRatio-titleTextWidth-paddingRight,paddingTop,titleText);
pixPaint.setFont(QFont(font, 15*fontFactor)); pixPaint.setFont(QFont(font, 15*fontFactor));
@ -68,11 +95,11 @@ SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle)
pixPaint.setFont(QFont(font, 10*fontFactor)); pixPaint.setFont(QFont(font, 10*fontFactor));
titleVersionVSpace -= 5; titleVersionVSpace -= 5;
} }
pixPaint.drawText(pixmap.width()-titleTextWidth-paddingRight+2,paddingTop+titleVersionVSpace,versionText); pixPaint.drawText(pixmap.width()/devicePixelRatio-titleTextWidth-paddingRight+2,paddingTop+titleVersionVSpace,versionText);
// draw copyright stuff // draw copyright stuff
pixPaint.setFont(QFont(font, 10*fontFactor)); pixPaint.setFont(QFont(font, 10*fontFactor));
pixPaint.drawText(pixmap.width()-titleTextWidth-paddingRight,paddingTop+titleCopyrightVSpace,copyrightText); pixPaint.drawText(pixmap.width()/devicePixelRatio-titleTextWidth-paddingRight,paddingTop+titleCopyrightVSpace,copyrightText);
// draw additional text if special network // draw additional text if special network
if(!titleAddText.isEmpty()) { if(!titleAddText.isEmpty()) {
@ -81,7 +108,7 @@ SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle)
pixPaint.setFont(boldFont); pixPaint.setFont(boldFont);
fm = pixPaint.fontMetrics(); fm = pixPaint.fontMetrics();
int titleAddTextWidth = fm.width(titleAddText); int titleAddTextWidth = fm.width(titleAddText);
pixPaint.drawText(pixmap.width()-titleAddTextWidth-10,15,titleAddText); pixPaint.drawText(pixmap.width()/devicePixelRatio-titleAddTextWidth-10,15,titleAddText);
} }
pixPaint.end(); pixPaint.end();
@ -90,7 +117,7 @@ SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle)
setWindowTitle(titleText + " " + titleAddText); setWindowTitle(titleText + " " + titleAddText);
// Resize window and move to center of desktop, disallow resizing // Resize window and move to center of desktop, disallow resizing
QRect r(QPoint(), pixmap.size()); QRect r(QPoint(), QSize(pixmap.size().width()/devicePixelRatio,pixmap.size().height()/devicePixelRatio));
resize(r.size()); resize(r.size());
setFixedSize(r.size()); setFixedSize(r.size());
move(QApplication::desktop()->screenGeometry().center() - r.center()); move(QApplication::desktop()->screenGeometry().center() - r.center());