diff options
| author | commit-queue@webkit.org <commit-queue@webkit.org@bbb929c8-8fbe-4397-9dbb-9b2b20218538> | 2010-12-14 18:30:34 +0000 |
|---|---|---|
| committer | commit-queue@webkit.org <commit-queue@webkit.org@bbb929c8-8fbe-4397-9dbb-9b2b20218538> | 2010-12-14 18:30:34 +0000 |
| commit | 6347ee438f71a42ffddfaff2b95ce2a3ecec799f (patch) | |
| tree | 3997bc6cbde58780c0bda3bebba319f9fbd2429f | |
| parent | ce0d9c8b6650bbb5d63f431ebfb0c7b31f41d294 (diff) | |
| download | chromium_src-6347ee438f71a42ffddfaff2b95ce2a3ecec799f.zip chromium_src-6347ee438f71a42ffddfaff2b95ce2a3ecec799f.tar.gz chromium_src-6347ee438f71a42ffddfaff2b95ce2a3ecec799f.tar.bz2 | |
2010-12-14 Helder Correia <helder@sencha.com>
Reviewed by Ariya Hidayat.
[Qt] Canvas shadow offset should not be affected by any transformation
https://bugs.webkit.org/show_bug.cgi?id=50422
On a canvas context, shadows are currently affected by all
transformations except scaling. According to the spec:
http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#shadows
"The shadowOffsetX and shadowOffsetY attributes specify the distance
that the shadow will be offset in the positive horizontal and positive
vertical distance respectively. Their values are in coordinate space
units. They are not affected by the current transformation matrix."
NOTE: this applies only to canvas, not to box shadows.
Add new test to ensure that shadows are correctly transformed keeping
the relative offset to the shape.
* fast/canvas/canvas-scale-strokePath-shadow-expected.txt:
* fast/canvas/canvas-transforms-fillRect-shadow-expected.txt: Added.
* fast/canvas/canvas-transforms-fillRect-shadow.html: Added.
* fast/canvas/script-tests/canvas-scale-fillPath-shadow.js:
* fast/canvas/script-tests/canvas-scale-fillRect-shadow.js:
* fast/canvas/script-tests/canvas-scale-strokePath-shadow.js: Now using
a lineWidth > 1 to make it easier to test and more fair among all
ports, since there can be different transformation smoothness or
aliasing settings.
* fast/canvas/script-tests/canvas-transforms-fillRect-shadow.js: Added.
2010-12-14 Helder Correia <helder@sencha.com>
Reviewed by Ariya Hidayat.
[Qt] Canvas shadow offset should not be affected by any transformation
https://bugs.webkit.org/show_bug.cgi?id=50422
On a canvas context, shadows are currently affected by all
transformations except scaling. According to the spec:
http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#shadows
"The shadowOffsetX and shadowOffsetY attributes specify the distance
that the shadow will be offset in the positive horizontal and positive
vertical distance respectively. Their values are in coordinate space
units. They are not affected by the current transformation matrix."
NOTE: this applies only to canvas, not to box shadows.
Test: fast/canvas/canvas-transforms-fillRect-shadow.html
* platform/graphics/ContextShadow.cpp:
(WebCore::ContextShadow::ContextShadow):
(WebCore::ContextShadow::calculateLayerBoundingRect):
* platform/graphics/ContextShadow.h:
(WebCore::ContextShadow::setShadowsIgnoreTransforms):
(WebCore::ContextShadow::shadowsIgnoreTransforms):
(WebCore::ContextShadow::offset):
* platform/graphics/qt/ContextShadowQt.cpp:
(WebCore::ContextShadow::beginShadowLayer):
(WebCore::ContextShadow::endShadowLayer):
* platform/graphics/qt/GraphicsContextQt.cpp:
(WebCore::mustUseContextShadow):
(WebCore::GraphicsContext::fillPath):
(WebCore::GraphicsContext::strokePath):
(WebCore::GraphicsContext::fillRect):
(WebCore::GraphicsContext::fillRoundedRect):
(WebCore::GraphicsContext::setPlatformShadow):
git-svn-id: svn://svn.chromium.org/blink/trunk@74040 bbb929c8-8fbe-4397-9dbb-9b2b20218538
13 files changed, 429 insertions, 117 deletions
diff --git a/third_party/WebKit/LayoutTests/ChangeLog b/third_party/WebKit/LayoutTests/ChangeLog index ac2e6e4..5f1c566 100644 --- a/third_party/WebKit/LayoutTests/ChangeLog +++ b/third_party/WebKit/LayoutTests/ChangeLog @@ -1,3 +1,35 @@ +2010-12-14 Helder Correia <helder@sencha.com> + + Reviewed by Ariya Hidayat. + + [Qt] Canvas shadow offset should not be affected by any transformation + https://bugs.webkit.org/show_bug.cgi?id=50422 + + On a canvas context, shadows are currently affected by all + transformations except scaling. According to the spec: + http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#shadows + + "The shadowOffsetX and shadowOffsetY attributes specify the distance + that the shadow will be offset in the positive horizontal and positive + vertical distance respectively. Their values are in coordinate space + units. They are not affected by the current transformation matrix." + + NOTE: this applies only to canvas, not to box shadows. + + Add new test to ensure that shadows are correctly transformed keeping + the relative offset to the shape. + + * fast/canvas/canvas-scale-strokePath-shadow-expected.txt: + * fast/canvas/canvas-transforms-fillRect-shadow-expected.txt: Added. + * fast/canvas/canvas-transforms-fillRect-shadow.html: Added. + * fast/canvas/script-tests/canvas-scale-fillPath-shadow.js: + * fast/canvas/script-tests/canvas-scale-fillRect-shadow.js: + * fast/canvas/script-tests/canvas-scale-strokePath-shadow.js: Now using + a lineWidth > 1 to make it easier to test and more fair among all + ports, since there can be different transformation smoothness or + aliasing settings. + * fast/canvas/script-tests/canvas-transforms-fillRect-shadow.js: Added. + 2010-12-14 Pavel Feldman <pfeldman@chromium.org> Not reviewed. Removed obsolete chromium expectations. diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-scale-strokePath-shadow-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-scale-strokePath-shadow-expected.txt index e56201f..a337c4b 100644 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-scale-strokePath-shadow-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-scale-strokePath-shadow-expected.txt @@ -30,27 +30,27 @@ PASS d[3] is around 76 PASS d[0] is 255 PASS d[1] is 0 PASS d[2] is 0 -PASS d[3] is around 20 +PASS d[3] is around 149 PASS d[0] is 255 PASS d[1] is 0 PASS d[2] is 0 -PASS d[3] is around 22 +PASS d[3] is around 116 PASS d[0] is 255 PASS d[1] is 0 PASS d[2] is 0 -PASS d[3] is around 28 +PASS d[3] is around 115 PASS d[0] is 255 PASS d[1] is 0 PASS d[2] is 0 -PASS d[3] is around 22 +PASS d[3] is around 70 PASS d[0] is 255 PASS d[1] is 0 PASS d[2] is 0 -PASS d[3] is around 15 +PASS d[3] is around 70 PASS d[0] is 255 PASS d[1] is 0 PASS d[2] is 0 -PASS d[3] is around 17 +PASS d[3] is around 69 PASS successfullyParsed is true TEST COMPLETE diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-transforms-fillRect-shadow-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-transforms-fillRect-shadow-expected.txt new file mode 100644 index 0000000..013b891 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-transforms-fillRect-shadow-expected.txt @@ -0,0 +1,57 @@ +Ensure correct behavior of canvas with fillRect+shadow after translation+rotation+scaling. A blue and red checkered pattern should be displayed. + +On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". + + +PASS d[0] is 255 +PASS d[1] is 0 +PASS d[2] is 0 +PASS d[3] is 255 +PASS d[0] is 255 +PASS d[1] is 0 +PASS d[2] is 0 +PASS d[3] is 255 +PASS d[0] is 255 +PASS d[1] is 0 +PASS d[2] is 0 +PASS d[3] is 255 +PASS d[0] is 255 +PASS d[1] is 0 +PASS d[2] is 0 +PASS d[3] is around 127 +PASS d[0] is 255 +PASS d[1] is 0 +PASS d[2] is 0 +PASS d[3] is around 127 +PASS d[0] is 255 +PASS d[1] is 0 +PASS d[2] is 0 +PASS d[3] is around 127 +PASS d[0] is 255 +PASS d[1] is 0 +PASS d[2] is 0 +PASS d[3] is around 106 +PASS d[0] is 255 +PASS d[1] is 0 +PASS d[2] is 0 +PASS d[3] is around 106 +PASS d[0] is 255 +PASS d[1] is 0 +PASS d[2] is 0 +PASS d[3] is around 83 +PASS d[0] is 255 +PASS d[1] is 0 +PASS d[2] is 0 +PASS d[3] is around 36 +PASS d[0] is 255 +PASS d[1] is 0 +PASS d[2] is 0 +PASS d[3] is around 36 +PASS d[0] is 255 +PASS d[1] is 0 +PASS d[2] is 0 +PASS d[3] is around 36 +PASS successfullyParsed is true + +TEST COMPLETE + diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-transforms-fillRect-shadow.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-transforms-fillRect-shadow.html new file mode 100644 index 0000000..2fc4e9a --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-transforms-fillRect-shadow.html @@ -0,0 +1,13 @@ +<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> +<html> +<head> +<link rel="stylesheet" href="../js/resources/js-test-style.css"> +<script src="../js/resources/js-test-pre.js"></script> +</head> +<body> +<p id="description"></p> +<div id="console"></div> +<script src="script-tests/canvas-transforms-fillRect-shadow.js"></script> +<script src="../js/resources/js-test-post.js"></script> +</body> +</html> diff --git a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-scale-fillPath-shadow.js b/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-scale-fillPath-shadow.js index 05286f5..3ca3321 100644 --- a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-scale-fillPath-shadow.js +++ b/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-scale-fillPath-shadow.js @@ -79,13 +79,13 @@ shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); shouldBe('d[3]', '255'); -d = ctx.getImageData(299, 295, 1, 1).data; +d = ctx.getImageData(298, 295, 1, 1).data; shouldBe('d[0]', '255'); shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); shouldBe('d[3]', '255'); -d = ctx.getImageData(200, 299, 1, 1).data; +d = ctx.getImageData(200, 298, 1, 1).data; shouldBe('d[0]', '255'); shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); @@ -98,13 +98,13 @@ shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); shouldBeAround('d[3]', '76'); -d = ctx.getImageData(299, 405, 1, 1).data; +d = ctx.getImageData(298, 405, 1, 1).data; shouldBe('d[0]', '255'); shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); shouldBeAround('d[3]', '76'); -d = ctx.getImageData(205, 499, 1, 1).data; +d = ctx.getImageData(205, 498, 1, 1).data; shouldBe('d[0]', '255'); shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); diff --git a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-scale-fillRect-shadow.js b/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-scale-fillRect-shadow.js index 99a1e59..f495c91 100644 --- a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-scale-fillRect-shadow.js +++ b/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-scale-fillRect-shadow.js @@ -58,13 +58,13 @@ shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); shouldBe('d[3]', '255'); -d = ctx.getImageData(299, 295, 1, 1).data; +d = ctx.getImageData(298, 298, 1, 1).data; shouldBe('d[0]', '255'); shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); shouldBe('d[3]', '255'); -d = ctx.getImageData(200, 299, 1, 1).data; +d = ctx.getImageData(201, 298, 1, 1).data; shouldBe('d[0]', '255'); shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); @@ -77,13 +77,13 @@ shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); shouldBeAround('d[3]', '76'); -d = ctx.getImageData(299, 405, 1, 1).data; +d = ctx.getImageData(298, 405, 1, 1).data; shouldBe('d[0]', '255'); shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); shouldBeAround('d[3]', '76'); -d = ctx.getImageData(205, 499, 1, 1).data; +d = ctx.getImageData(205, 498, 1, 1).data; shouldBe('d[0]', '255'); shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); diff --git a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-scale-strokePath-shadow.js b/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-scale-strokePath-shadow.js index 5198d94..91b927f 100644 --- a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-scale-strokePath-shadow.js +++ b/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-scale-strokePath-shadow.js @@ -35,6 +35,7 @@ ctx.scale(2, 2); ctx.shadowOffsetX = 100; ctx.shadowOffsetY = 100; ctx.strokeStyle = 'rgba(0, 0, 255, 1)'; +ctx.lineWidth = 5; ctx.shadowColor = 'rgba(255, 0, 0, 1.0)'; ctx.beginPath(); @@ -76,79 +77,79 @@ ctx.stroke(); var d; // imageData.data // Verify solid shadow. -d = ctx.getImageData(200, 205, 1, 1).data; +d = ctx.getImageData(250, 200, 1, 1).data; shouldBe('d[0]', '255'); shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); shouldBe('d[3]', '255'); -d = ctx.getImageData(299, 295, 1, 1).data; +d = ctx.getImageData(300, 290, 1, 1).data; shouldBe('d[0]', '255'); shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); shouldBe('d[3]', '255'); -d = ctx.getImageData(201, 299, 1, 1).data; +d = ctx.getImageData(200, 250, 1, 1).data; shouldBe('d[0]', '255'); shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); shouldBe('d[3]', '255'); // Verify solid alpha shadow. -d = ctx.getImageData(200, 405, 1, 1).data; +d = ctx.getImageData(201, 405, 1, 1).data; shouldBe('d[0]', '255'); shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); shouldBeAround('d[3]', '76'); -d = ctx.getImageData(299, 405, 1, 1).data; +d = ctx.getImageData(201, 500, 1, 1).data; shouldBe('d[0]', '255'); shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); shouldBeAround('d[3]', '76'); -d = ctx.getImageData(205, 499, 1, 1).data; +d = ctx.getImageData(300, 499, 1, 1).data; shouldBe('d[0]', '255'); shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); shouldBeAround('d[3]', '76'); // Verify blurry shadow. -d = ctx.getImageData(394, 208, 1, 1).data; +d = ctx.getImageData(404, 210, 1, 1).data; shouldBe('d[0]', '255'); shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); -shouldBeAround('d[3]', '20'); +shouldBeAround('d[3]', '149'); -d = ctx.getImageData(503, 301, 1, 1).data; +d = ctx.getImageData(505, 250, 1, 1).data; shouldBe('d[0]', '255'); shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); -shouldBeAround('d[3]', '22'); +shouldBeAround('d[3]', '116'); -d = ctx.getImageData(504, 250, 1, 1).data; +d = ctx.getImageData(450, 205, 1, 1).data; shouldBe('d[0]', '255'); shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); -shouldBeAround('d[3]', '28'); +shouldBeAround('d[3]', '115'); // Verify blurry alpha shadow. -d = ctx.getImageData(405, 405, 1, 1).data; +d = ctx.getImageData(505, 450, 1, 1).data; shouldBe('d[0]', '255'); shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); -shouldBeAround('d[3]', '22'); +shouldBeAround('d[3]', '70'); -d = ctx.getImageData(415, 495, 1, 1).data; +d = ctx.getImageData(505, 450, 1, 1).data; shouldBe('d[0]', '255'); shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); -shouldBeAround('d[3]', '15'); +shouldBeAround('d[3]', '70'); -d = ctx.getImageData(450, 504, 1, 1).data; +d = ctx.getImageData(450, 405, 1, 1).data; shouldBe('d[0]', '255'); shouldBe('d[1]', '0'); shouldBe('d[2]', '0'); -shouldBeAround('d[3]', '17'); +shouldBeAround('d[3]', '69'); var successfullyParsed = true; diff --git a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-transforms-fillRect-shadow.js b/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-transforms-fillRect-shadow.js new file mode 100644 index 0000000..b67f244 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-transforms-fillRect-shadow.js @@ -0,0 +1,133 @@ +description("Ensure correct behavior of canvas with fillRect+shadow after translation+rotation+scaling. A blue and red checkered pattern should be displayed."); + +function print(message, color) +{ + var paragraph = document.createElement("div"); + paragraph.appendChild(document.createTextNode(message)); + paragraph.style.fontFamily = "monospace"; + if (color) + paragraph.style.color = color; + document.getElementById("console").appendChild(paragraph); +} + +function shouldBeAround(a, b) +{ + var evalA; + try { + evalA = eval(a); + } catch(e) { + evalA = e; + } + + if (Math.abs(evalA - b) < 10) + print("PASS " + a + " is around " + b , "green") + else + print("FAIL " + a + " is not around " + b + " (actual: " + evalA + ")", "red"); +} + +var canvas = document.createElement('canvas'); +document.body.appendChild(canvas); +canvas.setAttribute('width', '600'); +canvas.setAttribute('height', '600'); +var ctx = canvas.getContext('2d'); + +ctx.fillStyle = 'rgba(0, 0, 255, 1.0)'; +ctx.shadowOffsetX = 100; +ctx.shadowOffsetY = 100; + +ctx.translate(-100, -100); +ctx.rotate(Math.PI/2); +ctx.scale(2, 2); + +ctx.shadowColor = 'rgba(255, 0, 0, 1.0)'; +ctx.fillRect(100, -150, 50, 50); + +ctx.shadowColor = 'rgba(255, 0, 0, 0.5)'; +ctx.fillRect(200, -150, 50, 50); + +ctx.shadowBlur = 5; +ctx.shadowColor = 'rgba(255, 0, 0, 1.0)'; +ctx.fillRect(100, -250, 50, 50); + +ctx.shadowColor = 'rgba(255, 0, 0, 0.5)'; +ctx.fillRect(200, -250, 50, 50); + +var d; // imageData.data + +// Verify solid shadow. +d = ctx.getImageData(200, 205, 1, 1).data; +shouldBe('d[0]', '255'); +shouldBe('d[1]', '0'); +shouldBe('d[2]', '0'); +shouldBe('d[3]', '255'); + +d = ctx.getImageData(298, 298, 1, 1).data; +shouldBe('d[0]', '255'); +shouldBe('d[1]', '0'); +shouldBe('d[2]', '0'); +shouldBe('d[3]', '255'); + +d = ctx.getImageData(201, 298, 1, 1).data; +shouldBe('d[0]', '255'); +shouldBe('d[1]', '0'); +shouldBe('d[2]', '0'); +shouldBe('d[3]', '255'); + +// Verify solid alpha shadow. +d = ctx.getImageData(201, 401, 1, 1).data; +shouldBe('d[0]', '255'); +shouldBe('d[1]', '0'); +shouldBe('d[2]', '0'); +shouldBeAround('d[3]', '127'); + +d = ctx.getImageData(299, 450, 1, 1).data; +shouldBe('d[0]', '255'); +shouldBe('d[1]', '0'); +shouldBe('d[2]', '0'); +shouldBeAround('d[3]', '127'); + +d = ctx.getImageData(205, 498, 1, 1).data; +shouldBe('d[0]', '255'); +shouldBe('d[1]', '0'); +shouldBe('d[2]', '0'); +shouldBeAround('d[3]', '127'); + +// Verify blurry shadow. +d = ctx.getImageData(399, 205, 1, 1).data; +shouldBe('d[0]', '255'); +shouldBe('d[1]', '0'); +shouldBe('d[2]', '0'); +shouldBeAround('d[3]', '106'); + +d = ctx.getImageData(500, 205, 1, 1).data; +shouldBe('d[0]', '255'); +shouldBe('d[1]', '0'); +shouldBe('d[2]', '0'); +shouldBeAround('d[3]', '106'); + +d = ctx.getImageData(499, 299, 1, 1).data; +shouldBe('d[0]', '255'); +shouldBe('d[1]', '0'); +shouldBe('d[2]', '0'); +shouldBeAround('d[3]', '83'); + +// Verify blurry alpha shadow. +d = ctx.getImageData(398, 405, 1, 1).data; +shouldBe('d[0]', '255'); +shouldBe('d[1]', '0'); +shouldBe('d[2]', '0'); +shouldBeAround('d[3]', '36'); + +d = ctx.getImageData(405, 501, 1, 1).data; +shouldBe('d[0]', '255'); +shouldBe('d[1]', '0'); +shouldBe('d[2]', '0'); +shouldBeAround('d[3]', '36'); + +d = ctx.getImageData(405, 501, 1, 1).data; +shouldBe('d[0]', '255'); +shouldBe('d[1]', '0'); +shouldBe('d[2]', '0'); +shouldBeAround('d[3]', '36'); + +var successfullyParsed = true; diff --git a/third_party/WebKit/WebCore/ChangeLog b/third_party/WebKit/WebCore/ChangeLog index 4d79928..bcdab61 100644 --- a/third_party/WebKit/WebCore/ChangeLog +++ b/third_party/WebKit/WebCore/ChangeLog @@ -1,3 +1,41 @@ +2010-12-14 Helder Correia <helder@sencha.com> + + Reviewed by Ariya Hidayat. + + [Qt] Canvas shadow offset should not be affected by any transformation + https://bugs.webkit.org/show_bug.cgi?id=50422 + + On a canvas context, shadows are currently affected by all + transformations except scaling. According to the spec: + http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#shadows + + "The shadowOffsetX and shadowOffsetY attributes specify the distance + that the shadow will be offset in the positive horizontal and positive + vertical distance respectively. Their values are in coordinate space + units. They are not affected by the current transformation matrix." + + NOTE: this applies only to canvas, not to box shadows. + + Test: fast/canvas/canvas-transforms-fillRect-shadow.html + + * platform/graphics/ContextShadow.cpp: + (WebCore::ContextShadow::ContextShadow): + (WebCore::ContextShadow::calculateLayerBoundingRect): + * platform/graphics/ContextShadow.h: + (WebCore::ContextShadow::setShadowsIgnoreTransforms): + (WebCore::ContextShadow::shadowsIgnoreTransforms): + (WebCore::ContextShadow::offset): + * platform/graphics/qt/ContextShadowQt.cpp: + (WebCore::ContextShadow::beginShadowLayer): + (WebCore::ContextShadow::endShadowLayer): + * platform/graphics/qt/GraphicsContextQt.cpp: + (WebCore::mustUseContextShadow): + (WebCore::GraphicsContext::fillPath): + (WebCore::GraphicsContext::strokePath): + (WebCore::GraphicsContext::fillRect): + (WebCore::GraphicsContext::fillRoundedRect): + (WebCore::GraphicsContext::setPlatformShadow): + 2010-12-14 Alexander Pavlov <apavlov@chromium.org> Reviewed by Yury Semikhatsky. diff --git a/third_party/WebKit/WebCore/platform/graphics/ContextShadow.cpp b/third_party/WebKit/WebCore/platform/graphics/ContextShadow.cpp index 87a1c5c..ebffd98 100644 --- a/third_party/WebKit/WebCore/platform/graphics/ContextShadow.cpp +++ b/third_party/WebKit/WebCore/platform/graphics/ContextShadow.cpp @@ -41,6 +41,7 @@ ContextShadow::ContextShadow() : m_type(NoShadow) , m_blurDistance(0) , m_layerContext(0) + , m_shadowsIgnoreTransforms(false) { } @@ -49,6 +50,7 @@ ContextShadow::ContextShadow(const Color& color, float radius, const FloatSize& , m_blurDistance(round(radius)) , m_offset(offset) , m_layerContext(0) + , m_shadowsIgnoreTransforms(false) { // See comments in http://webkit.org/b/40793, it seems sensible // to follow Skia's limit of 128 pixels of blur radius @@ -149,6 +151,53 @@ void ContextShadow::blurLayerImage(unsigned char* imageData, const IntSize& size } } +#if PLATFORM(QT) +IntRect ContextShadow::calculateLayerBoundingRect(const PlatformContext p, const FloatRect& layerArea, const IntRect& clipRect) +{ + // Calculate the destination of the blurred and/or transformed layer. + FloatRect layerFloatRect; + float inflation = 0; + + const QTransform transform = p->transform(); + if (m_shadowsIgnoreTransforms && !transform.isIdentity()) { + QPolygonF transformedPolygon = transform.map(QPolygonF(layerArea)); + transformedPolygon.translate(offset()); + layerFloatRect = transform.inverted().map(transformedPolygon).boundingRect(); + } else { + layerFloatRect = layerArea; + layerFloatRect.move(m_offset); + } + + // We expand the area by the blur radius to give extra space for the blur transition. + if (m_type == BlurShadow) { + layerFloatRect.inflate(m_blurDistance); + inflation += m_blurDistance; + } + + if (!clipRect.contains(enclosingIntRect(layerFloatRect))) { + // No need to have the buffer larger than the clip. + layerFloatRect.intersect(clipRect); + + // If we are totally outside the clip region, we aren't painting at all. + if (layerFloatRect.isEmpty()) + return IntRect(0, 0, 0, 0); + + // We adjust again because the pixels at the borders are still + // potentially affected by the pixels outside the buffer. + if (m_type == BlurShadow) { + layerFloatRect.inflate(m_blurDistance); + inflation += m_blurDistance; + } + } + + const int frameSize = inflation * 2; + m_sourceRect = IntRect(0, 0, layerArea.width() + frameSize, layerArea.height() + frameSize); + m_layerOrigin = FloatPoint(layerFloatRect.x(), layerFloatRect.y()); + return enclosingIntRect(layerFloatRect); +} +#endif + +#if PLATFORM(CAIRO) void ContextShadow::calculateLayerBoundingRect(const FloatRect& layerArea, const IntRect& clipRect) { // Calculate the destination of the blurred layer. @@ -173,5 +222,6 @@ void ContextShadow::calculateLayerBoundingRect(const FloatRect& layerArea, const m_layerRect.inflate(m_type == BlurShadow ? m_blurDistance : 0); } } +#endif } // namespace WebCore diff --git a/third_party/WebKit/WebCore/platform/graphics/ContextShadow.h b/third_party/WebKit/WebCore/platform/graphics/ContextShadow.h index fa778af..c0776bb 100644 --- a/third_party/WebKit/WebCore/platform/graphics/ContextShadow.h +++ b/third_party/WebKit/WebCore/platform/graphics/ContextShadow.h @@ -106,27 +106,41 @@ public: PlatformContext beginShadowLayer(PlatformContext, const FloatRect& layerArea); void endShadowLayer(PlatformContext); static void purgeScratchBuffer(); + void setShadowsIgnoreTransforms(bool enable) { m_shadowsIgnoreTransforms = enable; } + bool shadowsIgnoreTransforms() const { return m_shadowsIgnoreTransforms; } #if PLATFORM(CAIRO) void drawRectShadow(GraphicsContext* context, const IntRect& rect, const IntSize& topLeftRadius = IntSize(), const IntSize& topRightRadius = IntSize(), const IntSize& bottomLeftRadius = IntSize(), const IntSize& bottomRightRadius = IntSize()); #endif - #if PLATFORM(QT) - QPointF offset() { return QPointF(m_offset.width(), m_offset.height()); } + QPointF offset() const { return QPointF(m_offset.width(), m_offset.height()); } #endif private: - IntRect m_layerRect; + // Buffer to where the temporary shadow will be drawn to. PlatformImage m_layerImage; + // Context used to paint the shadow to the layer image. PlatformContext m_layerContext; - +#if PLATFORM(QT) + // Sub-rect of m_layerImage that contains the shadow pixels. + FloatRect m_sourceRect; + // Top-left corner of the bounding rect where the shadow image needs to be drawn. + FloatPoint m_layerOrigin; +#endif +#if PLATFORM(CAIRO) + // Enclosing int rect where shadow needs to be drawn to using the layer context. + IntRect m_layerRect; // Used for reference when canvas scale(x,y) was called. FloatRect m_unscaledLayerRect; +#endif + bool m_shadowsIgnoreTransforms; void blurLayerImage(unsigned char*, const IntSize& imageSize, int stride); - void calculateLayerBoundingRect(const FloatRect& layerArea, const IntRect& clipRect); #if PLATFORM(CAIRO) + void calculateLayerBoundingRect(const FloatRect& layerArea, const IntRect& clipRect); void drawRectShadowWithoutTiling(PlatformContext context, const IntRect& shadowRect, const IntSize& topLeftRadius, const IntSize& topRightRadius, const IntSize& bottomLeftRadius, const IntSize& bottomRightRadius, float alpha); +#else + IntRect calculateLayerBoundingRect(const PlatformContext, const FloatRect& layerArea, const IntRect& clipRect); #endif }; diff --git a/third_party/WebKit/WebCore/platform/graphics/qt/ContextShadowQt.cpp b/third_party/WebKit/WebCore/platform/graphics/qt/ContextShadowQt.cpp index f7c70f6..56e0673 100644 --- a/third_party/WebKit/WebCore/platform/graphics/qt/ContextShadowQt.cpp +++ b/third_party/WebKit/WebCore/platform/graphics/qt/ContextShadowQt.cpp @@ -114,24 +114,25 @@ PlatformContext ContextShadow::beginShadowLayer(PlatformContext p, const FloatRe else clipRect = p->transform().inverted().mapRect(p->window()); - m_unscaledLayerRect = layerArea; - calculateLayerBoundingRect(layerArea, IntRect(clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height())); + // Set m_layerOrigin and m_sourceRect. + IntRect clip(clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height()); + IntRect layerRect = calculateLayerBoundingRect(p, layerArea, clip); // Don't paint if we are totally outside the clip region. - if (m_layerRect.isEmpty()) + if (layerRect.isEmpty()) return 0; ShadowBuffer* shadowBuffer = scratchShadowBuffer(); - QImage* shadowImage = shadowBuffer->scratchImage(m_layerRect.size()); + QImage* shadowImage = shadowBuffer->scratchImage(layerRect.size()); m_layerImage = QImage(*shadowImage); m_layerContext = new QPainter; m_layerContext->begin(&m_layerImage); m_layerContext->setFont(p->font()); - m_layerContext->translate(m_offset.width(), m_offset.height()); - // The origin is now the top left corner of the scratch image. - m_layerContext->translate(-m_layerRect.x(), -m_layerRect.y()); + // Set the origin as the top left corner of the scratch image. + const int frameSize = (m_sourceRect.width() - layerArea.width()) / 2; + m_layerContext->translate(-layerArea.x() + frameSize, -layerArea.y() + frameSize); return m_layerContext; } @@ -155,13 +156,7 @@ void ContextShadow::endShadowLayer(PlatformContext p) p.end(); } - const QTransform transform = p->transform(); - if (transform.isScaling()) { - qreal x = m_unscaledLayerRect.x() + m_offset.width() / transform.m11() - m_blurDistance; - qreal y = m_unscaledLayerRect.y() + m_offset.height() / transform.m22() - m_blurDistance; - p->drawImage(QPointF(x, y), m_layerImage); - } else - p->drawImage(m_layerRect.topLeft(), m_layerImage); + p->drawImage(m_layerOrigin, m_layerImage, m_sourceRect); scratchShadowBuffer()->schedulePurge(); } diff --git a/third_party/WebKit/WebCore/platform/graphics/qt/GraphicsContextQt.cpp b/third_party/WebKit/WebCore/platform/graphics/qt/GraphicsContextQt.cpp index 254235e..42e5618 100644 --- a/third_party/WebKit/WebCore/platform/graphics/qt/GraphicsContextQt.cpp +++ b/third_party/WebKit/WebCore/platform/graphics/qt/GraphicsContextQt.cpp @@ -53,7 +53,6 @@ #include "TransparencyLayer.h" #include <QBrush> -#include <QDebug> #include <QGradient> #include <QPaintDevice> #include <QPaintEngine> @@ -175,6 +174,22 @@ static inline Qt::FillRule toQtFillRule(WindRule rule) return Qt::OddEvenFill; } +static inline bool mustUseContextShadow(QPainter* painter, ContextShadow* shadow) +{ + if (shadow->m_type == ContextShadow::BlurShadow) + // We can't avoid ContextShadow, since the shadow has blur. + return true; + if (!shadow->shadowsIgnoreTransforms()) + // We can avoid ContextShadow and optimize, since we're not drawing on a canvas and box + // shadows are affected by the transformation matrix. + return false; + if (painter->transform().isIdentity()) + // We can avoid ContextShadow, since there are no transformations to apply to the canvas. + return false; + // Otherwise, no chance avoiding ContextShadow. + return true; +} + class GraphicsContextPlatformPrivate : public Noncopyable { public: GraphicsContextPlatformPrivate(QPainter*, const QColor& initialSolidColor); @@ -506,33 +521,20 @@ void GraphicsContext::fillPath(const Path& path) if (m_data->hasShadow()) { ContextShadow* shadow = contextShadow(); - if (shadow->m_type != ContextShadow::BlurShadow - && !m_state.fillPattern && !m_state.fillGradient) + if (mustUseContextShadow(p, shadow) || m_state.fillPattern || m_state.fillGradient) { - QPointF offset = shadow->offset(); - const QTransform& transform = p->transform(); - if (transform.isScaling()) { - // If scaling is required, find the new coord for shadow origin, - // so that the relative offset to its shape is kept. - QPointF translatedOffset(offset.x() / transform.m11(), - offset.y() / transform.m22()); - platformPath.translate(translatedOffset); - p->fillPath(platformPath, QColor(shadow->m_color)); - platformPath.translate(-translatedOffset); - } else { - p->translate(offset); - p->fillPath(platformPath, QColor(shadow->m_color)); - p->translate(-offset); - } - } else { QPainter* shadowPainter = shadow->beginShadowLayer(p, platformPath.controlPointRect()); if (shadowPainter) { shadowPainter->setCompositionMode(QPainter::CompositionMode_Source); shadowPainter->fillPath(platformPath, QColor(m_data->shadow.m_color)); shadow->endShadowLayer(p); } + } else { + QPointF offset = shadow->offset(); + p->translate(offset); + p->fillPath(platformPath, QColor(shadow->m_color)); + p->translate(-offset); } - } if (m_state.fillPattern) { AffineTransform affine; @@ -557,28 +559,8 @@ void GraphicsContext::strokePath(const Path& path) if (m_data->hasShadow()) { ContextShadow* shadow = contextShadow(); - - if (shadow->m_type != ContextShadow::BlurShadow - && !m_state.strokePattern && !m_state.strokeGradient) + if (mustUseContextShadow(p, shadow) || m_state.strokePattern || m_state.strokeGradient) { - QPen shadowPen(pen); - shadowPen.setColor(m_data->shadow.m_color); - QPointF offset = shadow->offset(); - const QTransform& transform = p->transform(); - if (transform.isScaling()) { - // If scaling is required, find the new coord for shadow origin, - // so that the relative offset to its shape is kept. - QPointF translatedOffset(offset.x() / transform.m11(), - offset.y() / transform.m22()); - platformPath.translate(translatedOffset); - p->strokePath(platformPath, shadowPen); - platformPath.translate(-translatedOffset); - } else { - p->translate(offset); - p->strokePath(platformPath, shadowPen); - p->translate(-offset); - } - } else { FloatRect boundingRect = platformPath.controlPointRect(); boundingRect.inflate(pen.miterLimit() + pen.widthF()); QPainter* shadowPainter = shadow->beginShadowLayer(p, boundingRect); @@ -587,6 +569,13 @@ void GraphicsContext::strokePath(const Path& path) shadowPainter->strokePath(platformPath, pen); shadow->endShadowLayer(p); } + } else { + QPen shadowPen(pen); + shadowPen.setColor(m_data->shadow.m_color); + QPointF offset = shadow->offset(); + p->translate(offset); + p->strokePath(platformPath, shadowPen); + p->translate(-offset); } } @@ -706,7 +695,7 @@ void GraphicsContext::fillRect(const FloatRect& rect) p->fillRect(normalizedRect, brush); } else { if (m_data->hasShadow()) { - if (shadow->m_type == ContextShadow::BlurShadow) { + if (mustUseContextShadow(p, shadow)) { QPainter* shadowPainter = shadow->beginShadowLayer(p, normalizedRect); if (shadowPainter) { shadowPainter->setOpacity(static_cast<qreal>(shadow->m_color.alpha()) / 255); @@ -714,17 +703,11 @@ void GraphicsContext::fillRect(const FloatRect& rect) shadow->endShadowLayer(p); } } else { - // Solid rectangle fill with no blur shadow can be done faster - // without using the shadow layer at all. + // Solid rectangle fill with no blur shadow or transformations applied can be done + // faster without using the shadow layer at all. QColor shadowColor = shadow->m_color; shadowColor.setAlphaF(shadowColor.alphaF() * p->brush().color().alphaF()); - const QTransform& transform = p->transform(); - if (transform.isScaling()) { - p->fillRect(normalizedRect.translated(static_cast<qreal>(shadow->offset().x()) / transform.m11(), - static_cast<qreal>(shadow->offset().y() / transform.m22())), - shadowColor); - } else - p->fillRect(normalizedRect.translated(shadow->offset()), shadowColor); + p->fillRect(normalizedRect.translated(shadow->offset()), shadowColor); } } @@ -744,18 +727,15 @@ void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorS if (m_data->hasShadow()) { ContextShadow* shadow = contextShadow(); - - if (shadow->m_type != ContextShadow::BlurShadow) { - // We do not need any layer for simple shadow. - p->fillRect(normalizedRect.translated(shadow->offset()), shadow->m_color); - } else { + if (mustUseContextShadow(p, shadow)) { QPainter* shadowPainter = shadow->beginShadowLayer(p, normalizedRect); if (shadowPainter) { shadowPainter->setCompositionMode(QPainter::CompositionMode_Source); shadowPainter->fillRect(normalizedRect, shadow->m_color); shadow->endShadowLayer(p); } - } + } else + p->fillRect(normalizedRect.translated(shadow->offset()), shadow->m_color); } p->fillRect(normalizedRect, m_data->solidColor); @@ -771,19 +751,17 @@ void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLef QPainter* p = m_data->p(); if (m_data->hasShadow()) { ContextShadow* shadow = contextShadow(); - - if (shadow->m_type != ContextShadow::BlurShadow) { - // We do not need any layer for simple shadow. - p->translate(m_data->shadow.offset()); - p->fillPath(path.platformPath(), QColor(m_data->shadow.m_color)); - p->translate(-m_data->shadow.offset()); - } else { + if (mustUseContextShadow(p, shadow)) { QPainter* shadowPainter = shadow->beginShadowLayer(p, rect); if (shadowPainter) { shadowPainter->setCompositionMode(QPainter::CompositionMode_Source); shadowPainter->fillPath(path.platformPath(), QColor(m_data->shadow.m_color)); shadow->endShadowLayer(p); } + } else { + p->translate(m_data->shadow.offset()); + p->fillPath(path.platformPath(), QColor(m_data->shadow.m_color)); + p->translate(-m_data->shadow.offset()); } } p->fillPath(path.platformPath(), QColor(color)); @@ -971,9 +949,10 @@ void GraphicsContext::setPlatformShadow(const FloatSize& size, float blur, const // We flip the height since CG and HTML5 Canvas have opposite Y axis m_state.shadowOffset = FloatSize(size.width(), -size.height()); m_data->shadow = ContextShadow(color, blur, FloatSize(size.width(), -size.height())); - } else { + } else m_data->shadow = ContextShadow(color, blur, FloatSize(size.width(), size.height())); - } + + m_data->shadow.setShadowsIgnoreTransforms(m_state.shadowsIgnoreTransforms); } void GraphicsContext::clearPlatformShadow() |
