Title: [166836] trunk
Revision
166836
Author
k...@webkit.org
Date
2014-04-05 12:57:00 -0700 (Sat, 05 Apr 2014)

Log Message

Canvas stroke and strokeRect with SourceIn, DestinationIn, SourceOut, DestinationAtop and Copy have errors
https://bugs.webkit.org/show_bug.cgi?id=66762

Reviewed by Andreas Kling.

Source/WebCore:

For fill operations we create a new ImageBuffer to ensure that the results are composited
correctly according to the Canvas spec. This patch creates a new transparency layer
for stroking on certain compositing operators to archive the same. This makes WebKit's
behavior interoperable with Firefox and IE as well.

Test: fast/fast/canvas/canvas-composite-stroke-alpha.html

* html/canvas/CanvasRenderingContext2D.cpp:
(WebCore::CanvasRenderingContext2D::strokeInternal):
(WebCore::CanvasRenderingContext2D::strokeRect):

LayoutTests:

330 tests based on canvas-composite-alpha to test correct compositing behavior with
strokeRect() and stroke().

* fast/canvas/canvas-composite-stroke-alpha-expected.txt: Added.
* fast/canvas/canvas-composite-stroke-alpha.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (166835 => 166836)


--- trunk/LayoutTests/ChangeLog	2014-04-05 19:54:18 UTC (rev 166835)
+++ trunk/LayoutTests/ChangeLog	2014-04-05 19:57:00 UTC (rev 166836)
@@ -1,3 +1,16 @@
+2014-04-05  Dirk Schulze  <k...@webkit.org>
+
+        Canvas stroke and strokeRect with SourceIn, DestinationIn, SourceOut, DestinationAtop and Copy have errors
+        https://bugs.webkit.org/show_bug.cgi?id=66762
+
+        Reviewed by Andreas Kling.
+
+        330 tests based on canvas-composite-alpha to test correct compositing behavior with
+        strokeRect() and stroke().
+
+        * fast/canvas/canvas-composite-stroke-alpha-expected.txt: Added.
+        * fast/canvas/canvas-composite-stroke-alpha.html: Added.
+
 2014-04-05  Jeongeun Kim  <je_julie....@samsung.com>
 
         bad values from HTMLImageElement x and y attributes (CSSOM-View)

Added: trunk/LayoutTests/fast/canvas/canvas-composite-stroke-alpha-expected.txt (0 => 166836)


--- trunk/LayoutTests/fast/canvas/canvas-composite-stroke-alpha-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/canvas/canvas-composite-stroke-alpha-expected.txt	2014-04-05 19:57:00 UTC (rev 166836)
@@ -0,0 +1,363 @@
+This test exercises a bunch of alpha composition checks with stroking. The top-left rectangles are the source images and bottom-right rectangles are the destination images.
+
+Tests for stroke rect
+
+src 255, 0, 0, 255	src 255, 0, 0, 255	src 255, 0, 0, 255	src 0, 255, 0, 0	src 0, 255, 0, 1	src 255, 0, 0, 0	src 255, 0, 0, 127	src 255, 0, 0, 255	src 255, 0, 0, 127	src 127, 0, 0, 255	src 127, 0, 0, 127	src 255, 0, 0, 127	src 255, 127, 0, 32	src 255, 0, 0, 191	src 255, 0, 255, 191
+dst 0, 255, 0, 255	dst 0, 255, 0, 0	dst 0, 255, 0, 1	dst 255, 0, 0, 255	dst 255, 0, 0, 255	dst 0, 255, 0, 255	dst 0, 255, 0, 127	dst 0, 255, 0, 127	dst 0, 255, 0, 255	dst 0, 127, 0, 127	dst 0, 127, 0, 255	dst 255, 0, 0, 63	dst 255, 63, 0, 63	dst 0, 255, 0, 127	dst 0, 255, 255, 127
+source-over																														
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+source-in																														
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+source-out																														
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+source-atop																														
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+destination-over																														
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+destination-in																														
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+destination-out																														
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+destination-atop																														
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+lighter																														
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+copy																														
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+xor																														
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+Tests for path and stroke
+
+src 255, 0, 0, 255	src 255, 0, 0, 255	src 255, 0, 0, 255	src 0, 255, 0, 0	src 0, 255, 0, 1	src 255, 0, 0, 0	src 255, 0, 0, 127	src 255, 0, 0, 255	src 255, 0, 0, 127	src 127, 0, 0, 255	src 127, 0, 0, 127	src 255, 0, 0, 127	src 255, 127, 0, 32	src 255, 0, 0, 191	src 255, 0, 255, 191
+dst 0, 255, 0, 255	dst 0, 255, 0, 0	dst 0, 255, 0, 1	dst 255, 0, 0, 255	dst 255, 0, 0, 255	dst 0, 255, 0, 255	dst 0, 255, 0, 127	dst 0, 255, 0, 127	dst 0, 255, 0, 255	dst 0, 127, 0, 127	dst 0, 127, 0, 255	dst 255, 0, 0, 63	dst 255, 63, 0, 63	dst 0, 255, 0, 127	dst 0, 255, 255, 127
+source-over																														
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+source-in																														
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+source-out																														
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+source-atop																														
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+destination-over																														
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+destination-in																														
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+destination-out																														
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+destination-atop																														
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+lighter																														
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+copy																														
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+xor																														
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+

Added: trunk/LayoutTests/fast/canvas/canvas-composite-stroke-alpha.html (0 => 166836)


--- trunk/LayoutTests/fast/canvas/canvas-composite-stroke-alpha.html	                        (rev 0)
+++ trunk/LayoutTests/fast/canvas/canvas-composite-stroke-alpha.html	2014-04-05 19:57:00 UTC (rev 166836)
@@ -0,0 +1,448 @@
+<html>
+  <head>
+    <title>A canvas globalCompositeOperation test with alpha blending</title>
+    <!-- This test was inspired by http://canvex.lazyilluminati.com/misc/compositex.html -->
+    <script type="application/x-_javascript_">
+      if (window.testRunner) {
+          testRunner.dumpAsText();
+          testRunner.waitUntilDone();
+      }
+
+      var compositeTypes = [
+          'source-over','source-in','source-out','source-atop',
+          'destination-over','destination-in','destination-out','destination-atop',
+          'lighter','copy','xor'
+      ];
+      var inputColors = [
+          { source: [255, 0, 0, 255], destination: [0, 255, 0, 255] },
+          { source: [255, 0, 0, 255], destination: [0, 255, 0, 0] },
+          { source: [255, 0, 0, 255], destination: [0, 255, 0, 1] },
+          { source: [0, 255, 0, 0], destination: [255, 0, 0, 255] },
+          { source: [0, 255, 0, 1], destination: [255, 0, 0, 255] },
+          { source: [255, 0, 0, 0], destination: [0, 255, 0, 255] },
+          { source: [255, 0, 0, 127], destination: [0, 255, 0, 127] },
+          { source: [255, 0, 0, 255], destination: [0, 255, 0, 127] },
+          { source: [255, 0, 0, 127], destination: [0, 255, 0, 255] },
+          { source: [127, 0, 0, 255], destination: [0, 127, 0, 127] },
+          { source: [127, 0, 0, 127], destination: [0, 127, 0, 255] },
+          { source: [255, 0, 0, 127], destination: [255, 0, 0, 63] },
+          { source: [255, 127, 0, 32], destination: [255, 63, 0, 63] },
+          { source: [255, 0, 0, 191], destination: [0, 255, 0, 127] },
+          { source: [255, 0, 255, 191], destination: [0, 255, 255, 127] }
+      ];
+      var expectedColors = [
+          [
+              { source: [255, 0, 0, 255], composition: [255, 0, 0, 255], destination: [0, 255, 0, 255] },
+              { source: [255, 0, 0, 255], composition: [255, 0, 0, 255], destination: [0, 0, 0, 0] },
+              { source: [255, 0, 0, 255], composition: [255, 0, 0, 255], destination: [0, 255, 0, 1] },
+              { source: [0, 0, 0, 0], composition: [255, 0, 0, 255], destination: [255, 0, 0, 255] },
+              { source: [0, 255, 0, 1], composition: [254, 1, 0, 255], destination: [255, 0, 0, 255] },
+              { source: [0, 0, 0, 0], composition: [0, 255, 0, 255], destination: [0, 255, 0, 255] },
+              { source: [255, 0, 0, 127], composition: [170, 84, 0, 190], destination: [0, 255, 0, 127] },
+              { source: [255, 0, 0, 255], composition: [255, 0, 0, 255], destination: [0, 255, 0, 127] },
+              { source: [255, 0, 0, 127], composition: [127, 128, 0, 255], destination: [0, 255, 0, 255] },
+              { source: [127, 0, 0, 255], composition: [127, 0, 0, 255], destination: [0, 126, 0, 127] },
+              { source: [126, 0, 0, 127], composition: [63, 63, 0, 255], destination: [0, 127, 0, 255] },
+              { source: [255, 0, 0, 127], composition: [255, 0, 0, 158], destination: [255, 0, 0, 63] },
+              { source: [255, 127, 0, 32], composition: [255, 85, 0, 87], destination: [255, 64, 0, 63] },
+              { source: [255, 0, 0, 191], composition: [219, 35, 0, 222], destination: [0, 255, 0, 127] },
+              { source: [255, 0, 255, 191], composition: [219, 35, 255, 222], destination: [0, 255, 255, 127] }
+          ],
+          [
+              { source: [0, 0, 0, 0], composition: [255, 0, 0, 255], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [0, 0, 0, 0], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [0, 0, 0, 0], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [0, 0, 0, 0], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [0, 0, 0, 0], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [0, 0, 0, 0], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [255, 0, 0, 64], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [255, 0, 0, 127], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [255, 0, 0, 127], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [128, 0, 0, 127], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [126, 0, 0, 127], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [255, 0, 0, 32], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [255, 127, 0, 8], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [255, 0, 0, 96], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [255, 0, 255, 96], destination: [0, 0, 0, 0] }
+          ],
+          [
+              { source: [255, 0, 0, 255], composition: [0, 0, 0, 0], destination: [0, 0, 0, 0] },
+              { source: [255, 0, 0, 255], composition: [255, 0, 0, 255], destination: [0, 0, 0, 0] },
+              { source: [255, 0, 0, 255], composition: [255, 0, 0, 254], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [0, 0, 0, 0], destination: [0, 0, 0, 0] },
+              { source: [0, 255, 0, 1], composition: [0, 0, 0, 0], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [0, 0, 0, 0], destination: [0, 0, 0, 0] },
+              { source: [255, 0, 0, 127], composition: [255, 0, 0, 64], destination: [0, 0, 0, 0] },
+              { source: [255, 0, 0, 255], composition: [255, 0, 0, 128], destination: [0, 0, 0, 0] },
+              { source: [255, 0, 0, 127], composition: [0, 0, 0, 0], destination: [0, 0, 0, 0] },
+              { source: [127, 0, 0, 255], composition: [127, 0, 0, 128], destination: [0, 0, 0, 0] },
+              { source: [126, 0, 0, 127], composition: [0, 0, 0, 0], destination: [0, 0, 0, 0] },
+              { source: [255, 0, 0, 127], composition: [255, 0, 0, 96], destination: [0, 0, 0, 0] },
+              { source: [255, 127, 0, 32], composition: [255, 132, 0, 25], destination: [0, 0, 0, 0] },
+              { source: [255, 0, 0, 191], composition: [255, 0, 0, 96], destination: [0, 0, 0, 0] },
+              { source: [255, 0, 255, 191], composition: [255, 0, 255, 96], destination: [0, 0, 0, 0] }
+          ],
+          [
+              { source: [0, 0, 0, 0], composition: [255, 0, 0, 255], destination: [0, 255, 0, 255] },
+              { source: [0, 0, 0, 0], composition: [0, 0, 0, 0], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [255, 0, 0, 1], destination: [0, 255, 0, 1] },
+              { source: [0, 0, 0, 0], composition: [255, 0, 0, 255], destination: [255, 0, 0, 255] },
+              { source: [0, 0, 0, 0], composition: [254, 1, 0, 255], destination: [255, 0, 0, 255] },
+              { source: [0, 0, 0, 0], composition: [0, 255, 0, 255], destination: [0, 255, 0, 255] },
+              { source: [0, 0, 0, 0], composition: [126, 126, 0, 127], destination: [0, 255, 0, 127] },
+              { source: [0, 0, 0, 0], composition: [255, 0, 0, 127], destination: [0, 255, 0, 127] },
+              { source: [0, 0, 0, 0], composition: [127, 128, 0, 255], destination: [0, 255, 0, 255] },
+              { source: [0, 0, 0, 0], composition: [128, 0, 0, 127], destination: [0, 126, 0, 127] },
+              { source: [0, 0, 0, 0], composition: [63, 63, 0, 255], destination: [0, 127, 0, 255] },
+              { source: [0, 0, 0, 0], composition: [255, 0, 0, 63], destination: [255, 0, 0, 63] },
+              { source: [0, 0, 0, 0], composition: [255, 68, 0, 63], destination: [255, 64, 0, 63] },
+              { source: [0, 0, 0, 0], composition: [190, 62, 0, 127], destination: [0, 255, 0, 127] },
+              { source: [0, 0, 0, 0], composition: [190, 62, 255, 127], destination: [0, 255, 255, 127] }
+          ],
+          [
+              { source: [255, 0, 0, 255], composition: [0, 255, 0, 255], destination: [0, 255, 0, 255] },
+              { source: [255, 0, 0, 255], composition: [255, 0, 0, 255], destination: [0, 0, 0, 0] },
+              { source: [255, 0, 0, 255], composition: [254, 1, 0, 255], destination: [0, 255, 0, 1] },
+              { source: [0, 0, 0, 0], composition: [255, 0, 0, 255], destination: [255, 0, 0, 255] },
+              { source: [0, 255, 0, 1], composition: [255, 0, 0, 255], destination: [255, 0, 0, 255] },
+              { source: [0, 0, 0, 0], composition: [0, 255, 0, 255], destination: [0, 255, 0, 255] },
+              { source: [255, 0, 0, 127], composition: [84, 170, 0, 190], destination: [0, 255, 0, 127] },
+              { source: [255, 0, 0, 255], composition: [128, 127, 0, 255], destination: [0, 255, 0, 127] },
+              { source: [255, 0, 0, 127], composition: [0, 255, 0, 255], destination: [0, 255, 0, 255] },
+              { source: [127, 0, 0, 255], composition: [63, 63, 0, 255], destination: [0, 126, 0, 127] },
+              { source: [126, 0, 0, 127], composition: [0, 127, 0, 255], destination: [0, 127, 0, 255] },
+              { source: [255, 0, 0, 127], composition: [255, 0, 0, 158], destination: [255, 0, 0, 63] },
+              { source: [255, 127, 0, 32], composition: [255, 82, 0, 87], destination: [255, 64, 0, 63] },
+              { source: [255, 0, 0, 191], composition: [109, 145, 0, 222], destination: [0, 255, 0, 127] },
+              { source: [255, 0, 255, 191], composition: [109, 145, 255, 222], destination: [0, 255, 255, 127] }
+          ],
+          [
+              { source: [0, 0, 0, 0], composition: [0, 255, 0, 255], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [0, 0, 0, 0], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [0, 255, 0, 1], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [0, 0, 0, 0], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [255, 0, 0, 1], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [0, 0, 0, 0], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [0, 255, 0, 64], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [0, 255, 0, 127], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [0, 255, 0, 127], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [0, 126, 0, 127], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [0, 128, 0, 127], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [255, 0, 0, 32], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [255, 95, 0, 8], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [0, 255, 0, 96], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [0, 255, 255, 96], destination: [0, 0, 0, 0] }
+          ],
+          [
+              { source: [0, 0, 0, 0], composition: [0, 0, 0, 0], destination: [0, 255, 0, 255] },
+              { source: [0, 0, 0, 0], composition: [0, 0, 0, 0], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [0, 0, 0, 0], destination: [0, 255, 0, 1] },
+              { source: [0, 0, 0, 0], composition: [255, 0, 0, 255], destination: [255, 0, 0, 255] },
+              { source: [0, 0, 0, 0], composition: [255, 0, 0, 254], destination: [255, 0, 0, 255] },
+              { source: [0, 0, 0, 0], composition: [0, 255, 0, 255], destination: [0, 255, 0, 255] },
+              { source: [0, 0, 0, 0], composition: [0, 255, 0, 64], destination: [0, 255, 0, 127] },
+              { source: [0, 0, 0, 0], composition: [0, 0, 0, 0], destination: [0, 255, 0, 127] },
+              { source: [0, 0, 0, 0], composition: [0, 255, 0, 128], destination: [0, 255, 0, 255] },
+              { source: [0, 0, 0, 0], composition: [0, 0, 0, 0], destination: [0, 126, 0, 127] },
+              { source: [0, 0, 0, 0], composition: [0, 127, 0, 128], destination: [0, 127, 0, 255] },
+              { source: [0, 0, 0, 0], composition: [255, 0, 0, 32], destination: [255, 0, 0, 63] },
+              { source: [0, 0, 0, 0], composition: [255, 63, 0, 56], destination: [255, 64, 0, 63] },
+              { source: [0, 0, 0, 0], composition: [0, 255, 0, 32], destination: [0, 255, 0, 127] },
+              { source: [0, 0, 0, 0], composition: [0, 255, 255, 32], destination: [0, 255, 255, 127] }
+          ],
+          [
+              { source: [255, 0, 0, 255], composition: [0, 255, 0, 255], destination: [0,0,0,0] },
+              { source: [255, 0, 0, 255], composition: [255, 0, 0, 255], destination: [0, 0, 0, 0] },
+              { source: [255, 0, 0, 255], composition: [254, 1, 0, 255], destination: [0,0,0,0] },
+              { source: [0, 0, 0, 0], composition: [0, 0, 0, 0], destination: [0,0,0,0] },
+              { source: [0, 255, 0, 1], composition: [255, 0, 0, 1], destination: [0,0,0,0] },
+              { source: [0, 0, 0, 0], composition: [0, 0, 0, 0], destination: [0,0,0,0] },
+              { source: [255, 0, 0, 127], composition: [126, 126, 0, 127], destination: [0,0,0,0] },
+              { source: [255, 0, 0, 255], composition: [128, 127, 0, 255], destination: [0,0,0,0] },
+              { source: [255, 0, 0, 127], composition: [0, 255, 0, 127], destination: [0,0,0,0] },
+              { source: [127, 0, 0, 255], composition: [63, 63, 0, 255], destination: [0,0,0,0] },
+              { source: [126, 0, 0, 127], composition: [0, 126, 0, 127], destination: [0,0,0,0] },
+              { source: [255, 0, 0, 127], composition: [255, 0, 0, 127], destination: [0,0,0,0] },
+              { source: [255, 127, 0, 32], composition: [255, 111, 0, 32], destination: [0,0,0,0] },
+              { source: [255, 0, 0, 191], composition: [126, 126, 0, 191], destination: [0,0,0,0] },
+              { source: [255, 0, 255, 191], composition: [126, 126, 255, 191], destination: [0,0,0,0] }
+          ],
+          [
+              { source: [255, 0, 0, 255], composition: [255, 255, 0, 255], destination: [0, 255, 0, 255] },
+              { source: [255, 0, 0, 255], composition: [255, 0, 0, 255], destination: [0, 0, 0, 0] },
+              { source: [255, 0, 0, 255], composition: [255, 1, 0, 255], destination: [0, 255, 0, 1] },
+              { source: [0, 0, 0, 0], composition: [255, 0, 0, 255], destination: [255, 0, 0, 255] },
+              { source: [0, 255, 0, 1], composition: [255, 1, 0, 255], destination: [255, 0, 0, 255] },
+              { source: [0, 0, 0, 0], composition: [0, 255, 0, 255], destination: [0, 255, 0, 255] },
+              { source: [255, 0, 0, 127], composition: [127, 127, 0, 254], destination: [0, 255, 0, 127] },
+              { source: [255, 0, 0, 255], composition: [255, 127, 0, 255], destination: [0, 255, 0, 127] },
+              { source: [255, 0, 0, 127], composition: [127, 255, 0, 255], destination: [0, 255, 0, 255] },
+              { source: [127, 0, 0, 255], composition: [127, 63, 0, 255], destination: [0, 126, 0, 127] },
+              { source: [126, 0, 0, 127], composition: [63, 127, 0, 255], destination: [0, 127, 0, 255] },
+              { source: [255, 0, 0, 127], composition: [255, 0, 0, 190], destination: [255, 0, 0, 63] },
+              { source: [255, 127, 0, 32], composition: [255, 85, 0, 95], destination: [255, 64, 0, 63] },
+              { source: [255, 0, 0, 191], composition: [191, 127, 0, 255], destination: [0, 255, 0, 127] },
+              { source: [255, 0, 255, 191], composition: [191, 127, 255, 255], destination: [0, 255, 255, 127] }
+          ],
+          [
+              { source: [255, 0, 0, 255], composition: [255, 0, 0, 255], destination: [0, 0, 0, 0] },
+              { source: [255, 0, 0, 255], composition: [255, 0, 0, 255], destination: [0, 0, 0, 0] },
+              { source: [255, 0, 0, 255], composition: [255, 0, 0, 255], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [0, 0, 0, 0], destination: [0, 0, 0, 0] },
+              { source: [0, 255, 0, 1], composition: [0, 255, 0, 1], destination: [0, 0, 0, 0] },
+              { source: [0, 0, 0, 0], composition: [0, 0, 0, 0], destination: [0, 0, 0, 0] },
+              { source: [255, 0, 0, 127], composition: [255, 0, 0, 127], destination: [0, 0, 0, 0] },
+              { source: [255, 0, 0, 255], composition: [255, 0, 0, 255], destination: [0, 0, 0, 0] },
+              { source: [255, 0, 0, 127], composition: [255, 0, 0, 127], destination: [0, 0, 0, 0] },
+              { source: [127, 0, 0, 255], composition: [127, 0, 0, 255], destination: [0, 0, 0, 0] },
+              { source: [126, 0, 0, 127], composition: [126, 0, 0, 127], destination: [0, 0, 0, 0] },
+              { source: [255, 0, 0, 127], composition: [255, 0, 0, 127], destination: [0, 0, 0, 0] },
+              { source: [255, 127, 0, 32], composition: [255, 127, 0, 32], destination: [0, 0, 0, 0] },
+              { source: [255, 0, 0, 191], composition: [255, 0, 0, 191], destination: [0, 0, 0, 0] },
+              { source: [255, 0, 255, 191], composition: [255, 0, 255, 191], destination: [0, 0, 0, 0] }
+          ],
+          [
+              { source: [255, 0, 0, 255], composition: [0, 0, 0, 0], destination: [0, 255, 0, 255] },
+              { source: [255, 0, 0, 255], composition: [255, 0, 0, 255], destination: [0, 0, 0, 0] },
+              { source: [255, 0, 0, 255], composition: [255, 0, 0, 254], destination: [0, 255, 0, 1] },
+              { source: [0, 0, 0, 0], composition: [255, 0, 0, 255], destination: [255, 0, 0, 255] },
+              { source: [0, 255, 0, 1], composition: [255, 0, 0, 254], destination: [255, 0, 0, 255] },
+              { source: [0, 0, 0, 0], composition: [0, 255, 0, 255], destination: [0, 255, 0, 255] },
+              { source: [255, 0, 0, 127], composition: [126, 126, 0, 127], destination: [0, 255, 0, 127] },
+              { source: [255, 0, 0, 255], composition: [255, 0, 0, 128], destination: [0, 255, 0, 127] },
+              { source: [255, 0, 0, 127], composition: [0, 255, 0, 128], destination: [0, 255, 0, 255] },
+              { source: [127, 0, 0, 255], composition: [127, 0, 0, 128], destination: [0, 126, 0, 127] },
+              { source: [126, 0, 0, 127], composition: [0, 125, 0, 128], destination: [0, 127, 0, 255] },
+              { source: [255, 0, 0, 127], composition: [255, 0, 0, 127], destination: [255, 0, 0, 63] },
+              { source: [255, 127, 0, 32], composition: [255, 83, 0, 79], destination: [255, 64, 0, 63] },
+              { source: [255, 0, 0, 191], composition: [190, 62, 0, 127], destination: [0, 255, 0, 127] },
+              { source: [255, 0, 255, 191], composition: [190, 62, 255, 127], destination: [0, 255, 255, 127] }
+          ]
+      ];
+
+      // Compare two colors with a few margin.
+      function isDifferentColor(actualColor, expectedColor)
+      {
+          var actualAlpha = actualColor[3];
+          var expectedAlpha = expectedColor[3];
+          if (Math.abs(actualColor - expectedColor) > 3) return true;
+          // For the value of RGB, we compare the values the users actually see.
+          if (Math.abs(actualColor[0] * actualAlpha / 256 - expectedColor[0] * expectedAlpha / 256) > 3) return true;
+          if (Math.abs(actualColor[1] * actualAlpha / 256 - expectedColor[1] * expectedAlpha / 256) > 3) return true;
+          if (Math.abs(actualColor[2] * actualAlpha / 256 - expectedColor[2] * expectedAlpha / 256) > 3) return true;
+          return false;
+      }
+
+      function getRGBAString(a)
+      {
+          return "rgba(" + [a[0], a[1], a[2], a[3] / 255.0].join(",") + ")";
+      }
+
+      function drawTable(drawPolicy)
+      {
+          var tableElement = document.createElement("table");
+
+          // Create a header for source color.
+          var trElement = document.createElement("tr");
+          tableElement.appendChild(trElement);
+          trElement.appendChild(document.createElement("th"));
+          for (var column = 0; column < inputColors.length; column++) {
+              var inputColor = inputColors[column];
+              var thElement = document.createElement("th");
+              thElement.setAttribute("colspan", "2");
+              thElement.textContent = "src " + inputColor.source.join(", ");
+              trElement.appendChild(thElement);
+          }
+
+          // Create a header for destination color.
+          trElement = document.createElement("tr");
+          tableElement.appendChild(trElement);
+          trElement.appendChild(document.createElement("th"));
+          for (var column = 0; column < inputColors.length; column++) {
+              var inputColor = inputColors[column];
+              var thElement = document.createElement("th");
+              thElement.setAttribute("colspan", "2");
+              thElement.textContent = "dst " + inputColor.destination.join(", ");
+              trElement.appendChild(thElement);
+          }
+
+          var resultsElement = document.getElementById("results");
+          var titleElement = document.createElement("h1");
+          titleElement.textContent = "Tests for " + drawPolicy.name;
+          resultsElement.appendChild(titleElement);
+          resultsElement.appendChild(tableElement);
+
+          for (var row = 0; row < compositeTypes.length; row++){
+              var type = compositeTypes[row];
+
+              var trCanvasElement = document.createElement("tr");
+              var thElement = document.createElement("th");
+              thElement.setAttribute("rowspan", "2");
+              thElement.textContent = type;
+              trCanvasElement.appendChild(thElement);
+              var trMessageElement = document.createElement("tr");
+              tableElement.appendChild(trCanvasElement);
+              tableElement.appendChild(trMessageElement);
+
+              for (var column = 0; column < inputColors.length; column++) {
+                  var test = type + "-" + column;
+                  var inputColor = inputColors[column];
+                  var expectedColor = expectedColors[row][column];
+
+                  // Create canvas element for actual color.
+                  var actualCanvasElement = document.createElement("canvas");
+                  actualCanvasElement.setAttribute("width", "25");
+                  actualCanvasElement.setAttribute("height", "25");
+                  tdElement = document.createElement("td");
+                  tdElement.appendChild(actualCanvasElement);
+                  trCanvasElement.appendChild(tdElement);
+
+                  // Create canvas element for expected color.
+                  var expectedCanvasElement = document.createElement("canvas");
+                  expectedCanvasElement.setAttribute("width", "25");
+                  expectedCanvasElement.setAttribute("height", "25");
+                  var tdElement = document.createElement("td");
+                  tdElement.appendChild(expectedCanvasElement);
+                  trCanvasElement.appendChild(tdElement);
+
+                  // Create div element for pass/fail messages.
+                  var messageElement = document.createElement("div");
+                  tdElement = document.createElement("td");
+                  tdElement.setAttribute("colspan", "2");
+                  tdElement.appendChild(messageElement);
+                  trMessageElement.appendChild(tdElement);
+
+                  var ctx = expectedCanvasElement.getContext("2d");
+                  ctx.lineWidth = 10;
+                  // Draw expected image.
+                  ctx.globalCompositeOperation = "copy";
+                  ctx.fillStyle = getRGBAString(expectedColor.destination);
+                  ctx.strokeStyle = getRGBAString(expectedColor.destination);
+                  drawPolicy.drawDestination(ctx);
+                  ctx.fillStyle = getRGBAString(expectedColor.source);
+                  ctx.strokeStyle = getRGBAString(expectedColor.source);
+                  drawPolicy.drawSource(ctx);
+                  ctx.fillStyle = getRGBAString(expectedColor.composition);
+                  ctx.strokeStyle = getRGBAString(expectedColor.composition);
+                  drawPolicy.drawComposition(ctx);
+
+                  ctx = actualCanvasElement.getContext("2d");
+                  ctx.lineWidth = 10;
+
+                  // Draw destination rectangle.
+                  ctx.globalCompositeOperation = "copy";
+                  ctx.fillStyle = getRGBAString(inputColor.destination);
+                  ctx.strokeStyle = getRGBAString(inputColor.destination);
+                  drawPolicy.drawDestination(ctx);
+
+                  // Draw source rectangle.
+                  ctx.globalCompositeOperation = type;
+                  ctx.fillStyle = getRGBAString(inputColor.source);
+                  ctx.strokeStyle = getRGBAString(inputColor.source);
+                  drawPolicy.drawSource(ctx);
+
+                  // Let's check if the results are expected or not.
+                  var errorSuffix = ", composite type: " + type + ", source: " + inputColor.source + ", destination: " + inputColor.destination + "<br>";
+
+                  var results = "";
+                  // Note that (0, 0) may be affected by anti-alias.
+                  var img = ctx.getImageData(1, 1, 1, 1).data;
+                  var actualColor = [img[0], img[1], img[2], img[3]];
+                  if (isDifferentColor(actualColor, expectedColor.source)) {
+                      results += "Unexpected source! expected: " + expectedColor.source + " actual: " + actualColor + errorSuffix;
+                  }
+                  // Note that (24, 24) may be affected by anti-alias.
+                  img = ctx.getImageData(23, 23, 1, 1).data;
+                  actualColor = [img[0], img[1], img[2], img[3]];
+                  if (isDifferentColor(actualColor, expectedColor.destination)) {
+                      results += "Unexpected destination! expected: " + expectedColor.destination + " actual: " + actualColor + errorSuffix;
+                  }
+                  img = ctx.getImageData(12, 12, 1, 1).data;
+                  actualColor = [img[0], img[1], img[2], img[3]];
+                  if (isDifferentColor(actualColor, expectedColor.composition)) {
+                      results += "Unexpected composition! expected: " + expectedColor.composition + " actual: " + actualColor + errorSuffix;
+                  }
+
+                  if (results == "") {
+                      messageElement.style.backgroundColor = "green";
+                      messageElement.innerHTML = results = "PASS";
+                  } else {
+                      messageElement.style.backgroundColor = "red";
+                      messageElement.innerHTML = results;
+                  }
+
+                  // Dump colors into text area for debugging purpose.
+                  var debugText = document.getElementById("debug");
+                  img = ctx.getImageData(0, 0, 1, 1).data;
+                  debugText.value += img[0] + "," + img[1] + "," + img[2] + "," + img[3] + "\n";
+                  img = ctx.getImageData(12, 12, 1, 1).data;
+                  debugText.value += img[0] + "," + img[1] + "," + img[2] + "," + img[3] + "\n";
+                  img = ctx.getImageData(24, 24, 1, 1).data;
+                  debugText.value += img[0] + "," + img[1] + "," + img[2] + "," + img[3] + "\n";
+              }
+          }
+      }
+
+      var useStrokeRect = {
+          drawSource: function(ctx) {
+              ctx.strokeRect(5, 5, 10, 10);
+          },
+
+          drawDestination: function(ctx) {
+              ctx.fillRect(5, 5, 20, 20);
+          },
+
+          drawComposition: function(ctx) {
+              ctx.fillRect(5, 5, 15, 15);
+          },
+
+          name: "stroke rect"
+      };
+
+      var usePathAndStroke = {
+          drawSource: function(ctx) {
+              ctx.beginPath();
+              ctx.moveTo(5, 5);
+              ctx.lineTo(15, 5);
+              ctx.lineTo(15, 15);
+              ctx.lineTo(5, 15);
+              ctx.closePath();
+              ctx.stroke();
+          },
+
+          drawDestination: function(ctx) {
+              ctx.fillRect(5, 5, 20, 20);
+          },
+
+          drawComposition: function(ctx) {
+              ctx.fillRect(5, 5, 15, 15);
+          },
+
+          name: "path and stroke"
+      };
+
+      function draw()
+      {
+          drawTable(useStrokeRect);
+          drawTable(usePathAndStroke);
+          if (window.testRunner)
+              testRunner.notifyDone();
+      }
+    </script>
+    <style type="text/css">
+      body { margin: 20px; font-family: arial,verdana,helvetica; background: #fff;}
+      h1 { font-size: 140%; font-weight:normal; color: #036; border-bottom: 1px solid #ccc; }
+      canvas { border: 2px solid #000; margin-bottom: 5px; }
+      table { background: #00f; }
+      th { font-size: 70%; padding: 0; }
+      td { font-size: 70%; padding: 0; }
+      pre { float:left; display:block; background: rgb(238,238,238); border: 1px dashed #666; padding: 15px 20px; margin: 0 0 10px 0; }
+    </style>
+  </head>
+  <body _onload_="draw();">
+    <p>This test exercises a bunch of alpha composition checks with stroking. The top-left rectangles are the source images and bottom-right rectangles are the destination images.</p>
+    <div id="results">
+    </div>
+    <textarea id="debug"></textarea>
+  </body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (166835 => 166836)


--- trunk/Source/WebCore/ChangeLog	2014-04-05 19:54:18 UTC (rev 166835)
+++ trunk/Source/WebCore/ChangeLog	2014-04-05 19:57:00 UTC (rev 166836)
@@ -1,3 +1,21 @@
+2014-04-05  Dirk Schulze  <k...@webkit.org>
+
+        Canvas stroke and strokeRect with SourceIn, DestinationIn, SourceOut, DestinationAtop and Copy have errors
+        https://bugs.webkit.org/show_bug.cgi?id=66762
+
+        Reviewed by Andreas Kling.
+
+        For fill operations we create a new ImageBuffer to ensure that the results are composited
+        correctly according to the Canvas spec. This patch creates a new transparency layer
+        for stroking on certain compositing operators to archive the same. This makes WebKit's
+        behavior interoperable with Firefox and IE as well. 
+
+        Test: fast/fast/canvas/canvas-composite-stroke-alpha.html
+
+        * html/canvas/CanvasRenderingContext2D.cpp:
+        (WebCore::CanvasRenderingContext2D::strokeInternal):
+        (WebCore::CanvasRenderingContext2D::strokeRect):
+
 2014-04-05  Yusuke Suzuki  <utatane....@gmail.com>
 
         Use preallocated stack reference to store adjacentBacktrackingStart

Modified: trunk/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp (166835 => 166836)


--- trunk/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp	2014-04-05 19:54:18 UTC (rev 166835)
+++ trunk/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp	2014-04-05 19:57:00 UTC (rev 166836)
@@ -970,11 +970,21 @@
         return;
 
     if (!path.isEmpty()) {
-        FloatRect dirtyRect = path.fastBoundingRect();
-        inflateStrokeRect(dirtyRect);
-
-        c->strokePath(path);
-        didDraw(dirtyRect);
+        if (isFullCanvasCompositeMode(state().m_globalComposite)) {
+            c->beginTransparencyLayer(1);
+            c->strokePath(path);
+            c->endTransparencyLayer();
+            didDrawEntireCanvas();
+        } else if (state().m_globalComposite == CompositeCopy) {
+            clearCanvas();
+            c->strokePath(path);
+            didDrawEntireCanvas();
+        } else {
+            FloatRect dirtyRect = path.fastBoundingRect();
+            inflateStrokeRect(dirtyRect);
+            c->strokePath(path);
+            didDraw(dirtyRect);
+        }
     }
 }
 
@@ -1147,12 +1157,21 @@
         return;
 
     FloatRect rect(x, y, width, height);
-
-    FloatRect boundingRect = rect;
-    boundingRect.inflate(state().m_lineWidth / 2);
-
-    c->strokeRect(rect, state().m_lineWidth);
-    didDraw(boundingRect);
+    if (isFullCanvasCompositeMode(state().m_globalComposite)) {
+        c->beginTransparencyLayer(1);
+        c->strokeRect(rect, state().m_lineWidth);
+        c->endTransparencyLayer();
+        didDrawEntireCanvas();
+    } else if (state().m_globalComposite == CompositeCopy) {
+        clearCanvas();
+        c->strokeRect(rect, state().m_lineWidth);
+        didDrawEntireCanvas();
+    } else {
+        FloatRect boundingRect = rect;
+        boundingRect.inflate(state().m_lineWidth / 2);
+        c->strokeRect(rect, state().m_lineWidth);
+        didDraw(boundingRect);
+    }
 }
 
 void CanvasRenderingContext2D::setShadow(float width, float height, float blur)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to