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