• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
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 "SkBitmapProcShader.h"
11 #include "SkDeferredCanvas.h"
12 #include "SkDevice.h"
13 #include "SkShader.h"
14 
15 static const int gWidth = 2;
16 static const int gHeight = 2;
17 
create(SkBitmap * bm,SkBitmap::Config config,SkColor color)18 static void create(SkBitmap* bm, SkBitmap::Config config, SkColor color) {
19     bm->setConfig(config, gWidth, gHeight);
20     bm->allocPixels();
21     bm->eraseColor(color);
22 }
23 
TestDeferredCanvasBitmapAccess(skiatest::Reporter * reporter)24 static void TestDeferredCanvasBitmapAccess(skiatest::Reporter* reporter) {
25     SkBitmap store;
26 
27     create(&store, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
28     SkDevice device(store);
29     SkDeferredCanvas canvas(&device);
30 
31     canvas.clear(0x00000000);
32 
33     SkAutoLockPixels alp(store);
34     REPORTER_ASSERT(reporter, store.getColor(0,0) == 0xFFFFFFFF); //verify that clear was deferred
35     SkBitmap accessed = canvas.getDevice()->accessBitmap(false);
36     REPORTER_ASSERT(reporter, store.getColor(0,0) == 0x00000000); //verify that clear was executed
37     REPORTER_ASSERT(reporter, accessed.pixelRef() == store.pixelRef());
38 }
39 
TestDeferredCanvasFlush(skiatest::Reporter * reporter)40 static void TestDeferredCanvasFlush(skiatest::Reporter* reporter) {
41     SkBitmap store;
42 
43     create(&store, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
44     SkDevice device(store);
45     SkDeferredCanvas canvas(&device);
46 
47     canvas.clear(0x00000000);
48 
49     SkAutoLockPixels alp(store);
50     REPORTER_ASSERT(reporter, store.getColor(0,0) == 0xFFFFFFFF); //verify that clear was deferred
51     canvas.flush();
52     REPORTER_ASSERT(reporter, store.getColor(0,0) == 0x00000000); //verify that clear was executed
53 }
54 
TestDeferredCanvasFreshFrame(skiatest::Reporter * reporter)55 static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) {
56     SkBitmap store;
57     SkRect fullRect;
58     fullRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(gWidth),
59         SkIntToScalar(gHeight));
60     SkRect partialRect;
61     partialRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0),
62         SkIntToScalar(1), SkIntToScalar(1));
63     create(&store, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
64     SkDevice device(store);
65     SkDeferredCanvas canvas(&device);
66 
67     // verify that frame is intially fresh
68     REPORTER_ASSERT(reporter, canvas.isFreshFrame());
69     // no clearing op since last call to isFreshFrame -> not fresh
70     REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
71 
72     // Verify that clear triggers a fresh frame
73     canvas.clear(0x00000000);
74     REPORTER_ASSERT(reporter, canvas.isFreshFrame());
75 
76     // Verify that clear with saved state triggers a fresh frame
77     canvas.save(SkCanvas::kMatrixClip_SaveFlag);
78     canvas.clear(0x00000000);
79     canvas.restore();
80     REPORTER_ASSERT(reporter, canvas.isFreshFrame());
81 
82     // Verify that clear within a layer does NOT trigger a fresh frame
83     canvas.saveLayer(NULL, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag);
84     canvas.clear(0x00000000);
85     canvas.restore();
86     REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
87 
88     // Verify that a clear with clipping triggers a fresh frame
89     // (clear is not affected by clipping)
90     canvas.save(SkCanvas::kMatrixClip_SaveFlag);
91     canvas.clipRect(partialRect, SkRegion::kIntersect_Op, false);
92     canvas.clear(0x00000000);
93     canvas.restore();
94     REPORTER_ASSERT(reporter, canvas.isFreshFrame());
95 
96     // Verify that full frame rects with different forms of opaque paint
97     // trigger frames to be marked as fresh
98     {
99         SkPaint paint;
100         paint.setStyle( SkPaint::kFill_Style );
101         paint.setAlpha( 255 );
102         canvas.drawRect(fullRect, paint);
103         REPORTER_ASSERT(reporter, canvas.isFreshFrame());
104     }
105     {
106         SkPaint paint;
107         paint.setStyle( SkPaint::kFill_Style );
108         paint.setAlpha( 255 );
109         paint.setXfermodeMode(SkXfermode::kSrcIn_Mode);
110         canvas.drawRect(fullRect, paint);
111         REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
112     }
113     {
114         SkPaint paint;
115         paint.setStyle( SkPaint::kFill_Style );
116         SkBitmap bmp;
117         create(&bmp, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
118         bmp.setIsOpaque(true);
119         SkShader* shader = SkShader::CreateBitmapShader(bmp,
120             SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
121         paint.setShader(shader)->unref();
122         canvas.drawRect(fullRect, paint);
123         REPORTER_ASSERT(reporter, canvas.isFreshFrame());
124     }
125 
126     // Verify that full frame rects with different forms of non-opaque paint
127     // do not trigger frames to be marked as fresh
128     {
129         SkPaint paint;
130         paint.setStyle( SkPaint::kFill_Style );
131         paint.setAlpha( 254 );
132         canvas.drawRect(fullRect, paint);
133         REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
134     }
135     {
136         SkPaint paint;
137         paint.setStyle( SkPaint::kFill_Style );
138         SkBitmap bmp;
139         create(&bmp, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
140         bmp.setIsOpaque(false);
141         SkShader* shader = SkShader::CreateBitmapShader(bmp,
142             SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
143         paint.setShader(shader)->unref();
144         canvas.drawRect(fullRect, paint);
145         REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
146     }
147 
148     // Verify that incomplete coverage does not trigger a fresh frame
149     {
150         SkPaint paint;
151         paint.setStyle(SkPaint::kFill_Style);
152         paint.setAlpha(255);
153         canvas.drawRect(partialRect, paint);
154         REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
155     }
156 
157     // Verify that incomplete coverage due to clipping does not trigger a fresh
158     // frame
159     {
160         canvas.save(SkCanvas::kMatrixClip_SaveFlag);
161         canvas.clipRect(partialRect, SkRegion::kIntersect_Op, false);
162         SkPaint paint;
163         paint.setStyle(SkPaint::kFill_Style);
164         paint.setAlpha(255);
165         canvas.drawRect(fullRect, paint);
166         canvas.restore();
167         REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
168     }
169     {
170         canvas.save(SkCanvas::kMatrixClip_SaveFlag);
171         SkPaint paint;
172         paint.setStyle( SkPaint::kFill_Style );
173         paint.setAlpha( 255 );
174         SkPath path;
175         path.addCircle(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(2));
176         canvas.clipPath(path, SkRegion::kIntersect_Op, false);
177         canvas.drawRect(fullRect, paint);
178         canvas.restore();
179         REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
180     }
181 
182     // Verify that stroked rect does not trigger a fresh frame
183     {
184         SkPaint paint;
185         paint.setStyle( SkPaint::kStroke_Style );
186         paint.setAlpha( 255 );
187         canvas.drawRect(fullRect, paint);
188         REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
189     }
190 
191     // Verify kSrcMode triggers a fresh frame even with transparent color
192     {
193         SkPaint paint;
194         paint.setStyle( SkPaint::kFill_Style );
195         paint.setAlpha( 100 );
196         paint.setXfermodeMode(SkXfermode::kSrc_Mode);
197         canvas.drawRect(fullRect, paint);
198         REPORTER_ASSERT(reporter, canvas.isFreshFrame());
199     }
200 }
201 
202 class MockDevice : public SkDevice {
203 public:
MockDevice(const SkBitmap & bm)204     MockDevice(const SkBitmap& bm) : SkDevice(bm) {
205         fDrawBitmapCallCount = 0;
206     }
drawBitmap(const SkDraw &,const SkBitmap &,const SkIRect *,const SkMatrix &,const SkPaint &)207     virtual void drawBitmap(const SkDraw&, const SkBitmap&,
208                             const SkIRect*,
209                             const SkMatrix&, const SkPaint&) {
210         fDrawBitmapCallCount++;
211     }
212 
213     int fDrawBitmapCallCount;
214 };
215 
216 // Verifies that the deferred canvas triggers a flush when its memory
217 // limit is exceeded
TestDeferredCanvasMemoryLimit(skiatest::Reporter * reporter)218 static void TestDeferredCanvasMemoryLimit(skiatest::Reporter* reporter) {
219     SkBitmap store;
220     store.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
221     store.allocPixels();
222     MockDevice mockDevice(store);
223     SkDeferredCanvas canvas(&mockDevice);
224     canvas.setMaxRecordingStorage(160000);
225 
226     SkBitmap sourceImage;
227     // 100 by 100 image, takes 40,000 bytes in memory
228     sourceImage.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
229     sourceImage.allocPixels();
230 
231     for (int i = 0; i < 5; i++) {
232         sourceImage.notifyPixelsChanged(); // to force re-serialization
233         canvas.drawBitmap(sourceImage, 0, 0, NULL);
234     }
235 
236     REPORTER_ASSERT(reporter, mockDevice.fDrawBitmapCallCount == 4);
237 }
238 
239 class NotificationCounter : public SkDeferredCanvas::NotificationClient {
240 public:
NotificationCounter()241     NotificationCounter() {
242         fPrepareForDrawCount = fStorageAllocatedChangedCount =
243             fFlushedDrawCommandsCount = fSkippedPendingDrawCommandsCount = 0;
244     }
245 
prepareForDraw()246     virtual void prepareForDraw() SK_OVERRIDE {
247         fPrepareForDrawCount++;
248     }
storageAllocatedForRecordingChanged(size_t size)249     virtual void storageAllocatedForRecordingChanged(size_t size) SK_OVERRIDE {
250         fStorageAllocatedChangedCount++;
251     }
flushedDrawCommands()252     virtual void flushedDrawCommands() SK_OVERRIDE {
253         fFlushedDrawCommandsCount++;
254     }
skippedPendingDrawCommands()255     virtual void skippedPendingDrawCommands() SK_OVERRIDE {
256         fSkippedPendingDrawCommandsCount++;
257     }
258 
259     int fPrepareForDrawCount;
260     int fStorageAllocatedChangedCount;
261     int fFlushedDrawCommandsCount;
262     int fSkippedPendingDrawCommandsCount;
263 };
264 
TestDeferredCanvasBitmapCaching(skiatest::Reporter * reporter)265 static void TestDeferredCanvasBitmapCaching(skiatest::Reporter* reporter) {
266     SkBitmap store;
267     store.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
268     store.allocPixels();
269     SkDevice device(store);
270     NotificationCounter notificationCounter;
271     SkDeferredCanvas canvas(&device);
272     canvas.setNotificationClient(&notificationCounter);
273 
274     const int imageCount = 2;
275     SkBitmap sourceImages[imageCount];
276     for (int i = 0; i < imageCount; i++)
277     {
278         sourceImages[i].setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
279         sourceImages[i].allocPixels();
280     }
281 
282     size_t bitmapSize = sourceImages[0].getSize();
283 
284     canvas.drawBitmap(sourceImages[0], 0, 0, NULL);
285     REPORTER_ASSERT(reporter, 1 == notificationCounter.fStorageAllocatedChangedCount);
286     // stored bitmap + drawBitmap command
287     REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() > bitmapSize);
288 
289     // verify that nothing can be freed at this point
290     REPORTER_ASSERT(reporter, 0 == canvas.freeMemoryIfPossible(~0U));
291 
292     // verify that flush leaves image in cache
293     REPORTER_ASSERT(reporter, 0 == notificationCounter.fFlushedDrawCommandsCount);
294     REPORTER_ASSERT(reporter, 0 == notificationCounter.fPrepareForDrawCount);
295     canvas.flush();
296     REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
297     REPORTER_ASSERT(reporter, 1 == notificationCounter.fPrepareForDrawCount);
298     REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() >= bitmapSize);
299 
300     // verify that after a flush, cached image can be freed
301     REPORTER_ASSERT(reporter, canvas.freeMemoryIfPossible(~0U) >= bitmapSize);
302 
303     // Verify that caching works for avoiding multiple copies of the same bitmap
304     canvas.drawBitmap(sourceImages[0], 0, 0, NULL);
305     REPORTER_ASSERT(reporter, 2 == notificationCounter.fStorageAllocatedChangedCount);
306     canvas.drawBitmap(sourceImages[0], 0, 0, NULL);
307     REPORTER_ASSERT(reporter, 2 == notificationCounter.fStorageAllocatedChangedCount);
308     REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
309     REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() < 2 * bitmapSize);
310 
311     // Verify partial eviction based on bytesToFree
312     canvas.drawBitmap(sourceImages[1], 0, 0, NULL);
313     REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
314     canvas.flush();
315     REPORTER_ASSERT(reporter, 2 == notificationCounter.fFlushedDrawCommandsCount);
316     REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() > 2 * bitmapSize);
317     size_t bytesFreed = canvas.freeMemoryIfPossible(1);
318     REPORTER_ASSERT(reporter, 2 == notificationCounter.fFlushedDrawCommandsCount);
319     REPORTER_ASSERT(reporter,  bytesFreed >= bitmapSize);
320     REPORTER_ASSERT(reporter,  bytesFreed < 2*bitmapSize);
321 
322     // Verifiy that partial purge works, image zero is in cache but not reffed by
323     // a pending draw, while image 1 is locked-in.
324     canvas.freeMemoryIfPossible(~0U);
325     REPORTER_ASSERT(reporter, 2 == notificationCounter.fFlushedDrawCommandsCount);
326     canvas.drawBitmap(sourceImages[0], 0, 0, NULL);
327     canvas.flush();
328     canvas.drawBitmap(sourceImages[1], 0, 0, NULL);
329     bytesFreed = canvas.freeMemoryIfPossible(~0U);
330     // only one bitmap should have been freed.
331     REPORTER_ASSERT(reporter,  bytesFreed >= bitmapSize);
332     REPORTER_ASSERT(reporter,  bytesFreed < 2*bitmapSize);
333     // Clear for next test
334     canvas.flush();
335     canvas.freeMemoryIfPossible(~0U);
336     REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() < bitmapSize);
337 
338     // Verify the image cache is sensitive to genID bumps
339     canvas.drawBitmap(sourceImages[1], 0, 0, NULL);
340     sourceImages[1].notifyPixelsChanged();
341     canvas.drawBitmap(sourceImages[1], 0, 0, NULL);
342     REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() > 2*bitmapSize);
343 
344     // Verify that nothing in this test caused commands to be skipped
345     REPORTER_ASSERT(reporter, 0 == notificationCounter.fSkippedPendingDrawCommandsCount);
346 }
347 
TestDeferredCanvasSkip(skiatest::Reporter * reporter)348 static void TestDeferredCanvasSkip(skiatest::Reporter* reporter) {
349     SkBitmap store;
350     store.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
351     store.allocPixels();
352     SkDevice device(store);
353     NotificationCounter notificationCounter;
354     SkDeferredCanvas canvas(&device);
355     canvas.setNotificationClient(&notificationCounter);
356     canvas.clear(0x0);
357     REPORTER_ASSERT(reporter, 1 == notificationCounter.fSkippedPendingDrawCommandsCount);
358     REPORTER_ASSERT(reporter, 0 == notificationCounter.fFlushedDrawCommandsCount);
359     canvas.flush();
360     REPORTER_ASSERT(reporter, 1 == notificationCounter.fSkippedPendingDrawCommandsCount);
361     REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
362 
363 }
364 
TestDeferredCanvasBitmapShaderNoLeak(skiatest::Reporter * reporter)365 static void TestDeferredCanvasBitmapShaderNoLeak(skiatest::Reporter* reporter) {
366     // This is a regression test for crbug.com/155875
367     // This test covers a code path that inserts bitmaps into the bitmap heap through the
368     // flattening of SkBitmapProcShaders. The refcount in the bitmap heap is maintained through
369     // the flattening and unflattening of the shader.
370     SkBitmap store;
371     store.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
372     store.allocPixels();
373     SkDevice device(store);
374     SkDeferredCanvas canvas(&device);
375     // test will fail if nbIterations is not in sync with
376     // BITMAPS_TO_KEEP in SkGPipeWrite.cpp
377     const int nbIterations = 5;
378     size_t bytesAllocated = 0;
379     for(int pass = 0; pass < 2; ++pass) {
380         for(int i = 0; i < nbIterations; ++i) {
381             SkPaint paint;
382             SkBitmap paintPattern;
383             paintPattern.setConfig(SkBitmap::kARGB_8888_Config, 10, 10);
384             paintPattern.allocPixels();
385             paint.setShader(SkNEW_ARGS(SkBitmapProcShader,
386                 (paintPattern, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode)))->unref();
387             canvas.drawPaint(paint);
388             canvas.flush();
389 
390             // In the first pass, memory allocation should be monotonically increasing as
391             // the bitmap heap slots fill up.  In the second pass memory allocation should be
392             // stable as bitmap heap slots get recycled.
393             size_t newBytesAllocated = canvas.storageAllocatedForRecording();
394             if (pass == 0) {
395                 REPORTER_ASSERT(reporter, newBytesAllocated > bytesAllocated);
396                 bytesAllocated = newBytesAllocated;
397             } else {
398                 REPORTER_ASSERT(reporter, newBytesAllocated == bytesAllocated);
399             }
400         }
401     }
402     // All cached resources should be evictable since last canvas call was flush()
403     canvas.freeMemoryIfPossible(~0U);
404     REPORTER_ASSERT(reporter, 0 == canvas.storageAllocatedForRecording());
405 }
406 
TestDeferredCanvasBitmapSizeThreshold(skiatest::Reporter * reporter)407 static void TestDeferredCanvasBitmapSizeThreshold(skiatest::Reporter* reporter) {
408     SkBitmap store;
409     store.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
410     store.allocPixels();
411 
412     SkBitmap sourceImage;
413     // 100 by 100 image, takes 40,000 bytes in memory
414     sourceImage.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
415     sourceImage.allocPixels();
416 
417     // 1 under : should not store the image
418     {
419         SkDevice device(store);
420         SkDeferredCanvas canvas(&device);
421         canvas.setBitmapSizeThreshold(39999);
422         canvas.drawBitmap(sourceImage, 0, 0, NULL);
423         size_t newBytesAllocated = canvas.storageAllocatedForRecording();
424         REPORTER_ASSERT(reporter, newBytesAllocated == 0);
425     }
426 
427     // exact value : should store the image
428     {
429         SkDevice device(store);
430         SkDeferredCanvas canvas(&device);
431         canvas.setBitmapSizeThreshold(40000);
432         canvas.drawBitmap(sourceImage, 0, 0, NULL);
433         size_t newBytesAllocated = canvas.storageAllocatedForRecording();
434         REPORTER_ASSERT(reporter, newBytesAllocated > 0);
435     }
436 
437     // 1 over : should still store the image
438     {
439         SkDevice device(store);
440         SkDeferredCanvas canvas(&device);
441         canvas.setBitmapSizeThreshold(40001);
442         canvas.drawBitmap(sourceImage, 0, 0, NULL);
443         size_t newBytesAllocated = canvas.storageAllocatedForRecording();
444         REPORTER_ASSERT(reporter, newBytesAllocated > 0);
445     }
446 }
447 
TestDeferredCanvas(skiatest::Reporter * reporter)448 static void TestDeferredCanvas(skiatest::Reporter* reporter) {
449     TestDeferredCanvasBitmapAccess(reporter);
450     TestDeferredCanvasFlush(reporter);
451     TestDeferredCanvasFreshFrame(reporter);
452     TestDeferredCanvasMemoryLimit(reporter);
453     TestDeferredCanvasBitmapCaching(reporter);
454     TestDeferredCanvasSkip(reporter);
455     TestDeferredCanvasBitmapShaderNoLeak(reporter);
456     TestDeferredCanvasBitmapSizeThreshold(reporter);
457 }
458 
459 #include "TestClassDef.h"
460 DEFINE_TESTCLASS("DeferredCanvas", TestDeferredCanvasClass, TestDeferredCanvas)
461