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            if (data) {
193                int count = data->size() / sizeof(SkPixelRef*);
194                REPORTER_ASSERT(reporter, 1 == count);
195                REPORTER_ASSERT(reporter, *(SkPixelRef**)data->data() == refs[i]);
196            }
197        }
198
199        // Test a bunch of random (mostly) rects, and compare the gather results
200        // with a deduced list of refs by looking at the colors drawn.
201        for (int j = 0; j < 100; ++j) {
202            SkRect r;
203            rand_rect(&r, rand, 2*W, 2*H);
204
205            SkBitmap result;
206            draw(pic, r, &result);
207            SkTDArray<SkPixelRef*> array;
208
209            SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
210            size_t dataSize = data ? data->size() : 0;
211            int gatherCount = dataSize / sizeof(SkPixelRef*);
212            SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize);
213            SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL;
214            SkAutoDataUnref adu(data);
215
216            gather_from_colors(result, refs, N, &array);
217
218            /*
219             *  GatherPixelRefs is conservative, so it can return more bitmaps
220             *  that we actually can see (usually because of conservative bounds
221             *  inflation for antialiasing). Thus our check here is only that
222             *  Gather didn't miss any that we actually saw. Even that isn't
223             *  a strict requirement on Gather, which is meant to be quick and
224             *  only mostly-correct, but at the moment this test should work.
225             */
226            for (int i = 0; i < array.count(); ++i) {
227                bool found = find(gatherRefs, array[i], gatherCount);
228                REPORTER_ASSERT(reporter, found);
229#if 0
230                // enable this block of code to debug failures, as it will rerun
231                // the case that failed.
232                if (!found) {
233                    SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
234                    size_t dataSize = data ? data->size() : 0;
235                }
236#endif
237            }
238        }
239    }
240}
241
242#ifdef SK_DEBUG
243// Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only
244// run in debug mode.
245static void test_deleting_empty_playback() {
246    SkPicture picture;
247    // Creates an SkPictureRecord
248    picture.beginRecording(0, 0);
249    // Turns that into an SkPicturePlayback
250    picture.endRecording();
251    // Deletes the old SkPicturePlayback, and creates a new SkPictureRecord
252    picture.beginRecording(0, 0);
253}
254
255// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
256static void test_serializing_empty_picture() {
257    SkPicture picture;
258    picture.beginRecording(0, 0);
259    picture.endRecording();
260    SkDynamicMemoryWStream stream;
261    picture.serialize(&stream);
262}
263#endif
264
265static void rand_op(SkCanvas* canvas, SkMWCRandom& rand) {
266    SkPaint paint;
267    SkRect rect = SkRect::MakeWH(50, 50);
268
269    SkScalar unit = rand.nextUScalar1();
270    if (unit <= 0.3) {
271//        SkDebugf("save\n");
272        canvas->save();
273    } else if (unit <= 0.6) {
274//        SkDebugf("restore\n");
275        canvas->restore();
276    } else if (unit <= 0.9) {
277//        SkDebugf("clip\n");
278        canvas->clipRect(rect);
279    } else {
280//        SkDebugf("draw\n");
281        canvas->drawPaint(paint);
282    }
283}
284
285static void test_peephole() {
286    SkMWCRandom rand;
287
288    for (int j = 0; j < 100; j++) {
289        SkMWCRandom rand2(rand); // remember the seed
290
291        SkPicture picture;
292        SkCanvas* canvas = picture.beginRecording(100, 100);
293
294        for (int i = 0; i < 1000; ++i) {
295            rand_op(canvas, rand);
296        }
297        picture.endRecording();
298
299        rand = rand2;
300    }
301
302    {
303        SkPicture picture;
304        SkCanvas* canvas = picture.beginRecording(100, 100);
305        SkRect rect = SkRect::MakeWH(50, 50);
306
307        for (int i = 0; i < 100; ++i) {
308            canvas->save();
309        }
310        while (canvas->getSaveCount() > 1) {
311            canvas->clipRect(rect);
312            canvas->restore();
313        }
314        picture.endRecording();
315    }
316}
317
318#ifndef SK_DEBUG
319// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
320// should never do this.
321static void test_bad_bitmap() {
322    // This bitmap has a width and height but no pixels. As a result, attempting to record it will
323    // fail.
324    SkBitmap bm;
325    bm.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
326    SkPicture picture;
327    SkCanvas* recordingCanvas = picture.beginRecording(100, 100);
328    recordingCanvas->drawBitmap(bm, 0, 0);
329    picture.endRecording();
330
331    SkCanvas canvas;
332    canvas.drawPicture(picture);
333}
334#endif
335
336#include "SkData.h"
337#include "SkImageRef_GlobalPool.h"
338// Class to test SkPixelRef::onRefEncodedData, since there are currently no implementations in skia.
339class SkDataImageRef : public SkImageRef_GlobalPool {
340
341public:
342    SkDataImageRef(SkMemoryStream* stream)
343        : SkImageRef_GlobalPool(stream, SkBitmap::kNo_Config) {
344        SkASSERT(stream != NULL);
345        fData = stream->copyToData();
346        this->setImmutable();
347    }
348
349    ~SkDataImageRef() {
350        fData->unref();
351    }
352
353    virtual SkData* onRefEncodedData() SK_OVERRIDE {
354        fData->ref();
355        return fData;
356    }
357
358private:
359    SkData* fData;
360};
361
362#include "SkImageEncoder.h"
363
364static SkData* encode_bitmap_to_data(size_t* offset, const SkBitmap& bm) {
365    *offset = 0;
366    return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100);
367}
368
369static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
370    SkPicture picture;
371    SkCanvas* canvas = picture.beginRecording(bitmap.width(), bitmap.height());
372    canvas->drawBitmap(bitmap, 0, 0);
373    SkDynamicMemoryWStream wStream;
374    picture.serialize(&wStream, &encode_bitmap_to_data);
375    return wStream.copyToData();
376}
377
378struct ErrorContext {
379    int fErrors;
380    skiatest::Reporter* fReporter;
381};
382
383static void assert_one_parse_error_cb(SkError error, void* context) {
384    ErrorContext* errorContext = static_cast<ErrorContext*>(context);
385    errorContext->fErrors++;
386    // This test only expects one error, and that is a kParseError. If there are others,
387    // there is some unknown problem.
388    REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors,
389                            "This threw more errors than expected.");
390    REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error,
391                            SkGetLastErrorString());
392}
393
394static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
395    // Create a bitmap that will be encoded.
396    SkBitmap original;
397    make_bm(&original, 100, 100, SK_ColorBLUE, true);
398    SkDynamicMemoryWStream wStream;
399    if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
400        return;
401    }
402    SkAutoDataUnref data(wStream.copyToData());
403    SkMemoryStream memStream;
404    memStream.setData(data);
405
406    // Use the encoded bitmap as the data for an image ref.
407    SkBitmap bm;
408    SkAutoTUnref<SkDataImageRef> imageRef(SkNEW_ARGS(SkDataImageRef, (&memStream)));
409    imageRef->getInfo(&bm);
410    bm.setPixelRef(imageRef);
411
412    // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
413    // Flattening original will follow the old path of performing an encode, while flattening bm
414    // will use the already encoded data.
415    SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
416    SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
417    REPORTER_ASSERT(reporter, picture1->equals(picture2));
418    // Now test that a parse error was generated when trying to create a new SkPicture without
419    // providing a function to decode the bitmap.
420    ErrorContext context;
421    context.fErrors = 0;
422    context.fReporter = reporter;
423    SkSetErrorCallback(assert_one_parse_error_cb, &context);
424    SkMemoryStream pictureStream(picture1);
425    SkClearLastError();
426    SkAutoUnref pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL));
427    REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL);
428    SkClearLastError();
429    SkSetErrorCallback(NULL, NULL);
430}
431
432static void test_clone_empty(skiatest::Reporter* reporter) {
433    // This is a regression test for crbug.com/172062
434    // Before the fix, we used to crash accessing a null pointer when we
435    // had a picture with no paints. This test passes by not crashing.
436    {
437        SkPicture picture;
438        picture.beginRecording(1, 1);
439        picture.endRecording();
440        SkPicture* destPicture = picture.clone();
441        REPORTER_ASSERT(reporter, NULL != destPicture);
442        destPicture->unref();
443    }
444    {
445        // Test without call to endRecording
446        SkPicture picture;
447        picture.beginRecording(1, 1);
448        SkPicture* destPicture = picture.clone();
449        REPORTER_ASSERT(reporter, NULL != destPicture);
450        destPicture->unref();
451    }
452}
453
454static void test_clip_bound_opt(skiatest::Reporter* reporter) {
455    // Test for crbug.com/229011
456    SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
457                                    SkIntToScalar(2), SkIntToScalar(2));
458    SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
459                                    SkIntToScalar(1), SkIntToScalar(1));
460    SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
461                                    SkIntToScalar(1), SkIntToScalar(1));
462
463    SkPath invPath;
464    invPath.addOval(rect1);
465    invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
466    SkPath path;
467    path.addOval(rect2);
468    SkPath path2;
469    path2.addOval(rect3);
470    SkIRect clipBounds;
471    // Minimalist test set for 100% code coverage of
472    // SkPictureRecord::updateClipConservativelyUsingBounds
473    {
474        SkPicture picture;
475        SkCanvas* canvas = picture.beginRecording(10, 10,
476            SkPicture::kUsePathBoundsForClip_RecordingFlag);
477        canvas->clipPath(invPath, SkRegion::kIntersect_Op);
478        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
479        REPORTER_ASSERT(reporter, true == nonEmpty);
480        REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
481        REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
482        REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
483        REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
484    }
485    {
486        SkPicture picture;
487        SkCanvas* canvas = picture.beginRecording(10, 10,
488            SkPicture::kUsePathBoundsForClip_RecordingFlag);
489        canvas->clipPath(path, SkRegion::kIntersect_Op);
490        canvas->clipPath(invPath, SkRegion::kIntersect_Op);
491        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
492        REPORTER_ASSERT(reporter, true == nonEmpty);
493        REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
494        REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
495        REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
496        REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
497    }
498    {
499        SkPicture picture;
500        SkCanvas* canvas = picture.beginRecording(10, 10,
501            SkPicture::kUsePathBoundsForClip_RecordingFlag);
502        canvas->clipPath(path, SkRegion::kIntersect_Op);
503        canvas->clipPath(invPath, SkRegion::kUnion_Op);
504        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
505        REPORTER_ASSERT(reporter, true == nonEmpty);
506        REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
507        REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
508        REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
509        REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
510    }
511    {
512        SkPicture picture;
513        SkCanvas* canvas = picture.beginRecording(10, 10,
514            SkPicture::kUsePathBoundsForClip_RecordingFlag);
515        canvas->clipPath(path, SkRegion::kDifference_Op);
516        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
517        REPORTER_ASSERT(reporter, true == nonEmpty);
518        REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
519        REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
520        REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
521        REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
522    }
523    {
524        SkPicture picture;
525        SkCanvas* canvas = picture.beginRecording(10, 10,
526            SkPicture::kUsePathBoundsForClip_RecordingFlag);
527        canvas->clipPath(path, SkRegion::kReverseDifference_Op);
528        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
529        // True clip is actually empty in this case, but the best
530        // determination we can make using only bounds as input is that the
531        // clip is included in the bounds of 'path'.
532        REPORTER_ASSERT(reporter, true == nonEmpty);
533        REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
534        REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
535        REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
536        REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
537    }
538    {
539        SkPicture picture;
540        SkCanvas* canvas = picture.beginRecording(10, 10,
541            SkPicture::kUsePathBoundsForClip_RecordingFlag);
542        canvas->clipPath(path, SkRegion::kIntersect_Op);
543        canvas->clipPath(path2, SkRegion::kXOR_Op);
544        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
545        REPORTER_ASSERT(reporter, true == nonEmpty);
546        REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
547        REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
548        REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
549        REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
550    }
551}
552
553static void TestPicture(skiatest::Reporter* reporter) {
554#ifdef SK_DEBUG
555    test_deleting_empty_playback();
556    test_serializing_empty_picture();
557#else
558    test_bad_bitmap();
559#endif
560    test_peephole();
561    test_gatherpixelrefs(reporter);
562    test_bitmap_with_encoded_data(reporter);
563    test_clone_empty(reporter);
564    test_clip_bound_opt(reporter);
565}
566
567#include "TestClassDef.h"
568DEFINE_TESTCLASS("Pictures", PictureTestClass, TestPicture)
569