blob: 207e610e5430d45483b4644af0c2fcfa07eeeb9b [file] [log] [blame]
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001/*
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 */
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00007
scroggo@google.comd614c6a2012-09-14 17:26:37 +00008#include "Test.h"
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00009#include "TestClassDef.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000010#include "SkBitmapDevice.h"
reed@google.com21b519d2012-10-02 17:42:15 +000011#include "SkCanvas.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000012#include "SkColorPriv.h"
13#include "SkData.h"
reed@google.combf790232013-12-13 19:45:58 +000014#include "SkDecodingImageGenerator.h"
scroggo@google.com49ce11b2013-04-25 18:29:32 +000015#include "SkError.h"
halcanary@google.com3d50ea12014-01-02 13:15:13 +000016#include "SkImageEncoder.h"
17#include "SkImageGenerator.h"
reed@google.com21b519d2012-10-02 17:42:15 +000018#include "SkPaint.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000019#include "SkPicture.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000020#include "SkPictureUtils.h"
reed@google.com21b519d2012-10-02 17:42:15 +000021#include "SkRandom.h"
reed@google.com72aa79c2013-01-24 18:27:42 +000022#include "SkRRect.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000023#include "SkShader.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000024#include "SkStream.h"
25
robertphillips@google.comed9866c2014-01-09 19:20:45 +000026static const int gColorScale = 30;
27static const int gColorOffset = 60;
reed@google.comfe7b1ed2012-11-29 21:00:39 +000028
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
robertphillips@google.comfe5824a2014-01-09 19:45:29 +000038static void make_checkerboard(SkBitmap* bm, int w, int h, bool immutable) {
robertphillips@google.comed9866c2014-01-09 19:20:45 +000039 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}
reed@google.comfe7b1ed2012-11-29 21:00:39 +000060
robertphillips@google.comed9866c2014-01-09 19:20:45 +000061static 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) {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000146 canvas->drawBitmap(bm, pos.fX, pos.fY, NULL);
147}
148
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000149static 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
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000189 r.offset(pos.fX, pos.fY);
190 canvas->drawBitmapRectToRect(bm, NULL, r, NULL);
191}
192
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000193static 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()) };
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000201 r.offset(pos.fX, pos.fY);
202
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000203 // 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) {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000209 SkPaint paint;
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000210 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);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000259}
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) {
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000267 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();
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000274 }
275 pic->endRecording();
276 return pic;
277}
278
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000279static void rand_rect(SkRect* rect, SkRandom& rand, SkScalar W, SkScalar H) {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000280 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
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000292static void draw(SkPicture* pic, int width, int height, SkBitmap* result) {
293 make_bm(result, width, height, SK_ColorBLACK, false);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000294
295 SkCanvas canvas(*result);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000296 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
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000313// 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
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000329 // 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
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000338 for (int y = ir.fTop; y < ir.fBottom; ++y) {
339 for (int x = ir.fLeft; x < ir.fRight; ++x) {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000340 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) {
bsalomon@google.comc3d753e2013-01-08 17:24:44 +0000345 uint32_t index = SkGetPackedR32(pmc);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000346 SkASSERT(SkGetPackedG32(pmc) == index);
347 SkASSERT(SkGetPackedB32(pmc) == index);
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000348 if (0 == index) {
349 continue; // background color
350 }
351 SkASSERT(0 == (index - gColorOffset) % gColorScale);
352 index = (index - gColorOffset) / gColorScale;
bsalomon@google.com5f429b02013-01-08 18:42:20 +0000353 SkASSERT(static_cast<int>(index) < count);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000354 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) {
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000367 const int IW = 32;
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000368 const int IH = IW;
369 const SkScalar W = SkIntToScalar(IW);
370 const SkScalar H = W;
371
372 static const int N = 4;
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000373 SkBitmap bm[2*N];
374 SkPixelRef* refs[2*N];
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000375
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000376 const SkPoint pos[N] = {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000377 { 0, 0 }, { W, 0 }, { 0, H }, { W, H }
378 };
379
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000380 // 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
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000383 for (int i = 0; i < N; ++i) {
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000384 make_bm(&bm[i], IW, IH,
385 SkColorSetARGB(0xFF,
386 gColorScale*i+gColorOffset,
387 gColorScale*i+gColorOffset,
388 gColorScale*i+gColorOffset),
389 true);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000390 refs[i] = bm[i].pixelRef();
391 }
392
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000393 // 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
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000399 static const DrawBitmapProc procs[] = {
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000400 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,
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000418 };
419
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000420 SkRandom rand;
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000421 for (size_t k = 0; k < SK_ARRAY_COUNT(procs); ++k) {
422 SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, N, procs[k]));
423
tomhudson@google.com381010e2013-10-24 11:12:47 +0000424 REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000425 // 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);
commit-bot@chromium.orgfe433c12013-07-09 16:04:32 +0000433 if (data) {
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000434 SkPixelRef** gatheredRefs = (SkPixelRef**)data->data();
robertphillips@google.come9cd27d2013-10-16 17:48:11 +0000435 int count = static_cast<int>(data->size() / sizeof(SkPixelRef*));
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000436 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 }
commit-bot@chromium.orgfe433c12013-07-09 16:04:32 +0000444 }
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000445 }
446
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000447 SkBitmap image;
448 draw(pic, 2*IW, 2*IH, &image);
449
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000450 // 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
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000456 SkTDArray<SkPixelRef*> array;
457
458 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
459 size_t dataSize = data ? data->size() : 0;
robertphillips@google.come9cd27d2013-10-16 17:48:11 +0000460 int gatherCount = static_cast<int>(dataSize / sizeof(SkPixelRef*));
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000461 SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize);
462 SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL;
463 SkAutoDataUnref adu(data);
464
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000465 gather_from_image(image, refs, N, &array, r);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000466
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000467 /*
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
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000491#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
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000514static void rand_op(SkCanvas* canvas, SkRandom& rand) {
reed@google.com21b519d2012-10-02 17:42:15 +0000515 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
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000534static void test_peephole() {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000535 SkRandom rand;
reed@google.com21b519d2012-10-02 17:42:15 +0000536
537 for (int j = 0; j < 100; j++) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000538 SkRandom rand2(rand); // remember the seed
reed@google.com21b519d2012-10-02 17:42:15 +0000539
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();
jvanverth@google.comc490f802013-03-04 13:56:38 +0000547
548 rand = rand2;
reed@google.com21b519d2012-10-02 17:42:15 +0000549 }
550
551 {
552 SkPicture picture;
553 SkCanvas* canvas = picture.beginRecording(100, 100);
554 SkRect rect = SkRect::MakeWH(50, 50);
skia.committer@gmail.com52c24372012-10-03 02:01:13 +0000555
reed@google.com21b519d2012-10-02 17:42:15 +0000556 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
scroggo@google.com4b90b112012-12-04 15:08:56 +0000567#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
reed@google.com672588b2014-01-08 15:42:01 +0000585static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) {
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000586 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000587}
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;
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000594 picture.serialize(&wStream, &encode_bitmap_to_data);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000595 return wStream.copyToData();
596}
597
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000598struct 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
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000614static 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());
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000623
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000624 SkBitmap bm;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000625 bool installSuccess = SkInstallDiscardablePixelRef(
626 SkDecodingImageGenerator::Create(data, SkDecodingImageGenerator::Options()), &bm, NULL);
reed@google.combf790232013-12-13 19:45:58 +0000627 REPORTER_ASSERT(reporter, installSuccess);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000628
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));
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000635 // 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);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000642 SkClearLastError();
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000643 SkAutoUnref pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL));
644 REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000645 SkClearLastError();
646 SkSetErrorCallback(NULL, NULL);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000647}
648
junov@chromium.org94f20dc2013-01-28 21:04:44 +0000649static 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
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000671static 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
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000770/**
771 * A canvas that records the number of clip commands.
772 */
773class ClipCountingCanvas : public SkCanvas {
774public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000775 explicit ClipCountingCanvas(SkBaseDevice* device)
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000776 : 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
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000818 SkBitmapDevice testDevice(SkBitmap::kNo_Config, 10, 10);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000819 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
tomhudson@google.com381010e2013-10-24 11:12:47 +0000826static 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
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000869DEF_TEST(Picture, reporter) {
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000870#ifdef SK_DEBUG
871 test_deleting_empty_playback();
872 test_serializing_empty_picture();
scroggo@google.com4b90b112012-12-04 15:08:56 +0000873#else
874 test_bad_bitmap();
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000875#endif
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000876 test_peephole();
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000877 test_gatherpixelrefs(reporter);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000878 test_bitmap_with_encoded_data(reporter);
junov@chromium.org94f20dc2013-01-28 21:04:44 +0000879 test_clone_empty(reporter);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000880 test_clip_bound_opt(reporter);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000881 test_clip_expansion(reporter);
tomhudson@google.com381010e2013-10-24 11:12:47 +0000882 test_hierarchical(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000883}