<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);
 }