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() { }
     24     virtual ~FailureImageGenerator() { }
     25 
     26 protected:
     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 //
     37 static 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 
     66 static 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 
     83 static void rand_size(SkISize* size, SkRandom& rand) {
     84     size->set(rand.nextU() & 0xFFFF, rand.nextU() & 0xFFFF);
     85 }
     86 
     87 static bool treat_as_sprite(const SkMatrix& mat, const SkISize& size,
     88                             unsigned bits) {
     89     return SkTreatAsSprite(mat, size.width(), size.height(), bits);
     90 }
     91 
     92 static 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 
    151 static 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 
    171 static 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  */
    232 static 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 
    253 static 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 
    274 static 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 
    289 static const int gWidth = 256;
    290 static const int gHeight = 256;
    291 
    292 static void create(SkBitmap* bm, SkColor color) {
    293     bm->allocN32Pixels(gWidth, gHeight);
    294     bm->eraseColor(color);
    295 }
    296 
    297 DEF_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