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