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