All pastes #188208 Raw Edit

Divers

public text v1 · immutable
·published 2006-10-01 21:47 UTC
rendered paste body
Index: imgContainer.cpp
===================================================================
RCS file: /cvsroot/mozilla/modules/libpr0n/src/imgContainer.cpp,v
retrieving revision 1.42
diff -u -r1.42 imgContainer.cpp
--- imgContainer.cpp	12 Jan 2005 20:16:07 -0000	1.42
+++ imgContainer.cpp	1 Oct 2006 21:46:41 -0000
@@ -40,15 +40,24 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsComponentManagerUtils.h"
+#include "nsWeakReference.h"
 
 #include "imgContainer.h"
 
-NS_IMPL_ISUPPORTS2(imgContainer, imgIContainer, nsIProperties)
+NS_IMPL_ISUPPORTS3(imgContainer, imgIContainer, nsITimerCallback, nsIProperties)
 
 //******************************************************************************
 imgContainer::imgContainer() :
   mSize(0,0),
-  mFrame(nsnull)
+  mFirstFrameRefreshArea(),
+  mCurrentDecodingFrameIndex(0),
+  mCurrentAnimationFrameIndex(0),
+  mLastCompositedFrameIndex(-1),
+  mDoneDecoding(PR_FALSE),
+  mAnimating(PR_FALSE),
+  mAnimationMode(kNormalAnimMode),
+  mLoopCount(-1),
+  mObserver(nsnull)
 {
   mProperties = do_CreateInstance("@mozilla.org/properties;1");
 }
@@ -56,12 +65,15 @@
 //******************************************************************************
 imgContainer::~imgContainer()
 {
-  mFrame = nsnull;
+  if (mTimer)
+    mTimer->Cancel();
 }
 
 //******************************************************************************
-/* void init (in PRIn32 aWidth, in PRInt32 aHeight, in imgIContainerObserver aObserver); */
-NS_IMETHODIMP imgContainer::Init(PRInt32 aWidth, PRInt32 aHeight, imgIContainerObserver *aObserver)
+/* void init (in PRInt32 aWidth, in PRInt32 aHeight, 
+              in imgIContainerObserver aObserver); */
+NS_IMETHODIMP imgContainer::Init(PRInt32 aWidth, PRInt32 aHeight,
+                                 imgIContainerObserver *aObserver)
 {
   if (aWidth <= 0 || aHeight <= 0) {
     NS_WARNING("error - negative image size\n");
@@ -70,6 +82,10 @@
 
   mSize.SizeTo(aWidth, aHeight);
 
+  // no matching function for call do_GetWeakReference(imgIContainerObserver*&)
+  // is this referenced already?
+  mObserver = do_GetWeakReference(NS_STATIC_CAST(nsISupports*, aObserver));
+
   return NS_OK;
 }
 
@@ -117,15 +133,13 @@
   NS_ASSERTION(aCurrentFrame, "imgContainer::GetCurrentFrame; Invalid Arg");
   if (!aCurrentFrame)
     return NS_ERROR_INVALID_POINTER;
-
-  if (mFrame) {
-    *aCurrentFrame = mFrame;
-    NS_ADDREF(*aCurrentFrame);
-    return NS_OK;
-  } else {
-    *aCurrentFrame = nsnull;
+  
+  if (!(*aCurrentFrame = inlinedGetCurrentFrame()))
     return NS_ERROR_FAILURE;
-  }
+
+  NS_ADDREF(*aCurrentFrame);
+  
+  return NS_OK;
 }
 
 //******************************************************************************
@@ -136,7 +150,8 @@
   if (!aNumFrames)
     return NS_ERROR_INVALID_ARG;
 
-  *aNumFrames = mFrame ? 1 : 0;
+  *aNumFrames = mFrames.Count();
+  
   return NS_OK;
 }
 
@@ -144,14 +159,17 @@
 /* gfxIImageFrame getFrameAt (in unsigned long index); */
 NS_IMETHODIMP imgContainer::GetFrameAt(PRUint32 index, gfxIImageFrame **_retval)
 {
+  NS_ENSURE_ARG(index < NS_STATIC_CAST(PRUint32, mFrames.Count()));
+
   NS_ASSERTION(_retval, "imgContainer::GetFrameAt; Invalid Arg");
   if (!_retval)
     return NS_ERROR_INVALID_POINTER;
 
-  if (!mFrame || index != 0)
+  if (!(*_retval = mFrames[index]))
     return NS_ERROR_FAILURE;
-  *_retval = mFrame;
+
   NS_ADDREF(*_retval);
+  
   return NS_OK;
 }
 
@@ -162,12 +180,35 @@
   NS_ASSERTION(item, "imgContainer::AppendFrame; Invalid Arg");
   if (!item)
     return NS_ERROR_INVALID_ARG;
-  NS_ASSERTION(!mFrame, "imgContainer::AppendFrame; Tried to add another frame when only 1 is allowed");
+  
+  PRInt32 numFrames = mFrames.Count();
+  if (numFrames == 0) {
+    // First Frame
+    // If we dispose of the first frame by clearing it, then the
+    // First Frame's refresh area is all of itself.
+    // RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR)
+    PRInt32 frameDisposalMethod;
+    item->GetFrameDisposalMethod(&frameDisposalMethod);
+    if (frameDisposalMethod == DISPOSE_CLEAR ||
+        frameDisposalMethod == DISPOSE_RESTORE_PREVIOUS)
+      item->GetRect(mFirstFrameRefreshArea);
+  } else {
+    // Calculate mFirstFrameRefreshArea
+    // Some gifs are huge but only have a small area that they animate
+    // We only need to refresh that small area when Frame 0 comes around again
+    nsIntRect itemRect;
+    item->GetRect(itemRect);
+    mFirstFrameRefreshArea.UnionRect(mFirstFrameRefreshArea, itemRect);
+  }
 
-  if (mFrame)
-    return NS_ERROR_UNEXPECTED;
+  mFrames.AppendObject(item);
 
-  mFrame = item;
+  // If this is our second frame, start the animation.
+  // Must be called after AppendElement because StartAnimation checks for > 1
+  // frame
+  if (numFrames == 1)
+    StartAnimation();
+  
   return NS_OK;
 }
 
@@ -182,13 +223,10 @@
 /* void endFrameDecode (in gfxIImageFrame item, in unsigned long timeout); */
 NS_IMETHODIMP imgContainer::EndFrameDecode(PRUint32 aFrameNum, PRUint32 aTimeout)
 {
-  NS_ASSERTION(aFrameNum == 0, "Received an EndFrameDecode call with an invalid frame number");
-
-  if (aFrameNum != 0 || !mFrame)
-    return NS_ERROR_UNEXPECTED;
-
-  mFrame->SetTimeout(aTimeout);
-
+  // Assume there's another frame.
+  // both aFrameNum and mCurrentDecodingFrameIndex are 0 based.
+  mCurrentDecodingFrameIndex = aFrameNum + 1;
+  
   return NS_OK;
 }
 
@@ -196,6 +234,11 @@
 /* void decodingComplete (); */
 NS_IMETHODIMP imgContainer::DecodingComplete(void)
 {
+  mDoneDecoding = PR_TRUE;
+  // If there's only 1 frame, optimize it.
+  // Optimizing animated PNGs is not supported
+  if (mFrames.Count() == 1)
+    mFrames[0]->SetMutable(PR_FALSE);
   return NS_OK;
 }
 
@@ -203,7 +246,7 @@
 /* void clear (); */
 NS_IMETHODIMP imgContainer::Clear()
 {
-  mFrame = nsnull;
+  mFrames.Clear();
   return NS_OK;
 }
 
@@ -214,13 +257,31 @@
   if (!aAnimationMode)
     return NS_ERROR_INVALID_ARG;
 
-  *aAnimationMode = kDontAnimMode;
+  *aAnimationMode = mAnimationMode;
   return NS_OK;
 }
 
 NS_IMETHODIMP imgContainer::SetAnimationMode(PRUint16 aAnimationMode)
 {
-  // Ignore setting of animation mode
+  NS_ASSERTION(aAnimationMode == imgIContainer::kNormalAnimMode ||
+               aAnimationMode == imgIContainer::kDontAnimMode ||
+               aAnimationMode == imgIContainer::kLoopOnceAnimMode,
+               "Wrong Animation Mode is being set!");
+
+  switch (mAnimationMode = aAnimationMode) {
+    case kDontAnimMode:
+      StopAnimation();
+      break;
+    case kNormalAnimMode:
+      if (mLoopCount != 0 || mCurrentAnimationFrameIndex + 1 < mFrames.Count())
+        StartAnimation();
+      break;
+    case kLoopOnceAnimMode:
+      if (mCurrentAnimationFrameIndex + 1 < mFrames.Count())
+        StartAnimation();
+      break;
+  }
+  
   return NS_OK;
 }
 
@@ -228,6 +289,30 @@
 /* void startAnimation () */
 NS_IMETHODIMP imgContainer::StartAnimation()
 {
+  if (mAnimationMode == kDontAnimMode || mAnimating || mTimer)
+    return NS_OK;
+
+  if (mFrames.Count() > 1) {
+    PRInt32 timeout;
+    gfxIImageFrame *currentFrame = inlinedGetCurrentFrame();
+    if (currentFrame) {
+      currentFrame->GetTimeout(&timeout);
+      if (timeout <= 0) // -1 means display this frame forever
+        return NS_OK;
+    } else
+      timeout = 100; // XXX hack.. the timer notify code will do the right
+                     //     thing, so just get that started
+
+    mTimer = do_CreateInstance("@mozilla.org/timer;1");
+    if (!mTimer)
+      return NS_ERROR_OUT_OF_MEMORY;
+
+    // The only way mAnimating becomes true is if the mTimer is created
+    mAnimating = PR_TRUE;
+    mTimer->InitWithCallback(NS_STATIC_CAST(nsITimerCallback*, this),
+                             timeout, nsITimer::TYPE_REPEATING_SLACK);
+  }
+
   return NS_OK;
 }
 
@@ -235,24 +320,61 @@
 /* void stopAnimation (); */
 NS_IMETHODIMP imgContainer::StopAnimation()
 {
+  mAnimating = PR_FALSE;
+
+  if (!mTimer)
+    return NS_OK;
+
+  mTimer->Cancel();
+  mTimer = nsnull;
+
   return NS_OK;
 }
 
 //******************************************************************************
+/* void resetAnimation (); */
+NS_IMETHODIMP imgContainer::ResetAnimation()
+{
+  if (mCurrentAnimationFrameIndex == 0 || mAnimationMode == kDontAnimMode)
+    return NS_OK;
+
+  PRBool oldAnimating = mAnimating;
+
+  if (mAnimating) {
+    nsresult rv = StopAnimation();
+    if (NS_FAILED(rv))
+      return rv;
+  }
+
+  mLastCompositedFrameIndex = -1;
+  mCurrentAnimationFrameIndex = 0;
+  // Update display
+  //~ nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
+  //~ if (observer)
+    //~ observer->FrameChanged(this, mFrames[0], &mFirstFrameRefreshArea);
+
+  if (oldAnimating)
+    return StartAnimation();
+  else
+    return NS_OK;
+}
+
+//******************************************************************************
 /* attribute long loopCount; */
 NS_IMETHODIMP imgContainer::GetLoopCount(PRInt32 *aLoopCount)
 {
-  return NS_ERROR_NOT_IMPLEMENTED;
+  NS_ASSERTION(aLoopCount, "ptr is null");
+  *aLoopCount = mLoopCount;
+  return NS_OK;
 }
 
 NS_IMETHODIMP imgContainer::SetLoopCount(PRInt32 aLoopCount)
 {
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
+  // -1  infinite
+  //  0  no looping, one iteration
+  //  1  one loop, two iterations
+  //  ...
+  mLoopCount = aLoopCount;
 
-//******************************************************************************
-/* void resetAnimation (); */
-NS_IMETHODIMP imgContainer::ResetAnimation()
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
+  return NS_OK;
 }
Index: imgContainer.h
===================================================================
RCS file: /cvsroot/mozilla/modules/libpr0n/src/imgContainer.h,v
retrieving revision 1.20
diff -u -r1.20 imgContainer.h
--- imgContainer.h	12 Jan 2005 20:16:07 -0000	1.20
+++ imgContainer.h	1 Oct 2006 21:46:41 -0000
@@ -41,11 +41,13 @@
 #ifndef __imgContainer_h__
 #define __imgContainer_h__
 
+#include "nsCOMArray.h"
 #include "nsCOMPtr.h"
 #include "imgIContainer.h"
 #include "gfxIImageFrame.h"
 #include "nsIProperties.h"
-
+#include "nsITimer.h"
+#include "nsWeakReference.h"
 
 #define NS_IMGCONTAINER_CID \
 { /* 27f0682c-ff64-4dd2-ae7a-668e59f2fd38 */         \
@@ -55,19 +57,79 @@
     {0xae, 0x7a, 0x66, 0x8e, 0x59, 0xf2, 0xfd, 0x38} \
 }
 
-class imgContainer : public imgIContainer, public nsIProperties
+class imgContainer : public imgIContainer, 
+                     public nsITimerCallback, 
+                     public nsIProperties
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_IMGICONTAINER
+  NS_DECL_NSITIMERCALLBACK
   NS_FORWARD_SAFE_NSIPROPERTIES(mProperties)
 
   imgContainer();
   virtual ~imgContainer();
 
 private:
+
+  /** "Disposal" method indicates how the image should be handled before the
+   *   subsequent image is displayed.
+   */
+  enum {
+    DISPOSE_CLEAR_ALL       = -1, //!< Clear the whole image, revealing
+                                  //!  what was there before the gif displayed
+    DISPOSE_NOT_SPECIFIED    = 0, //!< Leave frame, let new frame draw on top
+    DISPOSE_KEEP             = 1, //!< Leave frame, let new frame draw on top
+    DISPOSE_CLEAR            = 2, //!< Clear the frame's area, revealing bg
+    DISPOSE_RESTORE_PREVIOUS = 3  //!< Restore the previous (composited) frame
+  };
+
+  inline gfxIImageFrame* inlinedGetCurrentFrame() {
+    if (mLastCompositedFrameIndex == mCurrentAnimationFrameIndex)
+      return mCompositingFrame;
+    return mFrames.SafeObjectAt(mCurrentAnimationFrameIndex);
+  }
+
   nsIntSize mSize;
-  nsCOMPtr<gfxIImageFrame> mFrame;
+
+  //! All the <gfxIImageFrame>s of the PNG
+  nsCOMArray<gfxIImageFrame> mFrames;
+
+  //! Area of the first frame that needs to be redrawn on subsequent loops
+  nsIntRect                  mFirstFrameRefreshArea;
+  
+  PRInt32                    mCurrentDecodingFrameIndex; // 0 to numFrames-1
+  PRInt32                    mCurrentAnimationFrameIndex; // 0 to numFrames-1
+  //! Track the last composited frame for Optimizations (See DoComposite code)
+  PRInt32                    mLastCompositedFrameIndex;
+  
+  //! Whether we can assume there will be no more frames
+  //! (and thus loop the animation)
+  PRBool                     mDoneDecoding;
+
+  //! Are we currently animating the PNG?
+  PRBool                     mAnimating;
+  //! See imgIContainer for mode constants
+  PRUint16                   mAnimationMode;
+  //! # loops remaining before animation stops (-1 no stop)
+  PRInt32                    mLoopCount;
+
+  /** 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.
+   */
+  nsCOMPtr<gfxIImageFrame>   mCompositingFrame;
+
+  //! imgIContainerObserver
+  nsWeakPtr                  mObserver;
+
+  //! Timer to animate multiframed images
+  nsCOMPtr<nsITimer>         mTimer;
+
   nsCOMPtr<nsIProperties>  mProperties;
 };