Scale BMP color samples with periodic bit expansion

Scale samples up by periodically repeating the bit pattern of each value to fill
up the lower bits instead of padding them with 0.

Vice versa truncate samples that exceed the bit depth of QRgb.

Change-Id: I777519e359932f52e54a33073a1ff297a76f620c
Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
This commit is contained in:
Martin Walch 2022-10-01 15:48:42 +02:00
parent 9f64065c1f
commit 1fee7adccf

View File

@ -135,16 +135,42 @@ static QDataStream &operator<<(QDataStream &s, const BMP_INFOHDR &bi)
return s; return s;
} }
static int calc_shift(uint mask) static uint calc_shift(uint mask)
{ {
int result = 0; uint result = 0;
while (mask && !(mask & 1)) { while ((mask >= 0x100) || (!(mask & 1) && mask)) {
result++; result++;
mask >>= 1; mask >>= 1;
} }
return result; return result;
} }
static uint calc_scale(uint low_mask)
{
uint result = 8;
while (low_mask && result) {
result--;
low_mask >>= 1;
}
return result;
}
static inline uint apply_scale(uint value, uint scale)
{
if (!(scale & 0x07)) // return immediately if scale == 8 or 0
return value;
uint filled = 8 - scale;
uint result = value << scale;
do {
result |= result >> filled;
filled <<= 1;
} while (filled < 8);
return result;
}
static bool read_dib_fileheader(QDataStream &s, BMP_FILEHDR &bf) static bool read_dib_fileheader(QDataStream &s, BMP_FILEHDR &bf)
{ {
// read BMP file header // read BMP file header
@ -207,14 +233,14 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 datapos,
uint green_mask = 0; uint green_mask = 0;
uint blue_mask = 0; uint blue_mask = 0;
uint alpha_mask = 0; uint alpha_mask = 0;
int red_shift = 0; uint red_shift = 0;
int green_shift = 0; uint green_shift = 0;
int blue_shift = 0; uint blue_shift = 0;
int alpha_shift = 0; uint alpha_shift = 0;
int red_scale = 0; uint red_scale = 0;
int green_scale = 0; uint green_scale = 0;
int blue_scale = 0; uint blue_scale = 0;
int alpha_scale = 0; uint alpha_scale = 0;
bool bitfields = comp == BMP_BITFIELDS || comp == BMP_ALPHABITFIELDS; bool bitfields = comp == BMP_BITFIELDS || comp == BMP_ALPHABITFIELDS;
if (!d->isSequential()) if (!d->isSequential())
@ -289,19 +315,19 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 datapos,
red_shift = calc_shift(red_mask); red_shift = calc_shift(red_mask);
if (((red_mask >> red_shift) + 1) == 0) if (((red_mask >> red_shift) + 1) == 0)
return false; return false;
red_scale = 256 / ((red_mask >> red_shift) + 1); red_scale = calc_scale(red_mask >> red_shift);
green_shift = calc_shift(green_mask); green_shift = calc_shift(green_mask);
if (((green_mask >> green_shift) + 1) == 0) if (((green_mask >> green_shift) + 1) == 0)
return false; return false;
green_scale = 256 / ((green_mask >> green_shift) + 1); green_scale = calc_scale(green_mask >> green_shift);
blue_shift = calc_shift(blue_mask); blue_shift = calc_shift(blue_mask);
if (((blue_mask >> blue_shift) + 1) == 0) if (((blue_mask >> blue_shift) + 1) == 0)
return false; return false;
blue_scale = 256 / ((blue_mask >> blue_shift) + 1); blue_scale = calc_scale(blue_mask >> blue_shift);
alpha_shift = calc_shift(alpha_mask); alpha_shift = calc_shift(alpha_mask);
if (((alpha_mask >> alpha_shift) + 1) == 0) if (((alpha_mask >> alpha_shift) + 1) == 0)
return false; return false;
alpha_scale = 256 / ((alpha_mask >> alpha_shift) + 1); alpha_scale = calc_scale(alpha_mask >> alpha_shift);
} else if (comp == BMP_RGB && (nbits == 24 || nbits == 32)) { } else if (comp == BMP_RGB && (nbits == 24 || nbits == 32)) {
blue_mask = 0x000000ff; blue_mask = 0x000000ff;
green_mask = 0x0000ff00; green_mask = 0x0000ff00;
@ -309,23 +335,21 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 datapos,
blue_shift = 0; blue_shift = 0;
green_shift = 8; green_shift = 8;
red_shift = 16; red_shift = 16;
blue_scale = green_scale = red_scale = 1; blue_scale = green_scale = red_scale = 0;
if (transp) { if (transp) {
alpha_shift = calc_shift(alpha_mask); alpha_shift = calc_shift(alpha_mask);
if (((alpha_mask >> alpha_shift) + 1) == 0) if (((alpha_mask >> alpha_shift) + 1) == 0)
return false; return false;
alpha_scale = 256 / ((alpha_mask >> alpha_shift) + 1); alpha_scale = calc_scale(alpha_mask >> alpha_shift);
} }
} else if (comp == BMP_RGB && nbits == 16) { } else if (comp == BMP_RGB && nbits == 16) {
blue_mask = 0x001f; blue_mask = 0x001f;
green_mask = 0x03e0; green_mask = 0x03e0;
red_mask = 0x7c00; red_mask = 0x7c00;
blue_shift = 0; blue_shift = 0;
green_shift = 2; green_shift = 5;
red_shift = 7; red_shift = 10;
red_scale = 1; blue_scale = green_scale = red_scale = 3;
green_scale = 1;
blue_scale = 8;
} }
image.setDotsPerMeterX(bi.biXPelsPerMeter); image.setDotsPerMeterX(bi.biXPelsPerMeter);
@ -533,10 +557,10 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 datapos,
c |= *(uchar*)(b+2)<<16; c |= *(uchar*)(b+2)<<16;
if (nbits > 24) if (nbits > 24)
c |= *(uchar*)(b+3)<<24; c |= *(uchar*)(b+3)<<24;
*p++ = qRgba(((c & red_mask) >> red_shift) * red_scale, *p++ = qRgba(apply_scale((c & red_mask) >> red_shift, red_scale),
((c & green_mask) >> green_shift) * green_scale, apply_scale((c & green_mask) >> green_shift, green_scale),
((c & blue_mask) >> blue_shift) * blue_scale, apply_scale((c & blue_mask) >> blue_shift, blue_scale),
transp ? ((c & alpha_mask) >> alpha_shift) * alpha_scale : 0xff); transp ? apply_scale((c & alpha_mask) >> alpha_shift, alpha_scale) : 0xff);
b += nbits/8; b += nbits/8;
} }
} }