Inplace versions of QImage rgbSwapped() and mirrored() for rvalue refs

Adds inplace version of QImage::rgbSwapped() and QImage::mirrored() that
can be used on temporary QImage objects when supported by the compiler.

[ChangeLog][QtGui][QImage]Rvalue qualified mirrored and rgbSwapped methods for inline conversions
Change-Id: I4ffb658bf620dfc472d9db14c1aa70291c1fd842
Reviewed-by: Gunnar Sletta <gunnar.sletta@jollamobile.com>
This commit is contained in:
Allan Sandfeld Jensen 2014-01-03 14:46:07 +01:00 committed by The Qt Project
parent af9910e450
commit b28764c641
6 changed files with 589 additions and 139 deletions

View File

@ -54,6 +54,9 @@ SOURCES += \
win32:!winrt: SOURCES += image/qpixmap_win.cpp win32:!winrt: SOURCES += image/qpixmap_win.cpp
NO_PCH_SOURCES += image/qimage_compat.cpp
false: SOURCES += $$NO_PCH_SOURCES # Hack for QtCreator
# Built-in image format support # Built-in image format support
HEADERS += \ HEADERS += \
image/qbmphandler_p.h \ image/qbmphandler_p.h \

View File

@ -1105,8 +1105,7 @@ void QImage::detach()
if (d->ref.load() != 1 || d->ro_data) if (d->ref.load() != 1 || d->ro_data)
*this = copy(); *this = copy();
if (d) ++d->detach_no;
++d->detach_no;
} }
} }
@ -4791,6 +4790,7 @@ QImage QImage::createMaskFromColor(QRgb color, Qt::MaskMode mode) const
*/ */
/*! /*!
\fn QImage QImage::mirrored(bool horizontal = false, bool vertical = true) const
Returns a mirror of the image, mirrored in the horizontal and/or Returns a mirror of the image, mirrored in the horizontal and/or
the vertical direction depending on whether \a horizontal and \a the vertical direction depending on whether \a horizontal and \a
vertical are set to true or false. vertical are set to true or false.
@ -4799,7 +4799,69 @@ QImage QImage::createMaskFromColor(QRgb color, Qt::MaskMode mode) const
\sa {QImage#Image Transformations}{Image Transformations} \sa {QImage#Image Transformations}{Image Transformations}
*/ */
QImage QImage::mirrored(bool horizontal, bool vertical) const
template<typename T>
inline void mirrored_helper_loop(int w, int h, int dxi, int dxs, int dyi, int dy, const uchar* sdata, uchar* ddata, int sbpl, int dbpl)
{
for (int sy = 0; sy < h; sy++, dy += dyi) {
const T* ssl = (T*)(sdata + sy*sbpl);
T* dsl = (T*)(ddata + dy*dbpl);
int dx = dxs;
for (int sx = 0; sx < w; sx++, dx += dxi)
dsl[dx] = ssl[sx];
}
}
template<typename T>
inline void mirrored_helper_loop_inplace(int w, int h, int dxi, int dxs, int dyi, int dy, uchar* sdata, int sbpl)
{
for (int sy = 0; sy < h; sy++, dy += dyi) {
T* ssl = (T*)(sdata + sy*sbpl);
T* dsl = (T*)(sdata + dy*sbpl);
int dx = dxs;
for (int sx = 0; sx < w; sx++, dx += dxi)
std::swap(dsl[dx], ssl[sx]);
}
}
inline void mirror_horizonal_bitmap(int w, int h, int dxs, uchar* data, int bpl, bool monolsb)
{
int shift = w % 8;
for (int y = h-1; y >= 0; y--) {
quint8* a0 = (quint8*)(data + y*bpl);
// Swap bytes
quint8* a = a0+dxs;
while (a >= a0) {
*a = bitflip[*a];
a--;
}
// Shift bits if unaligned
if (shift != 0) {
a = a0+dxs;
quint8 c = 0;
if (monolsb) {
while (a >= a0) {
quint8 nc = *a << shift;
*a = (*a >> (8-shift)) | c;
--a;
c = nc;
}
} else {
while (a >= a0) {
quint8 nc = *a >> shift;
*a = (*a << (8-shift)) | c;
--a;
c = nc;
}
}
}
}
}
/*!
\internal
*/
QImage QImage::mirrored_helper(bool horizontal, bool vertical) const
{ {
if (!d) if (!d)
return QImage(); return QImage();
@ -4821,92 +4883,80 @@ QImage QImage::mirrored(bool horizontal, bool vertical) const
result.d->has_alpha_clut = d->has_alpha_clut; result.d->has_alpha_clut = d->has_alpha_clut;
result.d->devicePixelRatio = d->devicePixelRatio; result.d->devicePixelRatio = d->devicePixelRatio;
if (depth() == 1) if (d->depth == 1)
w = (w+7)/8; w = (w+7)/8;
int dxi = horizontal ? -1 : 1; int dxi = horizontal ? -1 : 1;
int dxs = horizontal ? w-1 : 0; int dxs = horizontal ? w-1 : 0;
int dyi = vertical ? -1 : 1; int dyi = vertical ? -1 : 1;
int dy = vertical ? h-1: 0; int dys = vertical ? h-1 : 0;
// 1 bit, 8 bit // 1 bit, 8 bit
if (d->depth == 1 || d->depth == 8) { if (d->depth == 1 || d->depth == 8)
for (int sy = 0; sy < h; sy++, dy += dyi) { mirrored_helper_loop<quint8>(w, h, dxi, dxs, dyi, dys, d->data, result.d->data, d->bytes_per_line, result.d->bytes_per_line);
quint8* ssl = (quint8*)(d->data + sy*d->bytes_per_line);
quint8* dsl = (quint8*)(result.d->data + dy*result.d->bytes_per_line);
int dx = dxs;
for (int sx = 0; sx < w; sx++, dx += dxi)
dsl[dx] = ssl[sx];
}
}
// 16 bit // 16 bit
else if (d->depth == 16) { else if (d->depth == 16)
for (int sy = 0; sy < h; sy++, dy += dyi) { mirrored_helper_loop<quint16>(w, h, dxi, dxs, dyi, dys, d->data, result.d->data, d->bytes_per_line, result.d->bytes_per_line);
quint16* ssl = (quint16*)(d->data + sy*d->bytes_per_line);
quint16* dsl = (quint16*)(result.d->data + dy*result.d->bytes_per_line);
int dx = dxs;
for (int sx = 0; sx < w; sx++, dx += dxi)
dsl[dx] = ssl[sx];
}
}
// 24 bit // 24 bit
else if (d->depth == 24) { else if (d->depth == 24)
for (int sy = 0; sy < h; sy++, dy += dyi) { mirrored_helper_loop<quint24>(w, h, dxi, dxs, dyi, dys, d->data, result.d->data, d->bytes_per_line, result.d->bytes_per_line);
quint24* ssl = (quint24*)(d->data + sy*d->bytes_per_line);
quint24* dsl = (quint24*)(result.d->data + dy*result.d->bytes_per_line);
int dx = dxs;
for (int sx = 0; sx < w; sx++, dx += dxi)
dsl[dx] = ssl[sx];
}
}
// 32 bit // 32 bit
else if (d->depth == 32) { else if (d->depth == 32)
for (int sy = 0; sy < h; sy++, dy += dyi) { mirrored_helper_loop<quint32>(w, h, dxi, dxs, dyi, dys, d->data, result.d->data, d->bytes_per_line, result.d->bytes_per_line);
quint32* ssl = (quint32*)(d->data + sy*d->bytes_per_line);
quint32* dsl = (quint32*)(result.d->data + dy*result.d->bytes_per_line);
int dx = dxs;
for (int sx = 0; sx < w; sx++, dx += dxi)
dsl[dx] = ssl[sx];
}
}
// special handling of 1 bit images for horizontal mirroring // special handling of 1 bit images for horizontal mirroring
if (horizontal && d->depth == 1) { if (horizontal && d->depth == 1)
int shift = width() % 8; mirror_horizonal_bitmap(d->width, d->height, dxs, result.d->data, result.d->bytes_per_line, d->format == Format_MonoLSB);
for (int y = h-1; y >= 0; y--) {
quint8* a0 = (quint8*)(result.d->data + y*d->bytes_per_line);
// Swap bytes
quint8* a = a0+dxs;
while (a >= a0) {
*a = bitflip[*a];
a--;
}
// Shift bits if unaligned
if (shift != 0) {
a = a0+dxs;
quint8 c = 0;
if (format() == Format_MonoLSB) {
while (a >= a0) {
quint8 nc = *a << shift;
*a = (*a >> (8-shift)) | c;
--a;
c = nc;
}
} else {
while (a >= a0) {
quint8 nc = *a >> shift;
*a = (*a << (8-shift)) | c;
--a;
c = nc;
}
}
}
}
}
return result; return result;
} }
/*! /*!
\internal
*/
void QImage::mirrored_inplace(bool horizontal, bool vertical)
{
if (!d)
return;
if ((d->width <= 1 && d->height <= 1) || (!horizontal && !vertical))
return;
detach();
int w = d->width;
int h = d->height;
if (d->depth == 1)
w = (w+7)/8;
int dxi = horizontal ? -1 : 1;
int dxs = horizontal ? w-1 : 0;
int dyi = vertical ? -1 : 1;
int dys = vertical ? h-1 : 0;
if (vertical)
h = h/2;
else if (horizontal)
w = w/2;
// 1 bit, 8 bit
if (d->depth == 1 || d->depth == 8)
mirrored_helper_loop_inplace<quint8>(w, h, dxi, dxs, dyi, dys, d->data, d->bytes_per_line);
// 16 bit
else if (d->depth == 16)
mirrored_helper_loop_inplace<quint16>(w, h, dxi, dxs, dyi, dys, d->data, d->bytes_per_line);
// 24 bit
else if (d->depth == 24)
mirrored_helper_loop_inplace<quint24>(w, h, dxi, dxs, dyi, dys, d->data, d->bytes_per_line);
// 32 bit
else if (d->depth == 32)
mirrored_helper_loop_inplace<quint32>(w, h, dxi, dxs, dyi, dys, d->data, d->bytes_per_line);
// special handling of 1 bit images for horizontal mirroring
if (horizontal && d->depth == 1)
mirror_horizonal_bitmap(d->width, d->height, dxs, d->data, d->bytes_per_line, d->format == Format_MonoLSB);
}
/*!
\fn QImage QImage::rgbSwapped() const
Returns a QImage in which the values of the red and blue Returns a QImage in which the values of the red and blue
components of all pixels have been swapped, effectively converting components of all pixels have been swapped, effectively converting
an RGB image to an BGR image. an RGB image to an BGR image.
@ -4915,67 +4965,9 @@ QImage QImage::mirrored(bool horizontal, bool vertical) const
\sa {QImage#Image Transformations}{Image Transformations} \sa {QImage#Image Transformations}{Image Transformations}
*/ */
QImage QImage::rgbSwapped() const
{
if (isNull())
return *this;
QImage res;
switch (d->format) {
case Format_Invalid:
case NImageFormats:
Q_ASSERT(false);
return res;
case Format_Mono:
case Format_MonoLSB:
case Format_Indexed8:
res = copy();
for (int i = 0; i < res.d->colortable.size(); i++) {
QRgb c = res.d->colortable.at(i);
res.d->colortable[i] = QRgb(((c << 16) & 0xff0000) | ((c >> 16) & 0xff) | (c & 0xff00ff00));
}
return res;
case Format_RGB32:
case Format_ARGB32:
case Format_ARGB32_Premultiplied:
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
case Format_RGBX8888:
case Format_RGBA8888:
case Format_RGBA8888_Premultiplied:
#endif
res = QImage(d->width, d->height, d->format);
QIMAGE_SANITYCHECK_MEMORY(res);
for (int i = 0; i < d->height; i++) {
uint *q = (uint*)res.scanLine(i);
uint *p = (uint*)constScanLine(i);
uint *end = p + d->width;
while (p < end) {
*q = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00);
p++;
q++;
}
}
return res;
case Format_RGB16:
res = QImage(d->width, d->height, d->format);
QIMAGE_SANITYCHECK_MEMORY(res);
for (int i = 0; i < d->height; i++) {
ushort *q = (ushort*)res.scanLine(i);
const ushort *p = (const ushort*)constScanLine(i);
const ushort *end = p + d->width;
while (p < end) {
*q = ((*p << 11) & 0xf800) | ((*p >> 11) & 0x1f) | (*p & 0x07e0);
p++;
q++;
}
}
return res;
default:
break;
}
res = QImage(d->width, d->height, d->format); inline void rgbSwapped_generic(int width, int height, const QImage *src, QImage *dst, const QPixelLayout* layout)
QIMAGE_SANITYCHECK_MEMORY(res); {
const QPixelLayout *layout = &qPixelLayouts[d->format];
Q_ASSERT(layout->redWidth == layout->blueWidth); Q_ASSERT(layout->redWidth == layout->blueWidth);
FetchPixelsFunc fetch = qFetchPixels[layout->bpp]; FetchPixelsFunc fetch = qFetchPixels[layout->bpp];
StorePixelsFunc store = qStorePixels[layout->bpp]; StorePixelsFunc store = qStorePixels[layout->bpp];
@ -4986,12 +4978,12 @@ QImage QImage::rgbSwapped() const
const int buffer_size = 2048; const int buffer_size = 2048;
uint buffer[buffer_size]; uint buffer[buffer_size];
for (int i = 0; i < d->height; ++i) { for (int i = 0; i < height; ++i) {
uchar *q = res.scanLine(i); uchar *q = dst->scanLine(i);
const uchar *p = constScanLine(i); const uchar *p = src->constScanLine(i);
int x = 0; int x = 0;
while (x < d->width) { while (x < width) {
int l = qMin(d->width - x, buffer_size); int l = qMin(width - x, buffer_size);
const uint *ptr = fetch(buffer, p, x, l); const uint *ptr = fetch(buffer, p, x, l);
for (int j = 0; j < l; ++j) { for (int j = 0; j < l; ++j) {
uint red = (ptr[j] >> layout->redShift) & redBlueMask; uint red = (ptr[j] >> layout->redShift) & redBlueMask;
@ -5004,9 +4996,135 @@ QImage QImage::rgbSwapped() const
x += l; x += l;
} }
} }
}
/*!
\internal
*/
QImage QImage::rgbSwapped_helper() const
{
if (isNull())
return *this;
QImage res;
switch (d->format) {
case Format_Invalid:
case NImageFormats:
Q_ASSERT(false);
break;
case Format_Mono:
case Format_MonoLSB:
case Format_Indexed8:
res = copy();
for (int i = 0; i < res.d->colortable.size(); i++) {
QRgb c = res.d->colortable.at(i);
res.d->colortable[i] = QRgb(((c << 16) & 0xff0000) | ((c >> 16) & 0xff) | (c & 0xff00ff00));
}
break;
case Format_RGB32:
case Format_ARGB32:
case Format_ARGB32_Premultiplied:
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
case Format_RGBX8888:
case Format_RGBA8888:
case Format_RGBA8888_Premultiplied:
#endif
res = QImage(d->width, d->height, d->format);
QIMAGE_SANITYCHECK_MEMORY(res);
for (int i = 0; i < d->height; i++) {
uint *q = (uint*)res.scanLine(i);
const uint *p = (const uint*)constScanLine(i);
const uint *end = p + d->width;
while (p < end) {
uint c = *p;
*q = ((c << 16) & 0xff0000) | ((c >> 16) & 0xff) | (c & 0xff00ff00);
p++;
q++;
}
}
break;
case Format_RGB16:
res = QImage(d->width, d->height, d->format);
QIMAGE_SANITYCHECK_MEMORY(res);
for (int i = 0; i < d->height; i++) {
ushort *q = (ushort*)res.scanLine(i);
const ushort *p = (const ushort*)constScanLine(i);
const ushort *end = p + d->width;
while (p < end) {
ushort c = *p;
*q = ((c << 11) & 0xf800) | ((c >> 11) & 0x1f) | (c & 0x07e0);
p++;
q++;
}
}
break;
default:
res = QImage(d->width, d->height, d->format);
rgbSwapped_generic(d->width, d->height, this, &res, &qPixelLayouts[d->format]);
break;
}
return res; return res;
} }
/*!
\internal
*/
void QImage::rgbSwapped_inplace()
{
if (isNull())
return;
detach();
switch (d->format) {
case Format_Invalid:
case NImageFormats:
Q_ASSERT(false);
break;
case Format_Mono:
case Format_MonoLSB:
case Format_Indexed8:
for (int i = 0; i < d->colortable.size(); i++) {
QRgb c = d->colortable.at(i);
d->colortable[i] = QRgb(((c << 16) & 0xff0000) | ((c >> 16) & 0xff) | (c & 0xff00ff00));
}
break;
case Format_RGB32:
case Format_ARGB32:
case Format_ARGB32_Premultiplied:
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
case Format_RGBX8888:
case Format_RGBA8888:
case Format_RGBA8888_Premultiplied:
#endif
for (int i = 0; i < d->height; i++) {
uint *p = (uint*)scanLine(i);
uint *end = p + d->width;
while (p < end) {
uint c = *p;
*p = ((c << 16) & 0xff0000) | ((c >> 16) & 0xff) | (c & 0xff00ff00);
p++;
}
}
break;
case Format_RGB16:
for (int i = 0; i < d->height; i++) {
ushort *p = (ushort*)scanLine(i);
ushort *end = p + d->width;
while (p < end) {
ushort c = *p;
*p = ((c << 11) & 0xf800) | ((c >> 11) & 0x1f) | (c & 0x07e0);
p++;
}
}
break;
default:
rgbSwapped_generic(d->width, d->height, this, this, &qPixelLayouts[d->format]);
break;
}
}
/*! /*!
Loads an image from the file with the given \a fileName. Returns \c true if Loads an image from the file with the given \a fileName. Returns \c true if
the image was successfully loaded; otherwise invalidates the image the image was successfully loaded; otherwise invalidates the image

View File

@ -245,8 +245,19 @@ public:
static QMatrix trueMatrix(const QMatrix &, int w, int h); static QMatrix trueMatrix(const QMatrix &, int w, int h);
QImage transformed(const QTransform &matrix, Qt::TransformationMode mode = Qt::FastTransformation) const; QImage transformed(const QTransform &matrix, Qt::TransformationMode mode = Qt::FastTransformation) const;
static QTransform trueMatrix(const QTransform &, int w, int h); static QTransform trueMatrix(const QTransform &, int w, int h);
#if defined(Q_COMPILER_REF_QUALIFIERS) && !defined(QT_COMPILING_QIMAGE_COMPAT_CPP)
QImage mirrored(bool horizontally = false, bool vertically = true) const &
{ return mirrored_helper(horizontally, vertically); }
QImage &&mirrored(bool horizontally = false, bool vertically = true) &&
{ mirrored_inplace(horizontally, vertically); return qMove(*this); }
QImage rgbSwapped() const &
{ return rgbSwapped_helper(); }
QImage &&rgbSwapped() &&
{ rgbSwapped_inplace(); return qMove(*this); }
#else
QImage mirrored(bool horizontally = false, bool vertically = true) const; QImage mirrored(bool horizontally = false, bool vertically = true) const;
QImage rgbSwapped() const; QImage rgbSwapped() const;
#endif
void invertPixels(InvertMode = InvertRgb); void invertPixels(InvertMode = InvertRgb);
@ -298,6 +309,10 @@ public:
protected: protected:
virtual int metric(PaintDeviceMetric metric) const; virtual int metric(PaintDeviceMetric metric) const;
QImage mirrored_helper(bool horizontal, bool vertical) const;
QImage rgbSwapped_helper() const;
void mirrored_inplace(bool horizontal, bool vertical);
void rgbSwapped_inplace();
private: private:
friend class QWSOnScreenSurface; friend class QWSOnScreenSurface;

View File

@ -0,0 +1,62 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $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 Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/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 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt 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 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifdef QIMAGE_H
# error "This file cannot be used with precompiled headers"
#endif
#define QT_COMPILING_QIMAGE_COMPAT_CPP
#include "qimage.h"
QT_BEGIN_NAMESPACE
// These implementations must be the same as the inline versions in qimage.h
QImage QImage::mirrored(bool horizontally, bool vertically) const
{
return mirrored_helper(horizontally, vertically);
}
QImage QImage::rgbSwapped() const
{
return rgbSwapped_helper();
}
QT_END_NAMESPACE

View File

@ -4,5 +4,6 @@ TARGET = tst_qimage
SOURCES += tst_qimage.cpp SOURCES += tst_qimage.cpp
QT += core-private gui-private testlib QT += core-private gui-private testlib
contains(QT_CONFIG, c++11): CONFIG += c++11
TESTDATA += images/* TESTDATA += images/*

View File

@ -149,6 +149,17 @@ private slots:
void rgbSwapped_data(); void rgbSwapped_data();
void rgbSwapped(); void rgbSwapped();
void mirrored_data();
void mirrored();
void inplaceRgbSwapped_data();
void inplaceRgbSwapped();
void inplaceMirrored_data();
void inplaceMirrored();
void inplaceDoubleConversion();
void deepCopyWhenPaintingActive(); void deepCopyWhenPaintingActive();
void scaled_QTBUG19157(); void scaled_QTBUG19157();
@ -2106,6 +2117,246 @@ void tst_QImage::rgbSwapped()
QCOMPARE(memcmp(image.constBits(), imageSwappedTwice.constBits(), image.byteCount()), 0); QCOMPARE(memcmp(image.constBits(), imageSwappedTwice.constBits(), image.byteCount()), 0);
} }
void tst_QImage::mirrored_data()
{
QTest::addColumn<QImage::Format>("format");
QTest::addColumn<bool>("swap_vertical");
QTest::addColumn<bool>("swap_horizontal");
QTest::newRow("Format_RGB32, vertical") << QImage::Format_RGB32 << true << false;
QTest::newRow("Format_ARGB32, vertical") << QImage::Format_ARGB32 << true << false;
QTest::newRow("Format_ARGB32_Premultiplied, vertical") << QImage::Format_ARGB32_Premultiplied << true << false;
QTest::newRow("Format_RGB16, vertical") << QImage::Format_RGB16 << true << false;
QTest::newRow("Format_ARGB8565_Premultiplied, vertical") << QImage::Format_ARGB8565_Premultiplied << true << false;
QTest::newRow("Format_ARGB6666_Premultiplied, vertical") << QImage::Format_ARGB6666_Premultiplied << true << false;
QTest::newRow("Format_ARGB4444_Premultiplied, vertical") << QImage::Format_ARGB4444_Premultiplied << true << false;
QTest::newRow("Format_RGB666, vertical") << QImage::Format_RGB666 << true << false;
QTest::newRow("Format_RGB555, vertical") << QImage::Format_RGB555 << true << false;
QTest::newRow("Format_ARGB8555_Premultiplied, vertical") << QImage::Format_ARGB8555_Premultiplied << true << false;
QTest::newRow("Format_RGB888, vertical") << QImage::Format_RGB888 << true << false;
QTest::newRow("Format_RGB444, vertical") << QImage::Format_RGB444 << true << false;
QTest::newRow("Format_RGBX8888, vertical") << QImage::Format_RGBX8888 << true << false;
QTest::newRow("Format_RGBA8888_Premultiplied, vertical") << QImage::Format_RGBA8888_Premultiplied << true << false;
QTest::newRow("Format_Indexed8, vertical") << QImage::Format_Indexed8 << true << false;
QTest::newRow("Format_Mono, vertical") << QImage::Format_Mono << true << false;
QTest::newRow("Format_ARGB32_Premultiplied, horizontal") << QImage::Format_ARGB32_Premultiplied << false << true;
QTest::newRow("Format_RGB888, horizontal") << QImage::Format_RGB888 << false << true;
QTest::newRow("Format_RGB16, horizontal") << QImage::Format_RGB16 << false << true;
QTest::newRow("Format_Indexed8, horizontal") << QImage::Format_Indexed8 << false << true;
QTest::newRow("Format_Mono, horizontal") << QImage::Format_Mono << false << true;
QTest::newRow("Format_ARGB32_Premultiplied, horizontal+vertical") << QImage::Format_ARGB32_Premultiplied << true << true;
QTest::newRow("Format_RGB888, horizontal+vertical") << QImage::Format_RGB888 << true << true;
QTest::newRow("Format_RGB16, horizontal+vertical") << QImage::Format_RGB16 << true << true;
QTest::newRow("Format_Indexed8, horizontal+vertical") << QImage::Format_Indexed8 << true << true;
QTest::newRow("Format_Mono, horizontal+vertical") << QImage::Format_Mono << true << true;
}
void tst_QImage::mirrored()
{
QFETCH(QImage::Format, format);
QFETCH(bool, swap_vertical);
QFETCH(bool, swap_horizontal);
QImage image(16, 16, format);
switch (format) {
case QImage::Format_Mono:
for (int i = 0; i < image.height(); ++i) {
ushort* scanLine = (ushort*)image.scanLine(i);
*scanLine = (i % 2) ? 0x5555U : 0xCCCCU;
}
break;
case QImage::Format_Indexed8:
for (int i = 0; i < image.height(); ++i) {
for (int j = 0; j < image.width(); ++j) {
image.setColor(i*16+j, qRgb(j*16, i*16, 0));
image.setPixel(j, i, i*16+j);
}
}
break;
default:
for (int i = 0; i < image.height(); ++i)
for (int j = 0; j < image.width(); ++j)
image.setPixel(j, i, qRgb(j*16, i*16, 0));
break;
}
QImage imageMirrored = image.mirrored(swap_horizontal, swap_vertical);
for (int i = 0; i < image.height(); ++i) {
int mirroredI = swap_vertical ? (image.height() - i - 1) : i;
for (int j = 0; j < image.width(); ++j) {
QRgb referenceColor = image.pixel(j, i);
int mirroredJ = swap_horizontal ? (image.width() - j - 1) : j;
QRgb mirroredColor = imageMirrored.pixel(mirroredJ, mirroredI);
QCOMPARE(mirroredColor, referenceColor);
}
}
QImage imageMirroredTwice = imageMirrored.mirrored(swap_horizontal, swap_vertical);
QCOMPARE(image, imageMirroredTwice);
if (format != QImage::Format_Mono)
QCOMPARE(memcmp(image.constBits(), imageMirroredTwice.constBits(), image.byteCount()), 0);
else {
for (int i = 0; i < image.height(); ++i)
for (int j = 0; j < image.width(); ++j)
QCOMPARE(image.pixel(j,i), imageMirroredTwice.pixel(j,i));
}
}
void tst_QImage::inplaceRgbSwapped_data()
{
QTest::addColumn<QImage::Format>("format");
QTest::newRow("Format_ARGB32_Premultiplied") << QImage::Format_ARGB32_Premultiplied;
QTest::newRow("Format_RGBA8888") << QImage::Format_RGBA8888;
QTest::newRow("Format_RGB888") << QImage::Format_RGB888;
QTest::newRow("Format_RGB16") << QImage::Format_RGB16;
QTest::newRow("Format_Indexed8") << QImage::Format_Indexed8;
}
void tst_QImage::inplaceRgbSwapped()
{
#if defined(Q_COMPILER_REF_QUALIFIERS)
QFETCH(QImage::Format, format);
QImage image(64, 1, format);
image.fill(0);
QVector<QRgb> testColor(image.width());
for (int i = 0; i < image.width(); ++i)
testColor[i] = qRgb(i * 2, i * 3, 255 - i * 4);
if (format == QImage::Format_Indexed8) {
for (int i = 0; i < image.width(); ++i) {
image.setColor(i, testColor[i]);
image.setPixel(i, 0, i);
}
} else {
for (int i = 0; i < image.width(); ++i)
image.setPixel(i, 0, testColor[i]);
}
const uchar* orginalPtr = image.constScanLine(0);
QImage imageSwapped = std::move(image).rgbSwapped();
for (int i = 0; i < imageSwapped.width(); ++i) {
QRgb referenceColor = testColor[i];
QRgb swappedColor = imageSwapped.pixel(i, 0);
QCOMPARE(qRed(swappedColor) & 0xf8, qBlue(referenceColor) & 0xf8);
QCOMPARE(qGreen(swappedColor) & 0xf8, qGreen(referenceColor) & 0xf8);
QCOMPARE(qBlue(swappedColor) & 0xf8, qRed(referenceColor) & 0xf8);
}
QCOMPARE(imageSwapped.constScanLine(0), orginalPtr);
#endif
}
void tst_QImage::inplaceMirrored_data()
{
QTest::addColumn<QImage::Format>("format");
QTest::addColumn<bool>("swap_vertical");
QTest::addColumn<bool>("swap_horizontal");
QTest::newRow("Format_ARGB32, vertical") << QImage::Format_ARGB32 << true << false;
QTest::newRow("Format_RGB888, vertical") << QImage::Format_RGB888 << true << false;
QTest::newRow("Format_RGB16, vertical") << QImage::Format_RGB16 << true << false;
QTest::newRow("Format_Indexed8, vertical") << QImage::Format_Indexed8 << true << false;
QTest::newRow("Format_Mono, vertical") << QImage::Format_Mono << true << false;
QTest::newRow("Format_ARGB32, horizontal") << QImage::Format_ARGB32 << false << true;
QTest::newRow("Format_RGB888, horizontal") << QImage::Format_RGB888 << false << true;
QTest::newRow("Format_RGB16, horizontal") << QImage::Format_RGB16 << false << true;
QTest::newRow("Format_Indexed8, horizontal") << QImage::Format_Indexed8 << false << true;
QTest::newRow("Format_Mono, horizontal") << QImage::Format_Mono << false << true;
QTest::newRow("Format_ARGB32, horizontal+vertical") << QImage::Format_ARGB32 << true << true;
QTest::newRow("Format_RGB888, horizontal+vertical") << QImage::Format_RGB888 << true << true;
QTest::newRow("Format_RGB16, horizontal+vertical") << QImage::Format_RGB16 << true << true;
QTest::newRow("Format_Indexed8, horizontal+vertical") << QImage::Format_Indexed8 << true << true;
QTest::newRow("Format_Mono, horizontal+vertical") << QImage::Format_Mono << true << true;
}
void tst_QImage::inplaceMirrored()
{
#if defined(Q_COMPILER_REF_QUALIFIERS)
QFETCH(QImage::Format, format);
QFETCH(bool, swap_vertical);
QFETCH(bool, swap_horizontal);
QImage image(16, 16, format);
switch (format) {
case QImage::Format_Mono:
for (int i = 0; i < image.height(); ++i) {
ushort* scanLine = (ushort*)image.scanLine(i);
*scanLine = (i % 2) ? 0x0fffU : 0xf000U;
}
break;
case QImage::Format_Indexed8:
for (int i = 0; i < image.height(); ++i) {
for (int j = 0; j < image.width(); ++j) {
image.setColor(i*16+j, qRgb(j*16, i*16, 0));
image.setPixel(j, i, i*16+j);
}
}
break;
default:
for (int i = 0; i < image.height(); ++i)
for (int j = 0; j < image.width(); ++j)
image.setPixel(j, i, qRgb(j*16, i*16, 0));
}
const uchar* originalPtr = image.constScanLine(0);
QImage imageMirrored = std::move(image).mirrored(swap_horizontal, swap_vertical);
if (format != QImage::Format_Mono) {
for (int i = 0; i < imageMirrored.height(); ++i) {
int mirroredI = swap_vertical ? (imageMirrored.height() - i - 1) : i;
for (int j = 0; j < imageMirrored.width(); ++j) {
int mirroredJ = swap_horizontal ? (imageMirrored.width() - j - 1) : j;
QRgb mirroredColor = imageMirrored.pixel(mirroredJ, mirroredI);
QCOMPARE(qRed(mirroredColor) & 0xF8, j * 16);
QCOMPARE(qGreen(mirroredColor) & 0xF8, i * 16);
}
}
} else {
for (int i = 0; i < imageMirrored.height(); ++i) {
ushort* scanLine = (ushort*)imageMirrored.scanLine(i);
ushort expect;
if (swap_vertical && swap_horizontal)
expect = (i % 2) ? 0x000fU : 0xfff0U;
else if (swap_vertical)
expect = (i % 2) ? 0xf000U : 0x0fffU;
else
expect = (i % 2) ? 0xfff0U : 0x000fU;
QCOMPARE(*scanLine, expect);
}
}
QCOMPARE(imageMirrored.constScanLine(0), originalPtr);
#endif
}
void tst_QImage::inplaceDoubleConversion()
{
#if defined(Q_COMPILER_REF_QUALIFIERS)
QImage image1(32, 32, QImage::Format_ARGB32);
QImage image2(32, 32, QImage::Format_ARGB32);
image1.fill(0);
image2.fill(0);
const uchar* originalPtr1 = image1.constScanLine(0);
const uchar* originalPtr2 = image2.constScanLine(0);
QCOMPARE(std::move(image1).rgbSwapped().mirrored().constScanLine(0), originalPtr1);
QCOMPARE(std::move(image2).mirrored().rgbSwapped().constScanLine(0), originalPtr2);
#endif
}
void tst_QImage::deepCopyWhenPaintingActive() void tst_QImage::deepCopyWhenPaintingActive()
{ {
QImage image(64, 64, QImage::Format_ARGB32_Premultiplied); QImage image(64, 64, QImage::Format_ARGB32_Premultiplied);