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