1 /* 2 * Copyright 2011 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 8 #include "SkBitmap.h" 9 #include "SkCanvas.h" 10 #include "SkData.h" 11 #include "SkDiscardableMemoryPool.h" 12 #include "SkImageGeneratorPriv.h" 13 #include "SkMatrixUtils.h" 14 #include "SkPaint.h" 15 #include "SkRandom.h" 16 #include "SkShader.h" 17 #include "SkSurface.h" 18 #include "Test.h" 19 20 // A BitmapFactory that always fails when asked to return pixels. 21 class FailureImageGenerator : public SkImageGenerator { 22 public: 23 FailureImageGenerator() : SkImageGenerator(SkImageInfo::MakeN32Premul(100, 100)) {} 24 protected: 25 // default onGetPixels() returns kUnimplemented, which is what we want. 26 }; 27 28 // crbug.com/295895 29 // Crashing in skia when a pixelref fails in lockPixels 30 // 31 static void test_faulty_pixelref(skiatest::Reporter* reporter) { 32 // need a cache, but don't expect to use it, so the budget is not critical 33 SkAutoTUnref<SkDiscardableMemoryPool> pool( 34 SkDiscardableMemoryPool::Create(10 * 1000, NULL)); 35 SkBitmap bm; 36 bool installSuccess = SkInstallDiscardablePixelRef(SkNEW(FailureImageGenerator), &bm, pool); 37 REPORTER_ASSERT(reporter, installSuccess); 38 // now our bitmap has a pixelref, but we know it will fail to lock 39 40 SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(200, 200)); 41 SkCanvas* canvas = surface->getCanvas(); 42 43 const SkFilterQuality levels[] = { 44 kNone_SkFilterQuality, 45 kLow_SkFilterQuality, 46 kMedium_SkFilterQuality, 47 kHigh_SkFilterQuality, 48 }; 49 50 SkPaint paint; 51 canvas->scale(2, 2); // need a scale, otherwise we may ignore filtering 52 for (size_t i = 0; i < SK_ARRAY_COUNT(levels); ++i) { 53 paint.setFilterQuality(levels[i]); 54 canvas->drawBitmap(bm, 0, 0, &paint); 55 } 56 } 57 58 /////////////////////////////////////////////////////////////////////////////// 59 60 static void rand_matrix(SkMatrix* mat, SkRandom& rand, unsigned mask) { 61 mat->setIdentity(); 62 if (mask & SkMatrix::kTranslate_Mask) { 63 mat->postTranslate(rand.nextSScalar1(), rand.nextSScalar1()); 64 } 65 if (mask & SkMatrix::kScale_Mask) { 66 mat->postScale(rand.nextSScalar1(), rand.nextSScalar1()); 67 } 68 if (mask & SkMatrix::kAffine_Mask) { 69 mat->postRotate(rand.nextSScalar1() * 360); 70 } 71 if (mask & SkMatrix::kPerspective_Mask) { 72 mat->setPerspX(rand.nextSScalar1()); 73 mat->setPerspY(rand.nextSScalar1()); 74 } 75 } 76 77 static void rand_size(SkISize* size, SkRandom& rand) { 78 size->set(rand.nextU() & 0xFFFF, rand.nextU() & 0xFFFF); 79 } 80 81 static bool treat_as_sprite(const SkMatrix& mat, const SkISize& size, 82 unsigned bits) { 83 return SkTreatAsSprite(mat, size.width(), size.height(), bits); 84 } 85 86 static void test_treatAsSprite(skiatest::Reporter* reporter) { 87 const unsigned bilerBits = kSkSubPixelBitsForBilerp; 88 89 SkMatrix mat; 90 SkISize size; 91 SkRandom rand; 92 93 // assert: translate-only no-filter can always be treated as sprite 94 for (int i = 0; i < 1000; ++i) { 95 rand_matrix(&mat, rand, SkMatrix::kTranslate_Mask); 96 for (int j = 0; j < 1000; ++j) { 97 rand_size(&size, rand); 98 REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, 0)); 99 } 100 } 101 102 // assert: rotate/perspect is never treated as sprite 103 for (int i = 0; i < 1000; ++i) { 104 rand_matrix(&mat, rand, SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask); 105 for (int j = 0; j < 1000; ++j) { 106 rand_size(&size, rand); 107 REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, 0)); 108 REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits)); 109 } 110 } 111 112 size.set(500, 600); 113 114 const SkScalar tooMuchSubpixel = 100.1f; 115 mat.setTranslate(tooMuchSubpixel, 0); 116 REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits)); 117 mat.setTranslate(0, tooMuchSubpixel); 118 REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits)); 119 120 const SkScalar tinySubPixel = 100.02f; 121 mat.setTranslate(tinySubPixel, 0); 122 REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, bilerBits)); 123 mat.setTranslate(0, tinySubPixel); 124 REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, bilerBits)); 125 126 const SkScalar twoThirds = SK_Scalar1 * 2 / 3; 127 const SkScalar bigScale = (size.width() + twoThirds) / size.width(); 128 mat.setScale(bigScale, bigScale); 129 REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, false)); 130 REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits)); 131 132 const SkScalar oneThird = SK_Scalar1 / 3; 133 const SkScalar smallScale = (size.width() + oneThird) / size.width(); 134 mat.setScale(smallScale, smallScale); 135 REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, false)); 136 REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits)); 137 138 const SkScalar oneFortyth = SK_Scalar1 / 40; 139 const SkScalar tinyScale = (size.width() + oneFortyth) / size.width(); 140 mat.setScale(tinyScale, tinyScale); 141 REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, false)); 142 REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, bilerBits)); 143 } 144 145 static void assert_ifDrawnTo(skiatest::Reporter* reporter, 146 const SkBitmap& bm, bool shouldBeDrawn) { 147 for (int y = 0; y < bm.height(); ++y) { 148 for (int x = 0; x < bm.width(); ++x) { 149 if (shouldBeDrawn) { 150 if (SK_ColorTRANSPARENT == *bm.getAddr32(x, y)) { 151 REPORTER_ASSERT(reporter, false); 152 return; 153 } 154 } else { 155 // should not be drawn 156 if (SK_ColorTRANSPARENT != *bm.getAddr32(x, y)) { 157 REPORTER_ASSERT(reporter, false); 158 return; 159 } 160 } 161 } 162 } 163 } 164 165 static void test_wacky_bitmapshader(skiatest::Reporter* reporter, 166 int width, int height, bool shouldBeDrawn) { 167 SkBitmap dev; 168 dev.allocN32Pixels(0x56F, 0x4f6); 169 dev.eraseColor(SK_ColorTRANSPARENT); // necessary, so we know if we draw to it 170 171 SkMatrix matrix; 172 173 SkCanvas c(dev); 174 matrix.setAll(-119.34097f, 175 -43.436558f, 176 93489.945f, 177 43.436558f, 178 -119.34097f, 179 123.98426f, 180 0, 0, SK_Scalar1); 181 c.concat(matrix); 182 183 SkBitmap bm; 184 if (bm.tryAllocN32Pixels(width, height)) { 185 // allow this to fail silently, to test the code downstream 186 } 187 bm.eraseColor(SK_ColorRED); 188 189 matrix.setAll(0.0078740157f, 190 0, 191 SkIntToScalar(249), 192 0, 193 0.0078740157f, 194 SkIntToScalar(239), 195 0, 0, SK_Scalar1); 196 SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode, 197 SkShader::kRepeat_TileMode, &matrix); 198 199 SkPaint paint; 200 paint.setShader(s)->unref(); 201 202 SkRect r = SkRect::MakeXYWH(681, 239, 695, 253); 203 c.drawRect(r, paint); 204 205 assert_ifDrawnTo(reporter, dev, shouldBeDrawn); 206 } 207 208 /* 209 * Original bug was asserting that the matrix-proc had generated a (Y) value 210 * that was out of range. This led (in the release build) to the sampler-proc 211 * reading memory out-of-bounds of the original bitmap. 212 * 213 * We were numerically overflowing our 16bit coordinates that we communicate 214 * between these two procs. The fixes was in two parts: 215 * 216 * 1. Just don't draw bitmaps larger than 64K-1 in width or height, since we 217 * can't represent those coordinates in our transport format (yet). 218 * 2. Perform an unsigned shift during the calculation, so we don't get 219 * sign-extension bleed when packing the two values (X,Y) into our 32bit 220 * slot. 221 * 222 * This tests exercises the original setup, plus 3 more to ensure that we can, 223 * in fact, handle bitmaps at 64K-1 (assuming we don't exceed the total 224 * memory allocation limit). 225 */ 226 static void test_giantrepeat_crbug118018(skiatest::Reporter* reporter) { 227 static const struct { 228 int fWidth; 229 int fHeight; 230 bool fExpectedToDraw; 231 } gTests[] = { 232 { 0x1b294, 0x7f, false }, // crbug 118018 (width exceeds 64K) 233 { 0xFFFF, 0x7f, true }, // should draw, test max width 234 { 0x7f, 0xFFFF, true }, // should draw, test max height 235 { 0xFFFF, 0xFFFF, false }, // allocation fails (too much RAM) 236 }; 237 238 for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) { 239 test_wacky_bitmapshader(reporter, 240 gTests[i].fWidth, gTests[i].fHeight, 241 gTests[i].fExpectedToDraw); 242 } 243 } 244 245 /////////////////////////////////////////////////////////////////////////////// 246 247 static void test_nan_antihair() { 248 SkBitmap bm; 249 bm.allocN32Pixels(20, 20); 250 251 SkCanvas canvas(bm); 252 253 SkPath path; 254 path.moveTo(0, 0); 255 path.lineTo(10, SK_ScalarNaN); 256 257 SkPaint paint; 258 paint.setAntiAlias(true); 259 paint.setStyle(SkPaint::kStroke_Style); 260 261 // before our fix to SkScan_Antihair.cpp to check for integral NaN (0x800...) 262 // this would trigger an assert/crash. 263 // 264 // see rev. 3558 265 canvas.drawPath(path, paint); 266 } 267 268 static bool check_for_all_zeros(const SkBitmap& bm) { 269 SkAutoLockPixels alp(bm); 270 271 size_t count = bm.width() * bm.bytesPerPixel(); 272 for (int y = 0; y < bm.height(); y++) { 273 const uint8_t* ptr = reinterpret_cast<const uint8_t*>(bm.getAddr(0, y)); 274 for (size_t i = 0; i < count; i++) { 275 if (ptr[i]) { 276 return false; 277 } 278 } 279 } 280 return true; 281 } 282 283 static const int gWidth = 256; 284 static const int gHeight = 256; 285 286 static void create(SkBitmap* bm, SkColor color) { 287 bm->allocN32Pixels(gWidth, gHeight); 288 bm->eraseColor(color); 289 } 290 291 DEF_TEST(DrawBitmapRect, reporter) { 292 SkBitmap src, dst; 293 294 create(&src, 0xFFFFFFFF); 295 create(&dst, 0); 296 297 SkCanvas canvas(dst); 298 299 SkIRect srcR = { gWidth, 0, gWidth + 16, 16 }; 300 SkRect dstR = { 0, 0, SkIntToScalar(16), SkIntToScalar(16) }; 301 302 canvas.drawBitmapRect(src, &srcR, dstR, NULL); 303 304 // ensure that we draw nothing if srcR does not intersect the bitmap 305 REPORTER_ASSERT(reporter, check_for_all_zeros(dst)); 306 307 test_nan_antihair(); 308 test_giantrepeat_crbug118018(reporter); 309 310 test_treatAsSprite(reporter); 311 test_faulty_pixelref(reporter); 312 } 313