Bug 391295 - "APNG blending mode APNG_BLEND_OP_SOURCE not implemented" [p=asmith15@littlesvr.ca (Andrew Smith) r=stuart a1.9=damons]
authorreed@reedloden.com
Tue, 23 Oct 2007 12:33:20 -0700
changeset 7108 2ffd3198045226e3f8186d8a5fc37f182c55a16d
parent 7107 0644ef61b858a9e132d2c7cca2f51db1a4c3913c
child 7109 a83df293471cc17986381956a41de2bdd1cd6a28
push idunknown
push userunknown
push dateunknown
reviewersstuart
bugs391295
milestone1.9a9pre
Bug 391295 - "APNG blending mode APNG_BLEND_OP_SOURCE not implemented" [p=asmith15@littlesvr.ca (Andrew Smith) r=stuart a1.9=damons]
gfx/idl/gfxIImageFrame.idl
gfx/src/shared/gfxImageFrame.cpp
gfx/src/shared/gfxImageFrame.h
modules/libpr0n/decoders/png/nsPNGDecoder.cpp
modules/libpr0n/public/imgIContainer.idl
modules/libpr0n/src/imgContainer.cpp
--- 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(2502c898-73bd-4da5-8fae-21cf7a492f64)]
+[scriptable, uuid(7292afa2-3b94-424d-97d5-51ce2f04c0fe)]
 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.
@@ -151,9 +151,13 @@ interface gfxIImageFrame : nsISupports
    */
   attribute long timeout;
 
   /* frame disposal method, used only by GIFs. Should probably subclass image frame
    * and image container for GIFs special needs, but for simplicity it is here for the
    * moment
    */
   attribute long frameDisposalMethod;
+
+  /* PNG specific methods */
+
+  attribute long blendMethod;
 };
--- a/gfx/src/shared/gfxImageFrame.cpp
+++ b/gfx/src/shared/gfxImageFrame.cpp
@@ -41,17 +41,18 @@
 #include "nsIServiceManager.h"
 
 NS_IMPL_ISUPPORTS2(gfxImageFrame, gfxIImageFrame, nsIInterfaceRequestor)
 
 gfxImageFrame::gfxImageFrame() :
   mInitialized(PR_FALSE),
   mMutable(PR_TRUE),
   mTimeout(100),
-  mDisposalMethod(0)
+  mDisposalMethod(0), /* imgIContainer::kDisposeNotSpecified */
+  mBlendMethod(1) /* imgIContainer::kBlendOver */
 {
   /* member initializers and constructor code */
 }
 
 gfxImageFrame::~gfxImageFrame()
 {
   /* destructor code */
 }
@@ -338,16 +339,34 @@ NS_IMETHODIMP gfxImageFrame::SetFrameDis
 {
   if (!mInitialized)
     return NS_ERROR_NOT_INITIALIZED;
 
   mDisposalMethod = aFrameDisposalMethod;
   return NS_OK;
 }
 
+/* attribute long blendMethod; */
+NS_IMETHODIMP gfxImageFrame::GetBlendMethod(PRInt32 *aBlendMethod)
+{
+  if (!mInitialized)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  *aBlendMethod = mBlendMethod;
+  return NS_OK;
+}
+NS_IMETHODIMP gfxImageFrame::SetBlendMethod(PRInt32 aBlendMethod)
+{
+  if (!mInitialized)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  mBlendMethod = (PRInt8)aBlendMethod;
+  return NS_OK;
+}
+
 NS_IMETHODIMP gfxImageFrame::GetInterface(const nsIID & aIID, void * *result)
 {
   if (!mInitialized)
     return NS_ERROR_NOT_INITIALIZED;
 
   NS_ENSURE_ARG_POINTER(result);
 
   if (NS_SUCCEEDED(QueryInterface(aIID, result)))
--- a/gfx/src/shared/gfxImageFrame.h
+++ b/gfx/src/shared/gfxImageFrame.h
@@ -75,9 +75,10 @@ private:
 
   PRPackedBool mInitialized;
   PRPackedBool mMutable;
   gfx_format   mFormat;
 
   PRInt32      mTimeout; // -1 means display forever
   nsIntPoint   mOffset;
   PRInt32      mDisposalMethod;
+  PRInt8       mBlendMethod;
 };
--- a/modules/libpr0n/decoders/png/nsPNGDecoder.cpp
+++ b/modules/libpr0n/decoders/png/nsPNGDecoder.cpp
@@ -131,23 +131,23 @@ void nsPNGDecoder::CreateFrame(png_uint_
   mFrameHasNoAlpha = PR_TRUE;
 }
 
 // set timeout and frame disposal method for the current frame
 void nsPNGDecoder::SetAnimFrameInfo()
 {
   png_uint_16 delay_num, delay_den; /* in seconds */
   png_byte dispose_op;
+  png_byte blend_op;
   PRInt32 timeout; /* in milliseconds */
   
   delay_num = png_get_next_frame_delay_num(mPNG, mInfo);
   delay_den = png_get_next_frame_delay_den(mPNG, mInfo);
   dispose_op = png_get_next_frame_dispose_op(mPNG, mInfo);
-
-  // XXX need to handle blend_op here!
+  blend_op = png_get_next_frame_blend_op(mPNG, mInfo);
 
   if (delay_num == 0) {
     timeout = 0; // gfxImageFrame::SetTimeout() will set to a minimum
   } else {
     if (delay_den == 0)
       delay_den = 100; // so sais the APNG spec
     
     // Need to cast delay_num to float to have a proper division and
@@ -158,16 +158,21 @@ void nsPNGDecoder::SetAnimFrameInfo()
   mFrame->SetTimeout(timeout);
   
   if (dispose_op == PNG_DISPOSE_OP_PREVIOUS)
       mFrame->SetFrameDisposalMethod(imgIContainer::kDisposeRestorePrevious);
   else if (dispose_op == PNG_DISPOSE_OP_BACKGROUND)
       mFrame->SetFrameDisposalMethod(imgIContainer::kDisposeClear);
   else
       mFrame->SetFrameDisposalMethod(imgIContainer::kDisposeKeep);
+  
+  if (blend_op == PNG_BLEND_OP_SOURCE)
+      mFrame->SetBlendMethod(imgIContainer::kBlendSource);
+  /*else // 'over' is the default for a gfxImageFrame
+      mFrame->SetBlendMethod(imgIContainer::kBlendOver); */
 }
 
 
 /** imgIDecoder methods **/
 
 /* void init (in imgILoad aLoad); */
 NS_IMETHODIMP nsPNGDecoder::Init(imgILoad *aLoad)
 {
--- a/modules/libpr0n/public/imgIContainer.idl
+++ b/modules/libpr0n/public/imgIContainer.idl
@@ -63,16 +63,26 @@ interface imgIContainer : nsISupports
    */
   const long kDisposeClearAll         = -1; // Clear the whole image, revealing
                                             // what was there before the gif displayed
   const long kDisposeNotSpecified     =  0; // Leave frame, let new frame draw on top
   const long kDisposeKeep             =  1; // Leave frame, let new frame draw on top
   const long kDisposeClear            =  2; // Clear the frame's area, revealing bg
   const long kDisposeRestorePrevious  =  3; // Restore the previous (composited) frame
   
+  /*
+   * "Blend" method indicates how the current image is combined with the
+   * previous image.
+   */
+  const long kBlendSource             =  0; // All color components of the frame, including alpha, 
+                                            // overwrite the current contents of the frame's 
+                                            // output buffer region
+  const long kBlendOver               =  1; // The frame should be composited onto the output buffer 
+                                            // based on its alpha, using a simple OVER operation
+  
   /**
    * Create a new \a aWidth x \a aHeight sized image container.
    *
    * @param aWidth The width of the container in which all the
    *               gfxIImageFrame children will fit.
    * @param aHeight The height of the container in which all the
    *                gfxIImageFrame children will fit.
    * @param aObserver Observer to send animation notifications to.
--- a/modules/libpr0n/src/imgContainer.cpp
+++ b/modules/libpr0n/src/imgContainer.cpp
@@ -938,17 +938,17 @@ nsresult imgContainer::DoComposite(gfxII
           mAnim->compositingPrevFrame = nsnull;
       } else {
         ClearFrame(mAnim->compositingFrame);
       }
       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
+  // 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.
     // It would be better if we just stored the area that nextFrame is going to
     // overwrite.
     if (!mAnim->compositingPrevFrame) {
       nsresult rv;
@@ -1084,17 +1084,29 @@ nsresult imgContainer::DrawFrameTo(gfxII
   nsRefPtr<gfxASurface> srcSurf;
   srcImg->GetSurface(getter_AddRefs(srcSurf));
 
   nsCOMPtr<nsIImage> dstImg(do_GetInterface(aDst));
   nsRefPtr<gfxASurface> dstSurf;
   dstImg->GetSurface(getter_AddRefs(dstSurf));
 
   gfxContext dst(dstSurf);
+  
+  // first clear the surface if the blend flag says so
+  PRInt32 blendMethod;
+  aSrc->GetBlendMethod(&blendMethod);
+  gfxContext::GraphicsOperator defaultOperator = dst.CurrentOperator();
+  if (blendMethod == imgIContainer::kBlendSource) {
+    dst.SetOperator(gfxContext::OPERATOR_CLEAR);
+    dst.Rectangle(gfxRect(aDstRect.x, aDstRect.y, aDstRect.width, aDstRect.height));
+    dst.Fill();
+  }
+  
   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);