DrawBitmapRectTest.cpp revision 422188f3c6286d2991a029027958387b070e4dc6
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 15static 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 32static void rand_rect(SkRect* r, SkRandom& rand) { 33 r->set(rand.nextSScalar1() * 1000, rand.nextSScalar1() * 1000, 34 rand.nextSScalar1() * 1000, rand.nextSScalar1() * 1000); 35 r->sort(); 36} 37 38static void test_treatAsSprite(skiatest::Reporter* reporter) { 39 const unsigned bilerBits = kSkSubPixelBitsForBilerp; 40 41 SkMatrix mat; 42 SkRect r; 43 SkRandom rand; 44 45 // assert: translate-only no-filter can always be treated as sprite 46 for (int i = 0; i < 1000; ++i) { 47 rand_matrix(&mat, rand, SkMatrix::kTranslate_Mask); 48 for (int j = 0; j < 1000; ++j) { 49 rand_rect(&r, rand); 50 REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, r, 0)); 51 } 52 } 53 54 // assert: rotate/perspect is never treated as sprite 55 for (int i = 0; i < 1000; ++i) { 56 rand_matrix(&mat, rand, SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask); 57 for (int j = 0; j < 1000; ++j) { 58 rand_rect(&r, rand); 59 REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, r, 0)); 60 REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, r, bilerBits)); 61 } 62 } 63 64 r.set(10, 10, 500, 600); 65 66 const SkScalar tooMuchSubpixel = SkFloatToScalar(100.1f); 67 mat.setTranslate(tooMuchSubpixel, 0); 68 REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, r, bilerBits)); 69 mat.setTranslate(0, tooMuchSubpixel); 70 REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, r, bilerBits)); 71 72 const SkScalar tinySubPixel = SkFloatToScalar(100.02f); 73 mat.setTranslate(tinySubPixel, 0); 74 REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, r, bilerBits)); 75 mat.setTranslate(0, tinySubPixel); 76 REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, r, bilerBits)); 77 78 const SkScalar twoThirds = SK_Scalar1 * 2 / 3; 79 const SkScalar bigScale = SkScalarDiv(r.width() + twoThirds, r.width()); 80 mat.setScale(bigScale, bigScale); 81 REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, r, false)); 82 REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, r, bilerBits)); 83 84 const SkScalar oneThird = SK_Scalar1 / 3; 85 const SkScalar smallScale = SkScalarDiv(r.width() + oneThird, r.width()); 86 mat.setScale(smallScale, smallScale); 87 REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, r, false)); 88 REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, r, bilerBits)); 89 90 const SkScalar oneFortyth = SK_Scalar1 / 40; 91 const SkScalar tinyScale = SkScalarDiv(r.width() + oneFortyth, r.width()); 92 mat.setScale(tinyScale, tinyScale); 93 REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, r, false)); 94 REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, r, bilerBits)); 95} 96 97static void assert_ifDrawnTo(skiatest::Reporter* reporter, 98 const SkBitmap& bm, bool shouldBeDrawn) { 99 for (int y = 0; y < bm.height(); ++y) { 100 for (int x = 0; x < bm.width(); ++x) { 101 if (shouldBeDrawn) { 102 if (0 == *bm.getAddr32(x, y)) { 103 REPORTER_ASSERT(reporter, false); 104 return; 105 } 106 } else { 107 // should not be drawn 108 if (*bm.getAddr32(x, y)) { 109 REPORTER_ASSERT(reporter, false); 110 return; 111 } 112 } 113 } 114 } 115} 116 117static void test_wacky_bitmapshader(skiatest::Reporter* reporter, 118 int width, int height, bool shouldBeDrawn) { 119 SkBitmap dev; 120 dev.setConfig(SkBitmap::kARGB_8888_Config, 0x56F, 0x4f6); 121 dev.allocPixels(); 122 dev.eraseColor(SK_ColorTRANSPARENT); // necessary, so we know if we draw to it 123 124 SkMatrix matrix; 125 126 SkCanvas c(dev); 127 matrix.setAll(SkFloatToScalar(-119.34097f), 128 SkFloatToScalar(-43.436558f), 129 SkFloatToScalar(93489.945f), 130 SkFloatToScalar(43.436558f), 131 SkFloatToScalar(-119.34097f), 132 SkFloatToScalar(123.98426f), 133 0, 0, SK_Scalar1); 134 c.concat(matrix); 135 136 SkBitmap bm; 137 bm.setConfig(SkBitmap::kARGB_8888_Config, width, height); 138 bm.allocPixels(); 139 bm.eraseColor(SK_ColorRED); 140 141 SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode, 142 SkShader::kRepeat_TileMode); 143 matrix.setAll(SkFloatToScalar(0.0078740157f), 144 0, 145 SkIntToScalar(249), 146 0, 147 SkFloatToScalar(0.0078740157f), 148 SkIntToScalar(239), 149 0, 0, SK_Scalar1); 150 s->setLocalMatrix(matrix); 151 152 SkPaint paint; 153 paint.setShader(s)->unref(); 154 155 SkRect r = SkRect::MakeXYWH(681, 239, 695, 253); 156 c.drawRect(r, paint); 157 158 assert_ifDrawnTo(reporter, dev, shouldBeDrawn); 159} 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 3 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 */ 179static void test_giantrepeat_crbug118018(skiatest::Reporter* reporter) { 180#ifdef SK_SCALAR_IS_FLOAT 181 static const struct { 182 int fWidth; 183 int fHeight; 184 bool fExpectedToDraw; 185 } gTests[] = { 186 { 0x1b294, 0x7f, false }, // crbug 118018 (width exceeds 64K) 187 { 0xFFFF, 0x7f, true }, // should draw, test max width 188 { 0x7f, 0xFFFF, true }, // should draw, test max height 189 { 0xFFFF, 0xFFFF, false }, // allocation fails (too much RAM) 190 }; 191 192 for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) { 193 test_wacky_bitmapshader(reporter, 194 gTests[i].fWidth, gTests[i].fHeight, 195 gTests[i].fExpectedToDraw); 196 } 197#endif 198} 199 200/////////////////////////////////////////////////////////////////////////////// 201 202static void test_nan_antihair(skiatest::Reporter* reporter) { 203 SkBitmap bm; 204 bm.setConfig(SkBitmap::kARGB_8888_Config, 20, 20); 205 bm.allocPixels(); 206 207 SkCanvas canvas(bm); 208 209 SkPath path; 210 path.moveTo(0, 0); 211 path.lineTo(10, SK_ScalarNaN); 212 213 SkPaint paint; 214 paint.setAntiAlias(true); 215 paint.setStyle(SkPaint::kStroke_Style); 216 217 // before our fix to SkScan_Antihair.cpp to check for integral NaN (0x800...) 218 // this would trigger an assert/crash. 219 // 220 // see rev. 3558 221 canvas.drawPath(path, paint); 222} 223 224static bool check_for_all_zeros(const SkBitmap& bm) { 225 SkAutoLockPixels alp(bm); 226 227 size_t count = bm.width() * bm.bytesPerPixel(); 228 for (int y = 0; y < bm.height(); y++) { 229 const uint8_t* ptr = reinterpret_cast<const uint8_t*>(bm.getAddr(0, y)); 230 for (size_t i = 0; i < count; i++) { 231 if (ptr[i]) { 232 return false; 233 } 234 } 235 } 236 return true; 237} 238 239static const int gWidth = 256; 240static const int gHeight = 256; 241 242static void create(SkBitmap* bm, SkBitmap::Config config, SkColor color) { 243 bm->setConfig(config, gWidth, gHeight); 244 bm->allocPixels(); 245 bm->eraseColor(color); 246} 247 248static void TestDrawBitmapRect(skiatest::Reporter* reporter) { 249 SkBitmap src, dst; 250 251 create(&src, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF); 252 create(&dst, SkBitmap::kARGB_8888_Config, 0); 253 254 SkCanvas canvas(dst); 255 256 SkIRect srcR = { gWidth, 0, gWidth + 16, 16 }; 257 SkRect dstR = { 0, 0, SkIntToScalar(16), SkIntToScalar(16) }; 258 259 canvas.drawBitmapRect(src, &srcR, dstR, NULL); 260 261 // ensure that we draw nothing if srcR does not intersect the bitmap 262 REPORTER_ASSERT(reporter, check_for_all_zeros(dst)); 263 264 test_nan_antihair(reporter); 265 test_giantrepeat_crbug118018(reporter); 266 267 test_treatAsSprite(reporter); 268} 269 270#include "TestClassDef.h" 271DEFINE_TESTCLASS("DrawBitmapRect", TestDrawBitmapRectClass, TestDrawBitmapRect) 272