Bug 1201272 - use a SkBlurImageFilter for Skia canvas shadows so we can better control composite operations. r=gwright
authorLee Salzman <lsalzman@mozilla.com>
Fri, 04 Sep 2015 15:29:11 -0400
changeset 296064 5b73f3910cdd7e63c3fde8ac491691d61cb78883
parent 296063 90f84f2af43ec3d1b63ca1aec8ad84b3368dc5f2
child 296065 4683e2a8d998f66616b562c6ee53429b8564fd7c
push id962
push userjlund@mozilla.com
push dateFri, 04 Dec 2015 23:28:54 +0000
treeherdermozilla-release@23a2d286e80f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgwright
bugs1201272
milestone43.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 1201272 - use a SkBlurImageFilter for Skia canvas shadows so we can better control composite operations. r=gwright
gfx/2d/DrawTargetSkia.cpp
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -14,23 +14,19 @@
 
 #ifdef USE_SKIA_GPU
 #include "skia/include/gpu/SkGpuDevice.h"
 #include "skia/include/gpu/gl/GrGLInterface.h"
 #endif
 
 #include "skia/include/core/SkTypeface.h"
 #include "skia/include/effects/SkGradientShader.h"
-#include "skia/include/effects/SkBlurDrawLooper.h"
-#include "skia/include/effects/SkBlurMaskFilter.h"
 #include "skia/include/core/SkColorFilter.h"
-#include "skia/include/effects/SkDropShadowImageFilter.h"
+#include "skia/include/effects/SkBlurImageFilter.h"
 #include "skia/include/effects/SkLayerRasterizer.h"
-#include "skia/include/effects/SkLayerDrawLooper.h"
-#include "skia/include/effects/SkDashPathEffect.h"
 #include "Logging.h"
 #include "Tools.h"
 #include "DataSurfaceHelpers.h"
 #include <algorithm>
 
 namespace mozilla {
 namespace gfx {
 
@@ -424,25 +420,47 @@ DrawTargetSkia::DrawSurfaceWithShadow(So
   MarkChanged();
 
   mCanvas->save();
   mCanvas->resetMatrix();
 
   TempBitmap bitmap = GetBitmapForSurface(aSurface);
 
   SkPaint paint;
-
-  SkAutoTUnref<SkImageFilter> filter(SkDropShadowImageFilter::Create(aOffset.x, aOffset.y,
-                                                                     aSigma, aSigma,
-                                                                     ColorToSkColor(aColor, 1.0)));
-
-  paint.setImageFilter(filter.get());
   paint.setXfermodeMode(GfxOpToSkiaOp(aOperator));
 
-  mCanvas->drawBitmap(bitmap.mBitmap, aDest.x, aDest.y, &paint);
+  // bug 1201272
+  // We can't use the SkDropShadowImageFilter here because it applies the xfer
+  // mode first to render the bitmap to a temporary layer, and then implicitly
+  // uses src-over to composite the resulting shadow.
+  // The canvas spec, however, states that the composite op must be used to
+  // composite the resulting shadow, so we must instead use a SkBlurImageFilter
+  // to blur the image ourselves.
+
+  SkPaint shadowPaint;
+  SkAutoTUnref<SkImageFilter> blurFilter(SkBlurImageFilter::Create(aSigma, aSigma));
+  SkAutoTUnref<SkColorFilter> colorFilter(
+    SkColorFilter::CreateModeFilter(ColorToSkColor(aColor, 1.0), SkXfermode::kSrcIn_Mode));
+
+  shadowPaint.setXfermode(paint.getXfermode());
+  shadowPaint.setImageFilter(blurFilter.get());
+  shadowPaint.setColorFilter(colorFilter.get());
+
+  // drawBitmap implicitly calls saveLayer with a src-over xfer mode if given
+  // an image filter, whereas the supplied xfer mode gets used to render into
+  // the layer, which is the wrong order. We instead must use drawSprite which
+  // applies the image filter directly to the bitmap without rendering it first,
+  // then uses the xfer mode to composite it.
+  IntPoint shadowDest = RoundedToInt(aDest + aOffset);
+  mCanvas->drawSprite(bitmap.mBitmap, shadowDest.x, shadowDest.y, &shadowPaint);
+
+  // Composite the original image after the shadow
+  IntPoint dest = RoundedToInt(aDest);
+  mCanvas->drawSprite(bitmap.mBitmap, dest.x, dest.y, &paint);
+
   mCanvas->restore();
 }
 
 void
 DrawTargetSkia::FillRect(const Rect &aRect,
                          const Pattern &aPattern,
                          const DrawOptions &aOptions)
 {