Bug 1247152 (Part 2) - Remove even more code from the GIF decoder. r=edwin

This commit is contained in:
Seth Fowler 2016-03-09 15:39:02 -08:00
Родитель 875464424a
Коммит 3978c77cb6
3 изменённых файлов: 48 добавлений и 100 удалений

Просмотреть файл

@ -29,7 +29,6 @@ typedef enum {
gif_global_colormap,
gif_image_start,
gif_image_header,
gif_image_header_continue,
gif_image_colormap,
gif_lzw_start,
gif_lzw,
@ -70,10 +69,6 @@ typedef struct gif_struct {
int64_t pixels_remaining; // Pixels remaining to be output.
// Parameters for image frame currently being decoded
unsigned x_offset, y_offset; // With respect to "screen" origin
unsigned height, width;
unsigned clamped_height; // Size of the frame rectangle clamped to the
unsigned clamped_width; // global logical size after x_ and y_offset.
int tpixel; // Index of transparent pixel
int32_t disposal_method; // Restore to background, leave in place, etc.
uint32_t* local_colormap; // Per-image colormap
@ -83,15 +78,14 @@ typedef struct gif_struct {
// Global (multi-image) state
int version; // Either 89 for GIF89 or 87 for GIF87
unsigned screen_width; // Logical screen width & height
unsigned screen_height;
int32_t screen_width; // Logical screen width & height
int32_t screen_height;
uint32_t global_colormap_depth; // Depth of global colormap array
int images_decoded; // Counts images for multi-part GIFs
int loop_count; // Netscape specific extension block to control
// the number of animation loops a GIF
// renders.
bool interlaced; // TRUE, if scanlines arrive interlaced order
bool is_transparent; // TRUE, if tpixel is valid
uint16_t prefix[MAX_BITS]; // LZW decoding tables

Просмотреть файл

@ -185,21 +185,20 @@ nsGIFDecoder2::ClampToImageRect(const IntRect& aRect)
//******************************************************************************
nsresult
nsGIFDecoder2::BeginImageFrame(uint16_t aDepth)
nsGIFDecoder2::BeginImageFrame(const IntRect& aFrameRect,
uint16_t aDepth,
bool aIsInterlaced)
{
MOZ_ASSERT(HasSize());
IntRect frameRect(mGIFStruct.x_offset, mGIFStruct.y_offset,
mGIFStruct.width, mGIFStruct.height);
bool hasTransparency = CheckForTransparency(frameRect);
bool hasTransparency = CheckForTransparency(aFrameRect);
gfx::SurfaceFormat format = hasTransparency ? SurfaceFormat::B8G8R8A8
: SurfaceFormat::B8G8R8X8;
// Make sure there's no animation if we're downscaling.
MOZ_ASSERT_IF(mDownscaler, !GetImageMetadata().HasAnimation());
SurfacePipeFlags pipeFlags = mGIFStruct.interlaced
SurfacePipeFlags pipeFlags = aIsInterlaced
? SurfacePipeFlags::DEINTERLACE
: SurfacePipeFlags();
@ -210,7 +209,7 @@ nsGIFDecoder2::BeginImageFrame(uint16_t aDepth)
IntSize targetSize = mDownscaler ? mDownscaler->TargetSize()
: GetSize();
IntRect targetFrameRect = mDownscaler ? IntRect(IntPoint(), targetSize)
: frameRect;
: aFrameRect;
// The first frame may be displayed progressively.
pipeFlags |= SurfacePipeFlags::PROGRESSIVE_DISPLAY;
@ -226,7 +225,7 @@ nsGIFDecoder2::BeginImageFrame(uint16_t aDepth)
MOZ_ASSERT(!mDownscaler);
pipe =
SurfacePipeFactory::CreatePalettedSurfacePipe(this, mGIFStruct.images_decoded,
GetSize(), frameRect, format,
GetSize(), aFrameRect, format,
aDepth, pipeFlags);
}
@ -249,26 +248,10 @@ nsGIFDecoder2::EndImageFrame()
Opacity opacity = Opacity::SOME_TRANSPARENCY;
// First flush all pending image data
if (!mGIFStruct.images_decoded) {
if (mGIFStruct.images_decoded == 0) {
// Only need to flush first frame
FlushImageData();
// If the first frame is smaller in height than the entire image, send an
// invalidation for the area it does not have data for.
// This will clear the remaining bits of the placeholder. (Bug 37589)
const uint32_t realFrameHeight = mGIFStruct.height + mGIFStruct.y_offset;
if (realFrameHeight < mGIFStruct.screen_height) {
if (mDownscaler) {
IntRect targetRect = IntRect(IntPoint(), mDownscaler->TargetSize());
PostInvalidation(IntRect(IntPoint(), GetSize()), Some(targetRect));
} else {
nsIntRect r(0, realFrameHeight,
mGIFStruct.screen_width,
mGIFStruct.screen_height - realFrameHeight);
PostInvalidation(r);
}
}
// The first frame was preallocated with alpha; if it wasn't transparent, we
// should fix that. We can also mark it opaque unconditionally if we didn't
// actually see any transparent pixels - this test is only valid for the
@ -872,25 +855,26 @@ nsGIFDecoder2::WriteInternal(const char* aBuffer, uint32_t aCount)
}
}
IntRect frameRect;
// Get image offsets, with respect to the screen origin
mGIFStruct.x_offset = GETINT16(q);
mGIFStruct.y_offset = GETINT16(q + 2);
frameRect.x = GETINT16(q);
frameRect.y = GETINT16(q + 2);
// Get image width and height.
mGIFStruct.width = GETINT16(q + 4);
mGIFStruct.height = GETINT16(q + 6);
frameRect.width = GETINT16(q + 4);
frameRect.height = GETINT16(q + 6);
if (!mGIFStruct.images_decoded) {
// Work around broken GIF files where the logical screen
// size has weird width or height. We assume that GIF87a
// files don't contain animations.
if ((mGIFStruct.screen_height < mGIFStruct.height) ||
(mGIFStruct.screen_width < mGIFStruct.width) ||
if ((mGIFStruct.screen_height < frameRect.height) ||
(mGIFStruct.screen_width < frameRect.width) ||
(mGIFStruct.version == 87)) {
mGIFStruct.screen_height = mGIFStruct.height;
mGIFStruct.screen_width = mGIFStruct.width;
mGIFStruct.x_offset = 0;
mGIFStruct.y_offset = 0;
mGIFStruct.screen_height = frameRect.height;
mGIFStruct.screen_width = frameRect.width;
frameRect.MoveTo(0, 0);
}
// Create the image container with the right size.
BeginGIF();
@ -902,38 +886,21 @@ 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;
}
}
// Work around more broken GIF files that have zero image width or height
if (!mGIFStruct.height || !mGIFStruct.width) {
mGIFStruct.height = mGIFStruct.screen_height;
mGIFStruct.width = mGIFStruct.screen_width;
if (!mGIFStruct.height || !mGIFStruct.width) {
if (!frameRect.height || !frameRect.width) {
frameRect.height = mGIFStruct.screen_height;
frameRect.width = mGIFStruct.screen_width;
if (!frameRect.height || !frameRect.width) {
mGIFStruct.state = gif_error;
break;
}
}
// Hack around GIFs with frame rects outside the given screen bounds.
IntRect clampedRect =
ClampToImageRect(IntRect(mGIFStruct.x_offset, mGIFStruct.y_offset,
mGIFStruct.width, mGIFStruct.height));
if (clampedRect.IsEmpty()) {
// XXX Bug 1227546 - Maybe we should treat this as valid?
mGIFStruct.state = gif_error;
break;
}
mGIFStruct.clamped_width = clampedRect.width;
mGIFStruct.clamped_height = clampedRect.height;
MOZ_ASSERT(mGIFStruct.clamped_width <= mGIFStruct.width);
MOZ_ASSERT(mGIFStruct.clamped_height <= mGIFStruct.height);
// Depth of colors is determined by colormap
// (q[8] & 0x80) indicates local colormap
// bits per pixel is (q[8]&0x07 + 1) when local colormap is set
@ -945,17 +912,18 @@ nsGIFDecoder2::WriteInternal(const char* aBuffer, uint32_t aCount)
while (mGIFStruct.tpixel >= (1 << realDepth) && (realDepth < 8)) {
realDepth++;
}
// Mask to limit the color values within the colormap
mColorMask = 0xFF >> (8 - realDepth);
if (NS_FAILED(BeginImageFrame(realDepth))) {
// Determine if this frame is interlaced or not.
const bool isInterlaced = q[8] & 0x40;
if (NS_FAILED(BeginImageFrame(frameRect, realDepth, isInterlaced))) {
mGIFStruct.state = gif_error;
return;
}
MOZ_FALLTHROUGH; // to continue decoding header.
}
case gif_image_header_continue: {
// While decoders can reuse frames, we unconditionally increment
// mGIFStruct.images_decoded when we're done with a frame, so we both can
// and need to zero out the colormap and image data after every new frame.
@ -964,37 +932,9 @@ nsGIFDecoder2::WriteInternal(const char* aBuffer, uint32_t aCount)
memset(mColormap, 0, mColormapSize);
}
if (!mGIFStruct.images_decoded) {
// Send a onetime invalidation for the first frame if it has a y-axis
// offset. Otherwise, the area may never be refreshed and the
// placeholder will remain on the screen. (Bug 37589)
if (mGIFStruct.y_offset > 0) {
if (mDownscaler) {
IntRect targetRect = IntRect(IntPoint(), mDownscaler->TargetSize());
PostInvalidation(IntRect(IntPoint(), GetSize()), Some(targetRect));
} else {
nsIntRect r(0, 0, mGIFStruct.screen_width, mGIFStruct.y_offset);
PostInvalidation(r);
}
}
}
mGIFStruct.interlaced = bool(q[8] & 0x40);
// Clear state from last image
mGIFStruct.pixels_remaining = mGIFStruct.width * mGIFStruct.height;
mGIFStruct.pixels_remaining = frameRect.width * frameRect.height;
// Depth of colors is determined by colormap
// (q[8] & 0x80) indicates local colormap
// bits per pixel is (q[8]&0x07 + 1) when local colormap is set
uint32_t depth = mGIFStruct.global_colormap_depth;
if (q[8] & 0x80) {
depth = (q[8]&0x07) + 1;
}
uint32_t realDepth = depth;
while (mGIFStruct.tpixel >= (1 << realDepth) && (realDepth < 8)) {
realDepth++;
}
// has a local colormap?
if (q[8] & 0x80) {
mGIFStruct.local_colormap_size = 1 << depth;

Просмотреть файл

@ -33,11 +33,25 @@ private:
// Decoders should only be instantiated via DecoderFactory.
explicit nsGIFDecoder2(RasterImage* aImage);
// These functions will be called when the decoder has a decoded row,
// frame size information, etc.
/// Called when we begin decoding the image.
void BeginGIF();
nsresult BeginImageFrame(uint16_t aDepth);
/**
* Called when we begin decoding a frame.
*
* @param aFrameRect The region of the image that contains data. The region
* outside this rect is transparent.
* @param aDepth The palette depth of this frame.
* @param aIsInterlaced If true, this frame is an interlaced frame.
*/
nsresult BeginImageFrame(const gfx::IntRect& aFrameRect,
uint16_t aDepth,
bool aIsInterlaced);
/// Called when we finish decoding a frame.
void EndImageFrame();
/// Called when we finish decoding the entire image.
void FlushImageData();
nsresult GifWrite(const uint8_t* buf, uint32_t numbytes);