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