PictureTest.cpp revision c490f801b063a0837501feab3d12b73d71f46312
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 "SkRRect.h" 15#include "SkShader.h" 16#include "SkStream.h" 17 18#include "SkPictureUtils.h" 19 20static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) { 21 bm->setConfig(SkBitmap::kARGB_8888_Config, w, h); 22 bm->allocPixels(); 23 bm->eraseColor(color); 24 if (immutable) { 25 bm->setImmutable(); 26 } 27} 28 29typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&, const SkPoint&); 30 31static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm, 32 const SkPoint& pos) { 33 canvas->drawBitmap(bm, pos.fX, pos.fY, NULL); 34} 35 36static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm, 37 const SkPoint& pos) { 38 SkRect r = { 39 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) 40 }; 41 r.offset(pos.fX, pos.fY); 42 canvas->drawBitmapRectToRect(bm, NULL, r, NULL); 43} 44 45static void drawshader_proc(SkCanvas* canvas, const SkBitmap& bm, 46 const SkPoint& pos) { 47 SkRect r = { 48 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) 49 }; 50 r.offset(pos.fX, pos.fY); 51 52 SkShader* s = SkShader::CreateBitmapShader(bm, 53 SkShader::kClamp_TileMode, 54 SkShader::kClamp_TileMode); 55 SkPaint paint; 56 paint.setShader(s)->unref(); 57 canvas->drawRect(r, paint); 58 canvas->drawOval(r, paint); 59 SkRRect rr; 60 rr.setRectXY(r, 10, 10); 61 canvas->drawRRect(rr, paint); 62} 63 64// Return a picture with the bitmaps drawn at the specified positions. 65static SkPicture* record_bitmaps(const SkBitmap bm[], const SkPoint pos[], 66 int count, DrawBitmapProc proc) { 67 SkPicture* pic = new SkPicture; 68 SkCanvas* canvas = pic->beginRecording(1000, 1000); 69 for (int i = 0; i < count; ++i) { 70 proc(canvas, bm[i], pos[i]); 71 } 72 pic->endRecording(); 73 return pic; 74} 75 76static void rand_rect(SkRect* rect, SkMWCRandom& rand, SkScalar W, SkScalar H) { 77 rect->fLeft = rand.nextRangeScalar(-W, 2*W); 78 rect->fTop = rand.nextRangeScalar(-H, 2*H); 79 rect->fRight = rect->fLeft + rand.nextRangeScalar(0, W); 80 rect->fBottom = rect->fTop + rand.nextRangeScalar(0, H); 81 82 // we integralize rect to make our tests more predictable, since Gather is 83 // a little sloppy. 84 SkIRect ir; 85 rect->round(&ir); 86 rect->set(ir); 87} 88 89// Allocate result to be large enough to hold subset, and then draw the picture 90// into it, offsetting by subset's top/left corner. 91static void draw(SkPicture* pic, const SkRect& subset, SkBitmap* result) { 92 SkIRect ir; 93 subset.roundOut(&ir); 94 int w = ir.width(); 95 int h = ir.height(); 96 make_bm(result, w, h, 0, false); 97 98 SkCanvas canvas(*result); 99 canvas.translate(-SkIntToScalar(ir.left()), -SkIntToScalar(ir.top())); 100 canvas.drawPicture(*pic); 101} 102 103template <typename T> int find_index(const T* array, T elem, int count) { 104 for (int i = 0; i < count; ++i) { 105 if (array[i] == elem) { 106 return i; 107 } 108 } 109 return -1; 110} 111 112// Return true if 'ref' is found in array[] 113static bool find(SkPixelRef const * const * array, SkPixelRef const * ref, int count) { 114 return find_index<const SkPixelRef*>(array, ref, count) >= 0; 115} 116 117// Look at each pixel in bm, and if its color appears in colors[], find the 118// corresponding value in refs[] and append that ref into array, skipping 119// duplicates of the same value. 120static void gather_from_colors(const SkBitmap& bm, SkPixelRef* const refs[], 121 int count, SkTDArray<SkPixelRef*>* array) { 122 // Since we only want to return unique values in array, when we scan we just 123 // set a bit for each index'd color found. In practice we only have a few 124 // distinct colors, so we just use an int's bits as our array. Hence the 125 // assert that count <= number-of-bits-in-our-int. 126 SkASSERT((unsigned)count <= 32); 127 uint32_t bitarray = 0; 128 129 SkAutoLockPixels alp(bm); 130 131 for (int y = 0; y < bm.height(); ++y) { 132 for (int x = 0; x < bm.width(); ++x) { 133 SkPMColor pmc = *bm.getAddr32(x, y); 134 // the only good case where the color is not found would be if 135 // the color is transparent, meaning no bitmap was drawn in that 136 // pixel. 137 if (pmc) { 138 uint32_t index = SkGetPackedR32(pmc); 139 SkASSERT(SkGetPackedG32(pmc) == index); 140 SkASSERT(SkGetPackedB32(pmc) == index); 141 SkASSERT(static_cast<int>(index) < count); 142 bitarray |= 1 << index; 143 } 144 } 145 } 146 147 for (int i = 0; i < count; ++i) { 148 if (bitarray & (1 << i)) { 149 *array->append() = refs[i]; 150 } 151 } 152} 153 154static void test_gatherpixelrefs(skiatest::Reporter* reporter) { 155 const int IW = 8; 156 const int IH = IW; 157 const SkScalar W = SkIntToScalar(IW); 158 const SkScalar H = W; 159 160 static const int N = 4; 161 SkBitmap bm[N]; 162 SkPixelRef* refs[N]; 163 164 const SkPoint pos[] = { 165 { 0, 0 }, { W, 0 }, { 0, H }, { W, H } 166 }; 167 168 // Our convention is that the color components contain the index of their 169 // corresponding bitmap/pixelref 170 for (int i = 0; i < N; ++i) { 171 make_bm(&bm[i], IW, IH, SkColorSetARGB(0xFF, i, i, i), true); 172 refs[i] = bm[i].pixelRef(); 173 } 174 175 static const DrawBitmapProc procs[] = { 176 drawbitmap_proc, drawbitmaprect_proc, drawshader_proc 177 }; 178 179 SkMWCRandom rand; 180 for (size_t k = 0; k < SK_ARRAY_COUNT(procs); ++k) { 181 SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, N, procs[k])); 182 183 // quick check for a small piece of each quadrant, which should just 184 // contain 1 bitmap. 185 for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) { 186 SkRect r; 187 r.set(2, 2, W - 2, H - 2); 188 r.offset(pos[i].fX, pos[i].fY); 189 SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r)); 190 REPORTER_ASSERT(reporter, data); 191 int count = data->size() / sizeof(SkPixelRef*); 192 REPORTER_ASSERT(reporter, 1 == count); 193 REPORTER_ASSERT(reporter, *(SkPixelRef**)data->data() == refs[i]); 194 } 195 196 // Test a bunch of random (mostly) rects, and compare the gather results 197 // with a deduced list of refs by looking at the colors drawn. 198 for (int j = 0; j < 100; ++j) { 199 SkRect r; 200 rand_rect(&r, rand, 2*W, 2*H); 201 202 SkBitmap result; 203 draw(pic, r, &result); 204 SkTDArray<SkPixelRef*> array; 205 206 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r); 207 size_t dataSize = data ? data->size() : 0; 208 int gatherCount = dataSize / sizeof(SkPixelRef*); 209 SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize); 210 SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL; 211 SkAutoDataUnref adu(data); 212 213 gather_from_colors(result, refs, N, &array); 214 215 /* 216 * GatherPixelRefs is conservative, so it can return more bitmaps 217 * that we actually can see (usually because of conservative bounds 218 * inflation for antialiasing). Thus our check here is only that 219 * Gather didn't miss any that we actually saw. Even that isn't 220 * a strict requirement on Gather, which is meant to be quick and 221 * only mostly-correct, but at the moment this test should work. 222 */ 223 for (int i = 0; i < array.count(); ++i) { 224 bool found = find(gatherRefs, array[i], gatherCount); 225 REPORTER_ASSERT(reporter, found); 226#if 0 227 // enable this block of code to debug failures, as it will rerun 228 // the case that failed. 229 if (!found) { 230 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r); 231 size_t dataSize = data ? data->size() : 0; 232 } 233#endif 234 } 235 } 236 } 237} 238 239#ifdef SK_DEBUG 240// Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only 241// run in debug mode. 242static void test_deleting_empty_playback() { 243 SkPicture picture; 244 // Creates an SkPictureRecord 245 picture.beginRecording(0, 0); 246 // Turns that into an SkPicturePlayback 247 picture.endRecording(); 248 // Deletes the old SkPicturePlayback, and creates a new SkPictureRecord 249 picture.beginRecording(0, 0); 250} 251 252// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode. 253static void test_serializing_empty_picture() { 254 SkPicture picture; 255 picture.beginRecording(0, 0); 256 picture.endRecording(); 257 SkDynamicMemoryWStream stream; 258 picture.serialize(&stream); 259} 260#endif 261 262static void rand_op(SkCanvas* canvas, SkMWCRandom& rand) { 263 SkPaint paint; 264 SkRect rect = SkRect::MakeWH(50, 50); 265 266 SkScalar unit = rand.nextUScalar1(); 267 if (unit <= 0.3) { 268// SkDebugf("save\n"); 269 canvas->save(); 270 } else if (unit <= 0.6) { 271// SkDebugf("restore\n"); 272 canvas->restore(); 273 } else if (unit <= 0.9) { 274// SkDebugf("clip\n"); 275 canvas->clipRect(rect); 276 } else { 277// SkDebugf("draw\n"); 278 canvas->drawPaint(paint); 279 } 280} 281 282static void test_peephole() { 283 SkMWCRandom rand; 284 285 for (int j = 0; j < 100; j++) { 286 SkMWCRandom rand2(rand); // remember the seed 287 288 SkPicture picture; 289 SkCanvas* canvas = picture.beginRecording(100, 100); 290 291 for (int i = 0; i < 1000; ++i) { 292 rand_op(canvas, rand); 293 } 294 picture.endRecording(); 295 296 rand = rand2; 297 } 298 299 { 300 SkPicture picture; 301 SkCanvas* canvas = picture.beginRecording(100, 100); 302 SkRect rect = SkRect::MakeWH(50, 50); 303 304 for (int i = 0; i < 100; ++i) { 305 canvas->save(); 306 } 307 while (canvas->getSaveCount() > 1) { 308 canvas->clipRect(rect); 309 canvas->restore(); 310 } 311 picture.endRecording(); 312 } 313} 314 315#ifndef SK_DEBUG 316// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller 317// should never do this. 318static void test_bad_bitmap() { 319 // This bitmap has a width and height but no pixels. As a result, attempting to record it will 320 // fail. 321 SkBitmap bm; 322 bm.setConfig(SkBitmap::kARGB_8888_Config, 100, 100); 323 SkPicture picture; 324 SkCanvas* recordingCanvas = picture.beginRecording(100, 100); 325 recordingCanvas->drawBitmap(bm, 0, 0); 326 picture.endRecording(); 327 328 SkCanvas canvas; 329 canvas.drawPicture(picture); 330} 331#endif 332 333#include "SkData.h" 334#include "SkImageRef_GlobalPool.h" 335// Class to test SkPixelRef::onRefEncodedData, since there are currently no implementations in skia. 336class SkDataImageRef : public SkImageRef_GlobalPool { 337 338public: 339 SkDataImageRef(SkMemoryStream* stream) 340 : SkImageRef_GlobalPool(stream, SkBitmap::kNo_Config) { 341 SkASSERT(stream != NULL); 342 fData = stream->copyToData(); 343 this->setImmutable(); 344 } 345 346 ~SkDataImageRef() { 347 fData->unref(); 348 } 349 350 virtual SkData* onRefEncodedData() SK_OVERRIDE { 351 fData->ref(); 352 return fData; 353 } 354 355private: 356 SkData* fData; 357}; 358 359#include "SkImageEncoder.h" 360 361static bool PNGEncodeBitmapToStream(SkWStream* wStream, const SkBitmap& bm) { 362 return SkImageEncoder::EncodeStream(wStream, bm, SkImageEncoder::kPNG_Type, 100); 363} 364 365static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) { 366 SkPicture picture; 367 SkCanvas* canvas = picture.beginRecording(bitmap.width(), bitmap.height()); 368 canvas->drawBitmap(bitmap, 0, 0); 369 SkDynamicMemoryWStream wStream; 370 picture.serialize(&wStream, &PNGEncodeBitmapToStream); 371 return wStream.copyToData(); 372} 373 374static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) { 375 // Create a bitmap that will be encoded. 376 SkBitmap original; 377 make_bm(&original, 100, 100, SK_ColorBLUE, true); 378 SkDynamicMemoryWStream wStream; 379 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) { 380 return; 381 } 382 SkAutoDataUnref data(wStream.copyToData()); 383 SkMemoryStream memStream; 384 memStream.setData(data); 385 386 // Use the encoded bitmap as the data for an image ref. 387 SkBitmap bm; 388 SkAutoTUnref<SkDataImageRef> imageRef(SkNEW_ARGS(SkDataImageRef, (&memStream))); 389 imageRef->getInfo(&bm); 390 bm.setPixelRef(imageRef); 391 392 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same. 393 // Flattening original will follow the old path of performing an encode, while flattening bm 394 // will use the already encoded data. 395 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original)); 396 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm)); 397 REPORTER_ASSERT(reporter, picture1->equals(picture2)); 398} 399 400static void test_clone_empty(skiatest::Reporter* reporter) { 401 // This is a regression test for crbug.com/172062 402 // Before the fix, we used to crash accessing a null pointer when we 403 // had a picture with no paints. This test passes by not crashing. 404 { 405 SkPicture picture; 406 picture.beginRecording(1, 1); 407 picture.endRecording(); 408 SkPicture* destPicture = picture.clone(); 409 REPORTER_ASSERT(reporter, NULL != destPicture); 410 destPicture->unref(); 411 } 412 { 413 // Test without call to endRecording 414 SkPicture picture; 415 picture.beginRecording(1, 1); 416 SkPicture* destPicture = picture.clone(); 417 REPORTER_ASSERT(reporter, NULL != destPicture); 418 destPicture->unref(); 419 } 420} 421 422static void TestPicture(skiatest::Reporter* reporter) { 423#ifdef SK_DEBUG 424 test_deleting_empty_playback(); 425 test_serializing_empty_picture(); 426#else 427 test_bad_bitmap(); 428#endif 429 test_peephole(); 430 test_gatherpixelrefs(reporter); 431 test_bitmap_with_encoded_data(reporter); 432 test_clone_empty(reporter); 433} 434 435#include "TestClassDef.h" 436DEFINE_TESTCLASS("Pictures", PictureTestClass, TestPicture) 437