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 class CanvasRenderingContext2DTest : public ::testing::Test { | |
29 protected: | |
30 virtual void SetUp() override; | |
31 | |
32 DummyPageHolder& page() const { return *m_dummyPageHolder; } | |
33 HTMLDocument& document() const { return *m_document; } | |
34 HTMLCanvasElement& canvasElement() const { return *m_canvasElement; } | |
35 CanvasRenderingContext2D* context2d() const { return static_cast<CanvasRende ringContext2D*>(canvasElement().renderingContext()); } | |
36 | |
37 void createContext(OpacityMode); | |
38 | |
39 private: | |
40 OwnPtr<DummyPageHolder> m_dummyPageHolder; | |
41 RefPtrWillBePersistent<HTMLDocument> m_document; | |
42 RefPtrWillBePersistent<HTMLCanvasElement> m_canvasElement; | |
43 }; | |
44 | |
45 void CanvasRenderingContext2DTest::createContext(OpacityMode opacityMode) | |
46 { | |
47 String canvasType("2d"); | |
48 CanvasContextCreationAttributes attributes; | |
49 attributes.setAlpha(opacityMode == NonOpaque); | |
50 CanvasRenderingContext2DOrWebGLRenderingContext result; | |
51 m_canvasElement->getContext(canvasType, attributes, result); | |
52 } | |
53 | |
54 void CanvasRenderingContext2DTest::SetUp() | |
55 { | |
56 Page::PageClients pageClients; | |
57 fillWithEmptyClients(pageClients); | |
58 m_dummyPageHolder = DummyPageHolder::create(IntSize(800, 600), &pageClients) ; | |
59 m_document = toHTMLDocument(&m_dummyPageHolder->document()); | |
60 m_document->documentElement()->setInnerHTML("<body><canvas id='c'></canvas>< /body>", ASSERT_NO_EXCEPTION); | |
61 m_document->view()->updateLayoutAndStyleIfNeededRecursive(); | |
62 m_canvasElement = toHTMLCanvasElement(m_document->getElementById("c")); | |
63 } | |
64 | |
65 //============================================================================ | |
66 | |
67 class PLATFORM_EXPORT MockImageBufferSurfaceForOverwriteTesting : public Unaccel eratedImageBufferSurface { | |
68 public: | |
69 MockImageBufferSurfaceForOverwriteTesting(const IntSize& size, OpacityMode m ode) : UnacceleratedImageBufferSurface(size, mode) { } | |
70 virtual ~MockImageBufferSurfaceForOverwriteTesting() { } | |
71 bool isRecording() const override { return true; } // otherwise overwrites a re not tracked | |
72 | |
73 MOCK_METHOD0(willOverwriteCanvas, void()); | |
74 }; | |
75 | |
76 //============================================================================ | |
77 | |
78 #define TEST_OVERWRITE_SETUP(EXPECTED_OVERWRITES) \ | |
Ken Russell (switch to Gerrit)
2015/02/11 10:11:44
Throughout this test file, would it make sense to
Justin Novosad
2015/02/11 18:59:54
Done.
| |
79 OwnPtr<MockImageBufferSurfaceForOverwriteTesting> mockSurface = adoptPtr (new MockImageBufferSurfaceForOverwriteTesting(IntSize(10, 10), NonOpaque)); \ | |
80 MockImageBufferSurfaceForOverwriteTesting* surfacePtr = mockSurface.get( ); \ | |
81 canvasElement().createImageBufferUsingSurface(mockSurface.release()); \ | |
82 EXPECT_CALL(*surfacePtr, willOverwriteCanvas()).Times(EXPECTED_OVERWRITE S); \ | |
83 context2d()->save(); | |
84 | |
85 #define TEST_OVERWRITE_FINALIZE \ | |
86 context2d()->restore(); \ | |
87 Mock::VerifyAndClearExpectations(surfacePtr); | |
88 | |
89 #define TEST_OVERWRITE_1(EXPECTED_OVERWRITES, CALL1) \ | |
90 do { \ | |
91 TEST_OVERWRITE_SETUP(EXPECTED_OVERWRITES) \ | |
92 context2d()->CALL1; \ | |
93 TEST_OVERWRITE_FINALIZE \ | |
94 } while (0) | |
95 | |
96 #define TEST_OVERWRITE_2(EXPECTED_OVERWRITES, CALL1, CALL2) \ | |
97 do { \ | |
98 TEST_OVERWRITE_SETUP(EXPECTED_OVERWRITES) \ | |
99 context2d()->CALL1; \ | |
100 context2d()->CALL2; \ | |
101 TEST_OVERWRITE_FINALIZE \ | |
102 } while (0) | |
103 | |
104 #define TEST_OVERWRITE_3(EXPECTED_OVERWRITES, CALL1, CALL2, CALL3) \ | |
105 do { \ | |
106 TEST_OVERWRITE_SETUP(EXPECTED_OVERWRITES) \ | |
107 context2d()->CALL1; \ | |
108 context2d()->CALL2; \ | |
109 context2d()->CALL3; \ | |
110 TEST_OVERWRITE_FINALIZE \ | |
111 } while (0) | |
112 | |
113 enum BitmapOpacity { | |
114 OpaqueBitmap, | |
115 TransparentBitmap | |
116 }; | |
117 | |
118 //============================================================================ | |
119 | |
120 class FakeImageSource : public CanvasImageSource { | |
121 public: | |
122 FakeImageSource(IntSize, BitmapOpacity); | |
123 | |
124 PassRefPtr<Image> getSourceImageForCanvas(SourceImageMode, SourceImageStatus *) const override; | |
125 | |
126 bool wouldTaintOrigin(SecurityOrigin* destinationSecurityOrigin) const overr ide { return false; } | |
127 FloatSize sourceSize() const override { return FloatSize(m_size); } | |
128 bool isOpaque() const override { return m_isOpaque; } | |
129 | |
130 virtual ~FakeImageSource() { } | |
131 | |
132 private: | |
133 IntSize m_size; | |
134 RefPtr<Image> m_image; | |
135 bool m_isOpaque; | |
136 }; | |
137 | |
138 FakeImageSource::FakeImageSource(IntSize size, BitmapOpacity opacity) | |
139 : m_size(size) | |
140 , m_isOpaque(opacity == OpaqueBitmap) | |
141 { | |
142 SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(m_size.width() , m_size.height())); | |
143 surface->getCanvas()->clear(opacity == OpaqueBitmap ? SK_ColorWHITE : SK_Col orTRANSPARENT); | |
144 RefPtr<SkImage> image = adoptRef(surface->newImageSnapshot()); | |
145 m_image = StaticBitmapImage::create(image); | |
146 } | |
147 | |
148 PassRefPtr<Image> FakeImageSource::getSourceImageForCanvas(SourceImageMode, Sour ceImageStatus* status) const | |
149 { | |
150 if (status) | |
151 *status = NormalSourceImageStatus; | |
152 return m_image; | |
153 } | |
154 | |
155 //============================================================================ | |
156 | |
157 TEST_F(CanvasRenderingContext2DTest, detectOverwrite) | |
dshwang
2015/02/11 08:37:52
nice test!
Ken Russell (switch to Gerrit)
2015/02/11 10:11:44
Consider splitting up this test into a few so it's
Justin Novosad
2015/02/11 18:59:54
Done.
| |
158 { | |
159 RefPtrWillBeRawPtr<ImageData> fullImageData = ImageData::create(IntSize(10, 10)); | |
160 RefPtrWillBeRawPtr<ImageData> partialImageData = ImageData::create(IntSize(2 , 2)); | |
161 FakeImageSource opaqueBitmap(IntSize(10, 10), OpaqueBitmap); | |
162 FakeImageSource alphaBitmap(IntSize(10, 10), TransparentBitmap); | |
163 NonThrowableExceptionState exceptionState; | |
164 | |
165 RefPtrWillBeRawPtr<CanvasGradient> opaqueGradient = CanvasGradient::create(F loatPoint(0, 0), FloatPoint(10, 0)); | |
166 opaqueGradient->addColorStop(0, String("green"), exceptionState); | |
167 EXPECT_FALSE(exceptionState.hadException()); | |
168 opaqueGradient->addColorStop(1, String("blue"), exceptionState); | |
169 EXPECT_FALSE(exceptionState.hadException()); | |
170 StringOrCanvasGradientOrCanvasPattern wrappedOpaqueGradient; | |
171 wrappedOpaqueGradient.setCanvasGradient(opaqueGradient); | |
172 | |
173 RefPtrWillBeRawPtr<CanvasGradient> alphaGradient = CanvasGradient::create(Fl oatPoint(0, 0), FloatPoint(10, 0)); | |
174 alphaGradient->addColorStop(0, String("green"), exceptionState); | |
175 EXPECT_FALSE(exceptionState.hadException()); | |
176 alphaGradient->addColorStop(1, String("rgba(0, 0, 255, 0.5)"), exceptionStat e); | |
177 EXPECT_FALSE(exceptionState.hadException()); | |
178 StringOrCanvasGradientOrCanvasPattern wrappedAlphaGradient; | |
179 wrappedAlphaGradient.setCanvasGradient(alphaGradient); | |
180 | |
181 createContext(NonOpaque); | |
182 | |
183 // Test fillRect | |
184 TEST_OVERWRITE_1(1, fillRect(-1, -1, 12, 12)); | |
185 TEST_OVERWRITE_1(1, fillRect(0, 0, 10, 10)); | |
186 TEST_OVERWRITE_1(0, strokeRect(0, 0, 10, 10)); // stroking instead of fillin g does not overwrite | |
187 TEST_OVERWRITE_2(0, setGlobalAlpha(0.5f), fillRect(0, 0, 10, 10)); | |
188 TEST_OVERWRITE_1(0, fillRect(0, 0, 9, 9)); | |
189 TEST_OVERWRITE_2(0, translate(1, 1), fillRect(0, 0, 10, 10)); | |
Ken Russell (switch to Gerrit)
2015/02/11 10:11:44
Also test transformed rects that would still overd
Justin Novosad
2015/02/11 18:59:54
Done.
| |
190 TEST_OVERWRITE_2(1, setFillStyle(wrappedOpaqueGradient), fillRect(0, 0, 10, 10)); | |
191 TEST_OVERWRITE_2(0, setFillStyle(wrappedAlphaGradient), fillRect(0, 0, 10, 1 0)); | |
192 TEST_OVERWRITE_3(0, setGlobalAlpha(0.5), setFillStyle(wrappedOpaqueGradient) , fillRect(0, 0, 10, 10)); | |
193 TEST_OVERWRITE_3(1, setGlobalAlpha(0.5f), setGlobalCompositeOperation(String ("copy")), fillRect(0, 0, 10, 10)); | |
194 TEST_OVERWRITE_2(1, setGlobalCompositeOperation(String("copy")), fillRect(0, 0, 9, 9)); | |
Ken Russell (switch to Gerrit)
2015/02/11 10:11:44
I don't understand why setting the globalComposite
Justin Novosad
2015/02/11 18:59:54
Close. The way the spec defines the behavior: prim
| |
195 TEST_OVERWRITE_3(0, rect(0, 0, 5, 5), clip(), fillRect(0, 0, 10, 10)); | |
196 | |
197 // Test clearRect | |
198 TEST_OVERWRITE_1(1, clearRect(0, 0, 10, 10)); | |
199 TEST_OVERWRITE_1(0, clearRect(0, 0, 9, 9)); | |
200 TEST_OVERWRITE_2(1, setGlobalAlpha(0.5f), clearRect(0, 0, 10, 10)); | |
201 TEST_OVERWRITE_2(1, setFillStyle(wrappedAlphaGradient), clearRect(0, 0, 10, 10)); | |
202 TEST_OVERWRITE_2(1, setGlobalCompositeOperation(String("destination-in")), c learRect(0, 0, 10, 10)); | |
203 TEST_OVERWRITE_3(0, rect(0, 0, 5, 5), clip(), clearRect(0, 0, 10, 10)); | |
Ken Russell (switch to Gerrit)
2015/02/11 10:11:44
What about transforms in conjunction with clearRec
Justin Novosad
2015/02/11 18:59:54
Done.
| |
204 | |
205 // Test drawImage | |
206 TEST_OVERWRITE_1(1, drawImage(&opaqueBitmap, 0, 0, 10, 10, 0, 0, 10, 10, exc eptionState)); | |
207 EXPECT_FALSE(exceptionState.hadException()); | |
208 TEST_OVERWRITE_1(1, drawImage(&opaqueBitmap, 0, 0, 1, 1, 0, 0, 10, 10, excep tionState)); | |
209 EXPECT_FALSE(exceptionState.hadException()); | |
210 TEST_OVERWRITE_2(0, setGlobalAlpha(0.5f), drawImage(&opaqueBitmap, 0, 0, 10, 10, 0, 0, 10, 10, exceptionState)); | |
211 EXPECT_FALSE(exceptionState.hadException()); | |
212 TEST_OVERWRITE_1(0, drawImage(&alphaBitmap, 0, 0, 10, 10, 0, 0, 10, 10, exce ptionState)); | |
213 EXPECT_FALSE(exceptionState.hadException()); | |
214 TEST_OVERWRITE_2(0, setGlobalAlpha(0.5f), drawImage(&alphaBitmap, 0, 0, 10, 10, 0, 0, 10, 10, exceptionState)); | |
215 EXPECT_FALSE(exceptionState.hadException()); | |
216 TEST_OVERWRITE_1(0, drawImage(&opaqueBitmap, 0, 0, 10, 10, 1, 0, 10, 10, exc eptionState)); | |
217 EXPECT_FALSE(exceptionState.hadException()); | |
218 TEST_OVERWRITE_1(0, drawImage(&opaqueBitmap, 0, 0, 10, 10, 0, 0, 9, 9, excep tionState)); | |
219 EXPECT_FALSE(exceptionState.hadException()); | |
220 TEST_OVERWRITE_1(1, drawImage(&opaqueBitmap, 0, 0, 10, 10, 0, 0, 11, 11, exc eptionState)); | |
221 EXPECT_FALSE(exceptionState.hadException()); | |
222 TEST_OVERWRITE_2(1, translate(-1, 0), drawImage(&opaqueBitmap, 0, 0, 10, 10, 1, 0, 10, 10, exceptionState)); | |
Ken Russell (switch to Gerrit)
2015/02/11 10:11:44
Should also test the case where a transform makes
Justin Novosad
2015/02/11 18:59:54
Done.
| |
223 EXPECT_FALSE(exceptionState.hadException()); | |
224 TEST_OVERWRITE_2(0, setFillStyle(wrappedOpaqueGradient), drawImage(&alphaBit map, 0, 0, 10, 10, 0, 0, 10, 10, exceptionState)); // fillStyle ignored by drawI mage | |
225 EXPECT_FALSE(exceptionState.hadException()); | |
226 TEST_OVERWRITE_2(1, setFillStyle(wrappedAlphaGradient), drawImage(&opaqueBit map, 0, 0, 10, 10, 0, 0, 10, 10, exceptionState)); // fillStyle ignored by drawI mage | |
227 EXPECT_FALSE(exceptionState.hadException()); | |
228 TEST_OVERWRITE_2(1, setGlobalCompositeOperation(String("copy")), drawImage(& opaqueBitmap, 0, 0, 10, 10, 1, 0, 10, 10, exceptionState)); | |
229 EXPECT_FALSE(exceptionState.hadException()); | |
230 TEST_OVERWRITE_3(0, rect(0, 0, 5, 5), clip(), drawImage(&opaqueBitmap, 0, 0, 10, 10, 0, 0, 10, 10, exceptionState)); | |
231 EXPECT_FALSE(exceptionState.hadException()); | |
232 | |
233 // Test putImageData | |
234 TEST_OVERWRITE_1(1, putImageData(fullImageData.get(), 0, 0)); | |
235 TEST_OVERWRITE_1(1, putImageData(fullImageData.get(), 0, 0, 0, 0, 10, 10)); | |
236 TEST_OVERWRITE_1(0, putImageData(fullImageData.get(), 0, 0, 1, 1, 8, 8)); | |
237 TEST_OVERWRITE_2(1, setGlobalAlpha(0.5f), putImageData(fullImageData.get(), 0, 0)); // alpha has no effect | |
238 TEST_OVERWRITE_1(0, putImageData(partialImageData.get(), 0, 0)); | |
239 TEST_OVERWRITE_2(1, translate(1, 1), putImageData(fullImageData.get(), 0, 0) ); // ignores tranforms | |
240 TEST_OVERWRITE_1(0, putImageData(fullImageData.get(), 1, 0)); | |
241 TEST_OVERWRITE_3(1, rect(0, 0, 5, 5), clip(), putImageData(fullImageData.get (), 0, 0)); // ignores clip | |
242 | |
243 // Test composite operators with an opaque rect that covers the entire canva s | |
244 // Note: all the untested composite operations take the same code path as so urce-in, | |
245 // which assumes that the destination may not be overwritten | |
246 TEST_OVERWRITE_2(1, setGlobalCompositeOperation(String("clear")), fillRect(0 , 0, 10, 10)); | |
247 TEST_OVERWRITE_2(1, setGlobalCompositeOperation(String("copy")), fillRect(0, 0, 10, 10)); | |
248 TEST_OVERWRITE_2(1, setGlobalCompositeOperation(String("source-over")), fill Rect(0, 0, 10, 10)); | |
249 TEST_OVERWRITE_2(0, setGlobalCompositeOperation(String("source-in")), fillRe ct(0, 0, 10, 10)); | |
250 // Test composite operators with a transparent rect that covers the entire c anvas | |
251 TEST_OVERWRITE_3(1, setGlobalAlpha(0.5f), setGlobalCompositeOperation(String ("clear")), fillRect(0, 0, 10, 10)); | |
252 TEST_OVERWRITE_3(1, setGlobalAlpha(0.5f), setGlobalCompositeOperation(String ("copy")), fillRect(0, 0, 10, 10)); | |
253 TEST_OVERWRITE_3(0, setGlobalAlpha(0.5f), setGlobalCompositeOperation(String ("source-over")), fillRect(0, 0, 10, 10)); | |
254 TEST_OVERWRITE_3(0, setGlobalAlpha(0.5f), setGlobalCompositeOperation(String ("source-in")), fillRect(0, 0, 10, 10)); | |
255 // Test composite operators with an opaque rect that does not cover the enti re canvas | |
256 TEST_OVERWRITE_2(0, setGlobalCompositeOperation(String("clear")), fillRect(0 , 0, 5, 5)); | |
257 TEST_OVERWRITE_2(1, setGlobalCompositeOperation(String("copy")), fillRect(0, 0, 5, 5)); | |
258 TEST_OVERWRITE_2(0, setGlobalCompositeOperation(String("source-over")), fill Rect(0, 0, 5, 5)); | |
259 TEST_OVERWRITE_2(0, setGlobalCompositeOperation(String("source-in")), fillRe ct(0, 0, 5, 5)); | |
260 } | |
261 | |
262 } // unnamed namespace | |
OLD | NEW |