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