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