Title: [166840] trunk
Revision
166840
Author
k...@webkit.org
Date
2014-04-05 14:34:44 -0700 (Sat, 05 Apr 2014)

Log Message

Canvas strokeText and fillText with SourceIn, DestinationIn, SourceOut, DestinationAtop and Copy have errors
https://bugs.webkit.org/show_bug.cgi?id=66766

Reviewed by Andreas Kling.

Source/WebCore:

Use transparency layers to draw text with certain compositing modes on
the canvas. This follows the Canvas specification and makes WebKit fully
compatible with IE. It also makes it more compatible with Firefox, even though
Firefox still has some bugs.

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

* html/canvas/CanvasRenderingContext2D.cpp:
(WebCore::CanvasRenderingContext2D::drawTextInternal):

LayoutTests:

330 tests check compositing on Canvas with text.

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

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (166839 => 166840)


--- trunk/LayoutTests/ChangeLog	2014-04-05 21:33:19 UTC (rev 166839)
+++ trunk/LayoutTests/ChangeLog	2014-04-05 21:34:44 UTC (rev 166840)
@@ -1,5 +1,17 @@
 2014-04-05  Dirk Schulze  <k...@webkit.org>
 
+        Canvas strokeText and fillText with SourceIn, DestinationIn, SourceOut, DestinationAtop and Copy have errors
+        https://bugs.webkit.org/show_bug.cgi?id=66766
+
+        Reviewed by Andreas Kling.
+
+        330 tests check compositing on Canvas with text.
+
+        * fast/canvas/canvas-composite-text-alpha-expected.txt: Added.
+        * fast/canvas/canvas-composite-text-alpha.html: Added.
+
+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
 

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


--- trunk/LayoutTests/fast/canvas/canvas-composite-text-alpha-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/canvas/canvas-composite-text-alpha-expected.txt	2014-04-05 21:34:44 UTC (rev 166840)
@@ -0,0 +1,363 @@
+This test exercises a bunch of alpha composition operations on text. The top-left rectangles are the source images and bottom-right rectangles are the destination images.
+
+Tests for fill text
+
+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 stroke text
+
+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-text-alpha.html (0 => 166840)


--- trunk/LayoutTests/fast/canvas/canvas-composite-text-alpha.html	                        (rev 0)
+++ trunk/LayoutTests/fast/canvas/canvas-composite-text-alpha.html	2014-04-05 21:34:44 UTC (rev 166840)
@@ -0,0 +1,448 @@
+<html>
+  <head>
+    <title>A canvas globalCompositeOperation test with alpha compositing on text</title>
+    <style type="text/css">
+      @font-face {
+          font-family:ahem;
+          src: url(../../resources/Ahem.ttf);
+      }
+      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>
+    <!-- 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 useFillText = {
+          drawSource: function(ctx) {
+              ctx.font="20px ahem";
+              ctx.fillText("A", 0, 16);
+          },
+
+          drawDestination: function(ctx) {
+              ctx.fillRect(5, 5, 20, 20);
+          },
+
+          drawComposition: function(ctx) {
+              ctx.fillRect(5, 5, 15, 15);
+          },
+
+          name: "fill text"
+      };
+
+      var useStrokeText = {
+          drawSource: function(ctx) {
+              ctx.font="10px ahem";
+              ctx.strokeText("A", 5, 10);
+          },
+
+          drawDestination: function(ctx) {
+              ctx.fillRect(5, 5, 20, 20);
+          },
+
+          drawComposition: function(ctx) {
+              ctx.fillRect(5, 5, 15, 15);
+          },
+
+          name: "stroke text"
+      };
+
+      function draw()
+      {
+          drawTable(useFillText);
+          drawTable(useStrokeText);
+          if (window.testRunner)
+              testRunner.notifyDone();
+      }
+    </script>
+  </head>
+  <body _onload_="draw();">
+    <p>This test exercises a bunch of alpha composition operations on text. 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 (166839 => 166840)


--- trunk/Source/WebCore/ChangeLog	2014-04-05 21:33:19 UTC (rev 166839)
+++ trunk/Source/WebCore/ChangeLog	2014-04-05 21:34:44 UTC (rev 166840)
@@ -1,3 +1,20 @@
+2014-04-05  Dirk Schulze  <k...@webkit.org>
+
+        Canvas strokeText and fillText with SourceIn, DestinationIn, SourceOut, DestinationAtop and Copy have errors
+        https://bugs.webkit.org/show_bug.cgi?id=66766
+
+        Reviewed by Andreas Kling.
+
+        Use transparency layers to draw text with certain compositing modes on
+        the canvas. This follows the Canvas specification and makes WebKit fully
+        compatible with IE. It also makes it more compatible with Firefox, even though
+        Firefox still has some bugs.
+
+        Test: fast/canvas/canvas-composite-text-alpha.html
+
+        * html/canvas/CanvasRenderingContext2D.cpp:
+        (WebCore::CanvasRenderingContext2D::drawTextInternal):
+
 2014-04-05  Andreas Kling  <akl...@apple.com>
 
         Devirtualize isHTMLUnknownElement().

Modified: trunk/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp (166839 => 166840)


--- trunk/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp	2014-04-05 21:33:19 UTC (rev 166839)
+++ trunk/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp	2014-04-05 21:34:44 UTC (rev 166840)
@@ -2334,16 +2334,27 @@
 
     c->setTextDrawingMode(fill ? TextModeFill : TextModeStroke);
 
+    GraphicsContextStateSaver stateSaver(*c);
     if (useMaxWidth) {
-        GraphicsContextStateSaver stateSaver(*c);
         c->translate(location.x(), location.y());
         // We draw when fontWidth is 0 so compositing operations (eg, a "copy" op) still work.
         c->scale(FloatSize((fontWidth > 0 ? (width / fontWidth) : 0), 1));
-        c->drawBidiText(font, textRun, FloatPoint(0, 0), Font::UseFallbackIfFontNotReady);
-    } else
+        location = FloatPoint();
+    }
+
+    if (isFullCanvasCompositeMode(state().m_globalComposite)) {
+        c->beginTransparencyLayer(1);
         c->drawBidiText(font, textRun, location, Font::UseFallbackIfFontNotReady);
-
-    didDraw(textRect);
+        c->endTransparencyLayer();
+        didDrawEntireCanvas();
+    } else if (state().m_globalComposite == CompositeCopy) {
+        clearCanvas();
+        c->drawBidiText(font, textRun, location, Font::UseFallbackIfFontNotReady);
+        didDrawEntireCanvas();
+    } else {
+        c->drawBidiText(font, textRun, location, Font::UseFallbackIfFontNotReady);
+        didDraw(textRect);
+    }
 }
 
 void CanvasRenderingContext2D::inflateStrokeRect(FloatRect& rect) const
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to