Back to index

lightning-sunbird  0.9+nobinonly
Public Member Functions | Public Attributes | Private Types | Private Member Functions | Static Private Member Functions | Private Attributes
imgContainerGIF Class Reference

Handles animation of GIF frames. More...

#include <imgContainerGIF.h>

Inheritance diagram for imgContainerGIF:
Inheritance graph
[legend]
Collaboration diagram for imgContainerGIF:
Collaboration graph
[legend]

List of all members.

Public Member Functions

NS_DECL_ISUPPORTS
NS_DECL_IMGICONTAINER
NS_DECL_NSITIMERCALLBACK 
imgContainerGIF ()
virtual ~imgContainerGIF ()
void init (in PRInt32 aWidth, in PRInt32 aHeight, in imgIContainerObserver aObserver)
 Create a new aWidth x aHeight sized image container.
gfxIImageFrame getFrameAt (in unsigned long index)
void appendFrame (in gfxIImageFrame item)
 Adds item to the end of the list of frames.
void removeFrame (in gfxIImageFrame item)
void endFrameDecode (in unsigned long framenumber, in unsigned long timeout)
void decodingComplete ()
void clear ()
void startAnimation ()
void stopAnimation ()
void resetAnimation ()
void notify (in nsITimer timer)

Public Attributes

readonly attribute gfx_format preferredAlphaChannelFormat
readonly attribute PRInt32 width
 The width of the container rectangle.
readonly attribute PRInt32 height
 The height of the container rectangle.
readonly attribute gfxIImageFrame currentFrame
 Get the current frame that would be drawn if the image was to be drawn now.
readonly attribute unsigned long numFrames
const short kNormalAnimMode = 0
 Animation mode Constants 0 = normal 1 = don't animate 2 = loop once.
const short kDontAnimMode = 1
const short kLoopOnceAnimMode = 2
attribute unsigned short animationMode
attribute long loopCount
 number of times to loop the image.

Private Types

enum  {
  DISPOSE_CLEAR_ALL = -1, DISPOSE_NOT_SPECIFIED = 0, DISPOSE_KEEP = 1, DISPOSE_CLEAR = 2,
  DISPOSE_RESTORE_PREVIOUS = 3
}
 "Disposal" method indicates how the image should be handled before the subsequent image is displayed. More...

Private Member Functions

gfxIImageFrameinlinedGetCurrentFrame ()
nsresult DoComposite (gfxIImageFrame **aFrameToUse, nsIntRect *aDirtyRect, gfxIImageFrame *aPrevFrame, gfxIImageFrame *aNextFrame, PRInt32 aNextFrameIndex)
 Function for doing the frame compositing of animations.
void BuildCompositeMask (gfxIImageFrame *aCompositingFrame, gfxIImageFrame *aOverlayFrame)
 Combine aOverlayFrame's mask into aCompositingFrame's mask.
void SetMaskVisibility (gfxIImageFrame *aFrame, PRBool aVisible)
 Sets an area of the frame's mask.
void SetMaskVisibility (gfxIImageFrame *aFrame, PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight, PRBool aVisible)
void SetMaskVisibility (gfxIImageFrame *aFrame, nsIntRect &aRect, PRBool aVisible)
void BlackenFrame (gfxIImageFrame *aFrame)
 Fills an area of <aFrame> with black.
void BlackenFrame (gfxIImageFrame *aFrame, PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight)
void BlackenFrame (gfxIImageFrame *aFrame, nsIntRect &aRect)

Static Private Member Functions

static PRBool CopyFrameImage (gfxIImageFrame *aSrcFrame, gfxIImageFrame *aDstFrame)
 Copy one gfxIImageFrame's image and mask into another.

Private Attributes

nsWeakPtr mObserver
 imgIContainerObserver; used for telling observers that the frame changed
nsCOMArray< gfxIImageFramemFrames
 All the <gfxIImageFrame>s of the GIF.
nsIntSize mSize
 Size of GIF (not necessarily the frame)
nsIntRect mFirstFrameRefreshArea
 Area of the first frame that needs to be redrawn on subsequent loops.
PRInt32 mCurrentDecodingFrameIndex
PRInt32 mCurrentAnimationFrameIndex
PRInt32 mLastCompositedFrameIndex
 Track the last composited frame for Optimizations (See DoComposite code)
PRBool mDoneDecoding
 Whether we can assume there will be no more frames (and thus loop the animation)
PRBool mAnimating
 Are we currently animating the GIF?
PRUint16 mAnimationMode
 See imgIContainer for mode constants.
PRInt32 mLoopCount
 

loops remaining before animation stops (-1 no stop)


nsCOMPtr< nsITimermTimer
 Timer to animate multiframed images.
nsCOMPtr< gfxIImageFramemCompositingFrame
 For managing blending of frames.
nsCOMPtr< gfxIImageFramemCompositingPrevFrame
 the previous composited frame, for DISPOSE_RESTORE_PREVIOUS

Detailed Description

Handles animation of GIF frames.

A Quick Walk Through
nsGIFDecoder initializes this class and calls AppendFrame() to add a frame. Once imgContainerGIF detects more than one frame, it starts the animation with StartAnimation().
StartAnimation() checks if animating is allowed, and creates a timer. The timer calls Notify when the specified frame delay time is up.
Notify() moves on to the next frame, sets up the new timer delay, destroys the old frame, and forces a redraw via observer->FrameChanged().
Each GIF frame can have a different method of removing itself. These are listed as an enum prefixed with DISPOSE_. Notify() calls DoComposite() to handle any special frame destruction.
The basic path through DoComposite() is: 1) Calculate Area that needs updating, which is at least the area of aNextFrame. 2) Dispose of previous frame. 3) Draw new image onto mCompositingFrame. See comments in DoComposite() for more information and optimizations.
The rest of the imgContainerGIF specific functions are used by DoComposite to destroy the old frame and build the new one.
Note:
"Mask", "Alpha", and "Alpha Level" are interchangable phrases in respects to imgContainerGIF.
GIFs never have more than a 1 bit alpha.
GIFs are stored in a 24bit buffer. Although one GIF frame can never have more than 256 colors, due to frame disposal methods, one composited frame could end up with far more than 256 colors. (In the future each frame in mFrames[..] may be 8bit, and the compositing frames 24)
Background color specified in GIF is ignored by web browsers.
If Frame 3 wants to dispose by restoring previous, what it wants is to restore the composition up to and including Frame 2, as well as Frame 2s disposal. So, in the middle of DoComposite when composing Frame 3, right after destroying Frame 2's area, we copy mCompositingFrame to mPrevCompositingFrame. When DoComposite get's called to do Frame 4, we copy mPrevCompositingFrame back, and then draw Frame 4 on top.

Definition at line 127 of file imgContainerGIF.h.


Member Enumeration Documentation

anonymous enum [private]

"Disposal" method indicates how the image should be handled before the subsequent image is displayed.

Enumerator:
DISPOSE_CLEAR_ALL 

Clear the whole image, revealing what was there before the gif displayed.

DISPOSE_NOT_SPECIFIED 

Leave frame, let new frame draw on top.

DISPOSE_KEEP 

Leave frame, let new frame draw on top.

DISPOSE_CLEAR 

Clear the frame's area, revealing bg.

DISPOSE_RESTORE_PREVIOUS 

Restore the previous (composited) frame.

Definition at line 142 of file imgContainerGIF.h.


Constructor & Destructor Documentation

Definition at line 70 of file imgContainerGIF.cpp.

{
  if (mTimer)
    mTimer->Cancel();
}

Member Function Documentation

Adds item to the end of the list of frames.

Parameters:
itemframe to add.

Fills an area of <aFrame> with black.

Parameters:
aFrameTarget Frame
Note:
Does not set the mask

Definition at line 969 of file imgContainerGIF.cpp.

{
  if (!aFrame)
    return;

  aFrame->LockImageData();

  PRUint8* aData;
  PRUint32 aDataLength;

  aFrame->GetImageData(&aData, &aDataLength);
  memset(aData, 0, aDataLength);

  nsCOMPtr<nsIInterfaceRequestor> ireq(do_QueryInterface(aFrame));
  if (ireq) {
    PRInt32 width;
    PRInt32 height;
    aFrame->GetWidth(&width);
    aFrame->GetHeight(&height);

    nsCOMPtr<nsIImage> img(do_GetInterface(ireq));
    nsIntRect r(0, 0, width, height);

    img->ImageUpdated(nsnull, nsImageUpdateFlags_kBitsChanged, &r);
  }

  aFrame->UnlockImageData();
}

Here is the call graph for this function:

Here is the caller graph for this function:

void imgContainerGIF::BlackenFrame ( gfxIImageFrame aFrame,
PRInt32  aX,
PRInt32  aY,
PRInt32  aWidth,
PRInt32  aHeight 
) [private]

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

Definition at line 999 of file imgContainerGIF.cpp.

{
  if (!aFrame)
    return;

  aFrame->LockImageData();

  PRInt32 widthFrame;
  PRInt32 heightFrame;
  aFrame->GetWidth(&widthFrame);
  aFrame->GetHeight(&heightFrame);

  const PRInt32 width  = PR_MIN(aWidth, (widthFrame - aX));
  const PRInt32 height = PR_MIN(aHeight, (heightFrame - aY));

  if (width <= 0 || height <= 0) {
    aFrame->UnlockImageData();
    return;
  }

  PRUint32 bpr; // Bytes Per Row
  aFrame->GetImageBytesPerRow(&bpr);

#if defined(XP_MAC) || defined(XP_MACOSX)
  const PRUint8 bpp = 4;
#else
  const PRUint8 bpp = 3;
#endif
  const PRUint32 bprToWrite = width * bpp;
  const PRUint32 xOffset = aX * bpp; // offset into row to start writing

  PRUint8* tmpRow = NS_STATIC_CAST(PRUint8*, nsMemory::Alloc(bprToWrite));

  if (!tmpRow) {
    aFrame->UnlockImageData();
    return;
  }

  memset(tmpRow, 0, bprToWrite);

  for (PRInt32 y = 0; y < height; y++) {
    aFrame->SetImageData(tmpRow, bprToWrite, ((y + aY) * bpr) + xOffset);
  }
  nsMemory::Free(tmpRow);

  aFrame->UnlockImageData();
}

Here is the call graph for this function:

void imgContainerGIF::BlackenFrame ( gfxIImageFrame aFrame,
nsIntRect aRect 
) [inline, private]

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

Definition at line 216 of file imgContainerGIF.h.

                                                                     {
    BlackenFrame(aFrame, aRect.x, aRect.y, aRect.width, aRect.height);
  }

Here is the call graph for this function:

void imgContainerGIF::BuildCompositeMask ( gfxIImageFrame aCompositingFrame,
gfxIImageFrame aOverlayFrame 
) [private]

Combine aOverlayFrame's mask into aCompositingFrame's mask.

This takes the mask information from the passed in aOverlayFrame and inserts that information into the aCompositingFrame's mask at the proper offsets. It does not rebuild the entire mask.

Parameters:
aCompositingFrameTarget frame
aOverlayFrameThis frame's mask is being copied

Definition at line 692 of file imgContainerGIF.cpp.

{
  if (!aCompositingFrame || !aOverlayFrame) return;

  nsresult res;
  PRUint8* compositingAlphaData;
  PRUint32 compositingAlphaDataLength;
  aCompositingFrame->LockAlphaData();
  res = aCompositingFrame->GetAlphaData(&compositingAlphaData,
                                        &compositingAlphaDataLength);
  if (!compositingAlphaData || !compositingAlphaDataLength || NS_FAILED(res)) {
    aCompositingFrame->UnlockAlphaData();
    return;
  }

  PRInt32 widthOverlay, heightOverlay;
  PRInt32 overlayXOffset, overlayYOffset;
  aOverlayFrame->GetWidth(&widthOverlay);
  aOverlayFrame->GetHeight(&heightOverlay);
  aOverlayFrame->GetX(&overlayXOffset);
  aOverlayFrame->GetY(&overlayYOffset);

  if (NS_FAILED(aOverlayFrame->LockAlphaData())) {
    // set the region of the overlay frame to visible in compositingFrame
    SetMaskVisibility(aCompositingFrame, overlayXOffset, overlayYOffset,
                      widthOverlay, heightOverlay, PR_TRUE);
    aCompositingFrame->UnlockAlphaData();
    return;
  }

  PRUint32 abprComposite;
  aCompositingFrame->GetAlphaBytesPerRow(&abprComposite);

  PRUint32 abprOverlay;
  aOverlayFrame->GetAlphaBytesPerRow(&abprOverlay);

  // Only the composite's width & height are needed.  x & y should always be 0.
  PRInt32 widthComposite, heightComposite;
  aCompositingFrame->GetWidth(&widthComposite);
  aCompositingFrame->GetHeight(&heightComposite);

  PRUint8* overlayAlphaData;
  PRUint32 overlayAlphaDataLength;
  res = aOverlayFrame->GetAlphaData(&overlayAlphaData, &overlayAlphaDataLength);

  gfx_format format;
  aCompositingFrame->GetFormat(&format);
  if (format != gfxIFormats::RGB_A1 && format != gfxIFormats::BGR_A1) {
    NS_NOTREACHED("GIFs only support 1 bit alpha");
    aCompositingFrame->UnlockAlphaData();
    aOverlayFrame->UnlockAlphaData();
    return;
  }

  // Exit if overlay is beyond the area of the composite
  if (widthComposite <= overlayXOffset || heightComposite <= overlayYOffset)
    return;

  const PRUint32 width  = PR_MIN(widthOverlay,
                                 widthComposite - overlayXOffset);
  const PRUint32 height = PR_MIN(heightOverlay,
                                 heightComposite - overlayYOffset);

#ifdef MOZ_PLATFORM_IMAGES_BOTTOM_TO_TOP
  // Account for bottom-up storage
  PRInt32 offset = ((heightComposite - 1) - overlayYOffset) * abprComposite;
#else
  PRInt32 offset = overlayYOffset * abprComposite;
#endif
  PRUint8* alphaLine = compositingAlphaData + offset + (overlayXOffset >> 3);

#ifdef MOZ_PLATFORM_IMAGES_BOTTOM_TO_TOP
  offset = (heightOverlay - 1) * abprOverlay;
#else
  offset = 0;
#endif
  PRUint8* overlayLine = overlayAlphaData + offset;

  /*
    This is the number of pixels of offset between alpha and overlay
    (the number of bits at the front of alpha to skip when starting a row).
    I.e:, for a mask_offset of 3:
    (these are representations of bits)
    overlay 'pixels':   76543210 hgfedcba
    alpha:              xxx76543 210hgfed ...
    where 'x' is data already in alpha
    the first 5 pixels of overlay are or'd into the low 5 bits of alpha
  */
  PRUint8 mask_offset = (overlayXOffset & 0x7);

  for(PRUint32 i = 0; i < height; i++) {
    PRUint8 pixels;
    PRUint32 j;
    // use locals to avoid keeping track of how much we need to add
    // at the end of a line.  we don't really need this since we may
    // be able to calculate the ending offsets, but it's simpler and
    // cheap.
    PRUint8 *localOverlay = overlayLine;
    PRUint8 *localAlpha   = alphaLine;

    for (j = width; j >= 8; j -= 8) {
      // don't do in for(...) to avoid reference past end of buffer
      pixels = *localOverlay++;

      if (pixels == 0) // no bits to set - iterate and bump output pointer
        localAlpha++;
      else {
        // for the last few bits of a line, we need to special-case it
        if (mask_offset == 0) // simple case, no offset
          *localAlpha++ |= pixels;
        else {
          *localAlpha++ |= (pixels >> mask_offset);
          *localAlpha   |= (pixels << (8U-mask_offset));
        }
      }
    }
    if (j != 0) {
      // handle the end of the line, 1 to 7 pixels
      pixels = *localOverlay++;
      if (pixels != 0) {
        // last few bits have to be handled more carefully if
        // width is not a multiple of 8.

        // set bits we don't want to change to 0
        pixels = (pixels >> (8U-j)) << (8U-j);
        *localAlpha++ |= (pixels >> mask_offset);
        // don't touch this byte unless we have bits for it
        if (j > (8U - mask_offset))
          *localAlpha |= (pixels << (8U-mask_offset));
      }
    }

#ifdef MOZ_PLATFORM_IMAGES_BOTTOM_TO_TOP
    alphaLine   -= abprComposite;
    overlayLine -= abprOverlay;
#else
    alphaLine   += abprComposite;
    overlayLine += abprOverlay;
#endif
  }

  aCompositingFrame->UnlockAlphaData();
  aOverlayFrame->UnlockAlphaData();
  return;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void imgIContainer::clear ( ) [inherited]
PRBool imgContainerGIF::CopyFrameImage ( gfxIImageFrame aSrcFrame,
gfxIImageFrame aDstFrame 
) [static, private]

Copy one gfxIImageFrame's image and mask into another.

Definition at line 1053 of file imgContainerGIF.cpp.

{
  PRUint8* aDataSrc;
  PRUint8* aDataDest;
  PRUint32 aDataLengthSrc;
  PRUint32 aDataLengthDest;

  if (!aSrcFrame || !aDstFrame)
    return PR_FALSE;

  if (NS_FAILED(aDstFrame->LockImageData()))
    return PR_FALSE;

  // Copy Image Over
  aSrcFrame->GetImageData(&aDataSrc, &aDataLengthSrc);
  aDstFrame->GetImageData(&aDataDest, &aDataLengthDest);
  if (!aDataDest || !aDataSrc || aDataLengthDest != aDataLengthSrc) {
    aDstFrame->UnlockImageData();
    return PR_FALSE;
  }
  memcpy(aDataDest, aDataSrc, aDataLengthSrc);
  aDstFrame->UnlockImageData();

  // Copy Alpha/Mask Over
  // If no mask, lockAlpha will tell us
  if (NS_SUCCEEDED(aDstFrame->LockAlphaData())) {
    aSrcFrame->GetAlphaData(&aDataSrc, &aDataLengthSrc);
    aDstFrame->GetAlphaData(&aDataDest, &aDataLengthDest);
    if (aDataDest && aDataSrc && aDataLengthDest == aDataLengthSrc)
      memcpy(aDataDest, aDataSrc, aDataLengthSrc);
    else
      memset(aDataDest, 0xFF, aDataLengthDest);

    aDstFrame->UnlockAlphaData();
  }

  // Tell the image that it's data has been updated
  nsCOMPtr<nsIInterfaceRequestor> ireq(do_QueryInterface(aDstFrame));
  if (!ireq)
    return PR_FALSE;
  nsCOMPtr<nsIImage> img(do_GetInterface(ireq));
  if (!img)
    return PR_FALSE;
  nsIntRect r;
  aDstFrame->GetRect(r);
  img->ImageUpdated(nsnull, nsImageUpdateFlags_kBitsChanged, &r);

  return PR_TRUE;
}

Here is the call graph for this function:

Here is the caller graph for this function:

nsresult imgContainerGIF::DoComposite ( gfxIImageFrame **  aFrameToUse,
nsIntRect aDirtyRect,
gfxIImageFrame aPrevFrame,
gfxIImageFrame aNextFrame,
PRInt32  aNextFrameIndex 
) [private]

Function for doing the frame compositing of animations.

Parameters:
aFrameToUseSet by DoComposite (aNextFrame, mCompositingFrame, or mCompositingPrevFrame)
aDirtyRectArea that the display will need to update
aPrevFrameLast Frame seen/processed
aNextFrameFrame we need to incorperate/display
aNextFrameIndexPosition of aNextFrame in mFrames list

Definition at line 464 of file imgContainerGIF.cpp.

{
  NS_ASSERTION(aDirtyRect, "imgContainerGIF::DoComposite aDirtyRect is null");
  NS_ASSERTION(aPrevFrame, "imgContainerGIF::DoComposite aPrevFrame is null");
  NS_ASSERTION(aNextFrame, "imgContainerGIF::DoComposite aNextFrame is null");
  NS_ASSERTION(aFrameToUse, "imgContainerGIF::DoComposite aFrameToUse is null");

  PRInt32 prevFrameDisposalMethod;
  aPrevFrame->GetFrameDisposalMethod(&prevFrameDisposalMethod);

  if (prevFrameDisposalMethod == DISPOSE_RESTORE_PREVIOUS &&
      !mCompositingPrevFrame)
    prevFrameDisposalMethod = DISPOSE_CLEAR;

  // Optimization: Skip compositing if the previous frame wants to clear the
  //               whole image
  if (prevFrameDisposalMethod == DISPOSE_CLEAR_ALL) {
    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
  //               container and it's clearing itself
  if (isFullPrevFrame && prevFrameDisposalMethod == DISPOSE_CLEAR) {
    aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
    *aFrameToUse = aNextFrame;
    return NS_OK;
  }

  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);

  PRBool nextFrameHasAlpha;
  PRUint32 aBPR;
  nextFrameHasAlpha = NS_SUCCEEDED(aNextFrame->GetAlphaBytesPerRow(&aBPR));

  // 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 != DISPOSE_RESTORE_PREVIOUS) &&
      !nextFrameHasAlpha) {

    aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
    *aFrameToUse = aNextFrame;
    return NS_OK;
  }

  // Calculate area that needs updating
  switch (prevFrameDisposalMethod) {
    default:
    case DISPOSE_NOT_SPECIFIED:
    case DISPOSE_KEEP:
      *aDirtyRect = nextFrameRect;
      break;

    case DISPOSE_CLEAR:
      // 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.
      aDirtyRect->UnionRect(nextFrameRect, prevFrameRect);
      break;

    case DISPOSE_RESTORE_PREVIOUS:
      aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
      break;
  }

  // Optimization:
  //   Skip compositing if the last composited frame is this frame
  //   (Only one composited frame was made for this animation.  Example:
  //    Only Frame 3 of a 10 frame GIF required us to build a composite frame
  //    On the second loop of the GIF, we do not need to rebuild the frame
  //    since it's still sitting in mCompositingFrame)
  if (mLastCompositedFrameIndex == aNextFrameIndex) {
    *aFrameToUse = mCompositingFrame;
    return NS_OK;
  }

  PRBool needToBlankComposite = PR_FALSE;

  // Create the Compositing Frame
  if (!mCompositingFrame) {
    nsresult rv;
    mCompositingFrame = do_CreateInstance("@mozilla.org/gfx/image/frame;2", &rv);
    if (NS_FAILED(rv))
      return rv;
    rv = mCompositingFrame->Init(0, 0, mSize.width, mSize.height,
                                 gfxIFormats::RGB_A1, 24);
    if (NS_FAILED(rv)) {
      NS_WARNING("Failed to init mCompositingFrame!\n");
      mCompositingFrame = nsnull;
      return rv;
    }
    needToBlankComposite = PR_TRUE;
  }

  // Copy previous frame into mCompositingFrame 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 (mLastCompositedFrameIndex != aNextFrameIndex - 1 &&
      prevFrameDisposalMethod != DISPOSE_RESTORE_PREVIOUS) {

    // XXX If we had a method of drawing a section of a frame into another, we
    //     could optimize further:
    //     if aPrevFrameIndex == 1 && mLastCompositedFrameIndex <> -1,
    //     only mFirstFrameRefreshArea needs to be drawn back to composite
    if (isFullPrevFrame) {
      CopyFrameImage(aPrevFrame, mCompositingFrame);
    } else {
      BlackenFrame(mCompositingFrame);
      SetMaskVisibility(mCompositingFrame, PR_FALSE);
      aPrevFrame->DrawTo(mCompositingFrame, prevFrameRect.x, prevFrameRect.y,
                         prevFrameRect.width, prevFrameRect.height);

      BuildCompositeMask(mCompositingFrame, aPrevFrame);
      needToBlankComposite = PR_FALSE;
    }
  }

  // Dispose of previous
  switch (prevFrameDisposalMethod) {
    case DISPOSE_CLEAR:
      if (needToBlankComposite) {
        // If we just created the composite, it could have anything in it's
        // buffers. Clear them
        BlackenFrame(mCompositingFrame);
        SetMaskVisibility(mCompositingFrame, PR_FALSE);
        needToBlankComposite = PR_FALSE;
      } else {
        // Blank out previous frame area (both color & Mask/Alpha)
        BlackenFrame(mCompositingFrame, prevFrameRect);
        SetMaskVisibility(mCompositingFrame, prevFrameRect, PR_FALSE);
      }
      break;

    case DISPOSE_RESTORE_PREVIOUS:
      // It would be better to copy only the area changed back to
      // mCompositingFrame.
      if (mCompositingPrevFrame) {
        CopyFrameImage(mCompositingPrevFrame, mCompositingFrame);

        // destroy only if we don't need it for this frame's disposal
        if (nextFrameDisposalMethod != DISPOSE_RESTORE_PREVIOUS)
          mCompositingPrevFrame = nsnull;
      } else {
        BlackenFrame(mCompositingFrame);
        SetMaskVisibility(mCompositingFrame, PR_FALSE);
      }
      break;
  }

  // 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 it's image restored
  // too
  if ((nextFrameDisposalMethod == DISPOSE_RESTORE_PREVIOUS) &&
      (prevFrameDisposalMethod != DISPOSE_RESTORE_PREVIOUS)) {
    // We are storing the whole image.
    // It would be better if we just stored the area that nextFrame is going to
    // overwrite.
    if (!mCompositingPrevFrame) {
      nsresult rv;
      mCompositingPrevFrame = do_CreateInstance("@mozilla.org/gfx/image/frame;2",
                                                &rv);
      if (NS_FAILED(rv))
        return rv;
      rv = mCompositingPrevFrame->Init(0, 0, mSize.width, mSize.height,
                                       gfxIFormats::RGB_A1, 24);
      if (NS_FAILED(rv))
        return rv;
    }
    CopyFrameImage(mCompositingFrame, mCompositingPrevFrame);
  }

  // blit next frame into it's correct spot
  aNextFrame->DrawTo(mCompositingFrame, nextFrameRect.x, nextFrameRect.y,
                     nextFrameRect.width, nextFrameRect.height);
  // put the mask in
  BuildCompositeMask(mCompositingFrame, aNextFrame);
  // Set timeout of CompositeFrame to timeout of frame we just composed
  // Bug 177948
  PRInt32 timeout;
  aNextFrame->GetTimeout(&timeout);
  mCompositingFrame->SetTimeout(timeout);

  if (isFullNextFrame && mAnimationMode == kNormalAnimMode && mLoopCount != 0) {
    // 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(mCompositingFrame, aNextFrame)) {
      aPrevFrame->SetFrameDisposalMethod(DISPOSE_CLEAR_ALL);
      mLastCompositedFrameIndex = -1;
      *aFrameToUse = aNextFrame;
      return NS_OK;
    }
  }

  mLastCompositedFrameIndex = aNextFrameIndex;
  *aFrameToUse = mCompositingFrame;

  return NS_OK;
}

Here is the call graph for this function:

void imgIContainer::endFrameDecode ( in unsigned long  framenumber,
in unsigned long  timeout 
) [inherited]
gfxIImageFrame imgIContainer::getFrameAt ( in unsigned long  index) [inherited]
void imgIContainer::init ( in PRInt32  aWidth,
in PRInt32  aHeight,
in imgIContainerObserver  aObserver 
) [inherited]

Create a new aWidth x aHeight sized image container.

Parameters:
aWidthThe width of the container in which all the gfxIImageFrame children will fit.
aHeightThe height of the container in which all the gfxIImageFrame children will fit.
aObserverObserver to send animation notifications to.

Definition at line 151 of file imgContainerGIF.h.

Here is the call graph for this function:

void nsITimerCallback::notify ( in nsITimer  timer) [inherited]
Parameters:
aTimerthe timer which has expired
void imgContainerGIF::SetMaskVisibility ( gfxIImageFrame aFrame,
PRBool  aVisible 
) [private]

Sets an area of the frame's mask.

Parameters:
aFrameTarget Frame
aVisibleTurn on (PR_TRUE) or off (PR_FALSE) visibility
Note:
Invisible area of frame's image will need to be set to 0

Definition at line 950 of file imgContainerGIF.cpp.

{
  if (!aFrame)
    return;

  PRUint8* alphaData;
  PRUint32 alphaDataLength;
  const PRUint8 setMaskTo = aVisible ? 0xFF : 0x00;

  aFrame->LockAlphaData();
  nsresult res = aFrame->GetAlphaData(&alphaData, &alphaDataLength);
  if (NS_SUCCEEDED(res) && alphaData && alphaDataLength)
    memset(alphaData, setMaskTo, alphaDataLength);
  aFrame->UnlockAlphaData();
  return;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void imgContainerGIF::SetMaskVisibility ( gfxIImageFrame aFrame,
PRInt32  aX,
PRInt32  aY,
PRInt32  aWidth,
PRInt32  aHeight,
PRBool  aVisible 
) [private]

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

Definition at line 840 of file imgContainerGIF.cpp.

{
  if (!aFrame)
    return;

  nsresult res;
  PRUint8* alphaData;
  PRUint32 alphaDataLength;
  aFrame->LockAlphaData();
  res = aFrame->GetAlphaData(&alphaData, &alphaDataLength);
  if (!alphaData || !alphaDataLength || NS_FAILED(res)) {
    aFrame->UnlockAlphaData();
    return;
  }

  PRInt32 frameWidth;
  PRInt32 frameHeight;
  aFrame->GetWidth(&frameWidth);
  aFrame->GetHeight(&frameHeight);

  const PRInt32 width  = PR_MIN(aWidth, frameWidth - aX);
  const PRInt32 height = PR_MIN(aHeight, frameHeight - aY);

  if (width <= 0 || height <= 0) {
    aFrame->UnlockAlphaData();
    return;
  }

  gfx_format format;
  aFrame->GetFormat(&format);
  if (format != gfxIFormats::RGB_A1 && format != gfxIFormats::BGR_A1) {
    NS_NOTREACHED("GIFs only support 1 bit alpha");
    aFrame->UnlockAlphaData();
    return;
  }

  PRUint32 abpr;
  aFrame->GetAlphaBytesPerRow(&abpr);

#ifdef MOZ_PLATFORM_IMAGES_BOTTOM_TO_TOP
  // Account for bottom-up storage.
  // Start at the bottom (top in memory), go to the top (bottom in memory)
  PRUint8* alphaLine = alphaData + ((frameHeight - aY - height) * abpr) +
                       (aX >> 3);
#else
  PRUint8* alphaLine = alphaData + (aY * abpr) + (aX >> 3);
#endif
  PRUint8 maskShiftStartBy = aX & 0x7;
  PRUint8 numReplacingStart = 8U - maskShiftStartBy;
  PRUint32 rowBytes;
  PRUint8 maskStart = 0; // Init to shutup compiler; Only used if
                         // maskShiftStartBy != 0
  PRUint8 maskEnd;

  if (width <= numReplacingStart) {
    maskEnd = (0xFF >> (8U - width)) << (numReplacingStart - width);
    // Don't write start bits, only end bits (which contain both start & end)
    maskShiftStartBy = 0;
    rowBytes = 0;
  } else {
    if (maskShiftStartBy == 0)
      numReplacingStart = 0;
    else
      maskStart = 0xFF >> maskShiftStartBy;

    PRUint8 maskShiftEndBy = (width - numReplacingStart) & 0x7;
    maskEnd = ~(0xFF >> maskShiftEndBy);
    rowBytes = (width - numReplacingStart - maskShiftEndBy) >> 3;
  }

  if (aVisible) {
    for (PRInt32 i = 0; i < height; i++) {
      PRUint8 *localAlpha = alphaLine;

      if (maskShiftStartBy != 0)
        *localAlpha++ |= maskStart;

      if (rowBytes > 0)
        memset(localAlpha, 0xFF, rowBytes);

      if (maskEnd != 0)
        localAlpha[rowBytes] |= maskEnd;

      alphaLine += abpr;
    }
  } else {
    for (PRInt32 i = 0; i < height; i++) {
      PRUint8 *localAlpha = alphaLine;

      if (maskShiftStartBy != 0)
        *localAlpha++ &= ~maskStart;

      if (rowBytes > 0)
        memset(localAlpha, 0x00, rowBytes);

      if (maskEnd != 0)
        localAlpha[rowBytes] &= ~maskEnd;

      alphaLine += abpr;
    } // for
  } // if aVisible

  aFrame->UnlockAlphaData();
  return;
}

Here is the call graph for this function:

void imgContainerGIF::SetMaskVisibility ( gfxIImageFrame aFrame,
nsIntRect aRect,
PRBool  aVisible 
) [inline, private]

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

Definition at line 199 of file imgContainerGIF.h.

                                                            {
    SetMaskVisibility(aFrame, aRect.x, aRect.y,
                      aRect.width, aRect.height, aVisible);
  }

Here is the call graph for this function:


Member Data Documentation

attribute unsigned short imgIContainer::animationMode [inherited]

Definition at line 103 of file imgIContainer.idl.

Get the current frame that would be drawn if the image was to be drawn now.

Definition at line 88 of file imgIContainer.idl.

readonly attribute PRInt32 imgIContainer::height [inherited]

The height of the container rectangle.

Definition at line 82 of file imgIContainer.idl.

const short imgIContainer::kDontAnimMode = 1 [inherited]

Definition at line 100 of file imgIContainer.idl.

const short imgIContainer::kLoopOnceAnimMode = 2 [inherited]

Definition at line 101 of file imgIContainer.idl.

const short imgIContainer::kNormalAnimMode = 0 [inherited]

Animation mode Constants 0 = normal 1 = don't animate 2 = loop once.

Definition at line 99 of file imgIContainer.idl.

number of times to loop the image.

Note:
-1 means forever.

Definition at line 135 of file imgIContainer.idl.

Are we currently animating the GIF?

Definition at line 245 of file imgContainerGIF.h.

See imgIContainer for mode constants.

Definition at line 247 of file imgContainerGIF.h.

For managing blending of frames.

Some GIF animations will use the mCompositingFrame to composite images and just hand this back to the caller when it is time to draw the frame. NOTE: When clearing mCompositingFrame, remember to set mLastCompositedFrameIndex to -1. Code assume that if mLastCompositedFrameIndex >= 0 then mCompositingFrame exists.

Definition at line 262 of file imgContainerGIF.h.

the previous composited frame, for DISPOSE_RESTORE_PREVIOUS

The Previous Frame (all frames composited up to the current) needs to be stored in cases where the GIF specifies it wants the last frame back when it's done with the current frame.

Definition at line 270 of file imgContainerGIF.h.

Definition at line 236 of file imgContainerGIF.h.

Definition at line 235 of file imgContainerGIF.h.

Whether we can assume there will be no more frames (and thus loop the animation)

Definition at line 241 of file imgContainerGIF.h.

Area of the first frame that needs to be redrawn on subsequent loops.

Definition at line 233 of file imgContainerGIF.h.

All the <gfxIImageFrame>s of the GIF.

Definition at line 228 of file imgContainerGIF.h.

Track the last composited frame for Optimizations (See DoComposite code)

Definition at line 238 of file imgContainerGIF.h.

loops remaining before animation stops (-1 no stop)

Definition at line 249 of file imgContainerGIF.h.

imgIContainerObserver; used for telling observers that the frame changed

Definition at line 226 of file imgContainerGIF.h.

Size of GIF (not necessarily the frame)

Definition at line 231 of file imgContainerGIF.h.

Timer to animate multiframed images.

Definition at line 252 of file imgContainerGIF.h.

readonly attribute unsigned long imgIContainer::numFrames [inherited]

Definition at line 91 of file imgIContainer.idl.

Definition at line 72 of file imgIContainer.idl.

readonly attribute PRInt32 imgIContainer::width [inherited]

The width of the container rectangle.

Definition at line 77 of file imgIContainer.idl.


The documentation for this class was generated from the following files: