PictureTest.cpp revision 94f20dc89a55483ba63db01829332065e6e1b2ac
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 "SkRRect.h"
15#include "SkShader.h"
16#include "SkStream.h"
17
18#include "SkPictureUtils.h"
19
20static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
21    bm->setConfig(SkBitmap::kARGB_8888_Config, w, h);
22    bm->allocPixels();
23    bm->eraseColor(color);
24    if (immutable) {
25        bm->setImmutable();
26    }
27}
28
29typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&, const SkPoint&);
30
31static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm,
32                            const SkPoint& pos) {
33    canvas->drawBitmap(bm, pos.fX, pos.fY, NULL);
34}
35
36static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm,
37                                const SkPoint& pos) {
38    SkRect r = {
39        0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())
40    };
41    r.offset(pos.fX, pos.fY);
42    canvas->drawBitmapRectToRect(bm, NULL, r, NULL);
43}
44
45static void drawshader_proc(SkCanvas* canvas, const SkBitmap& bm,
46                            const SkPoint& pos) {
47    SkRect r = {
48        0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())
49    };
50    r.offset(pos.fX, pos.fY);
51
52    SkShader* s = SkShader::CreateBitmapShader(bm,
53                                               SkShader::kClamp_TileMode,
54                                               SkShader::kClamp_TileMode);
55    SkPaint paint;
56    paint.setShader(s)->unref();
57    canvas->drawRect(r, paint);
58    canvas->drawOval(r, paint);
59    SkRRect rr;
60    rr.setRectXY(r, 10, 10);
61    canvas->drawRRect(rr, paint);
62}
63
64// Return a picture with the bitmaps drawn at the specified positions.
65static SkPicture* record_bitmaps(const SkBitmap bm[], const SkPoint pos[],
66                                 int count, DrawBitmapProc proc) {
67    SkPicture* pic = new SkPicture;
68    SkCanvas* canvas = pic->beginRecording(1000, 1000);
69    for (int i = 0; i < count; ++i) {
70        proc(canvas, bm[i], pos[i]);
71    }
72    pic->endRecording();
73    return pic;
74}
75
76static void rand_rect(SkRect* rect, SkRandom& rand, SkScalar W, SkScalar H) {
77    rect->fLeft   = rand.nextRangeScalar(-W, 2*W);
78    rect->fTop    = rand.nextRangeScalar(-H, 2*H);
79    rect->fRight  = rect->fLeft + rand.nextRangeScalar(0, W);
80    rect->fBottom = rect->fTop + rand.nextRangeScalar(0, H);
81
82    // we integralize rect to make our tests more predictable, since Gather is
83    // a little sloppy.
84    SkIRect ir;
85    rect->round(&ir);
86    rect->set(ir);
87}
88
89// Allocate result to be large enough to hold subset, and then draw the picture
90// into it, offsetting by subset's top/left corner.
91static void draw(SkPicture* pic, const SkRect& subset, SkBitmap* result) {
92    SkIRect ir;
93    subset.roundOut(&ir);
94    int w = ir.width();
95    int h = ir.height();
96    make_bm(result, w, h, 0, false);
97
98    SkCanvas canvas(*result);
99    canvas.translate(-SkIntToScalar(ir.left()), -SkIntToScalar(ir.top()));
100    canvas.drawPicture(*pic);
101}
102
103template <typename T> int find_index(const T* array, T elem, int count) {
104    for (int i = 0; i < count; ++i) {
105        if (array[i] == elem) {
106            return i;
107        }
108    }
109    return -1;
110}
111
112// Return true if 'ref' is found in array[]
113static bool find(SkPixelRef const * const * array, SkPixelRef const * ref, int count) {
114    return find_index<const SkPixelRef*>(array, ref, count) >= 0;
115}
116
117// Look at each pixel in bm, and if its color appears in colors[], find the
118// corresponding value in refs[] and append that ref into array, skipping
119// duplicates of the same value.
120static void gather_from_colors(const SkBitmap& bm, SkPixelRef* const refs[],
121                               int count, SkTDArray<SkPixelRef*>* array) {
122    // Since we only want to return unique values in array, when we scan we just
123    // set a bit for each index'd color found. In practice we only have a few
124    // distinct colors, so we just use an int's bits as our array. Hence the
125    // assert that count <= number-of-bits-in-our-int.
126    SkASSERT((unsigned)count <= 32);
127    uint32_t bitarray = 0;
128
129    SkAutoLockPixels alp(bm);
130
131    for (int y = 0; y < bm.height(); ++y) {
132        for (int x = 0; x < bm.width(); ++x) {
133            SkPMColor pmc = *bm.getAddr32(x, y);
134            // the only good case where the color is not found would be if
135            // the color is transparent, meaning no bitmap was drawn in that
136            // pixel.
137            if (pmc) {
138                uint32_t index = SkGetPackedR32(pmc);
139                SkASSERT(SkGetPackedG32(pmc) == index);
140                SkASSERT(SkGetPackedB32(pmc) == index);
141                SkASSERT(static_cast<int>(index) < count);
142                bitarray |= 1 << index;
143            }
144        }
145    }
146
147    for (int i = 0; i < count; ++i) {
148        if (bitarray & (1 << i)) {
149            *array->append() = refs[i];
150        }
151    }
152}
153
154static void test_gatherpixelrefs(skiatest::Reporter* reporter) {
155    const int IW = 8;
156    const int IH = IW;
157    const SkScalar W = SkIntToScalar(IW);
158    const SkScalar H = W;
159
160    static const int N = 4;
161    SkBitmap bm[N];
162    SkPixelRef* refs[N];
163
164    const SkPoint pos[] = {
165        { 0, 0 }, { W, 0 }, { 0, H }, { W, H }
166    };
167
168    // Our convention is that the color components contain the index of their
169    // corresponding bitmap/pixelref
170    for (int i = 0; i < N; ++i) {
171        make_bm(&bm[i], IW, IH, SkColorSetARGB(0xFF, i, i, i), true);
172        refs[i] = bm[i].pixelRef();
173    }
174
175    static const DrawBitmapProc procs[] = {
176        drawbitmap_proc, drawbitmaprect_proc, drawshader_proc
177    };
178
179    SkRandom rand;
180    for (size_t k = 0; k < SK_ARRAY_COUNT(procs); ++k) {
181        SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, N, procs[k]));
182
183        // quick check for a small piece of each quadrant, which should just
184        // contain 1 bitmap.
185        for (size_t  i = 0; i < SK_ARRAY_COUNT(pos); ++i) {
186            SkRect r;
187            r.set(2, 2, W - 2, H - 2);
188            r.offset(pos[i].fX, pos[i].fY);
189            SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r));
190            REPORTER_ASSERT(reporter, data);
191            int count = data->size() / sizeof(SkPixelRef*);
192            REPORTER_ASSERT(reporter, 1 == count);
193            REPORTER_ASSERT(reporter, *(SkPixelRef**)data->data() == refs[i]);
194        }
195
196        // Test a bunch of random (mostly) rects, and compare the gather results
197        // with a deduced list of refs by looking at the colors drawn.
198        for (int j = 0; j < 100; ++j) {
199            SkRect r;
200            rand_rect(&r, rand, 2*W, 2*H);
201
202            SkBitmap result;
203            draw(pic, r, &result);
204            SkTDArray<SkPixelRef*> array;
205
206            SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
207            size_t dataSize = data ? data->size() : 0;
208            int gatherCount = dataSize / sizeof(SkPixelRef*);
209            SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize);
210            SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL;
211            SkAutoDataUnref adu(data);
212
213            gather_from_colors(result, refs, N, &array);
214
215            /*
216             *  GatherPixelRefs is conservative, so it can return more bitmaps
217             *  that we actually can see (usually because of conservative bounds
218             *  inflation for antialiasing). Thus our check here is only that
219             *  Gather didn't miss any that we actually saw. Even that isn't
220             *  a strict requirement on Gather, which is meant to be quick and
221             *  only mostly-correct, but at the moment this test should work.
222             */
223            for (int i = 0; i < array.count(); ++i) {
224                bool found = find(gatherRefs, array[i], gatherCount);
225                REPORTER_ASSERT(reporter, found);
226#if 0
227                // enable this block of code to debug failures, as it will rerun
228                // the case that failed.
229                if (!found) {
230                    SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
231                    size_t dataSize = data ? data->size() : 0;
232                }
233#endif
234            }
235        }
236    }
237}
238
239#ifdef SK_DEBUG
240// Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only
241// run in debug mode.
242static void test_deleting_empty_playback() {
243    SkPicture picture;
244    // Creates an SkPictureRecord
245    picture.beginRecording(0, 0);
246    // Turns that into an SkPicturePlayback
247    picture.endRecording();
248    // Deletes the old SkPicturePlayback, and creates a new SkPictureRecord
249    picture.beginRecording(0, 0);
250}
251
252// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
253static void test_serializing_empty_picture() {
254    SkPicture picture;
255    picture.beginRecording(0, 0);
256    picture.endRecording();
257    SkDynamicMemoryWStream stream;
258    picture.serialize(&stream);
259}
260#endif
261
262static void rand_op(SkCanvas* canvas, SkRandom& rand) {
263    SkPaint paint;
264    SkRect rect = SkRect::MakeWH(50, 50);
265
266    SkScalar unit = rand.nextUScalar1();
267    if (unit <= 0.3) {
268//        SkDebugf("save\n");
269        canvas->save();
270    } else if (unit <= 0.6) {
271//        SkDebugf("restore\n");
272        canvas->restore();
273    } else if (unit <= 0.9) {
274//        SkDebugf("clip\n");
275        canvas->clipRect(rect);
276    } else {
277//        SkDebugf("draw\n");
278        canvas->drawPaint(paint);
279    }
280}
281
282static void test_peephole(skiatest::Reporter* reporter) {
283    SkRandom rand;
284
285    for (int j = 0; j < 100; j++) {
286        SkRandom rand2(rand.getSeed()); // remember the seed
287
288        SkPicture picture;
289        SkCanvas* canvas = picture.beginRecording(100, 100);
290
291        for (int i = 0; i < 1000; ++i) {
292            rand_op(canvas, rand);
293        }
294        picture.endRecording();
295    }
296
297    {
298        SkPicture picture;
299        SkCanvas* canvas = picture.beginRecording(100, 100);
300        SkRect rect = SkRect::MakeWH(50, 50);
301
302        for (int i = 0; i < 100; ++i) {
303            canvas->save();
304        }
305        while (canvas->getSaveCount() > 1) {
306            canvas->clipRect(rect);
307            canvas->restore();
308        }
309        picture.endRecording();
310    }
311}
312
313#ifndef SK_DEBUG
314// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
315// should never do this.
316static void test_bad_bitmap() {
317    // This bitmap has a width and height but no pixels. As a result, attempting to record it will
318    // fail.
319    SkBitmap bm;
320    bm.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
321    SkPicture picture;
322    SkCanvas* recordingCanvas = picture.beginRecording(100, 100);
323    recordingCanvas->drawBitmap(bm, 0, 0);
324    picture.endRecording();
325
326    SkCanvas canvas;
327    canvas.drawPicture(picture);
328}
329#endif
330
331#include "SkData.h"
332#include "SkImageRef_GlobalPool.h"
333// Class to test SkPixelRef::onRefEncodedData, since there are currently no implementations in skia.
334class SkDataImageRef : public SkImageRef_GlobalPool {
335
336public:
337    SkDataImageRef(SkMemoryStream* stream)
338        : SkImageRef_GlobalPool(stream, SkBitmap::kNo_Config) {
339        SkASSERT(stream != NULL);
340        fData = stream->copyToData();
341        this->setImmutable();
342    }
343
344    ~SkDataImageRef() {
345        fData->unref();
346    }
347
348    virtual SkData* onRefEncodedData() SK_OVERRIDE {
349        fData->ref();
350        return fData;
351    }
352
353private:
354    SkData* fData;
355};
356
357#include "SkImageEncoder.h"
358
359static bool PNGEncodeBitmapToStream(SkWStream* wStream, const SkBitmap& bm) {
360    return SkImageEncoder::EncodeStream(wStream, bm, SkImageEncoder::kPNG_Type, 100);
361}
362
363static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
364    SkPicture picture;
365    SkCanvas* canvas = picture.beginRecording(bitmap.width(), bitmap.height());
366    canvas->drawBitmap(bitmap, 0, 0);
367    SkDynamicMemoryWStream wStream;
368    picture.serialize(&wStream, &PNGEncodeBitmapToStream);
369    return wStream.copyToData();
370}
371
372static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
373    // Create a bitmap that will be encoded.
374    SkBitmap original;
375    make_bm(&original, 100, 100, SK_ColorBLUE, true);
376    SkDynamicMemoryWStream wStream;
377    if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
378        return;
379    }
380    SkAutoDataUnref data(wStream.copyToData());
381    SkMemoryStream memStream;
382    memStream.setData(data);
383
384    // Use the encoded bitmap as the data for an image ref.
385    SkBitmap bm;
386    SkAutoTUnref<SkDataImageRef> imageRef(SkNEW_ARGS(SkDataImageRef, (&memStream)));
387    imageRef->getInfo(&bm);
388    bm.setPixelRef(imageRef);
389
390    // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
391    // Flattening original will follow the old path of performing an encode, while flattening bm
392    // will use the already encoded data.
393    SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
394    SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
395    REPORTER_ASSERT(reporter, picture1->equals(picture2));
396}
397
398static void test_clone_empty(skiatest::Reporter* reporter) {
399    // This is a regression test for crbug.com/172062
400    // Before the fix, we used to crash accessing a null pointer when we
401    // had a picture with no paints. This test passes by not crashing.
402    {
403        SkPicture picture;
404        picture.beginRecording(1, 1);
405        picture.endRecording();
406        SkPicture* destPicture = picture.clone();
407        REPORTER_ASSERT(reporter, NULL != destPicture);
408        destPicture->unref();
409    }
410    {
411        // Test without call to endRecording
412        SkPicture picture;
413        picture.beginRecording(1, 1);
414        SkPicture* destPicture = picture.clone();
415        REPORTER_ASSERT(reporter, NULL != destPicture);
416        destPicture->unref();
417    }
418}
419
420static void TestPicture(skiatest::Reporter* reporter) {
421#ifdef SK_DEBUG
422    test_deleting_empty_playback();
423    test_serializing_empty_picture();
424#else
425    test_bad_bitmap();
426#endif
427    test_peephole(reporter);
428    test_gatherpixelrefs(reporter);
429    test_bitmap_with_encoded_data(reporter);
430    test_clone_empty(reporter);
431}
432
433#include "TestClassDef.h"
434DEFINE_TESTCLASS("Pictures", PictureTestClass, TestPicture)
435