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;