OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "config.h" | |
6 | |
7 #include "core/html/canvas/CanvasRenderingContext2D.h" | |
8 | |
9 #include "core/frame/FrameView.h" | |
10 #include "core/html/HTMLDocument.h" | |
11 #include "core/html/ImageData.h" | |
12 #include "core/html/canvas/CanvasGradient.h" | |
13 #include "core/html/canvas/CanvasPattern.h" | |
14 #include "core/html/canvas/WebGLRenderingContext.h" | |
15 #include "core/loader/EmptyClients.h" | |
16 #include "core/testing/DummyPageHolder.h" | |
17 #include "platform/graphics/StaticBitmapImage.h" | |
18 #include "platform/graphics/UnacceleratedImageBufferSurface.h" | |
19 #include "third_party/skia/include/core/SkSurface.h" | |
20 #include <gmock/gmock.h> | |
21 #include <gtest/gtest.h> | |
22 | |
23 using namespace blink; | |
24 using ::testing::Mock; | |
25 | |
26 namespace { | |
27 | |
28 enum BitmapOpacity { | |
29 OpaqueBitmap, | |
30 TransparentBitmap | |
31 }; | |
32 | |
33 class FakeImageSource : public CanvasImageSource { | |
34 public: | |
35 FakeImageSource(IntSize, BitmapOpacity); | |
36 | |
37 PassRefPtr<Image> getSourceImageForCanvas(SourceImageMode, SourceImageStatus *) const override; | |
38 | |
39 bool wouldTaintOrigin(SecurityOrigin* destinationSecurityOrigin) const overr ide { return false; } | |
40 FloatSize sourceSize() const override { return FloatSize(m_size); } | |
41 bool isOpaque() const override { return m_isOpaque; } | |
42 | |
43 virtual ~FakeImageSource() { } | |
44 | |
45 private: | |
46 IntSize m_size; | |
47 RefPtr<Image> m_image; | |
48 bool m_isOpaque; | |
49 }; | |
50 | |
51 FakeImageSource::FakeImageSource(IntSize size, BitmapOpacity opacity) | |
52 : m_size(size) | |
53 , m_isOpaque(opacity == OpaqueBitmap) | |
54 { | |
55 SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(m_size.width() , m_size.height())); | |
56 surface->getCanvas()->clear(opacity == OpaqueBitmap ? SK_ColorWHITE : SK_Col orTRANSPARENT); | |
57 RefPtr<SkImage> image = adoptRef(surface->newImageSnapshot()); | |
58 m_image = StaticBitmapImage::create(image); | |
59 } | |
60 | |
61 PassRefPtr<Image> FakeImageSource::getSourceImageForCanvas(SourceImageMode, Sour ceImageStatus* status) const | |
62 { | |
63 if (status) | |
64 *status = NormalSourceImageStatus; | |
65 return m_image; | |
66 } | |
67 | |
68 //============================================================================ | |
69 | |
70 class CanvasRenderingContext2DTest : public ::testing::Test { | |
71 protected: | |
72 CanvasRenderingContext2DTest(); | |
73 virtual void SetUp() override; | |
74 | |
75 DummyPageHolder& page() const { return *m_dummyPageHolder; } | |
76 HTMLDocument& document() const { return *m_document; } | |
77 HTMLCanvasElement& canvasElement() const { return *m_canvasElement; } | |
78 CanvasRenderingContext2D* context2d() const { return static_cast<CanvasRende ringContext2D*>(canvasElement().renderingContext()); } | |
79 | |
80 void createContext(OpacityMode); | |
81 | |
82 private: | |
83 OwnPtr<DummyPageHolder> m_dummyPageHolder; | |
84 RefPtrWillBePersistent<HTMLDocument> m_document; | |
85 RefPtrWillBePersistent<HTMLCanvasElement> m_canvasElement; | |
86 | |
87 protected: | |
88 // Pre-canned objects for testing | |
89 RefPtrWillBePersistent<ImageData> m_fullImageData; | |
90 RefPtrWillBePersistent<ImageData> m_partialImageData; | |
91 FakeImageSource m_opaqueBitmap; | |
92 FakeImageSource m_alphaBitmap; | |
93 StringOrCanvasGradientOrCanvasPattern m_opaqueGradient; | |
94 StringOrCanvasGradientOrCanvasPattern m_alphaGradient; | |
95 }; | |
96 | |
97 CanvasRenderingContext2DTest::CanvasRenderingContext2DTest() | |
98 : m_opaqueBitmap(IntSize(10, 10), OpaqueBitmap) | |
99 , m_alphaBitmap(IntSize(10, 10), TransparentBitmap) | |
100 { } | |
101 | |
102 void CanvasRenderingContext2DTest::createContext(OpacityMode opacityMode) | |
103 { | |
104 String canvasType("2d"); | |
105 CanvasContextCreationAttributes attributes; | |
106 attributes.setAlpha(opacityMode == NonOpaque); | |
107 CanvasRenderingContext2DOrWebGLRenderingContext result; | |
108 m_canvasElement->getContext(canvasType, attributes, result); | |
109 } | |
110 | |
111 void CanvasRenderingContext2DTest::SetUp() | |
112 { | |
113 Page::PageClients pageClients; | |
114 fillWithEmptyClients(pageClients); | |
115 m_dummyPageHolder = DummyPageHolder::create(IntSize(800, 600), &pageClients) ; | |
116 m_document = toHTMLDocument(&m_dummyPageHolder->document()); | |
117 m_document->documentElement()->setInnerHTML("<body><canvas id='c'></canvas>< /body>", ASSERT_NO_EXCEPTION); | |
118 m_document->view()->updateLayoutAndStyleIfNeededRecursive(); | |
119 m_canvasElement = toHTMLCanvasElement(m_document->getElementById("c")); | |
120 | |
121 m_fullImageData = ImageData::create(IntSize(10, 10)); | |
122 m_partialImageData = ImageData::create(IntSize(2, 2)); | |
123 | |
124 NonThrowableExceptionState exceptionState; | |
125 RefPtrWillBeRawPtr<CanvasGradient> opaqueGradient = CanvasGradient::create(F loatPoint(0, 0), FloatPoint(10, 0)); | |
126 opaqueGradient->addColorStop(0, String("green"), exceptionState); | |
127 EXPECT_FALSE(exceptionState.hadException()); | |
128 opaqueGradient->addColorStop(1, String("blue"), exceptionState); | |
129 EXPECT_FALSE(exceptionState.hadException()); | |
130 m_opaqueGradient.setCanvasGradient(opaqueGradient); | |
131 | |
132 RefPtrWillBeRawPtr<CanvasGradient> alphaGradient = CanvasGradient::create(Fl oatPoint(0, 0), FloatPoint(10, 0)); | |
133 alphaGradient->addColorStop(0, String("green"), exceptionState); | |
134 EXPECT_FALSE(exceptionState.hadException()); | |
135 alphaGradient->addColorStop(1, String("rgba(0, 0, 255, 0.5)"), exceptionStat e); | |
136 EXPECT_FALSE(exceptionState.hadException()); | |
137 StringOrCanvasGradientOrCanvasPattern wrappedAlphaGradient; | |
138 m_alphaGradient.setCanvasGradient(alphaGradient); | |
139 } | |
140 | |
141 //============================================================================ | |
142 | |
143 class MockImageBufferSurfaceForOverwriteTesting : public UnacceleratedImageBuffe rSurface { | |
144 public: | |
145 MockImageBufferSurfaceForOverwriteTesting(const IntSize& size, OpacityMode m ode) : UnacceleratedImageBufferSurface(size, mode) { } | |
146 virtual ~MockImageBufferSurfaceForOverwriteTesting() { } | |
147 bool isRecording() const override { return true; } // otherwise overwrites a re not tracked | |
148 | |
149 MOCK_METHOD0(willOverwriteCanvas, void()); | |
150 }; | |
151 | |
152 //============================================================================ | |
153 | |
154 #define TEST_OVERDRAW_SETUP(EXPECTED_OVERDRAWS) \ | |
155 OwnPtr<MockImageBufferSurfaceForOverwriteTesting> mockSurface = adoptPtr (new MockImageBufferSurfaceForOverwriteTesting(IntSize(10, 10), NonOpaque)); \ | |
156 MockImageBufferSurfaceForOverwriteTesting* surfacePtr = mockSurface.get( ); \ | |
157 canvasElement().createImageBufferUsingSurface(mockSurface.release()); \ | |
158 EXPECT_CALL(*surfacePtr, willOverwriteCanvas()).Times(EXPECTED_OVERDRAWS ); \ | |
159 context2d()->save(); | |
160 | |
161 #define TEST_OVERDRAW_FINALIZE \ | |
162 context2d()->restore(); \ | |
163 Mock::VerifyAndClearExpectations(surfacePtr); | |
164 | |
165 #define TEST_OVERDRAW_1(EXPECTED_OVERDRAWS, CALL1) \ | |
166 do { \ | |
167 TEST_OVERDRAW_SETUP(EXPECTED_OVERDRAWS) \ | |
168 context2d()->CALL1; \ | |
169 TEST_OVERDRAW_FINALIZE \ | |
170 } while (0) | |
171 | |
172 #define TEST_OVERDRAW_2(EXPECTED_OVERDRAWS, CALL1, CALL2) \ | |
173 do { \ | |
174 TEST_OVERDRAW_SETUP(EXPECTED_OVERDRAWS) \ | |
175 context2d()->CALL1; \ | |
176 context2d()->CALL2; \ | |
177 TEST_OVERDRAW_FINALIZE \ | |
178 } while (0) | |
179 | |
180 #define TEST_OVERDRAW_3(EXPECTED_OVERDRAWS, CALL1, CALL2, CALL3) \ | |
181 do { \ | |
182 TEST_OVERDRAW_SETUP(EXPECTED_OVERDRAWS) \ | |
183 context2d()->CALL1; \ | |
184 context2d()->CALL2; \ | |
185 context2d()->CALL3; \ | |
186 TEST_OVERDRAW_FINALIZE \ | |
187 } while (0) | |
188 | |
189 #define TEST_OVERDRAW_4(EXPECTED_OVERDRAWS, CALL1, CALL2, CALL3, CALL4) \ | |
190 do { \ | |
191 TEST_OVERDRAW_SETUP(EXPECTED_OVERDRAWS) \ | |
192 context2d()->CALL1; \ | |
193 context2d()->CALL2; \ | |
194 context2d()->CALL3; \ | |
195 context2d()->CALL4; \ | |
196 TEST_OVERDRAW_FINALIZE \ | |
197 } while (0) | |
198 | |
199 //============================================================================ | |
200 | |
201 TEST_F(CanvasRenderingContext2DTest, detectOverdrawWithFillRect) | |
202 { | |
203 createContext(NonOpaque); | |
204 | |
205 TEST_OVERDRAW_1(1, fillRect(-1, -1, 12, 12)); | |
206 TEST_OVERDRAW_1(1, fillRect(0, 0, 10, 10)); | |
207 TEST_OVERDRAW_1(0, strokeRect(0, 0, 10, 10)); // stroking instead of filling does not overwrite | |
208 TEST_OVERDRAW_2(0, setGlobalAlpha(0.5f), fillRect(0, 0, 10, 10)); | |
209 TEST_OVERDRAW_1(0, fillRect(0, 0, 9, 9)); | |
210 TEST_OVERDRAW_2(0, translate(1, 1), fillRect(0, 0, 10, 10)); | |
211 TEST_OVERDRAW_2(1, translate(1, 1), fillRect(-1, -1, 10, 10)); | |
212 TEST_OVERDRAW_2(1, setFillStyle(m_opaqueGradient), fillRect(0, 0, 10, 10)); | |
213 TEST_OVERDRAW_2(0, setFillStyle(m_alphaGradient), fillRect(0, 0, 10, 10)); | |
214 TEST_OVERDRAW_3(0, setGlobalAlpha(0.5), setFillStyle(m_opaqueGradient), fill Rect(0, 0, 10, 10)); | |
215 TEST_OVERDRAW_3(1, setGlobalAlpha(0.5f), setGlobalCompositeOperation(String( "copy")), fillRect(0, 0, 10, 10)); | |
216 TEST_OVERDRAW_2(1, setGlobalCompositeOperation(String("copy")), fillRect(0, 0, 9, 9)); | |
217 TEST_OVERDRAW_3(0, rect(0, 0, 5, 5), clip(), fillRect(0, 0, 10, 10)); | |
218 TEST_OVERDRAW_4(0, rect(0, 0, 5, 5), clip(), setGlobalCompositeOperation(Str ing("copy")), fillRect(0, 0, 10, 10)); | |
219 } | |
220 | |
221 TEST_F(CanvasRenderingContext2DTest, detectOverdrawWithClearRect) | |
222 { | |
223 createContext(NonOpaque); | |
224 | |
225 TEST_OVERDRAW_1(1, clearRect(0, 0, 10, 10)); | |
226 TEST_OVERDRAW_1(0, clearRect(0, 0, 9, 9)); | |
227 TEST_OVERDRAW_2(1, setGlobalAlpha(0.5f), clearRect(0, 0, 10, 10)); | |
228 TEST_OVERDRAW_2(1, setFillStyle(m_alphaGradient), clearRect(0, 0, 10, 10)); | |
229 TEST_OVERDRAW_2(0, translate(1, 1), fillRect(0, 0, 10, 10)); | |
230 TEST_OVERDRAW_2(1, translate(1, 1), fillRect(-1, -1, 10, 10)); | |
Ken Russell (switch to Gerrit)
2015/02/12 11:48:06
These two fillRects should be clearRect.
| |
231 TEST_OVERDRAW_2(1, setGlobalCompositeOperation(String("destination-in")), cl earRect(0, 0, 10, 10)); // composite op ignored | |
232 TEST_OVERDRAW_3(0, rect(0, 0, 5, 5), clip(), clearRect(0, 0, 10, 10)); | |
233 } | |
234 | |
235 TEST_F(CanvasRenderingContext2DTest, detectOverdrawWithDrawImage) | |
236 { | |
237 createContext(NonOpaque); | |
238 NonThrowableExceptionState exceptionState; | |
239 | |
240 TEST_OVERDRAW_1(1, drawImage(&m_opaqueBitmap, 0, 0, 10, 10, 0, 0, 10, 10, ex ceptionState)); | |
241 EXPECT_FALSE(exceptionState.hadException()); | |
242 TEST_OVERDRAW_1(1, drawImage(&m_opaqueBitmap, 0, 0, 1, 1, 0, 0, 10, 10, exce ptionState)); | |
243 EXPECT_FALSE(exceptionState.hadException()); | |
244 TEST_OVERDRAW_2(0, setGlobalAlpha(0.5f), drawImage(&m_opaqueBitmap, 0, 0, 10 , 10, 0, 0, 10, 10, exceptionState)); | |
245 EXPECT_FALSE(exceptionState.hadException()); | |
246 TEST_OVERDRAW_1(0, drawImage(&m_alphaBitmap, 0, 0, 10, 10, 0, 0, 10, 10, exc eptionState)); | |
247 EXPECT_FALSE(exceptionState.hadException()); | |
248 TEST_OVERDRAW_2(0, setGlobalAlpha(0.5f), drawImage(&m_alphaBitmap, 0, 0, 10, 10, 0, 0, 10, 10, exceptionState)); | |
249 EXPECT_FALSE(exceptionState.hadException()); | |
250 TEST_OVERDRAW_1(0, drawImage(&m_opaqueBitmap, 0, 0, 10, 10, 1, 0, 10, 10, ex ceptionState)); | |
251 EXPECT_FALSE(exceptionState.hadException()); | |
252 TEST_OVERDRAW_1(0, drawImage(&m_opaqueBitmap, 0, 0, 10, 10, 0, 0, 9, 9, exce ptionState)); | |
253 EXPECT_FALSE(exceptionState.hadException()); | |
254 TEST_OVERDRAW_1(1, drawImage(&m_opaqueBitmap, 0, 0, 10, 10, 0, 0, 11, 11, ex ceptionState)); | |
255 EXPECT_FALSE(exceptionState.hadException()); | |
256 TEST_OVERDRAW_2(1, translate(-1, 0), drawImage(&m_opaqueBitmap, 0, 0, 10, 10 , 1, 0, 10, 10, exceptionState)); | |
257 EXPECT_FALSE(exceptionState.hadException()); | |
258 TEST_OVERDRAW_2(0, translate(-1, 0), drawImage(&m_opaqueBitmap, 0, 0, 10, 10 , 0, 0, 10, 10, exceptionState)); | |
259 EXPECT_FALSE(exceptionState.hadException()); | |
260 TEST_OVERDRAW_2(0, setFillStyle(m_opaqueGradient), drawImage(&m_alphaBitmap, 0, 0, 10, 10, 0, 0, 10, 10, exceptionState)); // fillStyle ignored by drawImage | |
261 EXPECT_FALSE(exceptionState.hadException()); | |
262 TEST_OVERDRAW_2(1, setFillStyle(m_alphaGradient), drawImage(&m_opaqueBitmap, 0, 0, 10, 10, 0, 0, 10, 10, exceptionState)); // fillStyle ignored by drawImage | |
263 EXPECT_FALSE(exceptionState.hadException()); | |
264 TEST_OVERDRAW_2(1, setGlobalCompositeOperation(String("copy")), drawImage(&m _opaqueBitmap, 0, 0, 10, 10, 1, 0, 10, 10, exceptionState)); | |
265 EXPECT_FALSE(exceptionState.hadException()); | |
266 TEST_OVERDRAW_3(0, rect(0, 0, 5, 5), clip(), drawImage(&m_opaqueBitmap, 0, 0 , 10, 10, 0, 0, 10, 10, exceptionState)); | |
267 EXPECT_FALSE(exceptionState.hadException()); | |
268 } | |
269 | |
270 TEST_F(CanvasRenderingContext2DTest, detectOverdrawWithPutImageData) | |
271 { | |
272 createContext(NonOpaque); | |
273 | |
274 // Test putImageData | |
275 TEST_OVERDRAW_1(1, putImageData(m_fullImageData.get(), 0, 0)); | |
276 TEST_OVERDRAW_1(1, putImageData(m_fullImageData.get(), 0, 0, 0, 0, 10, 10)); | |
277 TEST_OVERDRAW_1(0, putImageData(m_fullImageData.get(), 0, 0, 1, 1, 8, 8)); | |
278 TEST_OVERDRAW_2(1, setGlobalAlpha(0.5f), putImageData(m_fullImageData.get(), 0, 0)); // alpha has no effect | |
279 TEST_OVERDRAW_1(0, putImageData(m_partialImageData.get(), 0, 0)); | |
280 TEST_OVERDRAW_2(1, translate(1, 1), putImageData(m_fullImageData.get(), 0, 0 )); // ignores tranforms | |
281 TEST_OVERDRAW_1(0, putImageData(m_fullImageData.get(), 1, 0)); | |
282 TEST_OVERDRAW_3(1, rect(0, 0, 5, 5), clip(), putImageData(m_fullImageData.ge t(), 0, 0)); // ignores clip | |
283 } | |
284 | |
285 TEST_F(CanvasRenderingContext2DTest, detectOverdrawWithCompositeOperations) | |
286 { | |
287 createContext(NonOpaque); | |
288 | |
289 // Test composite operators with an opaque rect that covers the entire canva s | |
290 // Note: all the untested composite operations take the same code path as so urce-in, | |
291 // which assumes that the destination may not be overwritten | |
292 TEST_OVERDRAW_2(1, setGlobalCompositeOperation(String("clear")), fillRect(0, 0, 10, 10)); | |
293 TEST_OVERDRAW_2(1, setGlobalCompositeOperation(String("copy")), fillRect(0, 0, 10, 10)); | |
294 TEST_OVERDRAW_2(1, setGlobalCompositeOperation(String("source-over")), fillR ect(0, 0, 10, 10)); | |
295 TEST_OVERDRAW_2(0, setGlobalCompositeOperation(String("source-in")), fillRec t(0, 0, 10, 10)); | |
296 // Test composite operators with a transparent rect that covers the entire c anvas | |
297 TEST_OVERDRAW_3(1, setGlobalAlpha(0.5f), setGlobalCompositeOperation(String( "clear")), fillRect(0, 0, 10, 10)); | |
298 TEST_OVERDRAW_3(1, setGlobalAlpha(0.5f), setGlobalCompositeOperation(String( "copy")), fillRect(0, 0, 10, 10)); | |
299 TEST_OVERDRAW_3(0, setGlobalAlpha(0.5f), setGlobalCompositeOperation(String( "source-over")), fillRect(0, 0, 10, 10)); | |
300 TEST_OVERDRAW_3(0, setGlobalAlpha(0.5f), setGlobalCompositeOperation(String( "source-in")), fillRect(0, 0, 10, 10)); | |
301 // Test composite operators with an opaque rect that does not cover the enti re canvas | |
302 TEST_OVERDRAW_2(0, setGlobalCompositeOperation(String("clear")), fillRect(0, 0, 5, 5)); | |
303 TEST_OVERDRAW_2(1, setGlobalCompositeOperation(String("copy")), fillRect(0, 0, 5, 5)); | |
304 TEST_OVERDRAW_2(0, setGlobalCompositeOperation(String("source-over")), fillR ect(0, 0, 5, 5)); | |
305 TEST_OVERDRAW_2(0, setGlobalCompositeOperation(String("source-in")), fillRec t(0, 0, 5, 5)); | |
306 } | |
307 | |
308 } // unnamed namespace | |
OLD | NEW |