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