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