<https://webkit.org/b/120476> [CSS Masking] Implement luminance masking
Source/WebCore:
Added implementation for luminance masking. A luminance mask is applied by transforming its RGB values into
an alpha value, using luminance-to-alpha coefficients. Because this conversion is already implemented in
the ImageBuffer class, we used it in our implementation.
Patch by Andrei Parvu <parvu@adobe.com> on 2013-09-25
Reviewed by Dirk Schulze.
Tests: css3/masking/mask-luminance-gradient.html
css3/masking/mask-luminance-png.html
css3/masking/mask-luminance-svg.html
* platform/graphics/BitmapImage.cpp: Add a drawPattern method, in which an ImageBuffer is created and converted from alpha to luminance, if there is a luminance mask.
(WebCore::BitmapImage::BitmapImage):
(WebCore::BitmapImage::drawPattern):
* platform/graphics/BitmapImage.h:
* platform/graphics/GeneratorGeneratedImage.cpp: Convert the ImageBuffer to luminance, if necessary.
(WebCore::GeneratorGeneratedImage::drawPattern):
* platform/graphics/GraphicsContext.cpp: Add methods which set and check if a luminance mask is drawn.
(WebCore::GraphicsContext::setDrawLuminanceMask):
(WebCore::GraphicsContext::drawLuminanceMask):
* platform/graphics/GraphicsContext.h: Add property to ContextState for luminance drawing.
(WebCore::GraphicsContextState::GraphicsContextState):
* rendering/RenderBoxModelObject.cpp: Set the luminance property of the mask, if the layer has a mask source type of luminance.
(WebCore::RenderBoxModelObject::paintFillLayerExtended):
* svg/graphics/SVGImage.cpp: Convert the ImageBuffer to luminance, if necessary.
(WebCore::SVGImage::drawPatternForContainer):
* svg/graphics/SVGImageForContainer.cpp: Pass the luminance property to the SVG image.
(WebCore::SVGImageForContainer::drawPattern):
LayoutTests:
Added tests to verify the implementation of luminance masking.
Patch by Andrei Parvu <parvu@adobe.com> on 2013-09-25
Reviewed by Dirk Schulze.
* css3/masking/mask-luminance-gradient-expected.html: Added.
* css3/masking/mask-luminance-gradient.html: Added.
* css3/masking/mask-luminance-png.html: Added.
* css3/masking/mask-luminance-svg-expected.html: Added.
* css3/masking/mask-luminance-svg.html: Added.
* css3/masking/resources/circle-alpha.svg: Added.
* css3/masking/resources/circle2.svg: Added.
* css3/masking/resources/dice.png: Added.
* platform/mac/css3/masking/mask-luminance-png-expected.txt: Added.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@156391 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 7ca68e6..fbf924a 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,21 @@
+2013-09-25 Andrei Parvu <parvu@adobe.com>
+
+ <https://webkit.org/b/120476> [CSS Masking] Implement luminance masking
+
+ Added tests to verify the implementation of luminance masking.
+
+ Reviewed by Dirk Schulze.
+
+ * css3/masking/mask-luminance-gradient-expected.html: Added.
+ * css3/masking/mask-luminance-gradient.html: Added.
+ * css3/masking/mask-luminance-png.html: Added.
+ * css3/masking/mask-luminance-svg-expected.html: Added.
+ * css3/masking/mask-luminance-svg.html: Added.
+ * css3/masking/resources/circle-alpha.svg: Added.
+ * css3/masking/resources/circle2.svg: Added.
+ * css3/masking/resources/dice.png: Added.
+ * platform/mac/css3/masking/mask-luminance-png-expected.txt: Added.
+
2013-09-25 Krzysztof Czech <k.czech@samsung.com>
[EFL] Updated accessibility expectations after r154781 and r155599
diff --git a/LayoutTests/css3/masking/mask-luminance-gradient-expected.html b/LayoutTests/css3/masking/mask-luminance-gradient-expected.html
new file mode 100644
index 0000000..c3a9862
--- /dev/null
+++ b/LayoutTests/css3/masking/mask-luminance-gradient-expected.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <style>
+ #back {
+ width: 1000px;
+ height: 600px;
+ background-color: green;
+ }
+ #front {
+ width: 800px;
+ height: 400px;
+ background-color: red;
+ border: 50px solid blue;
+ padding: 50px;
+ -webkit-mask-image: linear-gradient(45deg, black, transparent 100%);
+ -webkit-mask-source-type: alpha;
+ -webkit-mask-size: 200px auto;
+ -webkit-mask-repeat: repeat;
+ -webkit-mask-origin: border-box;
+ -webkit-mask-clip: border-box;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div id="back">
+ <div id="front" />
+ </div>
+ </body>
+</html>
+
diff --git a/LayoutTests/css3/masking/mask-luminance-gradient.html b/LayoutTests/css3/masking/mask-luminance-gradient.html
new file mode 100644
index 0000000..3a2e951
--- /dev/null
+++ b/LayoutTests/css3/masking/mask-luminance-gradient.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <style>
+ #back {
+ width: 1000px;
+ height: 600px;
+ background-color: green;
+ }
+ #front {
+ width: 800px;
+ height: 400px;
+ background-color: red;
+ border: 50px solid blue;
+ padding: 50px;
+ -webkit-mask-image: linear-gradient(45deg, white, black);
+ -webkit-mask-source-type: luminance;
+ -webkit-mask-size: 200px auto;
+ -webkit-mask-repeat: repeat;
+ -webkit-mask-origin: border-box;
+ -webkit-mask-clip: border-box;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div id="back">
+ <div id="front" />
+ </div>
+ </body>
+</html>
+
diff --git a/LayoutTests/css3/masking/mask-luminance-png.html b/LayoutTests/css3/masking/mask-luminance-png.html
new file mode 100644
index 0000000..6d92a65
--- /dev/null
+++ b/LayoutTests/css3/masking/mask-luminance-png.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <style>
+ #back {
+ width: 1000px;
+ height: 600px;
+ background-color: green;
+ }
+ #front {
+ width: 800px;
+ height: 400px;
+ background-color: red;
+ border: 50px solid blue;
+ padding: 50px;
+ -webkit-mask-image: url(resources/dice.png);
+ -webkit-mask-source-type: luminance;
+ -webkit-mask-size: 200px auto;
+ -webkit-mask-repeat: repeat;
+ -webkit-mask-origin: border-box;
+ -webkit-mask-clip: border-box;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div id="back">
+ <div id="front" />
+ </div>
+ </body>
+</html>
+
diff --git a/LayoutTests/css3/masking/mask-luminance-svg-expected.html b/LayoutTests/css3/masking/mask-luminance-svg-expected.html
new file mode 100644
index 0000000..561315e
--- /dev/null
+++ b/LayoutTests/css3/masking/mask-luminance-svg-expected.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <style>
+ #back {
+ width: 1000px;
+ height: 600px;
+ background-color: green;
+ }
+ #front {
+ width: 800px;
+ height: 400px;
+ background-color: red;
+ border: 50px solid blue;
+ padding: 50px;
+ -webkit-mask-image: url(resources/circle-alpha.svg);
+ -webkit-mask-source-type: alpha;
+ -webkit-mask-size: 200px auto;
+ -webkit-mask-repeat: repeat;
+ -webkit-mask-origin: border-box;
+ -webkit-mask-clip: border-box;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div id="back">
+ <div id="front" />
+ </div>
+ </body>
+</html>
+
diff --git a/LayoutTests/css3/masking/mask-luminance-svg.html b/LayoutTests/css3/masking/mask-luminance-svg.html
new file mode 100644
index 0000000..684f4f0
--- /dev/null
+++ b/LayoutTests/css3/masking/mask-luminance-svg.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <style>
+ #back {
+ width: 1000px;
+ height: 600px;
+ background-color: green;
+ }
+ #front {
+ width: 800px;
+ height: 400px;
+ background-color: red;
+ border: 50px solid blue;
+ padding: 50px;
+ -webkit-mask-image: url(resources/circle2.svg);
+ -webkit-mask-source-type: luminance;
+ -webkit-mask-size: 200px auto;
+ -webkit-mask-repeat: repeat;
+ -webkit-mask-origin: border-box;
+ -webkit-mask-clip: border-box;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div id="back">
+ <div id="front" />
+ </div>
+ </body>
+</html>
+
diff --git a/LayoutTests/css3/masking/resources/circle-alpha.svg b/LayoutTests/css3/masking/resources/circle-alpha.svg
new file mode 100644
index 0000000..b317147
--- /dev/null
+++ b/LayoutTests/css3/masking/resources/circle-alpha.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="288">
+ <circle cx="180" cy="144" r="80" style="fill:rgb(0,0,0)" />
+</svg>
diff --git a/LayoutTests/css3/masking/resources/circle2.svg b/LayoutTests/css3/masking/resources/circle2.svg
new file mode 100644
index 0000000..aabab3d
--- /dev/null
+++ b/LayoutTests/css3/masking/resources/circle2.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="288">
+ <rect width="360" height="288" style="fill:rgb(0,0,0)" />
+ <circle cx="180" cy="144" r="80" style="fill:rgb(255,255,255)" />
+</svg>
+
diff --git a/LayoutTests/css3/masking/resources/dice.png b/LayoutTests/css3/masking/resources/dice.png
new file mode 100644
index 0000000..f18b814
--- /dev/null
+++ b/LayoutTests/css3/masking/resources/dice.png
Binary files differ
diff --git a/LayoutTests/platform/mac/css3/masking/mask-luminance-png-expected.txt b/LayoutTests/platform/mac/css3/masking/mask-luminance-png-expected.txt
new file mode 100644
index 0000000..360d299
--- /dev/null
+++ b/LayoutTests/platform/mac/css3/masking/mask-luminance-png-expected.txt
@@ -0,0 +1,8 @@
+layer at (0,0) size 1008x616
+ RenderView at (0,0) size 785x585
+layer at (0,0) size 785x616
+ RenderBlock {HTML} at (0,0) size 785x616
+ RenderBody {BODY} at (8,8) size 769x600
+ RenderBlock {DIV} at (0,0) size 1000x600 [bgcolor=#008000]
+layer at (8,8) size 1000x600
+ RenderBlock {DIV} at (0,0) size 1000x600 [bgcolor=#FF0000] [border: (50px solid #0000FF)]
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index bf8c425..69717cf 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,35 @@
+2013-09-25 Andrei Parvu <parvu@adobe.com>
+
+ <https://webkit.org/b/120476> [CSS Masking] Implement luminance masking
+
+ Added implementation for luminance masking. A luminance mask is applied by transforming its RGB values into
+ an alpha value, using luminance-to-alpha coefficients. Because this conversion is already implemented in
+ the ImageBuffer class, we used it in our implementation.
+
+ Reviewed by Dirk Schulze.
+
+ Tests: css3/masking/mask-luminance-gradient.html
+ css3/masking/mask-luminance-png.html
+ css3/masking/mask-luminance-svg.html
+
+ * platform/graphics/BitmapImage.cpp: Add a drawPattern method, in which an ImageBuffer is created and converted from alpha to luminance, if there is a luminance mask.
+ (WebCore::BitmapImage::BitmapImage):
+ (WebCore::BitmapImage::drawPattern):
+ * platform/graphics/BitmapImage.h:
+ * platform/graphics/GeneratorGeneratedImage.cpp: Convert the ImageBuffer to luminance, if necessary.
+ (WebCore::GeneratorGeneratedImage::drawPattern):
+ * platform/graphics/GraphicsContext.cpp: Add methods which set and check if a luminance mask is drawn.
+ (WebCore::GraphicsContext::setDrawLuminanceMask):
+ (WebCore::GraphicsContext::drawLuminanceMask):
+ * platform/graphics/GraphicsContext.h: Add property to ContextState for luminance drawing.
+ (WebCore::GraphicsContextState::GraphicsContextState):
+ * rendering/RenderBoxModelObject.cpp: Set the luminance property of the mask, if the layer has a mask source type of luminance.
+ (WebCore::RenderBoxModelObject::paintFillLayerExtended):
+ * svg/graphics/SVGImage.cpp: Convert the ImageBuffer to luminance, if necessary.
+ (WebCore::SVGImage::drawPatternForContainer):
+ * svg/graphics/SVGImageForContainer.cpp: Pass the luminance property to the SVG image.
+ (WebCore::SVGImageForContainer::drawPattern):
+
2013-09-25 Andreas Kling <akling@apple.com>
Remove EventPathWalker.
diff --git a/Source/WebCore/platform/graphics/BitmapImage.cpp b/Source/WebCore/platform/graphics/BitmapImage.cpp
index a0ee0dd..5395af3 100644
--- a/Source/WebCore/platform/graphics/BitmapImage.cpp
+++ b/Source/WebCore/platform/graphics/BitmapImage.cpp
@@ -28,6 +28,7 @@
#include "BitmapImage.h"
#include "FloatRect.h"
+#include "ImageBuffer.h"
#include "ImageObserver.h"
#include "IntRect.h"
#include "MIMETypeRegistry.h"
@@ -58,6 +59,7 @@
, m_sizeAvailable(false)
, m_hasUniformFrameSize(true)
, m_haveFrameCount(false)
+ , m_cachedImage(0)
{
}
@@ -492,6 +494,40 @@
return m_decodedSize;
}
+void BitmapImage::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const AffineTransform& transform,
+ const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect, BlendMode blendMode)
+{
+ if (tileRect.isEmpty())
+ return;
+
+ if (!ctxt->drawLuminanceMask()) {
+ Image::drawPattern(ctxt, tileRect, transform, phase, styleColorSpace, op, destRect, blendMode);
+ return;
+ }
+ if (!m_cachedImage) {
+ OwnPtr<ImageBuffer> buffer = ImageBuffer::create(expandedIntSize(tileRect.size()));
+ ASSERT(buffer.get() > 0);
+
+ ImageObserver* observer = imageObserver();
+ ASSERT(observer);
+
+ // Temporarily reset image observer, we don't want to receive any changeInRect() calls due to this relayout.
+ setImageObserver(0);
+
+ draw(buffer->context(), tileRect, tileRect, styleColorSpace, op, blendMode);
+
+ setImageObserver(observer);
+ buffer->convertToLuminanceMask();
+
+ m_cachedImage = buffer->copyImage(DontCopyBackingStore, Unscaled);
+ m_cachedImage->setSpaceSize(spaceSize());
+
+ setImageObserver(observer);
+ }
+
+ ctxt->setDrawLuminanceMask(false);
+ m_cachedImage->drawPattern(ctxt, tileRect, transform, phase, styleColorSpace, op, destRect, blendMode);
+}
void BitmapImage::advanceAnimation(Timer<BitmapImage>*)
diff --git a/Source/WebCore/platform/graphics/BitmapImage.h b/Source/WebCore/platform/graphics/BitmapImage.h
index 20fe46e..7e6abac 100644
--- a/Source/WebCore/platform/graphics/BitmapImage.h
+++ b/Source/WebCore/platform/graphics/BitmapImage.h
@@ -136,6 +136,9 @@
virtual unsigned decodedSize() const OVERRIDE;
+ virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const AffineTransform& patternTransform,
+ const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator, const FloatRect& destRect, BlendMode = BlendModeNormal) OVERRIDE;
+
#if PLATFORM(MAC)
// Accessors for native image formats.
virtual NSImage* getNSImage() OVERRIDE;
@@ -296,6 +299,8 @@
bool m_sizeAvailable : 1; // Whether or not we can obtain the size of the first image frame yet from ImageIO.
mutable bool m_hasUniformFrameSize : 1;
mutable bool m_haveFrameCount : 1;
+
+ RefPtr<Image> m_cachedImage;
};
}
diff --git a/Source/WebCore/platform/graphics/GradientImage.cpp b/Source/WebCore/platform/graphics/GradientImage.cpp
index f28916d..9b2d15c 100644
--- a/Source/WebCore/platform/graphics/GradientImage.cpp
+++ b/Source/WebCore/platform/graphics/GradientImage.cpp
@@ -72,9 +72,14 @@
m_cachedGeneratorHash = generatorHash;
m_cachedAdjustedSize = adjustedSize;
+
+ if (destContext->drawLuminanceMask())
+ m_cachedImageBuffer->convertToLuminanceMask();
}
m_cachedImageBuffer->setSpaceSize(spaceSize());
+ destContext->setDrawLuminanceMask(false);
+
// Tile the image buffer into the context.
m_cachedImageBuffer->drawPattern(destContext, adjustedSrcRect, adjustedPatternCTM, phase, styleColorSpace, compositeOp, destRect);
}
diff --git a/Source/WebCore/platform/graphics/GraphicsContext.cpp b/Source/WebCore/platform/graphics/GraphicsContext.cpp
index 2d572d3..8c5b5c3 100644
--- a/Source/WebCore/platform/graphics/GraphicsContext.cpp
+++ b/Source/WebCore/platform/graphics/GraphicsContext.cpp
@@ -738,6 +738,16 @@
return m_state.blendMode;
}
+void GraphicsContext::setDrawLuminanceMask(bool drawLuminanceMask)
+{
+ m_state.drawLuminanceMask = drawLuminanceMask;
+}
+
+bool GraphicsContext::drawLuminanceMask() const
+{
+ return m_state.drawLuminanceMask;
+}
+
#if !USE(CG)
// Implement this if you want to go ahead and push the drawing mode into your native context
// immediately.
diff --git a/Source/WebCore/platform/graphics/GraphicsContext.h b/Source/WebCore/platform/graphics/GraphicsContext.h
index 8ec2c59..0eb5136 100644
--- a/Source/WebCore/platform/graphics/GraphicsContext.h
+++ b/Source/WebCore/platform/graphics/GraphicsContext.h
@@ -150,6 +150,7 @@
// but we need to preserve this buggy behavior for canvas and -webkit-box-shadow.
, shadowsUseLegacyRadius(false)
#endif
+ , drawLuminanceMask(false)
{
}
@@ -188,6 +189,7 @@
#if USE(CG)
bool shadowsUseLegacyRadius : 1;
#endif
+ bool drawLuminanceMask : 1;
};
class GraphicsContext {
@@ -382,6 +384,9 @@
CompositeOperator compositeOperation() const;
BlendMode blendModeOperation() const;
+ void setDrawLuminanceMask(bool);
+ bool drawLuminanceMask() const;
+
void clip(const Path&, WindRule = RULE_EVENODD);
// This clip function is used only by <canvas> code. It allows
diff --git a/Source/WebCore/platform/graphics/Image.cpp b/Source/WebCore/platform/graphics/Image.cpp
index 8b6efc3..0abe883 100644
--- a/Source/WebCore/platform/graphics/Image.cpp
+++ b/Source/WebCore/platform/graphics/Image.cpp
@@ -120,7 +120,7 @@
oneTileRect.setSize(scaledTileSize);
// Check and see if a single draw of the image can cover the entire area we are supposed to tile.
- if (oneTileRect.contains(destRect)) {
+ if (oneTileRect.contains(destRect) && !ctxt->drawLuminanceMask()) {
FloatRect visibleSrcRect;
visibleSrcRect.setX((destRect.x() - oneTileRect.x()) / scale.width());
visibleSrcRect.setY((destRect.y() - oneTileRect.y()) / scale.height());
diff --git a/Source/WebCore/rendering/RenderBoxModelObject.cpp b/Source/WebCore/rendering/RenderBoxModelObject.cpp
index caa7b2e..8c06781 100644
--- a/Source/WebCore/rendering/RenderBoxModelObject.cpp
+++ b/Source/WebCore/rendering/RenderBoxModelObject.cpp
@@ -797,6 +797,7 @@
CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer->composite() : op;
RenderObject* clientForBackgroundImage = backgroundObject ? backgroundObject : this;
RefPtr<Image> image = bgImage->image(clientForBackgroundImage, geometry.tileSize());
+ context->setDrawLuminanceMask(bgLayer->maskSourceType() == MaskLuminance);
bool useLowQualityScaling = shouldPaintAtLowQuality(context, image.get(), bgLayer, geometry.tileSize());
if (image.get())
image->setSpaceSize(geometry.spaceSize());
diff --git a/Source/WebCore/svg/graphics/SVGImage.cpp b/Source/WebCore/svg/graphics/SVGImage.cpp
index 5b2e01c..75e12bd 100644
--- a/Source/WebCore/svg/graphics/SVGImage.cpp
+++ b/Source/WebCore/svg/graphics/SVGImage.cpp
@@ -201,6 +201,9 @@
if (!buffer) // Failed to allocate buffer.
return;
drawForContainer(buffer->context(), containerSize, zoom, imageBufferSize, zoomedContainerRect, ColorSpaceDeviceRGB, CompositeSourceOver, BlendModeNormal);
+ if (context->drawLuminanceMask())
+ buffer->convertToLuminanceMask();
+
RefPtr<Image> image = buffer->copyImage(DontCopyBackingStore, Unscaled);
image->setSpaceSize(spaceSize());
@@ -210,6 +213,7 @@
AffineTransform unscaledPatternTransform(patternTransform);
unscaledPatternTransform.scale(1 / imageBufferScale.width(), 1 / imageBufferScale.height());
+ context->setDrawLuminanceMask(false);
image->drawPattern(context, scaledSrcRect, unscaledPatternTransform, phase, colorSpace, compositeOp, dstRect);
}