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 "SkBitmapDevice.h"
      9 #include "SkCanvas.h"
     10 #include "SkColorPriv.h"
     11 #include "SkMathPriv.h"
     12 #include "SkRegion.h"
     13 #include "Test.h"
     14 #include "sk_tool_utils.h"
     15 
     16 #if SK_SUPPORT_GPU
     17 #include "GrContextFactory.h"
     18 #include "SkGpuDevice.h"
     19 #else
     20 class GrContext;
     21 class GrContextFactory;
     22 #endif
     23 
     24 static const int DEV_W = 100, DEV_H = 100;
     25 static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H);
     26 static const SkRect DEV_RECT_S = SkRect::MakeWH(DEV_W * SK_Scalar1,
     27                                                 DEV_H * SK_Scalar1);
     28 static const U8CPU DEV_PAD = 0xee;
     29 
     30 static SkPMColor getCanvasColor(int x, int y) {
     31     SkASSERT(x >= 0 && x < DEV_W);
     32     SkASSERT(y >= 0 && y < DEV_H);
     33 
     34     U8CPU r = x;
     35     U8CPU g = y;
     36     U8CPU b = 0xc;
     37 
     38     U8CPU a = 0x0;
     39     switch ((x+y) % 5) {
     40         case 0:
     41             a = 0xff;
     42             break;
     43         case 1:
     44             a = 0x80;
     45             break;
     46         case 2:
     47             a = 0xCC;
     48             break;
     49         case 3:
     50             a = 0x00;
     51             break;
     52         case 4:
     53             a = 0x01;
     54             break;
     55     }
     56     return SkPremultiplyARGBInline(a, r, g, b);
     57 }
     58 
     59 // assumes any premu/.unpremul has been applied
     60 static uint32_t packColorType(SkColorType ct, U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
     61     uint32_t r32;
     62     uint8_t* result = reinterpret_cast<uint8_t*>(&r32);
     63     switch (ct) {
     64         case kBGRA_8888_SkColorType:
     65             result[0] = b;
     66             result[1] = g;
     67             result[2] = r;
     68             result[3] = a;
     69             break;
     70         case kRGBA_8888_SkColorType:
     71             result[0] = r;
     72             result[1] = g;
     73             result[2] = b;
     74             result[3] = a;
     75             break;
     76         default:
     77             SkASSERT(0);
     78             return 0;
     79     }
     80     return r32;
     81 }
     82 
     83 static uint32_t getBitmapColor(int x, int y, int w, SkColorType ct, SkAlphaType at) {
     84     int n = y * w + x;
     85     U8CPU b = n & 0xff;
     86     U8CPU g = (n >> 8) & 0xff;
     87     U8CPU r = (n >> 16) & 0xff;
     88     U8CPU a = 0;
     89     switch ((x+y) % 5) {
     90         case 4:
     91             a = 0xff;
     92             break;
     93         case 3:
     94             a = 0x80;
     95             break;
     96         case 2:
     97             a = 0xCC;
     98             break;
     99         case 1:
    100             a = 0x01;
    101             break;
    102         case 0:
    103             a = 0x00;
    104             break;
    105     }
    106     if (kPremul_SkAlphaType == at) {
    107         r = SkMulDiv255Ceiling(r, a);
    108         g = SkMulDiv255Ceiling(g, a);
    109         b = SkMulDiv255Ceiling(b, a);
    110     }
    111     return packColorType(ct, a, r, g , b);
    112 }
    113 
    114 static void fillCanvas(SkCanvas* canvas) {
    115     SkBitmap bmp;
    116     if (bmp.isNull()) {
    117         SkDEBUGCODE(bool alloc = ) bmp.allocN32Pixels(DEV_W, DEV_H);
    118         SkASSERT(alloc);
    119         for (int y = 0; y < DEV_H; ++y) {
    120             for (int x = 0; x < DEV_W; ++x) {
    121                 *bmp.getAddr32(x, y) = getCanvasColor(x, y);
    122             }
    123         }
    124     }
    125     canvas->save();
    126     canvas->setMatrix(SkMatrix::I());
    127     canvas->clipRect(DEV_RECT_S, SkRegion::kReplace_Op);
    128     SkPaint paint;
    129     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
    130     canvas->drawBitmap(bmp, 0, 0, &paint);
    131     canvas->restore();
    132 }
    133 
    134 /**
    135  *  Lucky for us, alpha is always in the same spot (SK_A32_SHIFT), for both RGBA and BGRA.
    136  *  Thus this routine doesn't need to know the exact colortype
    137  */
    138 static uint32_t premul(uint32_t color) {
    139     unsigned a = SkGetPackedA32(color);
    140     // these next three are not necessarily r,g,b in that order, but they are r,g,b in some order.
    141     unsigned c0 = SkGetPackedR32(color);
    142     unsigned c1 = SkGetPackedG32(color);
    143     unsigned c2 = SkGetPackedB32(color);
    144     c0 = SkMulDiv255Ceiling(c0, a);
    145     c1 = SkMulDiv255Ceiling(c1, a);
    146     c2 = SkMulDiv255Ceiling(c2, a);
    147     return SkPackARGB32NoCheck(a, c0, c1, c2);
    148 }
    149 
    150 static SkPMColor convert_to_PMColor(SkColorType ct, SkAlphaType at, uint32_t color) {
    151     if (kUnpremul_SkAlphaType == at) {
    152         color = premul(color);
    153     }
    154     switch (ct) {
    155         case kRGBA_8888_SkColorType:
    156             color = SkSwizzle_RGBA_to_PMColor(color);
    157             break;
    158         case kBGRA_8888_SkColorType:
    159             color = SkSwizzle_BGRA_to_PMColor(color);
    160             break;
    161         default:
    162             SkASSERT(0);
    163             break;
    164     }
    165     return color;
    166 }
    167 
    168 static bool checkPixel(SkPMColor a, SkPMColor b, bool didPremulConversion) {
    169     if (!didPremulConversion) {
    170         return a == b;
    171     }
    172     int32_t aA = static_cast<int32_t>(SkGetPackedA32(a));
    173     int32_t aR = static_cast<int32_t>(SkGetPackedR32(a));
    174     int32_t aG = static_cast<int32_t>(SkGetPackedG32(a));
    175     int32_t aB = SkGetPackedB32(a);
    176 
    177     int32_t bA = static_cast<int32_t>(SkGetPackedA32(b));
    178     int32_t bR = static_cast<int32_t>(SkGetPackedR32(b));
    179     int32_t bG = static_cast<int32_t>(SkGetPackedG32(b));
    180     int32_t bB = static_cast<int32_t>(SkGetPackedB32(b));
    181 
    182     return aA == bA &&
    183            SkAbs32(aR - bR) <= 1 &&
    184            SkAbs32(aG - bG) <= 1 &&
    185            SkAbs32(aB - bB) <= 1;
    186 }
    187 
    188 static bool checkWrite(skiatest::Reporter* reporter, SkCanvas* canvas, const SkBitmap& bitmap,
    189                        int writeX, int writeY) {
    190     SkImageInfo canvasInfo;
    191     size_t canvasRowBytes;
    192     const uint32_t* canvasPixels;
    193 
    194     // Can't use canvas->peekPixels(), as we are trying to look at GPU pixels sometimes as well.
    195     // At some point this will be unsupported, as we won't allow accessBitmap() to magically call
    196     // readPixels for the client.
    197     SkBitmap secretDevBitmap;
    198     {
    199         SkBaseDevice* dev = canvas->getDevice();
    200         if (!dev) {
    201             return false;
    202         }
    203         secretDevBitmap = dev->accessBitmap(false);
    204     }
    205     SkAutoLockPixels alp(secretDevBitmap);
    206     canvasInfo = secretDevBitmap.info();
    207     canvasRowBytes = secretDevBitmap.rowBytes();
    208     canvasPixels = static_cast<const uint32_t*>(secretDevBitmap.getPixels());
    209 
    210     if (NULL == canvasPixels) {
    211         return false;
    212     }
    213 
    214     if (canvasInfo.width() != DEV_W ||
    215         canvasInfo.height() != DEV_H ||
    216         canvasInfo.colorType() != kN32_SkColorType) {
    217         return false;
    218     }
    219 
    220     const SkImageInfo bmInfo = bitmap.info();
    221 
    222     SkIRect writeRect = SkIRect::MakeXYWH(writeX, writeY, bitmap.width(), bitmap.height());
    223     for (int cy = 0; cy < DEV_H; ++cy) {
    224         for (int cx = 0; cx < DEV_W; ++cx) {
    225             SkPMColor canvasPixel = canvasPixels[cx];
    226             if (writeRect.contains(cx, cy)) {
    227                 int bx = cx - writeX;
    228                 int by = cy - writeY;
    229                 uint32_t bmpColor8888 = getBitmapColor(bx, by, bitmap.width(),
    230                                                        bmInfo.colorType(), bmInfo.alphaType());
    231                 bool mul = (kUnpremul_SkAlphaType == bmInfo.alphaType());
    232                 SkPMColor bmpPMColor = convert_to_PMColor(bmInfo.colorType(), bmInfo.alphaType(),
    233                                                           bmpColor8888);
    234                 bool check = checkPixel(bmpPMColor, canvasPixel, mul);
    235                 REPORTER_ASSERT(reporter, check);
    236                 if (!check) {
    237                     return false;
    238                 }
    239             } else {
    240                 bool check;
    241                 SkPMColor testColor = getCanvasColor(cx, cy);
    242                 REPORTER_ASSERT(reporter, check = (canvasPixel == testColor));
    243                 if (!check) {
    244                     return false;
    245                 }
    246             }
    247         }
    248         if (cy != DEV_H -1) {
    249             const char* pad = reinterpret_cast<const char*>(canvasPixels + DEV_W);
    250             for (size_t px = 0; px < canvasRowBytes - 4 * DEV_W; ++px) {
    251                 bool check;
    252                 REPORTER_ASSERT(reporter, check = (pad[px] == static_cast<char>(DEV_PAD)));
    253                 if (!check) {
    254                     return false;
    255                 }
    256             }
    257         }
    258         canvasPixels += canvasRowBytes/4;
    259     }
    260 
    261     return true;
    262 }
    263 
    264 enum DevType {
    265     kRaster_DevType,
    266 #if SK_SUPPORT_GPU
    267     kGpu_BottomLeft_DevType,
    268     kGpu_TopLeft_DevType,
    269 #endif
    270 };
    271 
    272 struct CanvasConfig {
    273     DevType fDevType;
    274     bool fTightRowBytes;
    275 };
    276 
    277 static const CanvasConfig gCanvasConfigs[] = {
    278     {kRaster_DevType, true},
    279     {kRaster_DevType, false},
    280 #if SK_SUPPORT_GPU
    281     {kGpu_BottomLeft_DevType, true}, // row bytes has no meaning on gpu devices
    282     {kGpu_TopLeft_DevType, true}, // row bytes has no meaning on gpu devices
    283 #endif
    284 };
    285 
    286 #include "SkMallocPixelRef.h"
    287 
    288 // This is a tricky pattern, because we have to setConfig+rowBytes AND specify
    289 // a custom pixelRef (which also has to specify its rowBytes), so we have to be
    290 // sure that the two rowBytes match (and the infos match).
    291 //
    292 static bool allocRowBytes(SkBitmap* bm, const SkImageInfo& info, size_t rowBytes) {
    293     if (!bm->setInfo(info, rowBytes)) {
    294         return false;
    295     }
    296     SkPixelRef* pr = SkMallocPixelRef::NewAllocate(info, rowBytes, NULL);
    297     bm->setPixelRef(pr)->unref();
    298     return true;
    299 }
    300 
    301 static SkBaseDevice* createDevice(const CanvasConfig& c, GrContext* grCtx) {
    302     switch (c.fDevType) {
    303         case kRaster_DevType: {
    304             SkBitmap bmp;
    305             size_t rowBytes = c.fTightRowBytes ? 0 : 4 * DEV_W + 100;
    306             SkImageInfo info = SkImageInfo::MakeN32Premul(DEV_W, DEV_H);
    307             if (!allocRowBytes(&bmp, info, rowBytes)) {
    308                 sk_throw();
    309                 return NULL;
    310             }
    311             // if rowBytes isn't tight then set the padding to a known value
    312             if (rowBytes) {
    313                 SkAutoLockPixels alp(bmp);
    314                 // We'd just use memset here but GCC 4.8.1 throws up a bogus warning when we do.
    315                 for (size_t i = 0; i < bmp.getSafeSize(); i++) {
    316                     ((uint8_t*)bmp.getPixels())[i] = DEV_PAD;
    317                 }
    318             }
    319             return new SkBitmapDevice(bmp);
    320         }
    321 #if SK_SUPPORT_GPU
    322         case kGpu_BottomLeft_DevType:
    323         case kGpu_TopLeft_DevType:
    324             GrTextureDesc desc;
    325             desc.fFlags = kRenderTarget_GrTextureFlagBit;
    326             desc.fWidth = DEV_W;
    327             desc.fHeight = DEV_H;
    328             desc.fConfig = kSkia8888_GrPixelConfig;
    329             desc.fOrigin = kGpu_TopLeft_DevType == c.fDevType ?
    330                 kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
    331             GrAutoScratchTexture ast(grCtx, desc, GrContext::kExact_ScratchTexMatch);
    332             SkAutoTUnref<GrTexture> tex(ast.detach());
    333             return new SkGpuDevice(grCtx, tex);
    334 #endif
    335     }
    336     return NULL;
    337 }
    338 
    339 static bool setupBitmap(SkBitmap* bm, SkColorType ct, SkAlphaType at, int w, int h, int tightRB) {
    340     size_t rowBytes = tightRB ? 0 : 4 * w + 60;
    341     SkImageInfo info = SkImageInfo::Make(w, h, ct, at);
    342     if (!allocRowBytes(bm, info, rowBytes)) {
    343         return false;
    344     }
    345     SkAutoLockPixels alp(*bm);
    346     for (int y = 0; y < h; ++y) {
    347         for (int x = 0; x < w; ++x) {
    348             *bm->getAddr32(x, y) = getBitmapColor(x, y, w, ct, at);
    349         }
    350     }
    351     return true;
    352 }
    353 
    354 DEF_GPUTEST(WritePixels, reporter, factory) {
    355     SkCanvas canvas;
    356 
    357     const SkIRect testRects[] = {
    358         // entire thing
    359         DEV_RECT,
    360         // larger on all sides
    361         SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H + 10),
    362         // fully contained
    363         SkIRect::MakeLTRB(DEV_W / 4, DEV_H / 4, 3 * DEV_W / 4, 3 * DEV_H / 4),
    364         // outside top left
    365         SkIRect::MakeLTRB(-10, -10, -1, -1),
    366         // touching top left corner
    367         SkIRect::MakeLTRB(-10, -10, 0, 0),
    368         // overlapping top left corner
    369         SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H / 4),
    370         // overlapping top left and top right corners
    371         SkIRect::MakeLTRB(-10, -10, DEV_W  + 10, DEV_H / 4),
    372         // touching entire top edge
    373         SkIRect::MakeLTRB(-10, -10, DEV_W  + 10, 0),
    374         // overlapping top right corner
    375         SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W  + 10, DEV_H / 4),
    376         // contained in x, overlapping top edge
    377         SkIRect::MakeLTRB(DEV_W / 4, -10, 3 * DEV_W  / 4, DEV_H / 4),
    378         // outside top right corner
    379         SkIRect::MakeLTRB(DEV_W + 1, -10, DEV_W + 10, -1),
    380         // touching top right corner
    381         SkIRect::MakeLTRB(DEV_W, -10, DEV_W + 10, 0),
    382         // overlapping top left and bottom left corners
    383         SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H + 10),
    384         // touching entire left edge
    385         SkIRect::MakeLTRB(-10, -10, 0, DEV_H + 10),
    386         // overlapping bottom left corner
    387         SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W / 4, DEV_H + 10),
    388         // contained in y, overlapping left edge
    389         SkIRect::MakeLTRB(-10, DEV_H / 4, DEV_W / 4, 3 * DEV_H / 4),
    390         // outside bottom left corner
    391         SkIRect::MakeLTRB(-10, DEV_H + 1, -1, DEV_H + 10),
    392         // touching bottom left corner
    393         SkIRect::MakeLTRB(-10, DEV_H, 0, DEV_H + 10),
    394         // overlapping bottom left and bottom right corners
    395         SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
    396         // touching entire left edge
    397         SkIRect::MakeLTRB(0, DEV_H, DEV_W, DEV_H + 10),
    398         // overlapping bottom right corner
    399         SkIRect::MakeLTRB(3 * DEV_W / 4, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
    400         // overlapping top right and bottom right corners
    401         SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H + 10),
    402     };
    403 
    404     for (size_t i = 0; i < SK_ARRAY_COUNT(gCanvasConfigs); ++i) {
    405         int glCtxTypeCnt = 1;
    406 #if SK_SUPPORT_GPU
    407         bool isGPUDevice = kGpu_TopLeft_DevType == gCanvasConfigs[i].fDevType ||
    408                            kGpu_BottomLeft_DevType == gCanvasConfigs[i].fDevType;
    409         if (isGPUDevice) {
    410             glCtxTypeCnt = GrContextFactory::kGLContextTypeCnt;
    411         }
    412 #endif
    413         for (int glCtxType = 0; glCtxType < glCtxTypeCnt; ++glCtxType) {
    414             GrContext* context = NULL;
    415 #if SK_SUPPORT_GPU
    416             if (isGPUDevice) {
    417                 GrContextFactory::GLContextType type =
    418                     static_cast<GrContextFactory::GLContextType>(glCtxType);
    419                 if (!GrContextFactory::IsRenderingGLContext(type)) {
    420                     continue;
    421                 }
    422                 context = factory->get(type);
    423                 if (NULL == context) {
    424                     continue;
    425                 }
    426             }
    427 #endif
    428 
    429             SkAutoTUnref<SkBaseDevice> device(createDevice(gCanvasConfigs[i], context));
    430             SkCanvas canvas(device);
    431 
    432             static const struct {
    433                 SkColorType fColorType;
    434                 SkAlphaType fAlphaType;
    435             } gSrcConfigs[] = {
    436                 { kRGBA_8888_SkColorType, kPremul_SkAlphaType },
    437                 { kRGBA_8888_SkColorType, kUnpremul_SkAlphaType },
    438                 { kBGRA_8888_SkColorType, kPremul_SkAlphaType },
    439                 { kBGRA_8888_SkColorType, kUnpremul_SkAlphaType },
    440             };
    441             for (size_t r = 0; r < SK_ARRAY_COUNT(testRects); ++r) {
    442                 const SkIRect& rect = testRects[r];
    443                 for (int tightBmp = 0; tightBmp < 2; ++tightBmp) {
    444                     for (size_t c = 0; c < SK_ARRAY_COUNT(gSrcConfigs); ++c) {
    445                         const SkColorType ct = gSrcConfigs[c].fColorType;
    446                         const SkAlphaType at = gSrcConfigs[c].fAlphaType;
    447 
    448                         fillCanvas(&canvas);
    449                         SkBitmap bmp;
    450                         REPORTER_ASSERT(reporter, setupBitmap(&bmp, ct, at, rect.width(),
    451                                                               rect.height(), SkToBool(tightBmp)));
    452                         uint32_t idBefore = canvas.getDevice()->accessBitmap(false).getGenerationID();
    453 
    454                        // sk_tool_utils::write_pixels(&canvas, bmp, rect.fLeft, rect.fTop, ct, at);
    455                         canvas.writePixels(bmp, rect.fLeft, rect.fTop);
    456 
    457                         uint32_t idAfter = canvas.getDevice()->accessBitmap(false).getGenerationID();
    458                         REPORTER_ASSERT(reporter, checkWrite(reporter, &canvas, bmp, rect.fLeft, rect.fTop));
    459 
    460                         // we should change the genID iff pixels were actually written.
    461                         SkIRect canvasRect = SkIRect::MakeSize(canvas.getDeviceSize());
    462                         SkIRect writeRect = SkIRect::MakeXYWH(rect.fLeft, rect.fTop,
    463                                                               bmp.width(), bmp.height());
    464                         bool intersects = SkIRect::Intersects(canvasRect, writeRect) ;
    465                         REPORTER_ASSERT(reporter, intersects == (idBefore != idAfter));
    466                     }
    467                 }
    468             }
    469         }
    470     }
    471 }
    472