PictureTest.cpp revision ed9866cc8ad9d9687eb0571e45128f1c9422d3f4
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
8#include "Test.h"
9#include "TestClassDef.h"
10#include "SkBitmapDevice.h"
11#include "SkCanvas.h"
12#include "SkColorPriv.h"
13#include "SkData.h"
14#include "SkDecodingImageGenerator.h"
15#include "SkError.h"
16#include "SkImageEncoder.h"
17#include "SkImageGenerator.h"
18#include "SkPaint.h"
19#include "SkPicture.h"
20#include "SkPictureUtils.h"
21#include "SkRandom.h"
22#include "SkRRect.h"
23#include "SkShader.h"
24#include "SkStream.h"
25
26static const int gColorScale = 30;
27static const int gColorOffset = 60;
28
29static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
30    bm->setConfig(SkBitmap::kARGB_8888_Config, w, h);
31    bm->allocPixels();
32    bm->eraseColor(color);
33    if (immutable) {
34        bm->setImmutable();
35    }
36}
37
38void make_checkerboard(SkBitmap* bm, int w, int h, bool immutable) {
39    SkASSERT(w % 2 == 0);
40    SkASSERT(h % 2 == 0);
41    bm->setConfig(SkBitmap::kA8_Config, w, h);
42    bm->allocPixels();
43    SkAutoLockPixels lock(*bm);
44    for (int y = 0; y < h; y += 2) {
45        uint8_t* s = bm->getAddr8(0, y);
46        for (int x = 0; x < w; x += 2) {
47            *s++ = 0xFF;
48            *s++ = 0x00;
49        }
50        s = bm->getAddr8(0, y + 1);
51        for (int x = 0; x < w; x += 2) {
52            *s++ = 0x00;
53            *s++ = 0xFF;
54        }
55    }
56    if (immutable) {
57        bm->setImmutable();
58    }
59}
60
61static void init_paint(SkPaint* paint, const SkBitmap &bm) {
62    SkShader* shader = SkShader::CreateBitmapShader(bm,
63                                                    SkShader::kClamp_TileMode,
64                                                    SkShader::kClamp_TileMode);
65    paint->setShader(shader)->unref();
66}
67
68typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&, const SkBitmap&, const SkPoint&);
69
70static void drawpaint_proc(SkCanvas* canvas, const SkBitmap& bm,
71                           const SkBitmap& altBM, const SkPoint& pos) {
72    SkPaint paint;
73    init_paint(&paint, bm);
74
75    canvas->drawPaint(paint);
76}
77
78static void drawpoints_proc(SkCanvas* canvas, const SkBitmap& bm,
79                            const SkBitmap& altBM, const SkPoint& pos) {
80    SkPaint paint;
81    init_paint(&paint, bm);
82
83    // draw a slightly inset rect
84    SkPoint points[5] = {
85        { pos.fX + 1, pos.fY + 1 },
86        { pos.fX + bm.width() - 2, pos.fY + 1 },
87        { pos.fX + bm.width() - 2, pos.fY + bm.height() - 2 },
88        { pos.fX + 1, pos.fY + bm.height() - 2 },
89        { pos.fX + 1, pos.fY + 1 },
90    };
91
92    canvas->drawPoints(SkCanvas::kPolygon_PointMode, 5, points, paint);
93}
94
95static void drawrect_proc(SkCanvas* canvas, const SkBitmap& bm,
96                          const SkBitmap& altBM, const SkPoint& pos) {
97    SkPaint paint;
98    init_paint(&paint, bm);
99
100    SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
101    r.offset(pos.fX, pos.fY);
102
103    canvas->drawRect(r, paint);
104}
105
106static void drawoval_proc(SkCanvas* canvas, const SkBitmap& bm,
107                          const SkBitmap& altBM, const SkPoint& pos) {
108    SkPaint paint;
109    init_paint(&paint, bm);
110
111    SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
112    r.offset(pos.fX, pos.fY);
113
114    canvas->drawOval(r, paint);
115}
116
117static void drawrrect_proc(SkCanvas* canvas, const SkBitmap& bm,
118                           const SkBitmap& altBM, const SkPoint& pos) {
119    SkPaint paint;
120    init_paint(&paint, bm);
121
122    SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
123    r.offset(pos.fX, pos.fY);
124
125    SkRRect rr;
126    rr.setRectXY(r, SkIntToScalar(bm.width())/4, SkIntToScalar(bm.height())/4);
127    canvas->drawRRect(rr, paint);
128}
129
130static void drawpath_proc(SkCanvas* canvas, const SkBitmap& bm,
131                          const SkBitmap& altBM, const SkPoint& pos) {
132    SkPaint paint;
133    init_paint(&paint, bm);
134
135    SkPath path;
136    path.lineTo(bm.width()/2.0f, SkIntToScalar(bm.height()));
137    path.lineTo(SkIntToScalar(bm.width()), 0);
138    path.close();
139    path.offset(pos.fX, pos.fY);
140
141    canvas->drawPath(path, paint);
142}
143
144static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm,
145                            const SkBitmap& altBM, const SkPoint& pos) {
146    canvas->drawBitmap(bm, pos.fX, pos.fY, NULL);
147}
148
149static void drawbitmap_withshader_proc(SkCanvas* canvas, const SkBitmap& bm,
150                                       const SkBitmap& altBM, const SkPoint& pos) {
151    SkPaint paint;
152    init_paint(&paint, bm);
153
154    // The bitmap in the paint is ignored unless we're drawing an A8 bitmap
155    canvas->drawBitmap(altBM, pos.fX, pos.fY, &paint);
156}
157
158static void drawsprite_proc(SkCanvas* canvas, const SkBitmap& bm,
159                            const SkBitmap& altBM, const SkPoint& pos) {
160    const SkMatrix& ctm = canvas->getTotalMatrix();
161
162    SkPoint p(pos);
163    ctm.mapPoints(&p, 1);
164
165    canvas->drawSprite(bm, (int)p.fX, (int)p.fY, NULL);
166}
167
168#if 0
169// Although specifiable, this case doesn't seem to make sense (i.e., the
170// bitmap in the shader is never used).
171static void drawsprite_withshader_proc(SkCanvas* canvas, const SkBitmap& bm,
172                                       const SkBitmap& altBM, const SkPoint& pos) {
173    SkPaint paint;
174    init_paint(&paint, bm);
175
176    const SkMatrix& ctm = canvas->getTotalMatrix();
177
178    SkPoint p(pos);
179    ctm.mapPoints(&p, 1);
180
181    canvas->drawSprite(altBM, (int)p.fX, (int)p.fY, &paint);
182}
183#endif
184
185static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm,
186                                const SkBitmap& altBM, const SkPoint& pos) {
187    SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
188
189    r.offset(pos.fX, pos.fY);
190    canvas->drawBitmapRectToRect(bm, NULL, r, NULL);
191}
192
193static void drawbitmaprect_withshader_proc(SkCanvas* canvas,
194                                           const SkBitmap& bm,
195                                           const SkBitmap& altBM,
196                                           const SkPoint& pos) {
197    SkPaint paint;
198    init_paint(&paint, bm);
199
200    SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
201    r.offset(pos.fX, pos.fY);
202
203    // The bitmap in the paint is ignored unless we're drawing an A8 bitmap
204    canvas->drawBitmapRectToRect(altBM, NULL, r, &paint);
205}
206
207static void drawtext_proc(SkCanvas* canvas, const SkBitmap& bm,
208                          const SkBitmap& altBM, const SkPoint& pos) {
209    SkPaint paint;
210    init_paint(&paint, bm);
211    paint.setTextSize(SkIntToScalar(1.5*bm.width()));
212
213    canvas->drawText("0", 1, pos.fX, pos.fY+bm.width(), paint);
214}
215
216static void drawpostext_proc(SkCanvas* canvas, const SkBitmap& bm,
217                             const SkBitmap& altBM, const SkPoint& pos) {
218    SkPaint paint;
219    init_paint(&paint, bm);
220    paint.setTextSize(SkIntToScalar(1.5*bm.width()));
221
222    SkPoint point = { pos.fX, pos.fY + bm.height() };
223    canvas->drawPosText("O", 1, &point, paint);
224}
225
226static void drawtextonpath_proc(SkCanvas* canvas, const SkBitmap& bm,
227                                const SkBitmap& altBM, const SkPoint& pos) {
228    SkPaint paint;
229
230    init_paint(&paint, bm);
231    paint.setTextSize(SkIntToScalar(1.5*bm.width()));
232
233    SkPath path;
234    path.lineTo(SkIntToScalar(bm.width()), 0);
235    path.offset(pos.fX, pos.fY+bm.height());
236
237    canvas->drawTextOnPath("O", 1, path, NULL, paint);
238}
239
240static void drawverts_proc(SkCanvas* canvas, const SkBitmap& bm,
241                           const SkBitmap& altBM, const SkPoint& pos) {
242    SkPaint paint;
243    init_paint(&paint, bm);
244
245    SkPoint verts[4] = {
246        { pos.fX+1, pos.fY+1 },
247        { pos.fX + bm.width()-1, pos.fY+1 },
248        { pos.fX + bm.width()-1, pos.fY + bm.height()-1 },
249        { pos.fX+1, pos.fY + bm.height()-1 }
250    };
251    SkPoint texs[4] = { { 0, 0 },
252                        { SkIntToScalar(bm.width()), 0 },
253                        { SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) },
254                        { 0, SkIntToScalar(bm.height()) } };
255    uint16_t indices[6] = { 0, 1, 2, 0, 2, 3 };
256
257    canvas->drawVertices(SkCanvas::kTriangles_VertexMode, 4, verts, texs, NULL, NULL,
258                         indices, 6, paint);
259}
260
261// Return a picture with the bitmaps drawn at the specified positions.
262static SkPicture* record_bitmaps(const SkBitmap bm[], const SkPoint pos[],
263                                 int count, DrawBitmapProc proc) {
264    SkPicture* pic = new SkPicture;
265    SkCanvas* canvas = pic->beginRecording(1000, 1000);
266    for (int i = 0; i < count; ++i) {
267        canvas->save();
268        SkRect clipRect = SkRect::MakeXYWH(pos[i].fX, pos[i].fY,
269                                           SkIntToScalar(bm[i].width()),
270                                           SkIntToScalar(bm[i].height()));
271        canvas->clipRect(clipRect, SkRegion::kIntersect_Op);
272        proc(canvas, bm[i], bm[count+i], pos[i]);
273        canvas->restore();
274    }
275    pic->endRecording();
276    return pic;
277}
278
279static void rand_rect(SkRect* rect, SkRandom& rand, SkScalar W, SkScalar H) {
280    rect->fLeft   = rand.nextRangeScalar(-W, 2*W);
281    rect->fTop    = rand.nextRangeScalar(-H, 2*H);
282    rect->fRight  = rect->fLeft + rand.nextRangeScalar(0, W);
283    rect->fBottom = rect->fTop + rand.nextRangeScalar(0, H);
284
285    // we integralize rect to make our tests more predictable, since Gather is
286    // a little sloppy.
287    SkIRect ir;
288    rect->round(&ir);
289    rect->set(ir);
290}
291
292static void draw(SkPicture* pic, int width, int height, SkBitmap* result) {
293    make_bm(result, width, height, SK_ColorBLACK, false);
294
295    SkCanvas canvas(*result);
296    canvas.drawPicture(*pic);
297}
298
299template <typename T> int find_index(const T* array, T elem, int count) {
300    for (int i = 0; i < count; ++i) {
301        if (array[i] == elem) {
302            return i;
303        }
304    }
305    return -1;
306}
307
308// Return true if 'ref' is found in array[]
309static bool find(SkPixelRef const * const * array, SkPixelRef const * ref, int count) {
310    return find_index<const SkPixelRef*>(array, ref, count) >= 0;
311}
312
313// Look at each pixel that is inside 'subset', and if its color appears in
314// colors[], find the corresponding value in refs[] and append that ref into
315// array, skipping duplicates of the same value.
316// Note that gathering pixelRefs from rendered colors suffers from the problem
317// that multiple simultaneous textures (e.g., A8 for alpha and 8888 for color)
318// isn't easy to reconstruct.
319static void gather_from_image(const SkBitmap& bm, SkPixelRef* const refs[],
320                              int count, SkTDArray<SkPixelRef*>* array,
321                              const SkRect& subset) {
322    SkIRect ir;
323    subset.roundOut(&ir);
324
325    if (!ir.intersect(0, 0, bm.width()-1, bm.height()-1)) {
326        return;
327    }
328
329    // Since we only want to return unique values in array, when we scan we just
330    // set a bit for each index'd color found. In practice we only have a few
331    // distinct colors, so we just use an int's bits as our array. Hence the
332    // assert that count <= number-of-bits-in-our-int.
333    SkASSERT((unsigned)count <= 32);
334    uint32_t bitarray = 0;
335
336    SkAutoLockPixels alp(bm);
337
338    for (int y = ir.fTop; y < ir.fBottom; ++y) {
339        for (int x = ir.fLeft; x < ir.fRight; ++x) {
340            SkPMColor pmc = *bm.getAddr32(x, y);
341            // the only good case where the color is not found would be if
342            // the color is transparent, meaning no bitmap was drawn in that
343            // pixel.
344            if (pmc) {
345                uint32_t index = SkGetPackedR32(pmc);
346                SkASSERT(SkGetPackedG32(pmc) == index);
347                SkASSERT(SkGetPackedB32(pmc) == index);
348                if (0 == index) {
349                    continue;           // background color
350                }
351                SkASSERT(0 == (index - gColorOffset) % gColorScale);
352                index = (index - gColorOffset) / gColorScale;
353                SkASSERT(static_cast<int>(index) < count);
354                bitarray |= 1 << index;
355            }
356        }
357    }
358
359    for (int i = 0; i < count; ++i) {
360        if (bitarray & (1 << i)) {
361            *array->append() = refs[i];
362        }
363    }
364}
365
366static void test_gatherpixelrefs(skiatest::Reporter* reporter) {
367    const int IW = 32;
368    const int IH = IW;
369    const SkScalar W = SkIntToScalar(IW);
370    const SkScalar H = W;
371
372    static const int N = 4;
373    SkBitmap bm[2*N];
374    SkPixelRef* refs[2*N];
375
376    const SkPoint pos[N] = {
377        { 0, 0 }, { W, 0 }, { 0, H }, { W, H }
378    };
379
380    // Our convention is that the color components contain an encoding of
381    // the index of their corresponding bitmap/pixelref. (0,0,0,0) is
382    // reserved for the background
383    for (int i = 0; i < N; ++i) {
384        make_bm(&bm[i], IW, IH,
385                SkColorSetARGB(0xFF,
386                               gColorScale*i+gColorOffset,
387                               gColorScale*i+gColorOffset,
388                               gColorScale*i+gColorOffset),
389                true);
390        refs[i] = bm[i].pixelRef();
391    }
392
393    // The A8 alternate bitmaps are all BW checkerboards
394    for (int i = 0; i < N; ++i) {
395        make_checkerboard(&bm[N+i], IW, IH, true);
396        refs[N+i] = bm[N+i].pixelRef();
397    }
398
399    static const DrawBitmapProc procs[] = {
400            drawpaint_proc,
401            drawpoints_proc,
402            drawrect_proc,
403            drawoval_proc,
404            drawrrect_proc,
405            drawpath_proc,
406            drawbitmap_proc,
407            drawbitmap_withshader_proc,
408            drawsprite_proc,
409#if 0
410            drawsprite_withshader_proc,
411#endif
412            drawbitmaprect_proc,
413            drawbitmaprect_withshader_proc,
414            drawtext_proc,
415            drawpostext_proc,
416            drawtextonpath_proc,
417            drawverts_proc,
418    };
419
420    SkRandom rand;
421    for (size_t k = 0; k < SK_ARRAY_COUNT(procs); ++k) {
422        SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, N, procs[k]));
423
424        REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0);
425        // quick check for a small piece of each quadrant, which should just
426        // contain 1 bitmap.
427        for (size_t  i = 0; i < SK_ARRAY_COUNT(pos); ++i) {
428            SkRect r;
429            r.set(2, 2, W - 2, H - 2);
430            r.offset(pos[i].fX, pos[i].fY);
431            SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r));
432            REPORTER_ASSERT(reporter, data);
433            if (data) {
434                SkPixelRef** gatheredRefs = (SkPixelRef**)data->data();
435                int count = static_cast<int>(data->size() / sizeof(SkPixelRef*));
436                REPORTER_ASSERT(reporter, 1 == count || 2 == count);
437                if (1 == count) {
438                    REPORTER_ASSERT(reporter, gatheredRefs[0] == refs[i]);
439                } else if (2 == count) {
440                    REPORTER_ASSERT(reporter,
441                        (gatheredRefs[0] == refs[i] && gatheredRefs[1] == refs[i+N]) ||
442                        (gatheredRefs[1] == refs[i] && gatheredRefs[0] == refs[i+N]));
443                }
444            }
445        }
446
447        SkBitmap image;
448        draw(pic, 2*IW, 2*IH, &image);
449
450        // Test a bunch of random (mostly) rects, and compare the gather results
451        // with a deduced list of refs by looking at the colors drawn.
452        for (int j = 0; j < 100; ++j) {
453            SkRect r;
454            rand_rect(&r, rand, 2*W, 2*H);
455
456            SkTDArray<SkPixelRef*> array;
457
458            SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
459            size_t dataSize = data ? data->size() : 0;
460            int gatherCount = static_cast<int>(dataSize / sizeof(SkPixelRef*));
461            SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize);
462            SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL;
463            SkAutoDataUnref adu(data);
464
465            gather_from_image(image, refs, N, &array, r);
466
467            /*
468             *  GatherPixelRefs is conservative, so it can return more bitmaps
469             *  that we actually can see (usually because of conservative bounds
470             *  inflation for antialiasing). Thus our check here is only that
471             *  Gather didn't miss any that we actually saw. Even that isn't
472             *  a strict requirement on Gather, which is meant to be quick and
473             *  only mostly-correct, but at the moment this test should work.
474             */
475            for (int i = 0; i < array.count(); ++i) {
476                bool found = find(gatherRefs, array[i], gatherCount);
477                REPORTER_ASSERT(reporter, found);
478#if 0
479                // enable this block of code to debug failures, as it will rerun
480                // the case that failed.
481                if (!found) {
482                    SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
483                    size_t dataSize = data ? data->size() : 0;
484                }
485#endif
486            }
487        }
488    }
489}
490
491#ifdef SK_DEBUG
492// Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only
493// run in debug mode.
494static void test_deleting_empty_playback() {
495    SkPicture picture;
496    // Creates an SkPictureRecord
497    picture.beginRecording(0, 0);
498    // Turns that into an SkPicturePlayback
499    picture.endRecording();
500    // Deletes the old SkPicturePlayback, and creates a new SkPictureRecord
501    picture.beginRecording(0, 0);
502}
503
504// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
505static void test_serializing_empty_picture() {
506    SkPicture picture;
507    picture.beginRecording(0, 0);
508    picture.endRecording();
509    SkDynamicMemoryWStream stream;
510    picture.serialize(&stream);
511}
512#endif
513
514static void rand_op(SkCanvas* canvas, SkRandom& rand) {
515    SkPaint paint;
516    SkRect rect = SkRect::MakeWH(50, 50);
517
518    SkScalar unit = rand.nextUScalar1();
519    if (unit <= 0.3) {
520//        SkDebugf("save\n");
521        canvas->save();
522    } else if (unit <= 0.6) {
523//        SkDebugf("restore\n");
524        canvas->restore();
525    } else if (unit <= 0.9) {
526//        SkDebugf("clip\n");
527        canvas->clipRect(rect);
528    } else {
529//        SkDebugf("draw\n");
530        canvas->drawPaint(paint);
531    }
532}
533
534static void test_peephole() {
535    SkRandom rand;
536
537    for (int j = 0; j < 100; j++) {
538        SkRandom rand2(rand); // remember the seed
539
540        SkPicture picture;
541        SkCanvas* canvas = picture.beginRecording(100, 100);
542
543        for (int i = 0; i < 1000; ++i) {
544            rand_op(canvas, rand);
545        }
546        picture.endRecording();
547
548        rand = rand2;
549    }
550
551    {
552        SkPicture picture;
553        SkCanvas* canvas = picture.beginRecording(100, 100);
554        SkRect rect = SkRect::MakeWH(50, 50);
555
556        for (int i = 0; i < 100; ++i) {
557            canvas->save();
558        }
559        while (canvas->getSaveCount() > 1) {
560            canvas->clipRect(rect);
561            canvas->restore();
562        }
563        picture.endRecording();
564    }
565}
566
567#ifndef SK_DEBUG
568// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
569// should never do this.
570static void test_bad_bitmap() {
571    // This bitmap has a width and height but no pixels. As a result, attempting to record it will
572    // fail.
573    SkBitmap bm;
574    bm.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
575    SkPicture picture;
576    SkCanvas* recordingCanvas = picture.beginRecording(100, 100);
577    recordingCanvas->drawBitmap(bm, 0, 0);
578    picture.endRecording();
579
580    SkCanvas canvas;
581    canvas.drawPicture(picture);
582}
583#endif
584
585static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) {
586    return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100);
587}
588
589static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
590    SkPicture picture;
591    SkCanvas* canvas = picture.beginRecording(bitmap.width(), bitmap.height());
592    canvas->drawBitmap(bitmap, 0, 0);
593    SkDynamicMemoryWStream wStream;
594    picture.serialize(&wStream, &encode_bitmap_to_data);
595    return wStream.copyToData();
596}
597
598struct ErrorContext {
599    int fErrors;
600    skiatest::Reporter* fReporter;
601};
602
603static void assert_one_parse_error_cb(SkError error, void* context) {
604    ErrorContext* errorContext = static_cast<ErrorContext*>(context);
605    errorContext->fErrors++;
606    // This test only expects one error, and that is a kParseError. If there are others,
607    // there is some unknown problem.
608    REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors,
609                            "This threw more errors than expected.");
610    REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error,
611                            SkGetLastErrorString());
612}
613
614static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
615    // Create a bitmap that will be encoded.
616    SkBitmap original;
617    make_bm(&original, 100, 100, SK_ColorBLUE, true);
618    SkDynamicMemoryWStream wStream;
619    if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
620        return;
621    }
622    SkAutoDataUnref data(wStream.copyToData());
623
624    SkBitmap bm;
625    bool installSuccess = SkInstallDiscardablePixelRef(
626         SkDecodingImageGenerator::Create(data, SkDecodingImageGenerator::Options()), &bm, NULL);
627    REPORTER_ASSERT(reporter, installSuccess);
628
629    // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
630    // Flattening original will follow the old path of performing an encode, while flattening bm
631    // will use the already encoded data.
632    SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
633    SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
634    REPORTER_ASSERT(reporter, picture1->equals(picture2));
635    // Now test that a parse error was generated when trying to create a new SkPicture without
636    // providing a function to decode the bitmap.
637    ErrorContext context;
638    context.fErrors = 0;
639    context.fReporter = reporter;
640    SkSetErrorCallback(assert_one_parse_error_cb, &context);
641    SkMemoryStream pictureStream(picture1);
642    SkClearLastError();
643    SkAutoUnref pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL));
644    REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL);
645    SkClearLastError();
646    SkSetErrorCallback(NULL, NULL);
647}
648
649static void test_clone_empty(skiatest::Reporter* reporter) {
650    // This is a regression test for crbug.com/172062
651    // Before the fix, we used to crash accessing a null pointer when we
652    // had a picture with no paints. This test passes by not crashing.
653    {
654        SkPicture picture;
655        picture.beginRecording(1, 1);
656        picture.endRecording();
657        SkPicture* destPicture = picture.clone();
658        REPORTER_ASSERT(reporter, NULL != destPicture);
659        destPicture->unref();
660    }
661    {
662        // Test without call to endRecording
663        SkPicture picture;
664        picture.beginRecording(1, 1);
665        SkPicture* destPicture = picture.clone();
666        REPORTER_ASSERT(reporter, NULL != destPicture);
667        destPicture->unref();
668    }
669}
670
671static void test_clip_bound_opt(skiatest::Reporter* reporter) {
672    // Test for crbug.com/229011
673    SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
674                                    SkIntToScalar(2), SkIntToScalar(2));
675    SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
676                                    SkIntToScalar(1), SkIntToScalar(1));
677    SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
678                                    SkIntToScalar(1), SkIntToScalar(1));
679
680    SkPath invPath;
681    invPath.addOval(rect1);
682    invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
683    SkPath path;
684    path.addOval(rect2);
685    SkPath path2;
686    path2.addOval(rect3);
687    SkIRect clipBounds;
688    // Minimalist test set for 100% code coverage of
689    // SkPictureRecord::updateClipConservativelyUsingBounds
690    {
691        SkPicture picture;
692        SkCanvas* canvas = picture.beginRecording(10, 10,
693            SkPicture::kUsePathBoundsForClip_RecordingFlag);
694        canvas->clipPath(invPath, SkRegion::kIntersect_Op);
695        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
696        REPORTER_ASSERT(reporter, true == nonEmpty);
697        REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
698        REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
699        REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
700        REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
701    }
702    {
703        SkPicture picture;
704        SkCanvas* canvas = picture.beginRecording(10, 10,
705            SkPicture::kUsePathBoundsForClip_RecordingFlag);
706        canvas->clipPath(path, SkRegion::kIntersect_Op);
707        canvas->clipPath(invPath, SkRegion::kIntersect_Op);
708        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
709        REPORTER_ASSERT(reporter, true == nonEmpty);
710        REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
711        REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
712        REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
713        REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
714    }
715    {
716        SkPicture picture;
717        SkCanvas* canvas = picture.beginRecording(10, 10,
718            SkPicture::kUsePathBoundsForClip_RecordingFlag);
719        canvas->clipPath(path, SkRegion::kIntersect_Op);
720        canvas->clipPath(invPath, SkRegion::kUnion_Op);
721        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
722        REPORTER_ASSERT(reporter, true == nonEmpty);
723        REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
724        REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
725        REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
726        REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
727    }
728    {
729        SkPicture picture;
730        SkCanvas* canvas = picture.beginRecording(10, 10,
731            SkPicture::kUsePathBoundsForClip_RecordingFlag);
732        canvas->clipPath(path, SkRegion::kDifference_Op);
733        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
734        REPORTER_ASSERT(reporter, true == nonEmpty);
735        REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
736        REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
737        REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
738        REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
739    }
740    {
741        SkPicture picture;
742        SkCanvas* canvas = picture.beginRecording(10, 10,
743            SkPicture::kUsePathBoundsForClip_RecordingFlag);
744        canvas->clipPath(path, SkRegion::kReverseDifference_Op);
745        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
746        // True clip is actually empty in this case, but the best
747        // determination we can make using only bounds as input is that the
748        // clip is included in the bounds of 'path'.
749        REPORTER_ASSERT(reporter, true == nonEmpty);
750        REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
751        REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
752        REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
753        REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
754    }
755    {
756        SkPicture picture;
757        SkCanvas* canvas = picture.beginRecording(10, 10,
758            SkPicture::kUsePathBoundsForClip_RecordingFlag);
759        canvas->clipPath(path, SkRegion::kIntersect_Op);
760        canvas->clipPath(path2, SkRegion::kXOR_Op);
761        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
762        REPORTER_ASSERT(reporter, true == nonEmpty);
763        REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
764        REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
765        REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
766        REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
767    }
768}
769
770/**
771 * A canvas that records the number of clip commands.
772 */
773class ClipCountingCanvas : public SkCanvas {
774public:
775    explicit ClipCountingCanvas(SkBaseDevice* device)
776        : SkCanvas(device)
777        , fClipCount(0){
778    }
779
780    virtual bool clipRect(const SkRect& r, SkRegion::Op op, bool doAA)
781        SK_OVERRIDE {
782        fClipCount += 1;
783        return this->INHERITED::clipRect(r, op, doAA);
784    }
785
786    virtual bool clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA)
787        SK_OVERRIDE {
788        fClipCount += 1;
789        return this->INHERITED::clipRRect(rrect, op, doAA);
790    }
791
792    virtual bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA)
793        SK_OVERRIDE {
794        fClipCount += 1;
795        return this->INHERITED::clipPath(path, op, doAA);
796    }
797
798    unsigned getClipCount() const { return fClipCount; }
799
800private:
801    unsigned fClipCount;
802
803    typedef SkCanvas INHERITED;
804};
805
806static void test_clip_expansion(skiatest::Reporter* reporter) {
807    SkPicture picture;
808    SkCanvas* canvas = picture.beginRecording(10, 10, 0);
809
810    canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op);
811    // The following expanding clip should not be skipped.
812    canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op);
813    // Draw something so the optimizer doesn't just fold the world.
814    SkPaint p;
815    p.setColor(SK_ColorBLUE);
816    canvas->drawPaint(p);
817
818    SkBitmapDevice testDevice(SkBitmap::kNo_Config, 10, 10);
819    ClipCountingCanvas testCanvas(&testDevice);
820    picture.draw(&testCanvas);
821
822    // Both clips should be present on playback.
823    REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
824}
825
826static void test_hierarchical(skiatest::Reporter* reporter) {
827    SkBitmap bm;
828    make_bm(&bm, 10, 10, SK_ColorRED, true);
829
830    SkCanvas* canvas;
831
832    SkPicture childPlain;
833    childPlain.beginRecording(10, 10);
834    childPlain.endRecording();
835    REPORTER_ASSERT(reporter, !childPlain.willPlayBackBitmaps()); // 0
836
837    SkPicture childWithBitmap;
838    childWithBitmap.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
839    childWithBitmap.endRecording();
840    REPORTER_ASSERT(reporter, childWithBitmap.willPlayBackBitmaps()); // 1
841
842    SkPicture parentPP;
843    canvas = parentPP.beginRecording(10, 10);
844    canvas->drawPicture(childPlain);
845    parentPP.endRecording();
846    REPORTER_ASSERT(reporter, !parentPP.willPlayBackBitmaps()); // 0
847
848    SkPicture parentPWB;
849    canvas = parentPWB.beginRecording(10, 10);
850    canvas->drawPicture(childWithBitmap);
851    parentPWB.endRecording();
852    REPORTER_ASSERT(reporter, parentPWB.willPlayBackBitmaps()); // 1
853
854    SkPicture parentWBP;
855    canvas = parentWBP.beginRecording(10, 10);
856    canvas->drawBitmap(bm, 0, 0);
857    canvas->drawPicture(childPlain);
858    parentWBP.endRecording();
859    REPORTER_ASSERT(reporter, parentWBP.willPlayBackBitmaps()); // 1
860
861    SkPicture parentWBWB;
862    canvas = parentWBWB.beginRecording(10, 10);
863    canvas->drawBitmap(bm, 0, 0);
864    canvas->drawPicture(childWithBitmap);
865    parentWBWB.endRecording();
866    REPORTER_ASSERT(reporter, parentWBWB.willPlayBackBitmaps()); // 2
867}
868
869DEF_TEST(Picture, reporter) {
870#ifdef SK_DEBUG
871    test_deleting_empty_playback();
872    test_serializing_empty_picture();
873#else
874    test_bad_bitmap();
875#endif
876    test_peephole();
877    test_gatherpixelrefs(reporter);
878    test_bitmap_with_encoded_data(reporter);
879    test_clone_empty(reporter);
880    test_clip_bound_opt(reporter);
881    test_clip_expansion(reporter);
882    test_hierarchical(reporter);
883}
884