Bug 1028288 Add canvas global transparency support to svgs. r=roc
authorJames Kolb <jck1089@gmail.com>
Sat, 02 Aug 2014 16:14:53 +0900
changeset 197578 950a3afc2b15226d88209eea5f80fa9bdfe0cc4b
parent 197577 cd4659598fe45b8a55a67c32ff7c0dc159a4a359
child 197579 dfb5303af6f9f16875ee10651fe4cd93bcd6e981
push id27249
push userryanvm@gmail.com
push dateMon, 04 Aug 2014 20:14:35 +0000
treeherdermozilla-central@7f81be7db528 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs1028288
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1028288 Add canvas global transparency support to svgs. r=roc
dom/canvas/CanvasRenderingContext2D.cpp
dom/canvas/CanvasRenderingContext2D.h
gfx/thebes/gfxDrawable.cpp
gfx/thebes/gfxDrawable.h
gfx/thebes/gfxUtils.cpp
gfx/thebes/gfxUtils.h
image/src/VectorImage.cpp
image/src/VectorImage.h
layout/svg/SVGImageContext.h
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -4,16 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CanvasRenderingContext2D.h"
 
 #include "nsXULElement.h"
 
 #include "nsIServiceManager.h"
 #include "nsMathUtils.h"
+#include "SVGPreserveAspectRatio.h"
+#include "SVGImageContext.h"
 
 #include "nsContentUtils.h"
 
 #include "nsIDocument.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "nsSVGEffects.h"
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
@@ -3427,28 +3429,29 @@ CanvasRenderingContext2D::DrawImage(cons
     AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
       DrawSurface(srcSurf,
                   mgfx::Rect(dx, dy, dw, dh),
                   mgfx::Rect(sx, sy, sw, sh),
                   DrawSurfaceOptions(filter),
                   DrawOptions(CurrentState().globalAlpha, UsedOperation()));
   } else {
     DrawDirectlyToCanvas(drawInfo, &bounds, dx, dy, dw, dh,
-                         sx, sy, sw, sh, imgSize);
+                         sx, sy, sw, sh, imgSize, CurrentState().globalAlpha);
   }
 
   RedrawUser(gfxRect(dx, dy, dw, dh));
 }
 
 void
 CanvasRenderingContext2D::DrawDirectlyToCanvas(
                           const nsLayoutUtils::DirectDrawInfo& image,
                           mgfx::Rect* bounds, double dx, double dy,
                           double dw, double dh, double sx, double sy,
-                          double sw, double sh, gfxIntSize imgSize)
+                          double sw, double sh, gfxIntSize imgSize,
+                          gfxFloat opacity)
 {
   gfxMatrix contextMatrix;
 
   AdjustedTarget tempTarget(this, bounds->IsEmpty() ? nullptr: bounds);
 
   // get any already existing transforms on the context. Include transformations used for context shadow
   if (tempTarget) {
     Matrix matrix = tempTarget->GetTransform();
@@ -3464,22 +3467,24 @@ CanvasRenderingContext2D::DrawDirectlyTo
   transformMatrix.Translate(gfxPoint(-dx, -dy));
 
   nsRefPtr<gfxContext> context = new gfxContext(tempTarget);
   context->SetMatrix(contextMatrix);
 
   // FLAG_CLAMP is added for increased performance
   uint32_t modifiedFlags = image.mDrawingFlags | imgIContainer::FLAG_CLAMP;
 
+  SVGImageContext svgContext(SVGPreserveAspectRatio(), opacity);
+
   nsresult rv = image.mImgContainer->
     Draw(context, GraphicsFilter::FILTER_GOOD, transformMatrix,
          gfxRect(gfxPoint(dx, dy), gfxIntSize(dw, dh)),
          nsIntRect(nsIntPoint(0, 0), gfxIntSize(imgSize.width, imgSize.height)),
-         gfxIntSize(imgSize.width, imgSize.height), nullptr, image.mWhichFrame,
-         modifiedFlags);
+         gfxIntSize(imgSize.width, imgSize.height),
+         &svgContext, image.mWhichFrame, modifiedFlags);
 
   NS_ENSURE_SUCCESS_VOID(rv);
 }
 
 static bool
 IsStandardCompositeOp(CompositionOp op)
 {
     return (op == CompositionOp::OP_SOURCE ||
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -662,17 +662,18 @@ protected:
   void DrawImage(const HTMLImageOrCanvasOrVideoElement &imgElt,
                  double sx, double sy, double sw, double sh,
                  double dx, double dy, double dw, double dh,
                  uint8_t optional_argc, mozilla::ErrorResult& error);
 
   void DrawDirectlyToCanvas(const nsLayoutUtils::DirectDrawInfo& image,
                             mozilla::gfx::Rect* bounds, double dx, double dy,
                             double dw, double dh, double sx, double sy,
-                            double sw, double sh, gfxIntSize imgSize);
+                            double sw, double sh, gfxIntSize imgSize,
+                            gfxFloat opacity);
 
   nsString& GetFont()
   {
     /* will initilize the value if not set, else does nothing */
     GetCurrentFontStyle();
 
     return CurrentState().font;
   }
--- a/gfx/thebes/gfxDrawable.cpp
+++ b/gfx/thebes/gfxDrawable.cpp
@@ -26,17 +26,18 @@ gfxSurfaceDrawable::gfxSurfaceDrawable(S
 {
 }
 
 bool
 gfxSurfaceDrawable::Draw(gfxContext* aContext,
                          const gfxRect& aFillRect,
                          bool aRepeat,
                          const GraphicsFilter& aFilter,
-                         const gfxMatrix& aTransform)
+                         const gfxMatrix& aTransform,
+                         gfxFloat aOpacity)
 {
     ExtendMode extend = ExtendMode::CLAMP;
 
     if (aRepeat) {
         extend = ExtendMode::REPEAT;
     }
 
     Matrix patternTransform = ToMatrix(aTransform * mTransform);
@@ -45,27 +46,27 @@ gfxSurfaceDrawable::Draw(gfxContext* aCo
     SurfacePattern pattern(mSourceSurface, extend,
                            patternTransform, ToFilter(aFilter));
 
     Rect fillRect = ToRect(aFillRect);
     DrawTarget* dt = aContext->GetDrawTarget();
 
     if (aContext->CurrentOperator() == gfxContext::OPERATOR_CLEAR) {
         dt->ClearRect(fillRect);
-    } else if (aContext->CurrentOperator() == gfxContext::OPERATOR_SOURCE) {
+    } else if (aContext->CurrentOperator() == gfxContext::OPERATOR_SOURCE && aOpacity == 1.0) {
         // Emulate cairo operator source which is bound by mask!
         dt->ClearRect(fillRect);
         dt->FillRect(fillRect, pattern);
     } else {
         CompositionOp op = CompositionOpForOp(aContext->CurrentOperator());
         AntialiasMode aaMode =
             aContext->CurrentAntialiasMode() == gfxContext::MODE_ALIASED ?
                 AntialiasMode::NONE :
                 AntialiasMode::SUBPIXEL;
-        dt->FillRect(fillRect, pattern, DrawOptions(1.0f, op, aaMode));
+        dt->FillRect(fillRect, pattern, DrawOptions(aOpacity, op, aaMode));
     }
     return true;
 }
 
 gfxCallbackDrawable::gfxCallbackDrawable(gfxDrawingCallback* aCallback,
                                          const gfxIntSize aSize)
  : gfxDrawable(aSize)
  , mCallback(aCallback)
@@ -91,25 +92,26 @@ gfxCallbackDrawable::MakeSurfaceDrawable
     return drawable.forget();
 }
 
 bool
 gfxCallbackDrawable::Draw(gfxContext* aContext,
                           const gfxRect& aFillRect,
                           bool aRepeat,
                           const GraphicsFilter& aFilter,
-                          const gfxMatrix& aTransform)
+                          const gfxMatrix& aTransform,
+                          gfxFloat aOpacity)
 {
-    if (aRepeat && !mSurfaceDrawable) {
+    if ((aRepeat || aOpacity != 1.0) && !mSurfaceDrawable) {
         mSurfaceDrawable = MakeSurfaceDrawable(aFilter);
     }
 
     if (mSurfaceDrawable)
         return mSurfaceDrawable->Draw(aContext, aFillRect, aRepeat, aFilter,
-                                      aTransform);
+                                      aTransform, aOpacity);
 
     if (mCallback)
         return (*mCallback)(aContext, aFillRect, aFilter, aTransform);
 
     return false;
 }
 
 gfxPatternDrawable::gfxPatternDrawable(gfxPattern* aPattern,
@@ -154,35 +156,36 @@ gfxPatternDrawable::MakeCallbackDrawable
     return callbackDrawable.forget();
 }
 
 bool
 gfxPatternDrawable::Draw(gfxContext* aContext,
                          const gfxRect& aFillRect,
                          bool aRepeat,
                          const GraphicsFilter& aFilter,
-                         const gfxMatrix& aTransform)
+                         const gfxMatrix& aTransform,
+                         gfxFloat aOpacity)
 {
     if (!mPattern)
         return false;
 
     if (aRepeat) {
         // We can't use mPattern directly: We want our repeated tiles to have
         // the size mSize, which might not be the case in mPattern.
         // So we need to draw mPattern into a surface of size mSize, create
         // a pattern from the surface and draw that pattern.
         // gfxCallbackDrawable and gfxSurfaceDrawable already know how to do
         // those things, so we use them here. Drawing mPattern into the surface
         // will happen through this Draw() method with aRepeat = false.
         nsRefPtr<gfxCallbackDrawable> callbackDrawable = MakeCallbackDrawable();
         return callbackDrawable->Draw(aContext, aFillRect, true, aFilter,
-                                      aTransform);
+                                      aTransform, aOpacity);
     }
 
     aContext->NewPath();
     gfxMatrix oldMatrix = mPattern->GetMatrix();
     mPattern->SetMatrix(aTransform * oldMatrix);
     aContext->SetPattern(mPattern);
     aContext->Rectangle(aFillRect);
-    aContext->Fill();
+    aContext->FillWithOpacity(aOpacity);
     mPattern->SetMatrix(oldMatrix);
     return true;
 }
--- a/gfx/thebes/gfxDrawable.h
+++ b/gfx/thebes/gfxDrawable.h
@@ -33,17 +33,18 @@ public:
      * draws using a gfxPattern, this is the matrix that should be set on the
      * pattern prior to rendering it.
      *  @return whether drawing was successful
      */
     virtual bool Draw(gfxContext* aContext,
                         const gfxRect& aFillRect,
                         bool aRepeat,
                         const GraphicsFilter& aFilter,
-                        const gfxMatrix& aTransform = gfxMatrix()) = 0;
+                        const gfxMatrix& aTransform = gfxMatrix(),
+                        gfxFloat aOpacity = 1.0) = 0;
     virtual gfxIntSize Size() { return mSize; }
 
 protected:
     // Protected destructor, to discourage deletion outside of Release():
     virtual ~gfxDrawable() {}
 
     const gfxIntSize mSize;
 };
@@ -57,17 +58,18 @@ public:
     gfxSurfaceDrawable(mozilla::gfx::SourceSurface* aSurface, const gfxIntSize aSize,
                        const gfxMatrix aTransform = gfxMatrix());
     virtual ~gfxSurfaceDrawable() {}
 
     virtual bool Draw(gfxContext* aContext,
                         const gfxRect& aFillRect,
                         bool aRepeat,
                         const GraphicsFilter& aFilter,
-                        const gfxMatrix& aTransform = gfxMatrix());
+                        const gfxMatrix& aTransform = gfxMatrix(),
+                        gfxFloat aOpacity = 1.0);
     
 protected:
     mozilla::RefPtr<mozilla::gfx::SourceSurface> mSourceSurface;
     const gfxMatrix mTransform;
 };
 
 /**
  * gfxDrawingCallback
@@ -102,17 +104,18 @@ class gfxCallbackDrawable : public gfxDr
 public:
     gfxCallbackDrawable(gfxDrawingCallback* aCallback, const gfxIntSize aSize);
     virtual ~gfxCallbackDrawable() {}
 
     virtual bool Draw(gfxContext* aContext,
                         const gfxRect& aFillRect,
                         bool aRepeat,
                         const GraphicsFilter& aFilter,
-                        const gfxMatrix& aTransform = gfxMatrix());
+                        const gfxMatrix& aTransform = gfxMatrix(),
+                        gfxFloat aOpacity = 1.0);
 
 protected:
     already_AddRefed<gfxSurfaceDrawable> MakeSurfaceDrawable(const GraphicsFilter aFilter = GraphicsFilter::FILTER_FAST);
 
     nsRefPtr<gfxDrawingCallback> mCallback;
     nsRefPtr<gfxSurfaceDrawable> mSurfaceDrawable;
 };
 
@@ -125,17 +128,18 @@ public:
     gfxPatternDrawable(gfxPattern* aPattern,
                        const gfxIntSize aSize);
     virtual ~gfxPatternDrawable();
 
     virtual bool Draw(gfxContext* aContext,
                         const gfxRect& aFillRect,
                         bool aRepeat,
                         const GraphicsFilter& aFilter,
-                        const gfxMatrix& aTransform = gfxMatrix());
+                        const gfxMatrix& aTransform = gfxMatrix(),
+                        gfxFloat aOpacity = 1.0);
 
 protected:
     already_AddRefed<gfxCallbackDrawable> MakeCallbackDrawable();
 
     nsRefPtr<gfxPattern> mPattern;
 };
 
 #endif /* GFX_DRAWABLE_H */
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -570,17 +570,18 @@ gfxUtils::DrawPixelSnapped(gfxContext*  
                            gfxDrawable*     aDrawable,
                            const gfxMatrix& aUserSpaceToImageSpace,
                            const gfxRect&   aSubimage,
                            const gfxRect&   aSourceRect,
                            const gfxRect&   aImageRect,
                            const gfxRect&   aFill,
                            const SurfaceFormat aFormat,
                            GraphicsFilter aFilter,
-                           uint32_t         aImageFlags)
+                           uint32_t         aImageFlags,
+                           gfxFloat         aOpacity)
 {
     PROFILER_LABEL("gfxUtils", "DrawPixelSnapped",
       js::ProfileEntry::Category::GRAPHICS);
 
     bool doTile = !aImageRect.Contains(aSourceRect) &&
                   !(aImageFlags & imgIContainer::FLAG_CLAMP);
 
     nsRefPtr<gfxASurface> currentTarget = aContext->CurrentSurface();
@@ -620,17 +621,17 @@ gfxUtils::DrawPixelSnapped(gfxContext*  
         }
         // We no longer need to tile: Either we never needed to, or we already
         // filled a surface with the tiled pattern; this surface can now be
         // drawn without tiling.
         doTile = false;
     }
 #endif
 
-    drawable->Draw(aContext, aFill, doTile, aFilter, userSpaceToImageSpace);
+    drawable->Draw(aContext, aFill, doTile, aFilter, userSpaceToImageSpace, aOpacity);
 }
 
 /* static */ int
 gfxUtils::ImageFormatToDepth(gfxImageFormat aFormat)
 {
     switch (aFormat) {
         case gfxImageFormat::ARGB32:
             return 32;
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -75,17 +75,18 @@ public:
                                  gfxDrawable*     aDrawable,
                                  const gfxMatrix& aUserSpaceToImageSpace,
                                  const gfxRect&   aSubimage,
                                  const gfxRect&   aSourceRect,
                                  const gfxRect&   aImageRect,
                                  const gfxRect&   aFill,
                                  const mozilla::gfx::SurfaceFormat aFormat,
                                  GraphicsFilter aFilter,
-                                 uint32_t         aImageFlags = imgIContainer::FLAG_NONE);
+                                 uint32_t         aImageFlags = imgIContainer::FLAG_NONE,
+                                 gfxFloat         aOpacity = 1.0);
 
     /**
      * Clip aContext to the region aRegion.
      */
     static void ClipToRegion(gfxContext* aContext, const nsIntRegion& aRegion);
 
     /**
      * Clip aTarget to the region aRegion.
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -875,28 +875,30 @@ VectorImage::Draw(gfxContext* aContext,
   nsRefPtr<gfxDrawable> drawable;
   if (!(aFlags & FLAG_BYPASS_SURFACE_CACHE)) {
     drawable =
       SurfaceCache::Lookup(ImageKey(this),
                            SurfaceKey(params.imageRect.Size(), params.scale,
                                       aSVGContext, animTime, aFlags));
   }
 
+  gfxFloat opacity = aSVGContext ? aSVGContext->GetGlobalOpacity() : 1.0;
+
   // Draw.
   if (drawable) {
-    Show(drawable, params);
+    Show(drawable, params, opacity);
   } else {
-    CreateDrawableAndShow(params);
+    CreateDrawableAndShow(params, opacity);
   }
 
   return NS_OK;
 }
 
 void
-VectorImage::CreateDrawableAndShow(const SVGDrawingParameters& aParams)
+VectorImage::CreateDrawableAndShow(const SVGDrawingParameters& aParams, gfxFloat aOpacity)
 {
   mSVGDocumentWrapper->UpdateViewportBounds(aParams.viewportSize);
   mSVGDocumentWrapper->FlushImageTransformInvalidation();
 
   nsRefPtr<gfxDrawingCallback> cb =
     new SVGDrawingCallback(mSVGDocumentWrapper,
                            nsIntRect(nsIntPoint(0, 0), aParams.viewportSize),
                            aParams.scale,
@@ -907,27 +909,27 @@ VectorImage::CreateDrawableAndShow(const
 
   bool bypassCache = bool(aParams.flags & FLAG_BYPASS_SURFACE_CACHE) ||
                      // Refuse to cache animated images:
                      // XXX(seth): We may remove this restriction in bug 922893.
                      mHaveAnimations ||
                      // The image is too big to fit in the cache:
                      !SurfaceCache::CanHold(aParams.imageRect.Size());
   if (bypassCache)
-    return Show(svgDrawable, aParams);
+    return Show(svgDrawable, aParams, aOpacity);
 
   // Try to create an offscreen surface.
   RefPtr<gfx::DrawTarget> target =
    gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(aParams.imageRect.Size(), gfx::SurfaceFormat::B8G8R8A8);
 
   // If we couldn't create the draw target, it was probably because it would end
   // up way too big. Generally it also wouldn't fit in the cache, but the prefs
   // could be set such that the cache isn't the limiting factor.
   if (!target)
-    return Show(svgDrawable, aParams);
+    return Show(svgDrawable, aParams, aOpacity);
 
   nsRefPtr<gfxContext> ctx = new gfxContext(target);
 
   // Actually draw. (We use FILTER_NEAREST since we never scale here.)
   gfxUtils::DrawPixelSnapped(ctx, svgDrawable, gfxMatrix(),
                              ThebesIntRect(aParams.imageRect),
                              ThebesIntRect(aParams.imageRect),
                              ThebesIntRect(aParams.imageRect),
@@ -944,30 +946,30 @@ VectorImage::CreateDrawableAndShow(const
                                   aParams.svgContext, aParams.animationTime,
                                   aParams.flags));
 
   // Draw. Note that if SurfaceCache::Insert failed for whatever reason,
   // then |target| is all that is keeping the pixel data alive, so we have
   // to draw before returning from this function.
   nsRefPtr<gfxDrawable> drawable =
     new gfxSurfaceDrawable(surface, ThebesIntSize(aParams.imageRect.Size()));
-  Show(drawable, aParams);
+  Show(drawable, aParams, aOpacity);
 }
 
 
 void
-VectorImage::Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams)
+VectorImage::Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams, gfxFloat aOpacity)
 {
   MOZ_ASSERT(aDrawable, "Should have a gfxDrawable by now");
   gfxUtils::DrawPixelSnapped(aParams.context, aDrawable,
                              aParams.userSpaceToImageSpace,
                              aParams.subimage, aParams.sourceRect,
                              ThebesIntRect(aParams.imageRect), aParams.fill,
                              SurfaceFormat::B8G8R8A8,
-                             aParams.filter, aParams.flags);
+                             aParams.filter, aParams.flags, aOpacity);
 
   MOZ_ASSERT(mRenderingObserver, "Should have a rendering observer by now");
   mRenderingObserver->ResumeHonoringInvalidations();
 }
 
 //******************************************************************************
 /* void requestDecode() */
 NS_IMETHODIMP
--- a/image/src/VectorImage.h
+++ b/image/src/VectorImage.h
@@ -81,18 +81,18 @@ protected:
   VectorImage(imgStatusTracker* aStatusTracker = nullptr,
               ImageURL* aURI = nullptr);
   virtual ~VectorImage();
 
   virtual nsresult StartAnimation();
   virtual nsresult StopAnimation();
   virtual bool     ShouldAnimate();
 
-  void CreateDrawableAndShow(const SVGDrawingParameters& aParams);
-  void Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams);
+  void CreateDrawableAndShow(const SVGDrawingParameters& aParams, gfxFloat aOpacity);
+  void Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams, gfxFloat aOpacity);
 
 private:
   void CancelAllListeners();
   void SendInvalidationNotifications();
 
   nsRefPtr<SVGDocumentWrapper>       mSVGDocumentWrapper;
   nsRefPtr<SVGRootRenderingObserver> mRenderingObserver;
   nsRefPtr<SVGLoadEventListener>     mLoadEventListener;
--- a/layout/svg/SVGImageContext.h
+++ b/layout/svg/SVGImageContext.h
@@ -13,35 +13,41 @@ namespace mozilla {
 // SVG image-specific rendering context. For imgIContainer::Draw.
 // Used to pass information about overridden attributes from an SVG <image>
 // element to the image's internal SVG document when it's drawn.
 class SVGImageContext
 {
 public:
   SVGImageContext() { }
 
-  SVGImageContext(SVGPreserveAspectRatio aPreserveAspectRatio)
-    : mPreserveAspectRatio(aPreserveAspectRatio)
+  SVGImageContext(SVGPreserveAspectRatio aPreserveAspectRatio,
+                  gfxFloat aOpacity = 1.0)
+    : mPreserveAspectRatio(aPreserveAspectRatio), mGlobalOpacity(aOpacity)
   { }
 
   const SVGPreserveAspectRatio& GetPreserveAspectRatio() const {
     return mPreserveAspectRatio;
   }
 
+  gfxFloat GetGlobalOpacity() const {
+    return mGlobalOpacity;
+  }
+
   bool operator==(const SVGImageContext& aOther) const {
     return mPreserveAspectRatio == aOther.mPreserveAspectRatio;
   }
 
   bool operator!=(const SVGImageContext& aOther) const {
     return !(*this == aOther);
   }
 
   uint32_t Hash() const {
     return mPreserveAspectRatio.Hash();
   }
 
 private:
   SVGPreserveAspectRatio mPreserveAspectRatio;
+  gfxFloat mGlobalOpacity;
 };
 
 } // namespace mozilla
 
 #endif // MOZILLA_SVGCONTEXT_H_