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