mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-22 18:32:00 +00:00
Bug 366465 - GIF Decoding in Cairo can skip all row buffers, patch by Alfred Kayser <alfredkayser@nl.ibm.com>, r=pavlov, sr=tor, a1.9=pavlov
This commit is contained in:
parent
f5b9eb6a9f
commit
4085c6630f
@ -94,16 +94,14 @@ typedef struct gif_struct {
|
|||||||
int ipass; /* Interlace pass; Ranges 1-4 if interlaced. */
|
int ipass; /* Interlace pass; Ranges 1-4 if interlaced. */
|
||||||
PRUintn rows_remaining; /* Rows remaining to be output */
|
PRUintn rows_remaining; /* Rows remaining to be output */
|
||||||
PRUintn irow; /* Current output row, starting at zero */
|
PRUintn irow; /* Current output row, starting at zero */
|
||||||
PRUint8 *rowbuf; /* Single scanline temporary buffer */
|
PRUint32 *rowp; /* Current output pointer */
|
||||||
PRUint8 *rowend; /* Pointer to end of rowbuf */
|
|
||||||
PRUint8 *rowp; /* Current output pointer */
|
|
||||||
|
|
||||||
/* Parameters for image frame currently being decoded*/
|
/* Parameters for image frame currently being decoded*/
|
||||||
PRUintn x_offset, y_offset; /* With respect to "screen" origin */
|
PRUintn x_offset, y_offset; /* With respect to "screen" origin */
|
||||||
PRUintn height, width;
|
PRUintn height, width;
|
||||||
int tpixel; /* Index of transparent pixel */
|
int tpixel; /* Index of transparent pixel */
|
||||||
PRInt32 disposal_method; /* Restore to background, leave in place, etc.*/
|
PRInt32 disposal_method; /* Restore to background, leave in place, etc.*/
|
||||||
PRUint8 *local_colormap; /* Per-image colormap */
|
PRUint32 *local_colormap; /* Per-image colormap */
|
||||||
int local_colormap_size; /* Size of local colormap array. */
|
int local_colormap_size; /* Size of local colormap array. */
|
||||||
PRUint32 delay_time; /* Display time, in milliseconds,
|
PRUint32 delay_time; /* Display time, in milliseconds,
|
||||||
for this image in a multi-image GIF */
|
for this image in a multi-image GIF */
|
||||||
@ -112,7 +110,7 @@ typedef struct gif_struct {
|
|||||||
int version; /* Either 89 for GIF89 or 87 for GIF87 */
|
int version; /* Either 89 for GIF89 or 87 for GIF87 */
|
||||||
PRUintn screen_width; /* Logical screen width & height */
|
PRUintn screen_width; /* Logical screen width & height */
|
||||||
PRUintn screen_height;
|
PRUintn screen_height;
|
||||||
int global_colormap_size; /* Size of global colormap array. */
|
PRUint32 global_colormap_size; /* Size of global colormap array. */
|
||||||
int images_decoded; /* Counts images for multi-part GIFs */
|
int images_decoded; /* Counts images for multi-part GIFs */
|
||||||
int loop_count; /* Netscape specific extension block to control
|
int loop_count; /* Netscape specific extension block to control
|
||||||
the number of animation loops a GIF renders. */
|
the number of animation loops a GIF renders. */
|
||||||
@ -120,11 +118,10 @@ typedef struct gif_struct {
|
|||||||
PRPackedBool progressive_display; /* If TRUE, do Haeberli interlace hack */
|
PRPackedBool progressive_display; /* If TRUE, do Haeberli interlace hack */
|
||||||
PRPackedBool interlaced; /* TRUE, if scanlines arrive interlaced order */
|
PRPackedBool interlaced; /* TRUE, if scanlines arrive interlaced order */
|
||||||
PRPackedBool is_transparent; /* TRUE, if tpixel is valid */
|
PRPackedBool is_transparent; /* TRUE, if tpixel is valid */
|
||||||
PRPackedBool is_local_colormap_defined;
|
|
||||||
|
|
||||||
PRUint16 prefix[MAX_BITS]; /* LZW decoding tables */
|
PRUint16 prefix[MAX_BITS]; /* LZW decoding tables */
|
||||||
PRUint8 hold[MAX_HOLD_SIZE]; /* Accumulation buffer */
|
PRUint8 hold[MAX_HOLD_SIZE]; /* Accumulation buffer */
|
||||||
PRUint8 global_colormap[3*MAX_COLORS]; /* Default colormap if local not supplied, 3 bytes for each color */
|
PRUint32 global_colormap[MAX_COLORS]; /* Default colormap if local not supplied */
|
||||||
PRUint8 suffix[MAX_BITS]; /* LZW decoding tables */
|
PRUint8 suffix[MAX_BITS]; /* LZW decoding tables */
|
||||||
PRUint8 stack[MAX_BITS]; /* Base of LZW decoder stack */
|
PRUint8 stack[MAX_BITS]; /* Base of LZW decoder stack */
|
||||||
|
|
||||||
|
@ -89,6 +89,7 @@ mailing address.
|
|||||||
|
|
||||||
#include "imgContainer.h"
|
#include "imgContainer.h"
|
||||||
|
|
||||||
|
#include "gfxColor.h"
|
||||||
#include "gfxPlatform.h"
|
#include "gfxPlatform.h"
|
||||||
#include "lcms.h"
|
#include "lcms.h"
|
||||||
|
|
||||||
@ -120,10 +121,10 @@ NS_IMPL_ISUPPORTS1(nsGIFDecoder2, imgIDecoder)
|
|||||||
nsGIFDecoder2::nsGIFDecoder2()
|
nsGIFDecoder2::nsGIFDecoder2()
|
||||||
: mCurrentRow(-1)
|
: mCurrentRow(-1)
|
||||||
, mLastFlushedRow(-1)
|
, mLastFlushedRow(-1)
|
||||||
, mRGBLine(nsnull)
|
, mImageData(nsnull)
|
||||||
, mRGBLineMaxSize(0)
|
|
||||||
, mCurrentPass(0)
|
, mCurrentPass(0)
|
||||||
, mLastFlushedPass(0)
|
, mLastFlushedPass(0)
|
||||||
|
, mOldColor(0)
|
||||||
, mGIFOpen(PR_FALSE)
|
, mGIFOpen(PR_FALSE)
|
||||||
{
|
{
|
||||||
// Clear out the structure, excluding the arrays
|
// Clear out the structure, excluding the arrays
|
||||||
@ -168,9 +169,7 @@ NS_IMETHODIMP nsGIFDecoder2::Close()
|
|||||||
EndImageFrame();
|
EndImageFrame();
|
||||||
EndGIF();
|
EndGIF();
|
||||||
|
|
||||||
PR_FREEIF(mGIFStruct.rowbuf);
|
|
||||||
PR_FREEIF(mGIFStruct.local_colormap);
|
PR_FREEIF(mGIFStruct.local_colormap);
|
||||||
PR_FREEIF(mRGBLine);
|
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
@ -204,38 +203,39 @@ static NS_METHOD ReadDataOut(nsIInputStream* in,
|
|||||||
// Push any new rows according to mCurrentPass/mLastFlushedPass and
|
// Push any new rows according to mCurrentPass/mLastFlushedPass and
|
||||||
// mCurrentRow/mLastFlushedRow. Note: caller is responsible for
|
// mCurrentRow/mLastFlushedRow. Note: caller is responsible for
|
||||||
// updating mlastFlushed{Row,Pass}.
|
// updating mlastFlushed{Row,Pass}.
|
||||||
|
void
|
||||||
|
nsGIFDecoder2::FlushImageData(PRUint32 fromRow, PRUint32 rows)
|
||||||
|
{
|
||||||
|
nsIntRect r(0, fromRow, mGIFStruct.screen_width, rows);
|
||||||
|
|
||||||
|
// Update image
|
||||||
|
nsCOMPtr<nsIImage> img(do_GetInterface(mImageFrame));
|
||||||
|
img->ImageUpdated(nsnull, nsImageUpdateFlags_kBitsChanged, &r);
|
||||||
|
|
||||||
|
// Offset to the frame position
|
||||||
|
// Only notify observer(s) for first frame
|
||||||
|
if (!mGIFStruct.images_decoded && mObserver) {
|
||||||
|
r.y += mGIFStruct.y_offset;
|
||||||
|
mObserver->OnDataAvailable(nsnull, mImageFrame, &r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsGIFDecoder2::FlushImageData()
|
nsGIFDecoder2::FlushImageData()
|
||||||
{
|
{
|
||||||
PRInt32 imgWidth;
|
|
||||||
mImageContainer->GetWidth(&imgWidth);
|
|
||||||
nsIntRect frameRect;
|
|
||||||
mImageFrame->GetRect(frameRect);
|
|
||||||
|
|
||||||
switch (mCurrentPass - mLastFlushedPass) {
|
switch (mCurrentPass - mLastFlushedPass) {
|
||||||
case 0: { // same pass
|
case 0: // same pass
|
||||||
PRInt32 remainingRows = mCurrentRow - mLastFlushedRow;
|
if (mCurrentRow - mLastFlushedRow)
|
||||||
if (remainingRows) {
|
FlushImageData(mLastFlushedRow + 1, mCurrentRow - mLastFlushedRow);
|
||||||
nsIntRect r(0, frameRect.y + mLastFlushedRow + 1,
|
break;
|
||||||
imgWidth, remainingRows);
|
|
||||||
mObserver->OnDataAvailable(nsnull, mImageFrame, &r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1: { // one pass on - need to handle bottom & top rects
|
case 1: // one pass on - need to handle bottom & top rects
|
||||||
nsIntRect r(0, frameRect.y, imgWidth, mCurrentRow + 1);
|
FlushImageData(0, mCurrentRow + 1);
|
||||||
mObserver->OnDataAvailable(nsnull, mImageFrame, &r);
|
FlushImageData(mLastFlushedRow + 1, mGIFStruct.height - (mLastFlushedRow + 1));
|
||||||
nsIntRect r2(0, frameRect.y + mLastFlushedRow + 1,
|
break;
|
||||||
imgWidth, frameRect.height - mLastFlushedRow - 1);
|
|
||||||
mObserver->OnDataAvailable(nsnull, mImageFrame, &r2);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: { // more than one pass on - push the whole frame
|
default: // more than one pass on - push the whole frame
|
||||||
nsIntRect r(0, frameRect.y, imgWidth, frameRect.height);
|
FlushImageData(0, mGIFStruct.height);
|
||||||
mObserver->OnDataAvailable(nsnull, mImageFrame, &r);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,7 +247,7 @@ nsresult nsGIFDecoder2::ProcessData(unsigned char *data, PRUint32 count, PRUint3
|
|||||||
nsresult rv = GifWrite(data, count);
|
nsresult rv = GifWrite(data, count);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
if (mImageFrame && mObserver) {
|
if (mImageFrame) {
|
||||||
FlushImageData();
|
FlushImageData();
|
||||||
mLastFlushedRow = mCurrentRow;
|
mLastFlushedRow = mCurrentRow;
|
||||||
mLastFlushedPass = mCurrentPass;
|
mLastFlushedPass = mCurrentPass;
|
||||||
@ -286,9 +286,7 @@ NS_IMETHODIMP nsGIFDecoder2::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
|
|||||||
//******************************************************************************
|
//******************************************************************************
|
||||||
void nsGIFDecoder2::BeginGIF()
|
void nsGIFDecoder2::BeginGIF()
|
||||||
{
|
{
|
||||||
// If we have passed an illogical screen size, bail and hope that we'll get
|
if (mGIFOpen)
|
||||||
// set later by the first frame's local image header.
|
|
||||||
if (mGIFStruct.screen_width == 0 || mGIFStruct.screen_height == 0)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (mObserver)
|
if (mObserver)
|
||||||
@ -323,7 +321,6 @@ void nsGIFDecoder2::EndGIF()
|
|||||||
void nsGIFDecoder2::BeginImageFrame()
|
void nsGIFDecoder2::BeginImageFrame()
|
||||||
{
|
{
|
||||||
mImageFrame = nsnull; // clear out our current frame reference
|
mImageFrame = nsnull; // clear out our current frame reference
|
||||||
mFrameHasNoAlpha = PR_TRUE;
|
|
||||||
|
|
||||||
if (!mGIFStruct.images_decoded) {
|
if (!mGIFStruct.images_decoded) {
|
||||||
// Send a onetime OnDataAvailable (Display Refresh) for the first frame
|
// Send a onetime OnDataAvailable (Display Refresh) for the first frame
|
||||||
@ -336,151 +333,72 @@ void nsGIFDecoder2::BeginImageFrame()
|
|||||||
mObserver->OnDataAvailable(nsnull, mImageFrame, &r);
|
mObserver->OnDataAvailable(nsnull, mImageFrame, &r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gfx_format format = gfxIFormats::RGB;
|
||||||
|
if (mGIFStruct.is_transparent) {
|
||||||
|
format = gfxIFormats::RGB_A1; // XXX not really
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize the frame and append it to the container
|
||||||
|
mImageFrame = do_CreateInstance("@mozilla.org/gfx/image/frame;2");
|
||||||
|
if (!mImageFrame || NS_FAILED(mImageFrame->Init(
|
||||||
|
mGIFStruct.x_offset, mGIFStruct.y_offset,
|
||||||
|
mGIFStruct.width, mGIFStruct.height, format, 24))) {
|
||||||
|
mImageFrame = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mImageFrame->SetFrameDisposalMethod(mGIFStruct.disposal_method);
|
||||||
|
mImageContainer->AppendFrame(mImageFrame);
|
||||||
|
|
||||||
|
if (mObserver)
|
||||||
|
mObserver->OnStartFrame(nsnull, mImageFrame);
|
||||||
|
|
||||||
|
PRUint32 imageDataLength;
|
||||||
|
mImageFrame->GetImageData((PRUint8 **)&mImageData, &imageDataLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//******************************************************************************
|
//******************************************************************************
|
||||||
void nsGIFDecoder2::EndImageFrame()
|
void nsGIFDecoder2::EndImageFrame()
|
||||||
{
|
{
|
||||||
|
// First flush all pending image data
|
||||||
|
FlushImageData();
|
||||||
|
mCurrentRow = mLastFlushedRow = -1;
|
||||||
|
mCurrentPass = mLastFlushedPass = 0;
|
||||||
|
|
||||||
|
if (!mGIFStruct.images_decoded) {
|
||||||
|
// If the first frame is smaller in height than the entire image, send a
|
||||||
|
// OnDataAvailable (Display Refresh) for the area it does not have data for.
|
||||||
|
// This will clear the remaining bits of the placeholder. (Bug 37589)
|
||||||
|
const PRUint32 realFrameHeight = mGIFStruct.height + mGIFStruct.y_offset;
|
||||||
|
if (realFrameHeight < mGIFStruct.screen_height) {
|
||||||
|
nsIntRect r(0, realFrameHeight,
|
||||||
|
mGIFStruct.screen_width,
|
||||||
|
mGIFStruct.screen_height - realFrameHeight);
|
||||||
|
mObserver->OnDataAvailable(nsnull, mImageFrame, &r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mGIFStruct.images_decoded++;
|
mGIFStruct.images_decoded++;
|
||||||
|
|
||||||
// If mImageFrame hasn't been initialized, call HaveDecodedRow to init it
|
// We actually have the timeout information before we get the lzw encoded
|
||||||
// One reason why it may not be initialized is because the frame
|
// image data, at least according to the spec, but we delay in setting the
|
||||||
// is out of the bounds of the image.
|
// timeout for the image until here to help ensure that we have the whole
|
||||||
if (!mImageFrame) {
|
// image frame decoded before we go off and try to display another frame.
|
||||||
HaveDecodedRow(nsnull,0,0,0);
|
mImageFrame->SetTimeout(mGIFStruct.delay_time);
|
||||||
} else {
|
|
||||||
// We actually have the timeout information before we get the lzw encoded
|
|
||||||
// image data, at least according to the spec, but we delay in setting the
|
|
||||||
// timeout for the image until here to help ensure that we have the whole
|
|
||||||
// image frame decoded before we go off and try to display another frame.
|
|
||||||
mImageFrame->SetTimeout(mGIFStruct.delay_time);
|
|
||||||
|
|
||||||
if (mFrameHasNoAlpha) {
|
|
||||||
nsCOMPtr<nsIImage> img(do_GetInterface(mImageFrame));
|
|
||||||
img->SetHasNoAlpha();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mImageContainer->EndFrameDecode(mGIFStruct.images_decoded, mGIFStruct.delay_time);
|
mImageContainer->EndFrameDecode(mGIFStruct.images_decoded, mGIFStruct.delay_time);
|
||||||
|
|
||||||
if (mObserver && mImageFrame) {
|
if (mObserver)
|
||||||
FlushImageData();
|
|
||||||
|
|
||||||
if (mGIFStruct.images_decoded == 1) {
|
|
||||||
// If the first frame is smaller in height than the entire image, send a
|
|
||||||
// OnDataAvailable (Display Refresh) for the area it does not have data for.
|
|
||||||
// This will clear the remaining bits of the placeholder. (Bug 37589)
|
|
||||||
PRInt32 imgHeight;
|
|
||||||
PRInt32 realFrameHeight = mGIFStruct.height + mGIFStruct.y_offset;
|
|
||||||
|
|
||||||
mImageContainer->GetHeight(&imgHeight);
|
|
||||||
if (imgHeight > realFrameHeight) {
|
|
||||||
PRInt32 imgWidth;
|
|
||||||
mImageContainer->GetWidth(&imgWidth);
|
|
||||||
|
|
||||||
nsIntRect r(0, realFrameHeight, imgWidth, imgHeight - realFrameHeight);
|
|
||||||
mObserver->OnDataAvailable(nsnull, mImageFrame, &r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mCurrentRow = mLastFlushedRow = -1;
|
|
||||||
mCurrentPass = mLastFlushedPass = 0;
|
|
||||||
|
|
||||||
mObserver->OnStopFrame(nsnull, mImageFrame);
|
mObserver->OnStopFrame(nsnull, mImageFrame);
|
||||||
}
|
|
||||||
|
|
||||||
// Clear state from this image
|
// Release reference to this frame
|
||||||
mImageFrame = nsnull;
|
mImageFrame = nsnull;
|
||||||
mGIFStruct.is_local_colormap_defined = PR_FALSE;
|
|
||||||
mGIFStruct.is_transparent = PR_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
//******************************************************************************
|
|
||||||
// GIF decoder callback notification that it has decoded a row
|
|
||||||
void nsGIFDecoder2::HaveDecodedRow(
|
|
||||||
PRUint8* aRowBufPtr, // Pointer to single scanline temporary buffer
|
|
||||||
int aRowNumber, // Row number?
|
|
||||||
int aDuplicateCount, // Number of times to duplicate the row?
|
|
||||||
int aInterlacePass) // interlace pass (1-4)
|
|
||||||
{
|
|
||||||
const PRUint32 bpr = mGIFStruct.width * sizeof(PRUint32);
|
|
||||||
|
|
||||||
// We have to delay allocation of the image frame until now because
|
// Reset the transparent pixel
|
||||||
// we won't have control block info (transparency) until now. The control
|
if (mOldColor) {
|
||||||
// block of a GIF stream shows up after the image header since transparency
|
mColormap[mGIFStruct.tpixel] = mOldColor;
|
||||||
// is added in GIF89a and control blocks are how the extensions are done.
|
mOldColor = 0;
|
||||||
// How annoying.
|
|
||||||
if (!mImageFrame) {
|
|
||||||
gfx_format format = gfxIFormats::RGB;
|
|
||||||
if (mGIFStruct.is_transparent) {
|
|
||||||
format = gfxIFormats::RGB_A1; // XXX not really
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize the frame and append it to the container
|
|
||||||
mImageFrame = do_CreateInstance("@mozilla.org/gfx/image/frame;2");
|
|
||||||
if (!mImageFrame || NS_FAILED(mImageFrame->Init(
|
|
||||||
mGIFStruct.x_offset, mGIFStruct.y_offset,
|
|
||||||
mGIFStruct.width, mGIFStruct.height, format, 24))) {
|
|
||||||
mImageFrame = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mImageFrame->SetFrameDisposalMethod(mGIFStruct.disposal_method);
|
|
||||||
mImageContainer->AppendFrame(mImageFrame);
|
|
||||||
|
|
||||||
if (mObserver)
|
|
||||||
mObserver->OnStartFrame(nsnull, mImageFrame);
|
|
||||||
|
|
||||||
if (bpr > mRGBLineMaxSize) {
|
|
||||||
mRGBLine = (PRUint8 *)PR_REALLOC(mRGBLine, bpr);
|
|
||||||
mRGBLineMaxSize = bpr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aRowBufPtr) {
|
|
||||||
// Map the data into colors
|
|
||||||
int cmapsize = mGIFStruct.global_colormap_size;
|
|
||||||
PRUint8* cmap = mGIFStruct.global_colormap;
|
|
||||||
if (mGIFStruct.is_local_colormap_defined) {
|
|
||||||
cmapsize = mGIFStruct.local_colormap_size;
|
|
||||||
cmap = mGIFStruct.local_colormap;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cmap) { // cmap could have null value if the global color table flag is 0
|
|
||||||
nsIntRect r(0, aRowNumber, mGIFStruct.width, aDuplicateCount);
|
|
||||||
imgContainer::ClearFrame(mImageFrame, r);
|
|
||||||
} else {
|
|
||||||
PRUint8* rowBufIndex = aRowBufPtr;
|
|
||||||
PRUint32* rgbRowIndex = (PRUint32*)mRGBLine;
|
|
||||||
PRBool rowHasNoAlpha = PR_TRUE;
|
|
||||||
|
|
||||||
const PRInt32 tpixel =
|
|
||||||
mGIFStruct.is_transparent ? mGIFStruct.tpixel : -1;
|
|
||||||
|
|
||||||
while (rowBufIndex != mGIFStruct.rowend) {
|
|
||||||
if (*rowBufIndex >= cmapsize || *rowBufIndex == tpixel) {
|
|
||||||
rowHasNoAlpha = PR_FALSE;
|
|
||||||
*rgbRowIndex++ = 0x00000000;
|
|
||||||
++rowBufIndex;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
PRUint32 colorIndex = *rowBufIndex * 3;
|
|
||||||
*rgbRowIndex++ = (0xFF << 24) |
|
|
||||||
(cmap[colorIndex] << 16) |
|
|
||||||
(cmap[colorIndex+1] << 8) |
|
|
||||||
(cmap[colorIndex+2]);
|
|
||||||
++rowBufIndex;
|
|
||||||
}
|
|
||||||
for (int i=0; i<aDuplicateCount; i++)
|
|
||||||
mImageFrame->SetImageData(mRGBLine, bpr, (aRowNumber+i)*bpr);
|
|
||||||
if (!rowHasNoAlpha)
|
|
||||||
mFrameHasNoAlpha = PR_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
mCurrentRow = aRowNumber + aDuplicateCount - 1;
|
|
||||||
mCurrentPass = aInterlacePass;
|
|
||||||
if (aInterlacePass == 1)
|
|
||||||
mLastFlushedPass = aInterlacePass; // interlaced starts at 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -489,8 +407,7 @@ void nsGIFDecoder2::HaveDecodedRow(
|
|||||||
// Send the data to the display front-end.
|
// Send the data to the display front-end.
|
||||||
PRUint32 nsGIFDecoder2::OutputRow()
|
PRUint32 nsGIFDecoder2::OutputRow()
|
||||||
{
|
{
|
||||||
int width, drow_start, drow_end;
|
int drow_start, drow_end;
|
||||||
|
|
||||||
drow_start = drow_end = mGIFStruct.irow;
|
drow_start = drow_end = mGIFStruct.irow;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -524,24 +441,24 @@ PRUint32 nsGIFDecoder2::OutputRow()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check for scanline below edge of logical screen */
|
|
||||||
if ((mGIFStruct.y_offset + mGIFStruct.irow) < mGIFStruct.screen_height) {
|
// Duplicate rows
|
||||||
/* Clip if right edge of image exceeds limits */
|
if (drow_end > drow_start) {
|
||||||
if ((mGIFStruct.x_offset + mGIFStruct.width) > mGIFStruct.screen_width)
|
// irow is the current row filled
|
||||||
width = mGIFStruct.screen_width - mGIFStruct.x_offset;
|
const PRUint32 width = mGIFStruct.width;
|
||||||
else
|
PRUint32 *rgbRowIndex = mImageData + mGIFStruct.irow * width;
|
||||||
width = mGIFStruct.width;
|
for (int r = drow_start; r <= drow_end; r++) {
|
||||||
|
if (r != mGIFStruct.irow) {
|
||||||
if (width > 0)
|
memcpy(mImageData + r * width, rgbRowIndex, width*sizeof(PRUint32));
|
||||||
/* Decoded data available callback */
|
}
|
||||||
HaveDecodedRow(
|
}
|
||||||
mGIFStruct.rowbuf, // Pointer to single scanline temporary buffer
|
|
||||||
drow_start, // Row number
|
|
||||||
drow_end - drow_start + 1, // Number of times to duplicate the row?
|
|
||||||
mGIFStruct.ipass); // interlace pass (1-4)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mGIFStruct.rowp = mGIFStruct.rowbuf;
|
mCurrentRow = drow_end;
|
||||||
|
mCurrentPass = mGIFStruct.ipass;
|
||||||
|
if (mGIFStruct.ipass == 1)
|
||||||
|
mLastFlushedPass = mGIFStruct.ipass; // interlaced starts at 1
|
||||||
|
|
||||||
|
|
||||||
if (!mGIFStruct.interlaced) {
|
if (!mGIFStruct.interlaced) {
|
||||||
mGIFStruct.irow++;
|
mGIFStruct.irow++;
|
||||||
@ -583,8 +500,9 @@ nsGIFDecoder2::DoLzw(const PRUint8 *q)
|
|||||||
PRUint8 *stackp = mGIFStruct.stackp;
|
PRUint8 *stackp = mGIFStruct.stackp;
|
||||||
PRUint8 *suffix = mGIFStruct.suffix;
|
PRUint8 *suffix = mGIFStruct.suffix;
|
||||||
PRUint8 *stack = mGIFStruct.stack;
|
PRUint8 *stack = mGIFStruct.stack;
|
||||||
PRUint8 *rowp = mGIFStruct.rowp;
|
PRUint32 *rowp = mGIFStruct.rowp;
|
||||||
PRUint8 *rowend = mGIFStruct.rowend;
|
PRUint32 *rowend = mImageData + (mGIFStruct.irow + 1) * mGIFStruct.width;
|
||||||
|
PRUint32 *cmap = mColormap;
|
||||||
|
|
||||||
if (rowp == rowend)
|
if (rowp == rowend)
|
||||||
return PR_TRUE;
|
return PR_TRUE;
|
||||||
@ -593,7 +511,8 @@ nsGIFDecoder2::DoLzw(const PRUint8 *q)
|
|||||||
PR_BEGIN_MACRO \
|
PR_BEGIN_MACRO \
|
||||||
if (!OutputRow()) \
|
if (!OutputRow()) \
|
||||||
goto END; \
|
goto END; \
|
||||||
rowp = mGIFStruct.rowp; \
|
rowp = mImageData + mGIFStruct.irow * mGIFStruct.width; \
|
||||||
|
rowend = rowp + mGIFStruct.width; \
|
||||||
PR_END_MACRO
|
PR_END_MACRO
|
||||||
|
|
||||||
for (const PRUint8* ch = q; count-- > 0; ch++)
|
for (const PRUint8* ch = q; count-- > 0; ch++)
|
||||||
@ -626,7 +545,7 @@ nsGIFDecoder2::DoLzw(const PRUint8 *q)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (oldcode == -1) {
|
if (oldcode == -1) {
|
||||||
*rowp++ = suffix[code];
|
*rowp++ = cmap[suffix[code]];
|
||||||
if (rowp == rowend)
|
if (rowp == rowend)
|
||||||
OUTPUT_ROW();
|
OUTPUT_ROW();
|
||||||
|
|
||||||
@ -676,7 +595,7 @@ nsGIFDecoder2::DoLzw(const PRUint8 *q)
|
|||||||
|
|
||||||
/* Copy the decoded data out to the scanline buffer. */
|
/* Copy the decoded data out to the scanline buffer. */
|
||||||
do {
|
do {
|
||||||
*rowp++ = *--stackp;
|
*rowp++ = cmap[*--stackp];
|
||||||
if (rowp == rowend)
|
if (rowp == rowend)
|
||||||
OUTPUT_ROW();
|
OUTPUT_ROW();
|
||||||
} while (stackp > stack);
|
} while (stackp > stack);
|
||||||
@ -700,6 +619,33 @@ nsGIFDecoder2::DoLzw(const PRUint8 *q)
|
|||||||
return PR_TRUE;
|
return PR_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand the colormap from RGB to Packed ARGB as needed by Cairo.
|
||||||
|
* And apply any LCMS transformation.
|
||||||
|
*/
|
||||||
|
static void ConvertColormap(PRUint32 *aColormap, PRUint32 aColors)
|
||||||
|
{
|
||||||
|
// Apply CMS transformation if enabled and available
|
||||||
|
if (gfxPlatform::IsCMSEnabled()) {
|
||||||
|
cmsHTRANSFORM transform = gfxPlatform::GetCMSRGBTransform();
|
||||||
|
if (transform)
|
||||||
|
cmsDoTransform(transform, aColormap, aColormap, aColors);
|
||||||
|
}
|
||||||
|
// Convert from the GIF's RGB format to the Cairo format.
|
||||||
|
// Work from end to begin, because of the in-place expansion
|
||||||
|
PRUint8 *from = ((PRUint8 *)aColormap) + 3 * aColors;
|
||||||
|
PRUint32 *to = aColormap + aColors;
|
||||||
|
|
||||||
|
// Clear part after defined colors
|
||||||
|
memset(to, 0, (MAX_COLORS - aColors)*sizeof(PRUint32));
|
||||||
|
|
||||||
|
// Convert color entries to Cairo format
|
||||||
|
for (PRUint32 c = aColors; c > 0; c--) {
|
||||||
|
from -= 3;
|
||||||
|
*--to = GFX_PACKED_PIXEL(0xFF, from[0], from[1], from[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/*
|
/*
|
||||||
* process data arriving from the stream for the gif decoder
|
* process data arriving from the stream for the gif decoder
|
||||||
@ -715,8 +661,8 @@ nsresult nsGIFDecoder2::GifWrite(const PRUint8 *buf, PRUint32 len)
|
|||||||
// Add what we have sofar to the block
|
// Add what we have sofar to the block
|
||||||
// If previous call to me left something in the hold first complete current block
|
// If previous call to me left something in the hold first complete current block
|
||||||
// Or if we are filling the colormaps, first complete the colormap
|
// Or if we are filling the colormaps, first complete the colormap
|
||||||
PRUint8* p = (mGIFStruct.state == gif_global_colormap) ? mGIFStruct.global_colormap :
|
PRUint8* p = (mGIFStruct.state == gif_global_colormap) ? (PRUint8*)mGIFStruct.global_colormap :
|
||||||
(mGIFStruct.state == gif_image_colormap) ? mGIFStruct.local_colormap :
|
(mGIFStruct.state == gif_image_colormap) ? (PRUint8*)mGIFStruct.local_colormap :
|
||||||
(mGIFStruct.bytes_in_hold) ? mGIFStruct.hold : nsnull;
|
(mGIFStruct.bytes_in_hold) ? mGIFStruct.hold : nsnull;
|
||||||
if (p) {
|
if (p) {
|
||||||
// Add what we have sofar to the block
|
// Add what we have sofar to the block
|
||||||
@ -761,6 +707,12 @@ nsresult nsGIFDecoder2::GifWrite(const PRUint8 *buf, PRUint32 len)
|
|||||||
|
|
||||||
case gif_lzw_start:
|
case gif_lzw_start:
|
||||||
{
|
{
|
||||||
|
// Make sure the transparent pixel is transparent in the colormap
|
||||||
|
if (mGIFStruct.is_transparent) {
|
||||||
|
mOldColor = mColormap[mGIFStruct.tpixel];
|
||||||
|
mColormap[mGIFStruct.tpixel] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Initialize LZW parser/decoder */
|
/* Initialize LZW parser/decoder */
|
||||||
mGIFStruct.datasize = *q;
|
mGIFStruct.datasize = *q;
|
||||||
const int clear_code = ClearCode();
|
const int clear_code = ClearCode();
|
||||||
@ -817,9 +769,6 @@ nsresult nsGIFDecoder2::GifWrite(const PRUint8 *buf, PRUint32 len)
|
|||||||
// Not used
|
// Not used
|
||||||
// float aspect = (float)((q[6] + 15) / 64.0);
|
// float aspect = (float)((q[6] + 15) / 64.0);
|
||||||
|
|
||||||
// Start the GIF container
|
|
||||||
BeginGIF();
|
|
||||||
|
|
||||||
if (q[4] & 0x80) { /* global map */
|
if (q[4] & 0x80) { /* global map */
|
||||||
// Get the global colormap
|
// Get the global colormap
|
||||||
const PRUint32 size = 3*mGIFStruct.global_colormap_size;
|
const PRUint32 size = 3*mGIFStruct.global_colormap_size;
|
||||||
@ -840,17 +789,9 @@ nsresult nsGIFDecoder2::GifWrite(const PRUint8 *buf, PRUint32 len)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case gif_global_colormap:
|
case gif_global_colormap:
|
||||||
if (gfxPlatform::IsCMSEnabled()) {
|
// Everything is already copied into global_colormap
|
||||||
// Everything is already copied into global_colormap
|
// Convert into Cairo colors including CMS transformation
|
||||||
cmsHTRANSFORM transform = gfxPlatform::GetCMSRGBTransform();
|
ConvertColormap(mGIFStruct.global_colormap, mGIFStruct.global_colormap_size);
|
||||||
if (transform) {
|
|
||||||
cmsDoTransform(transform,
|
|
||||||
mGIFStruct.global_colormap,
|
|
||||||
mGIFStruct.global_colormap,
|
|
||||||
mGIFStruct.global_colormap_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GETN(1, gif_image_start);
|
GETN(1, gif_image_start);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1001,20 +942,20 @@ nsresult nsGIFDecoder2::GifWrite(const PRUint8 *buf, PRUint32 len)
|
|||||||
mGIFStruct.width = GETINT16(q + 4);
|
mGIFStruct.width = GETINT16(q + 4);
|
||||||
mGIFStruct.height = GETINT16(q + 6);
|
mGIFStruct.height = GETINT16(q + 6);
|
||||||
|
|
||||||
/* Work around broken GIF files where the logical screen
|
if (!mGIFStruct.images_decoded) {
|
||||||
* size has weird width or height. We assume that GIF87a
|
/* Work around broken GIF files where the logical screen
|
||||||
* files don't contain animations.
|
* size has weird width or height. We assume that GIF87a
|
||||||
*/
|
* files don't contain animations.
|
||||||
if (!mGIFStruct.images_decoded &&
|
*/
|
||||||
((mGIFStruct.screen_height < mGIFStruct.height) ||
|
if ((mGIFStruct.screen_height < mGIFStruct.height) ||
|
||||||
(mGIFStruct.screen_width < mGIFStruct.width) ||
|
(mGIFStruct.screen_width < mGIFStruct.width) ||
|
||||||
(mGIFStruct.version == 87)))
|
(mGIFStruct.version == 87)) {
|
||||||
{
|
mGIFStruct.screen_height = mGIFStruct.height;
|
||||||
mGIFStruct.screen_height = mGIFStruct.height;
|
mGIFStruct.screen_width = mGIFStruct.width;
|
||||||
mGIFStruct.screen_width = mGIFStruct.width;
|
mGIFStruct.x_offset = 0;
|
||||||
mGIFStruct.x_offset = 0;
|
mGIFStruct.y_offset = 0;
|
||||||
mGIFStruct.y_offset = 0;
|
}
|
||||||
|
// Create the image container with the right size.
|
||||||
BeginGIF();
|
BeginGIF();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1031,25 +972,6 @@ nsresult nsGIFDecoder2::GifWrite(const PRUint8 *buf, PRUint32 len)
|
|||||||
|
|
||||||
BeginImageFrame();
|
BeginImageFrame();
|
||||||
|
|
||||||
/* This case will never be taken if this is the first image */
|
|
||||||
/* being decoded. If any of the later images are larger */
|
|
||||||
/* than the screen size, we need to reallocate buffers. */
|
|
||||||
if (mGIFStruct.screen_width < mGIFStruct.width) {
|
|
||||||
/* XXX Deviant! */
|
|
||||||
|
|
||||||
mGIFStruct.rowbuf = (PRUint8*)PR_REALLOC(mGIFStruct.rowbuf, mGIFStruct.width);
|
|
||||||
mGIFStruct.screen_width = mGIFStruct.width;
|
|
||||||
} else if (!mGIFStruct.rowbuf) {
|
|
||||||
mGIFStruct.rowbuf = (PRUint8*)PR_MALLOC(mGIFStruct.screen_width);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mGIFStruct.rowbuf) {
|
|
||||||
mGIFStruct.state = gif_oom;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (mGIFStruct.screen_height < mGIFStruct.height)
|
|
||||||
mGIFStruct.screen_height = mGIFStruct.height;
|
|
||||||
|
|
||||||
if (q[8] & 0x40) {
|
if (q[8] & 0x40) {
|
||||||
mGIFStruct.interlaced = PR_TRUE;
|
mGIFStruct.interlaced = PR_TRUE;
|
||||||
mGIFStruct.ipass = 1;
|
mGIFStruct.ipass = 1;
|
||||||
@ -1064,8 +986,7 @@ nsresult nsGIFDecoder2::GifWrite(const PRUint8 *buf, PRUint32 len)
|
|||||||
/* Clear state from last image */
|
/* Clear state from last image */
|
||||||
mGIFStruct.irow = 0;
|
mGIFStruct.irow = 0;
|
||||||
mGIFStruct.rows_remaining = mGIFStruct.height;
|
mGIFStruct.rows_remaining = mGIFStruct.height;
|
||||||
mGIFStruct.rowend = mGIFStruct.rowbuf + mGIFStruct.width;
|
mGIFStruct.rowp = mImageData;
|
||||||
mGIFStruct.rowp = mGIFStruct.rowbuf;
|
|
||||||
|
|
||||||
/* bits per pixel is q[8]&0x07 */
|
/* bits per pixel is q[8]&0x07 */
|
||||||
|
|
||||||
@ -1073,19 +994,18 @@ nsresult nsGIFDecoder2::GifWrite(const PRUint8 *buf, PRUint32 len)
|
|||||||
{
|
{
|
||||||
const int num_colors = 2 << (q[8] & 0x7);
|
const int num_colors = 2 << (q[8] & 0x7);
|
||||||
const PRUint32 size = 3*num_colors;
|
const PRUint32 size = 3*num_colors;
|
||||||
PRUint8 *map = mGIFStruct.local_colormap;
|
if (!mGIFStruct.local_colormap) {
|
||||||
if (!map || (num_colors > mGIFStruct.local_colormap_size)) {
|
mGIFStruct.local_colormap =
|
||||||
map = (PRUint8*)PR_REALLOC(map, size);
|
(PRUint32*)PR_MALLOC(MAX_COLORS * sizeof(PRUint32));
|
||||||
if (!map) {
|
if (!mGIFStruct.local_colormap) {
|
||||||
mGIFStruct.state = gif_oom;
|
mGIFStruct.state = gif_oom;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
mGIFStruct.local_colormap = map;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Switch to the new local palette after it loads */
|
/* Switch to the new local palette after it loads */
|
||||||
mGIFStruct.local_colormap_size = num_colors;
|
mGIFStruct.local_colormap_size = num_colors;
|
||||||
mGIFStruct.is_local_colormap_defined = PR_TRUE;
|
mColormap = mGIFStruct.local_colormap;
|
||||||
|
|
||||||
if (len < size) {
|
if (len < size) {
|
||||||
// Use 'hold' pattern to get the image colormap
|
// Use 'hold' pattern to get the image colormap
|
||||||
@ -1100,23 +1020,15 @@ nsresult nsGIFDecoder2::GifWrite(const PRUint8 *buf, PRUint32 len)
|
|||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
/* Switch back to the global palette */
|
/* Switch back to the global palette */
|
||||||
mGIFStruct.is_local_colormap_defined = PR_FALSE;
|
mColormap = mGIFStruct.global_colormap;
|
||||||
}
|
}
|
||||||
GETN(1, gif_lzw_start);
|
GETN(1, gif_lzw_start);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case gif_image_colormap:
|
case gif_image_colormap:
|
||||||
if (gfxPlatform::IsCMSEnabled()) {
|
// Everything is already copied into local_colormap
|
||||||
// Everything is already copied into local_colormap
|
// Convert into Cairo colors including CMS transformation
|
||||||
cmsHTRANSFORM transform = gfxPlatform::GetCMSRGBTransform();
|
ConvertColormap(mGIFStruct.local_colormap, mGIFStruct.local_colormap_size);
|
||||||
if (transform) {
|
|
||||||
cmsDoTransform(transform,
|
|
||||||
mGIFStruct.local_colormap,
|
|
||||||
mGIFStruct.local_colormap,
|
|
||||||
mGIFStruct.local_colormap_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GETN(1, gif_lzw_start);
|
GETN(1, gif_lzw_start);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1163,8 +1075,8 @@ nsresult nsGIFDecoder2::GifWrite(const PRUint8 *buf, PRUint32 len)
|
|||||||
mGIFStruct.bytes_in_hold = len;
|
mGIFStruct.bytes_in_hold = len;
|
||||||
if (len) {
|
if (len) {
|
||||||
// Add what we have sofar to the block
|
// Add what we have sofar to the block
|
||||||
PRUint8* p = (mGIFStruct.state == gif_global_colormap) ? mGIFStruct.global_colormap :
|
PRUint8* p = (mGIFStruct.state == gif_global_colormap) ? (PRUint8*)mGIFStruct.global_colormap :
|
||||||
(mGIFStruct.state == gif_image_colormap) ? mGIFStruct.local_colormap :
|
(mGIFStruct.state == gif_image_colormap) ? (PRUint8*)mGIFStruct.local_colormap :
|
||||||
mGIFStruct.hold;
|
mGIFStruct.hold;
|
||||||
memcpy(p, buf, len);
|
memcpy(p, buf, len);
|
||||||
mGIFStruct.bytes_to_consume -= len;
|
mGIFStruct.bytes_to_consume -= len;
|
||||||
|
@ -79,15 +79,11 @@ private:
|
|||||||
void BeginImageFrame();
|
void BeginImageFrame();
|
||||||
void EndImageFrame();
|
void EndImageFrame();
|
||||||
void FlushImageData();
|
void FlushImageData();
|
||||||
|
void FlushImageData(PRUint32 fromRow, PRUint32 rows);
|
||||||
|
|
||||||
nsresult GifWrite(const PRUint8 * buf, PRUint32 numbytes);
|
nsresult GifWrite(const PRUint8 * buf, PRUint32 numbytes);
|
||||||
PRUint32 OutputRow();
|
PRUint32 OutputRow();
|
||||||
PRBool DoLzw(const PRUint8 *q);
|
PRBool DoLzw(const PRUint8 *q);
|
||||||
void HaveDecodedRow(
|
|
||||||
PRUint8* aRowBufPtr, /* Pointer to single scanline temporary buffer */
|
|
||||||
int aRow, /* Row number? */
|
|
||||||
int aDuplicateCount, /* Number of times to duplicate the row? */
|
|
||||||
int aInterlacePass);
|
|
||||||
|
|
||||||
inline int ClearCode() const { return 1 << mGIFStruct.datasize; }
|
inline int ClearCode() const { return 1 << mGIFStruct.datasize; }
|
||||||
|
|
||||||
@ -97,12 +93,12 @@ private:
|
|||||||
PRInt32 mCurrentRow;
|
PRInt32 mCurrentRow;
|
||||||
PRInt32 mLastFlushedRow;
|
PRInt32 mLastFlushedRow;
|
||||||
|
|
||||||
PRUint8 *mRGBLine;
|
PRUint32 *mImageData; // Pointer to image data in Cairo format
|
||||||
PRUint32 mRGBLineMaxSize;
|
PRUint32 *mColormap; // Current colormap to be used in Cairo format
|
||||||
|
PRUint32 mOldColor; // The old value of the transparent pixel
|
||||||
PRUint8 mCurrentPass;
|
PRUint8 mCurrentPass;
|
||||||
PRUint8 mLastFlushedPass;
|
PRUint8 mLastFlushedPass;
|
||||||
PRPackedBool mGIFOpen;
|
PRPackedBool mGIFOpen;
|
||||||
PRPackedBool mFrameHasNoAlpha;
|
|
||||||
|
|
||||||
gif_struct mGIFStruct;
|
gif_struct mGIFStruct;
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user