--- a/gfx/idl/gfxIFormats.idl
+++ b/gfx/idl/gfxIFormats.idl
@@ -92,9 +92,23 @@ interface gfxIFormats
* RGBA - packed RGBA image
*/
const gfx_format RGBA = 6;
/**
* BGRA - packed RGBA image
*/
const gfx_format BGRA = 7;
+
+ /**
+ * PAL - Palette based image data, all opaque colors
+ * PRUint32 colormap[256];
+ * PRUint8 pixels[width*height];
+ */
+ const gfx_format PAL = 8;
+
+ /**
+ * PAL_A1 - Palette based image data, with transparency
+ * PRUint32 colormap[256];
+ * PRUint8 pixels[width*height];
+ */
+ const gfx_format PAL_A1 = 9;
};
--- a/gfx/idl/gfxIImageFrame.idl
+++ b/gfx/idl/gfxIImageFrame.idl
@@ -52,17 +52,17 @@ native nsRectRef(nsIntRect &);
* gfxIImageFrame interface
*
* All x, y, width, height values are in pixels.
*
* @author Tim Rowley <tor@cs.brown.edu>
* @author Stuart Parmenter <pavlov@netscape.com>
* @version 0.1
*/
-[scriptable, uuid(7292afa2-3b94-424d-97d5-51ce2f04c0fe)]
+[scriptable, uuid(9c37930b-cadd-453c-89e1-9ed456715b9c)]
interface gfxIImageFrame : nsISupports
{
/**
* Create a new \a aWidth x \a aHeight sized image.
*
* @param aX The x-offset from the origin of the gfxIImageContainer parent.
* @param aY The y-offset from the origin of the gfxIImageContainer parent.
* @param aWidth The width of the image to create.
@@ -128,16 +128,21 @@ interface gfxIImageFrame : nsISupports
* returns the number of bytes allocated for the image
*/
readonly attribute unsigned long imageDataLength;
// XXX do we copy here? lets not...
void getImageData([array, size_is(length)] out PRUint8 bits, out unsigned long length);
/**
+ * Get Palette data pointer
+ */
+ void getPaletteData([array, size_is(length)] out gfx_color palette, out unsigned long length);
+
+ /**
* Lock image pixels before addressing the data directly
*/
void lockImageData();
/**
* Unlock image pixels
*/
void unlockImageData();
--- a/gfx/src/shared/gfxImageFrame.cpp
+++ b/gfx/src/shared/gfxImageFrame.cpp
@@ -35,32 +35,36 @@
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "gfxImageFrame.h"
#include "nsIServiceManager.h"
#include <limits.h>
+#include "prmem.h"
NS_IMPL_ISUPPORTS2(gfxImageFrame, gfxIImageFrame, nsIInterfaceRequestor)
gfxImageFrame::gfxImageFrame() :
- mInitialized(PR_FALSE),
- mMutable(PR_TRUE),
+ mImageData(nsnull),
mTimeout(100),
mDisposalMethod(0), /* imgIContainer::kDisposeNotSpecified */
- mBlendMethod(1) /* imgIContainer::kBlendOver */
+ mBlendMethod(1), /* imgIContainer::kBlendOver */
+ mInitialized(PR_FALSE),
+ mMutable(PR_TRUE)
{
/* member initializers and constructor code */
}
gfxImageFrame::~gfxImageFrame()
{
/* destructor code */
+ PR_FREEIF(mImageData);
+ mInitialized = PR_FALSE;
}
/* void init (in PRInt32 aX, in PRInt32 aY, in PRInt32 aWidth, in PRInt32 aHeight, in gfx_format aFormat); */
NS_IMETHODIMP gfxImageFrame::Init(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight, gfx_format aFormat,gfx_depth aDepth)
{
if (mInitialized)
return NS_ERROR_FAILURE;
@@ -78,47 +82,38 @@ NS_IMETHODIMP gfxImageFrame::Init(PRInt3
return NS_ERROR_FAILURE;
}
tmp = tmp * 4;
if (tmp / 4 != aWidth * aHeight) {
NS_WARNING("width or height too large");
return NS_ERROR_FAILURE;
}
- if ( (aDepth != 8) && (aDepth != 24) ){
- NS_ERROR("This Depth is not supported");
- return NS_ERROR_FAILURE;
- }
-
/* reject over-wide or over-tall images */
const PRInt32 k64KLimit = 0x0000FFFF;
if ( aWidth > k64KLimit || aHeight > k64KLimit ){
NS_WARNING("image too big");
return NS_ERROR_FAILURE;
}
#if defined(XP_MACOSX)
// CoreGraphics is limited to images < 32K in *height*, so clamp all surfaces on the Mac to that height
if (aHeight > SHRT_MAX) {
NS_WARNING("image too big");
return NS_ERROR_FAILURE;
}
#endif
- nsresult rv;
-
mOffset.MoveTo(aX, aY);
mSize.SizeTo(aWidth, aHeight);
mFormat = aFormat;
+ mDepth = aDepth;
- mImage = do_CreateInstance("@mozilla.org/gfx/image;1", &rv);
- NS_ASSERTION(mImage, "creation of image failed");
- if (NS_FAILED(rv)) return rv;
-
+ PRBool needImage = PR_TRUE;
nsMaskRequirements maskReq;
switch (aFormat) {
case gfxIFormats::BGR:
case gfxIFormats::RGB:
maskReq = nsMaskRequirements_kNoMask;
break;
@@ -132,25 +127,47 @@ NS_IMETHODIMP gfxImageFrame::Init(PRInt3
maskReq = nsMaskRequirements_kNeeds1Bit;
break;
case gfxIFormats::BGR_A8:
case gfxIFormats::RGB_A8:
maskReq = nsMaskRequirements_kNeeds8Bit;
break;
+ case gfxIFormats::PAL:
+ case gfxIFormats::PAL_A1:
+ needImage = PR_FALSE;
+ break;
+
default:
-#ifdef DEBUG
- printf("unsupported gfx_format\n");
-#endif
- break;
+ NS_ERROR("unsupported gfx_format\n");
+ return NS_ERROR_FAILURE;
}
- rv = mImage->Init(aWidth, aHeight, aDepth, maskReq);
- if (NS_FAILED(rv)) return rv;
+ if (needImage) {
+ if (aDepth != 24) {
+ NS_ERROR("This Depth is not supported");
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+ mImage = do_CreateInstance("@mozilla.org/gfx/image;1", &rv);
+ NS_ASSERTION(mImage, "creation of image failed");
+ if (NS_FAILED(rv)) return rv;
+
+ rv = mImage->Init(aWidth, aHeight, aDepth, maskReq);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ if ((aDepth < 1) || (aDepth > 8)) {
+ NS_ERROR("This Depth is not supported\n");
+ return NS_ERROR_FAILURE;
+ }
+ mImageData = (PRUint8*)PR_MALLOC(PaletteDataLength() + ImageDataLength());
+ NS_ENSURE_TRUE(mImageData, NS_ERROR_OUT_OF_MEMORY);
+ }
mInitialized = PR_TRUE;
return NS_OK;
}
/* attribute boolean mutable */
NS_IMETHODIMP gfxImageFrame::GetMutable(PRBool *aMutable)
@@ -165,17 +182,17 @@ NS_IMETHODIMP gfxImageFrame::GetMutable(
NS_IMETHODIMP gfxImageFrame::SetMutable(PRBool aMutable)
{
if (!mInitialized)
return NS_ERROR_NOT_INITIALIZED;
mMutable = aMutable;
- if (!aMutable)
+ if (!aMutable && mImage)
mImage->Optimize(nsnull);
return NS_OK;
}
/* readonly attribute PRInt32 x; */
NS_IMETHODIMP gfxImageFrame::GetX(PRInt32 *aX)
{
@@ -240,69 +257,91 @@ NS_IMETHODIMP gfxImageFrame::GetFormat(g
/* readonly attribute boolean needsBackground; */
NS_IMETHODIMP gfxImageFrame::GetNeedsBackground(PRBool *aNeedsBackground)
{
if (!mInitialized)
return NS_ERROR_NOT_INITIALIZED;
*aNeedsBackground = (mFormat != gfxIFormats::RGB &&
+ mFormat != gfxIFormats::PAL &&
mFormat != gfxIFormats::BGR) ||
!mImage->GetIsImageComplete();
return NS_OK;
}
/* readonly attribute unsigned long imageBytesPerRow; */
NS_IMETHODIMP gfxImageFrame::GetImageBytesPerRow(PRUint32 *aBytesPerRow)
{
if (!mInitialized)
return NS_ERROR_NOT_INITIALIZED;
- *aBytesPerRow = mImage->GetLineStride();
+ *aBytesPerRow = mImage ? mImage->GetLineStride(): mSize.width;
return NS_OK;
}
/* readonly attribute unsigned long imageDataLength; */
NS_IMETHODIMP gfxImageFrame::GetImageDataLength(PRUint32 *aBitsLength)
{
if (!mInitialized)
return NS_ERROR_NOT_INITIALIZED;
- *aBitsLength = mImage->GetLineStride() * mSize.height;
+ *aBitsLength = ImageDataLength();
return NS_OK;
}
/* void getImageData([array, size_is(length)] out PRUint8 bits, out unsigned long length); */
NS_IMETHODIMP gfxImageFrame::GetImageData(PRUint8 **aData, PRUint32 *length)
{
if (!mInitialized)
return NS_ERROR_NOT_INITIALIZED;
- *aData = mImage->GetBits();
- *length = mImage->GetLineStride() * mSize.height;
+ NS_ASSERTION(mMutable, "trying to get data on an immutable frame");
+
+ *aData = mImage ? mImage->GetBits() : (mImageData + PaletteDataLength());
+ *length = ImageDataLength();
+
+ return NS_OK;
+}
+
+/* void getPaletteData ([array, size_is (length)] out PRUint32 palette, out unsigned long length); */
+NS_IMETHODIMP gfxImageFrame::GetPaletteData(gfx_color **aPalette, PRUint32 *length)
+{
+ if (!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ if (!mImageData)
+ return NS_ERROR_FAILURE;
+
+ *aPalette = (gfx_color*)mImageData;
+ *length = PaletteDataLength();
return NS_OK;
}
/* void lockImageData (); */
NS_IMETHODIMP gfxImageFrame::LockImageData()
{
if (!mInitialized)
return NS_ERROR_NOT_INITIALIZED;
+ if (!mImage)
+ return NS_OK;
return mImage->LockImagePixels(PR_FALSE);
}
/* void unlockImageData (); */
NS_IMETHODIMP gfxImageFrame::UnlockImageData()
{
if (!mInitialized)
return NS_ERROR_NOT_INITIALIZED;
+ if (!mImage)
+ return NS_OK;
return mImage->UnlockImagePixels(PR_FALSE);
}
/* attribute long timeout; */
NS_IMETHODIMP gfxImageFrame::GetTimeout(PRInt32 *aTimeout)
{
if (!mInitialized)
return NS_ERROR_NOT_INITIALIZED;
--- a/gfx/src/shared/gfxImageFrame.h
+++ b/gfx/src/shared/gfxImageFrame.h
@@ -66,19 +66,29 @@ public:
gfxImageFrame();
virtual ~gfxImageFrame();
protected:
nsIntSize mSize;
private:
- nsCOMPtr<nsIImage> mImage;
+ PRUint32 PaletteDataLength() const {
+ return ((1 << mDepth) * sizeof(gfx_color));
+ }
- PRPackedBool mInitialized;
- PRPackedBool mMutable;
- gfx_format mFormat;
+ PRUint32 ImageDataLength() const {
+ return (mImage ? mImage->GetLineStride() : mSize.width) * mSize.height;
+ }
+
+ nsCOMPtr<nsIImage> mImage;
+ PRUint8* mImageData;
PRInt32 mTimeout; // -1 means display forever
nsIntPoint mOffset;
PRInt32 mDisposalMethod;
+
+ gfx_format mFormat;
+ gfx_depth mDepth;
PRInt8 mBlendMethod;
+ PRPackedBool mInitialized;
+ PRPackedBool mMutable;
};
--- a/modules/libpr0n/decoders/gif/GIF2.h
+++ b/modules/libpr0n/decoders/gif/GIF2.h
@@ -97,33 +97,33 @@ typedef struct gif_struct {
int count; /* Remaining # bytes in sub-block */
int bits; /* Number of unread bits in "datum" */
int32 datum; /* 32-bit input buffer */
/* Output state machine */
int ipass; /* Interlace pass; Ranges 1-4 if interlaced. */
PRUintn rows_remaining; /* Rows remaining to be output */
PRUintn irow; /* Current output row, starting at zero */
- PRUint32 *rowp; /* Current output pointer */
+ PRUint8 *rowp; /* Current output pointer */
/* Parameters for image frame currently being decoded*/
PRUintn x_offset, y_offset; /* With respect to "screen" origin */
PRUintn height, width;
int tpixel; /* Index of transparent pixel */
PRInt32 disposal_method; /* Restore to background, leave in place, etc.*/
PRUint32 *local_colormap; /* Per-image colormap */
int local_colormap_size; /* Size of local colormap array. */
PRUint32 delay_time; /* Display time, in milliseconds,
for this image in a multi-image GIF */
/* Global (multi-image) state */
int version; /* Either 89 for GIF89 or 87 for GIF87 */
PRUintn screen_width; /* Logical screen width & height */
PRUintn screen_height;
- PRUint32 global_colormap_size; /* Size of global colormap array. */
+ PRUint32 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. */
PRPackedBool progressive_display; /* If TRUE, do Haeberli interlace hack */
PRPackedBool interlaced; /* TRUE, if scanlines arrive interlaced order */
PRPackedBool is_transparent; /* TRUE, if tpixel is valid */
--- a/modules/libpr0n/decoders/gif/nsGIFDecoder2.cpp
+++ b/modules/libpr0n/decoders/gif/nsGIFDecoder2.cpp
@@ -88,17 +88,17 @@ mailing address.
#include "gfxPlatform.h"
#include "lcms.h"
/*
* GETN(n, s) requests at least 'n' bytes available from 'q', at start of state 's'
*
* Note, the hold will never need to be bigger than 256 bytes to gather up in the hold,
* as each GIF block (except colormaps) can never be bigger than 256 bytes.
- * Colormaps are directly copied in the resp. global_colormap or dynamically allocated local_colormap.
+ * Colormaps are directly copied in the resp. global_colormap or the local_colormap of the PAL image frame
* So a fixed buffer in gif_struct is good enough.
* This buffer is only needed to copy left-over data from one GifWrite call to the next
*/
#define GETN(n,s) \
PR_BEGIN_MACRO \
mGIFStruct.bytes_to_consume = (n); \
mGIFStruct.state = (s); \
PR_END_MACRO
@@ -237,17 +237,18 @@ nsGIFDecoder2::FlushImageData()
//******************************************************************************
nsresult nsGIFDecoder2::ProcessData(unsigned char *data, PRUint32 count, PRUint32 *_retval)
{
// Push the data to the GIF decoder
nsresult rv = GifWrite(data, count);
NS_ENSURE_SUCCESS(rv, rv);
- if (mImageFrame) {
+ // Flushing is only needed for first frame
+ if (!mGIFStruct.images_decoded && mImageFrame) {
FlushImageData();
mLastFlushedRow = mCurrentRow;
mLastFlushedPass = mCurrentPass;
}
*_retval = count;
return NS_OK;
@@ -308,77 +309,85 @@ void nsGIFDecoder2::EndGIF()
mImageContainer->SetLoopCount(mGIFStruct.loop_count);
mImageContainer->DecodingComplete();
mGIFOpen = PR_FALSE;
}
//******************************************************************************
-void nsGIFDecoder2::BeginImageFrame()
+void nsGIFDecoder2::BeginImageFrame(gfx_depth aDepth)
{
mImageFrame = nsnull; // clear out our current frame reference
if (!mGIFStruct.images_decoded) {
// Send a onetime OnDataAvailable (Display Refresh) 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) {
PRInt32 imgWidth;
mImageContainer->GetWidth(&imgWidth);
nsIntRect r(0, 0, imgWidth, mGIFStruct.y_offset);
mObserver->OnDataAvailable(nsnull, mImageFrame, &r);
}
}
- gfx_format format = gfxIFormats::RGB;
- if (mGIFStruct.is_transparent) {
- format = gfxIFormats::RGB_A1; // XXX not really
+ // Use correct format, RGB for first frame, PAL for following frames
+ // and include transparency to allow for optimization of opaque images
+ gfx_format format;
+ if (mGIFStruct.images_decoded) {
+ // Image data is stored with original depth and palette
+ format = mGIFStruct.is_transparent ? gfxIFormats::PAL_A1 : gfxIFormats::PAL;
+ } else {
+ // Regardless of depth of input, image is decoded into 24bit RGB
+ format = mGIFStruct.is_transparent ? gfxIFormats::RGB_A1 : gfxIFormats::RGB;
+ aDepth = 24;
}
// 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))) {
+ mGIFStruct.width, mGIFStruct.height, format, aDepth))) {
mImageFrame = 0;
return;
}
mImageFrame->SetFrameDisposalMethod(mGIFStruct.disposal_method);
mImageContainer->AppendFrame(mImageFrame);
if (mObserver)
mObserver->OnStartFrame(nsnull, mImageFrame);
PRUint32 imageDataLength;
- mImageFrame->GetImageData((PRUint8 **)&mImageData, &imageDataLength);
+ mImageFrame->GetImageData(&mImageData, &imageDataLength);
}
//******************************************************************************
void nsGIFDecoder2::EndImageFrame()
{
// First flush all pending image data
- FlushImageData();
- mCurrentRow = mLastFlushedRow = -1;
- mCurrentPass = mLastFlushedPass = 0;
+ if (!mGIFStruct.images_decoded) {
+ // Only need to flush first frame
+ FlushImageData();
- 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);
}
}
+ mCurrentRow = mLastFlushedRow = -1;
+ mCurrentPass = mLastFlushedPass = 0;
mGIFStruct.images_decoded++;
// 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);
@@ -400,66 +409,76 @@ void nsGIFDecoder2::EndImageFrame()
//******************************************************************************
// Send the data to the display front-end.
PRUint32 nsGIFDecoder2::OutputRow()
{
int drow_start, drow_end;
drow_start = drow_end = mGIFStruct.irow;
- /*
- * Haeberli-inspired hack for interlaced GIFs: Replicate lines while
- * displaying to diminish the "venetian-blind" effect as the image is
- * loaded. Adjust pixel vertical positions to avoid the appearance of the
- * image crawling up the screen as successive passes are drawn.
- */
- if (mGIFStruct.progressive_display && mGIFStruct.interlaced && (mGIFStruct.ipass < 4)) {
- /* ipass = 1,2,3 results in resp. row_dup = 7,3,1 and row_shift = 3,1,0 */
- const PRUint32 row_dup = 15 >> mGIFStruct.ipass;
- const PRUint32 row_shift = row_dup >> 1;
-
- drow_start -= row_shift;
- drow_end = drow_start + row_dup;
-
- /* Extend if bottom edge isn't covered because of the shift upward. */
- if (((mGIFStruct.height - 1) - drow_end) <= row_shift)
- drow_end = mGIFStruct.height - 1;
-
- /* Clamp first and last rows to upper and lower edge of image. */
- if (drow_start < 0)
- drow_start = 0;
- if ((PRUintn)drow_end >= mGIFStruct.height)
- drow_end = mGIFStruct.height - 1;
- }
-
/* Protect against too much image data */
if ((PRUintn)drow_start >= mGIFStruct.height) {
NS_WARNING("GIF2.cpp::OutputRow - too much image data");
return 0;
}
+ if (!mGIFStruct.images_decoded) {
+ /*
+ * Haeberli-inspired hack for interlaced GIFs: Replicate lines while
+ * displaying to diminish the "venetian-blind" effect as the image is
+ * loaded. Adjust pixel vertical positions to avoid the appearance of the
+ * image crawling up the screen as successive passes are drawn.
+ */
+ if (mGIFStruct.progressive_display && mGIFStruct.interlaced && (mGIFStruct.ipass < 4)) {
+ /* ipass = 1,2,3 results in resp. row_dup = 7,3,1 and row_shift = 3,1,0 */
+ const PRUint32 row_dup = 15 >> mGIFStruct.ipass;
+ const PRUint32 row_shift = row_dup >> 1;
- // Duplicate rows
- if (drow_end > drow_start) {
- // irow is the current row filled
- const PRUint32 width = mGIFStruct.width;
- PRUint32 *rgbRowIndex = mImageData + mGIFStruct.irow * width;
- for (int r = drow_start; r <= drow_end; r++) {
- if (r != mGIFStruct.irow) {
- memcpy(mImageData + r * width, rgbRowIndex, width*sizeof(PRUint32));
+ drow_start -= row_shift;
+ drow_end = drow_start + row_dup;
+
+ /* Extend if bottom edge isn't covered because of the shift upward. */
+ if (((mGIFStruct.height - 1) - drow_end) <= row_shift)
+ drow_end = mGIFStruct.height - 1;
+
+ /* Clamp first and last rows to upper and lower edge of image. */
+ if (drow_start < 0)
+ drow_start = 0;
+ if ((PRUintn)drow_end >= mGIFStruct.height)
+ drow_end = mGIFStruct.height - 1;
+ }
+
+ // Row to process
+ const PRUint32 bpr = sizeof(PRUint32) * mGIFStruct.width;
+ PRUint8 *rowp = mImageData + (mGIFStruct.irow * bpr);
+
+ // Convert color indices to Cairo pixels
+ PRUint8 *from = rowp + mGIFStruct.width;
+ PRUint32 *to = ((PRUint32*)rowp) + mGIFStruct.width;
+ PRUint32 *cmap = mColormap;
+ for (PRUint32 c = mGIFStruct.width; c > 0; c--) {
+ *--to = cmap[*--from];
+ }
+
+ // Duplicate rows
+ if (drow_end > drow_start) {
+ // irow is the current row filled
+ for (int r = drow_start; r <= drow_end; r++) {
+ if (r != mGIFStruct.irow) {
+ memcpy(mImageData + (r * bpr), rowp, bpr);
+ }
}
}
}
mCurrentRow = drow_end;
mCurrentPass = mGIFStruct.ipass;
if (mGIFStruct.ipass == 1)
mLastFlushedPass = mGIFStruct.ipass; // interlaced starts at 1
-
if (!mGIFStruct.interlaced) {
mGIFStruct.irow++;
} else {
static const PRUint8 kjump[5] = { 1, 8, 8, 4, 2 };
do {
// Row increments resp. per 8,8,4,2 rows
mGIFStruct.irow += kjump[mGIFStruct.ipass];
if (mGIFStruct.irow >= mGIFStruct.height) {
@@ -493,24 +512,28 @@ nsGIFDecoder2::DoLzw(const PRUint8 *q)
int oldcode = mGIFStruct.oldcode;
const int clear_code = ClearCode();
PRUint8 firstchar = mGIFStruct.firstchar;
PRInt32 datum = mGIFStruct.datum;
PRUint16 *prefix = mGIFStruct.prefix;
PRUint8 *stackp = mGIFStruct.stackp;
PRUint8 *suffix = mGIFStruct.suffix;
PRUint8 *stack = mGIFStruct.stack;
- PRUint32 *rowp = mGIFStruct.rowp;
- PRUint32 *rowend = mImageData + (mGIFStruct.irow + 1) * mGIFStruct.width;
- PRUint32 *cmap = mColormap;
+ PRUint8 *rowp = mGIFStruct.rowp;
+
+ PRUint32 bpr = mGIFStruct.width;
+ if (!mGIFStruct.images_decoded)
+ bpr *= sizeof(PRUint32);
+ PRUint8 *rowend = mImageData + (bpr * mGIFStruct.irow) + mGIFStruct.width;
+
#define OUTPUT_ROW() \
PR_BEGIN_MACRO \
if (!OutputRow()) \
goto END; \
- rowp = mImageData + mGIFStruct.irow * mGIFStruct.width; \
+ rowp = mImageData + mGIFStruct.irow * bpr; \
rowend = rowp + mGIFStruct.width; \
PR_END_MACRO
for (const PRUint8* ch = q; count-- > 0; ch++)
{
/* Feed the next byte into the decoder's 32-bit input buffer. */
datum += ((int32) *ch) << bits;
bits += 8;
@@ -534,17 +557,17 @@ nsGIFDecoder2::DoLzw(const PRUint8 *q)
/* Check for explicit end-of-stream code */
if (code == (clear_code + 1)) {
/* end-of-stream should only appear after all image data */
return (mGIFStruct.rows_remaining == 0);
}
if (oldcode == -1) {
- *rowp++ = cmap[suffix[code]];
+ *rowp++ = suffix[code];
if (rowp == rowend)
OUTPUT_ROW();
firstchar = oldcode = code;
continue;
}
int incode = code;
@@ -584,17 +607,17 @@ nsGIFDecoder2::DoLzw(const PRUint8 *q)
codesize++;
codemask += avail;
}
}
oldcode = incode;
/* Copy the decoded data out to the scanline buffer. */
do {
- *rowp++ = cmap[*--stackp];
+ *rowp++ = *--stackp;
if (rowp == rowend)
OUTPUT_ROW();
} while (stackp > stack);
}
}
END:
@@ -625,19 +648,16 @@ static void ConvertColormap(PRUint32 *aC
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]);
}
}
/******************************************************************************/
@@ -651,17 +671,17 @@ nsresult nsGIFDecoder2::GifWrite(const P
return NS_ERROR_FAILURE;
const PRUint8 *q = buf;
// Add what we have sofar to the 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
PRUint8* p = (mGIFStruct.state == gif_global_colormap) ? (PRUint8*)mGIFStruct.global_colormap :
- (mGIFStruct.state == gif_image_colormap) ? (PRUint8*)mGIFStruct.local_colormap :
+ (mGIFStruct.state == gif_image_colormap) ? (PRUint8*)mColormap :
(mGIFStruct.bytes_in_hold) ? mGIFStruct.hold : nsnull;
if (p) {
// Add what we have sofar to the block
PRUint32 l = PR_MIN(len, mGIFStruct.bytes_to_consume);
memcpy(p+mGIFStruct.bytes_in_hold, buf, l);
if (l < mGIFStruct.bytes_to_consume) {
// Not enough in 'buf' to complete current block, get more
@@ -698,17 +718,19 @@ nsresult nsGIFDecoder2::GifWrite(const P
}
GETN(1, gif_sub_block);
break;
case gif_lzw_start:
{
// Make sure the transparent pixel is transparent in the colormap
if (mGIFStruct.is_transparent) {
- mOldColor = mColormap[mGIFStruct.tpixel];
+ // Save old value so we can restore it later
+ if (mColormap == mGIFStruct.global_colormap)
+ mOldColor = mColormap[mGIFStruct.tpixel];
mColormap[mGIFStruct.tpixel] = 0;
}
/* Initialize LZW parser/decoder */
mGIFStruct.datasize = *q;
const int clear_code = ClearCode();
if (mGIFStruct.datasize > MAX_LZW_BITS ||
clear_code >= MAX_BITS) {
@@ -750,27 +772,27 @@ nsresult nsGIFDecoder2::GifWrite(const P
* frame into which images are rendered. The
* individual images can be smaller than the
* screen size and located with an origin anywhere
* within the screen.
*/
mGIFStruct.screen_width = GETINT16(q);
mGIFStruct.screen_height = GETINT16(q + 2);
- mGIFStruct.global_colormap_size = 2<<(q[4]&0x07);
+ mGIFStruct.global_colormap_depth = (q[4]&0x07) + 1;
// screen_bgcolor is not used
//mGIFStruct.screen_bgcolor = q[5];
// q[6] = Pixel Aspect Ratio
// Not used
// float aspect = (float)((q[6] + 15) / 64.0);
if (q[4] & 0x80) { /* global map */
// Get the global colormap
- const PRUint32 size = 3*mGIFStruct.global_colormap_size;
+ const PRUint32 size = (3 << mGIFStruct.global_colormap_depth);
if (len < size) {
// Use 'hold' pattern to get the global colormap
GETN(size, gif_global_colormap);
break;
}
// Copy everything, go to colormap state to do CMS correction
memcpy(mGIFStruct.global_colormap, buf, size);
buf += size;
@@ -780,17 +802,17 @@ nsresult nsGIFDecoder2::GifWrite(const P
}
GETN(1, gif_image_start);
break;
case gif_global_colormap:
// Everything is already copied into global_colormap
// Convert into Cairo colors including CMS transformation
- ConvertColormap(mGIFStruct.global_colormap, mGIFStruct.global_colormap_size);
+ ConvertColormap(mGIFStruct.global_colormap, 1<<mGIFStruct.global_colormap_depth);
GETN(1, gif_image_start);
break;
case gif_image_start:
switch (*q) {
case GIF_TRAILER:
mGIFStruct.state = gif_done;
break;
@@ -923,16 +945,17 @@ nsresult nsGIFDecoder2::GifWrite(const P
default:
// 0,3-7 are yet to be defined netscape extension codes
mGIFStruct.state = gif_error;
}
break;
case gif_image_header:
+ {
/* Get image offsets, with respect to the screen origin */
mGIFStruct.x_offset = GETINT16(q);
mGIFStruct.y_offset = GETINT16(q + 2);
/* Get image width and height. */
mGIFStruct.width = GETINT16(q + 4);
mGIFStruct.height = GETINT16(q + 6);
@@ -959,17 +982,23 @@ nsresult nsGIFDecoder2::GifWrite(const P
mGIFStruct.height = mGIFStruct.screen_height;
mGIFStruct.width = mGIFStruct.screen_width;
if (!mGIFStruct.height || !mGIFStruct.width) {
mGIFStruct.state = gif_error;
break;
}
}
- BeginImageFrame();
+ /* 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 */
+ PRUint32 depth = mGIFStruct.global_colormap_depth;
+ if (q[8] & 0x80)
+ depth = (q[8]&0x07) + 1;
+ BeginImageFrame(depth);
// handle allocation error
if (!mImageFrame) {
mGIFStruct.state = gif_error;
break;
}
if (q[8] & 0x40) {
@@ -987,53 +1016,65 @@ nsresult nsGIFDecoder2::GifWrite(const P
mGIFStruct.irow = 0;
mGIFStruct.rows_remaining = mGIFStruct.height;
mGIFStruct.rowp = mImageData;
/* bits per pixel is q[8]&0x07 */
if (q[8] & 0x80) /* has a local colormap? */
{
- const int num_colors = 2 << (q[8] & 0x7);
- const PRUint32 size = 3*num_colors;
- if (!mGIFStruct.local_colormap) {
- mGIFStruct.local_colormap =
- (PRUint32*)PR_MALLOC(MAX_COLORS * sizeof(PRUint32));
+ mGIFStruct.local_colormap_size = 1 << depth;
+ if (mGIFStruct.images_decoded) {
+ // Copy directly into the palette of current frame,
+ // by pointing mColormap to that palette.
+ PRUint32 paletteSize;
+ mImageFrame->GetPaletteData(&mColormap, &paletteSize);
+ } else {
+ // First frame has local colormap, allocate space for it
+ // as the image frame doesn't have its own palette
if (!mGIFStruct.local_colormap) {
- mGIFStruct.state = gif_oom;
- break;
+ mGIFStruct.local_colormap =
+ (PRUint32*)PR_MALLOC(mGIFStruct.local_colormap_size * sizeof(PRUint32));
+ if (!mGIFStruct.local_colormap) {
+ mGIFStruct.state = gif_oom;
+ break;
+ }
}
+ mColormap = mGIFStruct.local_colormap;
}
-
- /* Switch to the new local palette after it loads */
- mGIFStruct.local_colormap_size = num_colors;
- mColormap = mGIFStruct.local_colormap;
-
+ const PRUint32 size = 3 << depth;
if (len < size) {
// Use 'hold' pattern to get the image colormap
GETN(size, gif_image_colormap);
break;
}
// Copy everything, go to colormap state to do CMS correction
- memcpy(mGIFStruct.local_colormap, buf, size);
+ memcpy(mColormap, buf, size);
buf += size;
len -= size;
GETN(0, gif_image_colormap);
break;
} else {
/* Switch back to the global palette */
mColormap = mGIFStruct.global_colormap;
+ if (mGIFStruct.images_decoded) {
+ // Copy global colormap into the palette of current frame
+ PRUint32 size;
+ mImageFrame->GetPaletteData(&mColormap, &size);
+ memcpy(mColormap, mGIFStruct.global_colormap, size);
+ }
}
GETN(1, gif_lzw_start);
- break;
+ }
+ break;
case gif_image_colormap:
// Everything is already copied into local_colormap
// Convert into Cairo colors including CMS transformation
- ConvertColormap(mGIFStruct.local_colormap, mGIFStruct.local_colormap_size);
+ ConvertColormap(mColormap, mGIFStruct.local_colormap_size);
GETN(1, gif_lzw_start);
break;
case gif_sub_block:
mGIFStruct.count = *q;
if (mGIFStruct.count) {
/* Still working on the same image: Process next LZW data block */
/* Make sure there are still rows left. If the GIF data */
@@ -1086,16 +1127,16 @@ nsresult nsGIFDecoder2::GifWrite(const P
return NS_ERROR_FAILURE;
}
// Copy the leftover into mGIFStruct.hold
mGIFStruct.bytes_in_hold = len;
if (len) {
// Add what we have sofar to the block
PRUint8* p = (mGIFStruct.state == gif_global_colormap) ? (PRUint8*)mGIFStruct.global_colormap :
- (mGIFStruct.state == gif_image_colormap) ? (PRUint8*)mGIFStruct.local_colormap :
+ (mGIFStruct.state == gif_image_colormap) ? (PRUint8*)mColormap :
mGIFStruct.hold;
memcpy(p, buf, len);
mGIFStruct.bytes_to_consume -= len;
}
return NS_OK;
}
--- a/modules/libpr0n/decoders/gif/nsGIFDecoder2.h
+++ b/modules/libpr0n/decoders/gif/nsGIFDecoder2.h
@@ -71,34 +71,34 @@ public:
nsresult ProcessData(unsigned char *data, PRUint32 count, PRUint32 *_retval);
private:
/* These functions will be called when the decoder has a decoded row,
* frame size information, etc. */
void BeginGIF();
void EndGIF();
- void BeginImageFrame();
+ void BeginImageFrame(gfx_depth aDepth);
void EndImageFrame();
void FlushImageData();
void FlushImageData(PRUint32 fromRow, PRUint32 rows);
nsresult GifWrite(const PRUint8 * buf, PRUint32 numbytes);
PRUint32 OutputRow();
PRBool DoLzw(const PRUint8 *q);
inline int ClearCode() const { return 1 << mGIFStruct.datasize; }
nsCOMPtr<imgIContainer> mImageContainer;
nsCOMPtr<gfxIImageFrame> mImageFrame;
nsCOMPtr<imgIDecoderObserver> mObserver; // this is just qi'd from mRequest for speed
PRInt32 mCurrentRow;
PRInt32 mLastFlushedRow;
- PRUint32 *mImageData; // Pointer to image data in Cairo format
+ PRUint8 *mImageData; // Pointer to image data in either Cairo or 8bit format
PRUint32 *mColormap; // Current colormap to be used in Cairo format
PRUint32 mOldColor; // The old value of the transparent pixel
PRUint8 mCurrentPass;
PRUint8 mLastFlushedPass;
PRPackedBool mGIFOpen;
gif_struct mGIFStruct;
};
--- a/modules/libpr0n/src/imgContainer.cpp
+++ b/modules/libpr0n/src/imgContainer.cpp
@@ -786,71 +786,71 @@ nsresult imgContainer::DoComposite(gfxII
NS_ASSERTION(aFrameToUse, "imgContainer::DoComposite aFrameToUse is null");
PRInt32 prevFrameDisposalMethod;
aPrevFrame->GetFrameDisposalMethod(&prevFrameDisposalMethod);
if (prevFrameDisposalMethod == imgIContainer::kDisposeRestorePrevious &&
!mAnim->compositingPrevFrame)
prevFrameDisposalMethod = imgIContainer::kDisposeClear;
-
- // Optimization: Skip compositing if the previous frame wants to clear the
- // whole image
- if (prevFrameDisposalMethod == imgIContainer::kDisposeClearAll) {
- aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
- *aFrameToUse = aNextFrame;
- return NS_OK;
- }
-
nsIntRect prevFrameRect;
aPrevFrame->GetRect(prevFrameRect);
PRBool isFullPrevFrame = (prevFrameRect.x == 0 && prevFrameRect.y == 0 &&
prevFrameRect.width == mSize.width &&
prevFrameRect.height == mSize.height);
- // Optimization: Skip compositing if the previous frame is the same size as
+ // Optimization: DisposeClearAll if the previous frame is the same size as
// container and it's clearing itself
- if (isFullPrevFrame && prevFrameDisposalMethod == imgIContainer::kDisposeClear) {
- aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
- *aFrameToUse = aNextFrame;
- return NS_OK;
- }
+ if (isFullPrevFrame &&
+ (prevFrameDisposalMethod == imgIContainer::kDisposeClear))
+ prevFrameDisposalMethod = imgIContainer::kDisposeClearAll;
PRInt32 nextFrameDisposalMethod;
nsIntRect nextFrameRect;
aNextFrame->GetFrameDisposalMethod(&nextFrameDisposalMethod);
aNextFrame->GetRect(nextFrameRect);
PRBool isFullNextFrame = (nextFrameRect.x == 0 && nextFrameRect.y == 0 &&
nextFrameRect.width == mSize.width &&
nextFrameRect.height == mSize.height);
gfx_format nextFormat;
aNextFrame->GetFormat(&nextFormat);
- PRBool nextFrameHasAlpha = (nextFormat != gfxIFormats::RGB) &&
- (nextFormat != gfxIFormats::BGR);
-
- // Optimization: Skip compositing if this frame is the same size as the
- // container and it's fully drawing over prev frame (no alpha)
- if (isFullNextFrame &&
- (nextFrameDisposalMethod != imgIContainer::kDisposeRestorePrevious) &&
- !nextFrameHasAlpha) {
-
- aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
- *aFrameToUse = aNextFrame;
- return NS_OK;
+ if (nextFormat != gfxIFormats::PAL && nextFormat != gfxIFormats::PAL_A1) {
+ // Optimization: Skip compositing if the previous frame wants to clear the
+ // whole image
+ if (prevFrameDisposalMethod == imgIContainer::kDisposeClearAll) {
+ aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
+ *aFrameToUse = aNextFrame;
+ return NS_OK;
+ }
+
+ // Optimization: Skip compositing if this frame is the same size as the
+ // container and it's fully drawing over prev frame (no alpha)
+ if (isFullNextFrame &&
+ (nextFrameDisposalMethod != imgIContainer::kDisposeRestorePrevious) &&
+ (nextFormat == gfxIFormats::RGB)) {
+ aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
+ *aFrameToUse = aNextFrame;
+ return NS_OK;
+ }
}
// Calculate area that needs updating
switch (prevFrameDisposalMethod) {
default:
case imgIContainer::kDisposeNotSpecified:
case imgIContainer::kDisposeKeep:
*aDirtyRect = nextFrameRect;
break;
+ case imgIContainer::kDisposeClearAll:
+ // Whole image container is cleared
+ aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
+ break;
+
case imgIContainer::kDisposeClear:
// Calc area that needs to be redrawn (the combination of previous and
// this frame)
// XXX - This could be done with multiple framechanged calls
// Having prevFrame way at the top of the image, and nextFrame
// way at the bottom, and both frames being small, we'd be
// telling framechanged to refresh the whole image when only two
// small areas are needed.
@@ -886,65 +886,99 @@ nsresult imgContainer::DoComposite(gfxII
if (NS_FAILED(rv)) {
NS_WARNING("Failed to init compositingFrame!\n");
mAnim->compositingFrame = nsnull;
return rv;
}
needToBlankComposite = PR_TRUE;
}
- // Copy previous frame into compositingFrame before we put the new frame on top
- // Assumes that the previous frame represents a full frame (it could be
- // smaller in size than the container, as long as the frame before it erased
- // itself)
- // Note: Frame 1 never gets into DoComposite(), so (aNextFrameIndex - 1) will
- // always be a valid frame number.
- if (mAnim->lastCompositedFrameIndex != aNextFrameIndex - 1 &&
- prevFrameDisposalMethod != imgIContainer::kDisposeRestorePrevious) {
-
- // XXX If we had a method of drawing a section of a frame into another, we
- // could optimize further:
- // if aPrevFrameIndex == 1 && lastCompositedFrameIndex <> -1,
- // only firstFrameRefreshArea needs to be drawn back to composite
- if (isFullPrevFrame) {
- CopyFrameImage(aPrevFrame, mAnim->compositingFrame);
+ // More optimizations possible when next frame is not transparent
+ PRBool doDisposal = PR_TRUE;
+ if ((nextFormat == gfxIFormats::RGB)||(nextFormat == gfxIFormats::PAL)) {
+ if (isFullNextFrame) {
+ // Optimization: No need to dispose prev.frame when
+ // next frame is full frame and not transparent.
+ doDisposal = PR_FALSE;
+ // No need to blank the composite frame
+ needToBlankComposite = PR_FALSE;
} else {
- ClearFrame(mAnim->compositingFrame);
- DrawFrameTo(aPrevFrame, mAnim->compositingFrame, prevFrameRect);
- needToBlankComposite = PR_FALSE;
- }
+ if ((prevFrameRect.x >= nextFrameRect.x) &&
+ (prevFrameRect.y >= nextFrameRect.y) &&
+ (prevFrameRect.x + prevFrameRect.width <= nextFrameRect.x + nextFrameRect.width) &&
+ (prevFrameRect.y + prevFrameRect.height <= nextFrameRect.y + nextFrameRect.height)) {
+ // Optimization: No need to dispose prev.frame when
+ // next frame fully overlaps previous frame.
+ doDisposal = PR_FALSE;
+ }
+ }
}
- // Dispose of previous
- switch (prevFrameDisposalMethod) {
- case imgIContainer::kDisposeClear:
- if (needToBlankComposite) {
- // If we just created the composite, it could have anything in it's
- // buffers. Clear them
+ if (doDisposal) {
+ // Dispose of previous: clear, restore, or keep (copy)
+ switch (prevFrameDisposalMethod) {
+ case imgIContainer::kDisposeClear:
+ if (needToBlankComposite) {
+ // If we just created the composite, it could have anything in it's
+ // buffer. Clear whole frame
+ ClearFrame(mAnim->compositingFrame);
+ } else {
+ // Only blank out previous frame area (both color & Mask/Alpha)
+ ClearFrame(mAnim->compositingFrame, prevFrameRect);
+ }
+ break;
+
+ case imgIContainer::kDisposeClearAll:
ClearFrame(mAnim->compositingFrame);
- needToBlankComposite = PR_FALSE;
- } else {
- // Blank out previous frame area (both color & Mask/Alpha)
- ClearFrame(mAnim->compositingFrame, prevFrameRect);
- }
- break;
-
- case imgIContainer::kDisposeRestorePrevious:
- // It would be better to copy only the area changed back to
- // compositingFrame.
- if (mAnim->compositingPrevFrame) {
- CopyFrameImage(mAnim->compositingPrevFrame, mAnim->compositingFrame);
-
- // destroy only if we don't need it for this frame's disposal
- if (nextFrameDisposalMethod != imgIContainer::kDisposeRestorePrevious)
- mAnim->compositingPrevFrame = nsnull;
- } else {
- ClearFrame(mAnim->compositingFrame);
- }
- break;
+ break;
+
+ case imgIContainer::kDisposeRestorePrevious:
+ // It would be better to copy only the area changed back to
+ // compositingFrame.
+ if (mAnim->compositingPrevFrame) {
+ CopyFrameImage(mAnim->compositingPrevFrame, mAnim->compositingFrame);
+
+ // destroy only if we don't need it for this frame's disposal
+ if (nextFrameDisposalMethod != imgIContainer::kDisposeRestorePrevious)
+ mAnim->compositingPrevFrame = nsnull;
+ } else {
+ ClearFrame(mAnim->compositingFrame);
+ }
+ break;
+
+ default:
+ // Copy previous frame into compositingFrame before we put the new frame on top
+ // Assumes that the previous frame represents a full frame (it could be
+ // smaller in size than the container, as long as the frame before it erased
+ // itself)
+ // Note: Frame 1 never gets into DoComposite(), so (aNextFrameIndex - 1) will
+ // always be a valid frame number.
+ if (mAnim->lastCompositedFrameIndex != aNextFrameIndex - 1) {
+ gfx_format prevFormat;
+ aPrevFrame->GetFormat(&prevFormat);
+ if (isFullPrevFrame &&
+ prevFormat != gfxIFormats::PAL && prevFormat != gfxIFormats::PAL_A1) {
+ // Just copy the bits
+ CopyFrameImage(aPrevFrame, mAnim->compositingFrame);
+ } else {
+ if (needToBlankComposite) {
+ // Only blank composite when prev is transparent or not full.
+ if (!isFullPrevFrame ||
+ (prevFormat != gfxIFormats::RGB && prevFormat != gfxIFormats::PAL)) {
+ ClearFrame(mAnim->compositingFrame);
+ }
+ }
+ DrawFrameTo(aPrevFrame, mAnim->compositingFrame, prevFrameRect);
+ }
+ }
+ }
+ } else if (needToBlankComposite) {
+ // If we just created the composite, it could have anything in it's
+ // buffers. Clear them
+ ClearFrame(mAnim->compositingFrame);
}
// Check if the frame we are composing wants the previous image restored afer
// it is done. Don't store it (again) if last frame wanted its image restored
// too
if ((nextFrameDisposalMethod == imgIContainer::kDisposeRestorePrevious) &&
(prevFrameDisposalMethod != imgIContainer::kDisposeRestorePrevious)) {
// We are storing the whole image.
@@ -967,17 +1001,25 @@ nsresult imgContainer::DoComposite(gfxII
// blit next frame into it's correct spot
DrawFrameTo(aNextFrame, mAnim->compositingFrame, nextFrameRect);
// Set timeout of CompositeFrame to timeout of frame we just composed
// Bug 177948
PRInt32 timeout;
aNextFrame->GetTimeout(&timeout);
mAnim->compositingFrame->SetTimeout(timeout);
- if (isFullNextFrame && mAnimationMode == kNormalAnimMode && mLoopCount != 0) {
+ // Tell the image that it is fully 'downloaded'.
+ nsIntRect r;
+ mAnim->compositingFrame->GetRect(r);
+ nsCOMPtr<nsIImage> img = do_GetInterface(mAnim->compositingFrame);
+ img->ImageUpdated(nsnull, nsImageUpdateFlags_kBitsChanged, &r);
+
+ // We don't want to keep composite images for 8bit frames...
+ if (isFullNextFrame && mAnimationMode == kNormalAnimMode && mLoopCount != 0 &&
+ nextFormat != gfxIFormats::PAL && nextFormat != gfxIFormats::PAL_A1) {
// We have a composited full frame
// Store the composited frame into the mFrames[..] so we don't have to
// continuously re-build it
// Then set the previous frame's disposal to CLEAR_ALL so we just draw the
// frame next time around
if (CopyFrameImage(mAnim->compositingFrame, aNextFrame)) {
aPrevFrame->SetFrameDisposalMethod(imgIContainer::kDisposeClearAll);
mAnim->lastCompositedFrameIndex = -1;
@@ -1002,20 +1044,16 @@ void imgContainer::ClearFrame(gfxIImageF
nsCOMPtr<nsIImage> img(do_GetInterface(aFrame));
nsRefPtr<gfxASurface> surf;
img->GetSurface(getter_AddRefs(surf));
// Erase the surface to transparent
gfxContext ctx(surf);
ctx.SetOperator(gfxContext::OPERATOR_CLEAR);
ctx.Paint();
-
- nsIntRect r;
- aFrame->GetRect(r);
- img->ImageUpdated(nsnull, nsImageUpdateFlags_kBitsChanged, &r);
}
//******************************************************************************
void imgContainer::ClearFrame(gfxIImageFrame *aFrame, nsIntRect &aRect)
{
if (!aFrame || aRect.width <= 0 || aRect.height <= 0) {
return;
}
@@ -1024,18 +1062,16 @@ void imgContainer::ClearFrame(gfxIImageF
nsRefPtr<gfxASurface> surf;
img->GetSurface(getter_AddRefs(surf));
// Erase the destination rectangle to transparent
gfxContext ctx(surf);
ctx.SetOperator(gfxContext::OPERATOR_CLEAR);
ctx.Rectangle(gfxRect(aRect.x, aRect.y, aRect.width, aRect.height));
ctx.Fill();
-
- img->ImageUpdated(nsnull, nsImageUpdateFlags_kBitsChanged, &aRect);
}
//******************************************************************************
// Whether we succeed or fail will not cause a crash, and there's not much
// we can do about a failure, so there we don't return a nsresult
PRBool imgContainer::CopyFrameImage(gfxIImageFrame *aSrcFrame,
gfxIImageFrame *aDstFrame)
@@ -1056,35 +1092,86 @@ PRBool imgContainer::CopyFrameImage(gfxI
aDstFrame->GetImageData(&aDataDest, &aDataLengthDest);
if (!aDataDest || !aDataSrc || aDataLengthDest != aDataLengthSrc) {
aDstFrame->UnlockImageData();
return PR_FALSE;
}
memcpy(aDataDest, aDataSrc, aDataLengthSrc);
aDstFrame->UnlockImageData();
- // Tell the image that it's data has been updated
- nsCOMPtr<nsIImage> img(do_GetInterface(aDstFrame));
- if (!img)
- return PR_FALSE;
- nsIntRect r;
- aDstFrame->GetRect(r);
- img->ImageUpdated(nsnull, nsImageUpdateFlags_kBitsChanged, &r);
-
return PR_TRUE;
}
//******************************************************************************
nsresult imgContainer::DrawFrameTo(gfxIImageFrame *aSrc,
gfxIImageFrame *aDst,
nsIntRect& aDstRect)
{
if (!aSrc || !aDst)
return NS_ERROR_NOT_INITIALIZED;
+ nsIntRect srcRect, dstRect;
+ aSrc->GetRect(srcRect);
+ aDst->GetRect(dstRect);
+
+ gfx_format format;
+ aSrc->GetFormat(&format);
+ if (format == gfxIFormats::PAL || format == gfxIFormats::PAL_A1) {
+ // dstRect must fully fit within destination image
+ NS_ASSERTION((aDstRect.x >= 0) && (aDstRect.y >= 0) &&
+ (aDstRect.x + aDstRect.width <= dstRect.width) &&
+ (aDstRect.y + aDstRect.height <= dstRect.height),
+ "imgContainer::DrawFrameTo: Invalid aDstRect");
+ // dstRect size may be smaller than source, but not larger
+ NS_ASSERTION((aDstRect.width <= srcRect.width) &&
+ (aDstRect.height <= srcRect.height),
+ "imgContainer::DrawFrameTo: source and dest size must be equal");
+
+ if (NS_FAILED(aDst->LockImageData()))
+ return NS_ERROR_FAILURE;
+ // Get pointers to image data
+ PRUint32 size;
+ PRUint8 *srcPixels;
+ gfx_color *colormap;
+ gfx_color *dstPixels;
+
+ aSrc->GetImageData(&srcPixels, &size);
+ aDst->GetImageData((PRUint8**)&dstPixels, &size);
+ aSrc->GetPaletteData(&colormap, &size);
+ if (!srcPixels || !dstPixels || !colormap) {
+ aDst->UnlockImageData();
+ return NS_ERROR_FAILURE;
+ }
+
+ // Skip to the right offset
+ dstPixels += aDstRect.x + (aDstRect.y * dstRect.width);
+ const PRUint32 width = (PRUint32)aDstRect.width;
+ if (format == gfxIFormats::PAL) {
+ for (PRUint32 r = aDstRect.height; r > 0; --r) {
+ for (PRUint32 c = width; c > 0; --c) {
+ *dstPixels++ = colormap[*srcPixels++];
+ }
+ dstPixels += dstRect.width - width;
+ }
+ } else {
+ // With transparent source, skip transparent pixels
+ for (PRUint32 r = aDstRect.height; r > 0; --r) {
+ for (PRUint32 c = width; c > 0; --c) {
+ const PRUint32 color = colormap[*srcPixels++];
+ if (color)
+ *dstPixels = color;
+ dstPixels ++;
+ }
+ dstPixels += dstRect.width - width;
+ }
+ }
+ aDst->UnlockImageData();
+ return NS_OK;
+ }
+
nsCOMPtr<nsIImage> srcImg(do_GetInterface(aSrc));
nsRefPtr<gfxASurface> srcSurf;
srcImg->GetSurface(getter_AddRefs(srcSurf));
nsCOMPtr<nsIImage> dstImg(do_GetInterface(aDst));
nsRefPtr<gfxASurface> dstSurf;
dstImg->GetSurface(getter_AddRefs(dstSurf));
@@ -1102,19 +1189,16 @@ nsresult imgContainer::DrawFrameTo(gfxII
dst.NewPath();
dst.SetOperator(defaultOperator);
// We don't use PixelSnappedRectangleAndSetPattern because if
// these coords aren't already pixel aligned, we've lost
// before we've even begun.
dst.Translate(gfxPoint(aDstRect.x, aDstRect.y));
dst.Rectangle(gfxRect(0, 0, aDstRect.width, aDstRect.height), PR_TRUE);
-
- nsIntRect srcRect;
- aSrc->GetRect(srcRect);
dst.Scale(double(aDstRect.width) / srcRect.width,
double(aDstRect.height) / srcRect.height);
dst.SetSource(srcSurf);
dst.Paint();
return NS_OK;
}