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