blob: 701b5cf392bf21f418ff5c6ea21d501c72ada6ba [file] [log] [blame]
junov@chromium.org1f9767c2012-02-07 16:27:57 +00001
2/*
3 * Copyright 2012 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8#include "Test.h"
9#include "SkBitmap.h"
10#include "SkDeferredCanvas.h"
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +000011#include "SkShader.h"
junov@chromium.org1f9767c2012-02-07 16:27:57 +000012
junov@chromium.org1f9767c2012-02-07 16:27:57 +000013static const int gWidth = 2;
14static const int gHeight = 2;
15
16static void create(SkBitmap* bm, SkBitmap::Config config, SkColor color) {
17 bm->setConfig(config, gWidth, gHeight);
18 bm->allocPixels();
19 bm->eraseColor(color);
20}
21
22static void TestDeferredCanvasBitmapAccess(skiatest::Reporter* reporter) {
23 SkBitmap store;
24
25 create(&store, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
26 SkDevice device(store);
27 SkDeferredCanvas canvas(&device);
28
29 canvas.clear(0x00000000);
30
31 SkAutoLockPixels alp(store);
32 REPORTER_ASSERT(reporter, store.getColor(0,0) == 0xFFFFFFFF); //verify that clear was deferred
33 SkBitmap accessed = canvas.getDevice()->accessBitmap(false);
34 REPORTER_ASSERT(reporter, store.getColor(0,0) == 0x00000000); //verify that clear was executed
35 REPORTER_ASSERT(reporter, accessed.pixelRef() == store.pixelRef());
36}
37
38static void TestDeferredCanvasFlush(skiatest::Reporter* reporter) {
39 SkBitmap store;
40
41 create(&store, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
42 SkDevice device(store);
43 SkDeferredCanvas canvas(&device);
44
45 canvas.clear(0x00000000);
46
47 SkAutoLockPixels alp(store);
48 REPORTER_ASSERT(reporter, store.getColor(0,0) == 0xFFFFFFFF); //verify that clear was deferred
49 canvas.flush();
50 REPORTER_ASSERT(reporter, store.getColor(0,0) == 0x00000000); //verify that clear was executed
51}
52
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +000053static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) {
54 SkBitmap store;
55 SkRect fullRect;
56 fullRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(gWidth),
57 SkIntToScalar(gHeight));
58 SkRect partialRect;
junov@chromium.orgb1e218e2012-02-13 22:27:58 +000059 partialRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0),
60 SkIntToScalar(1), SkIntToScalar(1));
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +000061 create(&store, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
62 SkDevice device(store);
63 SkDeferredCanvas canvas(&device);
64
65 // verify that frame is intially fresh
66 REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());
67 // no clearing op since last call to isFreshFrame -> not fresh
68 REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
69
70 // Verify that clear triggers a fresh frame
71 canvas.clear(0x00000000);
72 REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());
73
74 // Verify that clear with saved state triggers a fresh frame
75 canvas.save(SkCanvas::kMatrixClip_SaveFlag);
76 canvas.clear(0x00000000);
77 canvas.restore();
78 REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());
79
80 // Verify that clear within a layer does NOT trigger a fresh frame
81 canvas.saveLayer(NULL, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag);
82 canvas.clear(0x00000000);
83 canvas.restore();
84 REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
85
86 // Verify that a clear with clipping triggers a fresh frame
87 // (clear is not affected by clipping)
88 canvas.save(SkCanvas::kMatrixClip_SaveFlag);
89 canvas.clipRect(partialRect, SkRegion::kIntersect_Op, false);
90 canvas.clear(0x00000000);
91 canvas.restore();
92 REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());
93
94 // Verify that full frame rects with different forms of opaque paint
95 // trigger frames to be marked as fresh
96 {
97 SkPaint paint;
98 paint.setStyle( SkPaint::kFill_Style );
99 paint.setAlpha( 255 );
100 canvas.drawRect(fullRect, paint);
101 REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());
102 }
103 {
104 SkPaint paint;
105 paint.setStyle( SkPaint::kFill_Style );
106 SkBitmap bmp;
107 create(&bmp, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
108 bmp.setIsOpaque(true);
109 SkShader* shader = SkShader::CreateBitmapShader(bmp,
110 SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
111 paint.setShader(shader)->unref();
112 canvas.drawRect(fullRect, paint);
113 REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());
114 }
115
116 // Verify that full frame rects with different forms of non-opaque paint
117 // do not trigger frames to be marked as fresh
118 {
119 SkPaint paint;
120 paint.setStyle( SkPaint::kFill_Style );
121 paint.setAlpha( 254 );
122 canvas.drawRect(fullRect, paint);
123 REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
124 }
125 {
126 SkPaint paint;
127 paint.setStyle( SkPaint::kFill_Style );
128 SkBitmap bmp;
129 create(&bmp, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
130 bmp.setIsOpaque(false);
131 SkShader* shader = SkShader::CreateBitmapShader(bmp,
132 SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
133 paint.setShader(shader)->unref();
134 canvas.drawRect(fullRect, paint);
135 REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
136 }
137
138 // Verify that incomplete coverage does not trigger a fresh frame
139 {
140 SkPaint paint;
141 paint.setStyle(SkPaint::kFill_Style);
142 paint.setAlpha(255);
143 canvas.drawRect(partialRect, paint);
144 REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
145 }
146
147 // Verify that incomplete coverage due to clipping does not trigger a fresh
148 // frame
149 {
150 canvas.save(SkCanvas::kMatrixClip_SaveFlag);
151 canvas.clipRect(partialRect, SkRegion::kIntersect_Op, false);
152 SkPaint paint;
153 paint.setStyle(SkPaint::kFill_Style);
154 paint.setAlpha(255);
155 canvas.drawRect(fullRect, paint);
156 REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
157 }
158
159 // Verify that stroked rect does not trigger a fresh frame
160 {
161 SkPaint paint;
162 paint.setStyle( SkPaint::kStroke_Style );
163 paint.setAlpha( 255 );
164 canvas.drawRect(fullRect, paint);
165 REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
166 }
167
168 // Verify kSrcMode triggers a fresh frame even with transparent color
169 {
170 SkPaint paint;
171 paint.setStyle( SkPaint::kFill_Style );
172 paint.setAlpha( 100 );
173 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
174 canvas.drawRect(fullRect, paint);
175 REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
176 }
177}
178
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000179class MockDevice : public SkDevice {
180public:
181 MockDevice(const SkBitmap& bm) : SkDevice(bm) {
182 fDrawBitmapCallCount = 0;
183 }
184 virtual void drawBitmap(const SkDraw&, const SkBitmap&,
185 const SkIRect*,
186 const SkMatrix&, const SkPaint&) {
187 fDrawBitmapCallCount++;
188 }
189
190 int fDrawBitmapCallCount;
191};
192
193// Verifies that the deferred canvas triggers a flush when its memory
194// limit is exceeded
195static void TestDeferredCanvasMemoryLimit(skiatest::Reporter* reporter) {
196 SkBitmap store;
197 store.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
198 store.allocPixels();
199 MockDevice mockDevice(store);
200 SkDeferredCanvas canvas(&mockDevice);
201 canvas.setMaxRecordingStorage(160000);
202
203 SkBitmap sourceImage;
204 // 100 by 100 image, takes 40,000 bytes in memory
205 sourceImage.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
206 sourceImage.allocPixels();
207
junov@chromium.orgb10a6bd2012-07-25 17:27:13 +0000208 for (int i = 0; i < 5; i++) {
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000209 sourceImage.notifyPixelsChanged(); // to force re-serialization
210 canvas.drawBitmap(sourceImage, 0, 0, NULL);
211 }
212
junov@chromium.orgb10a6bd2012-07-25 17:27:13 +0000213 // SkPicture path is not fixed
214#if SK_DEFERRED_CANVAS_USES_GPIPE
scroggo@google.com15011ee2012-07-26 20:03:32 +0000215 REPORTER_ASSERT(reporter, mockDevice.fDrawBitmapCallCount == 4);
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000216#endif
217}
218
junov@chromium.org2e14ba82012-08-07 14:26:57 +0000219#if SK_DEFERRED_CANVAS_USES_GPIPE
220static void TestDeferredCanvasBitmapCaching(skiatest::Reporter* reporter) {
221 SkBitmap store;
222 store.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
223 store.allocPixels();
224 SkDevice device(store);
225 SkDeferredCanvas canvas(&device);
226
227 const int imageCount = 2;
228 SkBitmap sourceImages[imageCount];
229 for (int i = 0; i < imageCount; i++)
230 {
231 sourceImages[i].setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
232 sourceImages[i].allocPixels();
233 }
234
235 size_t bitmapSize = sourceImages[0].getSize();
236
237 canvas.drawBitmap(sourceImages[0], 0, 0, NULL);
238 // stored bitmap + drawBitmap command
239 REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() > bitmapSize);
240
241 // verify that nothing can be freed at this point
242 REPORTER_ASSERT(reporter, 0 == canvas.freeMemoryIfPossible(~0));
243
244 // verify that flush leaves image in cache
245 canvas.flush();
246 REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() >= bitmapSize);
247
248 // verify that after a flush, cached image can be freed
249 REPORTER_ASSERT(reporter, canvas.freeMemoryIfPossible(~0) >= bitmapSize);
250
251 // Verify that caching works for avoiding multiple copies of the same bitmap
252 canvas.drawBitmap(sourceImages[0], 0, 0, NULL);
253 canvas.drawBitmap(sourceImages[0], 0, 0, NULL);
254 REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() < 2 * bitmapSize);
255
256 // Verify partial eviction based on bytesToFree
257 canvas.drawBitmap(sourceImages[1], 0, 0, NULL);
258 canvas.flush();
259 REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() > 2 * bitmapSize);
260 size_t bytesFreed = canvas.freeMemoryIfPossible(1);
261 REPORTER_ASSERT(reporter, bytesFreed >= bitmapSize);
262 REPORTER_ASSERT(reporter, bytesFreed < 2*bitmapSize);
263
264 // Verifiy that partial purge works, image zero is in cache but not reffed by
265 // a pending draw, while image 1 is locked-in.
266 canvas.freeMemoryIfPossible(~0);
267 canvas.drawBitmap(sourceImages[0], 0, 0, NULL);
268 canvas.flush();
269 canvas.drawBitmap(sourceImages[1], 0, 0, NULL);
270 bytesFreed = canvas.freeMemoryIfPossible(~0);
271 // only one bitmap should have been freed.
272 REPORTER_ASSERT(reporter, bytesFreed >= bitmapSize);
273 REPORTER_ASSERT(reporter, bytesFreed < 2*bitmapSize);
274 // Clear for next test
275 canvas.flush();
276 canvas.freeMemoryIfPossible(~0);
277 REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() < bitmapSize);
278
279 // Verify the image cache is sensitive to genID bumps
280 canvas.drawBitmap(sourceImages[1], 0, 0, NULL);
281 sourceImages[1].notifyPixelsChanged();
282 canvas.drawBitmap(sourceImages[1], 0, 0, NULL);
283 REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() > 2*bitmapSize);
284}
285#endif
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000286
junov@chromium.org1f9767c2012-02-07 16:27:57 +0000287static void TestDeferredCanvas(skiatest::Reporter* reporter) {
288 TestDeferredCanvasBitmapAccess(reporter);
289 TestDeferredCanvasFlush(reporter);
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000290 TestDeferredCanvasFreshFrame(reporter);
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000291 TestDeferredCanvasMemoryLimit(reporter);
junov@chromium.org2e14ba82012-08-07 14:26:57 +0000292#if SK_DEFERRED_CANVAS_USES_GPIPE
293 TestDeferredCanvasBitmapCaching(reporter);
294#endif
junov@chromium.org1f9767c2012-02-07 16:27:57 +0000295}
296
297#include "TestClassDef.h"
298DEFINE_TESTCLASS("DeferredCanvas", TestDeferredCanvasClass, TestDeferredCanvas)