DrawBitmapRectTest.cpp revision 422188f3c6286d2991a029027958387b070e4dc6
1
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 */
8#include "Test.h"
9#include "SkBitmap.h"
10#include "SkCanvas.h"
11#include "SkShader.h"
12#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}
96
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();
122    dev.eraseColor(SK_ColorTRANSPARENT);  // necessary, so we know if we draw to it
123
124    SkMatrix matrix;
125
126    SkCanvas c(dev);
127    matrix.setAll(SkFloatToScalar(-119.34097f),
128                  SkFloatToScalar(-43.436558f),
129                  SkFloatToScalar(93489.945f),
130                  SkFloatToScalar(43.436558f),
131                  SkFloatToScalar(-119.34097f),
132                  SkFloatToScalar(123.98426f),
133                  0, 0, SK_Scalar1);
134    c.concat(matrix);
135
136    SkBitmap bm;
137    bm.setConfig(SkBitmap::kARGB_8888_Config, width, height);
138    bm.allocPixels();
139    bm.eraseColor(SK_ColorRED);
140
141    SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
142                                               SkShader::kRepeat_TileMode);
143    matrix.setAll(SkFloatToScalar(0.0078740157f),
144                  0,
145                  SkIntToScalar(249),
146                  0,
147                  SkFloatToScalar(0.0078740157f),
148                  SkIntToScalar(239),
149                  0, 0, SK_Scalar1);
150    s->setLocalMatrix(matrix);
151
152    SkPaint paint;
153    paint.setShader(s)->unref();
154
155    SkRect r = SkRect::MakeXYWH(681, 239, 695, 253);
156    c.drawRect(r, paint);
157
158    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) {
180#ifdef SK_SCALAR_IS_FLOAT
181    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    };
191
192    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    }
197#endif
198}
199
200///////////////////////////////////////////////////////////////////////////////
201
202static 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);
212
213    SkPaint paint;
214    paint.setAntiAlias(true);
215    paint.setStyle(SkPaint::kStroke_Style);
216
217    // 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
224static 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));
263
264    test_nan_antihair(reporter);
265    test_giantrepeat_crbug118018(reporter);
266
267    test_treatAsSprite(reporter);
268}
269
270#include "TestClassDef.h"
271DEFINE_TESTCLASS("DrawBitmapRect", TestDrawBitmapRectClass, TestDrawBitmapRect)
272