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