PictureTest.cpp revision c3d7d90973528527131c72549b10c2a21300e0ac
1/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7#include "Test.h"
8#include "SkCanvas.h"
9#include "SkColorPriv.h"
10#include "SkData.h"
11#include "SkPaint.h"
12#include "SkPicture.h"
13#include "SkRandom.h"
14#include "SkShader.h"
15#include "SkStream.h"
16
17#include "SkPictureUtils.h"
18
19static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
20    bm->setConfig(SkBitmap::kARGB_8888_Config, w, h);
21    bm->allocPixels();
22    bm->eraseColor(color);
23    if (immutable) {
24        bm->setImmutable();
25    }
26}
27
28typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&, const SkPoint&);
29
30static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm,
31                            const SkPoint& pos) {
32    canvas->drawBitmap(bm, pos.fX, pos.fY, NULL);
33}
34
35static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm,
36                                const SkPoint& pos) {
37    SkRect r = {
38        0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())
39    };
40    r.offset(pos.fX, pos.fY);
41    canvas->drawBitmapRectToRect(bm, NULL, r, NULL);
42}
43
44static void drawshader_proc(SkCanvas* canvas, const SkBitmap& bm,
45                            const SkPoint& pos) {
46    SkRect r = {
47        0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())
48    };
49    r.offset(pos.fX, pos.fY);
50
51    SkShader* s = SkShader::CreateBitmapShader(bm,
52                                               SkShader::kClamp_TileMode,
53                                               SkShader::kClamp_TileMode);
54    SkPaint paint;
55    paint.setShader(s)->unref();
56    canvas->drawRect(r, paint);
57}
58
59// Return a picture with the bitmaps drawn at the specified positions.
60static SkPicture* record_bitmaps(const SkBitmap bm[], const SkPoint pos[],
61                                 int count, DrawBitmapProc proc) {
62    SkPicture* pic = new SkPicture;
63    SkCanvas* canvas = pic->beginRecording(1000, 1000);
64    for (int i = 0; i < count; ++i) {
65        proc(canvas, bm[i], pos[i]);
66    }
67    pic->endRecording();
68    return pic;
69}
70
71static void rand_rect(SkRect* rect, SkRandom& rand, SkScalar W, SkScalar H) {
72    rect->fLeft   = rand.nextRangeScalar(-W, 2*W);
73    rect->fTop    = rand.nextRangeScalar(-H, 2*H);
74    rect->fRight  = rect->fLeft + rand.nextRangeScalar(0, W);
75    rect->fBottom = rect->fTop + rand.nextRangeScalar(0, H);
76
77    // we integralize rect to make our tests more predictable, since Gather is
78    // a little sloppy.
79    SkIRect ir;
80    rect->round(&ir);
81    rect->set(ir);
82}
83
84// Allocate result to be large enough to hold subset, and then draw the picture
85// into it, offsetting by subset's top/left corner.
86static void draw(SkPicture* pic, const SkRect& subset, SkBitmap* result) {
87    SkIRect ir;
88    subset.roundOut(&ir);
89    int w = ir.width();
90    int h = ir.height();
91    make_bm(result, w, h, 0, false);
92
93    SkCanvas canvas(*result);
94    canvas.translate(-SkIntToScalar(ir.left()), -SkIntToScalar(ir.top()));
95    canvas.drawPicture(*pic);
96}
97
98template <typename T> int find_index(const T* array, T elem, int count) {
99    for (int i = 0; i < count; ++i) {
100        if (array[i] == elem) {
101            return i;
102        }
103    }
104    return -1;
105}
106
107// Return true if 'ref' is found in array[]
108static bool find(SkPixelRef const * const * array, SkPixelRef const * ref, int count) {
109    return find_index<const SkPixelRef*>(array, ref, count) >= 0;
110}
111
112// Look at each pixel in bm, and if its color appears in colors[], find the
113// corresponding value in refs[] and append that ref into array, skipping
114// duplicates of the same value.
115static void gather_from_colors(const SkBitmap& bm, SkPixelRef* const refs[],
116                               int count, SkTDArray<SkPixelRef*>* array) {
117    // Since we only want to return unique values in array, when we scan we just
118    // set a bit for each index'd color found. In practice we only have a few
119    // distinct colors, so we just use an int's bits as our array. Hence the
120    // assert that count <= number-of-bits-in-our-int.
121    SkASSERT((unsigned)count <= 32);
122    uint32_t bitarray = 0;
123
124    SkAutoLockPixels alp(bm);
125
126    for (int y = 0; y < bm.height(); ++y) {
127        for (int x = 0; x < bm.width(); ++x) {
128            SkPMColor pmc = *bm.getAddr32(x, y);
129            // the only good case where the color is not found would be if
130            // the color is transparent, meaning no bitmap was drawn in that
131            // pixel.
132            if (pmc) {
133                int index = SkGetPackedR32(pmc);
134                SkASSERT(SkGetPackedG32(pmc) == index);
135                SkASSERT(SkGetPackedB32(pmc) == index);
136                SkASSERT(index < count);
137                bitarray |= 1 << index;
138            }
139        }
140    }
141
142    for (int i = 0; i < count; ++i) {
143        if (bitarray & (1 << i)) {
144            *array->append() = refs[i];
145        }
146    }
147}
148
149static void test_gatherpixelrefs(skiatest::Reporter* reporter) {
150    const int IW = 8;
151    const int IH = IW;
152    const SkScalar W = SkIntToScalar(IW);
153    const SkScalar H = W;
154
155    static const int N = 4;
156    SkBitmap bm[N];
157    SkPMColor pmcolors[N];
158    SkPixelRef* refs[N];
159
160    const SkPoint pos[] = {
161        { 0, 0 }, { W, 0 }, { 0, H }, { W, H }
162    };
163
164    // Our convention is that the color components contain the index of their
165    // corresponding bitmap/pixelref
166    for (int i = 0; i < N; ++i) {
167        make_bm(&bm[i], IW, IH, SkColorSetARGB(0xFF, i, i, i), true);
168        refs[i] = bm[i].pixelRef();
169    }
170
171    static const DrawBitmapProc procs[] = {
172        drawbitmap_proc, drawbitmaprect_proc, drawshader_proc
173    };
174
175    SkRandom rand;
176    for (size_t k = 0; k < SK_ARRAY_COUNT(procs); ++k) {
177        SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, N, procs[k]));
178
179        // quick check for a small piece of each quadrant, which should just
180        // contain 1 bitmap.
181        for (size_t  i = 0; i < SK_ARRAY_COUNT(pos); ++i) {
182            SkRect r;
183            r.set(2, 2, W - 2, H - 2);
184            r.offset(pos[i].fX, pos[i].fY);
185            SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r));
186            REPORTER_ASSERT(reporter, data);
187            int count = data->size() / sizeof(SkPixelRef*);
188            REPORTER_ASSERT(reporter, 1 == count);
189            REPORTER_ASSERT(reporter, *(SkPixelRef**)data->data() == refs[i]);
190        }
191
192        // Test a bunch of random (mostly) rects, and compare the gather results
193        // with a deduced list of refs by looking at the colors drawn.
194        for (int j = 0; j < 100; ++j) {
195            SkRect r;
196            rand_rect(&r, rand, 2*W, 2*H);
197
198            SkBitmap result;
199            draw(pic, r, &result);
200            SkTDArray<SkPixelRef*> array;
201
202            SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
203            size_t dataSize = data ? data->size() : 0;
204            int gatherCount = dataSize / sizeof(SkPixelRef*);
205            SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize);
206            SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL;
207            SkAutoDataUnref adu(data);
208
209            gather_from_colors(result, refs, N, &array);
210
211            /*
212             *  GatherPixelRefs is conservative, so it can return more bitmaps
213             *  that we actually can see (usually because of conservative bounds
214             *  inflation for antialiasing). Thus our check here is only that
215             *  Gather didn't miss any that we actually saw. Even that isn't
216             *  a strict requirement on Gather, which is meant to be quick and
217             *  only mostly-correct, but at the moment this test should work.
218             */
219            for (int i = 0; i < array.count(); ++i) {
220                bool found = find(gatherRefs, array[i], gatherCount);
221                REPORTER_ASSERT(reporter, found);
222#if 0
223                // enable this block of code to debug failures, as it will rerun
224                // the case that failed.
225                if (!found) {
226                    SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
227                    size_t dataSize = data ? data->size() : 0;
228                }
229#endif
230            }
231        }
232    }
233}
234
235#ifdef SK_DEBUG
236// Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only
237// run in debug mode.
238static void test_deleting_empty_playback() {
239    SkPicture picture;
240    // Creates an SkPictureRecord
241    picture.beginRecording(0, 0);
242    // Turns that into an SkPicturePlayback
243    picture.endRecording();
244    // Deletes the old SkPicturePlayback, and creates a new SkPictureRecord
245    picture.beginRecording(0, 0);
246}
247
248// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
249static void test_serializing_empty_picture() {
250    SkPicture picture;
251    picture.beginRecording(0, 0);
252    picture.endRecording();
253    SkDynamicMemoryWStream stream;
254    picture.serialize(&stream);
255}
256#endif
257
258static void rand_op(SkCanvas* canvas, SkRandom& rand) {
259    SkPaint paint;
260    SkRect rect = SkRect::MakeWH(50, 50);
261
262    SkScalar unit = rand.nextUScalar1();
263    if (unit <= 0.3) {
264//        SkDebugf("save\n");
265        canvas->save();
266    } else if (unit <= 0.6) {
267//        SkDebugf("restore\n");
268        canvas->restore();
269    } else if (unit <= 0.9) {
270//        SkDebugf("clip\n");
271        canvas->clipRect(rect);
272    } else {
273//        SkDebugf("draw\n");
274        canvas->drawPaint(paint);
275    }
276}
277
278static void test_peephole(skiatest::Reporter* reporter) {
279    SkRandom rand;
280
281    for (int j = 0; j < 100; j++) {
282        SkRandom rand2(rand.getSeed()); // remember the seed
283
284        SkPicture picture;
285        SkCanvas* canvas = picture.beginRecording(100, 100);
286
287        for (int i = 0; i < 1000; ++i) {
288            rand_op(canvas, rand);
289        }
290        picture.endRecording();
291    }
292
293    {
294        SkPicture picture;
295        SkCanvas* canvas = picture.beginRecording(100, 100);
296        SkRect rect = SkRect::MakeWH(50, 50);
297
298        for (int i = 0; i < 100; ++i) {
299            canvas->save();
300        }
301        while (canvas->getSaveCount() > 1) {
302            canvas->clipRect(rect);
303            canvas->restore();
304        }
305        picture.endRecording();
306    }
307}
308
309static void TestPicture(skiatest::Reporter* reporter) {
310#ifdef SK_DEBUG
311    test_deleting_empty_playback();
312    test_serializing_empty_picture();
313#endif
314    test_peephole(reporter);
315    test_gatherpixelrefs(reporter);
316}
317
318#include "TestClassDef.h"
319DEFINE_TESTCLASS("Pictures", PictureTestClass, TestPicture)
320