blob: 2bdcfb346134b43f7fafe0c11a44607c802f5460 [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"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00009#include "SkBitmapDevice.h"
reed@google.com21b519d2012-10-02 17:42:15 +000010#include "SkCanvas.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000011#include "SkColorPriv.h"
12#include "SkData.h"
reed@google.combf790232013-12-13 19:45:58 +000013#include "SkDecodingImageGenerator.h"
scroggo@google.com49ce11b2013-04-25 18:29:32 +000014#include "SkError.h"
halcanary@google.com3d50ea12014-01-02 13:15:13 +000015#include "SkImageEncoder.h"
16#include "SkImageGenerator.h"
reed@google.com21b519d2012-10-02 17:42:15 +000017#include "SkPaint.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000018#include "SkPicture.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000019#include "SkPictureUtils.h"
reed@google.com21b519d2012-10-02 17:42:15 +000020#include "SkRandom.h"
reed@google.com72aa79c2013-01-24 18:27:42 +000021#include "SkRRect.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000022#include "SkShader.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000023#include "SkStream.h"
24
robertphillips@google.comed9866c2014-01-09 19:20:45 +000025static const int gColorScale = 30;
26static const int gColorOffset = 60;
reed@google.comfe7b1ed2012-11-29 21:00:39 +000027
28static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
29 bm->setConfig(SkBitmap::kARGB_8888_Config, w, h);
30 bm->allocPixels();
31 bm->eraseColor(color);
32 if (immutable) {
33 bm->setImmutable();
34 }
35}
36
robertphillips@google.comfe5824a2014-01-09 19:45:29 +000037static void make_checkerboard(SkBitmap* bm, int w, int h, bool immutable) {
robertphillips@google.comed9866c2014-01-09 19:20:45 +000038 SkASSERT(w % 2 == 0);
39 SkASSERT(h % 2 == 0);
40 bm->setConfig(SkBitmap::kA8_Config, w, h);
41 bm->allocPixels();
42 SkAutoLockPixels lock(*bm);
43 for (int y = 0; y < h; y += 2) {
44 uint8_t* s = bm->getAddr8(0, y);
45 for (int x = 0; x < w; x += 2) {
46 *s++ = 0xFF;
47 *s++ = 0x00;
48 }
49 s = bm->getAddr8(0, y + 1);
50 for (int x = 0; x < w; x += 2) {
51 *s++ = 0x00;
52 *s++ = 0xFF;
53 }
54 }
55 if (immutable) {
56 bm->setImmutable();
57 }
58}
reed@google.comfe7b1ed2012-11-29 21:00:39 +000059
robertphillips@google.comed9866c2014-01-09 19:20:45 +000060static void init_paint(SkPaint* paint, const SkBitmap &bm) {
61 SkShader* shader = SkShader::CreateBitmapShader(bm,
62 SkShader::kClamp_TileMode,
63 SkShader::kClamp_TileMode);
64 paint->setShader(shader)->unref();
65}
66
skia.committer@gmail.com2e9a7152014-01-14 07:01:42 +000067typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&,
robertphillips@google.com56bf6e42014-01-13 13:33:26 +000068 const SkBitmap&, const SkPoint&,
69 SkTDArray<SkPixelRef*>* usedPixRefs);
robertphillips@google.comed9866c2014-01-09 19:20:45 +000070
skia.committer@gmail.com856673a2014-01-10 07:08:11 +000071static void drawpaint_proc(SkCanvas* canvas, const SkBitmap& bm,
robertphillips@google.com56bf6e42014-01-13 13:33:26 +000072 const SkBitmap& altBM, const SkPoint& pos,
73 SkTDArray<SkPixelRef*>* usedPixRefs) {
robertphillips@google.comed9866c2014-01-09 19:20:45 +000074 SkPaint paint;
75 init_paint(&paint, bm);
76
77 canvas->drawPaint(paint);
robertphillips@google.com56bf6e42014-01-13 13:33:26 +000078 *usedPixRefs->append() = bm.pixelRef();
robertphillips@google.comed9866c2014-01-09 19:20:45 +000079}
80
skia.committer@gmail.com856673a2014-01-10 07:08:11 +000081static void drawpoints_proc(SkCanvas* canvas, const SkBitmap& bm,
robertphillips@google.com56bf6e42014-01-13 13:33:26 +000082 const SkBitmap& altBM, const SkPoint& pos,
83 SkTDArray<SkPixelRef*>* usedPixRefs) {
robertphillips@google.comed9866c2014-01-09 19:20:45 +000084 SkPaint paint;
85 init_paint(&paint, bm);
86
robertphillips@google.com56bf6e42014-01-13 13:33:26 +000087 // draw a rect
robertphillips@google.comed9866c2014-01-09 19:20:45 +000088 SkPoint points[5] = {
skia.committer@gmail.com2e9a7152014-01-14 07:01:42 +000089 { pos.fX, pos.fY },
90 { pos.fX + bm.width() - 1, pos.fY },
91 { pos.fX + bm.width() - 1, pos.fY + bm.height() - 1 },
92 { pos.fX, pos.fY + bm.height() - 1 },
93 { pos.fX, pos.fY },
robertphillips@google.comed9866c2014-01-09 19:20:45 +000094 };
95
96 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 5, points, paint);
robertphillips@google.com56bf6e42014-01-13 13:33:26 +000097 *usedPixRefs->append() = bm.pixelRef();
robertphillips@google.comed9866c2014-01-09 19:20:45 +000098}
99
skia.committer@gmail.com856673a2014-01-10 07:08:11 +0000100static void drawrect_proc(SkCanvas* canvas, const SkBitmap& bm,
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000101 const SkBitmap& altBM, const SkPoint& pos,
102 SkTDArray<SkPixelRef*>* usedPixRefs) {
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000103 SkPaint paint;
104 init_paint(&paint, bm);
105
106 SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
107 r.offset(pos.fX, pos.fY);
108
109 canvas->drawRect(r, paint);
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000110 *usedPixRefs->append() = bm.pixelRef();
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000111}
112
skia.committer@gmail.com856673a2014-01-10 07:08:11 +0000113static void drawoval_proc(SkCanvas* canvas, const SkBitmap& bm,
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000114 const SkBitmap& altBM, const SkPoint& pos,
115 SkTDArray<SkPixelRef*>* usedPixRefs) {
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000116 SkPaint paint;
117 init_paint(&paint, bm);
118
119 SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
120 r.offset(pos.fX, pos.fY);
121
122 canvas->drawOval(r, paint);
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000123 *usedPixRefs->append() = bm.pixelRef();
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000124}
125
skia.committer@gmail.com856673a2014-01-10 07:08:11 +0000126static void drawrrect_proc(SkCanvas* canvas, const SkBitmap& bm,
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000127 const SkBitmap& altBM, const SkPoint& pos,
128 SkTDArray<SkPixelRef*>* usedPixRefs) {
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000129 SkPaint paint;
130 init_paint(&paint, bm);
skia.committer@gmail.com856673a2014-01-10 07:08:11 +0000131
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000132 SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
133 r.offset(pos.fX, pos.fY);
134
135 SkRRect rr;
136 rr.setRectXY(r, SkIntToScalar(bm.width())/4, SkIntToScalar(bm.height())/4);
137 canvas->drawRRect(rr, paint);
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000138 *usedPixRefs->append() = bm.pixelRef();
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000139}
140
skia.committer@gmail.com856673a2014-01-10 07:08:11 +0000141static void drawpath_proc(SkCanvas* canvas, const SkBitmap& bm,
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000142 const SkBitmap& altBM, const SkPoint& pos,
143 SkTDArray<SkPixelRef*>* usedPixRefs) {
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000144 SkPaint paint;
145 init_paint(&paint, bm);
146
147 SkPath path;
148 path.lineTo(bm.width()/2.0f, SkIntToScalar(bm.height()));
149 path.lineTo(SkIntToScalar(bm.width()), 0);
150 path.close();
151 path.offset(pos.fX, pos.fY);
152
153 canvas->drawPath(path, paint);
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000154 *usedPixRefs->append() = bm.pixelRef();
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000155}
156
skia.committer@gmail.com856673a2014-01-10 07:08:11 +0000157static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm,
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000158 const SkBitmap& altBM, const SkPoint& pos,
159 SkTDArray<SkPixelRef*>* usedPixRefs) {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000160 canvas->drawBitmap(bm, pos.fX, pos.fY, NULL);
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000161 *usedPixRefs->append() = bm.pixelRef();
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000162}
163
skia.committer@gmail.com856673a2014-01-10 07:08:11 +0000164static void drawbitmap_withshader_proc(SkCanvas* canvas, const SkBitmap& bm,
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000165 const SkBitmap& altBM, const SkPoint& pos,
166 SkTDArray<SkPixelRef*>* usedPixRefs) {
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000167 SkPaint paint;
168 init_paint(&paint, bm);
169
170 // The bitmap in the paint is ignored unless we're drawing an A8 bitmap
171 canvas->drawBitmap(altBM, pos.fX, pos.fY, &paint);
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000172 *usedPixRefs->append() = bm.pixelRef();
173 *usedPixRefs->append() = altBM.pixelRef();
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000174}
175
skia.committer@gmail.com856673a2014-01-10 07:08:11 +0000176static void drawsprite_proc(SkCanvas* canvas, const SkBitmap& bm,
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000177 const SkBitmap& altBM, const SkPoint& pos,
178 SkTDArray<SkPixelRef*>* usedPixRefs) {
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000179 const SkMatrix& ctm = canvas->getTotalMatrix();
180
181 SkPoint p(pos);
182 ctm.mapPoints(&p, 1);
183
184 canvas->drawSprite(bm, (int)p.fX, (int)p.fY, NULL);
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000185 *usedPixRefs->append() = bm.pixelRef();
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000186}
187
188#if 0
189// Although specifiable, this case doesn't seem to make sense (i.e., the
190// bitmap in the shader is never used).
skia.committer@gmail.com856673a2014-01-10 07:08:11 +0000191static void drawsprite_withshader_proc(SkCanvas* canvas, const SkBitmap& bm,
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000192 const SkBitmap& altBM, const SkPoint& pos,
193 SkTDArray<SkPixelRef*>* usedPixRefs) {
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000194 SkPaint paint;
195 init_paint(&paint, bm);
196
197 const SkMatrix& ctm = canvas->getTotalMatrix();
198
199 SkPoint p(pos);
200 ctm.mapPoints(&p, 1);
201
202 canvas->drawSprite(altBM, (int)p.fX, (int)p.fY, &paint);
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000203 *usedPixRefs->append() = bm.pixelRef();
204 *usedPixRefs->append() = altBM.pixelRef();
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000205}
206#endif
207
skia.committer@gmail.com856673a2014-01-10 07:08:11 +0000208static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm,
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000209 const SkBitmap& altBM, const SkPoint& pos,
210 SkTDArray<SkPixelRef*>* usedPixRefs) {
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000211 SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
212
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000213 r.offset(pos.fX, pos.fY);
214 canvas->drawBitmapRectToRect(bm, NULL, r, NULL);
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000215 *usedPixRefs->append() = bm.pixelRef();
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000216}
217
skia.committer@gmail.com856673a2014-01-10 07:08:11 +0000218static void drawbitmaprect_withshader_proc(SkCanvas* canvas,
219 const SkBitmap& bm,
220 const SkBitmap& altBM,
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000221 const SkPoint& pos,
222 SkTDArray<SkPixelRef*>* usedPixRefs) {
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000223 SkPaint paint;
224 init_paint(&paint, bm);
225
226 SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000227 r.offset(pos.fX, pos.fY);
228
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000229 // The bitmap in the paint is ignored unless we're drawing an A8 bitmap
230 canvas->drawBitmapRectToRect(altBM, NULL, r, &paint);
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000231 *usedPixRefs->append() = bm.pixelRef();
232 *usedPixRefs->append() = altBM.pixelRef();
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000233}
234
skia.committer@gmail.com856673a2014-01-10 07:08:11 +0000235static void drawtext_proc(SkCanvas* canvas, const SkBitmap& bm,
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000236 const SkBitmap& altBM, const SkPoint& pos,
237 SkTDArray<SkPixelRef*>* usedPixRefs) {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000238 SkPaint paint;
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000239 init_paint(&paint, bm);
240 paint.setTextSize(SkIntToScalar(1.5*bm.width()));
241
242 canvas->drawText("0", 1, pos.fX, pos.fY+bm.width(), paint);
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000243 *usedPixRefs->append() = bm.pixelRef();
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000244}
245
skia.committer@gmail.com856673a2014-01-10 07:08:11 +0000246static void drawpostext_proc(SkCanvas* canvas, const SkBitmap& bm,
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000247 const SkBitmap& altBM, const SkPoint& pos,
248 SkTDArray<SkPixelRef*>* usedPixRefs) {
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000249 SkPaint paint;
250 init_paint(&paint, bm);
251 paint.setTextSize(SkIntToScalar(1.5*bm.width()));
252
253 SkPoint point = { pos.fX, pos.fY + bm.height() };
254 canvas->drawPosText("O", 1, &point, paint);
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000255 *usedPixRefs->append() = bm.pixelRef();
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000256}
257
skia.committer@gmail.com856673a2014-01-10 07:08:11 +0000258static void drawtextonpath_proc(SkCanvas* canvas, const SkBitmap& bm,
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000259 const SkBitmap& altBM, const SkPoint& pos,
260 SkTDArray<SkPixelRef*>* usedPixRefs) {
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000261 SkPaint paint;
262
263 init_paint(&paint, bm);
264 paint.setTextSize(SkIntToScalar(1.5*bm.width()));
265
266 SkPath path;
267 path.lineTo(SkIntToScalar(bm.width()), 0);
268 path.offset(pos.fX, pos.fY+bm.height());
269
270 canvas->drawTextOnPath("O", 1, path, NULL, paint);
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000271 *usedPixRefs->append() = bm.pixelRef();
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000272}
273
skia.committer@gmail.com856673a2014-01-10 07:08:11 +0000274static void drawverts_proc(SkCanvas* canvas, const SkBitmap& bm,
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000275 const SkBitmap& altBM, const SkPoint& pos,
276 SkTDArray<SkPixelRef*>* usedPixRefs) {
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000277 SkPaint paint;
278 init_paint(&paint, bm);
279
skia.committer@gmail.com2e9a7152014-01-14 07:01:42 +0000280 SkPoint verts[4] = {
281 { pos.fX, pos.fY },
282 { pos.fX + bm.width(), pos.fY },
283 { pos.fX + bm.width(), pos.fY + bm.height() },
284 { pos.fX, pos.fY + bm.height() }
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000285 };
skia.committer@gmail.com856673a2014-01-10 07:08:11 +0000286 SkPoint texs[4] = { { 0, 0 },
287 { SkIntToScalar(bm.width()), 0 },
288 { SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) },
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000289 { 0, SkIntToScalar(bm.height()) } };
290 uint16_t indices[6] = { 0, 1, 2, 0, 2, 3 };
291
skia.committer@gmail.com856673a2014-01-10 07:08:11 +0000292 canvas->drawVertices(SkCanvas::kTriangles_VertexMode, 4, verts, texs, NULL, NULL,
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000293 indices, 6, paint);
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000294 *usedPixRefs->append() = bm.pixelRef();
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000295}
296
297// Return a picture with the bitmaps drawn at the specified positions.
skia.committer@gmail.com2e9a7152014-01-14 07:01:42 +0000298static SkPicture* record_bitmaps(const SkBitmap bm[],
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000299 const SkPoint pos[],
300 SkTDArray<SkPixelRef*> analytic[],
skia.committer@gmail.com2e9a7152014-01-14 07:01:42 +0000301 int count,
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000302 DrawBitmapProc proc) {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000303 SkPicture* pic = new SkPicture;
304 SkCanvas* canvas = pic->beginRecording(1000, 1000);
305 for (int i = 0; i < count; ++i) {
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000306 analytic[i].rewind();
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000307 canvas->save();
308 SkRect clipRect = SkRect::MakeXYWH(pos[i].fX, pos[i].fY,
309 SkIntToScalar(bm[i].width()),
310 SkIntToScalar(bm[i].height()));
311 canvas->clipRect(clipRect, SkRegion::kIntersect_Op);
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000312 proc(canvas, bm[i], bm[count+i], pos[i], &analytic[i]);
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000313 canvas->restore();
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000314 }
315 pic->endRecording();
316 return pic;
317}
318
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000319static void rand_rect(SkRect* rect, SkRandom& rand, SkScalar W, SkScalar H) {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000320 rect->fLeft = rand.nextRangeScalar(-W, 2*W);
321 rect->fTop = rand.nextRangeScalar(-H, 2*H);
322 rect->fRight = rect->fLeft + rand.nextRangeScalar(0, W);
323 rect->fBottom = rect->fTop + rand.nextRangeScalar(0, H);
324
325 // we integralize rect to make our tests more predictable, since Gather is
326 // a little sloppy.
327 SkIRect ir;
328 rect->round(&ir);
329 rect->set(ir);
330}
331
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000332static void draw(SkPicture* pic, int width, int height, SkBitmap* result) {
333 make_bm(result, width, height, SK_ColorBLACK, false);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000334
335 SkCanvas canvas(*result);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000336 canvas.drawPicture(*pic);
337}
338
339template <typename T> int find_index(const T* array, T elem, int count) {
340 for (int i = 0; i < count; ++i) {
341 if (array[i] == elem) {
342 return i;
343 }
344 }
345 return -1;
346}
347
348// Return true if 'ref' is found in array[]
349static bool find(SkPixelRef const * const * array, SkPixelRef const * ref, int count) {
350 return find_index<const SkPixelRef*>(array, ref, count) >= 0;
351}
352
skia.committer@gmail.com856673a2014-01-10 07:08:11 +0000353// Look at each pixel that is inside 'subset', and if its color appears in
354// colors[], find the corresponding value in refs[] and append that ref into
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000355// array, skipping duplicates of the same value.
356// Note that gathering pixelRefs from rendered colors suffers from the problem
357// that multiple simultaneous textures (e.g., A8 for alpha and 8888 for color)
358// isn't easy to reconstruct.
359static void gather_from_image(const SkBitmap& bm, SkPixelRef* const refs[],
360 int count, SkTDArray<SkPixelRef*>* array,
361 const SkRect& subset) {
362 SkIRect ir;
363 subset.roundOut(&ir);
364
365 if (!ir.intersect(0, 0, bm.width()-1, bm.height()-1)) {
366 return;
367 }
368
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000369 // Since we only want to return unique values in array, when we scan we just
370 // set a bit for each index'd color found. In practice we only have a few
371 // distinct colors, so we just use an int's bits as our array. Hence the
372 // assert that count <= number-of-bits-in-our-int.
373 SkASSERT((unsigned)count <= 32);
374 uint32_t bitarray = 0;
375
376 SkAutoLockPixels alp(bm);
377
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000378 for (int y = ir.fTop; y < ir.fBottom; ++y) {
379 for (int x = ir.fLeft; x < ir.fRight; ++x) {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000380 SkPMColor pmc = *bm.getAddr32(x, y);
381 // the only good case where the color is not found would be if
382 // the color is transparent, meaning no bitmap was drawn in that
383 // pixel.
384 if (pmc) {
bsalomon@google.comc3d753e2013-01-08 17:24:44 +0000385 uint32_t index = SkGetPackedR32(pmc);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000386 SkASSERT(SkGetPackedG32(pmc) == index);
387 SkASSERT(SkGetPackedB32(pmc) == index);
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000388 if (0 == index) {
389 continue; // background color
390 }
391 SkASSERT(0 == (index - gColorOffset) % gColorScale);
392 index = (index - gColorOffset) / gColorScale;
bsalomon@google.com5f429b02013-01-08 18:42:20 +0000393 SkASSERT(static_cast<int>(index) < count);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000394 bitarray |= 1 << index;
395 }
396 }
397 }
398
399 for (int i = 0; i < count; ++i) {
400 if (bitarray & (1 << i)) {
401 *array->append() = refs[i];
402 }
403 }
404}
405
robertphillips@google.com0e4ce142014-01-13 13:46:45 +0000406static void gather_from_analytic(const SkPoint pos[], SkScalar w, SkScalar h,
skia.committer@gmail.com2e9a7152014-01-14 07:01:42 +0000407 const SkTDArray<SkPixelRef*> analytic[],
408 int count,
409 SkTDArray<SkPixelRef*>* result,
robertphillips@google.com0e4ce142014-01-13 13:46:45 +0000410 const SkRect& subset) {
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000411 for (int i = 0; i < count; ++i) {
412 SkRect rect = SkRect::MakeXYWH(pos[i].fX, pos[i].fY, w, h);
413
414 if (SkRect::Intersects(subset, rect)) {
415 result->append(analytic[i].count(), analytic[i].begin());
416 }
417 }
418}
419
420static const DrawBitmapProc gProcs[] = {
421 drawpaint_proc,
422 drawpoints_proc,
423 drawrect_proc,
424 drawoval_proc,
425 drawrrect_proc,
426 drawpath_proc,
skia.committer@gmail.com2e9a7152014-01-14 07:01:42 +0000427 drawbitmap_proc,
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000428 drawbitmap_withshader_proc,
429 drawsprite_proc,
430#if 0
431 drawsprite_withshader_proc,
432#endif
skia.committer@gmail.com2e9a7152014-01-14 07:01:42 +0000433 drawbitmaprect_proc,
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000434 drawbitmaprect_withshader_proc,
435 drawtext_proc,
436 drawpostext_proc,
437 drawtextonpath_proc,
438 drawverts_proc,
439};
440
441static void create_textures(SkBitmap* bm, SkPixelRef** refs, int num, int w, int h) {
skia.committer@gmail.com2e9a7152014-01-14 07:01:42 +0000442 // Our convention is that the color components contain an encoding of
443 // the index of their corresponding bitmap/pixelref. (0,0,0,0) is
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000444 // reserved for the background
445 for (int i = 0; i < num; ++i) {
skia.committer@gmail.com2e9a7152014-01-14 07:01:42 +0000446 make_bm(&bm[i], w, h,
447 SkColorSetARGB(0xFF,
448 gColorScale*i+gColorOffset,
449 gColorScale*i+gColorOffset,
450 gColorScale*i+gColorOffset),
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000451 true);
452 refs[i] = bm[i].pixelRef();
453 }
454
455 // The A8 alternate bitmaps are all BW checkerboards
456 for (int i = 0; i < num; ++i) {
457 make_checkerboard(&bm[num+i], w, h, true);
458 refs[num+i] = bm[num+i].pixelRef();
459 }
460}
461
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000462static void test_gatherpixelrefs(skiatest::Reporter* reporter) {
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000463 const int IW = 32;
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000464 const int IH = IW;
465 const SkScalar W = SkIntToScalar(IW);
466 const SkScalar H = W;
467
468 static const int N = 4;
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000469 SkBitmap bm[2*N];
470 SkPixelRef* refs[2*N];
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000471 SkTDArray<SkPixelRef*> analytic[N];
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000472
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000473 const SkPoint pos[N] = {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000474 { 0, 0 }, { W, 0 }, { 0, H }, { W, H }
475 };
476
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000477 create_textures(bm, refs, N, IW, IH);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000478
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000479 SkRandom rand;
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000480 for (size_t k = 0; k < SK_ARRAY_COUNT(gProcs); ++k) {
481 SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, analytic, N, gProcs[k]));
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000482
tomhudson@google.com381010e2013-10-24 11:12:47 +0000483 REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000484 // quick check for a small piece of each quadrant, which should just
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000485 // contain 1 or 2 bitmaps.
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000486 for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) {
487 SkRect r;
488 r.set(2, 2, W - 2, H - 2);
489 r.offset(pos[i].fX, pos[i].fY);
490 SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r));
491 REPORTER_ASSERT(reporter, data);
commit-bot@chromium.orgfe433c12013-07-09 16:04:32 +0000492 if (data) {
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000493 SkPixelRef** gatheredRefs = (SkPixelRef**)data->data();
robertphillips@google.come9cd27d2013-10-16 17:48:11 +0000494 int count = static_cast<int>(data->size() / sizeof(SkPixelRef*));
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000495 REPORTER_ASSERT(reporter, 1 == count || 2 == count);
496 if (1 == count) {
497 REPORTER_ASSERT(reporter, gatheredRefs[0] == refs[i]);
498 } else if (2 == count) {
skia.committer@gmail.com856673a2014-01-10 07:08:11 +0000499 REPORTER_ASSERT(reporter,
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000500 (gatheredRefs[0] == refs[i] && gatheredRefs[1] == refs[i+N]) ||
501 (gatheredRefs[1] == refs[i] && gatheredRefs[0] == refs[i+N]));
502 }
commit-bot@chromium.orgfe433c12013-07-09 16:04:32 +0000503 }
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000504 }
505
robertphillips@google.comed9866c2014-01-09 19:20:45 +0000506 SkBitmap image;
507 draw(pic, 2*IW, 2*IH, &image);
508
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000509 // Test a bunch of random (mostly) rects, and compare the gather results
510 // with a deduced list of refs by looking at the colors drawn.
511 for (int j = 0; j < 100; ++j) {
512 SkRect r;
513 rand_rect(&r, rand, 2*W, 2*H);
514
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000515 SkTDArray<SkPixelRef*> fromImage;
516 gather_from_image(image, refs, N, &fromImage, r);
517
518 SkTDArray<SkPixelRef*> fromAnalytic;
519 gather_from_analytic(pos, W, H, analytic, N, &fromAnalytic, r);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000520
521 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
522 size_t dataSize = data ? data->size() : 0;
robertphillips@google.come9cd27d2013-10-16 17:48:11 +0000523 int gatherCount = static_cast<int>(dataSize / sizeof(SkPixelRef*));
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000524 SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize);
525 SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL;
526 SkAutoDataUnref adu(data);
527
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000528 // Everything that we saw drawn should appear in the analytic list
529 // but the analytic list may contain some pixelRefs that were not
530 // seen in the image (e.g., A8 textures used as masks)
531 for (int i = 0; i < fromImage.count(); ++i) {
532 REPORTER_ASSERT(reporter, -1 != fromAnalytic.find(fromImage[i]));
533 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000534
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000535 /*
536 * GatherPixelRefs is conservative, so it can return more bitmaps
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000537 * than are strictly required. Thus our check here is only that
538 * Gather didn't miss any that we actually needed. Even that isn't
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000539 * a strict requirement on Gather, which is meant to be quick and
540 * only mostly-correct, but at the moment this test should work.
541 */
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000542 for (int i = 0; i < fromAnalytic.count(); ++i) {
543 bool found = find(gatherRefs, fromAnalytic[i], gatherCount);
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000544 REPORTER_ASSERT(reporter, found);
545#if 0
546 // enable this block of code to debug failures, as it will rerun
547 // the case that failed.
548 if (!found) {
549 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
550 size_t dataSize = data ? data->size() : 0;
551 }
552#endif
553 }
554 }
555 }
556}
557
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000558static void test_gatherpixelrefsandrects(skiatest::Reporter* reporter) {
559 const int IW = 32;
560 const int IH = IW;
561 const SkScalar W = SkIntToScalar(IW);
562 const SkScalar H = W;
563
564 static const int N = 4;
565 SkBitmap bm[2*N];
566 SkPixelRef* refs[2*N];
567 SkTDArray<SkPixelRef*> analytic[N];
568
569 const SkPoint pos[N] = {
570 { 0, 0 }, { W, 0 }, { 0, H }, { W, H }
571 };
572
573 create_textures(bm, refs, N, IW, IH);
574
575 SkRandom rand;
576 for (size_t k = 0; k < SK_ARRAY_COUNT(gProcs); ++k) {
577 SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, analytic, N, gProcs[k]));
578
579 REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0);
580
581 SkAutoTUnref<SkPictureUtils::SkPixelRefContainer> prCont(
582 new SkPictureUtils::SkPixelRefsAndRectsList);
583
584 SkPictureUtils::GatherPixelRefsAndRects(pic, prCont);
585
586 // quick check for a small piece of each quadrant, which should just
587 // contain 1 or 2 bitmaps.
588 for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) {
589 SkRect r;
590 r.set(2, 2, W - 2, H - 2);
591 r.offset(pos[i].fX, pos[i].fY);
592
593 SkTDArray<SkPixelRef*> gatheredRefs;
594 prCont->query(r, &gatheredRefs);
595
596 int count = gatheredRefs.count();
597 REPORTER_ASSERT(reporter, 1 == count || 2 == count);
598 if (1 == count) {
599 REPORTER_ASSERT(reporter, gatheredRefs[0] == refs[i]);
600 } else if (2 == count) {
skia.committer@gmail.com2e9a7152014-01-14 07:01:42 +0000601 REPORTER_ASSERT(reporter,
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000602 (gatheredRefs[0] == refs[i] && gatheredRefs[1] == refs[i+N]) ||
603 (gatheredRefs[1] == refs[i] && gatheredRefs[0] == refs[i+N]));
604 }
605 }
606
607 SkBitmap image;
608 draw(pic, 2*IW, 2*IH, &image);
609
610 // Test a bunch of random (mostly) rects, and compare the gather results
611 // with the analytic results and the pixel refs seen in a rendering.
612 for (int j = 0; j < 100; ++j) {
613 SkRect r;
614 rand_rect(&r, rand, 2*W, 2*H);
615
616 SkTDArray<SkPixelRef*> fromImage;
617 gather_from_image(image, refs, N, &fromImage, r);
618
619 SkTDArray<SkPixelRef*> fromAnalytic;
620 gather_from_analytic(pos, W, H, analytic, N, &fromAnalytic, r);
621
622 SkTDArray<SkPixelRef*> gatheredRefs;
623 prCont->query(r, &gatheredRefs);
624
625 // Everything that we saw drawn should appear in the analytic list
626 // but the analytic list may contain some pixelRefs that were not
627 // seen in the image (e.g., A8 textures used as masks)
628 for (int i = 0; i < fromImage.count(); ++i) {
629 REPORTER_ASSERT(reporter, -1 != fromAnalytic.find(fromImage[i]));
630 }
631
632 // Everything in the analytic list should appear in the gathered
skia.committer@gmail.com2e9a7152014-01-14 07:01:42 +0000633 // list.
robertphillips@google.com56bf6e42014-01-13 13:33:26 +0000634 for (int i = 0; i < fromAnalytic.count(); ++i) {
635 REPORTER_ASSERT(reporter, -1 != gatheredRefs.find(fromAnalytic[i]));
636 }
637 }
638 }
639}
640
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000641#ifdef SK_DEBUG
642// Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only
643// run in debug mode.
644static void test_deleting_empty_playback() {
645 SkPicture picture;
646 // Creates an SkPictureRecord
647 picture.beginRecording(0, 0);
648 // Turns that into an SkPicturePlayback
649 picture.endRecording();
650 // Deletes the old SkPicturePlayback, and creates a new SkPictureRecord
651 picture.beginRecording(0, 0);
652}
653
654// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
655static void test_serializing_empty_picture() {
656 SkPicture picture;
657 picture.beginRecording(0, 0);
658 picture.endRecording();
659 SkDynamicMemoryWStream stream;
660 picture.serialize(&stream);
661}
662#endif
663
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000664static void rand_op(SkCanvas* canvas, SkRandom& rand) {
reed@google.com21b519d2012-10-02 17:42:15 +0000665 SkPaint paint;
666 SkRect rect = SkRect::MakeWH(50, 50);
667
668 SkScalar unit = rand.nextUScalar1();
669 if (unit <= 0.3) {
670// SkDebugf("save\n");
671 canvas->save();
672 } else if (unit <= 0.6) {
673// SkDebugf("restore\n");
674 canvas->restore();
675 } else if (unit <= 0.9) {
676// SkDebugf("clip\n");
677 canvas->clipRect(rect);
678 } else {
679// SkDebugf("draw\n");
680 canvas->drawPaint(paint);
681 }
682}
683
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000684static void test_peephole() {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000685 SkRandom rand;
reed@google.com21b519d2012-10-02 17:42:15 +0000686
687 for (int j = 0; j < 100; j++) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000688 SkRandom rand2(rand); // remember the seed
reed@google.com21b519d2012-10-02 17:42:15 +0000689
690 SkPicture picture;
691 SkCanvas* canvas = picture.beginRecording(100, 100);
692
693 for (int i = 0; i < 1000; ++i) {
694 rand_op(canvas, rand);
695 }
696 picture.endRecording();
jvanverth@google.comc490f802013-03-04 13:56:38 +0000697
698 rand = rand2;
reed@google.com21b519d2012-10-02 17:42:15 +0000699 }
700
701 {
702 SkPicture picture;
703 SkCanvas* canvas = picture.beginRecording(100, 100);
704 SkRect rect = SkRect::MakeWH(50, 50);
skia.committer@gmail.com52c24372012-10-03 02:01:13 +0000705
reed@google.com21b519d2012-10-02 17:42:15 +0000706 for (int i = 0; i < 100; ++i) {
707 canvas->save();
708 }
709 while (canvas->getSaveCount() > 1) {
710 canvas->clipRect(rect);
711 canvas->restore();
712 }
713 picture.endRecording();
714 }
715}
716
scroggo@google.com4b90b112012-12-04 15:08:56 +0000717#ifndef SK_DEBUG
718// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
719// should never do this.
720static void test_bad_bitmap() {
721 // This bitmap has a width and height but no pixels. As a result, attempting to record it will
722 // fail.
723 SkBitmap bm;
724 bm.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
725 SkPicture picture;
726 SkCanvas* recordingCanvas = picture.beginRecording(100, 100);
727 recordingCanvas->drawBitmap(bm, 0, 0);
728 picture.endRecording();
729
730 SkCanvas canvas;
731 canvas.drawPicture(picture);
732}
733#endif
734
reed@google.com672588b2014-01-08 15:42:01 +0000735static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) {
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000736 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000737}
738
739static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
740 SkPicture picture;
741 SkCanvas* canvas = picture.beginRecording(bitmap.width(), bitmap.height());
742 canvas->drawBitmap(bitmap, 0, 0);
743 SkDynamicMemoryWStream wStream;
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000744 picture.serialize(&wStream, &encode_bitmap_to_data);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000745 return wStream.copyToData();
746}
747
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000748struct ErrorContext {
749 int fErrors;
750 skiatest::Reporter* fReporter;
751};
752
753static void assert_one_parse_error_cb(SkError error, void* context) {
754 ErrorContext* errorContext = static_cast<ErrorContext*>(context);
755 errorContext->fErrors++;
756 // This test only expects one error, and that is a kParseError. If there are others,
757 // there is some unknown problem.
758 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors,
759 "This threw more errors than expected.");
760 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error,
761 SkGetLastErrorString());
762}
763
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000764static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
765 // Create a bitmap that will be encoded.
766 SkBitmap original;
767 make_bm(&original, 100, 100, SK_ColorBLUE, true);
768 SkDynamicMemoryWStream wStream;
769 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
770 return;
771 }
772 SkAutoDataUnref data(wStream.copyToData());
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000773
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000774 SkBitmap bm;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000775 bool installSuccess = SkInstallDiscardablePixelRef(
776 SkDecodingImageGenerator::Create(data, SkDecodingImageGenerator::Options()), &bm, NULL);
reed@google.combf790232013-12-13 19:45:58 +0000777 REPORTER_ASSERT(reporter, installSuccess);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000778
779 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
780 // Flattening original will follow the old path of performing an encode, while flattening bm
781 // will use the already encoded data.
782 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
783 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
784 REPORTER_ASSERT(reporter, picture1->equals(picture2));
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000785 // Now test that a parse error was generated when trying to create a new SkPicture without
786 // providing a function to decode the bitmap.
787 ErrorContext context;
788 context.fErrors = 0;
789 context.fReporter = reporter;
790 SkSetErrorCallback(assert_one_parse_error_cb, &context);
791 SkMemoryStream pictureStream(picture1);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000792 SkClearLastError();
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000793 SkAutoUnref pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL));
794 REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000795 SkClearLastError();
796 SkSetErrorCallback(NULL, NULL);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000797}
798
junov@chromium.org94f20dc2013-01-28 21:04:44 +0000799static void test_clone_empty(skiatest::Reporter* reporter) {
800 // This is a regression test for crbug.com/172062
801 // Before the fix, we used to crash accessing a null pointer when we
802 // had a picture with no paints. This test passes by not crashing.
803 {
804 SkPicture picture;
805 picture.beginRecording(1, 1);
806 picture.endRecording();
807 SkPicture* destPicture = picture.clone();
808 REPORTER_ASSERT(reporter, NULL != destPicture);
809 destPicture->unref();
810 }
811 {
812 // Test without call to endRecording
813 SkPicture picture;
814 picture.beginRecording(1, 1);
815 SkPicture* destPicture = picture.clone();
816 REPORTER_ASSERT(reporter, NULL != destPicture);
817 destPicture->unref();
818 }
819}
820
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000821static void test_clip_bound_opt(skiatest::Reporter* reporter) {
822 // Test for crbug.com/229011
823 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
824 SkIntToScalar(2), SkIntToScalar(2));
825 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
826 SkIntToScalar(1), SkIntToScalar(1));
827 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
828 SkIntToScalar(1), SkIntToScalar(1));
829
830 SkPath invPath;
831 invPath.addOval(rect1);
832 invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
833 SkPath path;
834 path.addOval(rect2);
835 SkPath path2;
836 path2.addOval(rect3);
837 SkIRect clipBounds;
838 // Minimalist test set for 100% code coverage of
839 // SkPictureRecord::updateClipConservativelyUsingBounds
840 {
841 SkPicture picture;
842 SkCanvas* canvas = picture.beginRecording(10, 10,
843 SkPicture::kUsePathBoundsForClip_RecordingFlag);
844 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
845 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
846 REPORTER_ASSERT(reporter, true == nonEmpty);
847 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
848 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
849 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
850 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
851 }
852 {
853 SkPicture picture;
854 SkCanvas* canvas = picture.beginRecording(10, 10,
855 SkPicture::kUsePathBoundsForClip_RecordingFlag);
856 canvas->clipPath(path, SkRegion::kIntersect_Op);
857 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
858 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
859 REPORTER_ASSERT(reporter, true == nonEmpty);
860 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
861 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
862 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
863 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
864 }
865 {
866 SkPicture picture;
867 SkCanvas* canvas = picture.beginRecording(10, 10,
868 SkPicture::kUsePathBoundsForClip_RecordingFlag);
869 canvas->clipPath(path, SkRegion::kIntersect_Op);
870 canvas->clipPath(invPath, SkRegion::kUnion_Op);
871 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
872 REPORTER_ASSERT(reporter, true == nonEmpty);
873 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
874 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
875 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
876 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
877 }
878 {
879 SkPicture picture;
880 SkCanvas* canvas = picture.beginRecording(10, 10,
881 SkPicture::kUsePathBoundsForClip_RecordingFlag);
882 canvas->clipPath(path, SkRegion::kDifference_Op);
883 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
884 REPORTER_ASSERT(reporter, true == nonEmpty);
885 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
886 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
887 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
888 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
889 }
890 {
891 SkPicture picture;
892 SkCanvas* canvas = picture.beginRecording(10, 10,
893 SkPicture::kUsePathBoundsForClip_RecordingFlag);
894 canvas->clipPath(path, SkRegion::kReverseDifference_Op);
895 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
896 // True clip is actually empty in this case, but the best
897 // determination we can make using only bounds as input is that the
898 // clip is included in the bounds of 'path'.
899 REPORTER_ASSERT(reporter, true == nonEmpty);
900 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
901 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
902 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
903 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
904 }
905 {
906 SkPicture picture;
907 SkCanvas* canvas = picture.beginRecording(10, 10,
908 SkPicture::kUsePathBoundsForClip_RecordingFlag);
909 canvas->clipPath(path, SkRegion::kIntersect_Op);
910 canvas->clipPath(path2, SkRegion::kXOR_Op);
911 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
912 REPORTER_ASSERT(reporter, true == nonEmpty);
913 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
914 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
915 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
916 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
917 }
918}
919
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000920/**
921 * A canvas that records the number of clip commands.
922 */
923class ClipCountingCanvas : public SkCanvas {
924public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000925 explicit ClipCountingCanvas(SkBaseDevice* device)
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000926 : SkCanvas(device)
927 , fClipCount(0){
928 }
929
930 virtual bool clipRect(const SkRect& r, SkRegion::Op op, bool doAA)
931 SK_OVERRIDE {
932 fClipCount += 1;
933 return this->INHERITED::clipRect(r, op, doAA);
934 }
935
936 virtual bool clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA)
937 SK_OVERRIDE {
938 fClipCount += 1;
939 return this->INHERITED::clipRRect(rrect, op, doAA);
940 }
941
942 virtual bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA)
943 SK_OVERRIDE {
944 fClipCount += 1;
945 return this->INHERITED::clipPath(path, op, doAA);
946 }
947
948 unsigned getClipCount() const { return fClipCount; }
949
950private:
951 unsigned fClipCount;
952
953 typedef SkCanvas INHERITED;
954};
955
956static void test_clip_expansion(skiatest::Reporter* reporter) {
957 SkPicture picture;
958 SkCanvas* canvas = picture.beginRecording(10, 10, 0);
959
960 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op);
961 // The following expanding clip should not be skipped.
962 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op);
963 // Draw something so the optimizer doesn't just fold the world.
964 SkPaint p;
965 p.setColor(SK_ColorBLUE);
966 canvas->drawPaint(p);
967
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000968 SkBitmapDevice testDevice(SkBitmap::kNo_Config, 10, 10);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000969 ClipCountingCanvas testCanvas(&testDevice);
970 picture.draw(&testCanvas);
971
972 // Both clips should be present on playback.
973 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
974}
975
tomhudson@google.com381010e2013-10-24 11:12:47 +0000976static void test_hierarchical(skiatest::Reporter* reporter) {
977 SkBitmap bm;
978 make_bm(&bm, 10, 10, SK_ColorRED, true);
979
980 SkCanvas* canvas;
981
982 SkPicture childPlain;
983 childPlain.beginRecording(10, 10);
984 childPlain.endRecording();
985 REPORTER_ASSERT(reporter, !childPlain.willPlayBackBitmaps()); // 0
986
987 SkPicture childWithBitmap;
988 childWithBitmap.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
989 childWithBitmap.endRecording();
990 REPORTER_ASSERT(reporter, childWithBitmap.willPlayBackBitmaps()); // 1
991
992 SkPicture parentPP;
993 canvas = parentPP.beginRecording(10, 10);
994 canvas->drawPicture(childPlain);
995 parentPP.endRecording();
996 REPORTER_ASSERT(reporter, !parentPP.willPlayBackBitmaps()); // 0
997
998 SkPicture parentPWB;
999 canvas = parentPWB.beginRecording(10, 10);
1000 canvas->drawPicture(childWithBitmap);
1001 parentPWB.endRecording();
1002 REPORTER_ASSERT(reporter, parentPWB.willPlayBackBitmaps()); // 1
1003
1004 SkPicture parentWBP;
1005 canvas = parentWBP.beginRecording(10, 10);
1006 canvas->drawBitmap(bm, 0, 0);
1007 canvas->drawPicture(childPlain);
1008 parentWBP.endRecording();
1009 REPORTER_ASSERT(reporter, parentWBP.willPlayBackBitmaps()); // 1
1010
1011 SkPicture parentWBWB;
1012 canvas = parentWBWB.beginRecording(10, 10);
1013 canvas->drawBitmap(bm, 0, 0);
1014 canvas->drawPicture(childWithBitmap);
1015 parentWBWB.endRecording();
1016 REPORTER_ASSERT(reporter, parentWBWB.willPlayBackBitmaps()); // 2
1017}
1018
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001019DEF_TEST(Picture, reporter) {
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001020#ifdef SK_DEBUG
1021 test_deleting_empty_playback();
1022 test_serializing_empty_picture();
scroggo@google.com4b90b112012-12-04 15:08:56 +00001023#else
1024 test_bad_bitmap();
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001025#endif
sugoi@google.com54f0d1b2013-02-27 19:17:41 +00001026 test_peephole();
reed@google.comfe7b1ed2012-11-29 21:00:39 +00001027 test_gatherpixelrefs(reporter);
robertphillips@google.com56bf6e42014-01-13 13:33:26 +00001028 test_gatherpixelrefsandrects(reporter);
scroggo@google.com7c9d5392012-12-10 15:40:55 +00001029 test_bitmap_with_encoded_data(reporter);
junov@chromium.org94f20dc2013-01-28 21:04:44 +00001030 test_clone_empty(reporter);
junov@chromium.orgd575eed2013-05-08 15:39:13 +00001031 test_clip_bound_opt(reporter);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001032 test_clip_expansion(reporter);
tomhudson@google.com381010e2013-10-24 11:12:47 +00001033 test_hierarchical(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001034}