Bug 1191114 (Part 1) - Always detect HAS_TRANSPARENCY during the metadata decode. r=tn

This commit is contained in:
Seth Fowler 2015-08-12 10:41:02 -07:00
parent 9141eb539e
commit c46c7a44cb
6 changed files with 80 additions and 34 deletions

View File

@ -369,6 +369,15 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
return;
}
// We treat BMPs as transparent if they're 32bpp and alpha is enabled, but
// also if they use RLE encoding, because the 'delta' mode can skip pixels
// and cause implicit transparency.
if ((mBIH.compression == BMPINFOHEADER::RLE8) ||
(mBIH.compression == BMPINFOHEADER::RLE4) ||
(mBIH.bpp == 32 && mUseAlphaData)) {
PostHasTransparency();
}
// We have the size. If we're doing a metadata decode, we're done.
if (IsMetadataDecode()) {
return;

View File

@ -50,6 +50,8 @@ mailing address.
#include <algorithm>
#include "mozilla/Telemetry.h"
using namespace mozilla::gfx;
namespace mozilla {
namespace image {
@ -161,6 +163,27 @@ nsGIFDecoder2::BeginGIF()
PostSize(mGIFStruct.screen_width, mGIFStruct.screen_height);
}
void
nsGIFDecoder2::CheckForTransparency(IntRect aFrameRect)
{
// Check if the image has a transparent color in its palette.
if (mGIFStruct.is_transparent) {
PostHasTransparency();
return;
}
if (mGIFStruct.images_decoded > 0) {
return; // We only care about first frame padding below.
}
// If we need padding on the first frame, that means we don't draw into part
// of the image at all. Report that as transparency.
IntRect imageRect(0, 0, mGIFStruct.screen_width, mGIFStruct.screen_height);
if (!imageRect.IsEqualEdges(aFrameRect)) {
PostHasTransparency();
}
}
//******************************************************************************
nsresult
nsGIFDecoder2::BeginImageFrame(uint16_t aDepth)
@ -170,13 +193,14 @@ nsGIFDecoder2::BeginImageFrame(uint16_t aDepth)
gfx::SurfaceFormat format;
if (mGIFStruct.is_transparent) {
format = gfx::SurfaceFormat::B8G8R8A8;
PostHasTransparency();
} else {
format = gfx::SurfaceFormat::B8G8R8X8;
}
nsIntRect frameRect(mGIFStruct.x_offset, mGIFStruct.y_offset,
mGIFStruct.width, mGIFStruct.height);
IntRect frameRect(mGIFStruct.x_offset, mGIFStruct.y_offset,
mGIFStruct.width, mGIFStruct.height);
CheckForTransparency(frameRect);
// Use correct format, RGB for first frame, PAL for following frames
// and include transparency to allow for optimization of opaque images
@ -186,12 +210,6 @@ nsGIFDecoder2::BeginImageFrame(uint16_t aDepth)
rv = AllocateFrame(mGIFStruct.images_decoded, GetSize(),
frameRect, format, aDepth);
} else {
if (!nsIntRect(nsIntPoint(), GetSize()).IsEqualEdges(frameRect)) {
// We need padding on the first frame, which means that we don't draw into
// part of the image at all. Report that as transparency.
PostHasTransparency();
}
// Regardless of depth of input, the first frame is decoded into 24bit RGB.
rv = AllocateFrame(mGIFStruct.images_decoded, GetSize(),
frameRect, format);
@ -689,12 +707,6 @@ nsGIFDecoder2::WriteInternal(const char* aBuffer, uint32_t aCount)
mGIFStruct.screen_height = GETINT16(q + 2);
mGIFStruct.global_colormap_depth = (q[4]&0x07) + 1;
if (IsMetadataDecode()) {
MOZ_ASSERT(!mGIFOpen, "Gif should not be open at this point");
PostSize(mGIFStruct.screen_width, mGIFStruct.screen_height);
return;
}
// screen_bgcolor is not used
//mGIFStruct.screen_bgcolor = q[5];
// q[6] = Pixel Aspect Ratio
@ -731,6 +743,9 @@ nsGIFDecoder2::WriteInternal(const char* aBuffer, uint32_t aCount)
case gif_image_start:
switch (*q) {
case GIF_TRAILER:
if (IsMetadataDecode()) {
return;
}
mGIFStruct.state = gif_done;
break;
@ -943,6 +958,9 @@ nsGIFDecoder2::WriteInternal(const char* aBuffer, uint32_t aCount)
// If we were doing a metadata decode, we're done.
if (IsMetadataDecode()) {
IntRect frameRect(mGIFStruct.x_offset, mGIFStruct.y_offset,
mGIFStruct.width, mGIFStruct.height);
CheckForTransparency(frameRect);
return;
}
}

View File

@ -47,6 +47,7 @@ private:
bool DoLzw(const uint8_t* q);
bool SetHold(const uint8_t* buf, uint32_t count,
const uint8_t* buf2 = nullptr, uint32_t count2 = 0);
void CheckForTransparency(gfx::IntRect aFrameRect);
inline int ClearCode() const { return 1 << mGIFStruct.datasize; }

View File

@ -19,6 +19,8 @@
#include <algorithm>
using namespace mozilla::gfx;
namespace mozilla {
namespace image {
@ -136,6 +138,20 @@ nsPNGDecoder::~nsPNGDecoder()
}
}
void
nsPNGDecoder::CheckForTransparency(SurfaceFormat aFormat,
const IntRect& aFrameRect)
{
// Check if the image has a transparent color in its palette.
if (aFormat == SurfaceFormat::B8G8R8A8) {
PostHasTransparency();
}
// PNGs shouldn't have first-frame padding.
MOZ_ASSERT_IF(mNumFrames == 0,
IntRect(IntPoint(), GetSize()).IsEqualEdges(aFrameRect));
}
// CreateFrame() is used for both simple and animated images
nsresult
nsPNGDecoder::CreateFrame(png_uint_32 aXOffset, png_uint_32 aYOffset,
@ -145,17 +161,8 @@ nsPNGDecoder::CreateFrame(png_uint_32 aXOffset, png_uint_32 aYOffset,
MOZ_ASSERT(HasSize());
MOZ_ASSERT(!IsMetadataDecode());
if (aFormat == gfx::SurfaceFormat::B8G8R8A8) {
PostHasTransparency();
}
nsIntRect frameRect(aXOffset, aYOffset, aWidth, aHeight);
if (mNumFrames == 0 &&
!nsIntRect(nsIntPoint(), GetSize()).IsEqualEdges(frameRect)) {
// We need padding on the first frame, which means that we don't draw into
// part of the image at all. Report that as transparency.
PostHasTransparency();
}
IntRect frameRect(aXOffset, aYOffset, aWidth, aHeight);
CheckForTransparency(aFormat, frameRect);
// XXX(seth): Some tests depend on the first frame of PNGs being B8G8R8A8.
// This is something we should fix.
@ -268,7 +275,7 @@ nsPNGDecoder::InitInternal()
#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
// Ignore unused chunks
if (mCMSMode == eCMSMode_Off) {
if (mCMSMode == eCMSMode_Off || IsMetadataDecode()) {
png_set_keep_unknown_chunks(mPNG, 1, color_chunks, 2);
}
@ -486,12 +493,6 @@ nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr)
png_longjmp(decoder->mPNG, 1);
}
if (decoder->IsMetadataDecode()) {
// We have the size, so we don't need to decode any further.
decoder->mSuccessfulEarlyFinish = true;
png_longjmp(decoder->mPNG, 1);
}
if (color_type == PNG_COLOR_TYPE_PALETTE) {
png_set_expand(png_ptr);
}
@ -594,6 +595,16 @@ nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr)
png_longjmp(decoder->mPNG, 1); // invalid number of channels
}
if (decoder->IsMetadataDecode()) {
decoder->CheckForTransparency(decoder->format,
IntRect(0, 0, width, height));
// We have the size and transparency information we're looking for, so we
// don't need to decode any further.
decoder->mSuccessfulEarlyFinish = true;
png_longjmp(decoder->mPNG, 1);
}
#ifdef PNG_APNG_SUPPORTED
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL)) {
png_set_progressive_frame_fn(png_ptr, nsPNGDecoder::frame_info_callback,

View File

@ -35,6 +35,9 @@ public:
gfx::SurfaceFormat aFormat);
void EndImageFrame();
void CheckForTransparency(gfx::SurfaceFormat aFormat,
const gfx::IntRect& aFrameRect);
// Check if PNG is valid ICO (32bpp RGBA)
// http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
bool IsValidICO() const

View File

@ -48,8 +48,12 @@ function testFiles() {
// GIFs with padding on the first frame are always transparent.
yield ["first-frame-padding.gif", true];
// JPEGs and BMPs are never transparent.
// JPEGs are never transparent.
yield ["damon.jpg", false];
// Most BMPs are not transparent. (The TestMetadata GTest, which will
// eventually replace this test totally, has coverage for the kinds that can be
// transparent.)
yield ["opaque.bmp", false];
// ICO files which contain BMPs have an additional type of transparency - the