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