PictureTest.cpp revision 4b90b1122c93e6600ea352f4ccf1dfc54c8bb146
1/* 2 * Copyright 2012 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7#include "Test.h" 8#include "SkCanvas.h" 9#include "SkColorPriv.h" 10#include "SkData.h" 11#include "SkPaint.h" 12#include "SkPicture.h" 13#include "SkRandom.h" 14#include "SkShader.h" 15#include "SkStream.h" 16 17#include "SkPictureUtils.h" 18 19static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) { 20 bm->setConfig(SkBitmap::kARGB_8888_Config, w, h); 21 bm->allocPixels(); 22 bm->eraseColor(color); 23 if (immutable) { 24 bm->setImmutable(); 25 } 26} 27 28typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&, const SkPoint&); 29 30static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm, 31 const SkPoint& pos) { 32 canvas->drawBitmap(bm, pos.fX, pos.fY, NULL); 33} 34 35static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm, 36 const SkPoint& pos) { 37 SkRect r = { 38 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) 39 }; 40 r.offset(pos.fX, pos.fY); 41 canvas->drawBitmapRectToRect(bm, NULL, r, NULL); 42} 43 44static void drawshader_proc(SkCanvas* canvas, const SkBitmap& bm, 45 const SkPoint& pos) { 46 SkRect r = { 47 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) 48 }; 49 r.offset(pos.fX, pos.fY); 50 51 SkShader* s = SkShader::CreateBitmapShader(bm, 52 SkShader::kClamp_TileMode, 53 SkShader::kClamp_TileMode); 54 SkPaint paint; 55 paint.setShader(s)->unref(); 56 canvas->drawRect(r, paint); 57} 58 59// Return a picture with the bitmaps drawn at the specified positions. 60static SkPicture* record_bitmaps(const SkBitmap bm[], const SkPoint pos[], 61 int count, DrawBitmapProc proc) { 62 SkPicture* pic = new SkPicture; 63 SkCanvas* canvas = pic->beginRecording(1000, 1000); 64 for (int i = 0; i < count; ++i) { 65 proc(canvas, bm[i], pos[i]); 66 } 67 pic->endRecording(); 68 return pic; 69} 70 71static void rand_rect(SkRect* rect, SkRandom& rand, SkScalar W, SkScalar H) { 72 rect->fLeft = rand.nextRangeScalar(-W, 2*W); 73 rect->fTop = rand.nextRangeScalar(-H, 2*H); 74 rect->fRight = rect->fLeft + rand.nextRangeScalar(0, W); 75 rect->fBottom = rect->fTop + rand.nextRangeScalar(0, H); 76 77 // we integralize rect to make our tests more predictable, since Gather is 78 // a little sloppy. 79 SkIRect ir; 80 rect->round(&ir); 81 rect->set(ir); 82} 83 84// Allocate result to be large enough to hold subset, and then draw the picture 85// into it, offsetting by subset's top/left corner. 86static void draw(SkPicture* pic, const SkRect& subset, SkBitmap* result) { 87 SkIRect ir; 88 subset.roundOut(&ir); 89 int w = ir.width(); 90 int h = ir.height(); 91 make_bm(result, w, h, 0, false); 92 93 SkCanvas canvas(*result); 94 canvas.translate(-SkIntToScalar(ir.left()), -SkIntToScalar(ir.top())); 95 canvas.drawPicture(*pic); 96} 97 98template <typename T> int find_index(const T* array, T elem, int count) { 99 for (int i = 0; i < count; ++i) { 100 if (array[i] == elem) { 101 return i; 102 } 103 } 104 return -1; 105} 106 107// Return true if 'ref' is found in array[] 108static bool find(SkPixelRef const * const * array, SkPixelRef const * ref, int count) { 109 return find_index<const SkPixelRef*>(array, ref, count) >= 0; 110} 111 112// Look at each pixel in bm, and if its color appears in colors[], find the 113// corresponding value in refs[] and append that ref into array, skipping 114// duplicates of the same value. 115static void gather_from_colors(const SkBitmap& bm, SkPixelRef* const refs[], 116 int count, SkTDArray<SkPixelRef*>* array) { 117 // Since we only want to return unique values in array, when we scan we just 118 // set a bit for each index'd color found. In practice we only have a few 119 // distinct colors, so we just use an int's bits as our array. Hence the 120 // assert that count <= number-of-bits-in-our-int. 121 SkASSERT((unsigned)count <= 32); 122 uint32_t bitarray = 0; 123 124 SkAutoLockPixels alp(bm); 125 126 for (int y = 0; y < bm.height(); ++y) { 127 for (int x = 0; x < bm.width(); ++x) { 128 SkPMColor pmc = *bm.getAddr32(x, y); 129 // the only good case where the color is not found would be if 130 // the color is transparent, meaning no bitmap was drawn in that 131 // pixel. 132 if (pmc) { 133 int index = SkGetPackedR32(pmc); 134 SkASSERT(SkGetPackedG32(pmc) == index); 135 SkASSERT(SkGetPackedB32(pmc) == index); 136 SkASSERT(index < count); 137 bitarray |= 1 << index; 138 } 139 } 140 } 141 142 for (int i = 0; i < count; ++i) { 143 if (bitarray & (1 << i)) { 144 *array->append() = refs[i]; 145 } 146 } 147} 148 149static void test_gatherpixelrefs(skiatest::Reporter* reporter) { 150 const int IW = 8; 151 const int IH = IW; 152 const SkScalar W = SkIntToScalar(IW); 153 const SkScalar H = W; 154 155 static const int N = 4; 156 SkBitmap bm[N]; 157 SkPixelRef* refs[N]; 158 159 const SkPoint pos[] = { 160 { 0, 0 }, { W, 0 }, { 0, H }, { W, H } 161 }; 162 163 // Our convention is that the color components contain the index of their 164 // corresponding bitmap/pixelref 165 for (int i = 0; i < N; ++i) { 166 make_bm(&bm[i], IW, IH, SkColorSetARGB(0xFF, i, i, i), true); 167 refs[i] = bm[i].pixelRef(); 168 } 169 170 static const DrawBitmapProc procs[] = { 171 drawbitmap_proc, drawbitmaprect_proc, drawshader_proc 172 }; 173 174 SkRandom rand; 175 for (size_t k = 0; k < SK_ARRAY_COUNT(procs); ++k) { 176 SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, N, procs[k])); 177 178 // quick check for a small piece of each quadrant, which should just 179 // contain 1 bitmap. 180 for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) { 181 SkRect r; 182 r.set(2, 2, W - 2, H - 2); 183 r.offset(pos[i].fX, pos[i].fY); 184 SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r)); 185 REPORTER_ASSERT(reporter, data); 186 int count = data->size() / sizeof(SkPixelRef*); 187 REPORTER_ASSERT(reporter, 1 == count); 188 REPORTER_ASSERT(reporter, *(SkPixelRef**)data->data() == refs[i]); 189 } 190 191 // Test a bunch of random (mostly) rects, and compare the gather results 192 // with a deduced list of refs by looking at the colors drawn. 193 for (int j = 0; j < 100; ++j) { 194 SkRect r; 195 rand_rect(&r, rand, 2*W, 2*H); 196 197 SkBitmap result; 198 draw(pic, r, &result); 199 SkTDArray<SkPixelRef*> array; 200 201 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r); 202 size_t dataSize = data ? data->size() : 0; 203 int gatherCount = dataSize / sizeof(SkPixelRef*); 204 SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize); 205 SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL; 206 SkAutoDataUnref adu(data); 207 208 gather_from_colors(result, refs, N, &array); 209 210 /* 211 * GatherPixelRefs is conservative, so it can return more bitmaps 212 * that we actually can see (usually because of conservative bounds 213 * inflation for antialiasing). Thus our check here is only that 214 * Gather didn't miss any that we actually saw. Even that isn't 215 * a strict requirement on Gather, which is meant to be quick and 216 * only mostly-correct, but at the moment this test should work. 217 */ 218 for (int i = 0; i < array.count(); ++i) { 219 bool found = find(gatherRefs, array[i], gatherCount); 220 REPORTER_ASSERT(reporter, found); 221#if 0 222 // enable this block of code to debug failures, as it will rerun 223 // the case that failed. 224 if (!found) { 225 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r); 226 size_t dataSize = data ? data->size() : 0; 227 } 228#endif 229 } 230 } 231 } 232} 233 234#ifdef SK_DEBUG 235// Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only 236// run in debug mode. 237static void test_deleting_empty_playback() { 238 SkPicture picture; 239 // Creates an SkPictureRecord 240 picture.beginRecording(0, 0); 241 // Turns that into an SkPicturePlayback 242 picture.endRecording(); 243 // Deletes the old SkPicturePlayback, and creates a new SkPictureRecord 244 picture.beginRecording(0, 0); 245} 246 247// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode. 248static void test_serializing_empty_picture() { 249 SkPicture picture; 250 picture.beginRecording(0, 0); 251 picture.endRecording(); 252 SkDynamicMemoryWStream stream; 253 picture.serialize(&stream); 254} 255#endif 256 257static void rand_op(SkCanvas* canvas, SkRandom& rand) { 258 SkPaint paint; 259 SkRect rect = SkRect::MakeWH(50, 50); 260 261 SkScalar unit = rand.nextUScalar1(); 262 if (unit <= 0.3) { 263// SkDebugf("save\n"); 264 canvas->save(); 265 } else if (unit <= 0.6) { 266// SkDebugf("restore\n"); 267 canvas->restore(); 268 } else if (unit <= 0.9) { 269// SkDebugf("clip\n"); 270 canvas->clipRect(rect); 271 } else { 272// SkDebugf("draw\n"); 273 canvas->drawPaint(paint); 274 } 275} 276 277static void test_peephole(skiatest::Reporter* reporter) { 278 SkRandom rand; 279 280 for (int j = 0; j < 100; j++) { 281 SkRandom rand2(rand.getSeed()); // remember the seed 282 283 SkPicture picture; 284 SkCanvas* canvas = picture.beginRecording(100, 100); 285 286 for (int i = 0; i < 1000; ++i) { 287 rand_op(canvas, rand); 288 } 289 picture.endRecording(); 290 } 291 292 { 293 SkPicture picture; 294 SkCanvas* canvas = picture.beginRecording(100, 100); 295 SkRect rect = SkRect::MakeWH(50, 50); 296 297 for (int i = 0; i < 100; ++i) { 298 canvas->save(); 299 } 300 while (canvas->getSaveCount() > 1) { 301 canvas->clipRect(rect); 302 canvas->restore(); 303 } 304 picture.endRecording(); 305 } 306} 307 308#ifndef SK_DEBUG 309// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller 310// should never do this. 311static void test_bad_bitmap() { 312 // This bitmap has a width and height but no pixels. As a result, attempting to record it will 313 // fail. 314 SkBitmap bm; 315 bm.setConfig(SkBitmap::kARGB_8888_Config, 100, 100); 316 SkPicture picture; 317 SkCanvas* recordingCanvas = picture.beginRecording(100, 100); 318 recordingCanvas->drawBitmap(bm, 0, 0); 319 picture.endRecording(); 320 321 SkCanvas canvas; 322 canvas.drawPicture(picture); 323} 324#endif 325 326static void TestPicture(skiatest::Reporter* reporter) { 327#ifdef SK_DEBUG 328 test_deleting_empty_playback(); 329 test_serializing_empty_picture(); 330#else 331 test_bad_bitmap(); 332#endif 333 test_peephole(reporter); 334 test_gatherpixelrefs(reporter); 335} 336 337#include "TestClassDef.h" 338DEFINE_TESTCLASS("Pictures", PictureTestClass, TestPicture) 339