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