[Chromium] Fix opaque tracking for box shadows and non-composited child elements
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 8 Feb 2012 23:48:18 +0000 (23:48 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 8 Feb 2012 23:48:18 +0000 (23:48 +0000)
https://bugs.webkit.org/show_bug.cgi?id=78073

Patch by Dana Jansens <danakj@chromium.org> on 2012-02-08
Reviewed by Stephen White.

Source/WebCore:

Tests: compositing/culling/scrolled-within-boxshadow.html
       compositing/culling/translated-boxshadow.html

Unit tests: PlatformContextSkiaTest.cpp

When painting a box shadow, a filter is applied to the skia canvas, that can make
pixels painted with an opaque color end up non-opaque. So consider image/mask/color
filters when deciding if a paint is opaque.

Also when painting the background of an element with a box shadow, the background is
painted with a transform on the skia canvas based on the size of the box shadow. This
transform needs to be considered when tracking an opaque paint.

However, when a layer's contentRect position is non-zero, we translate the GraphicsContext
to put the contentRect at 0,0 in the skia canvas. For tracking opaque regions in the resulting
layer, we need to unto this translation. Scaling can also occur which we must undo. So we pass
the transform in to PlatformContextSkia to go from the SkCanvas back to the layer's content
coordinate space. Opaque paints can then be tracked in the layer's content space rather than
in the skia canvas space.

* platform/graphics/chromium/BitmapCanvasLayerTextureUpdater.cpp:
(WebCore::BitmapCanvasLayerTextureUpdater::prepareToUpdate):
* platform/graphics/chromium/CanvasLayerTextureUpdater.cpp:
(WebCore::CanvasLayerTextureUpdater::paintContents):
* platform/graphics/chromium/CanvasLayerTextureUpdater.h:
(WebCore):
(CanvasLayerTextureUpdater):
* platform/graphics/chromium/SkPictureCanvasLayerTextureUpdater.cpp:
(WebCore::SkPictureCanvasLayerTextureUpdater::prepareToUpdate):
* platform/graphics/skia/OpaqueRegionSkia.cpp:
(WebCore::paintIsOpaque):
(WebCore::OpaqueRegionSkia::didDrawRect):
(WebCore::OpaqueRegionSkia::didDrawPath):
(WebCore::OpaqueRegionSkia::didDrawPoints):
(WebCore::OpaqueRegionSkia::didDrawBounded):
(WebCore::OpaqueRegionSkia::didDraw):
(WebCore::OpaqueRegionSkia::markRectAsOpaque):
(WebCore::OpaqueRegionSkia::markRectAsNonOpaque):
* platform/graphics/skia/OpaqueRegionSkia.h:
(WebCore):
(OpaqueRegionSkia):
* platform/graphics/skia/PlatformContextSkia.cpp:
(WebCore::PlatformContextSkia::didDrawRect):
(WebCore::PlatformContextSkia::didDrawPath):
(WebCore::PlatformContextSkia::didDrawPoints):
(WebCore::PlatformContextSkia::didDrawBounded):
* platform/graphics/skia/PlatformContextSkia.h:
(PlatformContextSkia):
(WebCore::PlatformContextSkia::setOpaqueRegionTransform):

Source/WebKit/chromium:

* tests/PlatformContextSkiaTest.cpp:
(WebCore::TEST):
(WebCore):

LayoutTests:

* compositing/culling/scrolled-within-boxshadow-expected.png: Added.
* compositing/culling/scrolled-within-boxshadow-expected.txt: Added.
* compositing/culling/scrolled-within-boxshadow.html: Added.
* compositing/culling/translated-boxshadow-expected.png: Added.
* compositing/culling/translated-boxshadow-expected.txt: Added.
* compositing/culling/translated-boxshadow.html: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@107143 268f45cc-cd09-0410-ab3c-d52691b4dbfc

18 files changed:
LayoutTests/ChangeLog
LayoutTests/compositing/culling/scrolled-within-boxshadow-expected.png [new file with mode: 0644]
LayoutTests/compositing/culling/scrolled-within-boxshadow-expected.txt [new file with mode: 0644]
LayoutTests/compositing/culling/scrolled-within-boxshadow.html [new file with mode: 0644]
LayoutTests/compositing/culling/translated-boxshadow-expected.png [new file with mode: 0644]
LayoutTests/compositing/culling/translated-boxshadow-expected.txt [new file with mode: 0644]
LayoutTests/compositing/culling/translated-boxshadow.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/chromium/BitmapCanvasLayerTextureUpdater.cpp
Source/WebCore/platform/graphics/chromium/CanvasLayerTextureUpdater.cpp
Source/WebCore/platform/graphics/chromium/CanvasLayerTextureUpdater.h
Source/WebCore/platform/graphics/chromium/SkPictureCanvasLayerTextureUpdater.cpp
Source/WebCore/platform/graphics/skia/OpaqueRegionSkia.cpp
Source/WebCore/platform/graphics/skia/OpaqueRegionSkia.h
Source/WebCore/platform/graphics/skia/PlatformContextSkia.cpp
Source/WebCore/platform/graphics/skia/PlatformContextSkia.h
Source/WebKit/chromium/ChangeLog
Source/WebKit/chromium/tests/PlatformContextSkiaTest.cpp

index 15b60ab..2f3d5f3 100644 (file)
@@ -1,3 +1,17 @@
+2012-02-08  Dana Jansens  <danakj@chromium.org>
+
+        [Chromium] Fix opaque tracking for box shadows and non-composited child elements
+        https://bugs.webkit.org/show_bug.cgi?id=78073
+
+        Reviewed by Stephen White.
+
+        * compositing/culling/scrolled-within-boxshadow-expected.png: Added.
+        * compositing/culling/scrolled-within-boxshadow-expected.txt: Added.
+        * compositing/culling/scrolled-within-boxshadow.html: Added.
+        * compositing/culling/translated-boxshadow-expected.png: Added.
+        * compositing/culling/translated-boxshadow-expected.txt: Added.
+        * compositing/culling/translated-boxshadow.html: Added.
+
 2012-02-08  Julien Chaffraix  <jchaffraix@webkit.org>
 
         Unreviewed gardening.
diff --git a/LayoutTests/compositing/culling/scrolled-within-boxshadow-expected.png b/LayoutTests/compositing/culling/scrolled-within-boxshadow-expected.png
new file mode 100644 (file)
index 0000000..af11737
Binary files /dev/null and b/LayoutTests/compositing/culling/scrolled-within-boxshadow-expected.png differ
diff --git a/LayoutTests/compositing/culling/scrolled-within-boxshadow-expected.txt b/LayoutTests/compositing/culling/scrolled-within-boxshadow-expected.txt
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/LayoutTests/compositing/culling/scrolled-within-boxshadow.html b/LayoutTests/compositing/culling/scrolled-within-boxshadow.html
new file mode 100644 (file)
index 0000000..920204e
--- /dev/null
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+.comp {
+  -webkit-transform: translateZ(0px);
+}
+body {
+  margin:0;
+  padding:0;
+}
+.shadow {
+  position:absolute;
+  left:20px; right:20px;
+  width:500px;
+  height:500px;
+  top:0px; bottom:20px;
+  z-index:120;
+  margin-top:20px;
+  border: 1px solid transparent;
+  -webkit-box-shadow: 0 0 6px #090;
+  background-color: #090;
+}
+#scroll {
+  overflow-y: hidden; /* scrollable but no scrollbars visible. */
+  width:500px;
+  height: 500px;
+}
+.scrollContent {
+  width:100%;
+  height: 1000px; /* make the container scrollable */
+  background-color: #0d0;
+}
+</style>
+<script type="text/javascript">
+  if (window.layoutTestController) {
+    layoutTestController.dumpAsText(true);
+    layoutTestController.waitUntilDone();
+  }
+
+  window.onload = function() { setTimeout(function() {
+    document.getElementById('scroll').scrollTop += 300;
+    if (window.layoutTestController) {
+      layoutTestController.notifyDone();
+    }
+  }, 0); };
+</script>
+</head>
+
+<body>
+<!-- If culled incorrectly, the top/left edges outside of the shadowed box end up being culled instead of painted/drawn properly. -->
+<div class="comp shadow">
+  <div id="scroll">
+    <div class="scrollContent">
+    </div>
+  </div>
+</div>
+</body>
+</html>
diff --git a/LayoutTests/compositing/culling/translated-boxshadow-expected.png b/LayoutTests/compositing/culling/translated-boxshadow-expected.png
new file mode 100644 (file)
index 0000000..8a06876
Binary files /dev/null and b/LayoutTests/compositing/culling/translated-boxshadow-expected.png differ
diff --git a/LayoutTests/compositing/culling/translated-boxshadow-expected.txt b/LayoutTests/compositing/culling/translated-boxshadow-expected.txt
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/LayoutTests/compositing/culling/translated-boxshadow.html b/LayoutTests/compositing/culling/translated-boxshadow.html
new file mode 100644 (file)
index 0000000..5e141f5
--- /dev/null
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+.comp {
+  -webkit-transform: translateZ(0px);
+}
+body {
+  margin:0;
+  padding:0;
+}
+.shadow {
+  position:absolute;
+  left:20px; right:20px;
+  width:500px;
+  height:500px;
+  top:0px; bottom:20px;
+  z-index:120;
+  margin-top:20px;
+  border: 1px solid transparent;
+  -webkit-box-shadow: 0 0 6px #090;
+  background-color: #090;
+}
+</style>
+<script type="text/javascript">
+  if (window.layoutTestController) {
+    layoutTestController.dumpAsText(true);
+  }
+</script>
+</head>
+
+<body>
+<!-- If culled incorrectly, the top/left edges outside of the shadowed box end up being culled instead of painted/drawn properly. -->
+<div class="comp shadow"></div>
+</body>
+</html>
index 24b10e3..c77587f 100644 (file)
@@ -1,3 +1,60 @@
+2012-02-08  Dana Jansens  <danakj@chromium.org>
+
+        [Chromium] Fix opaque tracking for box shadows and non-composited child elements
+        https://bugs.webkit.org/show_bug.cgi?id=78073
+
+        Reviewed by Stephen White.
+
+        Tests: compositing/culling/scrolled-within-boxshadow.html
+               compositing/culling/translated-boxshadow.html
+
+        Unit tests: PlatformContextSkiaTest.cpp
+
+        When painting a box shadow, a filter is applied to the skia canvas, that can make
+        pixels painted with an opaque color end up non-opaque. So consider image/mask/color
+        filters when deciding if a paint is opaque.
+
+        Also when painting the background of an element with a box shadow, the background is
+        painted with a transform on the skia canvas based on the size of the box shadow. This
+        transform needs to be considered when tracking an opaque paint.
+
+        However, when a layer's contentRect position is non-zero, we translate the GraphicsContext
+        to put the contentRect at 0,0 in the skia canvas. For tracking opaque regions in the resulting
+        layer, we need to unto this translation. Scaling can also occur which we must undo. So we pass
+        the transform in to PlatformContextSkia to go from the SkCanvas back to the layer's content
+        coordinate space. Opaque paints can then be tracked in the layer's content space rather than
+        in the skia canvas space.
+
+        * platform/graphics/chromium/BitmapCanvasLayerTextureUpdater.cpp:
+        (WebCore::BitmapCanvasLayerTextureUpdater::prepareToUpdate):
+        * platform/graphics/chromium/CanvasLayerTextureUpdater.cpp:
+        (WebCore::CanvasLayerTextureUpdater::paintContents):
+        * platform/graphics/chromium/CanvasLayerTextureUpdater.h:
+        (WebCore):
+        (CanvasLayerTextureUpdater):
+        * platform/graphics/chromium/SkPictureCanvasLayerTextureUpdater.cpp:
+        (WebCore::SkPictureCanvasLayerTextureUpdater::prepareToUpdate):
+        * platform/graphics/skia/OpaqueRegionSkia.cpp:
+        (WebCore::paintIsOpaque):
+        (WebCore::OpaqueRegionSkia::didDrawRect):
+        (WebCore::OpaqueRegionSkia::didDrawPath):
+        (WebCore::OpaqueRegionSkia::didDrawPoints):
+        (WebCore::OpaqueRegionSkia::didDrawBounded):
+        (WebCore::OpaqueRegionSkia::didDraw):
+        (WebCore::OpaqueRegionSkia::markRectAsOpaque):
+        (WebCore::OpaqueRegionSkia::markRectAsNonOpaque):
+        * platform/graphics/skia/OpaqueRegionSkia.h:
+        (WebCore):
+        (OpaqueRegionSkia):
+        * platform/graphics/skia/PlatformContextSkia.cpp:
+        (WebCore::PlatformContextSkia::didDrawRect):
+        (WebCore::PlatformContextSkia::didDrawPath):
+        (WebCore::PlatformContextSkia::didDrawPoints):
+        (WebCore::PlatformContextSkia::didDrawBounded):
+        * platform/graphics/skia/PlatformContextSkia.h:
+        (PlatformContextSkia):
+        (WebCore::PlatformContextSkia::setOpaqueRegionTransform):
+
 2012-02-08  Kentaro Hara  <haraken@chromium.org>
 
         Remove [CustomHeader] from CanvasPixelArray and rename [CustomHeader] to [JSCustomHeader]
index 0d97e52..00a3e46 100644 (file)
@@ -92,7 +92,7 @@ void BitmapCanvasLayerTextureUpdater::prepareToUpdate(const IntRect& contentRect
         borderTexels ? PlatformCanvas::Painter::GrayscaleText : PlatformCanvas::Painter::SubpixelText;
     PlatformCanvas::Painter canvasPainter(&m_canvas, textOption);
     canvasPainter.skiaContext()->setTrackOpaqueRegion(!layerIsOpaque);
-    paintContents(*canvasPainter.context(), contentRect, contentsScale);
+    paintContents(*canvasPainter.context(), *canvasPainter.skiaContext(), contentRect, contentsScale);
 
     if (!layerIsOpaque)
         *resultingOpaqueRect = canvasPainter.skiaContext()->opaqueRegion().asRect();
index 5fa6785..ea19303 100644 (file)
@@ -32,6 +32,7 @@
 
 #include "GraphicsContext.h"
 #include "LayerPainterChromium.h"
+#include "PlatformContextSkia.h"
 #include "TraceEvent.h"
 
 namespace WebCore {
@@ -45,7 +46,7 @@ CanvasLayerTextureUpdater::~CanvasLayerTextureUpdater()
 {
 }
 
-void CanvasLayerTextureUpdater::paintContents(GraphicsContext& context, const IntRect& contentRect, float contentsScale)
+void CanvasLayerTextureUpdater::paintContents(GraphicsContext& context, PlatformContextSkia& platformContext, const IntRect& contentRect, float contentsScale)
 {
     context.translate(-contentRect.x(), -contentRect.y());
     {
@@ -62,6 +63,10 @@ void CanvasLayerTextureUpdater::paintContents(GraphicsContext& context, const In
             scaledContentRect = enclosingIntRect(rect);
         }
 
+        // Transform tracked opaque paints back to our layer's content space.
+        ASSERT(context.getCTM().isInvertible());
+        platformContext.setOpaqueRegionTransform(context.getCTM().inverse());
+
         m_painter->paint(context, scaledContentRect);
 
         if (contentsScale != 1.0)
index 4415f69..d491348 100644 (file)
@@ -35,6 +35,7 @@ namespace WebCore {
 
 class GraphicsContext3D;
 class LayerPainterChromium;
+class PlatformContextSkia;
 
 // Base class for BitmapCanvasLayerTextureUpdater and
 // SkPictureCanvasLayerTextureUpdater that reduces code duplication between
@@ -46,7 +47,7 @@ public:
 protected:
     explicit CanvasLayerTextureUpdater(PassOwnPtr<LayerPainterChromium>);
 
-    void paintContents(GraphicsContext&, const IntRect& contentRect, float contentsScale);
+    void paintContents(GraphicsContext&, PlatformContextSkia&, const IntRect& contentRect, float contentsScale);
     const IntRect& contentRect() const { return m_contentRect; }
 
 private:
index 0d974fa..35cd35a 100644 (file)
@@ -56,7 +56,7 @@ void SkPictureCanvasLayerTextureUpdater::prepareToUpdate(const IntRect& contentR
     platformContext.setDeferred(true);
     platformContext.setTrackOpaqueRegion(!m_layerIsOpaque);
     GraphicsContext graphicsContext(&platformContext);
-    paintContents(graphicsContext, contentRect, contentsScale);
+    paintContents(graphicsContext, platformContext, contentRect, contentsScale);
     m_picture.endRecording();
 
     if (!m_layerIsOpaque)
index 74145f1..30b8a38 100644 (file)
@@ -35,6 +35,7 @@
 #include "PlatformContextSkia.h"
 
 #include "SkCanvas.h"
+#include "SkColorFilter.h"
 #include "SkShader.h"
 
 namespace WebCore {
@@ -134,10 +135,19 @@ static inline bool paintIsOpaque(const SkPaint& paint, const SkBitmap* bitmap =
         return false;
     if (bitmap && !bitmap->isOpaque())
         return false;
+    if (paint.getLooper())
+        return false;
+    if (paint.getImageFilter())
+        return false;
+    if (paint.getMaskFilter())
+        return false;
+    SkColorFilter* colorFilter = paint.getColorFilter();
+    if (colorFilter && !(colorFilter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag))
+        return false;
     return true;
 }
 
-void OpaqueRegionSkia::didDrawRect(const PlatformContextSkia* context, const SkRect& fillRect, const SkPaint& paint, const SkBitmap* bitmap)
+void OpaqueRegionSkia::didDrawRect(const PlatformContextSkia* context, const AffineTransform& transform, const SkRect& fillRect, const SkPaint& paint, const SkBitmap* bitmap)
 {
     // Any stroking may put alpha in pixels even if the filling part does not.
     if (paint.getStyle() != SkPaint::kFill_Style) {
@@ -149,21 +159,21 @@ void OpaqueRegionSkia::didDrawRect(const PlatformContextSkia* context, const SkR
         else {
             SkRect strokeRect;
             strokeRect = paint.computeFastBounds(fillRect, &strokeRect);
-            didDraw(context, strokeRect, paint, opaque, fillsBounds);
+            didDraw(context, transform, strokeRect, paint, opaque, fillsBounds);
         }
     }
 
     bool checkFillOnly = true;
     bool opaque = paintIsOpaque(paint, bitmap, checkFillOnly);
     bool fillsBounds = paint.getStyle() != SkPaint::kStroke_Style;
-    didDraw(context, fillRect, paint, opaque, fillsBounds);
+    didDraw(context, transform, fillRect, paint, opaque, fillsBounds);
 }
 
-void OpaqueRegionSkia::didDrawPath(const PlatformContextSkia* context, const SkPath& path, const SkPaint& paint)
+void OpaqueRegionSkia::didDrawPath(const PlatformContextSkia* context, const AffineTransform& transform, const SkPath& path, const SkPaint& paint)
 {
     SkRect rect;
     if (path.isRect(&rect)) {
-        didDrawRect(context, rect, paint, 0);
+        didDrawRect(context, transform, rect, paint, 0);
         return;
     }
 
@@ -174,11 +184,11 @@ void OpaqueRegionSkia::didDrawPath(const PlatformContextSkia* context, const SkP
         didDrawUnbounded(paint, opaque);
     else {
         rect = paint.computeFastBounds(path.getBounds(), &rect);
-        didDraw(context, rect, paint, opaque, fillsBounds);
+        didDraw(context, transform, rect, paint, opaque, fillsBounds);
     }
 }
 
-void OpaqueRegionSkia::didDrawPoints(const PlatformContextSkia* context, SkCanvas::PointMode mode, int numPoints, const SkPoint points[], const SkPaint& paint)
+void OpaqueRegionSkia::didDrawPoints(const PlatformContextSkia* context, const AffineTransform& transform, SkCanvas::PointMode mode, int numPoints, const SkPoint points[], const SkPaint& paint)
 {
     if (!numPoints)
         return;
@@ -203,11 +213,11 @@ void OpaqueRegionSkia::didDrawPoints(const PlatformContextSkia* context, SkCanva
         didDrawUnbounded(paint, opaque);
     else {
         rect = paint.computeFastBounds(rect, &rect);
-        didDraw(context, rect, paint, opaque, fillsBounds);
+        didDraw(context, transform, rect, paint, opaque, fillsBounds);
     }
 }
 
-void OpaqueRegionSkia::didDrawBounded(const PlatformContextSkia* context, const SkRect& bounds, const SkPaint& paint)
+void OpaqueRegionSkia::didDrawBounded(const PlatformContextSkia* context, const AffineTransform& transform, const SkRect& bounds, const SkPaint& paint)
 {
     bool opaque = paintIsOpaque(paint);
     bool fillsBounds = false;
@@ -217,16 +227,38 @@ void OpaqueRegionSkia::didDrawBounded(const PlatformContextSkia* context, const
     else {
         SkRect rect;
         rect = paint.computeFastBounds(bounds, &rect);
-        didDraw(context, rect, paint, opaque, fillsBounds);
+        didDraw(context, transform, rect, paint, opaque, fillsBounds);
     }
 }
 
-void OpaqueRegionSkia::didDraw(const PlatformContextSkia* context, const SkRect& rect, const SkPaint& paint, bool drawsOpaque, bool fillsBounds)
+void OpaqueRegionSkia::didDraw(const PlatformContextSkia* context, const AffineTransform& transform, const SkRect& rect, const SkPaint& paint, bool drawsOpaque, bool fillsBounds)
 {
+    SkRect targetRect = rect;
+
+    // Apply the current clip.
+    if (context->canvas()->getClipType() != SkCanvas::kRect_ClipType)
+        fillsBounds = false;
+    else {
+        SkIRect deviceClip;
+        context->canvas()->getClipDeviceBounds(&deviceClip);
+        if (!targetRect.intersect(SkIntToScalar(deviceClip.fLeft), SkIntToScalar(deviceClip.fTop), SkIntToScalar(deviceClip.fRight), SkIntToScalar(deviceClip.fBottom)))
+            return;
+    }
+    if (!context->clippedToImage().isOpaque())
+        fillsBounds = false;
+
+    // Apply the transforms.
+    SkMatrix canvasTransform = context->canvas()->getTotalMatrix();
+    if (!canvasTransform.mapRect(&targetRect))
+        fillsBounds = false;
+    SkMatrix canvasToTargetTransform = transform;
+    if (!canvasToTargetTransform.mapRect(&targetRect))
+        fillsBounds = false;
+
     if (fillsBounds && xfermodeIsOpaque(paint, drawsOpaque))
-        markRectAsOpaque(context, rect);
-    else if (SkRect::Intersects(rect, m_opaqueRect) && !xfermodePreservesOpaque(paint, drawsOpaque))
-        markRectAsNonOpaque(rect);
+        markRectAsOpaque(targetRect);
+    else if (SkRect::Intersects(targetRect, m_opaqueRect) && !xfermodePreservesOpaque(paint, drawsOpaque))
+        markRectAsNonOpaque(targetRect);
 }
 
 void OpaqueRegionSkia::didDrawUnbounded(const SkPaint& paint, bool drawsOpaque)
@@ -237,29 +269,17 @@ void OpaqueRegionSkia::didDrawUnbounded(const SkPaint& paint, bool drawsOpaque)
     }
 }
 
-void OpaqueRegionSkia::markRectAsOpaque(const PlatformContextSkia* context, const SkRect& paintRect)
+void OpaqueRegionSkia::markRectAsOpaque(const SkRect& rect)
 {
     // We want to keep track of an opaque region but bound its complexity at a constant size.
     // We keep track of the largest rectangle seen by area. If we can add the new rect to this
     // rectangle then we do that, as that is the cheapest way to increase the area returned
     // without increasing the complexity.
 
-    if (paintRect.isEmpty())
-        return;
-    if (!context->clippedToImage().isOpaque())
-        return;
-    if (context->canvas()->getClipType() != SkCanvas::kRect_ClipType)
+    if (rect.isEmpty())
         return;
-    if (m_opaqueRect.contains(paintRect))
+    if (m_opaqueRect.contains(rect))
         return;
-
-    // Apply the current clip.
-    SkIRect deviceClip;
-    context->canvas()->getClipDeviceBounds(&deviceClip);
-    SkRect rect = paintRect;
-    if (!rect.intersect(SkIntToScalar(deviceClip.fLeft), SkIntToScalar(deviceClip.fTop), SkIntToScalar(deviceClip.fRight), SkIntToScalar(deviceClip.fBottom)))
-        return;
-
     if (rect.contains(m_opaqueRect)) {
         m_opaqueRect = rect;
         return;
index 93f2c5a..45b0690 100644 (file)
@@ -40,6 +40,7 @@
 #include "SkRect.h"
 
 namespace WebCore {
+class AffineTransform;
 class PlatformContextSkia;
 
 // This class is an encapsulation of functionality for PlatformContextSkia, and its methods are mirrored
@@ -52,15 +53,15 @@ public:
     // The resulting opaque region as a single rect.
     IntRect asRect() const;
 
-    void didDrawRect(const PlatformContextSkia*, const SkRect&, const SkPaint&, const SkBitmap*);
-    void didDrawPath(const PlatformContextSkia*, const SkPath&, const SkPaint&);
-    void didDrawPoints(const PlatformContextSkia*, SkCanvas::PointMode, int numPoints, const SkPoint[], const SkPaint&);
-    void didDrawBounded(const PlatformContextSkia*, const SkRect&, const SkPaint&);
+    void didDrawRect(const PlatformContextSkia*, const AffineTransform&, const SkRect&, const SkPaint&, const SkBitmap*);
+    void didDrawPath(const PlatformContextSkia*, const AffineTransform&, const SkPath&, const SkPaint&);
+    void didDrawPoints(const PlatformContextSkia*, const AffineTransform&, SkCanvas::PointMode, int numPoints, const SkPoint[], const SkPaint&);
+    void didDrawBounded(const PlatformContextSkia*, const AffineTransform&, const SkRect&, const SkPaint&);
 
 private:
-    void didDraw(const PlatformContextSkia*, const SkRect&, const SkPaint&, bool drawsOpaque, bool fillsBounds);
+    void didDraw(const PlatformContextSkia*, const AffineTransform&, const SkRect&, const SkPaint&, bool drawsOpaque, bool fillsBounds);
     void didDrawUnbounded(const SkPaint&, bool drawsOpaque);
-    void markRectAsOpaque(const PlatformContextSkia*, const SkRect&);
+    void markRectAsOpaque(const SkRect&);
     void markRectAsNonOpaque(const SkRect&);
 
     SkRect m_opaqueRect;
index 33062ca..c323a0d 100644 (file)
@@ -32,7 +32,6 @@
 
 #include "PlatformContextSkia.h"
 
-#include "AffineTransform.h"
 #include "Extensions3D.h"
 #include "GraphicsContext.h"
 #include "GraphicsContext3D.h"
@@ -609,25 +608,25 @@ void PlatformContextSkia::setGraphicsContext3D(GraphicsContext3D* context)
 void PlatformContextSkia::didDrawRect(const SkRect& rect, const SkPaint& paint, const SkBitmap* bitmap)
 {
     if (m_trackOpaqueRegion)
-        m_opaqueRegion.didDrawRect(this, rect, paint, bitmap);
+        m_opaqueRegion.didDrawRect(this, m_opaqueRegionTransform, rect, paint, bitmap);
 }
 
 void PlatformContextSkia::didDrawPath(const SkPath& path, const SkPaint& paint)
 {
     if (m_trackOpaqueRegion)
-        m_opaqueRegion.didDrawPath(this, path, paint);
+        m_opaqueRegion.didDrawPath(this, m_opaqueRegionTransform, path, paint);
 }
 
 void PlatformContextSkia::didDrawPoints(SkCanvas::PointMode mode, int numPoints, const SkPoint points[], const SkPaint& paint)
 {
     if (m_trackOpaqueRegion)
-        m_opaqueRegion.didDrawPoints(this, mode, numPoints, points, paint);
+        m_opaqueRegion.didDrawPoints(this, m_opaqueRegionTransform, mode, numPoints, points, paint);
 }
 
 void PlatformContextSkia::didDrawBounded(const SkRect& rect, const SkPaint& paint)
 {
     if (m_trackOpaqueRegion)
-        m_opaqueRegion.didDrawBounded(this, rect, paint);
+        m_opaqueRegion.didDrawBounded(this, m_opaqueRegionTransform, rect, paint);
 }
 
 } // namespace WebCore
index 12bdf79..b306450 100644 (file)
@@ -31,6 +31,7 @@
 #ifndef PlatformContextSkia_h
 #define PlatformContextSkia_h
 
+#include "AffineTransform.h"
 #include "GraphicsContext.h"
 #include "Noncopyable.h"
 #include "OpaqueRegionSkia.h"
@@ -192,6 +193,8 @@ public:
     void setDeferred(bool deferred) { m_deferred = deferred; }
 
     void setTrackOpaqueRegion(bool track) { m_trackOpaqueRegion = track; }
+    // A transform applied to all tracked opaque paints. This is applied at the time the painting is done.
+    void setOpaqueRegionTransform(const AffineTransform& transform) { m_opaqueRegionTransform = transform; }
 
     // This will be an empty region unless tracking is enabled.
     const OpaqueRegionSkia& opaqueRegion() const { return m_opaqueRegion; }
@@ -229,6 +232,7 @@ private:
     // Tracks the region painted opaque via the GraphicsContext.
     OpaqueRegionSkia m_opaqueRegion;
     bool m_trackOpaqueRegion;
+    AffineTransform m_opaqueRegionTransform;
 
     // Stores image sizes for a hint to compute image resampling modes.
     // Values are used in ImageSkia.cpp
index acedbad..791d225 100644 (file)
@@ -1,3 +1,14 @@
+2012-02-08  Dana Jansens  <danakj@chromium.org>
+
+        [Chromium] Fix opaque tracking for box shadows and non-composited child elements
+        https://bugs.webkit.org/show_bug.cgi?id=78073
+
+        Reviewed by Stephen White.
+
+        * tests/PlatformContextSkiaTest.cpp:
+        (WebCore::TEST):
+        (WebCore):
+
 2012-02-08  Shawn Singh  <shawnsingh@chromium.org>
 
         [chromium] Remove incorrect early exit in CCDamageTracker
index dc1cf29..924b42d 100644 (file)
@@ -548,4 +548,73 @@ TEST(PlatformContextSkiaTest, trackOpaqueOvalTest)
     EXPECT_PIXELS_MATCH(bitmap, platformContext.opaqueRegion().asRect());
 }
 
+TEST(PlatformContextSkiaTest, layerTransformTranslateOpaqueTest)
+{
+    SkBitmap bitmap;
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, 400, 400);
+    bitmap.allocPixels();
+    bitmap.eraseColor(0);
+    SkCanvas canvas(bitmap);
+    AffineTransform transform;
+    transform.translate(10, 10);
+
+    PlatformContextSkia platformContext(&canvas);
+    platformContext.setTrackOpaqueRegion(true);
+    platformContext.setOpaqueRegionTransform(transform);
+    GraphicsContext context(&platformContext);
+
+    Color opaque(1.0f, 0.0f, 0.0f, 1.0f);
+
+    context.fillRect(FloatRect(10, 10, 90, 90), opaque, ColorSpaceDeviceRGB, CompositeSourceOver);
+    EXPECT_EQ_RECT(IntRect(20, 20, 90, 90), platformContext.opaqueRegion().asRect());
+    EXPECT_PIXELS_MATCH(bitmap, transform.inverse().mapRect(platformContext.opaqueRegion().asRect()));
+
+    context.clearRect(FloatRect(10, 10, 90, 90));
+    EXPECT_EQ_RECT(IntRect(), platformContext.opaqueRegion().asRect());
+
+    context.translate(30, 30);
+
+    context.fillRect(FloatRect(10, 10, 90, 90), opaque, ColorSpaceDeviceRGB, CompositeSourceOver);
+    EXPECT_EQ_RECT(IntRect(50, 50, 90, 90), platformContext.opaqueRegion().asRect());
+    EXPECT_PIXELS_MATCH(bitmap, transform.inverse().mapRect(platformContext.opaqueRegion().asRect()));
+
+    context.clearRect(FloatRect(10, 10, 90, 90));
+    EXPECT_EQ_RECT(IntRect(), platformContext.opaqueRegion().asRect());
+}
+
+TEST(PlatformContextSkiaTest, layerTransformScaleOpaqueTest)
+{
+    SkBitmap bitmap;
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, 400, 400);
+    bitmap.allocPixels();
+    bitmap.eraseColor(0);
+    SkCanvas canvas(bitmap);
+    AffineTransform transform;
+    transform.scale(2);
+
+    PlatformContextSkia platformContext(&canvas);
+    platformContext.setTrackOpaqueRegion(true);
+    platformContext.setOpaqueRegionTransform(transform);
+    GraphicsContext context(&platformContext);
+
+    Color opaque(1.0f, 0.0f, 0.0f, 1.0f);
+
+    context.fillRect(FloatRect(20, 20, 10, 10), opaque, ColorSpaceDeviceRGB, CompositeSourceOver);
+    EXPECT_EQ_RECT(IntRect(40, 40, 20, 20), platformContext.opaqueRegion().asRect());
+    EXPECT_PIXELS_MATCH(bitmap, transform.inverse().mapRect(platformContext.opaqueRegion().asRect()));
+
+    context.clearRect(FloatRect(20, 20, 10, 10));
+    EXPECT_EQ_RECT(IntRect(), platformContext.opaqueRegion().asRect());
+
+    context.scale(FloatSize(2, 1));
+    context.translate(0, 10);
+
+    context.fillRect(FloatRect(20, 20, 10, 10), opaque, ColorSpaceDeviceRGB, CompositeSourceOver);
+    EXPECT_EQ_RECT(IntRect(80, 60, 40, 20), platformContext.opaqueRegion().asRect());
+    EXPECT_PIXELS_MATCH(bitmap, transform.inverse().mapRect(platformContext.opaqueRegion().asRect()));
+
+    context.clearRect(FloatRect(20, 20, 10, 10));
+    EXPECT_EQ_RECT(IntRect(), platformContext.opaqueRegion().asRect());
+}
+
 } // namespace