PictureTest.cpp revision c3d7d90973528527131c72549b10c2a21300e0ac
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 SkPMColor pmcolors[N]; 158 SkPixelRef* refs[N]; 159 160 const SkPoint pos[] = { 161 { 0, 0 }, { W, 0 }, { 0, H }, { W, H } 162 }; 163 164 // Our convention is that the color components contain the index of their 165 // corresponding bitmap/pixelref 166 for (int i = 0; i < N; ++i) { 167 make_bm(&bm[i], IW, IH, SkColorSetARGB(0xFF, i, i, i), true); 168 refs[i] = bm[i].pixelRef(); 169 } 170 171 static const DrawBitmapProc procs[] = { 172 drawbitmap_proc, drawbitmaprect_proc, drawshader_proc 173 }; 174 175 SkRandom rand; 176 for (size_t k = 0; k < SK_ARRAY_COUNT(procs); ++k) { 177 SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, N, procs[k])); 178 179 // quick check for a small piece of each quadrant, which should just 180 // contain 1 bitmap. 181 for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) { 182 SkRect r; 183 r.set(2, 2, W - 2, H - 2); 184 r.offset(pos[i].fX, pos[i].fY); 185 SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r)); 186 REPORTER_ASSERT(reporter, data); 187 int count = data->size() / sizeof(SkPixelRef*); 188 REPORTER_ASSERT(reporter, 1 == count); 189 REPORTER_ASSERT(reporter, *(SkPixelRef**)data->data() == refs[i]); 190 } 191 192 // Test a bunch of random (mostly) rects, and compare the gather results 193 // with a deduced list of refs by looking at the colors drawn. 194 for (int j = 0; j < 100; ++j) { 195 SkRect r; 196 rand_rect(&r, rand, 2*W, 2*H); 197 198 SkBitmap result; 199 draw(pic, r, &result); 200 SkTDArray<SkPixelRef*> array; 201 202 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r); 203 size_t dataSize = data ? data->size() : 0; 204 int gatherCount = dataSize / sizeof(SkPixelRef*); 205 SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize); 206 SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL; 207 SkAutoDataUnref adu(data); 208 209 gather_from_colors(result, refs, N, &array); 210 211 /* 212 * GatherPixelRefs is conservative, so it can return more bitmaps 213 * that we actually can see (usually because of conservative bounds 214 * inflation for antialiasing). Thus our check here is only that 215 * Gather didn't miss any that we actually saw. Even that isn't 216 * a strict requirement on Gather, which is meant to be quick and 217 * only mostly-correct, but at the moment this test should work. 218 */ 219 for (int i = 0; i < array.count(); ++i) { 220 bool found = find(gatherRefs, array[i], gatherCount); 221 REPORTER_ASSERT(reporter, found); 222#if 0 223 // enable this block of code to debug failures, as it will rerun 224 // the case that failed. 225 if (!found) { 226 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r); 227 size_t dataSize = data ? data->size() : 0; 228 } 229#endif 230 } 231 } 232 } 233} 234 235#ifdef SK_DEBUG 236// Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only 237// run in debug mode. 238static void test_deleting_empty_playback() { 239 SkPicture picture; 240 // Creates an SkPictureRecord 241 picture.beginRecording(0, 0); 242 // Turns that into an SkPicturePlayback 243 picture.endRecording(); 244 // Deletes the old SkPicturePlayback, and creates a new SkPictureRecord 245 picture.beginRecording(0, 0); 246} 247 248// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode. 249static void test_serializing_empty_picture() { 250 SkPicture picture; 251 picture.beginRecording(0, 0); 252 picture.endRecording(); 253 SkDynamicMemoryWStream stream; 254 picture.serialize(&stream); 255} 256#endif 257 258static void rand_op(SkCanvas* canvas, SkRandom& rand) { 259 SkPaint paint; 260 SkRect rect = SkRect::MakeWH(50, 50); 261 262 SkScalar unit = rand.nextUScalar1(); 263 if (unit <= 0.3) { 264// SkDebugf("save\n"); 265 canvas->save(); 266 } else if (unit <= 0.6) { 267// SkDebugf("restore\n"); 268 canvas->restore(); 269 } else if (unit <= 0.9) { 270// SkDebugf("clip\n"); 271 canvas->clipRect(rect); 272 } else { 273// SkDebugf("draw\n"); 274 canvas->drawPaint(paint); 275 } 276} 277 278static void test_peephole(skiatest::Reporter* reporter) { 279 SkRandom rand; 280 281 for (int j = 0; j < 100; j++) { 282 SkRandom rand2(rand.getSeed()); // remember the seed 283 284 SkPicture picture; 285 SkCanvas* canvas = picture.beginRecording(100, 100); 286 287 for (int i = 0; i < 1000; ++i) { 288 rand_op(canvas, rand); 289 } 290 picture.endRecording(); 291 } 292 293 { 294 SkPicture picture; 295 SkCanvas* canvas = picture.beginRecording(100, 100); 296 SkRect rect = SkRect::MakeWH(50, 50); 297 298 for (int i = 0; i < 100; ++i) { 299 canvas->save(); 300 } 301 while (canvas->getSaveCount() > 1) { 302 canvas->clipRect(rect); 303 canvas->restore(); 304 } 305 picture.endRecording(); 306 } 307} 308 309static void TestPicture(skiatest::Reporter* reporter) { 310#ifdef SK_DEBUG 311 test_deleting_empty_playback(); 312 test_serializing_empty_picture(); 313#endif 314 test_peephole(reporter); 315 test_gatherpixelrefs(reporter); 316} 317 318#include "TestClassDef.h" 319DEFINE_TESTCLASS("Pictures", PictureTestClass, TestPicture) 320