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 "SkCanvas.h"
      9 #include "SkColorPriv.h"
     10 #include "SkMathPriv.h"
     11 #include "SkRegion.h"
     12 #include "SkSurface.h"
     13 #include "Test.h"
     14 
     15 #if SK_SUPPORT_GPU
     16 #include "GrContext.h"
     17 #include "SkGr.h"
     18 #endif
     19 
     20 #include <initializer_list>
     21 
     22 static const int DEV_W = 100, DEV_H = 100;
     23 static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H);
     24 static const SkRect DEV_RECT_S = SkRect::MakeWH(DEV_W * SK_Scalar1,
     25                                                 DEV_H * SK_Scalar1);
     26 
     27 static SkPMColor get_src_color(int x, int y) {
     28     SkASSERT(x >= 0 && x < DEV_W);
     29     SkASSERT(y >= 0 && y < DEV_H);
     30 
     31     U8CPU r = x;
     32     U8CPU g = y;
     33     U8CPU b = 0xc;
     34 
     35     U8CPU a = 0xff;
     36     switch ((x+y) % 5) {
     37         case 0:
     38             a = 0xff;
     39             break;
     40         case 1:
     41             a = 0x80;
     42             break;
     43         case 2:
     44             a = 0xCC;
     45             break;
     46         case 4:
     47             a = 0x01;
     48             break;
     49         case 3:
     50             a = 0x00;
     51             break;
     52     }
     53     return SkPremultiplyARGBInline(a, r, g, b);
     54 }
     55 
     56 static SkPMColor get_dst_bmp_init_color(int x, int y, int w) {
     57     int n = y * w + x;
     58 
     59     U8CPU b = n & 0xff;
     60     U8CPU g = (n >> 8) & 0xff;
     61     U8CPU r = (n >> 16) & 0xff;
     62     return SkPackARGB32(0xff, r, g , b);
     63 }
     64 
     65 static SkPMColor convert_to_pmcolor(SkColorType ct, SkAlphaType at, const uint32_t* addr,
     66                                     bool* doUnpremul) {
     67     *doUnpremul = (kUnpremul_SkAlphaType == at);
     68 
     69     const uint8_t* c = reinterpret_cast<const uint8_t*>(addr);
     70     U8CPU a,r,g,b;
     71     switch (ct) {
     72         case kBGRA_8888_SkColorType:
     73             b = static_cast<U8CPU>(c[0]);
     74             g = static_cast<U8CPU>(c[1]);
     75             r = static_cast<U8CPU>(c[2]);
     76             a = static_cast<U8CPU>(c[3]);
     77             break;
     78         case kRGBA_8888_SkColorType:
     79             r = static_cast<U8CPU>(c[0]);
     80             g = static_cast<U8CPU>(c[1]);
     81             b = static_cast<U8CPU>(c[2]);
     82             a = static_cast<U8CPU>(c[3]);
     83             break;
     84         default:
     85             SkDEBUGFAIL("Unexpected colortype");
     86             return 0;
     87     }
     88 
     89     if (*doUnpremul) {
     90         r = SkMulDiv255Ceiling(r, a);
     91         g = SkMulDiv255Ceiling(g, a);
     92         b = SkMulDiv255Ceiling(b, a);
     93     }
     94     return SkPackARGB32(a, r, g, b);
     95 }
     96 
     97 static SkBitmap make_src_bitmap() {
     98     static SkBitmap bmp;
     99     if (bmp.isNull()) {
    100         bmp.allocN32Pixels(DEV_W, DEV_H);
    101         intptr_t pixels = reinterpret_cast<intptr_t>(bmp.getPixels());
    102         for (int y = 0; y < DEV_H; ++y) {
    103             for (int x = 0; x < DEV_W; ++x) {
    104                 SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bmp.rowBytes() + x * bmp.bytesPerPixel());
    105                 *pixel = get_src_color(x, y);
    106             }
    107         }
    108     }
    109     return bmp;
    110 }
    111 
    112 static void fill_src_canvas(SkCanvas* canvas) {
    113     canvas->save();
    114     canvas->setMatrix(SkMatrix::I());
    115     canvas->clipRect(DEV_RECT_S, SkRegion::kReplace_Op);
    116     SkPaint paint;
    117     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
    118     canvas->drawBitmap(make_src_bitmap(), 0, 0, &paint);
    119     canvas->restore();
    120 }
    121 
    122 #if SK_SUPPORT_GPU
    123 static void fill_src_texture(GrTexture* texture) {
    124     SkBitmap bmp = make_src_bitmap();
    125     bmp.lockPixels();
    126     texture->writePixels(0, 0, DEV_W, DEV_H, kSkia8888_GrPixelConfig, bmp.getPixels(),
    127                          bmp.rowBytes());
    128     bmp.unlockPixels();
    129 }
    130 #endif
    131 
    132 static void fill_dst_bmp_with_init_data(SkBitmap* bitmap) {
    133     SkASSERT(bitmap->lockPixelsAreWritable());
    134     SkAutoLockPixels alp(*bitmap);
    135     int w = bitmap->width();
    136     int h = bitmap->height();
    137     intptr_t pixels = reinterpret_cast<intptr_t>(bitmap->getPixels());
    138     for (int y = 0; y < h; ++y) {
    139         for (int x = 0; x < w; ++x) {
    140             SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bitmap->rowBytes() + x * bitmap->bytesPerPixel());
    141             *pixel = get_dst_bmp_init_color(x, y, w);
    142         }
    143     }
    144 }
    145 
    146 static bool check_read_pixel(SkPMColor a, SkPMColor b, bool didPremulConversion) {
    147     if (!didPremulConversion) {
    148         return a == b;
    149     }
    150     int32_t aA = static_cast<int32_t>(SkGetPackedA32(a));
    151     int32_t aR = static_cast<int32_t>(SkGetPackedR32(a));
    152     int32_t aG = static_cast<int32_t>(SkGetPackedG32(a));
    153     int32_t aB = SkGetPackedB32(a);
    154 
    155     int32_t bA = static_cast<int32_t>(SkGetPackedA32(b));
    156     int32_t bR = static_cast<int32_t>(SkGetPackedR32(b));
    157     int32_t bG = static_cast<int32_t>(SkGetPackedG32(b));
    158     int32_t bB = static_cast<int32_t>(SkGetPackedB32(b));
    159 
    160     return aA == bA &&
    161            SkAbs32(aR - bR) <= 1 &&
    162            SkAbs32(aG - bG) <= 1 &&
    163            SkAbs32(aB - bB) <= 1;
    164 }
    165 
    166 // checks the bitmap contains correct pixels after the readPixels
    167 // if the bitmap was prefilled with pixels it checks that these weren't
    168 // overwritten in the area outside the readPixels.
    169 static bool check_read(skiatest::Reporter* reporter,
    170                        const SkBitmap& bitmap,
    171                        int x, int y,
    172                        bool checkCanvasPixels,
    173                        bool checkBitmapPixels) {
    174     SkASSERT(4 == bitmap.bytesPerPixel());
    175     SkASSERT(!bitmap.isNull());
    176     SkASSERT(checkCanvasPixels || checkBitmapPixels);
    177 
    178     const SkColorType ct = bitmap.colorType();
    179     const SkAlphaType at = bitmap.alphaType();
    180 
    181     int bw = bitmap.width();
    182     int bh = bitmap.height();
    183 
    184     SkIRect srcRect = SkIRect::MakeXYWH(x, y, bw, bh);
    185     SkIRect clippedSrcRect = DEV_RECT;
    186     if (!clippedSrcRect.intersect(srcRect)) {
    187         clippedSrcRect.setEmpty();
    188     }
    189     SkAutoLockPixels alp(bitmap);
    190     for (int by = 0; by < bh; ++by) {
    191         for (int bx = 0; bx < bw; ++bx) {
    192             int devx = bx + srcRect.fLeft;
    193             int devy = by + srcRect.fTop;
    194 
    195             const uint32_t* pixel = bitmap.getAddr32(bx, by);
    196 
    197             if (clippedSrcRect.contains(devx, devy)) {
    198                 if (checkCanvasPixels) {
    199                     SkPMColor canvasPixel = get_src_color(devx, devy);
    200                     bool didPremul;
    201                     SkPMColor pmPixel = convert_to_pmcolor(ct, at, pixel, &didPremul);
    202                     if (!check_read_pixel(pmPixel, canvasPixel, didPremul)) {
    203                         ERRORF(reporter, "Expected readback pixel value 0x%08x, got 0x%08x. "
    204                                "Readback was unpremul: %d", canvasPixel, pmPixel, didPremul);
    205                         return false;
    206                     }
    207                 }
    208             } else if (checkBitmapPixels) {
    209                 uint32_t origDstPixel = get_dst_bmp_init_color(bx, by, bw);
    210                 if (origDstPixel != *pixel) {
    211                     ERRORF(reporter, "Expected clipped out area of readback to be unchanged. "
    212                            "Expected 0x%08x, got 0x%08x", origDstPixel, *pixel);
    213                     return false;
    214                 }
    215             }
    216         }
    217     }
    218     return true;
    219 }
    220 
    221 enum BitmapInit {
    222     kFirstBitmapInit = 0,
    223 
    224     kNoPixels_BitmapInit = kFirstBitmapInit,
    225     kTight_BitmapInit,
    226     kRowBytes_BitmapInit,
    227     kRowBytesOdd_BitmapInit,
    228 
    229     kLastAligned_BitmapInit = kRowBytes_BitmapInit,
    230 
    231 #if 0  // THIS CAUSES ERRORS ON WINDOWS AND SOME ANDROID DEVICES
    232     kLast_BitmapInit = kRowBytesOdd_BitmapInit
    233 #else
    234     kLast_BitmapInit = kLastAligned_BitmapInit
    235 #endif
    236 };
    237 
    238 static BitmapInit nextBMI(BitmapInit bmi) {
    239     int x = bmi;
    240     return static_cast<BitmapInit>(++x);
    241 }
    242 
    243 static void init_bitmap(SkBitmap* bitmap, const SkIRect& rect, BitmapInit init, SkColorType ct,
    244                         SkAlphaType at) {
    245     SkImageInfo info = SkImageInfo::Make(rect.width(), rect.height(), ct, at);
    246     size_t rowBytes = 0;
    247     bool alloc = true;
    248     switch (init) {
    249         case kNoPixels_BitmapInit:
    250             alloc = false;
    251         case kTight_BitmapInit:
    252             break;
    253         case kRowBytes_BitmapInit:
    254             rowBytes = (info.width() + 16) * sizeof(SkPMColor);
    255             break;
    256         case kRowBytesOdd_BitmapInit:
    257             rowBytes = (info.width() * sizeof(SkPMColor)) + 3;
    258             break;
    259         default:
    260             SkASSERT(0);
    261             break;
    262     }
    263 
    264     if (alloc) {
    265         bitmap->allocPixels(info, rowBytes);
    266     } else {
    267         bitmap->setInfo(info, rowBytes);
    268     }
    269 }
    270 
    271 static const struct {
    272     SkColorType fColorType;
    273     SkAlphaType fAlphaType;
    274 } gReadPixelsConfigs[] = {
    275     { kRGBA_8888_SkColorType,   kPremul_SkAlphaType },
    276     { kRGBA_8888_SkColorType,   kUnpremul_SkAlphaType },
    277     { kBGRA_8888_SkColorType,   kPremul_SkAlphaType },
    278     { kBGRA_8888_SkColorType,   kUnpremul_SkAlphaType },
    279 };
    280 const SkIRect gReadPixelsTestRects[] = {
    281     // entire thing
    282     DEV_RECT,
    283     // larger on all sides
    284     SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H + 10),
    285     // fully contained
    286     SkIRect::MakeLTRB(DEV_W / 4, DEV_H / 4, 3 * DEV_W / 4, 3 * DEV_H / 4),
    287     // outside top left
    288     SkIRect::MakeLTRB(-10, -10, -1, -1),
    289     // touching top left corner
    290     SkIRect::MakeLTRB(-10, -10, 0, 0),
    291     // overlapping top left corner
    292     SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H / 4),
    293     // overlapping top left and top right corners
    294     SkIRect::MakeLTRB(-10, -10, DEV_W  + 10, DEV_H / 4),
    295     // touching entire top edge
    296     SkIRect::MakeLTRB(-10, -10, DEV_W  + 10, 0),
    297     // overlapping top right corner
    298     SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W  + 10, DEV_H / 4),
    299     // contained in x, overlapping top edge
    300     SkIRect::MakeLTRB(DEV_W / 4, -10, 3 * DEV_W  / 4, DEV_H / 4),
    301     // outside top right corner
    302     SkIRect::MakeLTRB(DEV_W + 1, -10, DEV_W + 10, -1),
    303     // touching top right corner
    304     SkIRect::MakeLTRB(DEV_W, -10, DEV_W + 10, 0),
    305     // overlapping top left and bottom left corners
    306     SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H + 10),
    307     // touching entire left edge
    308     SkIRect::MakeLTRB(-10, -10, 0, DEV_H + 10),
    309     // overlapping bottom left corner
    310     SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W / 4, DEV_H + 10),
    311     // contained in y, overlapping left edge
    312     SkIRect::MakeLTRB(-10, DEV_H / 4, DEV_W / 4, 3 * DEV_H / 4),
    313     // outside bottom left corner
    314     SkIRect::MakeLTRB(-10, DEV_H + 1, -1, DEV_H + 10),
    315     // touching bottom left corner
    316     SkIRect::MakeLTRB(-10, DEV_H, 0, DEV_H + 10),
    317     // overlapping bottom left and bottom right corners
    318     SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
    319     // touching entire left edge
    320     SkIRect::MakeLTRB(0, DEV_H, DEV_W, DEV_H + 10),
    321     // overlapping bottom right corner
    322     SkIRect::MakeLTRB(3 * DEV_W / 4, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
    323     // overlapping top right and bottom right corners
    324     SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H + 10),
    325 };
    326 
    327 static void test_readpixels(skiatest::Reporter* reporter, SkSurface* surface,
    328                             BitmapInit lastBitmapInit) {
    329     SkCanvas* canvas = surface->getCanvas();
    330     fill_src_canvas(canvas);
    331     for (size_t rect = 0; rect < SK_ARRAY_COUNT(gReadPixelsTestRects); ++rect) {
    332         const SkIRect& srcRect = gReadPixelsTestRects[rect];
    333         for (BitmapInit bmi = kFirstBitmapInit; bmi <= lastBitmapInit; bmi = nextBMI(bmi)) {
    334             for (size_t c = 0; c < SK_ARRAY_COUNT(gReadPixelsConfigs); ++c) {
    335                 SkBitmap bmp;
    336                 init_bitmap(&bmp, srcRect, bmi,
    337                             gReadPixelsConfigs[c].fColorType, gReadPixelsConfigs[c].fAlphaType);
    338 
    339                 // if the bitmap has pixels allocated before the readPixels,
    340                 // note that and fill them with pattern
    341                 bool startsWithPixels = !bmp.isNull();
    342                 if (startsWithPixels) {
    343                     fill_dst_bmp_with_init_data(&bmp);
    344                 }
    345                 uint32_t idBefore = surface->generationID();
    346                 bool success = canvas->readPixels(&bmp, srcRect.fLeft, srcRect.fTop);
    347                 uint32_t idAfter = surface->generationID();
    348 
    349                 // we expect to succeed when the read isn't fully clipped
    350                 // out.
    351                 bool expectSuccess = SkIRect::Intersects(srcRect, DEV_RECT);
    352                 // determine whether we expected the read to succeed.
    353                 REPORTER_ASSERT(reporter, success == expectSuccess);
    354                 // read pixels should never change the gen id
    355                 REPORTER_ASSERT(reporter, idBefore == idAfter);
    356 
    357                 if (success || startsWithPixels) {
    358                     check_read(reporter, bmp, srcRect.fLeft, srcRect.fTop,
    359                                success, startsWithPixels);
    360                 } else {
    361                     // if we had no pixels beforehand and the readPixels
    362                     // failed then our bitmap should still not have pixels
    363                     REPORTER_ASSERT(reporter, bmp.isNull());
    364                 }
    365             }
    366             // check the old webkit version of readPixels that clips the
    367             // bitmap size
    368             SkBitmap wkbmp;
    369             bool success = canvas->readPixels(srcRect, &wkbmp);
    370             SkIRect clippedRect = DEV_RECT;
    371             if (clippedRect.intersect(srcRect)) {
    372                 REPORTER_ASSERT(reporter, success);
    373                 REPORTER_ASSERT(reporter, kN32_SkColorType == wkbmp.colorType());
    374                 REPORTER_ASSERT(reporter, kPremul_SkAlphaType == wkbmp.alphaType());
    375                 check_read(reporter, wkbmp, clippedRect.fLeft,
    376                            clippedRect.fTop, true, false);
    377             } else {
    378                 REPORTER_ASSERT(reporter, !success);
    379             }
    380         }
    381     }
    382 }
    383 DEF_TEST(ReadPixels, reporter) {
    384     const SkImageInfo info = SkImageInfo::MakeN32Premul(DEV_W, DEV_H);
    385     SkAutoTUnref<SkSurface> surface(SkSurface::NewRaster(info));
    386     // SW readback fails a premul check when reading back to an unaligned rowbytes.
    387     test_readpixels(reporter, surface, kLastAligned_BitmapInit);
    388 }
    389 #if SK_SUPPORT_GPU
    390 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ReadPixels_Gpu, reporter, context) {
    391     for (auto& origin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) {
    392         GrSurfaceDesc desc;
    393         desc.fFlags = kRenderTarget_GrSurfaceFlag;
    394         desc.fWidth = DEV_W;
    395         desc.fHeight = DEV_H;
    396         desc.fConfig = kSkia8888_GrPixelConfig;
    397         desc.fOrigin = origin;
    398         SkAutoTUnref<GrTexture> surfaceTexture(
    399             context->textureProvider()->createTexture(desc, SkBudgeted::kNo));
    400         SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTargetDirect(surfaceTexture->asRenderTarget()));
    401         desc.fFlags = kNone_GrSurfaceFlags;
    402         test_readpixels(reporter, surface, kLast_BitmapInit);
    403     }
    404 }
    405 #endif
    406 
    407 #if SK_SUPPORT_GPU
    408 static void test_readpixels_texture(skiatest::Reporter* reporter, GrTexture* texture) {
    409     fill_src_texture(texture);
    410     for (size_t rect = 0; rect < SK_ARRAY_COUNT(gReadPixelsTestRects); ++rect) {
    411         const SkIRect& srcRect = gReadPixelsTestRects[rect];
    412         for (BitmapInit bmi = kFirstBitmapInit; bmi <= kLast_BitmapInit; bmi = nextBMI(bmi)) {
    413             for (size_t c = 0; c < SK_ARRAY_COUNT(gReadPixelsConfigs); ++c) {
    414                 SkBitmap bmp;
    415                 init_bitmap(&bmp, srcRect, bmi,
    416                             gReadPixelsConfigs[c].fColorType, gReadPixelsConfigs[c].fAlphaType);
    417 
    418                 // if the bitmap has pixels allocated before the readPixels,
    419                 // note that and fill them with pattern
    420                 bool startsWithPixels = !bmp.isNull();
    421                 // Try doing the read directly from a non-renderable texture
    422                 if (startsWithPixels) {
    423                     fill_dst_bmp_with_init_data(&bmp);
    424                     GrPixelConfig dstConfig =
    425                             SkImageInfo2GrPixelConfig(gReadPixelsConfigs[c].fColorType,
    426                                                       gReadPixelsConfigs[c].fAlphaType,
    427                                                       kLinear_SkColorProfileType);
    428                     uint32_t flags = 0;
    429                     if (gReadPixelsConfigs[c].fAlphaType == kUnpremul_SkAlphaType) {
    430                         flags = GrContext::kUnpremul_PixelOpsFlag;
    431                     }
    432                     bmp.lockPixels();
    433                     bool success = texture->readPixels(srcRect.fLeft, srcRect.fTop, bmp.width(),
    434                                                        bmp.height(), dstConfig, bmp.getPixels(),
    435                                                        bmp.rowBytes(), flags);
    436                     bmp.unlockPixels();
    437                     check_read(reporter, bmp, srcRect.fLeft, srcRect.fTop,
    438                                success, true);
    439                 }
    440             }
    441         }
    442     }
    443 }
    444 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ReadPixels_Texture, reporter, context) {
    445     // On the GPU we will also try reading back from a non-renderable texture.
    446     for (auto& origin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) {
    447         SkAutoTUnref<GrTexture> texture;
    448         GrSurfaceDesc desc;
    449         desc.fFlags = kRenderTarget_GrSurfaceFlag;
    450         desc.fWidth = DEV_W;
    451         desc.fHeight = DEV_H;
    452         desc.fConfig = kSkia8888_GrPixelConfig;
    453         desc.fOrigin = origin;
    454         desc.fFlags = kNone_GrSurfaceFlags;
    455         texture.reset(context->textureProvider()->createTexture(desc, SkBudgeted::kNo));
    456         test_readpixels_texture(reporter, texture);
    457     }
    458 }
    459 #endif
    460 /////////////////////
    461 #if SK_SUPPORT_GPU
    462 
    463 // make_ringed_bitmap was lifted from gm/bleed.cpp, as that GM was what showed the following
    464 // bug when a change was made to SkImage_Raster.cpp. It is possible that other test bitmaps
    465 // would also tickle https://bug.skia.org/4351 but this one is know to do it, so I've pasted the code
    466 // here so we have a dependable repro case.
    467 
    468 // Create a black&white checked texture with 2 1-pixel rings
    469 // around the outside edge. The inner ring is red and the outer ring is blue.
    470 static void make_ringed_bitmap(SkBitmap* result, int width, int height) {
    471     SkASSERT(0 == width % 2 && 0 == height % 2);
    472 
    473     static const SkPMColor kRed = SkPreMultiplyColor(SK_ColorRED);
    474     static const SkPMColor kBlue = SkPreMultiplyColor(SK_ColorBLUE);
    475     static const SkPMColor kBlack = SkPreMultiplyColor(SK_ColorBLACK);
    476     static const SkPMColor kWhite = SkPreMultiplyColor(SK_ColorWHITE);
    477 
    478     result->allocN32Pixels(width, height, true);
    479 
    480     SkPMColor* scanline = result->getAddr32(0, 0);
    481     for (int x = 0; x < width; ++x) {
    482         scanline[x] = kBlue;
    483     }
    484     scanline = result->getAddr32(0, 1);
    485     scanline[0] = kBlue;
    486     for (int x = 1; x < width - 1; ++x) {
    487         scanline[x] = kRed;
    488     }
    489     scanline[width-1] = kBlue;
    490 
    491     for (int y = 2; y < height/2; ++y) {
    492         scanline = result->getAddr32(0, y);
    493         scanline[0] = kBlue;
    494         scanline[1] = kRed;
    495         for (int x = 2; x < width/2; ++x) {
    496             scanline[x] = kBlack;
    497         }
    498         for (int x = width/2; x < width-2; ++x) {
    499             scanline[x] = kWhite;
    500         }
    501         scanline[width-2] = kRed;
    502         scanline[width-1] = kBlue;
    503     }
    504 
    505     for (int y = height/2; y < height-2; ++y) {
    506         scanline = result->getAddr32(0, y);
    507         scanline[0] = kBlue;
    508         scanline[1] = kRed;
    509         for (int x = 2; x < width/2; ++x) {
    510             scanline[x] = kWhite;
    511         }
    512         for (int x = width/2; x < width-2; ++x) {
    513             scanline[x] = kBlack;
    514         }
    515         scanline[width-2] = kRed;
    516         scanline[width-1] = kBlue;
    517     }
    518 
    519     scanline = result->getAddr32(0, height-2);
    520     scanline[0] = kBlue;
    521     for (int x = 1; x < width - 1; ++x) {
    522         scanline[x] = kRed;
    523     }
    524     scanline[width-1] = kBlue;
    525 
    526     scanline = result->getAddr32(0, height-1);
    527     for (int x = 0; x < width; ++x) {
    528         scanline[x] = kBlue;
    529     }
    530     result->setImmutable();
    531 }
    532 
    533 static void compare_textures(skiatest::Reporter* reporter, GrTexture* txa, GrTexture* txb) {
    534     REPORTER_ASSERT(reporter, txa->width() == 2);
    535     REPORTER_ASSERT(reporter, txa->height() == 2);
    536     REPORTER_ASSERT(reporter, txb->width() == 2);
    537     REPORTER_ASSERT(reporter, txb->height() == 2);
    538     REPORTER_ASSERT(reporter, txa->config() == txb->config());
    539 
    540     SkPMColor pixelsA[4], pixelsB[4];
    541     REPORTER_ASSERT(reporter, txa->readPixels(0, 0, 2, 2, txa->config(), pixelsA));
    542     REPORTER_ASSERT(reporter, txb->readPixels(0, 0, 2, 2, txa->config(), pixelsB));
    543     REPORTER_ASSERT(reporter, 0 == memcmp(pixelsA, pixelsB, sizeof(pixelsA)));
    544 }
    545 
    546 static SkData* draw_into_surface(SkSurface* surf, const SkBitmap& bm, SkFilterQuality quality) {
    547     SkCanvas* canvas = surf->getCanvas();
    548     canvas->clear(SK_ColorBLUE);
    549 
    550     SkPaint paint;
    551     paint.setFilterQuality(quality);
    552 
    553     canvas->translate(40, 100);
    554     canvas->rotate(30);
    555     canvas->scale(20, 30);
    556     canvas->translate(-SkScalarHalf(bm.width()), -SkScalarHalf(bm.height()));
    557     canvas->drawBitmap(bm, 0, 0, &paint);
    558 
    559     SkAutoTUnref<SkImage> image(surf->newImageSnapshot());
    560     return image->encode();
    561 }
    562 
    563 #include "SkStream.h"
    564 static void dump_to_file(const char name[], SkData* data) {
    565     SkFILEWStream file(name);
    566     file.write(data->data(), data->size());
    567 }
    568 
    569 /*
    570  *  Test two different ways to turn a subset of a bitmap into a texture
    571  *  - subset and then upload to a texture
    572  *  - upload to a texture and then subset
    573  *
    574  *  These two techniques result in the same pixels (ala readPixels)
    575  *  but when we draw them (rotated+scaled) we don't always get the same results.
    576  *
    577  *  https://bug.skia.org/4351
    578  */
    579 DEF_GPUTEST_FOR_NATIVE_CONTEXT(ReadPixels_Subset_Gpu, reporter, context) {
    580     SkBitmap bitmap;
    581     make_ringed_bitmap(&bitmap, 6, 6);
    582     const SkIRect subset = SkIRect::MakeLTRB(2, 2, 4, 4);
    583 
    584     // make two textures...
    585     SkBitmap bm_subset, tx_subset;
    586 
    587     // ... one from a texture-subset
    588     SkAutoTUnref<GrTexture> fullTx(GrRefCachedBitmapTexture(context, bitmap,
    589                                                             GrTextureParams::ClampNoFilter()));
    590     SkBitmap tx_full;
    591     GrWrapTextureInBitmap(fullTx, bitmap.width(), bitmap.height(), true, &tx_full);
    592     tx_full.extractSubset(&tx_subset, subset);
    593 
    594     // ... one from a bitmap-subset
    595     SkBitmap tmp_subset;
    596     bitmap.extractSubset(&tmp_subset, subset);
    597     SkAutoTUnref<GrTexture> subsetTx(GrRefCachedBitmapTexture(context, tmp_subset,
    598                                                               GrTextureParams::ClampNoFilter()));
    599     GrWrapTextureInBitmap(subsetTx, tmp_subset.width(), tmp_subset.height(), true, &bm_subset);
    600 
    601     // did we get the same subset?
    602     compare_textures(reporter, bm_subset.getTexture(), tx_subset.getTexture());
    603 
    604     // do they draw the same?
    605     const SkImageInfo info = SkImageInfo::MakeN32Premul(128, 128);
    606     SkAutoTUnref<SkSurface> surfA(SkSurface::NewRenderTarget(context, SkBudgeted::kNo, info, 0));
    607     SkAutoTUnref<SkSurface> surfB(SkSurface::NewRenderTarget(context, SkBudgeted::kNo, info, 0));
    608 
    609     if (false) {
    610         //
    611         //  BUG: depending on the driver, if we calls this with various quality settings, it
    612         //       may fail.
    613         //
    614         SkFilterQuality quality = kLow_SkFilterQuality;
    615 
    616         SkAutoTUnref<SkData> dataA(draw_into_surface(surfA, bm_subset, quality));
    617         SkAutoTUnref<SkData> dataB(draw_into_surface(surfB, tx_subset, quality));
    618 
    619         REPORTER_ASSERT(reporter, dataA->equals(dataB));
    620         if (false) {
    621             dump_to_file("test_image_A.png", dataA);
    622             dump_to_file("test_image_B.png", dataB);
    623         }
    624     }
    625 }
    626 #endif
    627