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 #include "sk_tool_utils.h"
     15 
     16 #if SK_SUPPORT_GPU
     17 #include "GrContext.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 static const U8CPU DEV_PAD = 0xee;
     27 
     28 static SkPMColor get_canvas_color(int x, int y) {
     29     SkASSERT(x >= 0 && x < DEV_W);
     30     SkASSERT(y >= 0 && y < DEV_H);
     31 
     32     U8CPU r = x;
     33     U8CPU g = y;
     34     U8CPU b = 0xc;
     35 
     36     U8CPU a = 0x0;
     37     switch ((x+y) % 5) {
     38         case 0:
     39             a = 0xff;
     40             break;
     41         case 1:
     42             a = 0x80;
     43             break;
     44         case 2:
     45             a = 0xCC;
     46             break;
     47         case 3:
     48             a = 0x00;
     49             break;
     50         case 4:
     51             a = 0x01;
     52             break;
     53     }
     54     return SkPremultiplyARGBInline(a, r, g, b);
     55 }
     56 
     57 // assumes any premu/.unpremul has been applied
     58 static uint32_t pack_color_type(SkColorType ct, U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
     59     uint32_t r32;
     60     uint8_t* result = reinterpret_cast<uint8_t*>(&r32);
     61     switch (ct) {
     62         case kBGRA_8888_SkColorType:
     63             result[0] = b;
     64             result[1] = g;
     65             result[2] = r;
     66             result[3] = a;
     67             break;
     68         case kRGBA_8888_SkColorType:
     69             result[0] = r;
     70             result[1] = g;
     71             result[2] = b;
     72             result[3] = a;
     73             break;
     74         default:
     75             SkASSERT(0);
     76             return 0;
     77     }
     78     return r32;
     79 }
     80 
     81 static uint32_t get_bitmap_color(int x, int y, int w, SkColorType ct, SkAlphaType at) {
     82     int n = y * w + x;
     83     U8CPU b = n & 0xff;
     84     U8CPU g = (n >> 8) & 0xff;
     85     U8CPU r = (n >> 16) & 0xff;
     86     U8CPU a = 0;
     87     switch ((x+y) % 5) {
     88         case 4:
     89             a = 0xff;
     90             break;
     91         case 3:
     92             a = 0x80;
     93             break;
     94         case 2:
     95             a = 0xCC;
     96             break;
     97         case 1:
     98             a = 0x01;
     99             break;
    100         case 0:
    101             a = 0x00;
    102             break;
    103     }
    104     if (kPremul_SkAlphaType == at) {
    105         r = SkMulDiv255Ceiling(r, a);
    106         g = SkMulDiv255Ceiling(g, a);
    107         b = SkMulDiv255Ceiling(b, a);
    108     }
    109     return pack_color_type(ct, a, r, g , b);
    110 }
    111 
    112 static void fill_canvas(SkCanvas* canvas) {
    113     SkBitmap bmp;
    114     if (bmp.isNull()) {
    115         bmp.allocN32Pixels(DEV_W, DEV_H);
    116         for (int y = 0; y < DEV_H; ++y) {
    117             for (int x = 0; x < DEV_W; ++x) {
    118                 *bmp.getAddr32(x, y) = get_canvas_color(x, y);
    119             }
    120         }
    121     }
    122     canvas->save();
    123     canvas->setMatrix(SkMatrix::I());
    124     canvas->clipRect(DEV_RECT_S, SkRegion::kReplace_Op);
    125     SkPaint paint;
    126     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
    127     canvas->drawBitmap(bmp, 0, 0, &paint);
    128     canvas->restore();
    129 }
    130 
    131 /**
    132  *  Lucky for us, alpha is always in the same spot (SK_A32_SHIFT), for both RGBA and BGRA.
    133  *  Thus this routine doesn't need to know the exact colortype
    134  */
    135 static uint32_t premul(uint32_t color) {
    136     unsigned a = SkGetPackedA32(color);
    137     // these next three are not necessarily r,g,b in that order, but they are r,g,b in some order.
    138     unsigned c0 = SkGetPackedR32(color);
    139     unsigned c1 = SkGetPackedG32(color);
    140     unsigned c2 = SkGetPackedB32(color);
    141     c0 = SkMulDiv255Ceiling(c0, a);
    142     c1 = SkMulDiv255Ceiling(c1, a);
    143     c2 = SkMulDiv255Ceiling(c2, a);
    144     return SkPackARGB32NoCheck(a, c0, c1, c2);
    145 }
    146 
    147 static SkPMColor convert_to_PMColor(SkColorType ct, SkAlphaType at, uint32_t color) {
    148     if (kUnpremul_SkAlphaType == at) {
    149         color = premul(color);
    150     }
    151     switch (ct) {
    152         case kRGBA_8888_SkColorType:
    153             color = SkSwizzle_RGBA_to_PMColor(color);
    154             break;
    155         case kBGRA_8888_SkColorType:
    156             color = SkSwizzle_BGRA_to_PMColor(color);
    157             break;
    158         default:
    159             SkASSERT(0);
    160             break;
    161     }
    162     return color;
    163 }
    164 
    165 static bool check_pixel(SkPMColor a, SkPMColor b, bool didPremulConversion) {
    166     if (!didPremulConversion) {
    167         return a == b;
    168     }
    169     int32_t aA = static_cast<int32_t>(SkGetPackedA32(a));
    170     int32_t aR = static_cast<int32_t>(SkGetPackedR32(a));
    171     int32_t aG = static_cast<int32_t>(SkGetPackedG32(a));
    172     int32_t aB = SkGetPackedB32(a);
    173 
    174     int32_t bA = static_cast<int32_t>(SkGetPackedA32(b));
    175     int32_t bR = static_cast<int32_t>(SkGetPackedR32(b));
    176     int32_t bG = static_cast<int32_t>(SkGetPackedG32(b));
    177     int32_t bB = static_cast<int32_t>(SkGetPackedB32(b));
    178 
    179     return aA == bA &&
    180            SkAbs32(aR - bR) <= 1 &&
    181            SkAbs32(aG - bG) <= 1 &&
    182            SkAbs32(aB - bB) <= 1;
    183 }
    184 
    185 static bool check_write(skiatest::Reporter* reporter, SkCanvas* canvas, const SkBitmap& bitmap,
    186                        int writeX, int writeY) {
    187     const SkImageInfo canvasInfo = canvas->imageInfo();
    188     size_t canvasRowBytes;
    189     const uint32_t* canvasPixels;
    190 
    191     // Can't use canvas->peekPixels(), as we are trying to look at GPU pixels sometimes as well.
    192     // At some point this will be unsupported, as we won't allow accessBitmap() to magically call
    193     // readPixels for the client.
    194     SkBitmap secretDevBitmap;
    195     canvas->readPixels(canvasInfo.bounds(), &secretDevBitmap);
    196 
    197     SkAutoLockPixels alp(secretDevBitmap);
    198     canvasRowBytes = secretDevBitmap.rowBytes();
    199     canvasPixels = static_cast<const uint32_t*>(secretDevBitmap.getPixels());
    200 
    201     if (nullptr == canvasPixels) {
    202         return false;
    203     }
    204 
    205     if (canvasInfo.width() != DEV_W ||
    206         canvasInfo.height() != DEV_H ||
    207         canvasInfo.colorType() != kN32_SkColorType) {
    208         return false;
    209     }
    210 
    211     const SkImageInfo bmInfo = bitmap.info();
    212 
    213     SkIRect writeRect = SkIRect::MakeXYWH(writeX, writeY, bitmap.width(), bitmap.height());
    214     for (int cy = 0; cy < DEV_H; ++cy) {
    215         for (int cx = 0; cx < DEV_W; ++cx) {
    216             SkPMColor canvasPixel = canvasPixels[cx];
    217             if (writeRect.contains(cx, cy)) {
    218                 int bx = cx - writeX;
    219                 int by = cy - writeY;
    220                 uint32_t bmpColor8888 = get_bitmap_color(bx, by, bitmap.width(),
    221                                                        bmInfo.colorType(), bmInfo.alphaType());
    222                 bool mul = (kUnpremul_SkAlphaType == bmInfo.alphaType());
    223                 SkPMColor bmpPMColor = convert_to_PMColor(bmInfo.colorType(), bmInfo.alphaType(),
    224                                                           bmpColor8888);
    225                 if (!check_pixel(bmpPMColor, canvasPixel, mul)) {
    226                     ERRORF(reporter, "Expected canvas pixel at %d, %d to be 0x%08x, got 0x%08x. "
    227                            "Write performed premul: %d", cx, cy, bmpPMColor, canvasPixel, mul);
    228                     return false;
    229                 }
    230             } else {
    231                 SkPMColor testColor = get_canvas_color(cx, cy);
    232                 if (canvasPixel != testColor) {
    233                     ERRORF(reporter, "Canvas pixel outside write rect at %d, %d changed."
    234                            " Should be 0x%08x, got 0x%08x. ", cx, cy, testColor, canvasPixel);
    235                     return false;
    236                 }
    237             }
    238         }
    239         if (cy != DEV_H -1) {
    240             const char* pad = reinterpret_cast<const char*>(canvasPixels + DEV_W);
    241             for (size_t px = 0; px < canvasRowBytes - 4 * DEV_W; ++px) {
    242                 bool check;
    243                 REPORTER_ASSERT(reporter, check = (pad[px] == static_cast<char>(DEV_PAD)));
    244                 if (!check) {
    245                     return false;
    246                 }
    247             }
    248         }
    249         canvasPixels += canvasRowBytes/4;
    250     }
    251 
    252     return true;
    253 }
    254 
    255 #include "SkMallocPixelRef.h"
    256 
    257 // This is a tricky pattern, because we have to setConfig+rowBytes AND specify
    258 // a custom pixelRef (which also has to specify its rowBytes), so we have to be
    259 // sure that the two rowBytes match (and the infos match).
    260 //
    261 static bool alloc_row_bytes(SkBitmap* bm, const SkImageInfo& info, size_t rowBytes) {
    262     if (!bm->setInfo(info, rowBytes)) {
    263         return false;
    264     }
    265     SkPixelRef* pr = SkMallocPixelRef::NewAllocate(info, rowBytes, nullptr);
    266     bm->setPixelRef(pr)->unref();
    267     return true;
    268 }
    269 
    270 static void free_pixels(void* pixels, void* ctx) {
    271     sk_free(pixels);
    272 }
    273 
    274 static bool setup_bitmap(SkBitmap* bm, SkColorType ct, SkAlphaType at, int w, int h, int tightRB) {
    275     size_t rowBytes = tightRB ? 0 : 4 * w + 60;
    276     SkImageInfo info = SkImageInfo::Make(w, h, ct, at);
    277     if (!alloc_row_bytes(bm, info, rowBytes)) {
    278         return false;
    279     }
    280     SkAutoLockPixels alp(*bm);
    281     for (int y = 0; y < h; ++y) {
    282         for (int x = 0; x < w; ++x) {
    283             *bm->getAddr32(x, y) = get_bitmap_color(x, y, w, ct, at);
    284         }
    285     }
    286     return true;
    287 }
    288 
    289 static void call_writepixels(SkCanvas* canvas) {
    290     const SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
    291     SkPMColor pixel = 0;
    292     canvas->writePixels(info, &pixel, sizeof(SkPMColor), 0, 0);
    293 }
    294 
    295 DEF_TEST(WritePixelsSurfaceGenID, reporter) {
    296     const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
    297     SkAutoTUnref<SkSurface> surface(SkSurface::NewRaster(info));
    298     uint32_t genID1 = surface->generationID();
    299     call_writepixels(surface->getCanvas());
    300     uint32_t genID2 = surface->generationID();
    301     REPORTER_ASSERT(reporter, genID1 != genID2);
    302 }
    303 
    304 static void test_write_pixels(skiatest::Reporter* reporter, SkSurface* surface) {
    305     const SkIRect testRects[] = {
    306         // entire thing
    307         DEV_RECT,
    308         // larger on all sides
    309         SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H + 10),
    310         // fully contained
    311         SkIRect::MakeLTRB(DEV_W / 4, DEV_H / 4, 3 * DEV_W / 4, 3 * DEV_H / 4),
    312         // outside top left
    313         SkIRect::MakeLTRB(-10, -10, -1, -1),
    314         // touching top left corner
    315         SkIRect::MakeLTRB(-10, -10, 0, 0),
    316         // overlapping top left corner
    317         SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H / 4),
    318         // overlapping top left and top right corners
    319         SkIRect::MakeLTRB(-10, -10, DEV_W  + 10, DEV_H / 4),
    320         // touching entire top edge
    321         SkIRect::MakeLTRB(-10, -10, DEV_W  + 10, 0),
    322         // overlapping top right corner
    323         SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W  + 10, DEV_H / 4),
    324         // contained in x, overlapping top edge
    325         SkIRect::MakeLTRB(DEV_W / 4, -10, 3 * DEV_W  / 4, DEV_H / 4),
    326         // outside top right corner
    327         SkIRect::MakeLTRB(DEV_W + 1, -10, DEV_W + 10, -1),
    328         // touching top right corner
    329         SkIRect::MakeLTRB(DEV_W, -10, DEV_W + 10, 0),
    330         // overlapping top left and bottom left corners
    331         SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H + 10),
    332         // touching entire left edge
    333         SkIRect::MakeLTRB(-10, -10, 0, DEV_H + 10),
    334         // overlapping bottom left corner
    335         SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W / 4, DEV_H + 10),
    336         // contained in y, overlapping left edge
    337         SkIRect::MakeLTRB(-10, DEV_H / 4, DEV_W / 4, 3 * DEV_H / 4),
    338         // outside bottom left corner
    339         SkIRect::MakeLTRB(-10, DEV_H + 1, -1, DEV_H + 10),
    340         // touching bottom left corner
    341         SkIRect::MakeLTRB(-10, DEV_H, 0, DEV_H + 10),
    342         // overlapping bottom left and bottom right corners
    343         SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
    344         // touching entire left edge
    345         SkIRect::MakeLTRB(0, DEV_H, DEV_W, DEV_H + 10),
    346         // overlapping bottom right corner
    347         SkIRect::MakeLTRB(3 * DEV_W / 4, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
    348         // overlapping top right and bottom right corners
    349         SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H + 10),
    350     };
    351 
    352     SkCanvas& canvas = *surface->getCanvas();
    353 
    354     static const struct {
    355         SkColorType fColorType;
    356         SkAlphaType fAlphaType;
    357     } gSrcConfigs[] = {
    358         { kRGBA_8888_SkColorType, kPremul_SkAlphaType },
    359         { kRGBA_8888_SkColorType, kUnpremul_SkAlphaType },
    360         { kBGRA_8888_SkColorType, kPremul_SkAlphaType },
    361         { kBGRA_8888_SkColorType, kUnpremul_SkAlphaType },
    362     };
    363     for (size_t r = 0; r < SK_ARRAY_COUNT(testRects); ++r) {
    364         const SkIRect& rect = testRects[r];
    365         for (int tightBmp = 0; tightBmp < 2; ++tightBmp) {
    366             for (size_t c = 0; c < SK_ARRAY_COUNT(gSrcConfigs); ++c) {
    367                 const SkColorType ct = gSrcConfigs[c].fColorType;
    368                 const SkAlphaType at = gSrcConfigs[c].fAlphaType;
    369 
    370                 fill_canvas(&canvas);
    371                 SkBitmap bmp;
    372                 REPORTER_ASSERT(reporter, setup_bitmap(&bmp, ct, at, rect.width(),
    373                                                        rect.height(), SkToBool(tightBmp)));
    374                 uint32_t idBefore = surface->generationID();
    375 
    376                 // sk_tool_utils::write_pixels(&canvas, bmp, rect.fLeft, rect.fTop, ct, at);
    377                 canvas.writePixels(bmp, rect.fLeft, rect.fTop);
    378 
    379                 uint32_t idAfter = surface->generationID();
    380                 REPORTER_ASSERT(reporter, check_write(reporter, &canvas, bmp,
    381                                                       rect.fLeft, rect.fTop));
    382 
    383                 // we should change the genID iff pixels were actually written.
    384                 SkIRect canvasRect = SkIRect::MakeSize(canvas.getDeviceSize());
    385                 SkIRect writeRect = SkIRect::MakeXYWH(rect.fLeft, rect.fTop,
    386                                                       bmp.width(), bmp.height());
    387                 bool intersects = SkIRect::Intersects(canvasRect, writeRect) ;
    388                 REPORTER_ASSERT(reporter, intersects == (idBefore != idAfter));
    389             }
    390         }
    391     }
    392 }
    393 DEF_TEST(WritePixels, reporter) {
    394     const SkImageInfo info = SkImageInfo::MakeN32Premul(DEV_W, DEV_H);
    395     for (auto& tightRowBytes : { true, false }) {
    396         const size_t rowBytes = tightRowBytes ? info.minRowBytes() : 4 * DEV_W + 100;
    397         const size_t size = info.getSafeSize(rowBytes);
    398         void* pixels = sk_malloc_throw(size);
    399         // if rowBytes isn't tight then set the padding to a known value
    400         if (!tightRowBytes) {
    401             memset(pixels, DEV_PAD, size);
    402         }
    403         SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterDirectReleaseProc(info, pixels, rowBytes, free_pixels, nullptr));
    404         test_write_pixels(reporter, surface);
    405     }
    406 }
    407 #if SK_SUPPORT_GPU
    408 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WritePixels_Gpu, reporter, context) {
    409     for (auto& origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin }) {
    410         GrSurfaceDesc desc;
    411         desc.fFlags = kRenderTarget_GrSurfaceFlag;
    412         desc.fWidth = DEV_W;
    413         desc.fHeight = DEV_H;
    414         desc.fConfig = kSkia8888_GrPixelConfig;
    415         desc.fOrigin = origin;
    416         SkAutoTUnref<GrTexture> texture(context->textureProvider()->createTexture(desc,
    417                                                                                   SkBudgeted::kNo));
    418         SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTargetDirect(texture->asRenderTarget()));
    419         test_write_pixels(reporter, surface);
    420     }
    421 }
    422 #endif
    423