mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-14 03:30:17 +01:00
599 lines
21 KiB
C++
599 lines
21 KiB
C++
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||
|
*
|
||
|
* ***** BEGIN LICENSE BLOCK *****
|
||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||
|
*
|
||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||
|
* the License. You may obtain a copy of the License at
|
||
|
* http://www.mozilla.org/MPL/
|
||
|
*
|
||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
|
* for the specific language governing rights and limitations under the
|
||
|
* License.
|
||
|
*
|
||
|
* The Original Code is mozilla.org code.
|
||
|
*
|
||
|
* The Initial Developer of the Original Code is
|
||
|
* Netscape Communications Corporation.
|
||
|
* Portions created by the Initial Developer are Copyright (C) 2001
|
||
|
* the Initial Developer. All Rights Reserved.
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
* Chris Saari <saari@netscape.com>
|
||
|
*
|
||
|
* Alternatively, the contents of this file may be used under the terms of
|
||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||
|
* of those above. If you wish to allow use of your version of this file only
|
||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||
|
* use your version of this file under the terms of the MPL, indicate your
|
||
|
* decision by deleting the provisions above and replace them with the notice
|
||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||
|
* 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 "prmem.h"
|
||
|
|
||
|
#include "nsGIFDecoder2.h"
|
||
|
#include "nsIInputStream.h"
|
||
|
#include "nsIComponentManager.h"
|
||
|
#include "nsRecyclingAllocator.h"
|
||
|
|
||
|
#include "imgIContainerObserver.h"
|
||
|
|
||
|
#include "imgILoad.h"
|
||
|
|
||
|
|
||
|
/*******************************************************************************
|
||
|
* Gif decoder allocator
|
||
|
*
|
||
|
* For every image that gets loaded, we allocate a 'gif_struct'
|
||
|
* This allocator tries to keep one set of these around
|
||
|
* and reuses them; automatically fails over to use calloc/free when all
|
||
|
* buckets are full.
|
||
|
*/
|
||
|
const int kGifAllocatorNBucket = 3;
|
||
|
static nsRecyclingAllocator *gGifAllocator = nsnull;
|
||
|
|
||
|
void nsGifShutdown()
|
||
|
{
|
||
|
// Release cached buffers from zlib allocator
|
||
|
delete gGifAllocator;
|
||
|
gGifAllocator = nsnull;
|
||
|
}
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
// GIF Decoder Implementation
|
||
|
// This is an adaptor between GIF2 and imgIDecoder
|
||
|
|
||
|
NS_IMPL_ISUPPORTS1(nsGIFDecoder2, imgIDecoder)
|
||
|
|
||
|
nsGIFDecoder2::nsGIFDecoder2()
|
||
|
: mCurrentRow(-1)
|
||
|
, mLastFlushedRow(-1)
|
||
|
, mGIFStruct(nsnull)
|
||
|
, mAlphaLine(nsnull)
|
||
|
, mRGBLine(nsnull)
|
||
|
, mAlphaLineMaxSize(0)
|
||
|
, mRGBLineMaxSize(0)
|
||
|
, mBackgroundRGBIndex(0)
|
||
|
, mCurrentPass(0)
|
||
|
, mLastFlushedPass(0)
|
||
|
, mGIFOpen(PR_FALSE)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
nsGIFDecoder2::~nsGIFDecoder2(void)
|
||
|
{
|
||
|
Close();
|
||
|
}
|
||
|
|
||
|
//******************************************************************************
|
||
|
/** imgIDecoder methods **/
|
||
|
//******************************************************************************
|
||
|
|
||
|
//******************************************************************************
|
||
|
/* void init (in imgILoad aLoad); */
|
||
|
NS_IMETHODIMP nsGIFDecoder2::Init(imgILoad *aLoad)
|
||
|
{
|
||
|
mObserver = do_QueryInterface(aLoad);
|
||
|
|
||
|
mImageContainer = do_CreateInstance("@mozilla.org/image/container;1?type=image/gif");
|
||
|
aLoad->SetImage(mImageContainer);
|
||
|
|
||
|
if (!gGifAllocator) {
|
||
|
gGifAllocator = new nsRecyclingAllocator(kGifAllocatorNBucket,
|
||
|
NS_DEFAULT_RECYCLE_TIMEOUT, "gif");
|
||
|
if (!gGifAllocator)
|
||
|
return NS_ERROR_FAILURE;
|
||
|
}
|
||
|
mGIFStruct = (gif_struct *)gGifAllocator->Malloc(sizeof(gif_struct));
|
||
|
NS_ASSERTION(mGIFStruct, "gif_create failed");
|
||
|
if (!mGIFStruct)
|
||
|
return NS_ERROR_FAILURE;
|
||
|
|
||
|
// Call GIF decoder init routine
|
||
|
GIFInit(mGIFStruct, this);
|
||
|
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
//******************************************************************************
|
||
|
/** nsIOutputStream methods **/
|
||
|
//******************************************************************************
|
||
|
|
||
|
//******************************************************************************
|
||
|
/* void close (); */
|
||
|
NS_IMETHODIMP nsGIFDecoder2::Close()
|
||
|
{
|
||
|
if (mGIFStruct) {
|
||
|
nsGIFDecoder2 *decoder = NS_STATIC_CAST(nsGIFDecoder2*, mGIFStruct->clientptr);
|
||
|
if (decoder->mImageFrame)
|
||
|
EndImageFrame(mGIFStruct->clientptr, mGIFStruct->images_decoded + 1,
|
||
|
mGIFStruct->delay_time);
|
||
|
if (decoder->mGIFOpen)
|
||
|
EndGIF(mGIFStruct->clientptr, mGIFStruct->loop_count);
|
||
|
|
||
|
gif_destroy(mGIFStruct);
|
||
|
if (gGifAllocator)
|
||
|
gGifAllocator->Free(mGIFStruct);
|
||
|
mGIFStruct = nsnull;
|
||
|
}
|
||
|
PR_FREEIF(mAlphaLine);
|
||
|
PR_FREEIF(mRGBLine);
|
||
|
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
//******************************************************************************
|
||
|
/* void flush (); */
|
||
|
NS_IMETHODIMP nsGIFDecoder2::Flush()
|
||
|
{
|
||
|
return NS_ERROR_NOT_IMPLEMENTED;
|
||
|
}
|
||
|
|
||
|
//******************************************************************************
|
||
|
/* static callback from nsIInputStream::ReadSegments */
|
||
|
static NS_METHOD ReadDataOut(nsIInputStream* in,
|
||
|
void* closure,
|
||
|
const char* fromRawSegment,
|
||
|
PRUint32 toOffset,
|
||
|
PRUint32 count,
|
||
|
PRUint32 *writeCount)
|
||
|
{
|
||
|
nsGIFDecoder2 *decoder = NS_STATIC_CAST(nsGIFDecoder2*, closure);
|
||
|
nsresult rv = decoder->ProcessData((unsigned char*)fromRawSegment, count, writeCount);
|
||
|
if (NS_FAILED(rv)) {
|
||
|
*writeCount = 0;
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
// Push any new rows according to mCurrentPass/mLastFlushedPass and
|
||
|
// mCurrentRow/mLastFlushedRow. Note: caller is responsible for
|
||
|
// updating mlastFlushed{Row,Pass}.
|
||
|
NS_METHOD
|
||
|
nsGIFDecoder2::FlushImageData()
|
||
|
{
|
||
|
PRInt32 imgWidth;
|
||
|
mImageContainer->GetWidth(&imgWidth);
|
||
|
nsIntRect frameRect;
|
||
|
mImageFrame->GetRect(frameRect);
|
||
|
|
||
|
switch (mCurrentPass - mLastFlushedPass) {
|
||
|
case 0: { // same pass
|
||
|
PRInt32 remainingRows = mCurrentRow - mLastFlushedRow;
|
||
|
if (remainingRows) {
|
||
|
nsIntRect r(0, frameRect.y + mLastFlushedRow + 1,
|
||
|
imgWidth, remainingRows);
|
||
|
mObserver->OnDataAvailable(nsnull, mImageFrame, &r);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 1: { // one pass on - need to handle bottom & top rects
|
||
|
nsIntRect r(0, frameRect.y, imgWidth, mCurrentRow + 1);
|
||
|
mObserver->OnDataAvailable(nsnull, mImageFrame, &r);
|
||
|
nsIntRect r2(0, frameRect.y + mLastFlushedRow + 1,
|
||
|
imgWidth, frameRect.height - mLastFlushedRow - 1);
|
||
|
mObserver->OnDataAvailable(nsnull, mImageFrame, &r2);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default: { // more than one pass on - push the whole frame
|
||
|
nsIntRect r(0, frameRect.y, imgWidth, frameRect.height);
|
||
|
mObserver->OnDataAvailable(nsnull, mImageFrame, &r);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
//******************************************************************************
|
||
|
nsresult nsGIFDecoder2::ProcessData(unsigned char *data, PRUint32 count, PRUint32 *_retval)
|
||
|
{
|
||
|
// Push the data to the GIF decoder
|
||
|
|
||
|
// First we ask if the gif decoder is ready for more data, and if so, push it.
|
||
|
// In the new decoder, we should always be able to process more data since
|
||
|
// we don't wait to decode each frame in an animation now.
|
||
|
if (gif_write_ready(mGIFStruct)) {
|
||
|
PRStatus result = gif_write(mGIFStruct, data, count);
|
||
|
if (result != PR_SUCCESS)
|
||
|
return NS_ERROR_FAILURE;
|
||
|
}
|
||
|
|
||
|
if (mImageFrame && mObserver) {
|
||
|
FlushImageData();
|
||
|
mLastFlushedRow = mCurrentRow;
|
||
|
mLastFlushedPass = mCurrentPass;
|
||
|
}
|
||
|
|
||
|
*_retval = count;
|
||
|
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
//******************************************************************************
|
||
|
/* unsigned long writeFrom (in nsIInputStream inStr, in unsigned long count); */
|
||
|
NS_IMETHODIMP nsGIFDecoder2::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *_retval)
|
||
|
{
|
||
|
nsresult rv = inStr->ReadSegments(ReadDataOut, this, count, _retval);
|
||
|
|
||
|
/* necko doesn't propagate the errors from ReadDataOut - take matters
|
||
|
into our own hands. if we have at least one frame of an animated
|
||
|
gif, then return success so we keep displaying as much as possible. */
|
||
|
if (NS_SUCCEEDED(rv) && mGIFStruct && mGIFStruct->state == gif_error) {
|
||
|
PRUint32 numFrames = 0;
|
||
|
if (mImageContainer)
|
||
|
mImageContainer->GetNumFrames(&numFrames);
|
||
|
if (numFrames <= 0)
|
||
|
return NS_ERROR_FAILURE;
|
||
|
}
|
||
|
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
|
||
|
//******************************************************************************
|
||
|
// GIF decoder callback methods. Part of pulic API for GIF2
|
||
|
//******************************************************************************
|
||
|
|
||
|
//******************************************************************************
|
||
|
int nsGIFDecoder2::BeginGIF(
|
||
|
void* aClientData,
|
||
|
PRUint32 aLogicalScreenWidth,
|
||
|
PRUint32 aLogicalScreenHeight,
|
||
|
PRUint8 aBackgroundRGBIndex)
|
||
|
{
|
||
|
// If we have passed an illogical screen size, bail and hope that we'll get
|
||
|
// set later by the first frame's local image header.
|
||
|
if(aLogicalScreenWidth == 0 || aLogicalScreenHeight == 0)
|
||
|
return 0;
|
||
|
|
||
|
// copy GIF info into imagelib structs
|
||
|
nsGIFDecoder2 *decoder = NS_STATIC_CAST(nsGIFDecoder2*, aClientData);
|
||
|
|
||
|
decoder->mBackgroundRGBIndex = aBackgroundRGBIndex;
|
||
|
|
||
|
if (decoder->mObserver)
|
||
|
decoder->mObserver->OnStartDecode(nsnull);
|
||
|
|
||
|
decoder->mImageContainer->Init(aLogicalScreenWidth, aLogicalScreenHeight, decoder->mObserver);
|
||
|
|
||
|
if (decoder->mObserver)
|
||
|
decoder->mObserver->OnStartContainer(nsnull, decoder->mImageContainer);
|
||
|
|
||
|
decoder->mGIFOpen = PR_TRUE;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//******************************************************************************
|
||
|
int nsGIFDecoder2::EndGIF(
|
||
|
void* aClientData,
|
||
|
int aAnimationLoopCount)
|
||
|
{
|
||
|
nsGIFDecoder2 *decoder = NS_STATIC_CAST(nsGIFDecoder2*, aClientData);
|
||
|
|
||
|
if (!decoder->mGIFOpen)
|
||
|
return 0;
|
||
|
|
||
|
if (decoder->mObserver) {
|
||
|
decoder->mObserver->OnStopContainer(nsnull, decoder->mImageContainer);
|
||
|
decoder->mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
|
||
|
}
|
||
|
|
||
|
decoder->mImageContainer->SetLoopCount(aAnimationLoopCount);
|
||
|
decoder->mImageContainer->DecodingComplete();
|
||
|
|
||
|
decoder->mGIFOpen = PR_FALSE;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//******************************************************************************
|
||
|
int nsGIFDecoder2::BeginImageFrame(
|
||
|
void* aClientData,
|
||
|
PRUint32 aFrameNumber, /* Frame number, 1-n */
|
||
|
PRUint32 aFrameXOffset, /* X offset in logical screen */
|
||
|
PRUint32 aFrameYOffset, /* Y offset in logical screen */
|
||
|
PRUint32 aFrameWidth,
|
||
|
PRUint32 aFrameHeight)
|
||
|
{
|
||
|
nsGIFDecoder2* decoder = NS_STATIC_CAST(nsGIFDecoder2*, aClientData);
|
||
|
|
||
|
decoder->mImageFrame = nsnull; // clear out our current frame reference
|
||
|
decoder->mGIFStruct->x_offset = aFrameXOffset;
|
||
|
decoder->mGIFStruct->y_offset = aFrameYOffset;
|
||
|
decoder->mGIFStruct->width = aFrameWidth;
|
||
|
decoder->mGIFStruct->height = aFrameHeight;
|
||
|
|
||
|
if (aFrameNumber == 1) {
|
||
|
// 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)
|
||
|
PRInt32 imgWidth;
|
||
|
decoder->mImageContainer->GetWidth(&imgWidth);
|
||
|
if (aFrameYOffset > 0) {
|
||
|
nsIntRect r(0, 0, imgWidth, aFrameYOffset);
|
||
|
decoder->mObserver->OnDataAvailable(nsnull, decoder->mImageFrame, &r);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//******************************************************************************
|
||
|
int nsGIFDecoder2::EndImageFrame(
|
||
|
void* aClientData,
|
||
|
PRUint32 aFrameNumber,
|
||
|
PRUint32 aDelayTimeout) /* Time this frame should be displayed before the next frame
|
||
|
we can't have this in the image frame init because it doesn't
|
||
|
show up in the GIF frame header, it shows up in a sub control
|
||
|
block.*/
|
||
|
{
|
||
|
nsGIFDecoder2* decoder = NS_STATIC_CAST(nsGIFDecoder2*, aClientData);
|
||
|
|
||
|
// If mImageFrame hasn't been initialized, call HaveDecodedRow to init it
|
||
|
// One reason why it may not be initialized is because the frame
|
||
|
// is out of the bounds of the image.
|
||
|
if (!decoder->mImageFrame) {
|
||
|
HaveDecodedRow(aClientData,nsnull,0,0,0);
|
||
|
} 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.
|
||
|
decoder->mImageFrame->SetTimeout(aDelayTimeout);
|
||
|
}
|
||
|
decoder->mImageContainer->EndFrameDecode(aFrameNumber, aDelayTimeout);
|
||
|
|
||
|
if (decoder->mObserver && decoder->mImageFrame) {
|
||
|
decoder->FlushImageData();
|
||
|
|
||
|
if (aFrameNumber == 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 = decoder->mGIFStruct->height + decoder->mGIFStruct->y_offset;
|
||
|
|
||
|
decoder->mImageContainer->GetHeight(&imgHeight);
|
||
|
if (imgHeight > realFrameHeight) {
|
||
|
PRInt32 imgWidth;
|
||
|
decoder->mImageContainer->GetWidth(&imgWidth);
|
||
|
|
||
|
nsIntRect r(0, realFrameHeight, imgWidth, imgHeight - realFrameHeight);
|
||
|
decoder->mObserver->OnDataAvailable(nsnull, decoder->mImageFrame, &r);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
decoder->mCurrentRow = decoder->mLastFlushedRow = -1;
|
||
|
decoder->mCurrentPass = decoder->mLastFlushedPass = 0;
|
||
|
|
||
|
decoder->mObserver->OnStopFrame(nsnull, decoder->mImageFrame);
|
||
|
}
|
||
|
|
||
|
decoder->mImageFrame = nsnull;
|
||
|
decoder->mGIFStruct->is_transparent = PR_FALSE;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//******************************************************************************
|
||
|
// GIF decoder callback notification that it has decoded a row
|
||
|
int nsGIFDecoder2::HaveDecodedRow(
|
||
|
void* aClientData,
|
||
|
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)
|
||
|
{
|
||
|
nsGIFDecoder2* decoder = NS_STATIC_CAST(nsGIFDecoder2*, aClientData);
|
||
|
PRUint32 bpr, abpr;
|
||
|
// We have to delay allocation of the image frame until now because
|
||
|
// we won't have control block info (transparency) until now. The conrol
|
||
|
// block of a GIF stream shows up after the image header since transparency
|
||
|
// is added in GIF89a and control blocks are how the extensions are done.
|
||
|
// How annoying.
|
||
|
if(! decoder->mImageFrame) {
|
||
|
gfx_format format = gfxIFormats::RGB;
|
||
|
if (decoder->mGIFStruct->is_transparent) {
|
||
|
format = gfxIFormats::RGB_A1;
|
||
|
}
|
||
|
|
||
|
#if defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) || defined(MOZ_WIDGET_PHOTON)
|
||
|
// XXX this works...
|
||
|
format += 1; // RGB to BGR
|
||
|
#endif
|
||
|
|
||
|
// initalize the frame and append it to the container
|
||
|
decoder->mImageFrame = do_CreateInstance("@mozilla.org/gfx/image/frame;2");
|
||
|
if (!decoder->mImageFrame || NS_FAILED(decoder->mImageFrame->Init(
|
||
|
decoder->mGIFStruct->x_offset, decoder->mGIFStruct->y_offset,
|
||
|
decoder->mGIFStruct->width, decoder->mGIFStruct->height, format, 24))) {
|
||
|
decoder->mImageFrame = 0;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
decoder->mImageFrame->SetFrameDisposalMethod(decoder->mGIFStruct->disposal_method);
|
||
|
decoder->mImageContainer->AppendFrame(decoder->mImageFrame);
|
||
|
|
||
|
if (decoder->mObserver)
|
||
|
decoder->mObserver->OnStartFrame(nsnull, decoder->mImageFrame);
|
||
|
|
||
|
decoder->mImageFrame->GetImageBytesPerRow(&bpr);
|
||
|
decoder->mImageFrame->GetAlphaBytesPerRow(&abpr);
|
||
|
|
||
|
if (bpr > decoder->mRGBLineMaxSize) {
|
||
|
decoder->mRGBLine = (PRUint8 *)PR_REALLOC(decoder->mRGBLine, bpr);
|
||
|
decoder->mRGBLineMaxSize = bpr;
|
||
|
}
|
||
|
|
||
|
if (format == gfxIFormats::RGB_A1 || format == gfxIFormats::BGR_A1) {
|
||
|
if (abpr > decoder->mAlphaLineMaxSize) {
|
||
|
decoder->mAlphaLine = (PRUint8 *)PR_REALLOC(decoder->mAlphaLine, abpr);
|
||
|
decoder->mAlphaLineMaxSize = abpr;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
decoder->mImageFrame->GetImageBytesPerRow(&bpr);
|
||
|
decoder->mImageFrame->GetAlphaBytesPerRow(&abpr);
|
||
|
}
|
||
|
|
||
|
if (aRowBufPtr) {
|
||
|
PRInt32 width;
|
||
|
decoder->mImageFrame->GetWidth(&width);
|
||
|
|
||
|
gfx_format format;
|
||
|
decoder->mImageFrame->GetFormat(&format);
|
||
|
|
||
|
// XXX map the data into colors
|
||
|
int cmapsize;
|
||
|
PRUint8* cmap;
|
||
|
cmapsize = decoder->mGIFStruct->global_colormap_size;
|
||
|
cmap = decoder->mGIFStruct->global_colormap;
|
||
|
|
||
|
if(decoder->mGIFStruct->global_colormap &&
|
||
|
decoder->mGIFStruct->screen_bgcolor < cmapsize) {
|
||
|
gfx_color bgColor = 0;
|
||
|
PRUint32 bgIndex = decoder->mGIFStruct->screen_bgcolor * 3;
|
||
|
bgColor |= cmap[bgIndex];
|
||
|
bgColor |= cmap[bgIndex + 1] << 8;
|
||
|
bgColor |= cmap[bgIndex + 2] << 16;
|
||
|
decoder->mImageFrame->SetBackgroundColor(bgColor);
|
||
|
}
|
||
|
if (decoder->mGIFStruct->is_local_colormap_defined) {
|
||
|
cmapsize = decoder->mGIFStruct->local_colormap_size;
|
||
|
cmap = decoder->mGIFStruct->local_colormap;
|
||
|
}
|
||
|
|
||
|
if (!cmap) { // cmap could have null value if the global color table flag is 0
|
||
|
for (int i = 0; i < aDuplicateCount; ++i) {
|
||
|
if (format == gfxIFormats::RGB_A1 ||
|
||
|
format == gfxIFormats::BGR_A1) {
|
||
|
decoder->mImageFrame->SetAlphaData(nsnull,
|
||
|
abpr, (aRowNumber+i)*abpr);
|
||
|
}
|
||
|
decoder->mImageFrame->SetImageData(nsnull,
|
||
|
bpr, (aRowNumber+i)*bpr);
|
||
|
}
|
||
|
} else {
|
||
|
PRUint8* rgbRowIndex = decoder->mRGBLine;
|
||
|
PRUint8* rowBufIndex = aRowBufPtr;
|
||
|
|
||
|
switch (format) {
|
||
|
case gfxIFormats::RGB:
|
||
|
case gfxIFormats::BGR:
|
||
|
{
|
||
|
while (rowBufIndex != decoder->mGIFStruct->rowend) {
|
||
|
#if defined(XP_MAC) || defined(XP_MACOSX)
|
||
|
*rgbRowIndex++ = 0; // Mac is always 32bits per pixel, this is pad
|
||
|
#endif
|
||
|
if (*rowBufIndex < cmapsize) {
|
||
|
PRUint32 colorIndex = *rowBufIndex * 3;
|
||
|
#if defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) || defined(MOZ_WIDGET_PHOTON)
|
||
|
*rgbRowIndex++ = cmap[colorIndex + 2]; // blue
|
||
|
*rgbRowIndex++ = cmap[colorIndex + 1]; // green
|
||
|
*rgbRowIndex++ = cmap[colorIndex]; // red
|
||
|
#else
|
||
|
*rgbRowIndex++ = cmap[colorIndex]; // red
|
||
|
*rgbRowIndex++ = cmap[colorIndex + 1]; // green
|
||
|
*rgbRowIndex++ = cmap[colorIndex + 2]; // blue
|
||
|
#endif
|
||
|
} else {
|
||
|
*rgbRowIndex++ = 0; // red
|
||
|
*rgbRowIndex++ = 0; // green
|
||
|
*rgbRowIndex++ = 0; // blue
|
||
|
}
|
||
|
++rowBufIndex;
|
||
|
}
|
||
|
for (int i=0; i<aDuplicateCount; i++) {
|
||
|
decoder->mImageFrame->SetImageData(decoder->mRGBLine,
|
||
|
bpr, (aRowNumber+i)*bpr);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case gfxIFormats::RGB_A1:
|
||
|
case gfxIFormats::BGR_A1:
|
||
|
{
|
||
|
memset(decoder->mRGBLine, 0, bpr);
|
||
|
memset(decoder->mAlphaLine, 0, abpr);
|
||
|
for (PRUint32 x = 0; x < (PRUint32)width; ++x) {
|
||
|
if (*rowBufIndex != decoder->mGIFStruct->tpixel) {
|
||
|
#if defined(XP_MAC) || defined(XP_MACOSX)
|
||
|
*rgbRowIndex++ = 0; // Mac is always 32bits per pixel, this is pad
|
||
|
#endif
|
||
|
if (*rowBufIndex < cmapsize) {
|
||
|
PRUint32 colorIndex = *rowBufIndex * 3;
|
||
|
#if defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) || defined(MOZ_WIDGET_PHOTON)
|
||
|
*rgbRowIndex++ = cmap[colorIndex + 2]; // blue
|
||
|
*rgbRowIndex++ = cmap[colorIndex + 1]; // green
|
||
|
*rgbRowIndex++ = cmap[colorIndex]; // red
|
||
|
#else
|
||
|
*rgbRowIndex++ = cmap[colorIndex]; // red
|
||
|
*rgbRowIndex++ = cmap[colorIndex + 1]; // green
|
||
|
*rgbRowIndex++ = cmap[colorIndex + 2]; // blue
|
||
|
#endif
|
||
|
} else {
|
||
|
*rgbRowIndex++ = 0; // red
|
||
|
*rgbRowIndex++ = 0; // green
|
||
|
*rgbRowIndex++ = 0; // blue
|
||
|
}
|
||
|
decoder->mAlphaLine[x>>3] |= 1<<(7-x&0x7);
|
||
|
} else {
|
||
|
#if defined(XP_MAC) || defined(XP_MACOSX)
|
||
|
rgbRowIndex+=4;
|
||
|
#else
|
||
|
rgbRowIndex+=3;
|
||
|
#endif
|
||
|
}
|
||
|
++rowBufIndex;
|
||
|
}
|
||
|
for (int i=0; i<aDuplicateCount; i++) {
|
||
|
decoder->mImageFrame->SetAlphaData(decoder->mAlphaLine,
|
||
|
abpr, (aRowNumber+i)*abpr);
|
||
|
decoder->mImageFrame->SetImageData(decoder->mRGBLine,
|
||
|
bpr, (aRowNumber+i)*bpr);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
decoder->mCurrentRow = aRowNumber + aDuplicateCount - 1;
|
||
|
decoder->mCurrentPass = aInterlacePass;
|
||
|
if (aInterlacePass == 1)
|
||
|
decoder->mLastFlushedPass = aInterlacePass; // interlaced starts at 1
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|