Simplify mirroring code and add tests for non-aliged 1 bit images
Change-Id: I309714bc52de87c702194a4a82803d383f6ac3b3 Reviewed-by: Lars Knoll <lars.knoll@digia.com>
This commit is contained in:
parent
1e54f1316d
commit
a5f3df04af
@ -2697,13 +2697,6 @@ QImage QImage::createMaskFromColor(QRgb color, Qt::MaskMode mode) const
|
||||
return maskImage;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
This code is contributed by Philipp Lang,
|
||||
GeneriCom Software Germany (www.generi.com)
|
||||
under the terms of the QPL, Version 1.0
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QImage QImage::mirrored(bool horizontal = false, bool vertical = true) const
|
||||
Returns a mirror of the image, mirrored in the horizontal and/or
|
||||
@ -2715,61 +2708,108 @@ QImage QImage::createMaskFromColor(QRgb color, Qt::MaskMode mode) const
|
||||
\sa {QImage#Image Transformations}{Image Transformations}
|
||||
*/
|
||||
|
||||
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)
|
||||
template<class T> inline void do_mirror_data(QImageData *dst, QImageData *src,
|
||||
int dstX0, int dstY0,
|
||||
int dstXIncr, int dstYIncr,
|
||||
int w, int h)
|
||||
{
|
||||
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;
|
||||
const uchar* bitflip = qt_get_bitflip_array();
|
||||
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--;
|
||||
if (dst == src) {
|
||||
// When mirroring in-place, stop in the middle for one of the directions, since we
|
||||
// are swapping the bytes instead of merely copying.
|
||||
const int srcXEnd = dstX0 ? w / 2 : w;
|
||||
const int srcYEnd = !dstX0 && dstY0 ? h / 2 : h;
|
||||
for (int srcY = 0, dstY = dstY0; srcY < srcYEnd; ++srcY, dstY += dstYIncr) {
|
||||
T *srcPtr = (T *) (src->data + srcY * src->bytes_per_line);
|
||||
T *dstPtr = (T *) (dst->data + dstY * dst->bytes_per_line);
|
||||
for (int srcX = 0, dstX = dstX0; srcX < srcXEnd; ++srcX, dstX += dstXIncr)
|
||||
std::swap(srcPtr[srcX], dstPtr[dstX]);
|
||||
}
|
||||
// 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;
|
||||
} else {
|
||||
for (int srcY = 0, dstY = dstY0; srcY < h; ++srcY, dstY += dstYIncr) {
|
||||
T *srcPtr = (T *) (src->data + srcY * src->bytes_per_line);
|
||||
T *dstPtr = (T *) (dst->data + dstY * dst->bytes_per_line);
|
||||
for (int srcX = 0, dstX = dstX0; srcX < w; ++srcX, dstX += dstXIncr)
|
||||
dstPtr[dstX] = srcPtr[srcX];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void do_mirror(QImageData *dst, QImageData *src, bool horizontal, bool vertical)
|
||||
{
|
||||
Q_ASSERT(src->width == dst->width && src->height == dst->height && src->depth == dst->depth);
|
||||
int w = src->width;
|
||||
int h = src->height;
|
||||
int depth = src->depth;
|
||||
|
||||
if (src->depth == 1) {
|
||||
w = (w + 7) / 8; // byte aligned width
|
||||
depth = 8;
|
||||
}
|
||||
|
||||
int dstX0 = 0, dstXIncr = 1;
|
||||
int dstY0 = 0, dstYIncr = 1;
|
||||
if (horizontal) {
|
||||
// 0 -> w-1, 1 -> w-2, 2 -> w-3, ...
|
||||
dstX0 = w - 1;
|
||||
dstXIncr = -1;
|
||||
}
|
||||
if (vertical) {
|
||||
// 0 -> h-1, 1 -> h-2, 2 -> h-3, ...
|
||||
dstY0 = h - 1;
|
||||
dstYIncr = -1;
|
||||
}
|
||||
|
||||
switch (depth) {
|
||||
case 32:
|
||||
do_mirror_data<quint32>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
|
||||
break;
|
||||
case 24:
|
||||
do_mirror_data<quint24>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
|
||||
break;
|
||||
case 16:
|
||||
do_mirror_data<quint16>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
|
||||
break;
|
||||
case 8:
|
||||
do_mirror_data<quint8>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
|
||||
break;
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
|
||||
// The bytes are now all in the correct place. In addition, the bits in the individual
|
||||
// bytes have to be flipped too when horizontally mirroring a 1 bit-per-pixel image.
|
||||
if (horizontal && dst->depth == 1) {
|
||||
Q_ASSERT(dst->format == QImage::Format_Mono || dst->format == QImage::Format_MonoLSB);
|
||||
const int shift = 8 - (dst->width % 8);
|
||||
const uchar *bitflip = qt_get_bitflip_array();
|
||||
for (int y = 0; y < h; ++y) {
|
||||
uchar *begin = dst->data + y * dst->bytes_per_line;
|
||||
uchar *end = begin + dst->bytes_per_line;
|
||||
for (uchar *p = begin; p < end; ++p) {
|
||||
*p = bitflip[*p];
|
||||
// When the data is non-byte aligned, an extra bit shift (of the number of
|
||||
// unused bits at the end) is needed for the entire scanline.
|
||||
if (shift != 8 && p != begin) {
|
||||
if (dst->format == QImage::Format_Mono) {
|
||||
for (int i = 0; i < shift; ++i) {
|
||||
p[-1] <<= 1;
|
||||
p[-1] |= (*p & (128 >> i)) >> (7 - i);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < shift; ++i) {
|
||||
p[-1] >>= 1;
|
||||
p[-1] |= (*p & (1 << i)) << (7 - i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (shift != 8) {
|
||||
if (dst->format == QImage::Format_Mono)
|
||||
end[-1] <<= shift;
|
||||
else
|
||||
end[-1] >>= shift;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2785,8 +2825,6 @@ QImage QImage::mirrored_helper(bool horizontal, bool vertical) const
|
||||
if ((d->width <= 1 && d->height <= 1) || (!horizontal && !vertical))
|
||||
return *this;
|
||||
|
||||
int w = d->width;
|
||||
int h = d->height;
|
||||
// Create result image, copy colormap
|
||||
QImage result(d->width, d->height, d->format);
|
||||
QIMAGE_SANITYCHECK_MEMORY(result);
|
||||
@ -2799,29 +2837,8 @@ QImage QImage::mirrored_helper(bool horizontal, bool vertical) const
|
||||
result.d->has_alpha_clut = d->has_alpha_clut;
|
||||
result.d->devicePixelRatio = d->devicePixelRatio;
|
||||
|
||||
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;
|
||||
do_mirror(result.d, d, horizontal, vertical);
|
||||
|
||||
// 1 bit, 8 bit
|
||||
if (d->depth == 1 || d->depth == 8)
|
||||
mirrored_helper_loop<quint8>(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)
|
||||
mirrored_helper_loop<quint16>(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)
|
||||
mirrored_helper_loop<quint24>(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)
|
||||
mirrored_helper_loop<quint32>(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)
|
||||
mirror_horizonal_bitmap(d->width, d->height, dxs, result.d->data, result.d->bytes_per_line, d->format == Format_MonoLSB);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -2830,45 +2847,12 @@ QImage QImage::mirrored_helper(bool horizontal, bool vertical) const
|
||||
*/
|
||||
void QImage::mirrored_inplace(bool horizontal, bool vertical)
|
||||
{
|
||||
if (!d)
|
||||
return;
|
||||
|
||||
if ((d->width <= 1 && d->height <= 1) || (!horizontal && !vertical))
|
||||
if (!d || (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);
|
||||
do_mirror(d, d, horizontal, vertical);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -2163,35 +2163,56 @@ void tst_QImage::mirrored_data()
|
||||
QTest::addColumn<QImage::Format>("format");
|
||||
QTest::addColumn<bool>("swap_vertical");
|
||||
QTest::addColumn<bool>("swap_horizontal");
|
||||
QTest::addColumn<int>("width");
|
||||
QTest::addColumn<int>("height");
|
||||
|
||||
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_RGB32, vertical") << QImage::Format_RGB32 << true << false << 16 << 16;
|
||||
QTest::newRow("Format_ARGB32, vertical") << QImage::Format_ARGB32 << true << false << 16 << 16;
|
||||
QTest::newRow("Format_ARGB32_Premultiplied, vertical") << QImage::Format_ARGB32_Premultiplied << true << false << 16 << 16;
|
||||
QTest::newRow("Format_RGB16, vertical") << QImage::Format_RGB16 << true << false << 16 << 16;
|
||||
QTest::newRow("Format_ARGB8565_Premultiplied, vertical") << QImage::Format_ARGB8565_Premultiplied << true << false << 16 << 16;
|
||||
QTest::newRow("Format_ARGB6666_Premultiplied, vertical") << QImage::Format_ARGB6666_Premultiplied << true << false << 16 << 16;
|
||||
QTest::newRow("Format_ARGB4444_Premultiplied, vertical") << QImage::Format_ARGB4444_Premultiplied << true << false << 16 << 16;
|
||||
QTest::newRow("Format_RGB666, vertical") << QImage::Format_RGB666 << true << false << 16 << 16;
|
||||
QTest::newRow("Format_RGB555, vertical") << QImage::Format_RGB555 << true << false << 16 << 16;
|
||||
QTest::newRow("Format_ARGB8555_Premultiplied, vertical") << QImage::Format_ARGB8555_Premultiplied << true << false << 16 << 16;
|
||||
QTest::newRow("Format_RGB888, vertical") << QImage::Format_RGB888 << true << false << 16 << 16;
|
||||
QTest::newRow("Format_RGB444, vertical") << QImage::Format_RGB444 << true << false << 16 << 16;
|
||||
QTest::newRow("Format_RGBX8888, vertical") << QImage::Format_RGBX8888 << true << false << 16 << 16;
|
||||
QTest::newRow("Format_RGBA8888_Premultiplied, vertical") << QImage::Format_RGBA8888_Premultiplied << true << false << 16 << 16;
|
||||
QTest::newRow("Format_Indexed8, vertical") << QImage::Format_Indexed8 << true << false << 16 << 16;
|
||||
QTest::newRow("Format_Mono, vertical") << QImage::Format_Mono << true << false << 16 << 16;
|
||||
QTest::newRow("Format_MonoLSB, vertical") << QImage::Format_MonoLSB << true << false << 16 << 16;
|
||||
|
||||
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") << QImage::Format_ARGB32_Premultiplied << false << true << 16 << 16;
|
||||
QTest::newRow("Format_RGB888, horizontal") << QImage::Format_RGB888 << false << true << 16 << 16;
|
||||
QTest::newRow("Format_RGB16, horizontal") << QImage::Format_RGB16 << false << true << 16 << 16;
|
||||
QTest::newRow("Format_Indexed8, horizontal") << QImage::Format_Indexed8 << false << true << 16 << 16;
|
||||
QTest::newRow("Format_Mono, horizontal") << QImage::Format_Mono << false << true << 16 << 16;
|
||||
QTest::newRow("Format_MonoLSB, horizontal") << QImage::Format_MonoLSB << false << true << 16 << 16;
|
||||
|
||||
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;
|
||||
QTest::newRow("Format_ARGB32_Premultiplied, horizontal+vertical") << QImage::Format_ARGB32_Premultiplied << true << true << 16 << 16;
|
||||
QTest::newRow("Format_RGB888, horizontal+vertical") << QImage::Format_RGB888 << true << true << 16 << 16;
|
||||
QTest::newRow("Format_RGB16, horizontal+vertical") << QImage::Format_RGB16 << true << true << 16 << 16;
|
||||
QTest::newRow("Format_Indexed8, horizontal+vertical") << QImage::Format_Indexed8 << true << true << 16 << 16;
|
||||
QTest::newRow("Format_Mono, horizontal+vertical") << QImage::Format_Mono << true << true << 16 << 16;
|
||||
QTest::newRow("Format_MonoLSB, horizontal+vertical") << QImage::Format_MonoLSB << true << true << 16 << 16;
|
||||
|
||||
QTest::newRow("Format_RGB32, vertical") << QImage::Format_RGB32 << true << false << 8 << 16;
|
||||
QTest::newRow("Format_ARGB32, vertical") << QImage::Format_ARGB32 << true << false << 16 << 8;
|
||||
QTest::newRow("Format_Mono, vertical, non-aligned") << QImage::Format_Mono << true << false << 19 << 25;
|
||||
QTest::newRow("Format_MonoLSB, vertical, non-aligned") << QImage::Format_MonoLSB << true << false << 19 << 25;
|
||||
|
||||
// Non-aligned horizontal 1-bit needs special handling so test this.
|
||||
QTest::newRow("Format_Mono, horizontal, non-aligned") << QImage::Format_Mono << false << true << 13 << 17;
|
||||
QTest::newRow("Format_Mono, horizontal, non-aligned") << QImage::Format_Mono << false << true << 19 << 25;
|
||||
QTest::newRow("Format_Mono, horizontal+vertical, non-aligned") << QImage::Format_Mono << true << true << 25 << 47;
|
||||
QTest::newRow("Format_Mono, horizontal+vertical, non-aligned") << QImage::Format_Mono << true << true << 21 << 16;
|
||||
|
||||
QTest::newRow("Format_MonoLSB, horizontal, non-aligned") << QImage::Format_MonoLSB << false << true << 13 << 17;
|
||||
QTest::newRow("Format_MonoLSB, horizontal, non-aligned") << QImage::Format_MonoLSB << false << true << 19 << 25;
|
||||
QTest::newRow("Format_MonoLSB, horizontal+vertical, non-aligned") << QImage::Format_MonoLSB << true << true << 25 << 47;
|
||||
QTest::newRow("Format_MonoLSB, horizontal+vertical, non-aligned") << QImage::Format_MonoLSB << true << true << 21 << 16;
|
||||
}
|
||||
|
||||
void tst_QImage::mirrored()
|
||||
@ -2199,11 +2220,14 @@ void tst_QImage::mirrored()
|
||||
QFETCH(QImage::Format, format);
|
||||
QFETCH(bool, swap_vertical);
|
||||
QFETCH(bool, swap_horizontal);
|
||||
QFETCH(int, width);
|
||||
QFETCH(int, height);
|
||||
|
||||
QImage image(16, 16, format);
|
||||
QImage image(width, height, format);
|
||||
|
||||
switch (format) {
|
||||
case QImage::Format_Mono:
|
||||
case QImage::Format_MonoLSB:
|
||||
for (int i = 0; i < image.height(); ++i) {
|
||||
ushort* scanLine = (ushort*)image.scanLine(i);
|
||||
*scanLine = (i % 2) ? 0x5555U : 0xCCCCU;
|
||||
@ -2240,7 +2264,7 @@ void tst_QImage::mirrored()
|
||||
|
||||
QCOMPARE(image, imageMirroredTwice);
|
||||
|
||||
if (format != QImage::Format_Mono)
|
||||
if (format != QImage::Format_Mono && format != QImage::Format_MonoLSB)
|
||||
QCOMPARE(memcmp(image.constBits(), imageMirroredTwice.constBits(), image.byteCount()), 0);
|
||||
else {
|
||||
for (int i = 0; i < image.height(); ++i)
|
||||
|
Loading…
Reference in New Issue
Block a user