From b28764c641e4fef19d5e800b7537439fb2054914 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Fri, 3 Jan 2014 14:46:07 +0100 Subject: 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 --- src/gui/image/image.pri | 3 + src/gui/image/qimage.cpp | 338 +++++++++++++++++++---------- src/gui/image/qimage.h | 15 ++ src/gui/image/qimage_compat.cpp | 62 ++++++ tests/auto/gui/image/qimage/qimage.pro | 1 + tests/auto/gui/image/qimage/tst_qimage.cpp | 251 +++++++++++++++++++++ 6 files changed, 560 insertions(+), 110 deletions(-) create mode 100644 src/gui/image/qimage_compat.cpp diff --git a/src/gui/image/image.pri b/src/gui/image/image.pri index 5b9bc4b56f..fce7c6f4ec 100644 --- a/src/gui/image/image.pri +++ b/src/gui/image/image.pri @@ -54,6 +54,9 @@ SOURCES += \ 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 HEADERS += \ image/qbmphandler_p.h \ diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 61d524841d..27068020cd 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -1105,8 +1105,7 @@ void QImage::detach() if (d->ref.load() != 1 || d->ro_data) *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 the vertical direction depending on whether \a horizontal and \a 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} */ -QImage QImage::mirrored(bool horizontal, bool vertical) const + +template +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 +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) 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->devicePixelRatio = d->devicePixelRatio; - if (depth() == 1) + 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 dy = vertical ? h-1: 0; + int dys = vertical ? h-1 : 0; // 1 bit, 8 bit - if (d->depth == 1 || d->depth == 8) { - for (int sy = 0; sy < h; sy++, dy += dyi) { - 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]; - } - } + if (d->depth == 1 || d->depth == 8) + mirrored_helper_loop(w, h, dxi, dxs, dyi, dys, d->data, result.d->data, d->bytes_per_line, result.d->bytes_per_line); // 16 bit - else if (d->depth == 16) { - for (int sy = 0; sy < h; sy++, dy += dyi) { - 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]; - } - } + else if (d->depth == 16) + mirrored_helper_loop(w, h, dxi, dxs, dyi, dys, d->data, result.d->data, d->bytes_per_line, result.d->bytes_per_line); // 24 bit - else if (d->depth == 24) { - for (int sy = 0; sy < h; sy++, dy += dyi) { - 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]; - } - } + else if (d->depth == 24) + mirrored_helper_loop(w, h, dxi, dxs, dyi, dys, d->data, result.d->data, d->bytes_per_line, result.d->bytes_per_line); // 32 bit - else if (d->depth == 32) { - for (int sy = 0; sy < h; sy++, dy += dyi) { - 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]; - } - } + else if (d->depth == 32) + mirrored_helper_loop(w, h, dxi, dxs, dyi, dys, d->data, result.d->data, d->bytes_per_line, result.d->bytes_per_line); // special handling of 1 bit images for horizontal mirroring - if (horizontal && d->depth == 1) { - int shift = width() % 8; - 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; - } - } - } - } - } - + if (horizontal && d->depth == 1) + mirror_horizonal_bitmap(d->width, d->height, dxs, result.d->data, result.d->bytes_per_line, d->format == Format_MonoLSB); 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(w, h, dxi, dxs, dyi, dys, d->data, d->bytes_per_line); + // 16 bit + else if (d->depth == 16) + mirrored_helper_loop_inplace(w, h, dxi, dxs, dyi, dys, d->data, d->bytes_per_line); + // 24 bit + else if (d->depth == 24) + mirrored_helper_loop_inplace(w, h, dxi, dxs, dyi, dys, d->data, d->bytes_per_line); + // 32 bit + else if (d->depth == 32) + mirrored_helper_loop_inplace(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 components of all pixels have been swapped, effectively converting an RGB image to an BGR image. @@ -4915,16 +4965,54 @@ QImage QImage::mirrored(bool horizontal, bool vertical) const \sa {QImage#Image Transformations}{Image Transformations} */ -QImage QImage::rgbSwapped() const + +inline void rgbSwapped_generic(int width, int height, const QImage *src, QImage *dst, const QPixelLayout* layout) +{ + Q_ASSERT(layout->redWidth == layout->blueWidth); + FetchPixelsFunc fetch = qFetchPixels[layout->bpp]; + StorePixelsFunc store = qStorePixels[layout->bpp]; + + const uint redBlueMask = (1 << layout->redWidth) - 1; + const uint alphaGreenMask = (((1 << layout->alphaWidth) - 1) << layout->alphaShift) + | (((1 << layout->greenWidth) - 1) << layout->greenShift); + + const int buffer_size = 2048; + uint buffer[buffer_size]; + for (int i = 0; i < height; ++i) { + uchar *q = dst->scanLine(i); + const uchar *p = src->constScanLine(i); + int x = 0; + while (x < width) { + int l = qMin(width - x, buffer_size); + const uint *ptr = fetch(buffer, p, x, l); + for (int j = 0; j < l; ++j) { + uint red = (ptr[j] >> layout->redShift) & redBlueMask; + uint blue = (ptr[j] >> layout->blueShift) & redBlueMask; + buffer[j] = (ptr[j] & alphaGreenMask) + | (red << layout->blueShift) + | (blue << layout->redShift); + } + store(q, buffer, 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); - return res; + break; case Format_Mono: case Format_MonoLSB: case Format_Indexed8: @@ -4933,7 +5021,7 @@ QImage QImage::rgbSwapped() const QRgb c = res.d->colortable.at(i); res.d->colortable[i] = QRgb(((c << 16) & 0xff0000) | ((c >> 16) & 0xff) | (c & 0xff00ff00)); } - return res; + break; case Format_RGB32: case Format_ARGB32: case Format_ARGB32_Premultiplied: @@ -4946,15 +5034,16 @@ QImage QImage::rgbSwapped() const 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; + const uint *p = (const uint*)constScanLine(i); + const uint *end = p + d->width; while (p < end) { - *q = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00); + uint c = *p; + *q = ((c << 16) & 0xff0000) | ((c >> 16) & 0xff) | (c & 0xff00ff00); p++; q++; } } - return res; + break; case Format_RGB16: res = QImage(d->width, d->height, d->format); QIMAGE_SANITYCHECK_MEMORY(res); @@ -4963,48 +5052,77 @@ QImage QImage::rgbSwapped() const 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); + ushort c = *p; + *q = ((c << 11) & 0xf800) | ((c >> 11) & 0x1f) | (c & 0x07e0); p++; q++; } } - return res; + break; default: + res = QImage(d->width, d->height, d->format); + rgbSwapped_generic(d->width, d->height, this, &res, &qPixelLayouts[d->format]); break; } + return res; +} - res = QImage(d->width, d->height, d->format); - QIMAGE_SANITYCHECK_MEMORY(res); - const QPixelLayout *layout = &qPixelLayouts[d->format]; - Q_ASSERT(layout->redWidth == layout->blueWidth); - FetchPixelsFunc fetch = qFetchPixels[layout->bpp]; - StorePixelsFunc store = qStorePixels[layout->bpp]; +/*! + \internal +*/ +void QImage::rgbSwapped_inplace() +{ + if (isNull()) + return; - const uint redBlueMask = (1 << layout->redWidth) - 1; - const uint alphaGreenMask = (((1 << layout->alphaWidth) - 1) << layout->alphaShift) - | (((1 << layout->greenWidth) - 1) << layout->greenShift); + detach(); - const int buffer_size = 2048; - uint buffer[buffer_size]; - for (int i = 0; i < d->height; ++i) { - uchar *q = res.scanLine(i); - const uchar *p = constScanLine(i); - int x = 0; - while (x < d->width) { - int l = qMin(d->width - x, buffer_size); - const uint *ptr = fetch(buffer, p, x, l); - for (int j = 0; j < l; ++j) { - uint red = (ptr[j] >> layout->redShift) & redBlueMask; - uint blue = (ptr[j] >> layout->blueShift) & redBlueMask; - buffer[j] = (ptr[j] & alphaGreenMask) - | (red << layout->blueShift) - | (blue << layout->redShift); + 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++; } - store(q, buffer, x, l); - x += l; } + 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; } - return res; } /*! diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h index bc7f3729ad..e8f195c18e 100644 --- a/src/gui/image/qimage.h +++ b/src/gui/image/qimage.h @@ -245,8 +245,19 @@ public: static QMatrix trueMatrix(const QMatrix &, int w, int h); QImage transformed(const QTransform &matrix, Qt::TransformationMode mode = Qt::FastTransformation) const; 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 rgbSwapped() const; +#endif void invertPixels(InvertMode = InvertRgb); @@ -298,6 +309,10 @@ public: protected: 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: friend class QWSOnScreenSurface; diff --git a/src/gui/image/qimage_compat.cpp b/src/gui/image/qimage_compat.cpp new file mode 100644 index 0000000000..9886d392fb --- /dev/null +++ b/src/gui/image/qimage_compat.cpp @@ -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 diff --git a/tests/auto/gui/image/qimage/qimage.pro b/tests/auto/gui/image/qimage/qimage.pro index 467a59ec9f..117e34653d 100644 --- a/tests/auto/gui/image/qimage/qimage.pro +++ b/tests/auto/gui/image/qimage/qimage.pro @@ -4,5 +4,6 @@ TARGET = tst_qimage SOURCES += tst_qimage.cpp QT += core-private gui-private testlib +contains(QT_CONFIG, c++11): CONFIG += c++11 TESTDATA += images/* diff --git a/tests/auto/gui/image/qimage/tst_qimage.cpp b/tests/auto/gui/image/qimage/tst_qimage.cpp index 7fdb2f4cba..5016abff4f 100644 --- a/tests/auto/gui/image/qimage/tst_qimage.cpp +++ b/tests/auto/gui/image/qimage/tst_qimage.cpp @@ -149,6 +149,17 @@ private slots: void rgbSwapped_data(); void rgbSwapped(); + void mirrored_data(); + void mirrored(); + + void inplaceRgbSwapped_data(); + void inplaceRgbSwapped(); + + void inplaceMirrored_data(); + void inplaceMirrored(); + + void inplaceDoubleConversion(); + void deepCopyWhenPaintingActive(); void scaled_QTBUG19157(); @@ -2106,6 +2117,246 @@ void tst_QImage::rgbSwapped() QCOMPARE(memcmp(image.constBits(), imageSwappedTwice.constBits(), image.byteCount()), 0); } +void tst_QImage::mirrored_data() +{ + QTest::addColumn("format"); + QTest::addColumn("swap_vertical"); + QTest::addColumn("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("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 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("format"); + QTest::addColumn("swap_vertical"); + QTest::addColumn("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() { QImage image(64, 64, QImage::Format_ARGB32_Premultiplied); -- cgit v1.2.3