Home | History | Annotate | Download | only in tests
      1 
      2 /*
      3  * Copyright 2011 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 #include "Test.h"
     10 #include "SkCanvas.h"
     11 #include "SkColorPriv.h"
     12 #include "SkDevice.h"
     13 #include "SkMathPriv.h"
     14 #include "SkRegion.h"
     15 #if SK_SUPPORT_GPU
     16 #include "SkGpuDevice.h"
     17 #include "GrContextFactory.h"
     18 #else
     19 class GrContext;
     20 class GrContextFactory;
     21 #endif
     22 
     23 static const int DEV_W = 100, DEV_H = 100;
     24 static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H);
     25 static const SkRect DEV_RECT_S = SkRect::MakeWH(DEV_W * SK_Scalar1,
     26                                                 DEV_H * SK_Scalar1);
     27 static const U8CPU DEV_PAD = 0xee;
     28 
     29 namespace {
     30 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 bool config8888IsPremul(SkCanvas::Config8888 config8888) {
     60     switch (config8888) {
     61         case SkCanvas::kNative_Premul_Config8888:
     62         case SkCanvas::kBGRA_Premul_Config8888:
     63         case SkCanvas::kRGBA_Premul_Config8888:
     64             return true;
     65         case SkCanvas::kNative_Unpremul_Config8888:
     66         case SkCanvas::kBGRA_Unpremul_Config8888:
     67         case SkCanvas::kRGBA_Unpremul_Config8888:
     68             return false;
     69         default:
     70             SkASSERT(0);
     71             return false;
     72     }
     73 }
     74 
     75 // assumes any premu/.unpremul has been applied
     76 uint32_t packConfig8888(SkCanvas::Config8888 config8888,
     77                         U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
     78     uint32_t r32;
     79     uint8_t* result = reinterpret_cast<uint8_t*>(&r32);
     80     switch (config8888) {
     81         case SkCanvas::kNative_Premul_Config8888:
     82         case SkCanvas::kNative_Unpremul_Config8888:
     83             r32 = SkPackARGB32NoCheck(a, r, g, b);
     84             break;
     85         case SkCanvas::kBGRA_Premul_Config8888:
     86         case SkCanvas::kBGRA_Unpremul_Config8888:
     87             result[0] = b;
     88             result[1] = g;
     89             result[2] = r;
     90             result[3] = a;
     91             break;
     92         case SkCanvas::kRGBA_Premul_Config8888:
     93         case SkCanvas::kRGBA_Unpremul_Config8888:
     94             result[0] = r;
     95             result[1] = g;
     96             result[2] = b;
     97             result[3] = a;
     98             break;
     99         default:
    100             SkASSERT(0);
    101             return 0;
    102     }
    103     return r32;
    104 }
    105 
    106 uint32_t getBitmapColor(int x, int y, int w, SkCanvas::Config8888 config8888) {
    107     int n = y * w + x;
    108     U8CPU b = n & 0xff;
    109     U8CPU g = (n >> 8) & 0xff;
    110     U8CPU r = (n >> 16) & 0xff;
    111     U8CPU a = 0;
    112     switch ((x+y) % 5) {
    113         case 4:
    114             a = 0xff;
    115             break;
    116         case 3:
    117             a = 0x80;
    118             break;
    119         case 2:
    120             a = 0xCC;
    121             break;
    122         case 1:
    123             a = 0x01;
    124             break;
    125         case 0:
    126             a = 0x00;
    127             break;
    128     }
    129     if (config8888IsPremul(config8888)) {
    130         r = SkMulDiv255Ceiling(r, a);
    131         g = SkMulDiv255Ceiling(g, a);
    132         b = SkMulDiv255Ceiling(b, a);
    133     }
    134     return packConfig8888(config8888, a, r, g , b);
    135 }
    136 
    137 void fillCanvas(SkCanvas* canvas) {
    138     static SkBitmap bmp;
    139     if (bmp.isNull()) {
    140         bmp.setConfig(SkBitmap::kARGB_8888_Config, DEV_W, DEV_H);
    141         SkDEBUGCODE(bool alloc = ) bmp.allocPixels();
    142         SkASSERT(alloc);
    143         SkAutoLockPixels alp(bmp);
    144         intptr_t pixels = reinterpret_cast<intptr_t>(bmp.getPixels());
    145         for (int y = 0; y < DEV_H; ++y) {
    146             for (int x = 0; x < DEV_W; ++x) {
    147                 SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bmp.rowBytes() + x * bmp.bytesPerPixel());
    148                 *pixel = getCanvasColor(x, y);
    149             }
    150         }
    151     }
    152     canvas->save();
    153     canvas->setMatrix(SkMatrix::I());
    154     canvas->clipRect(DEV_RECT_S, SkRegion::kReplace_Op);
    155     SkPaint paint;
    156     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
    157     canvas->drawBitmap(bmp, 0, 0, &paint);
    158     canvas->restore();
    159 }
    160 
    161 SkPMColor convertConfig8888ToPMColor(SkCanvas::Config8888 config8888,
    162                                      uint32_t color,
    163                                      bool* premul) {
    164     const uint8_t* c = reinterpret_cast<uint8_t*>(&color);
    165     U8CPU a,r,g,b;
    166     *premul = false;
    167     switch (config8888) {
    168         case SkCanvas::kNative_Premul_Config8888:
    169             return color;
    170         case SkCanvas::kNative_Unpremul_Config8888:
    171             *premul = true;
    172             a = SkGetPackedA32(color);
    173             r = SkGetPackedR32(color);
    174             g = SkGetPackedG32(color);
    175             b = SkGetPackedB32(color);
    176             break;
    177         case SkCanvas::kBGRA_Unpremul_Config8888:
    178             *premul = true; // fallthru
    179         case SkCanvas::kBGRA_Premul_Config8888:
    180             a = static_cast<U8CPU>(c[3]);
    181             r = static_cast<U8CPU>(c[2]);
    182             g = static_cast<U8CPU>(c[1]);
    183             b = static_cast<U8CPU>(c[0]);
    184             break;
    185         case SkCanvas::kRGBA_Unpremul_Config8888:
    186             *premul = true; // fallthru
    187         case SkCanvas::kRGBA_Premul_Config8888:
    188             a = static_cast<U8CPU>(c[3]);
    189             r = static_cast<U8CPU>(c[0]);
    190             g = static_cast<U8CPU>(c[1]);
    191             b = static_cast<U8CPU>(c[2]);
    192             break;
    193         default:
    194             SkDEBUGFAIL("Unexpected Config8888");
    195             return 0;
    196     }
    197     if (*premul) {
    198         r = SkMulDiv255Ceiling(r, a);
    199         g = SkMulDiv255Ceiling(g, a);
    200         b = SkMulDiv255Ceiling(b, a);
    201     }
    202     return SkPackARGB32(a, r, g, b);
    203 }
    204 
    205 bool checkPixel(SkPMColor a, SkPMColor b, bool didPremulConversion) {
    206     if (!didPremulConversion) {
    207         return a == b;
    208     }
    209     int32_t aA = static_cast<int32_t>(SkGetPackedA32(a));
    210     int32_t aR = static_cast<int32_t>(SkGetPackedR32(a));
    211     int32_t aG = static_cast<int32_t>(SkGetPackedG32(a));
    212     int32_t aB = SkGetPackedB32(a);
    213 
    214     int32_t bA = static_cast<int32_t>(SkGetPackedA32(b));
    215     int32_t bR = static_cast<int32_t>(SkGetPackedR32(b));
    216     int32_t bG = static_cast<int32_t>(SkGetPackedG32(b));
    217     int32_t bB = static_cast<int32_t>(SkGetPackedB32(b));
    218 
    219     return aA == bA &&
    220            SkAbs32(aR - bR) <= 1 &&
    221            SkAbs32(aG - bG) <= 1 &&
    222            SkAbs32(aB - bB) <= 1;
    223 }
    224 
    225 bool checkWrite(skiatest::Reporter* reporter,
    226                 SkCanvas* canvas,
    227                 const SkBitmap& bitmap,
    228                 int writeX, int writeY,
    229                 SkCanvas::Config8888 config8888) {
    230     SkDevice* dev = canvas->getDevice();
    231     if (!dev) {
    232         return false;
    233     }
    234     SkBitmap devBmp = dev->accessBitmap(false);
    235     if (devBmp.width() != DEV_W ||
    236         devBmp.height() != DEV_H ||
    237         devBmp.config() != SkBitmap::kARGB_8888_Config ||
    238         devBmp.isNull()) {
    239         return false;
    240     }
    241     SkAutoLockPixels alp(devBmp);
    242 
    243     intptr_t canvasPixels = reinterpret_cast<intptr_t>(devBmp.getPixels());
    244     size_t canvasRowBytes = devBmp.rowBytes();
    245     SkIRect writeRect = SkIRect::MakeXYWH(writeX, writeY, bitmap.width(), bitmap.height());
    246     for (int cy = 0; cy < DEV_H; ++cy) {
    247         const SkPMColor* canvasRow = reinterpret_cast<const SkPMColor*>(canvasPixels);
    248         for (int cx = 0; cx < DEV_W; ++cx) {
    249             SkPMColor canvasPixel = canvasRow[cx];
    250             if (writeRect.contains(cx, cy)) {
    251                 int bx = cx - writeX;
    252                 int by = cy - writeY;
    253                 uint32_t bmpColor8888 = getBitmapColor(bx, by, bitmap.width(), config8888);
    254                 bool mul;
    255                 SkPMColor bmpPMColor = convertConfig8888ToPMColor(config8888, bmpColor8888, &mul);
    256                 bool check;
    257                 REPORTER_ASSERT(reporter, check = checkPixel(bmpPMColor, canvasPixel, mul));
    258                 if (!check) {
    259                     return false;
    260                 }
    261             } else {
    262                 bool check;
    263                 SkPMColor testColor = getCanvasColor(cx, cy);
    264                 REPORTER_ASSERT(reporter, check = (canvasPixel == testColor));
    265                 if (!check) {
    266                     return false;
    267                 }
    268             }
    269         }
    270         if (cy != DEV_H -1) {
    271             const char* pad = reinterpret_cast<const char*>(canvasPixels + 4 * DEV_W);
    272             for (size_t px = 0; px < canvasRowBytes - 4 * DEV_W; ++px) {
    273                 bool check;
    274                 REPORTER_ASSERT(reporter, check = (pad[px] == static_cast<char>(DEV_PAD)));
    275                 if (!check) {
    276                     return false;
    277                 }
    278             }
    279         }
    280         canvasPixels += canvasRowBytes;
    281     }
    282 
    283     return true;
    284 }
    285 
    286 enum DevType {
    287     kRaster_DevType,
    288 #if SK_SUPPORT_GPU
    289     kGpu_BottomLeft_DevType,
    290     kGpu_TopLeft_DevType,
    291 #endif
    292 };
    293 
    294 struct CanvasConfig {
    295     DevType fDevType;
    296     bool fTightRowBytes;
    297 };
    298 
    299 static const CanvasConfig gCanvasConfigs[] = {
    300     {kRaster_DevType, true},
    301     {kRaster_DevType, false},
    302 #if SK_SUPPORT_GPU && defined(SK_SCALAR_IS_FLOAT)
    303     {kGpu_BottomLeft_DevType, true}, // row bytes has no meaning on gpu devices
    304     {kGpu_TopLeft_DevType, true}, // row bytes has no meaning on gpu devices
    305 #endif
    306 };
    307 
    308 SkDevice* createDevice(const CanvasConfig& c, GrContext* grCtx) {
    309     switch (c.fDevType) {
    310         case kRaster_DevType: {
    311             SkBitmap bmp;
    312             size_t rowBytes = c.fTightRowBytes ? 0 : 4 * DEV_W + 100;
    313             bmp.setConfig(SkBitmap::kARGB_8888_Config, DEV_W, DEV_H, rowBytes);
    314             if (!bmp.allocPixels()) {
    315                 sk_throw();
    316                 return NULL;
    317             }
    318             // if rowBytes isn't tight then set the padding to a known value
    319             if (rowBytes) {
    320                 SkAutoLockPixels alp(bmp);
    321                 memset(bmp.getPixels(), DEV_PAD, bmp.getSafeSize());
    322             }
    323             return new SkDevice(bmp);
    324         }
    325 #if SK_SUPPORT_GPU
    326         case kGpu_BottomLeft_DevType:
    327         case kGpu_TopLeft_DevType:
    328             GrTextureDesc desc;
    329             desc.fFlags = kRenderTarget_GrTextureFlagBit;
    330             desc.fWidth = DEV_W;
    331             desc.fHeight = DEV_H;
    332             desc.fConfig = kSkia8888_GrPixelConfig;
    333             desc.fOrigin = kGpu_TopLeft_DevType == c.fDevType ?
    334                 kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
    335             GrAutoScratchTexture ast(grCtx, desc, GrContext::kExact_ScratchTexMatch);
    336             SkAutoTUnref<GrTexture> tex(ast.detach());
    337             return new SkGpuDevice(grCtx, tex);
    338 #endif
    339     }
    340     return NULL;
    341 }
    342 
    343 bool setupBitmap(SkBitmap* bitmap,
    344               SkCanvas::Config8888 config8888,
    345               int w, int h,
    346               bool tightRowBytes) {
    347     size_t rowBytes = tightRowBytes ? 0 : 4 * w + 60;
    348     bitmap->setConfig(SkBitmap::kARGB_8888_Config, w, h, rowBytes);
    349     if (!bitmap->allocPixels()) {
    350         return false;
    351     }
    352     SkAutoLockPixels alp(*bitmap);
    353     intptr_t pixels = reinterpret_cast<intptr_t>(bitmap->getPixels());
    354     for (int y = 0; y < h; ++y) {
    355         for (int x = 0; x < w; ++x) {
    356             uint32_t* pixel = reinterpret_cast<uint32_t*>(pixels + y * bitmap->rowBytes() + x * 4);
    357             *pixel = getBitmapColor(x, y, w, config8888);
    358         }
    359     }
    360     return true;
    361 }
    362 
    363 void WritePixelsTest(skiatest::Reporter* reporter, GrContextFactory* factory) {
    364     SkCanvas canvas;
    365 
    366     const SkIRect testRects[] = {
    367         // entire thing
    368         DEV_RECT,
    369         // larger on all sides
    370         SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H + 10),
    371         // fully contained
    372         SkIRect::MakeLTRB(DEV_W / 4, DEV_H / 4, 3 * DEV_W / 4, 3 * DEV_H / 4),
    373         // outside top left
    374         SkIRect::MakeLTRB(-10, -10, -1, -1),
    375         // touching top left corner
    376         SkIRect::MakeLTRB(-10, -10, 0, 0),
    377         // overlapping top left corner
    378         SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H / 4),
    379         // overlapping top left and top right corners
    380         SkIRect::MakeLTRB(-10, -10, DEV_W  + 10, DEV_H / 4),
    381         // touching entire top edge
    382         SkIRect::MakeLTRB(-10, -10, DEV_W  + 10, 0),
    383         // overlapping top right corner
    384         SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W  + 10, DEV_H / 4),
    385         // contained in x, overlapping top edge
    386         SkIRect::MakeLTRB(DEV_W / 4, -10, 3 * DEV_W  / 4, DEV_H / 4),
    387         // outside top right corner
    388         SkIRect::MakeLTRB(DEV_W + 1, -10, DEV_W + 10, -1),
    389         // touching top right corner
    390         SkIRect::MakeLTRB(DEV_W, -10, DEV_W + 10, 0),
    391         // overlapping top left and bottom left corners
    392         SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H + 10),
    393         // touching entire left edge
    394         SkIRect::MakeLTRB(-10, -10, 0, DEV_H + 10),
    395         // overlapping bottom left corner
    396         SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W / 4, DEV_H + 10),
    397         // contained in y, overlapping left edge
    398         SkIRect::MakeLTRB(-10, DEV_H / 4, DEV_W / 4, 3 * DEV_H / 4),
    399         // outside bottom left corner
    400         SkIRect::MakeLTRB(-10, DEV_H + 1, -1, DEV_H + 10),
    401         // touching bottom left corner
    402         SkIRect::MakeLTRB(-10, DEV_H, 0, DEV_H + 10),
    403         // overlapping bottom left and bottom right corners
    404         SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
    405         // touching entire left edge
    406         SkIRect::MakeLTRB(0, DEV_H, DEV_W, DEV_H + 10),
    407         // overlapping bottom right corner
    408         SkIRect::MakeLTRB(3 * DEV_W / 4, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
    409         // overlapping top right and bottom right corners
    410         SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H + 10),
    411     };
    412 
    413     for (size_t i = 0; i < SK_ARRAY_COUNT(gCanvasConfigs); ++i) {
    414         int glCtxTypeCnt = 1;
    415 #if SK_SUPPORT_GPU
    416         bool isGPUDevice = kGpu_TopLeft_DevType == gCanvasConfigs[i].fDevType ||
    417                            kGpu_BottomLeft_DevType == gCanvasConfigs[i].fDevType;
    418         if (isGPUDevice) {
    419             glCtxTypeCnt = GrContextFactory::kGLContextTypeCnt;
    420         }
    421 #endif
    422         for (int glCtxType = 0; glCtxType < glCtxTypeCnt; ++glCtxType) {
    423             GrContext* context = NULL;
    424 #if SK_SUPPORT_GPU
    425             if (isGPUDevice) {
    426                 GrContextFactory::GLContextType type =
    427                     static_cast<GrContextFactory::GLContextType>(glCtxType);
    428                 if (!GrContextFactory::IsRenderingGLContext(type)) {
    429                     continue;
    430                 }
    431                 context = factory->get(type);
    432                 if (NULL == context) {
    433                     continue;
    434                 }
    435             }
    436 #endif
    437 
    438             SkAutoTUnref<SkDevice> device(createDevice(gCanvasConfigs[i], context));
    439             SkCanvas canvas(device);
    440 
    441             static const SkCanvas::Config8888 gSrcConfigs[] = {
    442                 SkCanvas::kNative_Premul_Config8888,
    443                 SkCanvas::kNative_Unpremul_Config8888,
    444                 SkCanvas::kBGRA_Premul_Config8888,
    445                 SkCanvas::kBGRA_Unpremul_Config8888,
    446                 SkCanvas::kRGBA_Premul_Config8888,
    447                 SkCanvas::kRGBA_Unpremul_Config8888,
    448             };
    449             for (size_t r = 0; r < SK_ARRAY_COUNT(testRects); ++r) {
    450                 const SkIRect& rect = testRects[r];
    451                 for (int tightBmp = 0; tightBmp < 2; ++tightBmp) {
    452                     for (size_t c = 0; c < SK_ARRAY_COUNT(gSrcConfigs); ++c) {
    453                         fillCanvas(&canvas);
    454                         SkCanvas::Config8888 config8888 = gSrcConfigs[c];
    455                         SkBitmap bmp;
    456                         REPORTER_ASSERT(reporter, setupBitmap(&bmp, config8888, rect.width(), rect.height(), SkToBool(tightBmp)));
    457                         uint32_t idBefore = canvas.getDevice()->accessBitmap(false).getGenerationID();
    458                         canvas.writePixels(bmp, rect.fLeft, rect.fTop, config8888);
    459                         uint32_t idAfter = canvas.getDevice()->accessBitmap(false).getGenerationID();
    460                         REPORTER_ASSERT(reporter, checkWrite(reporter, &canvas, bmp, rect.fLeft, rect.fTop, config8888));
    461 
    462                         // we should change the genID iff pixels were actually written.
    463                         SkIRect canvasRect = SkIRect::MakeSize(canvas.getDeviceSize());
    464                         SkIRect writeRect = SkIRect::MakeXYWH(rect.fLeft, rect.fTop,
    465                                                               bmp.width(), bmp.height());
    466                         bool intersects = SkIRect::Intersects(canvasRect, writeRect) ;
    467                         REPORTER_ASSERT(reporter, intersects == (idBefore != idAfter));
    468                     }
    469                 }
    470             }
    471         }
    472     }
    473 }
    474 }
    475 
    476 #include "TestClassDef.h"
    477 DEFINE_GPUTESTCLASS("WritePixels", WritePixelsTestClass, WritePixelsTest)
    478