Skip to content

Commit 3593380

Browse files
alpqrLars Knoll
authored andcommitted
Simplify mirroring code and add tests for non-aliged 1 bit images
Like it is done in Qt 5. Also add the autotest which was completely missing in Qt 4. Change-Id: Iaf89272b4e5b7f377c4b2f1ce929661f3d0edd9a Reviewed-by: Lars Knoll <[email protected]>
1 parent 4ba8bab commit 3593380

File tree

2 files changed

+225
-89
lines changed

2 files changed

+225
-89
lines changed

src/gui/image/qimage.cpp

Lines changed: 109 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -4761,18 +4761,118 @@ QImage QImage::createMaskFromColor(QRgb color, Qt::MaskMode mode) const
47614761
}
47624762

47634763

4764-
/*
4765-
This code is contributed by Philipp Lang,
4766-
GeneriCom Software Germany (www.generi.com)
4767-
under the terms of the QPL, Version 1.0
4768-
*/
4769-
47704764
/*!
47714765
\fn QImage QImage::mirror(bool horizontal, bool vertical) const
47724766
47734767
Use mirrored() instead.
47744768
*/
47754769

4770+
template<class T> inline void do_mirror_data(QImageData *dst, QImageData *src,
4771+
int dstX0, int dstY0,
4772+
int dstXIncr, int dstYIncr,
4773+
int w, int h)
4774+
{
4775+
if (dst == src) {
4776+
// When mirroring in-place, stop in the middle for one of the directions, since we
4777+
// are swapping the bytes instead of merely copying.
4778+
const int srcXEnd = dstX0 ? w / 2 : w;
4779+
const int srcYEnd = !dstX0 && dstY0 ? h / 2 : h;
4780+
for (int srcY = 0, dstY = dstY0; srcY < srcYEnd; ++srcY, dstY += dstYIncr) {
4781+
T *srcPtr = (T *) (src->data + srcY * src->bytes_per_line);
4782+
T *dstPtr = (T *) (dst->data + dstY * dst->bytes_per_line);
4783+
for (int srcX = 0, dstX = dstX0; srcX < srcXEnd; ++srcX, dstX += dstXIncr)
4784+
std::swap(srcPtr[srcX], dstPtr[dstX]);
4785+
}
4786+
} else {
4787+
for (int srcY = 0, dstY = dstY0; srcY < h; ++srcY, dstY += dstYIncr) {
4788+
T *srcPtr = (T *) (src->data + srcY * src->bytes_per_line);
4789+
T *dstPtr = (T *) (dst->data + dstY * dst->bytes_per_line);
4790+
for (int srcX = 0, dstX = dstX0; srcX < w; ++srcX, dstX += dstXIncr)
4791+
dstPtr[dstX] = srcPtr[srcX];
4792+
}
4793+
}
4794+
}
4795+
4796+
inline void do_mirror(QImageData *dst, QImageData *src, bool horizontal, bool vertical)
4797+
{
4798+
Q_ASSERT(src->width == dst->width && src->height == dst->height && src->depth == dst->depth);
4799+
int w = src->width;
4800+
int h = src->height;
4801+
int depth = src->depth;
4802+
4803+
if (src->depth == 1) {
4804+
w = (w + 7) / 8; // byte aligned width
4805+
depth = 8;
4806+
}
4807+
4808+
int dstX0 = 0, dstXIncr = 1;
4809+
int dstY0 = 0, dstYIncr = 1;
4810+
if (horizontal) {
4811+
// 0 -> w-1, 1 -> w-2, 2 -> w-3, ...
4812+
dstX0 = w - 1;
4813+
dstXIncr = -1;
4814+
}
4815+
if (vertical) {
4816+
// 0 -> h-1, 1 -> h-2, 2 -> h-3, ...
4817+
dstY0 = h - 1;
4818+
dstYIncr = -1;
4819+
}
4820+
4821+
switch (depth) {
4822+
case 32:
4823+
do_mirror_data<quint32>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
4824+
break;
4825+
case 24:
4826+
do_mirror_data<quint24>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
4827+
break;
4828+
case 16:
4829+
do_mirror_data<quint16>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
4830+
break;
4831+
case 8:
4832+
do_mirror_data<quint8>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
4833+
break;
4834+
default:
4835+
Q_ASSERT(false);
4836+
break;
4837+
}
4838+
4839+
// The bytes are now all in the correct place. In addition, the bits in the individual
4840+
// bytes have to be flipped too when horizontally mirroring a 1 bit-per-pixel image.
4841+
if (horizontal && dst->depth == 1) {
4842+
Q_ASSERT(dst->format == QImage::Format_Mono || dst->format == QImage::Format_MonoLSB);
4843+
const int shift = 8 - (dst->width % 8);
4844+
const uchar *bitflip = qt_get_bitflip_array();
4845+
for (int y = 0; y < h; ++y) {
4846+
uchar *begin = dst->data + y * dst->bytes_per_line;
4847+
uchar *end = begin + dst->bytes_per_line;
4848+
for (uchar *p = begin; p < end; ++p) {
4849+
*p = bitflip[*p];
4850+
// When the data is non-byte aligned, an extra bit shift (of the number of
4851+
// unused bits at the end) is needed for the entire scanline.
4852+
if (shift != 8 && p != begin) {
4853+
if (dst->format == QImage::Format_Mono) {
4854+
for (int i = 0; i < shift; ++i) {
4855+
p[-1] <<= 1;
4856+
p[-1] |= (*p & (128 >> i)) >> (7 - i);
4857+
}
4858+
} else {
4859+
for (int i = 0; i < shift; ++i) {
4860+
p[-1] >>= 1;
4861+
p[-1] |= (*p & (1 << i)) << (7 - i);
4862+
}
4863+
}
4864+
}
4865+
}
4866+
if (shift != 8) {
4867+
if (dst->format == QImage::Format_Mono)
4868+
end[-1] <<= shift;
4869+
else
4870+
end[-1] >>= shift;
4871+
}
4872+
}
4873+
}
4874+
}
4875+
47764876
/*!
47774877
Returns a mirror of the image, mirrored in the horizontal and/or
47784878
the vertical direction depending on whether \a horizontal and \a
@@ -4790,8 +4890,6 @@ QImage QImage::mirrored(bool horizontal, bool vertical) const
47904890
if ((d->width <= 1 && d->height <= 1) || (!horizontal && !vertical))
47914891
return *this;
47924892

4793-
int w = d->width;
4794-
int h = d->height;
47954893
// Create result image, copy colormap
47964894
QImage result(d->width, d->height, d->format);
47974895
QIMAGE_SANITYCHECK_MEMORY(result);
@@ -4802,88 +4900,10 @@ QImage QImage::mirrored(bool horizontal, bool vertical) const
48024900

48034901
result.d->colortable = d->colortable;
48044902
result.d->has_alpha_clut = d->has_alpha_clut;
4903+
result.d->dpmx = d->dpmx;
4904+
result.d->dpmy = d->dpmy;
48054905

4806-
if (depth() == 1)
4807-
w = (w+7)/8;
4808-
int dxi = horizontal ? -1 : 1;
4809-
int dxs = horizontal ? w-1 : 0;
4810-
int dyi = vertical ? -1 : 1;
4811-
int dy = vertical ? h-1: 0;
4812-
4813-
// 1 bit, 8 bit
4814-
if (d->depth == 1 || d->depth == 8) {
4815-
for (int sy = 0; sy < h; sy++, dy += dyi) {
4816-
quint8* ssl = (quint8*)(d->data + sy*d->bytes_per_line);
4817-
quint8* dsl = (quint8*)(result.d->data + dy*result.d->bytes_per_line);
4818-
int dx = dxs;
4819-
for (int sx = 0; sx < w; sx++, dx += dxi)
4820-
dsl[dx] = ssl[sx];
4821-
}
4822-
}
4823-
// 16 bit
4824-
else if (d->depth == 16) {
4825-
for (int sy = 0; sy < h; sy++, dy += dyi) {
4826-
quint16* ssl = (quint16*)(d->data + sy*d->bytes_per_line);
4827-
quint16* dsl = (quint16*)(result.d->data + dy*result.d->bytes_per_line);
4828-
int dx = dxs;
4829-
for (int sx = 0; sx < w; sx++, dx += dxi)
4830-
dsl[dx] = ssl[sx];
4831-
}
4832-
}
4833-
// 24 bit
4834-
else if (d->depth == 24) {
4835-
for (int sy = 0; sy < h; sy++, dy += dyi) {
4836-
quint24* ssl = (quint24*)(d->data + sy*d->bytes_per_line);
4837-
quint24* dsl = (quint24*)(result.d->data + dy*result.d->bytes_per_line);
4838-
int dx = dxs;
4839-
for (int sx = 0; sx < w; sx++, dx += dxi)
4840-
dsl[dx] = ssl[sx];
4841-
}
4842-
}
4843-
// 32 bit
4844-
else if (d->depth == 32) {
4845-
for (int sy = 0; sy < h; sy++, dy += dyi) {
4846-
quint32* ssl = (quint32*)(d->data + sy*d->bytes_per_line);
4847-
quint32* dsl = (quint32*)(result.d->data + dy*result.d->bytes_per_line);
4848-
int dx = dxs;
4849-
for (int sx = 0; sx < w; sx++, dx += dxi)
4850-
dsl[dx] = ssl[sx];
4851-
}
4852-
}
4853-
4854-
// special handling of 1 bit images for horizontal mirroring
4855-
if (horizontal && d->depth == 1) {
4856-
int shift = width() % 8;
4857-
for (int y = h-1; y >= 0; y--) {
4858-
quint8* a0 = (quint8*)(result.d->data + y*d->bytes_per_line);
4859-
// Swap bytes
4860-
quint8* a = a0+dxs;
4861-
while (a >= a0) {
4862-
*a = bitflip[*a];
4863-
a--;
4864-
}
4865-
// Shift bits if unaligned
4866-
if (shift != 0) {
4867-
a = a0+dxs;
4868-
quint8 c = 0;
4869-
if (format() == Format_MonoLSB) {
4870-
while (a >= a0) {
4871-
quint8 nc = *a << shift;
4872-
*a = (*a >> (8-shift)) | c;
4873-
--a;
4874-
c = nc;
4875-
}
4876-
} else {
4877-
while (a >= a0) {
4878-
quint8 nc = *a >> shift;
4879-
*a = (*a << (8-shift)) | c;
4880-
--a;
4881-
c = nc;
4882-
}
4883-
}
4884-
}
4885-
}
4886-
}
4906+
do_mirror(result.d, d, horizontal, vertical);
48874907

48884908
return result;
48894909
}

tests/auto/qimage/tst_qimage.cpp

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ private slots:
150150
void rgbSwapped_data();
151151
void rgbSwapped();
152152

153+
void mirrored_data();
154+
void mirrored();
155+
153156
void deepCopyWhenPaintingActive();
154157
void scaled_QTBUG19157();
155158
};
@@ -2016,6 +2019,119 @@ void tst_QImage::rgbSwapped()
20162019
QCOMPARE(memcmp(image.constBits(), imageSwappedTwice.constBits(), image.numBytes()), 0);
20172020
}
20182021

2022+
void tst_QImage::mirrored_data()
2023+
{
2024+
QTest::addColumn<QImage::Format>("format");
2025+
QTest::addColumn<bool>("swap_vertical");
2026+
QTest::addColumn<bool>("swap_horizontal");
2027+
QTest::addColumn<int>("width");
2028+
QTest::addColumn<int>("height");
2029+
2030+
QTest::newRow("Format_RGB32, vertical") << QImage::Format_RGB32 << true << false << 16 << 16;
2031+
QTest::newRow("Format_ARGB32, vertical") << QImage::Format_ARGB32 << true << false << 16 << 16;
2032+
QTest::newRow("Format_ARGB32_Premultiplied, vertical") << QImage::Format_ARGB32_Premultiplied << true << false << 16 << 16;
2033+
QTest::newRow("Format_RGB16, vertical") << QImage::Format_RGB16 << true << false << 16 << 16;
2034+
QTest::newRow("Format_ARGB8565_Premultiplied, vertical") << QImage::Format_ARGB8565_Premultiplied << true << false << 16 << 16;
2035+
QTest::newRow("Format_ARGB6666_Premultiplied, vertical") << QImage::Format_ARGB6666_Premultiplied << true << false << 16 << 16;
2036+
QTest::newRow("Format_ARGB4444_Premultiplied, vertical") << QImage::Format_ARGB4444_Premultiplied << true << false << 16 << 16;
2037+
QTest::newRow("Format_RGB666, vertical") << QImage::Format_RGB666 << true << false << 16 << 16;
2038+
QTest::newRow("Format_RGB555, vertical") << QImage::Format_RGB555 << true << false << 16 << 16;
2039+
QTest::newRow("Format_ARGB8555_Premultiplied, vertical") << QImage::Format_ARGB8555_Premultiplied << true << false << 16 << 16;
2040+
QTest::newRow("Format_RGB888, vertical") << QImage::Format_RGB888 << true << false << 16 << 16;
2041+
QTest::newRow("Format_RGB444, vertical") << QImage::Format_RGB444 << true << false << 16 << 16;
2042+
QTest::newRow("Format_Indexed8, vertical") << QImage::Format_Indexed8 << true << false << 16 << 16;
2043+
QTest::newRow("Format_Mono, vertical") << QImage::Format_Mono << true << false << 16 << 16;
2044+
QTest::newRow("Format_MonoLSB, vertical") << QImage::Format_MonoLSB << true << false << 16 << 16;
2045+
2046+
QTest::newRow("Format_ARGB32_Premultiplied, horizontal") << QImage::Format_ARGB32_Premultiplied << false << true << 16 << 16;
2047+
QTest::newRow("Format_RGB888, horizontal") << QImage::Format_RGB888 << false << true << 16 << 16;
2048+
QTest::newRow("Format_RGB16, horizontal") << QImage::Format_RGB16 << false << true << 16 << 16;
2049+
QTest::newRow("Format_Indexed8, horizontal") << QImage::Format_Indexed8 << false << true << 16 << 16;
2050+
QTest::newRow("Format_Mono, horizontal") << QImage::Format_Mono << false << true << 16 << 16;
2051+
QTest::newRow("Format_MonoLSB, horizontal") << QImage::Format_MonoLSB << false << true << 16 << 16;
2052+
2053+
QTest::newRow("Format_ARGB32_Premultiplied, horizontal+vertical") << QImage::Format_ARGB32_Premultiplied << true << true << 16 << 16;
2054+
QTest::newRow("Format_RGB888, horizontal+vertical") << QImage::Format_RGB888 << true << true << 16 << 16;
2055+
QTest::newRow("Format_RGB16, horizontal+vertical") << QImage::Format_RGB16 << true << true << 16 << 16;
2056+
QTest::newRow("Format_Indexed8, horizontal+vertical") << QImage::Format_Indexed8 << true << true << 16 << 16;
2057+
QTest::newRow("Format_Mono, horizontal+vertical") << QImage::Format_Mono << true << true << 16 << 16;
2058+
QTest::newRow("Format_MonoLSB, horizontal+vertical") << QImage::Format_MonoLSB << true << true << 16 << 16;
2059+
2060+
QTest::newRow("Format_RGB32, vertical") << QImage::Format_RGB32 << true << false << 8 << 16;
2061+
QTest::newRow("Format_ARGB32, vertical") << QImage::Format_ARGB32 << true << false << 16 << 8;
2062+
QTest::newRow("Format_Mono, vertical, non-aligned") << QImage::Format_Mono << true << false << 19 << 25;
2063+
QTest::newRow("Format_MonoLSB, vertical, non-aligned") << QImage::Format_MonoLSB << true << false << 19 << 25;
2064+
2065+
// Non-aligned horizontal 1-bit needs special handling so test this.
2066+
QTest::newRow("Format_Mono, horizontal, non-aligned") << QImage::Format_Mono << false << true << 13 << 17;
2067+
QTest::newRow("Format_Mono, horizontal, non-aligned") << QImage::Format_Mono << false << true << 19 << 25;
2068+
QTest::newRow("Format_Mono, horizontal+vertical, non-aligned") << QImage::Format_Mono << true << true << 25 << 47;
2069+
QTest::newRow("Format_Mono, horizontal+vertical, non-aligned") << QImage::Format_Mono << true << true << 21 << 16;
2070+
2071+
QTest::newRow("Format_MonoLSB, horizontal, non-aligned") << QImage::Format_MonoLSB << false << true << 13 << 17;
2072+
QTest::newRow("Format_MonoLSB, horizontal, non-aligned") << QImage::Format_MonoLSB << false << true << 19 << 25;
2073+
QTest::newRow("Format_MonoLSB, horizontal+vertical, non-aligned") << QImage::Format_MonoLSB << true << true << 25 << 47;
2074+
QTest::newRow("Format_MonoLSB, horizontal+vertical, non-aligned") << QImage::Format_MonoLSB << true << true << 21 << 16;
2075+
}
2076+
2077+
void tst_QImage::mirrored()
2078+
{
2079+
QFETCH(QImage::Format, format);
2080+
QFETCH(bool, swap_vertical);
2081+
QFETCH(bool, swap_horizontal);
2082+
QFETCH(int, width);
2083+
QFETCH(int, height);
2084+
2085+
QImage image(width, height, format);
2086+
2087+
switch (format) {
2088+
case QImage::Format_Mono:
2089+
case QImage::Format_MonoLSB:
2090+
for (int i = 0; i < image.height(); ++i) {
2091+
ushort* scanLine = (ushort*)image.scanLine(i);
2092+
*scanLine = (i % 2) ? 0x5555U : 0xCCCCU;
2093+
}
2094+
break;
2095+
case QImage::Format_Indexed8:
2096+
for (int i = 0; i < image.height(); ++i) {
2097+
for (int j = 0; j < image.width(); ++j) {
2098+
image.setColor(i*16+j, qRgb(j*16, i*16, 0));
2099+
image.setPixel(j, i, i*16+j);
2100+
}
2101+
}
2102+
break;
2103+
default:
2104+
for (int i = 0; i < image.height(); ++i)
2105+
for (int j = 0; j < image.width(); ++j)
2106+
image.setPixel(j, i, qRgb(j*16, i*16, 0));
2107+
break;
2108+
}
2109+
2110+
QImage imageMirrored = image.mirrored(swap_horizontal, swap_vertical);
2111+
2112+
for (int i = 0; i < image.height(); ++i) {
2113+
int mirroredI = swap_vertical ? (image.height() - i - 1) : i;
2114+
for (int j = 0; j < image.width(); ++j) {
2115+
QRgb referenceColor = image.pixel(j, i);
2116+
int mirroredJ = swap_horizontal ? (image.width() - j - 1) : j;
2117+
QRgb mirroredColor = imageMirrored.pixel(mirroredJ, mirroredI);
2118+
QCOMPARE(mirroredColor, referenceColor);
2119+
}
2120+
}
2121+
2122+
QImage imageMirroredTwice = imageMirrored.mirrored(swap_horizontal, swap_vertical);
2123+
2124+
QCOMPARE(image, imageMirroredTwice);
2125+
2126+
if (format != QImage::Format_Mono && format != QImage::Format_MonoLSB)
2127+
QCOMPARE(memcmp(image.constBits(), imageMirroredTwice.constBits(), image.byteCount()), 0);
2128+
else {
2129+
for (int i = 0; i < image.height(); ++i)
2130+
for (int j = 0; j < image.width(); ++j)
2131+
QCOMPARE(image.pixel(j,i), imageMirroredTwice.pixel(j,i));
2132+
}
2133+
}
2134+
20192135
void tst_QImage::deepCopyWhenPaintingActive()
20202136
{
20212137
QImage image(64, 64, QImage::Format_ARGB32_Premultiplied);

0 commit comments

Comments
 (0)