mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 75077 - Interpolate interlaced PNG images instead of libpng blocky display. r=seth
This commit is contained in:
parent
a31f2d4b14
commit
713fce5b4b
@ -679,13 +679,89 @@ nsPNGDecoder::PostFullInvalidation()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
InterpolateInterlacedPNG(const int aPass, const bool aHasAlpha,
|
||||||
|
const uint32_t aWidth, const uint32_t aHeight,
|
||||||
|
uint8_t* aImageData)
|
||||||
|
{
|
||||||
|
// At this point we have a completed pass of an interlaced image in
|
||||||
|
// imageData as an array of uint8_t ARGB or XRGB pixels, optionally
|
||||||
|
// premultiplied, 4 bytes per pixel. If there are leftover partial
|
||||||
|
// blocks at the right edge or bottom of the image, we just use the
|
||||||
|
// uninterpolated pixels that libpng gave us.
|
||||||
|
//
|
||||||
|
// See Bug #75077, Interpolation of interlaced PNG
|
||||||
|
// See https://en.wikipedia.org/wiki/Bilinear_interpolation
|
||||||
|
//
|
||||||
|
// Note: this doesn't work when downscaling so we simply show
|
||||||
|
// the uninterpolated blocks that libpng gives us.
|
||||||
|
//
|
||||||
|
// Don't try to interpolate images that are less than 8 columns wide
|
||||||
|
// or 8 rows high; do only square passes (0, 2, 4)
|
||||||
|
if ((aPass != 0 && aPass != 2 && aPass != 4) || aWidth < 8 || aHeight < 8) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Block dimensions are defined by the PNG specification */
|
||||||
|
uint32_t block_width[] = { 8, 4, 4, 2, 2 };
|
||||||
|
uint32_t bw = block_width[aPass];
|
||||||
|
uint32_t bh = bw;
|
||||||
|
|
||||||
|
bool first_component = aHasAlpha ? 0: 1;
|
||||||
|
|
||||||
|
// Reduced version of the PNG_PASS_ROW_SHIFT(pass) macro in libpng/png.h
|
||||||
|
// Only works with square passes 0, 2, and 4
|
||||||
|
uint32_t divisor_shift = 3 - (aPass >> 1);
|
||||||
|
|
||||||
|
// Loop over blocks
|
||||||
|
for (uint32_t y = 0; y < aHeight - bh; y += bh) {
|
||||||
|
for (uint32_t x = 0; x < aWidth - bw; x += bw) {
|
||||||
|
// (x,y) is the top left corner of the block
|
||||||
|
// topleft is the first component of the top left pixel of the block
|
||||||
|
uint8_t* topleft = aImageData + 4 * (x + aWidth * y);
|
||||||
|
|
||||||
|
// Loop over component=[A,]R,G,B
|
||||||
|
for (uint32_t component = first_component; component < 4; component++) {
|
||||||
|
if (x == 0) {
|
||||||
|
// Interpolate ARGB along the left side of the block
|
||||||
|
uint32_t top = *(topleft + component);
|
||||||
|
uint32_t bottom = *(topleft + component + (bh * 4 * aWidth));
|
||||||
|
for (uint32_t j = 1; j < bh; j++) {
|
||||||
|
*(topleft + component + j * 4 * aWidth) =
|
||||||
|
((top * (bh - j) + bottom * j) >> divisor_shift) & 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interpolate ARGB along the right side of the block
|
||||||
|
uint32_t top = *(topleft + component + 4 * bw);
|
||||||
|
uint32_t bottom = *(topleft + component + 4 * (bw + (bh * aWidth)));
|
||||||
|
for (uint32_t j = 1; j < bh; j++) {
|
||||||
|
*(topleft + component + 4 * (bw + j * aWidth)) =
|
||||||
|
((top * (bh - j) + bottom * j) >> divisor_shift) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interpolate ARGB in the X-direction along the top edge
|
||||||
|
// and within the block
|
||||||
|
for (uint32_t j = 0; j < bh; j++) {
|
||||||
|
uint32_t left = *(topleft + component + 4 * j * aWidth);
|
||||||
|
uint32_t right = *(topleft + component + 4 * (bw + j * aWidth));
|
||||||
|
for (uint32_t i = 1; i < bw; i++) {
|
||||||
|
*(topleft + component + 4 * (i + j * aWidth)) =
|
||||||
|
((left * (bw - i) + right * i) >> divisor_shift) & 0xff;
|
||||||
|
} // i
|
||||||
|
} // j
|
||||||
|
} // component
|
||||||
|
} // x
|
||||||
|
} // y
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsPNGDecoder::row_callback(png_structp png_ptr, png_bytep new_row,
|
nsPNGDecoder::row_callback(png_structp png_ptr, png_bytep new_row,
|
||||||
png_uint_32 row_num, int pass)
|
png_uint_32 row_num, int pass)
|
||||||
{
|
{
|
||||||
/* libpng comments:
|
/* libpng comments:
|
||||||
*
|
*
|
||||||
* this function is called for every row in the image. If the
|
* This function is called for every row in the image. If the
|
||||||
* image is interlacing, and you turned on the interlace handler,
|
* image is interlacing, and you turned on the interlace handler,
|
||||||
* this function will be called for every row in every pass.
|
* this function will be called for every row in every pass.
|
||||||
* Some of these rows will not be changed from the previous pass.
|
* Some of these rows will not be changed from the previous pass.
|
||||||
@ -722,12 +798,19 @@ nsPNGDecoder::row_callback(png_structp png_ptr, png_bytep new_row,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If |new_row| is null, that indicates that this is an interlaced image and
|
bool lastRow =
|
||||||
// |row_callback| is being called for a row that hasn't changed. Ordinarily
|
row_num == static_cast<png_uint_32>(decoder->mFrameRect.height) - 1;
|
||||||
// we don't need to do anything in this case, but if we're downscaling, the
|
|
||||||
// downscaler doesn't store the rows from previous passes, so we still need to
|
if (!new_row && !decoder->mDownscaler && !lastRow) {
|
||||||
// process the row.
|
// If |new_row| is null, that indicates that this is an interlaced image
|
||||||
if (new_row || decoder->mDownscaler) {
|
// and |row_callback| is being called for a row that hasn't changed.
|
||||||
|
// Ordinarily we don't need to do anything in this case, but if we're
|
||||||
|
// downscaling, the downscaler doesn't store the rows from previous passes,
|
||||||
|
// so we still need to process the row. If |lastRow| is true we need
|
||||||
|
// to finish the interlace pass.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int32_t width = decoder->mFrameRect.width;
|
int32_t width = decoder->mFrameRect.width;
|
||||||
uint32_t iwidth = decoder->mFrameRect.width;
|
uint32_t iwidth = decoder->mFrameRect.width;
|
||||||
|
|
||||||
@ -811,9 +894,17 @@ nsPNGDecoder::row_callback(png_structp png_ptr, png_bytep new_row,
|
|||||||
if (!decoder->interlacebuf) {
|
if (!decoder->interlacebuf) {
|
||||||
// Do line-by-line partial invalidations for non-interlaced images.
|
// Do line-by-line partial invalidations for non-interlaced images.
|
||||||
decoder->PostPartialInvalidation(IntRect(0, row_num, width, 1));
|
decoder->PostPartialInvalidation(IntRect(0, row_num, width, 1));
|
||||||
} else if (row_num ==
|
} else if (lastRow) {
|
||||||
static_cast<png_uint_32>(decoder->mFrameRect.height - 1)) {
|
// Do only one full image invalidation for each even pass. (Bug 1187569)
|
||||||
// Do only one full image invalidation for each pass. (Bug 1187569)
|
if (decoder->mDownscaler) {
|
||||||
|
decoder->PostFullInvalidation();
|
||||||
|
} else if (pass % 2 == 0) {
|
||||||
|
|
||||||
|
const bool hasAlpha = decoder->format != SurfaceFormat::B8G8R8X8;
|
||||||
|
InterpolateInterlacedPNG(pass, hasAlpha,
|
||||||
|
static_cast<uint32_t>(width),
|
||||||
|
decoder->mFrameRect.height,
|
||||||
|
decoder->mImageData);
|
||||||
decoder->PostFullInvalidation();
|
decoder->PostFullInvalidation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user