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