Bug 1250037 - part 2 - use DrawTarget::DrawSurfaceWithShadow to render box shadows on platforms that accelerate it. r=mchang
authorLee Salzman <lsalzman@mozilla.com>
Mon, 21 Nov 2016 13:17:09 -0500
changeset 323720 fddfac71bb12f64093846c50f64cb52535ad14fb
parent 323719 9b47a1c822ae434f21d7177045c3e94dca21499b
child 323721 edefce9fdb67ed34c50e2db610ab9b7f60041da0
push id21
push usermaklebus@msu.edu
push dateThu, 01 Dec 2016 06:22:08 +0000
reviewersmchang
bugs1250037
milestone53.0a1
Bug 1250037 - part 2 - use DrawTarget::DrawSurfaceWithShadow to render box shadows on platforms that accelerate it. r=mchang MozReview-Commit-ID: 5MERz8RmGUd
gfx/2d/Blur.cpp
gfx/2d/Blur.h
gfx/2d/DrawTargetCairo.cpp
gfx/2d/DrawTargetD2D1.cpp
gfx/2d/DrawTargetSkia.cpp
gfx/2d/HelpersD2D.h
gfx/thebes/gfxBlur.cpp
gfx/thebes/gfxBlur.h
gfx/thebes/gfxPlatform.cpp
layout/base/nsCSSRendering.cpp
layout/reftests/box-shadow/reftest.list
layout/reftests/bugs/reftest.list
--- a/gfx/2d/Blur.cpp
+++ b/gfx/2d/Blur.cpp
@@ -772,10 +772,16 @@ IntSize
 AlphaBoxBlur::CalculateBlurRadius(const Point& aStd)
 {
     IntSize size(static_cast<int32_t>(floor(aStd.x * GAUSSIAN_SCALE_FACTOR + 0.5f)),
                  static_cast<int32_t>(floor(aStd.y * GAUSSIAN_SCALE_FACTOR + 0.5f)));
 
     return size;
 }
 
+Float
+AlphaBoxBlur::CalculateBlurSigma(int32_t aBlurRadius)
+{
+  return aBlurRadius / GAUSSIAN_SCALE_FACTOR;
+}
+
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/2d/Blur.h
+++ b/gfx/2d/Blur.h
@@ -96,16 +96,26 @@ public:
 
   /**
    * Return a pointer to a dirty rect, as passed in to the constructor, or nullptr
    * if none was passed in.
    */
   Rect* GetDirtyRect();
 
   /**
+   * Return the spread radius, in pixels.
+   */
+  IntSize GetSpreadRadius() const { return mSpreadRadius; }
+
+  /**
+   * Return the blur radius, in pixels.
+   */
+  IntSize GetBlurRadius() const { return mBlurRadius; }
+
+  /**
    * Return the minimum buffer size that should be given to Blur() method.  If
    * zero, the class is not properly setup for blurring.  Note that this
    * includes the extra three bytes on top of the stride*width, where something
    * like gfxImageSurface::GetDataSize() would report without it, even if it 
    * happens to have the extra bytes.
    */
   size_t GetSurfaceAllocationSize() const;
 
@@ -118,16 +128,17 @@ public:
 
   /**
    * Calculates a blur radius that, when used with box blur, approximates a
    * Gaussian blur with the given standard deviation.  The result of this
    * function should be used as the aBlurRadius parameter to AlphaBoxBlur's
    * constructor, above.
    */
   static IntSize CalculateBlurRadius(const Point& aStandardDeviation);
+  static Float CalculateBlurSigma(int32_t aBlurRadius);
 
 private:
 
   void BoxBlur_C(uint8_t* aData,
                  int32_t aLeftLobe, int32_t aRightLobe, int32_t aTopLobe,
                  int32_t aBottomLobe, uint32_t *aIntegralImage, size_t aIntegralImageStride);
   void BoxBlur_SSE2(uint8_t* aData,
                     int32_t aLeftLobe, int32_t aRightLobe, int32_t aTopLobe,
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -926,55 +926,56 @@ DrawTargetCairo::DrawSurfaceWithShadow(S
   cairo_surface_t* blursurf;
   cairo_surface_t* surf;
 
   // We only use the A8 surface for blurred shadows. Unblurred shadows can just
   // use the RGBA surface directly.
   if (cairo_surface_get_type(sourcesurf) == CAIRO_SURFACE_TYPE_TEE) {
     blursurf = cairo_tee_surface_index(sourcesurf, 0);
     surf = cairo_tee_surface_index(sourcesurf, 1);
+  } else {
+    blursurf = sourcesurf;
+    surf = sourcesurf;
+  }
 
+  if (aSigma != 0.0f) {
     MOZ_ASSERT(cairo_surface_get_type(blursurf) == CAIRO_SURFACE_TYPE_IMAGE);
     Rect extents(0, 0, width, height);
     AlphaBoxBlur blur(extents,
                       cairo_image_surface_get_stride(blursurf),
                       aSigma, aSigma);
     blur.Blur(cairo_image_surface_get_data(blursurf));
-  } else {
-    blursurf = sourcesurf;
-    surf = sourcesurf;
   }
 
   WillChange();
   ClearSurfaceForUnboundedSource(aOperator);
 
   cairo_save(mContext);
   cairo_set_operator(mContext, GfxOpToCairoOp(aOperator));
   cairo_identity_matrix(mContext);
   cairo_translate(mContext, aDest.x, aDest.y);
 
-  if (IsOperatorBoundByMask(aOperator)){
-    cairo_set_source_rgba(mContext, aColor.r, aColor.g, aColor.b, aColor.a);
-    cairo_mask_surface(mContext, blursurf, aOffset.x, aOffset.y);
+  bool needsGroup = !IsOperatorBoundByMask(aOperator);
+  if (needsGroup) {
+    cairo_push_group(mContext);
+  }
 
+  cairo_set_source_rgba(mContext, aColor.r, aColor.g, aColor.b, aColor.a);
+  cairo_mask_surface(mContext, blursurf, aOffset.x, aOffset.y);
+
+  if (blursurf != surf ||
+      aSurface->GetFormat() != SurfaceFormat::A8) {
     // Now that the shadow has been drawn, we can draw the surface on top.
     cairo_set_source_surface(mContext, surf, 0, 0);
     cairo_new_path(mContext);
     cairo_rectangle(mContext, 0, 0, width, height);
     cairo_fill(mContext);
-  } else {
-    cairo_push_group(mContext);
-      cairo_set_source_rgba(mContext, aColor.r, aColor.g, aColor.b, aColor.a);
-      cairo_mask_surface(mContext, blursurf, aOffset.x, aOffset.y);
+  }
 
-      // Now that the shadow has been drawn, we can draw the surface on top.
-      cairo_set_source_surface(mContext, surf, 0, 0);
-      cairo_new_path(mContext);
-      cairo_rectangle(mContext, 0, 0, width, height);
-      cairo_fill(mContext);
+  if (needsGroup) {
     cairo_pop_group_to_source(mContext);
     cairo_paint(mContext);
   }
 
   cairo_restore(mContext);
 }
 
 void
@@ -1919,17 +1920,17 @@ DrawTargetCairo::CreateShadowDrawTarget(
                                                           aSize.width, aSize.height);
 
   if (cairo_surface_status(similar)) {
     return nullptr;
   }
 
   // If we don't have a blur then we can use the RGBA mask and keep all the
   // operations in graphics memory.
-  if (aSigma == 0.0F) {
+  if (aSigma == 0.0f || aFormat == SurfaceFormat::A8) {
     RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
     if (target->InitAlreadyReferenced(similar, aSize)) {
       return target.forget();
     } else {
       return nullptr;
     }
   }
 
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -238,31 +238,39 @@ DrawTargetD2D1::DrawSurfaceWithShadow(So
 
   if (!mat.IsIdentity()) {
     gfxDebug() << *this << ": At this point complex partial uploads are not supported for Shadow surfaces.";
     return;
   }
 
   // Step 1, create the shadow effect.
   RefPtr<ID2D1Effect> shadowEffect;
-  HRESULT hr = mDC->CreateEffect(CLSID_D2D1Shadow, getter_AddRefs(shadowEffect));
+  HRESULT hr = mDC->CreateEffect(mFormat == SurfaceFormat::A8 ? CLSID_D2D1GaussianBlur : CLSID_D2D1Shadow,
+                                 getter_AddRefs(shadowEffect));
   if (FAILED(hr) || !shadowEffect) {
     gfxWarning() << "Failed to create shadow effect. Code: " << hexa(hr);
     return;
   }
   shadowEffect->SetInput(0, image);
-  shadowEffect->SetValue(D2D1_SHADOW_PROP_BLUR_STANDARD_DEVIATION, aSigma);
-  D2D1_VECTOR_4F color = { aColor.r, aColor.g, aColor.b, aColor.a };
-  shadowEffect->SetValue(D2D1_SHADOW_PROP_COLOR, color);
+  if (mFormat == SurfaceFormat::A8) {
+    shadowEffect->SetValue(D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION, aSigma);
+    shadowEffect->SetValue(D2D1_GAUSSIANBLUR_PROP_BORDER_MODE, D2D1_BORDER_MODE_HARD);
+  } else {
+    shadowEffect->SetValue(D2D1_SHADOW_PROP_BLUR_STANDARD_DEVIATION, aSigma);
+    D2D1_VECTOR_4F color = { aColor.r, aColor.g, aColor.b, aColor.a };
+    shadowEffect->SetValue(D2D1_SHADOW_PROP_COLOR, color);
+  }
 
   D2D1_POINT_2F shadowPoint = D2DPoint(aDest + aOffset);
   mDC->DrawImage(shadowEffect, &shadowPoint, nullptr, D2D1_INTERPOLATION_MODE_LINEAR, D2DCompositionMode(aOperator));
 
-  D2D1_POINT_2F imgPoint = D2DPoint(aDest);
-  mDC->DrawImage(image, &imgPoint, nullptr, D2D1_INTERPOLATION_MODE_LINEAR, D2DCompositionMode(aOperator));
+  if (aSurface->GetFormat() != SurfaceFormat::A8) {
+    D2D1_POINT_2F imgPoint = D2DPoint(aDest);
+    mDC->DrawImage(image, &imgPoint, nullptr, D2D1_INTERPOLATION_MODE_LINEAR, D2DCompositionMode(aOperator));
+  }
 }
 
 void
 DrawTargetD2D1::ClearRect(const Rect &aRect)
 {
   MarkChanged();
 
   PopAllClips();
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -700,19 +700,21 @@ DrawTargetSkia::DrawSurfaceWithShadow(So
       SkColorFilter::MakeModeFilter(ColorToSkColor(aColor, 1.0f), SkBlendMode::kSrcIn));
 
     shadowPaint.setImageFilter(blurFilter);
     shadowPaint.setColorFilter(colorFilter);
 
     mCanvas->drawImage(image, shadowDest.x, shadowDest.y, &shadowPaint);
   }
 
-  // Composite the original image after the shadow
-  auto dest = IntPoint::Round(aDest);
-  mCanvas->drawImage(image, dest.x, dest.y, &paint);
+  if (aSurface->GetFormat() != SurfaceFormat::A8) {
+    // Composite the original image after the shadow
+    auto dest = IntPoint::Round(aDest);
+    mCanvas->drawImage(image, dest.x, dest.y, &paint);
+  }
 
   mCanvas->restore();
 }
 
 void
 DrawTargetSkia::FillRect(const Rect &aRect,
                          const Pattern &aPattern,
                          const DrawOptions &aOptions)
--- a/gfx/2d/HelpersD2D.h
+++ b/gfx/2d/HelpersD2D.h
@@ -590,16 +590,18 @@ CreatePartialBitmapForSurface(DataSource
   //   |               |rect          |               |
   //   |          .---------.         .----.     .----.          .---------------.
   //   |          |         |  ---->  |    |     |    |   ---->  |               |
   //   |          '---------'         '----'     '----'          '---------------'
   //   '---------------'              '---------------'
   //
   //
 
+  int Bpp = BytesPerPixel(aSurface->GetFormat());
+
   if (uploadRect.Contains(rect)) {
     // Extend mode is irrelevant, the displayed rect is completely contained
     // by the source bitmap.
     uploadRect = rect;
   } else if (aExtendMode == ExtendMode::CLAMP && uploadRect.Intersects(rect)) {
     // Calculate the rectangle on the source bitmap that touches our
     // surface, and upload that, for ExtendMode::CLAMP we can actually guarantee
     // correct behaviour in this case.
@@ -621,28 +623,26 @@ CreatePartialBitmapForSurface(DataSource
       // Scope to auto-Unmap() |mapping|.
       DataSourceSurface::ScopedMap mapping(aSurface, DataSourceSurface::READ);
       if (MOZ2D_WARN_IF(!mapping.IsMapped())) {
         return nullptr;
       }
 
       // A partial upload will suffice.
       aRT->CreateBitmap(D2D1::SizeU(uint32_t(uploadRect.width), uint32_t(uploadRect.height)),
-                        mapping.GetData() + int(uploadRect.x) * 4 + int(uploadRect.y) * mapping.GetStride(),
+                        mapping.GetData() + int(uploadRect.x) * Bpp + int(uploadRect.y) * mapping.GetStride(),
                         mapping.GetStride(),
                         D2D1::BitmapProperties(D2DPixelFormat(aSurface->GetFormat())),
                         getter_AddRefs(bitmap));
     }
 
     aSourceTransform.PreTranslate(uploadRect.x, uploadRect.y);
 
     return bitmap.forget();
   } else {
-    int Bpp = BytesPerPixel(aSurface->GetFormat());
-
     if (Bpp != 4) {
       // This shouldn't actually happen in practice!
       MOZ_ASSERT(false);
       return nullptr;
     }
 
     {
       // Scope to auto-Unmap() |mapping|.
--- a/gfx/thebes/gfxBlur.cpp
+++ b/gfx/thebes/gfxBlur.cpp
@@ -15,146 +15,194 @@
 #include "nsExpirationTracker.h"
 #include "nsClassHashtable.h"
 #include "gfxUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
 gfxAlphaBoxBlur::gfxAlphaBoxBlur()
-  : mData(nullptr)
+  : mData(nullptr),
+    mAccelerated(false)
 {
 }
 
 gfxAlphaBoxBlur::~gfxAlphaBoxBlur()
 {
   if (mData) {
     free(mData);
   }
 }
 
 already_AddRefed<gfxContext>
-gfxAlphaBoxBlur::Init(const gfxRect& aRect,
+gfxAlphaBoxBlur::Init(gfxContext* aDestinationCtx,
+                      const gfxRect& aRect,
                       const IntSize& aSpreadRadius,
                       const IntSize& aBlurRadius,
                       const gfxRect* aDirtyRect,
                       const gfxRect* aSkipRect)
 {
+  DrawTarget* refDT = aDestinationCtx->GetDrawTarget();
   Maybe<Rect> dirtyRect = aDirtyRect ? Some(ToRect(*aDirtyRect)) : Nothing();
   Maybe<Rect> skipRect = aSkipRect ? Some(ToRect(*aSkipRect)) : Nothing();
   RefPtr<DrawTarget> dt =
-    InitDrawTarget(ToRect(aRect), aSpreadRadius, aBlurRadius,
+    InitDrawTarget(refDT, ToRect(aRect), aSpreadRadius, aBlurRadius,
                    dirtyRect.ptrOr(nullptr), skipRect.ptrOr(nullptr));
   if (!dt) {
     return nullptr;
   }
 
   RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt);
   MOZ_ASSERT(context); // already checked for target above
   context->SetMatrix(gfxMatrix::Translation(-mBlur.GetRect().TopLeft()));
   return context.forget();
 }
 
 already_AddRefed<DrawTarget>
-gfxAlphaBoxBlur::InitDrawTarget(const Rect& aRect,
+gfxAlphaBoxBlur::InitDrawTarget(const DrawTarget* aReferenceDT,
+                                const Rect& aRect,
                                 const IntSize& aSpreadRadius,
                                 const IntSize& aBlurRadius,
                                 const Rect* aDirtyRect,
                                 const Rect* aSkipRect)
 {
   mBlur.Init(aRect, aSpreadRadius, aBlurRadius, aDirtyRect, aSkipRect);
   size_t blurDataSize = mBlur.GetSurfaceAllocationSize();
   if (blurDataSize == 0) {
     return nullptr;
   }
 
-  // Make an alpha-only surface to draw on. We will play with the data after
-  // everything is drawn to create a blur effect.
-  mData = static_cast<uint8_t*>(calloc(1, blurDataSize));
-  if (!mData) {
-    return nullptr;
+  BackendType backend = aReferenceDT->GetBackendType();
+
+  // Check if the backend has an accelerated DrawSurfaceWithShadow.
+  // Currently, only D2D1.1 supports this.
+  // Otherwise, DrawSurfaceWithShadow only supports square blurs without spread.
+  if (aBlurRadius.IsSquare() && aSpreadRadius.IsEmpty() &&
+      backend == BackendType::DIRECT2D1_1) {
+    mAccelerated = true;
+    mDrawTarget =
+      aReferenceDT->CreateShadowDrawTarget(mBlur.GetSize(),
+                                           SurfaceFormat::A8,
+                                           AlphaBoxBlur::CalculateBlurSigma(aBlurRadius.width));
+  } else {
+    // Make an alpha-only surface to draw on. We will play with the data after
+    // everything is drawn to create a blur effect.
+    mData = static_cast<uint8_t*>(calloc(1, blurDataSize));
+    if (!mData) {
+      return nullptr;
+    }
+    mDrawTarget =
+      Factory::DoesBackendSupportDataDrawtarget(backend) ?
+        Factory::CreateDrawTargetForData(backend,
+                                         mData,
+                                         mBlur.GetSize(),
+                                         mBlur.GetStride(),
+                                         SurfaceFormat::A8) :
+        gfxPlatform::CreateDrawTargetForData(mData,
+                                             mBlur.GetSize(),
+                                             mBlur.GetStride(),
+                                             SurfaceFormat::A8);
   }
 
-  RefPtr<DrawTarget> dt =
-    gfxPlatform::CreateDrawTargetForData(mData,
-                                         mBlur.GetSize(),
-                                         mBlur.GetStride(),
-                                         SurfaceFormat::A8);
-  if (!dt || !dt->IsValid()) {
+  if (!mDrawTarget || !mDrawTarget->IsValid()) {
     return nullptr;
   }
-  dt->SetTransform(Matrix::Translation(-mBlur.GetRect().TopLeft()));
-  return dt.forget();
-}
-
-void
-DrawBlur(gfxContext* aDestinationCtx,
-         SourceSurface* aBlur,
-         const IntPoint& aTopLeft,
-         const Rect* aDirtyRect)
-{
-    DrawTarget *dest = aDestinationCtx->GetDrawTarget();
-
-    RefPtr<gfxPattern> thebesPat = aDestinationCtx->GetPattern();
-    Pattern* pat = thebesPat->GetPattern(dest, nullptr);
-
-    Matrix oldTransform = dest->GetTransform();
-    Matrix newTransform = oldTransform;
-    newTransform.PreTranslate(aTopLeft.x, aTopLeft.y);
-
-    // Avoid a semi-expensive clip operation if we can, otherwise
-    // clip to the dirty rect
-    if (aDirtyRect) {
-        dest->PushClipRect(*aDirtyRect);
-    }
-
-    dest->SetTransform(newTransform);
-    dest->MaskSurface(*pat, aBlur, Point(0, 0));
-    dest->SetTransform(oldTransform);
-
-    if (aDirtyRect) {
-        dest->PopClip();
-    }
+  mDrawTarget->SetTransform(Matrix::Translation(-mBlur.GetRect().TopLeft()));
+  return do_AddRef(mDrawTarget);
 }
 
 already_AddRefed<SourceSurface>
-gfxAlphaBoxBlur::DoBlur(DrawTarget* aDT, IntPoint* aTopLeft)
+gfxAlphaBoxBlur::DoBlur(const Color* aShadowColor, IntPoint* aOutTopLeft)
 {
+  if (mData) {
     mBlur.Blur(mData);
+  }
+
+  if (aOutTopLeft) {
+    *aOutTopLeft = mBlur.GetRect().TopLeft();
+  }
 
-    *aTopLeft = mBlur.GetRect().TopLeft();
+  RefPtr<SourceSurface> blurMask = mDrawTarget->Snapshot();
+  if (mAccelerated) {
+    RefPtr<DrawTarget> blurDT =
+      Factory::CreateDrawTarget(mDrawTarget->GetBackendType(),
+                                blurMask->GetSize(),
+                                SurfaceFormat::A8);
+    if (!blurDT) {
+      return nullptr;
+    }
+    blurDT->DrawSurfaceWithShadow(blurMask, Point(0, 0), Color(1, 1, 1), Point(0, 0),
+                                  AlphaBoxBlur::CalculateBlurSigma(mBlur.GetBlurRadius().width),
+                                  CompositionOp::OP_OVER);
+    blurMask = blurDT->Snapshot();
+  }
 
-    return aDT->CreateSourceSurfaceFromData(mData,
-                                            mBlur.GetSize(),
-                                            mBlur.GetStride(),
-                                            SurfaceFormat::A8);
+  if (!aShadowColor) {
+    return blurMask.forget();
+  }
+
+  RefPtr<DrawTarget> shadowDT =
+    Factory::CreateDrawTarget(mDrawTarget->GetBackendType(),
+                              blurMask->GetSize(),
+                              SurfaceFormat::B8G8R8A8);
+  if (!shadowDT) {
+    return nullptr;
+  }
+  ColorPattern shadowColor(ToDeviceColor(*aShadowColor));
+  shadowDT->MaskSurface(shadowColor, blurMask, Point(0, 0));
+
+  return shadowDT->Snapshot();
 }
 
 void
 gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx)
 {
-    if (!mData)
-        return;
+  if (!mAccelerated && !mData) {
+    return;
+  }
 
-    DrawTarget *dest = aDestinationCtx->GetDrawTarget();
-    if (!dest) {
-      NS_WARNING("Blurring not supported for Thebes contexts!");
-      return;
-    }
+  DrawTarget *dest = aDestinationCtx->GetDrawTarget();
+  if (!dest) {
+    NS_WARNING("Blurring not supported for Thebes contexts!");
+    return;
+  }
+
+  RefPtr<gfxPattern> thebesPat = aDestinationCtx->GetPattern();
+  Pattern* pat = thebesPat->GetPattern(dest, nullptr);
+  if (!pat) {
+    NS_WARNING("Failed to get pattern for blur!");
+    return;
+  }
 
-    Rect* dirtyRect = mBlur.GetDirtyRect();
+  IntPoint topLeft;
+  RefPtr<SourceSurface> mask = DoBlur(nullptr, &topLeft);
+  if (!mask) {
+    NS_ERROR("Failed to create mask!");
+    return;
+  }
 
-    IntPoint topLeft;
-    RefPtr<SourceSurface> mask = DoBlur(dest, &topLeft);
-    if (!mask) {
-      NS_ERROR("Failed to create mask!");
-      return;
-    }
+  // Avoid a semi-expensive clip operation if we can, otherwise
+  // clip to the dirty rect
+  Rect* dirtyRect = mBlur.GetDirtyRect();
+  if (dirtyRect) {
+    dest->PushClipRect(*dirtyRect);
+  }
 
-    DrawBlur(aDestinationCtx, mask, topLeft, dirtyRect);
+  Matrix oldTransform = dest->GetTransform();
+  Matrix newTransform = oldTransform;
+  newTransform.PreTranslate(topLeft);
+  dest->SetTransform(newTransform);
+
+  dest->MaskSurface(*pat, mask, Point(0, 0));
+
+  dest->SetTransform(oldTransform);
+
+  if (dirtyRect) {
+    dest->PopClip();
+  }
 }
 
 IntSize gfxAlphaBoxBlur::CalculateBlurRadius(const gfxPoint& aStd)
 {
     mozilla::gfx::Point std(Float(aStd.x), Float(aStd.y));
     IntSize size = AlphaBoxBlur::CalculateBlurRadius(std);
     return IntSize(size.width, size.height);
 }
@@ -428,82 +476,67 @@ CacheBlur(DrawTarget* aDT,
 {
   BlurCacheKey key(aMinSize, aBlurRadius, aCornerRadii, aShadowColor, aDT->GetBackendType());
   BlurCacheData* data = new BlurCacheData(aBoxShadow, aBlurMargin, key);
   if (!gBlurCache->RegisterEntry(data)) {
     delete data;
   }
 }
 
-// Blurs a small surface and creates the mask.
+// Blurs a small surface and creates the colored box shadow.
 static already_AddRefed<SourceSurface>
-CreateBlurMask(DrawTarget* aDestDrawTarget,
-               const IntSize& aMinSize,
-               const RectCornerRadii* aCornerRadii,
-               const IntSize& aBlurRadius,
-               bool aMirrorCorners,
-               IntMargin& aOutBlurMargin)
+CreateBoxShadow(DrawTarget* aDestDrawTarget,
+                const IntSize& aMinSize,
+                const RectCornerRadii* aCornerRadii,
+                const IntSize& aBlurRadius,
+                const Color& aShadowColor,
+                bool aMirrorCorners,
+                IntMargin& aOutBlurMargin)
 {
   gfxAlphaBoxBlur blur;
   Rect minRect(Point(0, 0), Size(aMinSize));
   Rect blurRect(minRect);
   // If mirroring corners, we only need to draw the top-left quadrant.
   // Use ceil to preserve the remaining 1x1 middle area for minimized box
   // shadows.
   if (aMirrorCorners) {
     blurRect.SizeTo(ceil(blurRect.width * 0.5f), ceil(blurRect.height * 0.5f));
   }
   IntSize zeroSpread(0, 0);
   RefPtr<DrawTarget> blurDT =
-    blur.InitDrawTarget(blurRect, zeroSpread, aBlurRadius);
+    blur.InitDrawTarget(aDestDrawTarget, blurRect, zeroSpread, aBlurRadius);
   if (!blurDT) {
     return nullptr;
   }
 
   ColorPattern black(Color(0.f, 0.f, 0.f, 1.f));
 
   if (aCornerRadii) {
     RefPtr<Path> roundedRect =
       MakePathForRoundedRect(*blurDT, minRect, *aCornerRadii);
     blurDT->Fill(roundedRect, black);
   } else {
     blurDT->FillRect(minRect, black);
   }
 
   IntPoint topLeft;
-  RefPtr<SourceSurface> result = blur.DoBlur(aDestDrawTarget, &topLeft);
+  RefPtr<SourceSurface> result = blur.DoBlur(&aShadowColor, &topLeft);
   if (!result) {
     return nullptr;
   }
 
   // Since blurRect is at (0, 0), we can find the inflated margin by
   // negating the new rect origin, which would have been negative if
   // the rect was inflated.
   aOutBlurMargin = IntMargin(-topLeft.y, -topLeft.x, -topLeft.y, -topLeft.x);
 
   return result.forget();
 }
 
 static already_AddRefed<SourceSurface>
-CreateBoxShadow(DrawTarget* aDestDT, SourceSurface* aBlurMask, const Color& aShadowColor)
-{
-  IntSize blurredSize = aBlurMask->GetSize();
-  RefPtr<DrawTarget> boxShadowDT =
-    Factory::CreateDrawTarget(aDestDT->GetBackendType(), blurredSize, SurfaceFormat::B8G8R8A8);
-
-  if (!boxShadowDT) {
-    return nullptr;
-  }
-
-  ColorPattern shadowColor(ToDeviceColor(aShadowColor));
-  boxShadowDT->MaskSurface(shadowColor, aBlurMask, Point(0, 0));
-  return boxShadowDT->Snapshot();
-}
-
-static already_AddRefed<SourceSurface>
 GetBlur(gfxContext* aDestinationCtx,
         const IntSize& aRectSize,
         const IntSize& aBlurRadius,
         const RectCornerRadii* aCornerRadii,
         const Color& aShadowColor,
         bool aMirrorCorners,
         IntMargin& aOutBlurMargin,
         IntMargin& aOutSlice,
@@ -528,31 +561,26 @@ GetBlur(gfxContext* aDestinationCtx,
 
   DrawTarget* destDT = aDestinationCtx->GetDrawTarget();
 
   if (!useDestRect) {
     BlurCacheData* cached = gBlurCache->Lookup(minSize, aBlurRadius,
                                                aCornerRadii, aShadowColor,
                                                destDT->GetBackendType());
     if (cached) {
-      // See CreateBlurMask() for these values
+      // See CreateBoxShadow() for these values
       aOutBlurMargin = cached->mBlurMargin;
       RefPtr<SourceSurface> blur = cached->mBlur;
       return blur.forget();
     }
   }
 
-  RefPtr<SourceSurface> blurMask =
-    CreateBlurMask(destDT, minSize, aCornerRadii, aBlurRadius,
-                   aMirrorCorners, aOutBlurMargin);
-  if (!blurMask) {
-    return nullptr;
-  }
-
-  RefPtr<SourceSurface> boxShadow = CreateBoxShadow(destDT, blurMask, aShadowColor);
+  RefPtr<SourceSurface> boxShadow =
+    CreateBoxShadow(destDT, minSize, aCornerRadii, aBlurRadius,
+                    aShadowColor, aMirrorCorners, aOutBlurMargin);
   if (!boxShadow) {
     return nullptr;
   }
 
   if (!useDestRect) {
     CacheBlur(destDT, minSize, aBlurRadius, aCornerRadii, aShadowColor,
               aOutBlurMargin, boxShadow);
   }
@@ -1059,17 +1087,17 @@ gfxAlphaBoxBlur::GetInsetBlur(const Rect
   // If mirroring corners, we only need to draw the top-left quadrant.
   // Use ceil to preserve the remaining 1x1 middle area for minimized box
   // shadows.
   if (aMirrorCorners) {
     blurRect.SizeTo(ceil(blurRect.width * 0.5f), ceil(blurRect.height * 0.5f));
   }
   IntSize zeroSpread(0, 0);
   RefPtr<DrawTarget> minDrawTarget =
-    InitDrawTarget(blurRect, zeroSpread, aBlurRadius);
+    InitDrawTarget(aDestDrawTarget, blurRect, zeroSpread, aBlurRadius);
   if (!minDrawTarget) {
     return nullptr;
   }
 
   // This is really annoying. When we create the AlphaBoxBlur, the DrawTarget
   // has a translation applied to it that is the topLeft point. This is actually
   // the rect we gave it plus the blur radius. The rects we give this for the outer
   // and whitespace rects are based at (0, 0). We could either translate those rects
@@ -1083,25 +1111,18 @@ gfxAlphaBoxBlur::GetInsetBlur(const Rect
   // NOT the inner frame
   RefPtr<Path> maskPath =
     GetBoxShadowInsetPath(minDrawTarget, aOuterRect,
                           aWhitespaceRect, aInnerClipRadii);
 
   ColorPattern black(Color(0.f, 0.f, 0.f, 1.f));
   minDrawTarget->Fill(maskPath, black);
 
-  // Create the A8 mask
-  IntPoint topLeft;
-  RefPtr<SourceSurface> minMask = DoBlur(minDrawTarget, &topLeft);
-  if (!minMask) {
-    return nullptr;
-  }
-
-  // Fill in with the color we actually wanted
-  RefPtr<SourceSurface> minInsetBlur = CreateBoxShadow(aDestDrawTarget, minMask, aShadowColor);
+  // Blur and fill in with the color we actually wanted
+  RefPtr<SourceSurface> minInsetBlur = DoBlur(&aShadowColor);
   if (!minInsetBlur) {
     return nullptr;
   }
 
   if (!aIsDestRect) {
     CacheInsetBlur(outerSize, whitespaceSize,
                    aBlurRadius, aInnerClipRadii,
                    aShadowColor, aDestDrawTarget->GetBackendType(),
--- a/gfx/thebes/gfxBlur.h
+++ b/gfx/thebes/gfxBlur.h
@@ -51,16 +51,19 @@ class gfxAlphaBoxBlur
 
 public:
     gfxAlphaBoxBlur();
 
     ~gfxAlphaBoxBlur();
 
     /**
      * Constructs a box blur and initializes the temporary surface.
+     *
+     * @param aDestinationCtx The destination to blur to.
+     *
      * @param aRect The coordinates of the surface to create in device units.
      *
      * @param aBlurRadius The blur radius in pixels.  This is the radius of
      *   the entire (triple) kernel function.  Each individual box blur has
      *   radius approximately 1/3 this value, or diameter approximately 2/3
      *   this value.  This parameter should nearly always be computed using
      *   CalculateBlurRadius, below.
      *
@@ -68,31 +71,37 @@ public:
      *  if available. This will be used for optimizing the blur operation. It
      *  is safe to pass nullptr here.
      *
      * @param aSkipRect A pointer to a rect, measured in device units, that
      *  represents an area where blurring is unnecessary and shouldn't be done
      *  for speed reasons. It is safe to pass nullptr here.
      */
     already_AddRefed<gfxContext>
-    Init(const gfxRect& aRect,
+    Init(gfxContext* aDestinationCtx,
+         const gfxRect& aRect,
          const mozilla::gfx::IntSize& aSpreadRadius,
          const mozilla::gfx::IntSize& aBlurRadius,
          const gfxRect* aDirtyRect,
          const gfxRect* aSkipRect);
 
     already_AddRefed<DrawTarget>
-    InitDrawTarget(const mozilla::gfx::Rect& aRect,
+    InitDrawTarget(const mozilla::gfx::DrawTarget* aReferenceDT,
+                   const mozilla::gfx::Rect& aRect,
                    const mozilla::gfx::IntSize& aSpreadRadius,
                    const mozilla::gfx::IntSize& aBlurRadius,
                    const mozilla::gfx::Rect* aDirtyRect = nullptr,
                    const mozilla::gfx::Rect* aSkipRect = nullptr);
 
+    /**
+     * Performs the blur and optionally colors the result if aShadowColor is not null.
+     */
     already_AddRefed<mozilla::gfx::SourceSurface>
-    DoBlur(DrawTarget* aDT, mozilla::gfx::IntPoint* aTopLeft);
+    DoBlur(const mozilla::gfx::Color* aShadowColor = nullptr,
+           mozilla::gfx::IntPoint* aOutTopLeft = nullptr);
 
     /**
      * Does the actual blurring/spreading and mask applying. Users of this
      * object must have drawn whatever they want to be blurred onto the internal
      * gfxContext returned by GetContext before calling this.
      *
      * @param aDestinationCtx The graphics context on which to apply the
      *  blurred mask.
@@ -163,20 +172,31 @@ protected:
                  const mozilla::gfx::Rect& aWhitespaceRect,
                  bool aIsDestRect,
                  const mozilla::gfx::Color& aShadowColor,
                  const mozilla::gfx::IntSize& aBlurRadius,
                  const RectCornerRadii* aInnerClipRadii,
                  DrawTarget* aDestDrawTarget,
                  bool aMirrorCorners);
 
+
+    /**
+     * The DrawTarget of the temporary alpha surface.
+     */
+    RefPtr<DrawTarget> mDrawTarget;
+
     /**
      * The temporary alpha surface.
      */
     uint8_t* mData;
 
      /**
       * The object that actually does the blurring for us.
       */
     mozilla::gfx::AlphaBoxBlur mBlur;
+
+    /**
+     * Indicates using DrawTarget-accelerated blurs.
+     */
+    bool mAccelerated;
 };
 
 #endif /* GFX_BLUR_H */
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -1452,30 +1452,39 @@ gfxPlatform::CreateSimilarSoftwareDrawTa
                                              const IntSize& aSize,
                                              SurfaceFormat aFormat)
 {
   RefPtr<DrawTarget> dt;
 
   if (Factory::DoesBackendSupportDataDrawtarget(aDT->GetBackendType())) {
     dt = aDT->CreateSimilarDrawTarget(aSize, aFormat);
   } else {
-    dt = Factory::CreateDrawTarget(BackendType::SKIA, aSize, aFormat);
+#ifdef USE_SKIA
+    BackendType backendType = BackendType::SKIA;
+#else
+    BackendType backendType = BackendType::CAIRO;
+#endif
+    dt = Factory::CreateDrawTarget(backendType, aSize, aFormat);
   }
 
   return dt.forget();
 }
 
 /* static */ already_AddRefed<DrawTarget>
 gfxPlatform::CreateDrawTargetForData(unsigned char* aData, const IntSize& aSize, int32_t aStride, SurfaceFormat aFormat)
 {
   BackendType backendType = gfxVars::ContentBackend();
   NS_ASSERTION(backendType != BackendType::NONE, "No backend.");
 
   if (!Factory::DoesBackendSupportDataDrawtarget(backendType)) {
+#ifdef USE_SKIA
+    backendType = BackendType::SKIA;
+#else
     backendType = BackendType::CAIRO;
+#endif
   }
 
   RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(backendType,
                                                            aData, aSize,
                                                            aStride, aFormat);
 
   return dt.forget();
 }
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -5904,20 +5904,20 @@ nsContextBoxBlur::Init(const nsRect& aRe
   rect = transform.TransformBounds(rect);
 
   mPreTransformed = !transform.IsIdentity();
 
   // Create the temporary surface for blurring
   dirtyRect = transform.TransformBounds(dirtyRect);
   if (aSkipRect) {
     gfxRect skipRect = transform.TransformBounds(*aSkipRect);
-    mContext = mAlphaBoxBlur.Init(rect, spreadRadius,
+    mContext = mAlphaBoxBlur.Init(aDestinationCtx, rect, spreadRadius,
                                   blurRadius, &dirtyRect, &skipRect);
   } else {
-    mContext = mAlphaBoxBlur.Init(rect, spreadRadius,
+    mContext = mAlphaBoxBlur.Init(aDestinationCtx, rect, spreadRadius,
                                   blurRadius, &dirtyRect, nullptr);
   }
 
   if (mContext) {
     // we don't need to blur if skipRect is equal to rect
     // and mContext will be nullptr
     mContext->Multiply(transform);
   }
--- a/layout/reftests/box-shadow/reftest.list
+++ b/layout/reftests/box-shadow/reftest.list
@@ -17,25 +17,25 @@ random-if(d2d) == boxshadow-rounded-spre
 fuzzy-if(skiaContent,1,50) HTTP(..) == boxshadow-dynamic.xul boxshadow-dynamic-ref.xul
 random-if(d2d) == boxshadow-onecorner.html boxshadow-onecorner-ref.html
 random-if(d2d) == boxshadow-twocorners.html boxshadow-twocorners-ref.html
 random-if(d2d) == boxshadow-threecorners.html boxshadow-threecorners-ref.html
 fuzzy(2,440) == boxshadow-skiprect.html boxshadow-skiprect-ref.html
 == boxshadow-opacity.html boxshadow-opacity-ref.html
 == boxshadow-color-rounding.html boxshadow-color-rounding-ref.html
 == boxshadow-color-rounding-middle.html boxshadow-color-rounding-middle-ref.html
-fuzzy-if(OSX==1010,1,24) fuzzy-if(d2d,16,568) == boxshadow-large-border-radius.html boxshadow-large-border-radius-ref.html # Bug 1209649
+fuzzy-if(OSX==1010,1,24) fuzzy-if(d2d,16,908) == boxshadow-large-border-radius.html boxshadow-large-border-radius-ref.html # Bug 1209649
 fuzzy(3,500) fuzzy-if(d2d,2,1080) == boxshadow-border-radius-int.html boxshadow-border-radius-int-ref.html
 == boxshadow-inset-neg-spread.html about:blank
 == boxshadow-inset-neg-spread2.html boxshadow-inset-neg-spread2-ref.html
 fuzzy(26,3610) == boxshadow-rotated.html boxshadow-rotated-ref.html # Bug 1211264
 == boxshadow-inset-large-border-radius.html boxshadow-inset-large-border-radius-ref.html
 
 # fuzzy due to blur going inside, but as long as it's essentially black instead of a light gray its ok.
-fuzzy(12,9445) == boxshadow-inset-large-offset.html boxshadow-inset-large-offset-ref.html
+fuzzy(12,9445) fuzzy-if(d2d,13,10926) == boxshadow-inset-large-offset.html boxshadow-inset-large-offset-ref.html
 
 == overflow-not-scrollable-1.html overflow-not-scrollable-1-ref.html
 == overflow-not-scrollable-1.html overflow-not-scrollable-1-ref2.html
 == overflow-not-scrollable-2.html overflow-not-scrollable-2-ref.html
 == 611574-1.html 611574-1-ref.html
 == 611574-2.html 611574-2-ref.html
 fuzzy-if(winWidget,5,30) fuzzy-if(skiaContent,16,10) == fieldset.html fieldset-ref.html # minor anti-aliasing problem on Windows
 fuzzy-if(winWidget,5,30) fuzzy-if(skiaContent,16,10) == fieldset-inset.html fieldset-inset-ref.html # minor anti-aliasing problem on Windows
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1815,17 +1815,17 @@ fuzzy-if(skiaContent,1,123) == 978911-1.
 == 983084-2.html 983084-2-ref.html
 == 983084-3.html 983084-1-ref.html
 == 983691-1.html 983691-ref.html
 HTTP(..) == 983985-1.html 983985-1-ref.html
 HTTP(..) == 983985-2.html 983985-2-ref.html
 == 985303-1a.html 985303-1-ref.html
 == 985303-1b.html 985303-1-ref.html
 == 987680-1.html 987680-1-ref.html
-fuzzy-if(d2d,1,36) == 991046-1.html 991046-1-ref.html
+fuzzy-if(d2d,1,601) == 991046-1.html 991046-1-ref.html
 pref(layout.css.overflow-clip-box.enabled,true) fuzzy-if(skiaContent,2,845) == 992447.html 992447-ref.html
 == 1003425-1.html 1003425-1-ref.html
 == 1003425-2.html 1003425-2-ref.html
 == 1005405-1.html 1005405-1-ref.html
 == 1012640-1.html 1012640-1-ref.html
 fuzzy-if(/^Windows\x20NT\x205\.1/.test(http.oscpu),255,1) == 1013054-1.html 1013054-1-ref.html
 == 1018522-1.html 1018522-1-ref.html
 == 1021564-1.html 1021564-ref.html