blob: 0a8d730a97febbb924d0ade3411c5cff246a9935 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
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 */
reed@google.com2ade0862011-03-17 17:48:04 +00008#include "Test.h"
9#include "SkBitmap.h"
10#include "SkCanvas.h"
reed@google.com99c114e2012-05-03 20:14:26 +000011#include "SkShader.h"
reed@google.comad514302013-01-02 20:19:45 +000012#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}
reed@google.com99c114e2012-05-03 20:14:26 +000096
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();
junov@google.comdbfac8a2012-12-06 21:47:40 +0000122 dev.eraseColor(SK_ColorTRANSPARENT); // necessary, so we know if we draw to it
rmistry@google.comd6176b02012-08-23 18:14:13 +0000123
reed@google.com99c114e2012-05-03 20:14:26 +0000124 SkMatrix matrix;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000125
reed@google.com99c114e2012-05-03 20:14:26 +0000126 SkCanvas c(dev);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000127 matrix.setAll(SkFloatToScalar(-119.34097f),
128 SkFloatToScalar(-43.436558f),
robertphillips@google.com4debcac2012-05-14 16:33:36 +0000129 SkFloatToScalar(93489.945f),
rmistry@google.comd6176b02012-08-23 18:14:13 +0000130 SkFloatToScalar(43.436558f),
131 SkFloatToScalar(-119.34097f),
robertphillips@google.com4debcac2012-05-14 16:33:36 +0000132 SkFloatToScalar(123.98426f),
133 0, 0, SK_Scalar1);
reed@google.com99c114e2012-05-03 20:14:26 +0000134 c.concat(matrix);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000135
reed@google.com99c114e2012-05-03 20:14:26 +0000136 SkBitmap bm;
137 bm.setConfig(SkBitmap::kARGB_8888_Config, width, height);
138 bm.allocPixels();
139 bm.eraseColor(SK_ColorRED);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000140
reed@google.com99c114e2012-05-03 20:14:26 +0000141 SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
142 SkShader::kRepeat_TileMode);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000143 matrix.setAll(SkFloatToScalar(0.0078740157f),
144 0,
robertphillips@google.com4debcac2012-05-14 16:33:36 +0000145 SkIntToScalar(249),
rmistry@google.comd6176b02012-08-23 18:14:13 +0000146 0,
147 SkFloatToScalar(0.0078740157f),
robertphillips@google.com4debcac2012-05-14 16:33:36 +0000148 SkIntToScalar(239),
149 0, 0, SK_Scalar1);
reed@google.com99c114e2012-05-03 20:14:26 +0000150 s->setLocalMatrix(matrix);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000151
reed@google.com99c114e2012-05-03 20:14:26 +0000152 SkPaint paint;
153 paint.setShader(s)->unref();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000154
reed@google.com99c114e2012-05-03 20:14:26 +0000155 SkRect r = SkRect::MakeXYWH(681, 239, 695, 253);
156 c.drawRect(r, paint);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000157
reed@google.com99c114e2012-05-03 20:14:26 +0000158 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) {
reed@google.com0da06272012-05-03 20:26:06 +0000180#ifdef SK_SCALAR_IS_FLOAT
reed@google.com99c114e2012-05-03 20:14:26 +0000181 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 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000191
reed@google.com99c114e2012-05-03 20:14:26 +0000192 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 }
reed@google.com0da06272012-05-03 20:26:06 +0000197#endif
reed@google.com99c114e2012-05-03 20:14:26 +0000198}
reed@google.com0da06272012-05-03 20:26:06 +0000199
reed@google.com99c114e2012-05-03 20:14:26 +0000200///////////////////////////////////////////////////////////////////////////////
reed@google.com2ade0862011-03-17 17:48:04 +0000201
reed@google.com6de0bfc2012-03-30 17:43:33 +0000202static 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);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000212
reed@google.com6de0bfc2012-03-30 17:43:33 +0000213 SkPaint paint;
214 paint.setAntiAlias(true);
215 paint.setStyle(SkPaint::kStroke_Style);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000216
reed@google.com6de0bfc2012-03-30 17:43:33 +0000217 // 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
reed@google.com2ade0862011-03-17 17:48:04 +0000224static 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));
reed@google.com6de0bfc2012-03-30 17:43:33 +0000263
264 test_nan_antihair(reporter);
reed@google.com99c114e2012-05-03 20:14:26 +0000265 test_giantrepeat_crbug118018(reporter);
reed@google.comad514302013-01-02 20:19:45 +0000266
267 test_treatAsSprite(reporter);
reed@google.com2ade0862011-03-17 17:48:04 +0000268}
269
270#include "TestClassDef.h"
271DEFINE_TESTCLASS("DrawBitmapRect", TestDrawBitmapRectClass, TestDrawBitmapRect)