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