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 "SkColorData.h" 10 #include "SkMathPriv.h" 11 #include "SkSurface.h" 12 #include "Test.h" 13 #include "sk_tool_utils.h" 14 15 #if SK_SUPPORT_GPU 16 #include "GrBackendSurface.h" 17 #include "GrContext.h" 18 #include "GrContextPriv.h" 19 #include "GrGpu.h" 20 #include "GrProxyProvider.h" 21 #include "GrTest.h" 22 #endif 23 24 #include <initializer_list> 25 26 static const int DEV_W = 100, DEV_H = 100; 27 static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H); 28 static const SkRect DEV_RECT_S = SkRect::MakeWH(DEV_W * SK_Scalar1, 29 DEV_H * SK_Scalar1); 30 static const U8CPU DEV_PAD = 0xee; 31 32 static SkPMColor get_canvas_color(int x, int y) { 33 SkASSERT(x >= 0 && x < DEV_W); 34 SkASSERT(y >= 0 && y < DEV_H); 35 36 U8CPU r = x; 37 U8CPU g = y; 38 U8CPU b = 0xc; 39 40 U8CPU a = 0x0; 41 switch ((x+y) % 5) { 42 case 0: 43 a = 0xff; 44 break; 45 case 1: 46 a = 0x80; 47 break; 48 case 2: 49 a = 0xCC; 50 break; 51 case 3: 52 a = 0x00; 53 break; 54 case 4: 55 a = 0x01; 56 break; 57 } 58 return SkPremultiplyARGBInline(a, r, g, b); 59 } 60 61 // assumes any premu/.unpremul has been applied 62 static uint32_t pack_color_type(SkColorType ct, U8CPU a, U8CPU r, U8CPU g, U8CPU b) { 63 uint32_t r32; 64 uint8_t* result = reinterpret_cast<uint8_t*>(&r32); 65 switch (ct) { 66 case kBGRA_8888_SkColorType: 67 result[0] = b; 68 result[1] = g; 69 result[2] = r; 70 result[3] = a; 71 break; 72 case kRGBA_8888_SkColorType: 73 result[0] = r; 74 result[1] = g; 75 result[2] = b; 76 result[3] = a; 77 break; 78 default: 79 SkASSERT(0); 80 return 0; 81 } 82 return r32; 83 } 84 85 static uint32_t get_bitmap_color(int x, int y, int w, SkColorType ct, SkAlphaType at) { 86 int n = y * w + x; 87 U8CPU b = n & 0xff; 88 U8CPU g = (n >> 8) & 0xff; 89 U8CPU r = (n >> 16) & 0xff; 90 U8CPU a = 0; 91 switch ((x+y) % 5) { 92 case 4: 93 a = 0xff; 94 break; 95 case 3: 96 a = 0x80; 97 break; 98 case 2: 99 a = 0xCC; 100 break; 101 case 1: 102 a = 0x01; 103 break; 104 case 0: 105 a = 0x00; 106 break; 107 } 108 if (kPremul_SkAlphaType == at) { 109 r = SkMulDiv255Ceiling(r, a); 110 g = SkMulDiv255Ceiling(g, a); 111 b = SkMulDiv255Ceiling(b, a); 112 } 113 return pack_color_type(ct, a, r, g , b); 114 } 115 116 static void fill_canvas(SkCanvas* canvas) { 117 SkBitmap bmp; 118 if (bmp.isNull()) { 119 bmp.allocN32Pixels(DEV_W, DEV_H); 120 for (int y = 0; y < DEV_H; ++y) { 121 for (int x = 0; x < DEV_W; ++x) { 122 *bmp.getAddr32(x, y) = get_canvas_color(x, y); 123 } 124 } 125 } 126 canvas->save(); 127 canvas->setMatrix(SkMatrix::I()); 128 canvas->clipRect(DEV_RECT_S, kReplace_SkClipOp); 129 SkPaint paint; 130 paint.setBlendMode(SkBlendMode::kSrc); 131 canvas->drawBitmap(bmp, 0, 0, &paint); 132 canvas->restore(); 133 } 134 135 /** 136 * Lucky for us, alpha is always in the same spot (SK_A32_SHIFT), for both RGBA and BGRA. 137 * Thus this routine doesn't need to know the exact colortype 138 */ 139 static uint32_t premul(uint32_t color) { 140 unsigned a = SkGetPackedA32(color); 141 // these next three are not necessarily r,g,b in that order, but they are r,g,b in some order. 142 unsigned c0 = SkGetPackedR32(color); 143 unsigned c1 = SkGetPackedG32(color); 144 unsigned c2 = SkGetPackedB32(color); 145 c0 = SkMulDiv255Ceiling(c0, a); 146 c1 = SkMulDiv255Ceiling(c1, a); 147 c2 = SkMulDiv255Ceiling(c2, a); 148 return SkPackARGB32NoCheck(a, c0, c1, c2); 149 } 150 151 static SkPMColor convert_to_PMColor(SkColorType ct, SkAlphaType at, uint32_t color) { 152 if (kUnpremul_SkAlphaType == at) { 153 color = premul(color); 154 } 155 switch (ct) { 156 case kRGBA_8888_SkColorType: 157 color = SkSwizzle_RGBA_to_PMColor(color); 158 break; 159 case kBGRA_8888_SkColorType: 160 color = SkSwizzle_BGRA_to_PMColor(color); 161 break; 162 default: 163 SkASSERT(0); 164 break; 165 } 166 return color; 167 } 168 169 static bool check_pixel(SkPMColor a, SkPMColor b, bool didPremulConversion) { 170 if (!didPremulConversion) { 171 return a == b; 172 } 173 int32_t aA = static_cast<int32_t>(SkGetPackedA32(a)); 174 int32_t aR = static_cast<int32_t>(SkGetPackedR32(a)); 175 int32_t aG = static_cast<int32_t>(SkGetPackedG32(a)); 176 int32_t aB = SkGetPackedB32(a); 177 178 int32_t bA = static_cast<int32_t>(SkGetPackedA32(b)); 179 int32_t bR = static_cast<int32_t>(SkGetPackedR32(b)); 180 int32_t bG = static_cast<int32_t>(SkGetPackedG32(b)); 181 int32_t bB = static_cast<int32_t>(SkGetPackedB32(b)); 182 183 return aA == bA && 184 SkAbs32(aR - bR) <= 1 && 185 SkAbs32(aG - bG) <= 1 && 186 SkAbs32(aB - bB) <= 1; 187 } 188 189 static bool check_write(skiatest::Reporter* reporter, SkSurface* surf, const SkBitmap& bitmap, 190 int writeX, int writeY) { 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 secretDevBitmap.allocN32Pixels(surf->width(), surf->height()); 199 if (!surf->readPixels(secretDevBitmap, 0, 0)) { 200 return false; 201 } 202 203 canvasRowBytes = secretDevBitmap.rowBytes(); 204 canvasPixels = static_cast<const uint32_t*>(secretDevBitmap.getPixels()); 205 206 if (nullptr == canvasPixels) { 207 return false; 208 } 209 210 if (surf->width() != DEV_W || surf->height() != DEV_H) { 211 return false; 212 } 213 214 const SkImageInfo bmInfo = bitmap.info(); 215 216 SkIRect writeRect = SkIRect::MakeXYWH(writeX, writeY, bitmap.width(), bitmap.height()); 217 for (int cy = 0; cy < DEV_H; ++cy) { 218 for (int cx = 0; cx < DEV_W; ++cx) { 219 SkPMColor canvasPixel = canvasPixels[cx]; 220 if (writeRect.contains(cx, cy)) { 221 int bx = cx - writeX; 222 int by = cy - writeY; 223 uint32_t bmpColor8888 = get_bitmap_color(bx, by, bitmap.width(), 224 bmInfo.colorType(), bmInfo.alphaType()); 225 bool mul = (kUnpremul_SkAlphaType == bmInfo.alphaType()); 226 SkPMColor bmpPMColor = convert_to_PMColor(bmInfo.colorType(), bmInfo.alphaType(), 227 bmpColor8888); 228 if (!check_pixel(bmpPMColor, canvasPixel, mul)) { 229 ERRORF(reporter, "Expected canvas pixel at %d, %d to be 0x%08x, got 0x%08x. " 230 "Write performed premul: %d", cx, cy, bmpPMColor, canvasPixel, mul); 231 return false; 232 } 233 } else { 234 SkPMColor testColor = get_canvas_color(cx, cy); 235 if (canvasPixel != testColor) { 236 ERRORF(reporter, "Canvas pixel outside write rect at %d, %d changed." 237 " Should be 0x%08x, got 0x%08x. ", cx, cy, testColor, canvasPixel); 238 return false; 239 } 240 } 241 } 242 if (cy != DEV_H -1) { 243 const char* pad = reinterpret_cast<const char*>(canvasPixels + DEV_W); 244 for (size_t px = 0; px < canvasRowBytes - 4 * DEV_W; ++px) { 245 bool check; 246 REPORTER_ASSERT(reporter, check = (pad[px] == static_cast<char>(DEV_PAD))); 247 if (!check) { 248 return false; 249 } 250 } 251 } 252 canvasPixels += canvasRowBytes/4; 253 } 254 255 return true; 256 } 257 258 #include "SkMallocPixelRef.h" 259 260 // This is a tricky pattern, because we have to setConfig+rowBytes AND specify 261 // a custom pixelRef (which also has to specify its rowBytes), so we have to be 262 // sure that the two rowBytes match (and the infos match). 263 // 264 static bool alloc_row_bytes(SkBitmap* bm, const SkImageInfo& info, size_t rowBytes) { 265 if (!bm->setInfo(info, rowBytes)) { 266 return false; 267 } 268 sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeAllocate(info, rowBytes); 269 bm->setPixelRef(std::move(pr), 0, 0); 270 return true; 271 } 272 273 static void free_pixels(void* pixels, void* ctx) { 274 sk_free(pixels); 275 } 276 277 static bool setup_bitmap(SkBitmap* bm, SkColorType ct, SkAlphaType at, int w, int h, int tightRB) { 278 size_t rowBytes = tightRB ? 0 : 4 * w + 60; 279 SkImageInfo info = SkImageInfo::Make(w, h, ct, at); 280 if (!alloc_row_bytes(bm, info, rowBytes)) { 281 return false; 282 } 283 for (int y = 0; y < h; ++y) { 284 for (int x = 0; x < w; ++x) { 285 *bm->getAddr32(x, y) = get_bitmap_color(x, y, w, ct, at); 286 } 287 } 288 return true; 289 } 290 291 static void call_writepixels(SkCanvas* canvas) { 292 const SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1); 293 SkPMColor pixel = 0; 294 canvas->writePixels(info, &pixel, sizeof(SkPMColor), 0, 0); 295 } 296 297 DEF_TEST(WritePixelsSurfaceGenID, reporter) { 298 const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100); 299 auto surface(SkSurface::MakeRaster(info)); 300 uint32_t genID1 = surface->generationID(); 301 call_writepixels(surface->getCanvas()); 302 uint32_t genID2 = surface->generationID(); 303 REPORTER_ASSERT(reporter, genID1 != genID2); 304 } 305 306 static void test_write_pixels(skiatest::Reporter* reporter, SkSurface* surface) { 307 const SkIRect testRects[] = { 308 // entire thing 309 DEV_RECT, 310 // larger on all sides 311 SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H + 10), 312 // fully contained 313 SkIRect::MakeLTRB(DEV_W / 4, DEV_H / 4, 3 * DEV_W / 4, 3 * DEV_H / 4), 314 // outside top left 315 SkIRect::MakeLTRB(-10, -10, -1, -1), 316 // touching top left corner 317 SkIRect::MakeLTRB(-10, -10, 0, 0), 318 // overlapping top left corner 319 SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H / 4), 320 // overlapping top left and top right corners 321 SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H / 4), 322 // touching entire top edge 323 SkIRect::MakeLTRB(-10, -10, DEV_W + 10, 0), 324 // overlapping top right corner 325 SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H / 4), 326 // contained in x, overlapping top edge 327 SkIRect::MakeLTRB(DEV_W / 4, -10, 3 * DEV_W / 4, DEV_H / 4), 328 // outside top right corner 329 SkIRect::MakeLTRB(DEV_W + 1, -10, DEV_W + 10, -1), 330 // touching top right corner 331 SkIRect::MakeLTRB(DEV_W, -10, DEV_W + 10, 0), 332 // overlapping top left and bottom left corners 333 SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H + 10), 334 // touching entire left edge 335 SkIRect::MakeLTRB(-10, -10, 0, DEV_H + 10), 336 // overlapping bottom left corner 337 SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W / 4, DEV_H + 10), 338 // contained in y, overlapping left edge 339 SkIRect::MakeLTRB(-10, DEV_H / 4, DEV_W / 4, 3 * DEV_H / 4), 340 // outside bottom left corner 341 SkIRect::MakeLTRB(-10, DEV_H + 1, -1, DEV_H + 10), 342 // touching bottom left corner 343 SkIRect::MakeLTRB(-10, DEV_H, 0, DEV_H + 10), 344 // overlapping bottom left and bottom right corners 345 SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10), 346 // touching entire left edge 347 SkIRect::MakeLTRB(0, DEV_H, DEV_W, DEV_H + 10), 348 // overlapping bottom right corner 349 SkIRect::MakeLTRB(3 * DEV_W / 4, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10), 350 // overlapping top right and bottom right corners 351 SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H + 10), 352 }; 353 354 SkCanvas* canvas = surface->getCanvas(); 355 356 static const struct { 357 SkColorType fColorType; 358 SkAlphaType fAlphaType; 359 } gSrcConfigs[] = { 360 { kRGBA_8888_SkColorType, kPremul_SkAlphaType }, 361 { kRGBA_8888_SkColorType, kUnpremul_SkAlphaType }, 362 { kBGRA_8888_SkColorType, kPremul_SkAlphaType }, 363 { kBGRA_8888_SkColorType, kUnpremul_SkAlphaType }, 364 }; 365 for (size_t r = 0; r < SK_ARRAY_COUNT(testRects); ++r) { 366 const SkIRect& rect = testRects[r]; 367 for (int tightBmp = 0; tightBmp < 2; ++tightBmp) { 368 for (size_t c = 0; c < SK_ARRAY_COUNT(gSrcConfigs); ++c) { 369 const SkColorType ct = gSrcConfigs[c].fColorType; 370 const SkAlphaType at = gSrcConfigs[c].fAlphaType; 371 372 fill_canvas(canvas); 373 SkBitmap bmp; 374 REPORTER_ASSERT(reporter, setup_bitmap(&bmp, ct, at, rect.width(), 375 rect.height(), SkToBool(tightBmp))); 376 uint32_t idBefore = surface->generationID(); 377 378 // sk_tool_utils::write_pixels(&canvas, bmp, rect.fLeft, rect.fTop, ct, at); 379 canvas->writePixels(bmp, rect.fLeft, rect.fTop); 380 381 uint32_t idAfter = surface->generationID(); 382 REPORTER_ASSERT(reporter, check_write(reporter, surface, bmp, 383 rect.fLeft, rect.fTop)); 384 385 // we should change the genID iff pixels were actually written. 386 SkIRect canvasRect = SkIRect::MakeSize(canvas->getBaseLayerSize()); 387 SkIRect writeRect = SkIRect::MakeXYWH(rect.fLeft, rect.fTop, 388 bmp.width(), bmp.height()); 389 bool intersects = SkIRect::Intersects(canvasRect, writeRect) ; 390 REPORTER_ASSERT(reporter, intersects == (idBefore != idAfter)); 391 } 392 } 393 } 394 } 395 DEF_TEST(WritePixels, reporter) { 396 const SkImageInfo info = SkImageInfo::MakeN32Premul(DEV_W, DEV_H); 397 for (auto& tightRowBytes : { true, false }) { 398 const size_t rowBytes = tightRowBytes ? info.minRowBytes() : 4 * DEV_W + 100; 399 const size_t size = info.computeByteSize(rowBytes); 400 void* pixels = sk_malloc_throw(size); 401 // if rowBytes isn't tight then set the padding to a known value 402 if (!tightRowBytes) { 403 memset(pixels, DEV_PAD, size); 404 } 405 auto surface(SkSurface::MakeRasterDirectReleaseProc(info, pixels, rowBytes, 406 free_pixels, nullptr)); 407 test_write_pixels(reporter, surface.get()); 408 } 409 } 410 #if SK_SUPPORT_GPU 411 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WritePixels_Gpu, reporter, ctxInfo) { 412 const SkImageInfo ii = SkImageInfo::MakeN32Premul(DEV_W, DEV_H); 413 414 for (auto& origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin }) { 415 for (int sampleCnt : {1, 4}) { 416 sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(ctxInfo.grContext(), 417 SkBudgeted::kNo, ii, sampleCnt, 418 origin, nullptr)); 419 if (!surface && sampleCnt > 1) { 420 // Some platforms don't support MSAA 421 continue; 422 } 423 test_write_pixels(reporter, surface.get()); 424 } 425 } 426 } 427 428 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WritePixelsNonTexture_Gpu, reporter, ctxInfo) { 429 GrContext* context = ctxInfo.grContext(); 430 GrGpu* gpu = context->contextPriv().getGpu(); 431 432 for (auto& origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin }) { 433 for (int sampleCnt : {1, 4}) { 434 GrBackendTexture backendTex = gpu->createTestingOnlyBackendTexture( 435 nullptr, DEV_W, DEV_H, kSkia8888_GrPixelConfig, true, GrMipMapped::kNo); 436 SkColorType colorType; 437 if (kRGBA_8888_GrPixelConfig == kSkia8888_GrPixelConfig) { 438 colorType = kRGBA_8888_SkColorType; 439 } else { 440 colorType = kBGRA_8888_SkColorType; 441 } 442 sk_sp<SkSurface> surface(SkSurface::MakeFromBackendTextureAsRenderTarget( 443 context, backendTex, origin, sampleCnt, colorType, nullptr, nullptr)); 444 if (!surface) { 445 gpu->deleteTestingOnlyBackendTexture(&backendTex); 446 continue; 447 } 448 449 test_write_pixels(reporter, surface.get()); 450 451 surface.reset(); 452 gpu->deleteTestingOnlyBackendTexture(&backendTex); 453 } 454 } 455 } 456 457 static sk_sp<SkSurface> create_surf(GrContext* context, int width, int height) { 458 const SkImageInfo ii = SkImageInfo::Make(width, height, 459 kRGBA_8888_SkColorType, kPremul_SkAlphaType); 460 461 sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, ii); 462 surf->flush(); 463 return surf; 464 } 465 466 static sk_sp<SkImage> upload(const sk_sp<SkSurface>& surf, SkColor color) { 467 const SkImageInfo smII = SkImageInfo::Make(16, 16, kRGBA_8888_SkColorType, kPremul_SkAlphaType); 468 SkBitmap bm; 469 bm.allocPixels(smII); 470 bm.eraseColor(color); 471 472 surf->getCanvas()->writePixels(bm, 0, 0); 473 474 return surf->makeImageSnapshot(); 475 } 476 477 // This is tests whether the first writePixels is completed before the 478 // second writePixels takes effect (i.e., that writePixels correctly flushes 479 // in between uses of the shared backing resource). 480 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WritePixelsPendingIO, reporter, ctxInfo) { 481 GrContext* context = ctxInfo.grContext(); 482 GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); 483 484 static const int kFullSize = 62; 485 static const int kHalfSize = 31; 486 487 static const uint32_t kLeftColor = 0xFF222222; 488 static const uint32_t kRightColor = 0xFFAAAAAA; 489 490 const SkImageInfo fullII = SkImageInfo::Make(kFullSize, kFullSize, 491 kRGBA_8888_SkColorType, kPremul_SkAlphaType); 492 const SkImageInfo halfII = SkImageInfo::Make(kHalfSize, kFullSize, 493 kRGBA_8888_SkColorType, kPremul_SkAlphaType); 494 495 sk_sp<SkSurface> dest = SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, fullII); 496 497 { 498 // Seed the resource cached with a scratch texture that will be 499 // reused by writeSurfacePixels 500 GrSurfaceDesc desc; 501 desc.fFlags = kNone_GrSurfaceFlags; 502 desc.fWidth = 32; 503 desc.fHeight = 64; 504 desc.fConfig = kRGBA_8888_GrPixelConfig; 505 506 sk_sp<GrTextureProxy> temp = proxyProvider->createProxy(desc, SkBackingFit::kApprox, 507 SkBudgeted::kYes); 508 temp->instantiate(context->contextPriv().resourceProvider()); 509 } 510 511 // Create the surfaces and flush them to ensure there is no lingering pendingIO 512 sk_sp<SkSurface> leftSurf = create_surf(context, kHalfSize, kFullSize); 513 sk_sp<SkSurface> rightSurf = create_surf(context, kHalfSize, kFullSize); 514 515 sk_sp<SkImage> leftImg = upload(std::move(leftSurf), kLeftColor); 516 dest->getCanvas()->drawImage(std::move(leftImg), 0, 0); 517 518 sk_sp<SkImage> rightImg = upload(std::move(rightSurf), kRightColor); 519 dest->getCanvas()->drawImage(std::move(rightImg), kHalfSize, 0); 520 521 SkBitmap bm; 522 bm.allocPixels(fullII); 523 SkAssertResult(dest->readPixels(bm, 0, 0)); 524 525 bool isCorrect = true; 526 for (int y = 0; isCorrect && y < 16; ++y) { 527 const uint32_t* sl = bm.getAddr32(0, y); 528 529 for (int x = 0; x < 16; ++x) { 530 if (kLeftColor != sl[x]) { 531 isCorrect = false; 532 break; 533 } 534 } 535 for (int x = kHalfSize; x < kHalfSize+16; ++x) { 536 if (kRightColor != sl[x]) { 537 isCorrect = false; 538 break; 539 } 540 } 541 } 542 543 REPORTER_ASSERT(reporter, isCorrect); 544 } 545 546 547 #endif 548