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