summaryrefslogtreecommitdiffstats
path: root/src/gui/image
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/image')
-rw-r--r--src/gui/image/qicon.cpp106
-rw-r--r--src/gui/image/qicon_p.h2
-rw-r--r--src/gui/image/qiconengine.cpp102
-rw-r--r--src/gui/image/qiconengine.h12
-rw-r--r--src/gui/image/qiconloader.cpp48
-rw-r--r--src/gui/image/qiconloader_p.h5
-rw-r--r--src/gui/image/qimage.cpp165
-rw-r--r--src/gui/image/qimage.h1
-rw-r--r--src/gui/image/qimage_conversions.cpp56
-rw-r--r--src/gui/image/qimage_darwin.mm34
-rw-r--r--src/gui/image/qimagereader.cpp37
-rw-r--r--src/gui/image/qimagewriter.cpp14
-rw-r--r--src/gui/image/qpixmap_raster.cpp25
-rw-r--r--src/gui/image/qpixmap_raster_p.h2
-rw-r--r--src/gui/image/qpnghandler.cpp2
-rw-r--r--src/gui/image/qppmhandler.cpp59
-rw-r--r--src/gui/image/qxbmhandler.cpp2
-rw-r--r--src/gui/image/qxpmhandler.cpp3
18 files changed, 431 insertions, 244 deletions
diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp
index 0d2f55b1c2..07dcf8b726 100644
--- a/src/gui/image/qicon.cpp
+++ b/src/gui/image/qicon.cpp
@@ -97,7 +97,11 @@ QT_BEGIN_NAMESPACE
\value On Display the pixmap when the widget is in an "on" state
*/
-static QBasicAtomicInt serialNumCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
+static int nextSerialNumCounter()
+{
+ static QBasicAtomicInt serial = Q_BASIC_ATOMIC_INITIALIZER(0);
+ return 1 + serial.fetchAndAddRelaxed(1);
+}
static void qt_cleanup_icon_cache();
namespace {
@@ -137,9 +141,9 @@ static qreal qt_effective_device_pixel_ratio(QWindow *window = 0)
return qApp->devicePixelRatio(); // Don't know which window to target.
}
-QIconPrivate::QIconPrivate()
- : engine(0), ref(1),
- serialNum(serialNumCounter.fetchAndAddRelaxed(1)),
+QIconPrivate::QIconPrivate(QIconEngine *e)
+ : engine(e), ref(1),
+ serialNum(nextSerialNumCounter()),
detach_no(0),
is_mask(false)
{
@@ -605,6 +609,51 @@ QFactoryLoader *qt_iconEngineFactoryLoader()
\note QIcon needs a QGuiApplication instance before the icon is created.
+ \section1 High DPI Icons
+
+ There are two ways that QIcon supports \l {High DPI Displays}{high DPI}
+ icons: via \l addFile() and \l fromTheme().
+
+ \l addFile() is useful if you have your own custom directory structure and do
+ not need to use the \l {Icon Theme Specification}{freedesktop.org Icon Theme
+ Specification}. Icons created via this approach use Qt's \l {High Resolution
+ Versions of Images}{"@nx" high DPI syntax}.
+
+ Using \l fromTheme() is necessary if you plan on following the Icon Theme
+ Specification. To make QIcon use the high DPI version of an image, add an
+ additional entry to the appropriate \c index.theme file:
+
+ \badcode
+ [Icon Theme]
+ Name=Test
+ Comment=Test Theme
+
+ Directories=32x32/actions,32x32@2/actions
+
+ [32x32/actions]
+ Size=32
+ Context=Actions
+ Type=Fixed
+
+ # High DPI version of the entry above.
+ [32x32@2/actions]
+ Size=32
+ Scale=2
+ Type=Fixed
+ \endcode
+
+ Your icon theme directory would then look something like this:
+
+ \badcode
+ ├── 32x32
+ │ └── actions
+ │ └── appointment-new.png
+ ├── 32x32@2
+ │ └── actions
+ │ └── appointment-new.png
+ └── index.theme
+ \endcode
+
\sa {fowler}{GUI Design Handbook: Iconic Label}, {Icons Example}
*/
@@ -651,7 +700,7 @@ QIcon::QIcon(const QIcon &other)
the relevant file must be found relative to the runtime working
directory.
- The file name can be either refer to an actual file on disk or to
+ The file name can refer to an actual file on disk or to
one of the application's embedded resources. See the
\l{resources.html}{Resource System} overview for details on how to
embed images and other resource files in the application's
@@ -673,9 +722,8 @@ QIcon::QIcon(const QString &fileName)
ownership of the engine.
*/
QIcon::QIcon(QIconEngine *engine)
- :d(new QIconPrivate)
+ :d(new QIconPrivate(engine))
{
- d->engine = engine;
}
/*!
@@ -848,9 +896,10 @@ QPixmap QIcon::pixmap(QWindow *window, const QSize &size, Mode mode, State state
}
// Try get a pixmap that is big enough to be displayed at device pixel resolution.
- QPixmap pixmap = d->engine->pixmap(size * devicePixelRatio, mode, state);
- pixmap.setDevicePixelRatio(d->pixmapDevicePixelRatio(devicePixelRatio, size, pixmap.size()));
- return pixmap;
+ QIconEngine::ScaledPixmapArgument scalePixmapArg = { size * devicePixelRatio, mode, state, devicePixelRatio, QPixmap() };
+ d->engine->virtual_hook(QIconEngine::ScaledPixmapHook, reinterpret_cast<void*>(&scalePixmapArg));
+ scalePixmapArg.pixmap.setDevicePixelRatio(d->pixmapDevicePixelRatio(devicePixelRatio, size, scalePixmapArg.pixmap.size()));
+ return scalePixmapArg.pixmap;
}
/*!
@@ -950,8 +999,7 @@ void QIcon::detach()
d = 0;
return;
} else if (d->ref.load() != 1) {
- QIconPrivate *x = new QIconPrivate;
- x->engine = d->engine->clone();
+ QIconPrivate *x = new QIconPrivate(d->engine->clone());
if (!d->ref.deref())
delete d;
d = x;
@@ -974,10 +1022,8 @@ void QIcon::addPixmap(const QPixmap &pixmap, Mode mode, State state)
if (pixmap.isNull())
return;
detach();
- if (!d) {
- d = new QIconPrivate;
- d->engine = new QPixmapIconEngine;
- }
+ if (!d)
+ d = new QIconPrivate(new QPixmapIconEngine);
d->engine->addPixmap(pixmap, mode, state);
}
@@ -1003,7 +1049,7 @@ static QIconEngine *iconEngineFromSuffix(const QString &fileName, const QString
the relevant file must be found relative to the runtime working
directory.
- The file name can be either refer to an actual file on disk or to
+ The file name can refer to an actual file on disk or to
one of the application's embedded resources. See the
\l{resources.html}{Resource System} overview for details on how to
embed images and other resource files in the application's
@@ -1037,8 +1083,7 @@ void QIcon::addFile(const QString &fileName, const QSize &size, Mode mode, State
if (!engine)
engine = iconEngineFromSuffix(fileName, QMimeDatabase().mimeTypeForFile(info).preferredSuffix());
#endif // !QT_NO_MIMETYPE
- d = new QIconPrivate;
- d->engine = engine ? engine : new QPixmapIconEngine;
+ d = new QIconPrivate(engine ? engine : new QPixmapIconEngine);
}
d->engine->addFile(fileName, size, mode, state);
@@ -1240,12 +1285,10 @@ bool QIcon::hasThemeIcon(const QString &name)
*/
void QIcon::setIsMask(bool isMask)
{
- if (!d) {
- d = new QIconPrivate;
- d->engine = new QPixmapIconEngine;
- } else {
+ if (!d)
+ d = new QIconPrivate(new QPixmapIconEngine);
+ else
detach();
- }
d->is_mask = isMask;
}
@@ -1326,22 +1369,17 @@ QDataStream &operator>>(QDataStream &s, QIcon &icon)
QString key;
s >> key;
if (key == QLatin1String("QPixmapIconEngine")) {
- icon.d = new QIconPrivate;
- QIconEngine *engine = new QPixmapIconEngine;
- icon.d->engine = engine;
- engine->read(s);
+ icon.d = new QIconPrivate(new QPixmapIconEngine);
+ icon.d->engine->read(s);
} else if (key == QLatin1String("QIconLoaderEngine")) {
- icon.d = new QIconPrivate;
- QIconEngine *engine = new QIconLoaderEngine();
- icon.d->engine = engine;
- engine->read(s);
+ icon.d = new QIconPrivate(new QIconLoaderEngine());
+ icon.d->engine->read(s);
} else {
const int index = loader()->indexOf(key);
if (index != -1) {
if (QIconEnginePlugin *factory = qobject_cast<QIconEnginePlugin*>(loader()->instance(index))) {
if (QIconEngine *engine= factory->create()) {
- icon.d = new QIconPrivate;
- icon.d->engine = engine;
+ icon.d = new QIconPrivate(engine);
engine->read(s);
} // factory
} // instance
diff --git a/src/gui/image/qicon_p.h b/src/gui/image/qicon_p.h
index a978a72192..aa358e88af 100644
--- a/src/gui/image/qicon_p.h
+++ b/src/gui/image/qicon_p.h
@@ -64,7 +64,7 @@ QT_BEGIN_NAMESPACE
class QIconPrivate
{
public:
- QIconPrivate();
+ explicit QIconPrivate(QIconEngine *e);
~QIconPrivate() {
delete engine;
diff --git a/src/gui/image/qiconengine.cpp b/src/gui/image/qiconengine.cpp
index 0ba9844f7a..1f8e5f321a 100644
--- a/src/gui/image/qiconengine.cpp
+++ b/src/gui/image/qiconengine.cpp
@@ -169,6 +169,12 @@ void QIconEngine::addFile(const QString &/*fileName*/, const QSize &/*size*/, QI
bool that can be set to true if the icon is null. This enum value
was added in Qt 5.7.
+ \value ScaledPixmapHook Provides a way to get a pixmap that is scaled
+ according to the given scale (typically equal to the \l {Glossary Of High
+ DPI Terms}{device pixel ratio}). The \a data argument of the virtual_hook()
+ function is a \l ScaledPixmapArgument pointer that contains both the input and
+ output arguments. This enum value was added in Qt 5.9.
+
\sa virtual_hook()
*/
@@ -207,6 +213,60 @@ void QIconEngine::addFile(const QString &/*fileName*/, const QSize &/*size*/, QI
vectorial format normally return an empty list.
*/
+/*!
+ \class QIconEngine::ScaledPixmapArgument
+ \since 5.9
+
+ \inmodule QtGui
+
+ This struct represents arguments to the virtual_hook() function when
+ the \a id parameter is QIconEngine::ScaledPixmapHook.
+
+ The struct provides a way for icons created via \l QIcon::fromTheme()
+ to return pixmaps that are designed for the current \l {Glossary Of High
+ DPI Terms}{device pixel ratio}. The scale for such an icon is specified
+ using the \l {Icon Theme Specification - Directory Layout}{Scale directory key}
+ in the appropriate \c index.theme file.
+
+ Icons created via other approaches will return the same result as a call to
+ \l pixmap() would, and continue to benefit from Qt's \l {High Resolution
+ Versions of Images}{"@nx" high DPI syntax}.
+
+ \sa virtual_hook(), QIconEngine::IconEngineHook, {High DPI Icons}
+ */
+
+/*!
+ \variable QIconEngine::ScaledPixmapArgument::size
+ \brief The requested size of the pixmap.
+*/
+
+/*!
+ \variable QIconEngine::ScaledPixmapArgument::mode
+ \brief The requested mode of the pixmap.
+
+ \sa QIcon::Mode
+*/
+
+/*!
+ \variable QIconEngine::ScaledPixmapArgument::state
+ \brief The requested state of the pixmap.
+
+ \sa QIcon::State
+*/
+
+/*!
+ \variable QIconEngine::ScaledPixmapArgument::scale
+ \brief The requested scale of the pixmap.
+*/
+
+/*!
+ \variable QIconEngine::ScaledPixmapArgument::pixmap
+
+ \brief The pixmap that is the best match for the given \l size, \l mode, \l
+ \state, and \l scale. This is an output parameter that is set after calling
+ \l virtual_hook().
+*/
+
/*!
Returns a key that identifies this icon engine.
@@ -262,6 +322,13 @@ void QIconEngine::virtual_hook(int id, void *data)
arg.sizes.clear();
break;
}
+ case QIconEngine::ScaledPixmapHook: {
+ // We don't have any notion of scale besides "@nx", so just call pixmap() here.
+ QIconEngine::ScaledPixmapArgument &arg =
+ *reinterpret_cast<QIconEngine::ScaledPixmapArgument*>(data);
+ arg.pixmap = pixmap(arg.size, arg.mode, arg.state);
+ break;
+ }
default:
break;
}
@@ -273,9 +340,7 @@ void QIconEngine::virtual_hook(int id, void *data)
Returns sizes of all images that are contained in the engine for the
specific \a mode and \a state.
- \note This is a helper method and the actual work is done by
- virtual_hook() method, hence this method depends on icon engine support
- and may not work with all icon engines.
+ \include qiconengine-virtualhookhelper.qdocinc
*/
QList<QSize> QIconEngine::availableSizes(QIcon::Mode mode, QIcon::State state) const
{
@@ -291,9 +356,7 @@ QList<QSize> QIconEngine::availableSizes(QIcon::Mode mode, QIcon::State state) c
Returns the name used to create the engine, if available.
- \note This is a helper method and the actual work is done by
- virtual_hook() method, hence this method depends on icon engine support
- and may not work with all icon engines.
+ \include qiconengine-virtualhookhelper.qdocinc
*/
QString QIconEngine::iconName() const
{
@@ -306,6 +369,8 @@ QString QIconEngine::iconName() const
\since 5.7
Returns true if this icon engine represent a null QIcon.
+
+ \include qiconengine-virtualhookhelper.qdocinc
*/
bool QIconEngine::isNull() const
{
@@ -314,4 +379,29 @@ bool QIconEngine::isNull() const
return isNull;
}
+/*!
+ \since 5.9
+
+ Returns a pixmap for the given \a size, \a mode, \a state and \a scale.
+
+ The \a scale argument is typically equal to the \l {Glossary Of High DPI
+ Terms}{device pixel ratio} of the display.
+
+ \include qiconengine-virtualhookhelper.qdocinc
+
+ \note Some engines may cast \a scale to an integer.
+
+ \sa ScaledPixmapArgument
+*/
+QPixmap QIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale)
+{
+ ScaledPixmapArgument arg;
+ arg.size = size;
+ arg.mode = mode;
+ arg.state = state;
+ arg.scale = scale;
+ const_cast<QIconEngine *>(this)->virtual_hook(QIconEngine::ScaledPixmapHook, reinterpret_cast<void*>(&arg));
+ return arg.pixmap;
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/image/qiconengine.h b/src/gui/image/qiconengine.h
index 783770cd30..0c67ef2686 100644
--- a/src/gui/image/qiconengine.h
+++ b/src/gui/image/qiconengine.h
@@ -65,7 +65,7 @@ public:
virtual bool read(QDataStream &in);
virtual bool write(QDataStream &out) const;
- enum IconEngineHook { AvailableSizesHook = 1, IconNameHook, IsNullHook };
+ enum IconEngineHook { AvailableSizesHook = 1, IconNameHook, IsNullHook, ScaledPixmapHook };
struct AvailableSizesArgument
{
@@ -79,6 +79,16 @@ public:
virtual QString iconName() const;
bool isNull() const; // ### Qt6 make virtual
+ QPixmap scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale); // ### Qt6 make virtual
+
+ struct ScaledPixmapArgument
+ {
+ QSize size;
+ QIcon::Mode mode;
+ QIcon::State state;
+ qreal scale;
+ QPixmap pixmap;
+ };
virtual void virtual_hook(int id, void *data);
diff --git a/src/gui/image/qiconloader.cpp b/src/gui/image/qiconloader.cpp
index 324f13a17b..d72c05a3c5 100644
--- a/src/gui/image/qiconloader.cpp
+++ b/src/gui/image/qiconloader.cpp
@@ -47,6 +47,7 @@
#include <qpa/qplatformtheme.h>
#include <QtGui/QIconEngine>
#include <QtGui/QPalette>
+#include <QtCore/qmath.h>
#include <QtCore/QList>
#include <QtCore/QDir>
#include <QtCore/QSettings>
@@ -347,6 +348,10 @@ QIconTheme::QIconTheme(const QString &themeName)
dirInfo.maxSize = indexReader.value(directoryKey +
QLatin1String("/MaxSize"),
size).toInt();
+
+ dirInfo.scale = indexReader.value(directoryKey +
+ QLatin1String("/Scale"),
+ 1).toInt();
m_keyList.append(dirInfo);
}
}
@@ -553,8 +558,11 @@ void QIconLoaderEngine::paint(QPainter *painter, const QRect &rect,
* This algorithm is defined by the freedesktop spec:
* http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
*/
-static bool directoryMatchesSize(const QIconDirInfo &dir, int iconsize)
+static bool directoryMatchesSize(const QIconDirInfo &dir, int iconsize, int iconscale)
{
+ if (dir.scale != iconscale)
+ return false;
+
if (dir.type == QIconDirInfo::Fixed) {
return dir.size == iconsize;
@@ -575,24 +583,25 @@ static bool directoryMatchesSize(const QIconDirInfo &dir, int iconsize)
* This algorithm is defined by the freedesktop spec:
* http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
*/
-static int directorySizeDistance(const QIconDirInfo &dir, int iconsize)
+static int directorySizeDistance(const QIconDirInfo &dir, int iconsize, int iconscale)
{
+ const int scaledIconSize = iconsize * iconscale;
if (dir.type == QIconDirInfo::Fixed) {
- return qAbs(dir.size - iconsize);
+ return qAbs(dir.size * dir.scale - scaledIconSize);
} else if (dir.type == QIconDirInfo::Scalable) {
- if (iconsize < dir.minSize)
- return dir.minSize - iconsize;
- else if (iconsize > dir.maxSize)
- return iconsize - dir.maxSize;
+ if (scaledIconSize < dir.minSize * dir.scale)
+ return dir.minSize * dir.scale - scaledIconSize;
+ else if (scaledIconSize > dir.maxSize * dir.scale)
+ return scaledIconSize - dir.maxSize * dir.scale;
else
return 0;
} else if (dir.type == QIconDirInfo::Threshold) {
- if (iconsize < dir.size - dir.threshold)
- return dir.minSize - iconsize;
- else if (iconsize > dir.size + dir.threshold)
- return iconsize - dir.maxSize;
+ if (scaledIconSize < (dir.size - dir.threshold) * dir.scale)
+ return dir.minSize * dir.scale - scaledIconSize;
+ else if (scaledIconSize > (dir.size + dir.threshold) * dir.scale)
+ return scaledIconSize - dir.maxSize * dir.scale;
else return 0;
}
@@ -600,7 +609,7 @@ static int directorySizeDistance(const QIconDirInfo &dir, int iconsize)
return INT_MAX;
}
-QIconLoaderEngineEntry *QIconLoaderEngine::entryForSize(const QSize &size)
+QIconLoaderEngineEntry *QIconLoaderEngine::entryForSize(const QSize &size, int scale)
{
int iconsize = qMin(size.width(), size.height());
@@ -612,7 +621,7 @@ QIconLoaderEngineEntry *QIconLoaderEngine::entryForSize(const QSize &size)
// Search for exact matches first
for (int i = 0; i < numEntries; ++i) {
QIconLoaderEngineEntry *entry = m_info.entries.at(i);
- if (directoryMatchesSize(entry->dir, iconsize)) {
+ if (directoryMatchesSize(entry->dir, iconsize, scale)) {
return entry;
}
}
@@ -622,7 +631,7 @@ QIconLoaderEngineEntry *QIconLoaderEngine::entryForSize(const QSize &size)
QIconLoaderEngineEntry *closestMatch = 0;
for (int i = 0; i < numEntries; ++i) {
QIconLoaderEngineEntry *entry = m_info.entries.at(i);
- int distance = directorySizeDistance(entry->dir, iconsize);
+ int distance = directorySizeDistance(entry->dir, iconsize, scale);
if (distance < minimalSize) {
minimalSize = distance;
closestMatch = entry;
@@ -665,6 +674,8 @@ QPixmap PixmapEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State st
basePixmap.load(filename);
QSize actualSize = basePixmap.size();
+ // If the size of the best match we have (basePixmap) is larger than the
+ // requested size, we downscale it to match.
if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height()))
actualSize.scale(size, Qt::KeepAspectRatio);
@@ -748,6 +759,15 @@ void QIconLoaderEngine::virtual_hook(int id, void *data)
*reinterpret_cast<bool*>(data) = m_info.entries.isEmpty();
}
break;
+ case QIconEngine::ScaledPixmapHook:
+ {
+ QIconEngine::ScaledPixmapArgument &arg = *reinterpret_cast<QIconEngine::ScaledPixmapArgument*>(data);
+ // QIcon::pixmap() multiplies size by the device pixel ratio.
+ const int integerScale = qCeil(arg.scale);
+ QIconLoaderEngineEntry *entry = entryForSize(arg.size / integerScale, integerScale);
+ arg.pixmap = entry ? entry->pixmap(arg.size, arg.mode, arg.state) : QPixmap();
+ }
+ break;
default:
QIconEngine::virtual_hook(id, data);
}
diff --git a/src/gui/image/qiconloader_p.h b/src/gui/image/qiconloader_p.h
index ed7b7ff7ae..5f3a3ef948 100644
--- a/src/gui/image/qiconloader_p.h
+++ b/src/gui/image/qiconloader_p.h
@@ -76,12 +76,14 @@ struct QIconDirInfo
maxSize(0),
minSize(0),
threshold(0),
+ scale(1),
type(Threshold) {}
QString path;
short size;
short maxSize;
short minSize;
short threshold;
+ short scale;
Type type;
};
Q_DECLARE_TYPEINFO(QIconDirInfo, Q_MOVABLE_TYPE);
@@ -135,7 +137,8 @@ private:
bool hasIcon() const;
void ensureLoaded();
void virtual_hook(int id, void *data) Q_DECL_OVERRIDE;
- QIconLoaderEngineEntry *entryForSize(const QSize &size);
+ QIconLoaderEngineEntry *entryForSize(const QSize &size, int scale = 1);
+
QIconLoaderEngine(const QIconLoaderEngine &other);
QThemeIconInfo m_info;
QString m_iconName;
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp
index 9e9f01a46a..c1ea053204 100644
--- a/src/gui/image/qimage.cpp
+++ b/src/gui/image/qimage.cpp
@@ -89,12 +89,16 @@ static QImage rotated90(const QImage &src);
static QImage rotated180(const QImage &src);
static QImage rotated270(const QImage &src);
-QBasicAtomicInt qimage_serial_number = Q_BASIC_ATOMIC_INITIALIZER(1);
+static int next_qimage_serial_number()
+{
+ static QBasicAtomicInt serial = Q_BASIC_ATOMIC_INITIALIZER(0);
+ return 1 + serial.fetchAndAddRelaxed(1);
+}
QImageData::QImageData()
: ref(0), width(0), height(0), depth(0), nbytes(0), devicePixelRatio(1.0), data(0),
format(QImage::Format_ARGB32), bytes_per_line(0),
- ser_no(qimage_serial_number.fetchAndAddRelaxed(1)),
+ ser_no(next_qimage_serial_number()),
detach_no(0),
dpmx(qt_defaultDpiX() * 100 / qreal(2.54)),
dpmy(qt_defaultDpiY() * 100 / qreal(2.54)),
@@ -2121,6 +2125,44 @@ QImage QImage::convertToFormat(Format format, const QVector<QRgb> &colorTable, Q
}
/*!
+ \since 5.9
+
+ Changes the format of the image without changing the data. Only
+ works between formats of the same depth.
+
+ Returns \c true if successful.
+
+ This function can be used to change images with alpha-channels to
+ their corresponding opaque formats if the data is known to be opaque-only,
+ or to change the format of a given image buffer before overwriting
+ it with new data.
+
+ \warning The function does not check if the image data is valid in the
+ new format and will still return \c true if the depths are compatible.
+ Operations on an image with invalid data are undefined.
+
+ \warning If the image is not detached, this will cause the data to be
+ copied.
+
+ \sa isDetached(), hasAlphaChannel(), convertToFormat()
+*/
+
+bool QImage::reinterpretAsFormat(Format format)
+{
+ if (!d)
+ return false;
+ if (d->format == format)
+ return true;
+ if (qt_depthForFormat(format) != qt_depthForFormat(d->format))
+ return false;
+ if (!isDetached()) // Detach only if shared, not for read-only data.
+ detach();
+
+ d->format = format;
+ return true;
+}
+
+/*!
\fn bool QImage::valid(const QPoint &pos) const
Returns \c true if \a pos is a valid coordinate pair within the
@@ -4458,7 +4500,8 @@ QImage QImage::smoothScaled(int w, int h) const {
return src;
}
-static QImage rotated90(const QImage &image) {
+static QImage rotated90(const QImage &image)
+{
QImage out(image.height(), image.width(), image.format());
out.setDotsPerMeterX(image.dotsPerMeterY());
out.setDotsPerMeterY(image.dotsPerMeterX());
@@ -4466,49 +4509,10 @@ static QImage rotated90(const QImage &image) {
out.setColorTable(image.colorTable());
int w = image.width();
int h = image.height();
- switch (image.format()) {
- case QImage::Format_RGB32:
- case QImage::Format_ARGB32:
- case QImage::Format_ARGB32_Premultiplied:
- case QImage::Format_RGBX8888:
- case QImage::Format_RGBA8888:
- case QImage::Format_RGBA8888_Premultiplied:
- case QImage::Format_BGR30:
- case QImage::Format_A2BGR30_Premultiplied:
- case QImage::Format_RGB30:
- case QImage::Format_A2RGB30_Premultiplied:
- qt_memrotate270(reinterpret_cast<const quint32*>(image.bits()),
- w, h, image.bytesPerLine(),
- reinterpret_cast<quint32*>(out.bits()),
- out.bytesPerLine());
- break;
- case QImage::Format_RGB666:
- case QImage::Format_ARGB6666_Premultiplied:
- case QImage::Format_ARGB8565_Premultiplied:
- case QImage::Format_ARGB8555_Premultiplied:
- case QImage::Format_RGB888:
- qt_memrotate270(reinterpret_cast<const quint24*>(image.bits()),
- w, h, image.bytesPerLine(),
- reinterpret_cast<quint24*>(out.bits()),
- out.bytesPerLine());
- break;
- case QImage::Format_RGB555:
- case QImage::Format_RGB16:
- case QImage::Format_ARGB4444_Premultiplied:
- qt_memrotate270(reinterpret_cast<const quint16*>(image.bits()),
- w, h, image.bytesPerLine(),
- reinterpret_cast<quint16*>(out.bits()),
- out.bytesPerLine());
- break;
- case QImage::Format_Alpha8:
- case QImage::Format_Grayscale8:
- case QImage::Format_Indexed8:
- qt_memrotate270(reinterpret_cast<const quint8*>(image.bits()),
- w, h, image.bytesPerLine(),
- reinterpret_cast<quint8*>(out.bits()),
- out.bytesPerLine());
- break;
- default:
+ const MemRotateFunc memrotate = qMemRotateFunctions[qPixelLayouts[image.format()].bpp][2];
+ if (memrotate) {
+ memrotate(image.constBits(), w, h, image.bytesPerLine(), out.bits(), out.bytesPerLine());
+ } else {
for (int y=0; y<h; ++y) {
if (image.colorCount())
for (int x=0; x<w; ++x)
@@ -4517,18 +4521,29 @@ static QImage rotated90(const QImage &image) {
for (int x=0; x<w; ++x)
out.setPixel(h-y-1, x, image.pixel(x, y));
}
- break;
}
return out;
}
+static QImage rotated180(const QImage &image)
+{
+ const MemRotateFunc memrotate = qMemRotateFunctions[qPixelLayouts[image.format()].bpp][1];
+ if (!memrotate)
+ return image.mirrored(true, true);
-static QImage rotated180(const QImage &image) {
- return image.mirrored(true, true);
+ QImage out(image.width(), image.height(), image.format());
+ out.setDotsPerMeterX(image.dotsPerMeterY());
+ out.setDotsPerMeterY(image.dotsPerMeterX());
+ if (image.colorCount() > 0)
+ out.setColorTable(image.colorTable());
+ int w = image.width();
+ int h = image.height();
+ memrotate(image.constBits(), w, h, image.bytesPerLine(), out.bits(), out.bytesPerLine());
+ return out;
}
-
-static QImage rotated270(const QImage &image) {
+static QImage rotated270(const QImage &image)
+{
QImage out(image.height(), image.width(), image.format());
out.setDotsPerMeterX(image.dotsPerMeterY());
out.setDotsPerMeterY(image.dotsPerMeterX());
@@ -4536,49 +4551,10 @@ static QImage rotated270(const QImage &image) {
out.setColorTable(image.colorTable());
int w = image.width();
int h = image.height();
- switch (image.format()) {
- case QImage::Format_RGB32:
- case QImage::Format_ARGB32:
- case QImage::Format_ARGB32_Premultiplied:
- case QImage::Format_RGBX8888:
- case QImage::Format_RGBA8888:
- case QImage::Format_RGBA8888_Premultiplied:
- case QImage::Format_BGR30:
- case QImage::Format_A2BGR30_Premultiplied:
- case QImage::Format_RGB30:
- case QImage::Format_A2RGB30_Premultiplied:
- qt_memrotate90(reinterpret_cast<const quint32*>(image.bits()),
- w, h, image.bytesPerLine(),
- reinterpret_cast<quint32*>(out.bits()),
- out.bytesPerLine());
- break;
- case QImage::Format_RGB666:
- case QImage::Format_ARGB6666_Premultiplied:
- case QImage::Format_ARGB8565_Premultiplied:
- case QImage::Format_ARGB8555_Premultiplied:
- case QImage::Format_RGB888:
- qt_memrotate90(reinterpret_cast<const quint24*>(image.bits()),
- w, h, image.bytesPerLine(),
- reinterpret_cast<quint24*>(out.bits()),
- out.bytesPerLine());
- break;
- case QImage::Format_RGB555:
- case QImage::Format_RGB16:
- case QImage::Format_ARGB4444_Premultiplied:
- qt_memrotate90(reinterpret_cast<const quint16*>(image.bits()),
- w, h, image.bytesPerLine(),
- reinterpret_cast<quint16*>(out.bits()),
- out.bytesPerLine());
- break;
- case QImage::Format_Alpha8:
- case QImage::Format_Grayscale8:
- case QImage::Format_Indexed8:
- qt_memrotate90(reinterpret_cast<const quint8*>(image.bits()),
- w, h, image.bytesPerLine(),
- reinterpret_cast<quint8*>(out.bits()),
- out.bytesPerLine());
- break;
- default:
+ const MemRotateFunc memrotate = qMemRotateFunctions[qPixelLayouts[image.format()].bpp][0];
+ if (memrotate) {
+ memrotate(image.constBits(), w, h, image.bytesPerLine(), out.bits(), out.bytesPerLine());
+ } else {
for (int y=0; y<h; ++y) {
if (image.colorCount())
for (int x=0; x<w; ++x)
@@ -4587,7 +4563,6 @@ static QImage rotated270(const QImage &image) {
for (int x=0; x<w; ++x)
out.setPixel(y, w-x-1, image.pixel(x, y));
}
- break;
}
return out;
}
diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h
index fd2298561e..204e7a54b2 100644
--- a/src/gui/image/qimage.h
+++ b/src/gui/image/qimage.h
@@ -192,6 +192,7 @@ public:
QImage convertToFormat(Format f, Qt::ImageConversionFlags flags = Qt::AutoColor) const Q_REQUIRED_RESULT;
#endif
QImage convertToFormat(Format f, const QVector<QRgb> &colorTable, Qt::ImageConversionFlags flags = Qt::AutoColor) const Q_REQUIRED_RESULT;
+ bool reinterpretAsFormat(Format f);
int width() const;
int height() const;
diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp
index 9c9db9a245..50fad1566c 100644
--- a/src/gui/image/qimage_conversions.cpp
+++ b/src/gui/image/qimage_conversions.cpp
@@ -39,12 +39,29 @@
#include <private/qdrawhelper_p.h>
#include <private/qguiapplication_p.h>
+#include <private/qcolorprofile_p.h>
#include <private/qsimd_p.h>
#include <private/qimage_p.h>
#include <qendian.h>
QT_BEGIN_NAMESPACE
+struct QDefaultColorTables
+{
+ QDefaultColorTables()
+ : gray(256), alpha(256)
+ {
+ for (int i = 0; i < 256; ++i) {
+ gray[i] = qRgb(i, i, i);
+ alpha[i] = qRgba(0, 0, 0, i);
+ }
+ }
+
+ QVector<QRgb> gray, alpha;
+};
+
+Q_GLOBAL_STATIC(QDefaultColorTables, defaultColorTables);
+
// table to flip bits
static const uchar bitflip[256] = {
/*
@@ -82,23 +99,17 @@ const uchar *qt_get_bitflip_array()
void qGamma_correct_back_to_linear_cs(QImage *image)
{
- const QDrawHelperGammaTables *tables = QGuiApplicationPrivate::instance()->gammaTables();
- if (!tables)
+ const QColorProfile *cp = QGuiApplicationPrivate::instance()->colorProfileForA32Text();
+ if (!cp)
return;
- const uchar *gamma = tables->qt_pow_rgb_gamma;
// gamma correct the pixels back to linear color space...
int h = image->height();
int w = image->width();
for (int y=0; y<h; ++y) {
- uint *pixels = (uint *) image->scanLine(y);
- for (int x=0; x<w; ++x) {
- uint p = pixels[x];
- uint r = gamma[qRed(p)];
- uint g = gamma[qGreen(p)];
- uint b = gamma[qBlue(p)];
- pixels[x] = (r << 16) | (g << 8) | b | 0xff000000;
- }
+ QRgb *pixels = reinterpret_cast<QRgb *>(image->scanLine(y));
+ for (int x=0; x<w; ++x)
+ pixels[x] = cp->toLinear(pixels[x]);
}
}
@@ -1957,11 +1968,7 @@ static void convert_Alpha8_to_Indexed8(QImageData *dest, const QImageData *src,
memcpy(dest->data, src->data, src->bytes_per_line * src->height);
- QVector<QRgb> colors(256);
- for (int i=0; i<256; ++i)
- colors[i] = qRgba(0, 0, 0, i);
-
- dest->colortable = colors;
+ dest->colortable = defaultColorTables->alpha;
}
static void convert_Grayscale8_to_Indexed8(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
@@ -1971,22 +1978,15 @@ static void convert_Grayscale8_to_Indexed8(QImageData *dest, const QImageData *s
memcpy(dest->data, src->data, src->bytes_per_line * src->height);
- QVector<QRgb> colors(256);
- for (int i=0; i<256; ++i)
- colors[i] = qRgb(i, i, i);
- dest->colortable = colors;
+ dest->colortable = defaultColorTables->gray;
}
static bool convert_Alpha8_to_Indexed8_inplace(QImageData *data, Qt::ImageConversionFlags)
{
Q_ASSERT(data->format == QImage::Format_Alpha8);
- QVector<QRgb> colors(256);
- for (int i=0; i<256; ++i)
- colors[i] = qRgba(0, 0, 0, i);
-
- data->colortable = colors;
+ data->colortable = defaultColorTables->alpha;
data->format = QImage::Format_Indexed8;
return true;
@@ -1996,11 +1996,7 @@ static bool convert_Grayscale8_to_Indexed8_inplace(QImageData *data, Qt::ImageCo
{
Q_ASSERT(data->format == QImage::Format_Grayscale8);
- QVector<QRgb> colors(256);
- for (int i=0; i<256; ++i)
- colors[i] = qRgb(i, i, i);
-
- data->colortable = colors;
+ data->colortable = defaultColorTables->gray;
data->format = QImage::Format_Indexed8;
return true;
diff --git a/src/gui/image/qimage_darwin.mm b/src/gui/image/qimage_darwin.mm
index 2d38468cc5..3764bef06b 100644
--- a/src/gui/image/qimage_darwin.mm
+++ b/src/gui/image/qimage_darwin.mm
@@ -1,31 +1,37 @@
/****************************************************************************
**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtGui module of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:LGPL21$
+** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://www.qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 or version 3 as published by the Free
-** Software Foundation and appearing in the file LICENSE.LGPLv21 and
-** LICENSE.LGPLv3 included in the packaging of this file. Please review the
-** following information to ensure the GNU Lesser General Public License
-** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
-** As a special exception, The Qt Company gives you certain additional
-** rights. These rights are described in The Qt Company LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp
index 4390e46fde..d01f0d640b 100644
--- a/src/gui/image/qimagereader.cpp
+++ b/src/gui/image/qimagereader.cpp
@@ -190,32 +190,42 @@ enum _qt_BuiltInFormatType {
_qt_NoFormat = -1
};
+#if !defined(QT_NO_IMAGEFORMAT_PPM)
+# define MAX_MT_SIZE 20
+#elif !defined(QT_NO_IMAGEFORMAT_XBM) || !defined(QT_NO_IMAGEFORMAT_XPM)
+# define MAX_MT_SIZE 10
+#else
+# define MAX_MT_SIZE 4
+#endif
+
struct _qt_BuiltInFormatStruct
{
- const char *extension;
- const char *mimeType;
+ char extension[4];
+ char mimeType[MAX_MT_SIZE];
};
+#undef MAX_MT_SIZE
+
static const _qt_BuiltInFormatStruct _qt_BuiltInFormats[] = {
#ifndef QT_NO_IMAGEFORMAT_PNG
- {"png", "image/png"},
+ {"png", "png"},
#endif
#ifndef QT_NO_IMAGEFORMAT_BMP
- {"bmp", "image/bmp"},
+ {"bmp", "bmp"},
#endif
#ifndef QT_NO_IMAGEFORMAT_PPM
- {"ppm", "image/x-portable-pixmap"},
- {"pgm", "image/x-portable-graymap"},
- {"pbm", "image/x-portable-bitmap"},
+ {"ppm", "x-portable-pixmap"},
+ {"pgm", "x-portable-graymap"},
+ {"pbm", "x-portable-bitmap"},
#endif
#ifndef QT_NO_IMAGEFORMAT_XBM
- {"xbm", "image/x-xbitmap"},
+ {"xbm", "x-xbitmap"},
#endif
#ifndef QT_NO_IMAGEFORMAT_XPM
- {"xpm", "image/x-xpixmap"},
+ {"xpm", "x-xpixmap"},
#endif
- {"", ""}
};
+Q_STATIC_ASSERT(_qt_NumFormats == sizeof _qt_BuiltInFormats / sizeof *_qt_BuiltInFormats);
static QImageIOHandler *createReadHandlerHelper(QIODevice *device,
const QByteArray &format,
@@ -461,7 +471,8 @@ static QImageIOHandler *createReadHandlerHelper(QIODevice *device,
--numFormats;
++currentFormat;
- currentFormat %= _qt_NumFormats;
+ if (currentFormat >= _qt_NumFormats)
+ currentFormat = 0;
}
}
@@ -1604,8 +1615,8 @@ QList<QByteArray> QImageReader::supportedMimeTypes()
{
QList<QByteArray> mimeTypes;
mimeTypes.reserve(_qt_NumFormats);
- for (int i = 0; i < _qt_NumFormats; ++i)
- mimeTypes << _qt_BuiltInFormats[i].mimeType;
+ for (const auto &fmt : _qt_BuiltInFormats)
+ mimeTypes.append(QByteArrayLiteral("image/") + fmt.mimeType);
#ifndef QT_NO_IMAGEFORMATPLUGIN
supportedImageHandlerMimeTypes(loader(), QImageIOPlugin::CanRead, &mimeTypes);
diff --git a/src/gui/image/qimagewriter.cpp b/src/gui/image/qimagewriter.cpp
index f3af2738af..ab15d8ee29 100644
--- a/src/gui/image/qimagewriter.cpp
+++ b/src/gui/image/qimagewriter.cpp
@@ -283,8 +283,13 @@ bool QImageWriterPrivate::canWriteHelper()
errorString = QImageWriter::tr("Device is not set");
return false;
}
- if (!device->isOpen())
- device->open(QIODevice::WriteOnly);
+ if (!device->isOpen()) {
+ if (!device->open(QIODevice::WriteOnly)) {
+ imageWriterError = QImageWriter::DeviceError;
+ errorString = QImageWriter::tr("Cannot open device for writing: %1").arg(device->errorString());
+ return false;
+ }
+ }
if (!device->isWritable()) {
imageWriterError = QImageWriter::DeviceError;
errorString = QImageWriter::tr("Device not writable");
@@ -705,6 +710,11 @@ bool QImageWriter::canWrite() const
if (QFile *file = qobject_cast<QFile *>(d->device)) {
const bool remove = !file->isOpen() && !file->exists();
const bool result = d->canWriteHelper();
+
+ // This looks strange (why remove if it doesn't exist?) but the issue
+ // here is that canWriteHelper will create the file in the process of
+ // checking if the write can succeed. If it subsequently fails, we
+ // should remove that empty file.
if (!result && remove)
file->remove();
return result;
diff --git a/src/gui/image/qpixmap_raster.cpp b/src/gui/image/qpixmap_raster.cpp
index 741f7713da..7eb61adb2d 100644
--- a/src/gui/image/qpixmap_raster.cpp
+++ b/src/gui/image/qpixmap_raster.cpp
@@ -51,7 +51,6 @@
#include <QImageReader>
#include <QGuiApplication>
#include <QScreen>
-#include <private/qimage_p.h>
#include <private/qsimd_p.h>
#include <private/qdrawhelper_p.h>
#include <qpa/qplatformscreen.h>
@@ -135,7 +134,7 @@ bool QRasterPlatformPixmap::fromData(const uchar *buffer, uint len, const char *
if (image.isNull())
return false;
- createPixmapForImage(image, flags, /* inplace = */true);
+ createPixmapForImage(std::move(image), flags);
return !isNull();
}
@@ -143,13 +142,13 @@ void QRasterPlatformPixmap::fromImage(const QImage &sourceImage,
Qt::ImageConversionFlags flags)
{
QImage image = sourceImage;
- createPixmapForImage(image, flags, /* inplace = */false);
+ createPixmapForImage(std::move(image), flags);
}
void QRasterPlatformPixmap::fromImageInPlace(QImage &sourceImage,
Qt::ImageConversionFlags flags)
{
- createPixmapForImage(sourceImage, flags, /* inplace = */true);
+ createPixmapForImage(std::move(sourceImage), flags);
}
void QRasterPlatformPixmap::fromImageReader(QImageReader *imageReader,
@@ -160,7 +159,7 @@ void QRasterPlatformPixmap::fromImageReader(QImageReader *imageReader,
if (image.isNull())
return;
- createPixmapForImage(image, flags, /* inplace = */true);
+ createPixmapForImage(std::move(image), flags);
}
// from qbackingstore.cpp
@@ -301,7 +300,7 @@ int QRasterPlatformPixmap::metric(QPaintDevice::PaintDeviceMetric metric) const
return 0;
}
-void QRasterPlatformPixmap::createPixmapForImage(QImage &sourceImage, Qt::ImageConversionFlags flags, bool inPlace)
+void QRasterPlatformPixmap::createPixmapForImage(QImage sourceImage, Qt::ImageConversionFlags flags)
{
QImage::Format format;
if (flags & Qt::NoFormatConversion)
@@ -335,16 +334,10 @@ void QRasterPlatformPixmap::createPixmapForImage(QImage &sourceImage, Qt::ImageC
if (format == QImage::Format_RGB32 && (sourceImage.format() == QImage::Format_ARGB32
|| sourceImage.format() == QImage::Format_ARGB32_Premultiplied))
{
- inPlace = inPlace && sourceImage.isDetached();
- image = sourceImage;
- if (!inPlace)
- image.detach();
- if (image.d)
- image.d->format = QImage::Format_RGB32;
- } else if (inPlace && sourceImage.d->convertInPlace(format, flags)) {
- image = sourceImage;
+ image = std::move(sourceImage);
+ image.reinterpretAsFormat(QImage::Format_RGB32);
} else {
- image = sourceImage.convertToFormat(format, flags);
+ image = std::move(sourceImage).convertToFormat(format, flags);
}
if (image.d) {
@@ -356,8 +349,6 @@ void QRasterPlatformPixmap::createPixmapForImage(QImage &sourceImage, Qt::ImageC
}
is_null = (w <= 0 || h <= 0);
- if (image.d)
- image.d->devicePixelRatio = sourceImage.devicePixelRatio();
//ensure the pixmap and the image resulting from toImage() have the same cacheKey();
setSerialNumber(image.cacheKey() >> 32);
if (image.d)
diff --git a/src/gui/image/qpixmap_raster_p.h b/src/gui/image/qpixmap_raster_p.h
index 95e018eb35..6ea965a324 100644
--- a/src/gui/image/qpixmap_raster_p.h
+++ b/src/gui/image/qpixmap_raster_p.h
@@ -85,7 +85,7 @@ public:
protected:
int metric(QPaintDevice::PaintDeviceMetric metric) const Q_DECL_OVERRIDE;
- void createPixmapForImage(QImage &sourceImage, Qt::ImageConversionFlags flags, bool inPlace);
+ void createPixmapForImage(QImage sourceImage, Qt::ImageConversionFlags flags);
void setImage(const QImage &image);
QImage image;
static QImage::Format systemOpaqueFormat();
diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp
index d55a26b2a4..1d19c165fc 100644
--- a/src/gui/image/qpnghandler.cpp
+++ b/src/gui/image/qpnghandler.cpp
@@ -745,7 +745,7 @@ static void set_text(const QImage &image, png_structp png_ptr, png_infop info_pt
#ifdef PNG_iTXt_SUPPORTED
bool needsItxt = false;
- foreach(const QChar c, it.value()) {
+ for (const QChar c : it.value()) {
uchar ch = c.cell();
if (c.row() || (ch < 0x20 && ch != '\n') || (ch > 0x7e && ch < 0xa0)) {
needsItxt = true;
diff --git a/src/gui/image/qppmhandler.cpp b/src/gui/image/qppmhandler.cpp
index 42d3684aea..e9f5a905f0 100644
--- a/src/gui/image/qppmhandler.cpp
+++ b/src/gui/image/qppmhandler.cpp
@@ -45,6 +45,7 @@
#include <qvariant.h>
#include <qvector.h>
#include <ctype.h>
+#include <qrgba64.h>
QT_BEGIN_NAMESPACE
@@ -120,6 +121,11 @@ static bool read_pbm_header(QIODevice *device, char& type, int& w, int& h, int&
return true;
}
+static inline QRgb scale_pbm_color(quint16 mx, quint16 rv, quint16 gv, quint16 bv)
+{
+ return QRgba64::fromRgba64((rv * 0xffff) / mx, (gv * 0xffff) / mx, (bv * 0xffff) / mx, 0xffff).toArgb32();
+}
+
static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, QImage *outImage)
{
int nbits, y;
@@ -148,9 +154,6 @@ static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, Q
}
raw = type >= '4';
- int maxc = mcc;
- if (maxc > 255)
- maxc = 255;
if (outImage->size() != QSize(w, h) || outImage->format() != format) {
*outImage = QImage(w, h, format);
if (outImage->isNull())
@@ -175,22 +178,50 @@ static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, Q
b = buf24;
while (p < end) {
if (mcc < 256) {
- *p++ = qRgb(b[0],b[1],b[2]);
+ if (mcc == 255)
+ *p++ = qRgb(b[0],b[1],b[2]);
+ else
+ *p++ = scale_pbm_color(mcc, b[0], b[1], b[2]);
b += 3;
} else {
- *p++ = qRgb(((int(b[0]) * 256 + int(b[1]) + 1) * 256) / (mcc + 1) - 1,
- ((int(b[2]) * 256 + int(b[3]) + 1) * 256) / (mcc + 1) - 1,
- ((int(b[4]) * 256 + int(b[5]) + 1) * 256) / (mcc + 1) - 1);
+ quint16 rv = b[0] << 8 | b[1];
+ quint16 gv = b[2] << 8 | b[3];
+ quint16 bv = b[4] << 8 | b[5];
+ if (mcc == 0xffff)
+ *p++ = QRgba64::fromRgba64(rv, gv, bv, 0xffff).toArgb32();
+ else
+ *p++ = scale_pbm_color(mcc, rv, gv, bv);
b += 6;
}
}
}
delete[] buf24;
+ } else if (nbits == 8 && mcc > 255) { // type 5 16bit
+ pbm_bpl = 2*w;
+ uchar *buf16 = new uchar[pbm_bpl];
+ for (y=0; y<h; y++) {
+ if (device->read((char *)buf16, pbm_bpl) != pbm_bpl) {
+ delete[] buf16;
+ return false;
+ }
+ uchar *p = outImage->scanLine(y);
+ uchar *end = p + w;
+ uchar *b = buf16;
+ while (p < end) {
+ *p++ = (b[0] << 8 | b[1]) * 255 / mcc;
+ b += 2;
+ }
+ }
+ delete[] buf16;
} else { // type 4,5
for (y=0; y<h; y++) {
- if (device->read((char *)outImage->scanLine(y), pbm_bpl)
- != pbm_bpl)
+ uchar *p = outImage->scanLine(y);
+ if (device->read((char *)p, pbm_bpl) != pbm_bpl)
return false;
+ if (nbits == 8 && mcc < 255) {
+ for (int i = 0; i < pbm_bpl; i++)
+ p[i] = (p[i] * 255) / mcc;
+ }
}
}
} else { // read ascii data
@@ -227,7 +258,7 @@ static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, Q
} else { // 32 bits
n /= 4;
int r, g, b;
- if (mcc == maxc) {
+ if (mcc == 255) {
while (n--) {
r = read_pbm_int(device);
g = read_pbm_int(device);
@@ -237,10 +268,10 @@ static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, Q
}
} else {
while (n--) {
- r = read_pbm_int(device) * maxc / mcc;
- g = read_pbm_int(device) * maxc / mcc;
- b = read_pbm_int(device) * maxc / mcc;
- *((QRgb*)p) = qRgb(r, g, b);
+ r = read_pbm_int(device);
+ g = read_pbm_int(device);
+ b = read_pbm_int(device);
+ *((QRgb*)p) = scale_pbm_color(mcc, r, g, b);
p += 4;
}
}
diff --git a/src/gui/image/qxbmhandler.cpp b/src/gui/image/qxbmhandler.cpp
index 19015c5dcd..155a4f88b4 100644
--- a/src/gui/image/qxbmhandler.cpp
+++ b/src/gui/image/qxbmhandler.cpp
@@ -143,6 +143,8 @@ static bool read_xbm_body(QIODevice *device, int w, int h, QImage *outImage)
return false;
}
+ outImage->fill(Qt::color0); // in case the image data does not cover the full image
+
outImage->setColorCount(2);
outImage->setColor(0, qRgb(255,255,255)); // white
outImage->setColor(1, qRgb(0,0,0)); // black
diff --git a/src/gui/image/qxpmhandler.cpp b/src/gui/image/qxpmhandler.cpp
index 1f1f6b388f..ce7f7b8a0f 100644
--- a/src/gui/image/qxpmhandler.cpp
+++ b/src/gui/image/qxpmhandler.cpp
@@ -852,6 +852,9 @@ static bool read_xpm_header(
#endif
return false; // < 4 numbers parsed
+ if (*w <= 0 || *w > 32767 || *h <= 0 || *h > 32767 || *ncols <= 0 || *ncols > (64 * 64 * 64 * 64) || *cpp <= 0 || *cpp > 15)
+ return false; // failed sanity check
+
return true;
}