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 20 static 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 29 typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&, const SkPoint&); 30 31 static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm, 32 const SkPoint& pos) { 33 canvas->drawBitmap(bm, pos.fX, pos.fY, NULL); 34 } 35 36 static 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 45 static 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. 65 static 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 76 static void rand_rect(SkRect* rect, SkRandom& 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. 91 static 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 103 template <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[] 113 static 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. 120 static 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 154 static 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 SkRandom 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. 242 static 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. 253 static 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 262 static void rand_op(SkCanvas* canvas, SkRandom& 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 282 static void test_peephole(skiatest::Reporter* reporter) { 283 SkRandom rand; 284 285 for (int j = 0; j < 100; j++) { 286 SkRandom rand2(rand.getSeed()); // 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 297 { 298 SkPicture picture; 299 SkCanvas* canvas = picture.beginRecording(100, 100); 300 SkRect rect = SkRect::MakeWH(50, 50); 301 302 for (int i = 0; i < 100; ++i) { 303 canvas->save(); 304 } 305 while (canvas->getSaveCount() > 1) { 306 canvas->clipRect(rect); 307 canvas->restore(); 308 } 309 picture.endRecording(); 310 } 311 } 312 313 #ifndef SK_DEBUG 314 // Only test this is in release mode. We deliberately crash in debug mode, since a valid caller 315 // should never do this. 316 static void test_bad_bitmap() { 317 // This bitmap has a width and height but no pixels. As a result, attempting to record it will 318 // fail. 319 SkBitmap bm; 320 bm.setConfig(SkBitmap::kARGB_8888_Config, 100, 100); 321 SkPicture picture; 322 SkCanvas* recordingCanvas = picture.beginRecording(100, 100); 323 recordingCanvas->drawBitmap(bm, 0, 0); 324 picture.endRecording(); 325 326 SkCanvas canvas; 327 canvas.drawPicture(picture); 328 } 329 #endif 330 331 #include "SkData.h" 332 #include "SkImageRef_GlobalPool.h" 333 // Class to test SkPixelRef::onRefEncodedData, since there are currently no implementations in skia. 334 class SkDataImageRef : public SkImageRef_GlobalPool { 335 336 public: 337 SkDataImageRef(SkMemoryStream* stream) 338 : SkImageRef_GlobalPool(stream, SkBitmap::kNo_Config) { 339 SkASSERT(stream != NULL); 340 fData = stream->copyToData(); 341 this->setImmutable(); 342 } 343 344 ~SkDataImageRef() { 345 fData->unref(); 346 } 347 348 virtual SkData* onRefEncodedData() SK_OVERRIDE { 349 fData->ref(); 350 return fData; 351 } 352 353 private: 354 SkData* fData; 355 }; 356 357 #include "SkImageEncoder.h" 358 359 static bool PNGEncodeBitmapToStream(SkWStream* wStream, const SkBitmap& bm) { 360 return SkImageEncoder::EncodeStream(wStream, bm, SkImageEncoder::kPNG_Type, 100); 361 } 362 363 static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) { 364 SkPicture picture; 365 SkCanvas* canvas = picture.beginRecording(bitmap.width(), bitmap.height()); 366 canvas->drawBitmap(bitmap, 0, 0); 367 SkDynamicMemoryWStream wStream; 368 picture.serialize(&wStream, &PNGEncodeBitmapToStream); 369 return wStream.copyToData(); 370 } 371 372 static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) { 373 // Create a bitmap that will be encoded. 374 SkBitmap original; 375 make_bm(&original, 100, 100, SK_ColorBLUE, true); 376 SkDynamicMemoryWStream wStream; 377 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) { 378 return; 379 } 380 SkAutoDataUnref data(wStream.copyToData()); 381 SkMemoryStream memStream; 382 memStream.setData(data); 383 384 // Use the encoded bitmap as the data for an image ref. 385 SkBitmap bm; 386 SkAutoTUnref<SkDataImageRef> imageRef(SkNEW_ARGS(SkDataImageRef, (&memStream))); 387 imageRef->getInfo(&bm); 388 bm.setPixelRef(imageRef); 389 390 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same. 391 // Flattening original will follow the old path of performing an encode, while flattening bm 392 // will use the already encoded data. 393 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original)); 394 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm)); 395 REPORTER_ASSERT(reporter, picture1->equals(picture2)); 396 } 397 398 static void test_clone_empty(skiatest::Reporter* reporter) { 399 // This is a regression test for crbug.com/172062 400 // Before the fix, we used to crash accessing a null pointer when we 401 // had a picture with no paints. This test passes by not crashing. 402 { 403 SkPicture picture; 404 picture.beginRecording(1, 1); 405 picture.endRecording(); 406 SkPicture* destPicture = picture.clone(); 407 REPORTER_ASSERT(reporter, NULL != destPicture); 408 destPicture->unref(); 409 } 410 { 411 // Test without call to endRecording 412 SkPicture picture; 413 picture.beginRecording(1, 1); 414 SkPicture* destPicture = picture.clone(); 415 REPORTER_ASSERT(reporter, NULL != destPicture); 416 destPicture->unref(); 417 } 418 } 419 420 static void TestPicture(skiatest::Reporter* reporter) { 421 #ifdef SK_DEBUG 422 test_deleting_empty_playback(); 423 test_serializing_empty_picture(); 424 #else 425 test_bad_bitmap(); 426 #endif 427 test_peephole(reporter); 428 test_gatherpixelrefs(reporter); 429 test_bitmap_with_encoded_data(reporter); 430 test_clone_empty(reporter); 431 } 432 433 #include "TestClassDef.h" 434 DEFINE_TESTCLASS("Pictures", PictureTestClass, TestPicture) 435