Title: [115139] trunk/Source
Revision
115139
Author
[email protected]
Date
2012-04-24 16:58:04 -0700 (Tue, 24 Apr 2012)

Log Message

[chromium] Image masks are considered opaque incorrectly
https://bugs.webkit.org/show_bug.cgi?id=84275

Reviewed by Adrienne Walker.

Source/WebCore:

Match the behaviour of SkCanvas layers more closely while tracking
opaque paints. SkCanvas layers actually act as a separate device
(ie. pixels) and when the layer is popped off, the pixels are copied
down to the layer below.

While we can use the total clip to decide what pixels the the
drawing operation will affect in the final device, the blending
down through layers needs to consider each layer carefully.

In this case the image mask is drawn into a layer which is copied
down using the DestinationIn operation. Since the layer contains
non-opaque pixels, the DestinationIn copy can destroy opaque
areas in the next layer. We add OpaqueRegionSkia::FillByCopy to
distinguish the case where we are copying a block of pixels, and the
alpha values are essentially unknown.

Unit test: PlatformContextSkiaTest.trackImageMask
           PlatformContextSkiaTest.trackImageMaskWithOpaqueRect

* platform/graphics/skia/OpaqueRegionSkia.cpp:
(WebCore::paintIsOpaque):
(WebCore::OpaqueRegionSkia::applyOpaqueRegionFromLayer):
(WebCore::OpaqueRegionSkia::pushCanvasLayer):
(WebCore::OpaqueRegionSkia::popCanvasLayer):
(WebCore::OpaqueRegionSkia::didDrawRect):
(WebCore::OpaqueRegionSkia::didDrawPath):
(WebCore::OpaqueRegionSkia::didDrawPoints):
(WebCore::OpaqueRegionSkia::didDrawBounded):
(WebCore::OpaqueRegionSkia::didDraw):
(WebCore::OpaqueRegionSkia::didDrawUnbounded):
(WebCore::OpaqueRegionSkia::markRectAsOpaque):
(WebCore::OpaqueRegionSkia::markRectAsNonOpaque):
(WebCore::OpaqueRegionSkia::markAllAsNonOpaque):
* platform/graphics/skia/OpaqueRegionSkia.h:
(OpaqueRegionSkia):
(CanvasLayerState):
* platform/graphics/skia/PlatformContextSkia.cpp:
(WebCore::PlatformContextSkia::restoreLayer):

Source/WebKit/chromium:

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

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (115138 => 115139)


--- trunk/Source/WebCore/ChangeLog	2012-04-24 23:55:50 UTC (rev 115138)
+++ trunk/Source/WebCore/ChangeLog	2012-04-24 23:58:04 UTC (rev 115139)
@@ -1,3 +1,49 @@
+2012-04-24  Dana Jansens  <[email protected]>
+
+        [chromium] Image masks are considered opaque incorrectly
+        https://bugs.webkit.org/show_bug.cgi?id=84275
+
+        Reviewed by Adrienne Walker.
+
+        Match the behaviour of SkCanvas layers more closely while tracking
+        opaque paints. SkCanvas layers actually act as a separate device
+        (ie. pixels) and when the layer is popped off, the pixels are copied
+        down to the layer below.
+
+        While we can use the total clip to decide what pixels the the
+        drawing operation will affect in the final device, the blending
+        down through layers needs to consider each layer carefully.
+
+        In this case the image mask is drawn into a layer which is copied
+        down using the DestinationIn operation. Since the layer contains
+        non-opaque pixels, the DestinationIn copy can destroy opaque
+        areas in the next layer. We add OpaqueRegionSkia::FillByCopy to
+        distinguish the case where we are copying a block of pixels, and the
+        alpha values are essentially unknown.
+
+        Unit test: PlatformContextSkiaTest.trackImageMask
+                   PlatformContextSkiaTest.trackImageMaskWithOpaqueRect
+
+        * platform/graphics/skia/OpaqueRegionSkia.cpp:
+        (WebCore::paintIsOpaque):
+        (WebCore::OpaqueRegionSkia::applyOpaqueRegionFromLayer):
+        (WebCore::OpaqueRegionSkia::pushCanvasLayer):
+        (WebCore::OpaqueRegionSkia::popCanvasLayer):
+        (WebCore::OpaqueRegionSkia::didDrawRect):
+        (WebCore::OpaqueRegionSkia::didDrawPath):
+        (WebCore::OpaqueRegionSkia::didDrawPoints):
+        (WebCore::OpaqueRegionSkia::didDrawBounded):
+        (WebCore::OpaqueRegionSkia::didDraw):
+        (WebCore::OpaqueRegionSkia::didDrawUnbounded):
+        (WebCore::OpaqueRegionSkia::markRectAsOpaque):
+        (WebCore::OpaqueRegionSkia::markRectAsNonOpaque):
+        (WebCore::OpaqueRegionSkia::markAllAsNonOpaque):
+        * platform/graphics/skia/OpaqueRegionSkia.h:
+        (OpaqueRegionSkia):
+        (CanvasLayerState):
+        * platform/graphics/skia/PlatformContextSkia.cpp:
+        (WebCore::PlatformContextSkia::restoreLayer):
+
 2012-04-24  Alexandru Chiculita  <[email protected]>
 
         CSS Shaders: Repainting the FECustomFilter requires full source image

Modified: trunk/Source/WebCore/platform/graphics/skia/OpaqueRegionSkia.cpp (115138 => 115139)


--- trunk/Source/WebCore/platform/graphics/skia/OpaqueRegionSkia.cpp	2012-04-24 23:55:50 UTC (rev 115138)
+++ trunk/Source/WebCore/platform/graphics/skia/OpaqueRegionSkia.cpp	2012-04-24 23:58:04 UTC (rev 115139)
@@ -124,10 +124,11 @@
 }
 
 // Returns true if all pixels painted will be opaque.
-static inline bool paintIsOpaque(const SkPaint& paint, const SkBitmap* bitmap = 0, bool checkFillOnly = false)
+static inline bool paintIsOpaque(const SkPaint& paint, OpaqueRegionSkia::DrawType drawType, const SkBitmap* bitmap)
 {
     if (paint.getAlpha() < 0xFF)
         return false;
+    bool checkFillOnly = drawType != OpaqueRegionSkia::FillOrStroke;
     if (!checkFillOnly && paint.getStyle() != SkPaint::kFill_Style && paint.isAntiAlias())
         return false;
     SkShader* shader = paint.getShader();
@@ -147,6 +148,30 @@
     return true;
 }
 
+// Returns true if there is a rectangular clip, with the result in |deviceClipRect|.
+static inline bool getDeviceClipAsRect(const PlatformContextSkia* context, SkRect& deviceClipRect)
+{
+    // Get the current clip in device coordinate space.
+    if (context->canvas()->getClipType() != SkCanvas::kRect_ClipType)
+        return false;
+
+    SkIRect deviceClipIRect;
+    if (context->canvas()->getClipDeviceBounds(&deviceClipIRect))
+        deviceClipRect.set(deviceClipIRect);
+    else
+        deviceClipRect.setEmpty();
+
+    return true;
+}
+
+static inline SkRect& currentTrackingOpaqueRect(SkRect& rootOpaqueRect, Vector<OpaqueRegionSkia::CanvasLayerState, 3>& canvasLayerStack)
+{
+    // If we are drawing into a canvas layer, then track the opaque rect in that layer.
+    if (!canvasLayerStack.isEmpty())
+        return canvasLayerStack.last().opaqueRect;
+    return rootOpaqueRect;
+}
+
 void OpaqueRegionSkia::pushCanvasLayer(const SkPaint* paint)
 {
     CanvasLayerState state;
@@ -155,9 +180,19 @@
     m_canvasLayerStack.append(state);
 }
 
-void OpaqueRegionSkia::popCanvasLayer()
+void OpaqueRegionSkia::popCanvasLayer(const PlatformContextSkia* context)
 {
+    const CanvasLayerState& canvasLayer = m_canvasLayerStack.last();
+    SkRect layerOpaqueRect = canvasLayer.opaqueRect;
+    SkPaint layerPaint = canvasLayer.paint;
+
+    // Apply the image mask.
+    if (canvasLayer.hasImageMask && !layerOpaqueRect.intersect(canvasLayer.imageOpaqueRect))
+        layerOpaqueRect.setEmpty();
+
     m_canvasLayerStack.removeLast();
+
+    applyOpaqueRegionFromLayer(context, layerOpaqueRect, layerPaint);
 }
 
 void OpaqueRegionSkia::setImageMask(const SkRect& imageOpaqueRect)
@@ -254,71 +289,67 @@
     if (!canvasTransform.mapRect(&targetRect))
         fillsBounds = false;
 
-    // Apply the current clip in device coordinate space.
-    if (context->canvas()->getClipType() != SkCanvas::kRect_ClipType)
+    // Apply the current clip.
+    SkRect deviceClipRect;
+    if (!getDeviceClipAsRect(context, deviceClipRect))
         fillsBounds = false;
-    else {
-        SkIRect deviceClip;
-        if (!context->canvas()->getClipDeviceBounds(&deviceClip))
-            return;
-        if (!targetRect.intersect(SkIntToScalar(deviceClip.fLeft), SkIntToScalar(deviceClip.fTop), SkIntToScalar(deviceClip.fRight), SkIntToScalar(deviceClip.fBottom)))
-            return;
-    }
+    else if (!targetRect.intersect(deviceClipRect))
+        return;
 
-    bool checkFillOnly = drawType == FillOnly;
-    bool lastLayerDrawsOpaque = paintIsOpaque(paint, sourceBitmap, checkFillOnly);
+    bool drawsOpaque = paintIsOpaque(paint, drawType, sourceBitmap);
+    bool xfersOpaque = xfermodeIsOpaque(paint, drawsOpaque);
+    bool preservesOpaque = xfermodePreservesOpaque(paint, drawsOpaque);
 
-    bool xfersOpaque = xfermodeIsOpaque(paint, lastLayerDrawsOpaque);
-
-    // Apply the SkCanvas layers we will be drawing through.
-    for (size_t i = m_canvasLayerStack.size(); i > 0; --i) {
-        const CanvasLayerState& canvasLayer = m_canvasLayerStack[i-1];
-
-        // FIXME: We could still track the opaque part but it's always empty right now anyways.
-        if (canvasLayer.hasImageMask && !canvasLayer.imageOpaqueRect.contains(targetRect))
-            fillsBounds = false;
-
-        bool checkFillOnly = drawType == FillOnly;
-        lastLayerDrawsOpaque = paintIsOpaque(canvasLayer.paint, 0, checkFillOnly);
-        // If any layer doesn't paint opaque, then the result will not be opaque.
-        xfersOpaque &= xfermodeIsOpaque(canvasLayer.paint, lastLayerDrawsOpaque);
-    }
-
-    // Preserving opaque only matters for the bottom-most layer. Its contents are either opaque or not, and if not
-    // then we care if it preserves the opaqueness of the target device when it is drawn into the device.
-    bool preservesOpaque;
-    if (m_canvasLayerStack.isEmpty())
-        preservesOpaque = xfermodePreservesOpaque(paint, lastLayerDrawsOpaque);
-    else
-        preservesOpaque = xfermodePreservesOpaque(m_canvasLayerStack[0].paint, lastLayerDrawsOpaque);
-
     if (fillsBounds && xfersOpaque)
         markRectAsOpaque(targetRect);
-    else if (SkRect::Intersects(targetRect, m_opaqueRect) && !preservesOpaque)
+    else if (!preservesOpaque)
         markRectAsNonOpaque(targetRect);
 }
 
 void OpaqueRegionSkia::didDrawUnbounded(const SkPaint& paint)
 {
-    // Preserving opaque only matters for the bottom-most layer. Its contents are either opaque or not, and if not
-    // then we care if it preserves the opaqueness of the target device when it is drawn into the device.
-    bool preservesOpaque;
+    bool drawsOpaque = paintIsOpaque(paint, FillOrStroke, 0);
+    bool preservesOpaque = xfermodePreservesOpaque(paint, drawsOpaque);
 
-    bool checkFillOnly = false;
-    if (m_canvasLayerStack.isEmpty()) {
-        bool lastLayerDrawsOpaque = paintIsOpaque(paint, 0, checkFillOnly);
-        preservesOpaque = xfermodePreservesOpaque(paint, lastLayerDrawsOpaque);
-    } else {
-        bool lastLayerDrawsOpaque = paintIsOpaque(m_canvasLayerStack[0].paint, 0, checkFillOnly);
-        preservesOpaque = xfermodePreservesOpaque(m_canvasLayerStack[0].paint, lastLayerDrawsOpaque);
-    }
+    if (preservesOpaque)
+        return;
 
-    if (!preservesOpaque) {
-        // We don't know what was drawn on so just destroy the known opaque area.
-        m_opaqueRect = SkRect::MakeEmpty();
-    }
+    markAllAsNonOpaque();
 }
 
+void OpaqueRegionSkia::applyOpaqueRegionFromLayer(const PlatformContextSkia* context, const SkRect& layerOpaqueRect, const SkPaint& paint)
+{
+    SkRect deviceClipRect;
+    bool deviceClipIsARect = getDeviceClipAsRect(context, deviceClipRect);
+
+    if (deviceClipRect.isEmpty())
+        return;
+
+    SkRect sourceOpaqueRect = layerOpaqueRect;
+    // Save the opaque area in the destination, so we can preserve the parts of it under the source opaque area if possible.
+    SkRect destinationOpaqueRect = currentTrackingOpaqueRect(m_opaqueRect, m_canvasLayerStack);
+
+    bool outsideSourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, false);
+    if (!outsideSourceOpaqueRectPreservesOpaque)
+        markRectAsNonOpaque(deviceClipRect);
+
+    if (!deviceClipIsARect)
+        return;
+    if (!sourceOpaqueRect.intersect(deviceClipRect))
+        return;
+
+    bool sourceOpaqueRectDrawsOpaque = paintIsOpaque(paint, FillOnly, 0);
+    bool sourceOpaqueRectXfersOpaque = xfermodeIsOpaque(paint, sourceOpaqueRectDrawsOpaque);
+    bool sourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, sourceOpaqueRectDrawsOpaque);
+
+    // If the layer's opaque area is being drawn opaque in the layer below, then mark it opaque. Otherwise,
+    // if it preserves opaque then keep the intersection of the two.
+    if (sourceOpaqueRectXfersOpaque)
+        markRectAsOpaque(sourceOpaqueRect);
+    else if (sourceOpaqueRectPreservesOpaque && sourceOpaqueRect.intersect(destinationOpaqueRect))
+        markRectAsOpaque(sourceOpaqueRect);
+}
+
 void OpaqueRegionSkia::markRectAsOpaque(const SkRect& rect)
 {
     // We want to keep track of an opaque region but bound its complexity at a constant size.
@@ -326,31 +357,33 @@
     // rectangle then we do that, as that is the cheapest way to increase the area returned
     // without increasing the complexity.
 
+    SkRect& opaqueRect = currentTrackingOpaqueRect(m_opaqueRect, m_canvasLayerStack);
+
     if (rect.isEmpty())
         return;
-    if (m_opaqueRect.contains(rect))
+    if (opaqueRect.contains(rect))
         return;
-    if (rect.contains(m_opaqueRect)) {
-        m_opaqueRect = rect;
+    if (rect.contains(opaqueRect)) {
+        opaqueRect = rect;
         return;
     }
 
-    if (rect.fTop <= m_opaqueRect.fTop && rect.fBottom >= m_opaqueRect.fBottom) {
-        if (rect.fLeft < m_opaqueRect.fLeft && rect.fRight >= m_opaqueRect.fLeft)
-            m_opaqueRect.fLeft = rect.fLeft;
-        if (rect.fRight > m_opaqueRect.fRight && rect.fLeft <= m_opaqueRect.fRight)
-            m_opaqueRect.fRight = rect.fRight;
-    } else if (rect.fLeft <= m_opaqueRect.fLeft && rect.fRight >= m_opaqueRect.fRight) {
-        if (rect.fTop < m_opaqueRect.fTop && rect.fBottom >= m_opaqueRect.fTop)
-            m_opaqueRect.fTop = rect.fTop;
-        if (rect.fBottom > m_opaqueRect.fBottom && rect.fTop <= m_opaqueRect.fBottom)
-            m_opaqueRect.fBottom = rect.fBottom;
+    if (rect.fTop <= opaqueRect.fTop && rect.fBottom >= opaqueRect.fBottom) {
+        if (rect.fLeft < opaqueRect.fLeft && rect.fRight >= opaqueRect.fLeft)
+            opaqueRect.fLeft = rect.fLeft;
+        if (rect.fRight > opaqueRect.fRight && rect.fLeft <= opaqueRect.fRight)
+            opaqueRect.fRight = rect.fRight;
+    } else if (rect.fLeft <= opaqueRect.fLeft && rect.fRight >= opaqueRect.fRight) {
+        if (rect.fTop < opaqueRect.fTop && rect.fBottom >= opaqueRect.fTop)
+            opaqueRect.fTop = rect.fTop;
+        if (rect.fBottom > opaqueRect.fBottom && rect.fTop <= opaqueRect.fBottom)
+            opaqueRect.fBottom = rect.fBottom;
     }
 
-    long opaqueArea = (long)m_opaqueRect.width() * (long)m_opaqueRect.height();
+    long opaqueArea = (long)opaqueRect.width() * (long)opaqueRect.height();
     long area = (long)rect.width() * (long)rect.height();
     if (area > opaqueArea)
-        m_opaqueRect = rect;
+        opaqueRect = rect;
 }
 
 void OpaqueRegionSkia::markRectAsNonOpaque(const SkRect& rect)
@@ -358,34 +391,43 @@
     // We want to keep as much of the current opaque rectangle as we can, so find the one largest
     // rectangle inside m_opaqueRect that does not intersect with |rect|.
 
-    if (rect.contains(m_opaqueRect)) {
-        m_opaqueRect.setEmpty();
+    SkRect& opaqueRect = currentTrackingOpaqueRect(m_opaqueRect, m_canvasLayerStack);
+
+    if (!SkRect::Intersects(rect, opaqueRect))
         return;
+    if (rect.contains(opaqueRect)) {
+        markAllAsNonOpaque();
+        return;
     }
 
-    int deltaLeft = rect.fLeft - m_opaqueRect.fLeft;
-    int deltaRight = m_opaqueRect.fRight - rect.fRight;
-    int deltaTop = rect.fTop - m_opaqueRect.fTop;
-    int deltaBottom = m_opaqueRect.fBottom - rect.fBottom;
+    int deltaLeft = rect.fLeft - opaqueRect.fLeft;
+    int deltaRight = opaqueRect.fRight - rect.fRight;
+    int deltaTop = rect.fTop - opaqueRect.fTop;
+    int deltaBottom = opaqueRect.fBottom - rect.fBottom;
 
-    // horizontal is the larger of the two rectangles to the left or to the right of |rect| and inside m_opaqueRect.
-    // vertical is the larger of the two rectangles above or below |rect| and inside m_opaqueRect.
-    SkRect horizontal = m_opaqueRect;
+    // horizontal is the larger of the two rectangles to the left or to the right of |rect| and inside opaqueRect.
+    // vertical is the larger of the two rectangles above or below |rect| and inside opaqueRect.
+    SkRect horizontal = opaqueRect;
     if (deltaTop > deltaBottom)
         horizontal.fBottom = rect.fTop;
     else
         horizontal.fTop = rect.fBottom;
-    SkRect vertical = m_opaqueRect;
+    SkRect vertical = opaqueRect;
     if (deltaLeft > deltaRight)
         vertical.fRight = rect.fLeft;
     else
         vertical.fLeft = rect.fRight;
 
     if ((long)horizontal.width() * (long)horizontal.height() > (long)vertical.width() * (long)vertical.height())
-        m_opaqueRect = horizontal;
+        opaqueRect = horizontal;
     else
-        m_opaqueRect = vertical;
+        opaqueRect = vertical;
 }
 
+void OpaqueRegionSkia::markAllAsNonOpaque()
+{
+    SkRect& opaqueRect = currentTrackingOpaqueRect(m_opaqueRect, m_canvasLayerStack);
+    opaqueRect.setEmpty();
+}
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/platform/graphics/skia/OpaqueRegionSkia.h (115138 => 115139)


--- trunk/Source/WebCore/platform/graphics/skia/OpaqueRegionSkia.h	2012-04-24 23:55:50 UTC (rev 115138)
+++ trunk/Source/WebCore/platform/graphics/skia/OpaqueRegionSkia.h	2012-04-24 23:58:04 UTC (rev 115139)
@@ -53,7 +53,7 @@
     IntRect asRect() const;
 
     void pushCanvasLayer(const SkPaint*);
-    void popCanvasLayer();
+    void popCanvasLayer(const PlatformContextSkia*);
 
     void setImageMask(const SkRect& imageOpaqueRect);
 
@@ -62,21 +62,16 @@
     void didDrawPoints(const PlatformContextSkia*, SkCanvas::PointMode, int numPoints, const SkPoint[], const SkPaint&);
     void didDrawBounded(const PlatformContextSkia*, const SkRect&, const SkPaint&);
 
-private:
     enum DrawType {
         FillOnly,
         FillOrStroke
     };
 
-    void didDraw(const PlatformContextSkia*, const SkRect&, const SkPaint&, const SkBitmap* sourceBitmap, bool fillsBounds, DrawType);
-    void didDrawUnbounded(const SkPaint&);
-    void markRectAsOpaque(const SkRect&);
-    void markRectAsNonOpaque(const SkRect&);
-
-    SkRect m_opaqueRect;
-
     struct CanvasLayerState {
-        CanvasLayerState() : hasImageMask(false) { }
+        CanvasLayerState()
+            : hasImageMask(false)
+            , opaqueRect(SkRect::MakeEmpty())
+        { }
 
         SkPaint paint;
 
@@ -84,8 +79,20 @@
         bool hasImageMask;
         // The opaque area in the image mask.
         SkRect imageOpaqueRect;
+
+        SkRect opaqueRect;
     };
 
+private:
+    void didDraw(const PlatformContextSkia*, const SkRect&, const SkPaint&, const SkBitmap* sourceBitmap, bool fillsBounds, DrawType);
+    void didDrawUnbounded(const SkPaint&);
+    void applyOpaqueRegionFromLayer(const PlatformContextSkia*, const SkRect& layerOpaqueRect, const SkPaint&);
+    void markRectAsOpaque(const SkRect&);
+    void markRectAsNonOpaque(const SkRect&);
+    void markAllAsNonOpaque();
+
+    SkRect m_opaqueRect;
+
     Vector<CanvasLayerState, 3> m_canvasLayerStack;
 };
 

Modified: trunk/Source/WebCore/platform/graphics/skia/PlatformContextSkia.cpp (115138 => 115139)


--- trunk/Source/WebCore/platform/graphics/skia/PlatformContextSkia.cpp	2012-04-24 23:55:50 UTC (rev 115138)
+++ trunk/Source/WebCore/platform/graphics/skia/PlatformContextSkia.cpp	2012-04-24 23:58:04 UTC (rev 115139)
@@ -248,7 +248,7 @@
 {
     m_canvas->restore();
     if (m_trackOpaqueRegion)
-        m_opaqueRegion.popCanvasLayer();
+        m_opaqueRegion.popCanvasLayer(this);
 }
 
 void PlatformContextSkia::beginLayerClippedToImage(const FloatRect& rect,

Modified: trunk/Source/WebKit/chromium/ChangeLog (115138 => 115139)


--- trunk/Source/WebKit/chromium/ChangeLog	2012-04-24 23:55:50 UTC (rev 115138)
+++ trunk/Source/WebKit/chromium/ChangeLog	2012-04-24 23:58:04 UTC (rev 115139)
@@ -1,3 +1,14 @@
+2012-04-24  Dana Jansens  <[email protected]>
+
+        [chromium] Image masks are considered opaque incorrectly
+        https://bugs.webkit.org/show_bug.cgi?id=84275
+
+        Reviewed by Adrienne Walker.
+
+        * tests/PlatformContextSkiaTest.cpp:
+        (WebCore):
+        (WebCore::TEST):
+
 2012-04-24  Alexis Menard  <[email protected]>
 
         Replace occurences of style selector from variables and methods names by style resolver.

Modified: trunk/Source/WebKit/chromium/tests/PlatformContextSkiaTest.cpp (115138 => 115139)


--- trunk/Source/WebKit/chromium/tests/PlatformContextSkiaTest.cpp	2012-04-24 23:55:50 UTC (rev 115138)
+++ trunk/Source/WebKit/chromium/tests/PlatformContextSkiaTest.cpp	2012-04-24 23:58:04 UTC (rev 115139)
@@ -39,9 +39,9 @@
 
 #define EXPECT_EQ_RECT(a, b) \
     EXPECT_EQ(a.x(), b.x()); \
-    EXPECT_EQ(a.maxX(), b.maxX()); \
     EXPECT_EQ(a.y(), b.y()); \
-    EXPECT_EQ(a.maxY(), b.maxY());
+    EXPECT_EQ(a.width(), b.width()); \
+    EXPECT_EQ(a.height(), b.height());
 
 #define EXPECT_PIXELS_MATCH(bitmap, opaqueRect) \
 { \
@@ -226,11 +226,92 @@
     context.save();
     context.clipToImageBuffer(alphaImage.get(), FloatRect(30, 30, 10, 10));
     context.fillRect(FloatRect(10, 10, 90, 90), opaque, ColorSpaceDeviceRGB, CompositeSourceOver);
+    context.restore();
     EXPECT_EQ_RECT(IntRect(), platformContext.opaqueRegion().asRect());
     EXPECT_PIXELS_MATCH(bitmap, platformContext.opaqueRegion().asRect());
-    context.restore();
 }
 
+TEST(PlatformContextSkiaTest, trackImageMask)
+{
+    SkBitmap bitmap;
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, 400, 400);
+    bitmap.allocPixels();
+    bitmap.eraseColor(0);
+    SkCanvas canvas(bitmap);
+
+    PlatformContextSkia platformContext(&canvas);
+    platformContext.setTrackOpaqueRegion(true);
+    GraphicsContext context(&platformContext);
+
+    Color opaque(1.0f, 0.0f, 0.0f, 1.0f);
+    Color alpha(0.0f, 0.0f, 0.0f, 0.0f);
+
+    // Image masks are done by drawing a bitmap into a transparency layer that uses DstIn to mask
+    // out a transparency layer below that is filled with the mask color. In the end this should
+    // not be marked opaque.
+
+    context.setCompositeOperation(CompositeSourceOver);
+    context.beginTransparencyLayer(1);
+    context.fillRect(FloatRect(10, 10, 10, 10), opaque, ColorSpaceDeviceRGB, CompositeSourceOver);
+
+    context.setCompositeOperation(CompositeDestinationIn);
+    context.beginTransparencyLayer(1);
+
+    OwnPtr<ImageBuffer> alphaImage = ImageBuffer::create(IntSize(100, 100));
+    alphaImage->context()->fillRect(IntRect(0, 0, 100, 100), alpha, ColorSpaceDeviceRGB);
+
+    context.setCompositeOperation(CompositeSourceOver);
+    context.drawImageBuffer(alphaImage.get(), ColorSpaceDeviceRGB, FloatRect(10, 10, 10, 10));
+
+    context.endTransparencyLayer();
+    context.endTransparencyLayer();
+
+    EXPECT_EQ_RECT(IntRect(), platformContext.opaqueRegion().asRect());
+    EXPECT_PIXELS_MATCH_EXACT(bitmap, platformContext.opaqueRegion().asRect());
+}
+
+TEST(PlatformContextSkiaTest, trackImageMaskWithOpaqueRect)
+{
+    SkBitmap bitmap;
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, 400, 400);
+    bitmap.allocPixels();
+    bitmap.eraseColor(0);
+    SkCanvas canvas(bitmap);
+
+    PlatformContextSkia platformContext(&canvas);
+    platformContext.setTrackOpaqueRegion(true);
+    GraphicsContext context(&platformContext);
+
+    Color opaque(1.0f, 0.0f, 0.0f, 1.0f);
+    Color alpha(0.0f, 0.0f, 0.0f, 0.0f);
+
+    // Image masks are done by drawing a bitmap into a transparency layer that uses DstIn to mask
+    // out a transparency layer below that is filled with the mask color. In the end this should
+    // not be marked opaque.
+
+    context.setCompositeOperation(CompositeSourceOver);
+    context.beginTransparencyLayer(1);
+    context.fillRect(FloatRect(10, 10, 10, 10), opaque, ColorSpaceDeviceRGB, CompositeSourceOver);
+
+    context.setCompositeOperation(CompositeDestinationIn);
+    context.beginTransparencyLayer(1);
+
+    OwnPtr<ImageBuffer> alphaImage = ImageBuffer::create(IntSize(100, 100));
+    alphaImage->context()->fillRect(IntRect(0, 0, 100, 100), alpha, ColorSpaceDeviceRGB);
+
+    context.setCompositeOperation(CompositeSourceOver);
+    context.drawImageBuffer(alphaImage.get(), ColorSpaceDeviceRGB, FloatRect(10, 10, 10, 10));
+
+    // We can't have an opaque mask actually, but we can pretend here like it would look if we did.
+    context.fillRect(FloatRect(12, 12, 3, 3), opaque, ColorSpaceDeviceRGB, CompositeSourceOver);
+
+    context.endTransparencyLayer();
+    context.endTransparencyLayer();
+
+    EXPECT_EQ_RECT(IntRect(12, 12, 3, 3), platformContext.opaqueRegion().asRect());
+    EXPECT_PIXELS_MATCH_EXACT(bitmap, platformContext.opaqueRegion().asRect());
+}
+
 TEST(PlatformContextSkiaTest, trackOpaqueJoinTest)
 {
     SkBitmap bitmap;
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to