rendered paste bodyIndex: 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;
};