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