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 "include/core/SkBitmap.h" 9#include "include/core/SkCanvas.h" 10#include "include/core/SkSurface.h" 11#include "include/gpu/GrBackendSurface.h" 12#include "include/gpu/GrDirectContext.h" 13#include "include/private/SkColorData.h" 14#include "include/private/SkImageInfoPriv.h" 15#include "src/core/SkMathPriv.h" 16#include "src/gpu/GrDirectContextPriv.h" 17#include "src/gpu/GrGpu.h" 18#include "src/gpu/GrProxyProvider.h" 19#include "tests/Test.h" 20#include "tools/gpu/BackendSurfaceFactory.h" 21 22#include <initializer_list> 23 24static const int DEV_W = 100, DEV_H = 100; 25static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H); 26static const U8CPU DEV_PAD = 0xee; 27 28static SkPMColor get_canvas_color(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// assumes any premu/.unpremul has been applied 58static uint32_t pack_color_type(SkColorType ct, U8CPU a, U8CPU r, U8CPU g, U8CPU b) { 59 uint32_t r32; 60 uint8_t* result = reinterpret_cast<uint8_t*>(&r32); 61 switch (ct) { 62 case kBGRA_8888_SkColorType: 63 result[0] = b; 64 result[1] = g; 65 result[2] = r; 66 result[3] = a; 67 break; 68 case kRGBA_8888_SkColorType: // fallthrough 69 case kRGB_888x_SkColorType: 70 result[0] = r; 71 result[1] = g; 72 result[2] = b; 73 result[3] = a; 74 break; 75 default: 76 SkASSERT(0); 77 return 0; 78 } 79 return r32; 80} 81 82static uint32_t get_bitmap_color(int x, int y, int w, SkColorType ct, SkAlphaType at) { 83 int n = y * w + x; 84 U8CPU b = n & 0xff; 85 U8CPU g = (n >> 8) & 0xff; 86 U8CPU r = (n >> 16) & 0xff; 87 U8CPU a = 0; 88 switch ((x+y) % 5) { 89 case 4: 90 a = 0xff; 91 break; 92 case 3: 93 a = 0x80; 94 break; 95 case 2: 96 a = 0xCC; 97 break; 98 case 1: 99 a = 0x01; 100 break; 101 case 0: 102 a = 0x00; 103 break; 104 } 105 if (kPremul_SkAlphaType == at) { 106 r = SkMulDiv255Ceiling(r, a); 107 g = SkMulDiv255Ceiling(g, a); 108 b = SkMulDiv255Ceiling(b, a); 109 } 110 return pack_color_type(ct, a, r, g , b); 111} 112 113static void fill_surface(SkSurface* surface) { 114 SkBitmap bmp; 115 bmp.allocN32Pixels(DEV_W, DEV_H); 116 for (int y = 0; y < DEV_H; ++y) { 117 for (int x = 0; x < DEV_W; ++x) { 118 *bmp.getAddr32(x, y) = get_canvas_color(x, y); 119 } 120 } 121 surface->writePixels(bmp, 0, 0); 122} 123 124/** 125 * Lucky for us, alpha is always in the same spot (SK_A32_SHIFT), for both RGBA and BGRA. 126 * Thus this routine doesn't need to know the exact colortype 127 */ 128static uint32_t premul(uint32_t color) { 129 unsigned a = SkGetPackedA32(color); 130 // these next three are not necessarily r,g,b in that order, but they are r,g,b in some order. 131 unsigned c0 = SkGetPackedR32(color); 132 unsigned c1 = SkGetPackedG32(color); 133 unsigned c2 = SkGetPackedB32(color); 134 c0 = SkMulDiv255Ceiling(c0, a); 135 c1 = SkMulDiv255Ceiling(c1, a); 136 c2 = SkMulDiv255Ceiling(c2, a); 137 return SkPackARGB32NoCheck(a, c0, c1, c2); 138} 139 140static SkPMColor convert_to_PMColor(SkColorType ct, SkAlphaType at, uint32_t color) { 141 if (kUnpremul_SkAlphaType == at) { 142 color = premul(color); 143 } 144 switch (ct) { 145 case kRGBA_8888_SkColorType: // fallthrough 146 case kRGB_888x_SkColorType: 147 color = SkSwizzle_RGBA_to_PMColor(color); 148 break; 149 case kBGRA_8888_SkColorType: 150 color = SkSwizzle_BGRA_to_PMColor(color); 151 break; 152 default: 153 SkASSERT(0); 154 break; 155 } 156 return color; 157} 158 159static bool check_pixel(SkPMColor a, SkPMColor b, bool didPremulConversion) { 160 if (!didPremulConversion) { 161 return a == b; 162 } 163 int32_t aA = static_cast<int32_t>(SkGetPackedA32(a)); 164 int32_t aR = static_cast<int32_t>(SkGetPackedR32(a)); 165 int32_t aG = static_cast<int32_t>(SkGetPackedG32(a)); 166 int32_t aB = SkGetPackedB32(a); 167 168 int32_t bA = static_cast<int32_t>(SkGetPackedA32(b)); 169 int32_t bR = static_cast<int32_t>(SkGetPackedR32(b)); 170 int32_t bG = static_cast<int32_t>(SkGetPackedG32(b)); 171 int32_t bB = static_cast<int32_t>(SkGetPackedB32(b)); 172 173 return aA == bA && 174 SkAbs32(aR - bR) <= 1 && 175 SkAbs32(aG - bG) <= 1 && 176 SkAbs32(aB - bB) <= 1; 177} 178 179bool write_should_succeed(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo, bool isGPU) { 180 if (!SkImageInfoValidConversion(dstInfo, srcInfo)) { 181 return false; 182 } 183 if (!isGPU) { 184 return true; 185 } 186 // The GPU backend supports writing unpremul data to a premul dst but not vice versa. 187 if (srcInfo.alphaType() == kPremul_SkAlphaType && 188 dstInfo.alphaType() == kUnpremul_SkAlphaType) { 189 return false; 190 } 191 if (!SkColorTypeIsAlwaysOpaque(srcInfo.colorType()) && 192 SkColorTypeIsAlwaysOpaque(dstInfo.colorType())) { 193 return false; 194 } 195 // The source has no alpha value and the dst is only alpha 196 if (SkColorTypeIsAlwaysOpaque(srcInfo.colorType()) && 197 SkColorTypeIsAlphaOnly(dstInfo.colorType())) { 198 return false; 199 } 200 return true; 201} 202 203static bool check_write(skiatest::Reporter* reporter, SkSurface* surf, SkAlphaType surfaceAlphaType, 204 const SkBitmap& bitmap, int writeX, int writeY) { 205 size_t canvasRowBytes; 206 const uint32_t* canvasPixels; 207 208 // Can't use canvas->peekPixels(), as we are trying to look at GPU pixels sometimes as well. 209 // At some point this will be unsupported, as we won't allow accessBitmap() to magically call 210 // readPixels for the client. 211 SkBitmap secretDevBitmap; 212 secretDevBitmap.allocN32Pixels(surf->width(), surf->height()); 213 if (!surf->readPixels(secretDevBitmap, 0, 0)) { 214 return false; 215 } 216 217 canvasRowBytes = secretDevBitmap.rowBytes(); 218 canvasPixels = static_cast<const uint32_t*>(secretDevBitmap.getPixels()); 219 220 if (nullptr == canvasPixels) { 221 return false; 222 } 223 224 if (surf->width() != DEV_W || surf->height() != DEV_H) { 225 return false; 226 } 227 228 const SkImageInfo& bmInfo = bitmap.info(); 229 230 SkIRect writeRect = SkIRect::MakeXYWH(writeX, writeY, bitmap.width(), bitmap.height()); 231 for (int cy = 0; cy < DEV_H; ++cy) { 232 for (int cx = 0; cx < DEV_W; ++cx) { 233 SkPMColor canvasPixel = canvasPixels[cx]; 234 if (writeRect.contains(cx, cy)) { 235 int bx = cx - writeX; 236 int by = cy - writeY; 237 uint32_t bmpColor8888 = get_bitmap_color(bx, by, bitmap.width(), 238 bmInfo.colorType(), bmInfo.alphaType()); 239 bool mul = (kUnpremul_SkAlphaType == bmInfo.alphaType()); 240 SkPMColor bmpPMColor = convert_to_PMColor(bmInfo.colorType(), bmInfo.alphaType(), 241 bmpColor8888); 242 if (bmInfo.alphaType() == kOpaque_SkAlphaType || 243 surfaceAlphaType == kOpaque_SkAlphaType) { 244 bmpPMColor |= 0xFF000000; 245 } 246 if (!check_pixel(bmpPMColor, canvasPixel, mul)) { 247 ERRORF(reporter, "Expected canvas pixel at %d, %d to be 0x%08x, got 0x%08x. " 248 "Write performed premul: %d", cx, cy, bmpPMColor, canvasPixel, mul); 249 return false; 250 } 251 } else { 252 SkPMColor testColor = get_canvas_color(cx, cy); 253 if (canvasPixel != testColor) { 254 ERRORF(reporter, "Canvas pixel outside write rect at %d, %d changed." 255 " Should be 0x%08x, got 0x%08x. ", cx, cy, testColor, canvasPixel); 256 return false; 257 } 258 } 259 } 260 if (cy != DEV_H -1) { 261 const char* pad = reinterpret_cast<const char*>(canvasPixels + DEV_W); 262 for (size_t px = 0; px < canvasRowBytes - 4 * DEV_W; ++px) { 263 bool check; 264 REPORTER_ASSERT(reporter, check = (pad[px] == static_cast<char>(DEV_PAD))); 265 if (!check) { 266 return false; 267 } 268 } 269 } 270 canvasPixels += canvasRowBytes/4; 271 } 272 273 return true; 274} 275 276#include "include/core/SkMallocPixelRef.h" 277 278// This is a tricky pattern, because we have to setConfig+rowBytes AND specify 279// a custom pixelRef (which also has to specify its rowBytes), so we have to be 280// sure that the two rowBytes match (and the infos match). 281// 282static bool alloc_row_bytes(SkBitmap* bm, const SkImageInfo& info, size_t rowBytes) { 283 if (!bm->setInfo(info, rowBytes)) { 284 return false; 285 } 286 sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeAllocate(info, rowBytes); 287 bm->setPixelRef(std::move(pr), 0, 0); 288 return true; 289} 290 291static void free_pixels(void* pixels, void* ctx) { 292 sk_free(pixels); 293} 294 295static bool setup_bitmap(SkBitmap* bm, SkColorType ct, SkAlphaType at, int w, int h, int tightRB) { 296 size_t rowBytes = tightRB ? 0 : 4 * w + 60; 297 SkImageInfo info = SkImageInfo::Make(w, h, ct, at); 298 if (!alloc_row_bytes(bm, info, rowBytes)) { 299 return false; 300 } 301 for (int y = 0; y < h; ++y) { 302 for (int x = 0; x < w; ++x) { 303 *bm->getAddr32(x, y) = get_bitmap_color(x, y, w, ct, at); 304 } 305 } 306 return true; 307} 308 309static void call_writepixels(SkSurface* surface) { 310 const SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1); 311 SkPMColor pixel = 0; 312 surface->writePixels({info, &pixel, sizeof(SkPMColor)}, 0, 0); 313} 314 315DEF_TEST(WritePixelsSurfaceGenID, reporter) { 316 const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100); 317 auto surface(SkSurface::MakeRaster(info)); 318 uint32_t genID1 = surface->generationID(); 319 call_writepixels(surface.get()); 320 uint32_t genID2 = surface->generationID(); 321 REPORTER_ASSERT(reporter, genID1 != genID2); 322} 323 324static void test_write_pixels(skiatest::Reporter* reporter, SkSurface* surface, 325 const SkImageInfo& surfaceInfo) { 326 const SkIRect testRects[] = { 327 // entire thing 328 DEV_RECT, 329 // larger on all sides 330 SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H + 10), 331 // fully contained 332 SkIRect::MakeLTRB(DEV_W / 4, DEV_H / 4, 3 * DEV_W / 4, 3 * DEV_H / 4), 333 // outside top left 334 SkIRect::MakeLTRB(-10, -10, -1, -1), 335 // touching top left corner 336 SkIRect::MakeLTRB(-10, -10, 0, 0), 337 // overlapping top left corner 338 SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H / 4), 339 // overlapping top left and top right corners 340 SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H / 4), 341 // touching entire top edge 342 SkIRect::MakeLTRB(-10, -10, DEV_W + 10, 0), 343 // overlapping top right corner 344 SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H / 4), 345 // contained in x, overlapping top edge 346 SkIRect::MakeLTRB(DEV_W / 4, -10, 3 * DEV_W / 4, DEV_H / 4), 347 // outside top right corner 348 SkIRect::MakeLTRB(DEV_W + 1, -10, DEV_W + 10, -1), 349 // touching top right corner 350 SkIRect::MakeLTRB(DEV_W, -10, DEV_W + 10, 0), 351 // overlapping top left and bottom left corners 352 SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H + 10), 353 // touching entire left edge 354 SkIRect::MakeLTRB(-10, -10, 0, DEV_H + 10), 355 // overlapping bottom left corner 356 SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W / 4, DEV_H + 10), 357 // contained in y, overlapping left edge 358 SkIRect::MakeLTRB(-10, DEV_H / 4, DEV_W / 4, 3 * DEV_H / 4), 359 // outside bottom left corner 360 SkIRect::MakeLTRB(-10, DEV_H + 1, -1, DEV_H + 10), 361 // touching bottom left corner 362 SkIRect::MakeLTRB(-10, DEV_H, 0, DEV_H + 10), 363 // overlapping bottom left and bottom right corners 364 SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10), 365 // touching entire left edge 366 SkIRect::MakeLTRB(0, DEV_H, DEV_W, DEV_H + 10), 367 // overlapping bottom right corner 368 SkIRect::MakeLTRB(3 * DEV_W / 4, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10), 369 // overlapping top right and bottom right corners 370 SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H + 10), 371 }; 372 373 SkCanvas* canvas = surface->getCanvas(); 374 375 static const struct { 376 SkColorType fColorType; 377 SkAlphaType fAlphaType; 378 } gSrcConfigs[] = { 379 {kRGBA_8888_SkColorType, kPremul_SkAlphaType}, 380 {kRGBA_8888_SkColorType, kUnpremul_SkAlphaType}, 381 {kRGB_888x_SkColorType, kOpaque_SkAlphaType}, 382 {kBGRA_8888_SkColorType, kPremul_SkAlphaType}, 383 {kBGRA_8888_SkColorType, kUnpremul_SkAlphaType}, 384 }; 385 for (size_t r = 0; r < SK_ARRAY_COUNT(testRects); ++r) { 386 const SkIRect& rect = testRects[r]; 387 for (int tightBmp = 0; tightBmp < 2; ++tightBmp) { 388 for (size_t c = 0; c < SK_ARRAY_COUNT(gSrcConfigs); ++c) { 389 const SkColorType ct = gSrcConfigs[c].fColorType; 390 const SkAlphaType at = gSrcConfigs[c].fAlphaType; 391 392 bool isGPU = SkToBool(surface->getCanvas()->recordingContext()); 393 fill_surface(surface); 394 SkBitmap bmp; 395 REPORTER_ASSERT(reporter, setup_bitmap(&bmp, ct, at, rect.width(), 396 rect.height(), SkToBool(tightBmp))); 397 uint32_t idBefore = surface->generationID(); 398 399 surface->writePixels(bmp, rect.fLeft, rect.fTop); 400 401 uint32_t idAfter = surface->generationID(); 402 REPORTER_ASSERT(reporter, check_write(reporter, surface, surfaceInfo.alphaType(), 403 bmp, rect.fLeft, rect.fTop)); 404 405 // we should change the genID iff pixels were actually written. 406 SkIRect canvasRect = SkIRect::MakeSize(canvas->getBaseLayerSize()); 407 SkIRect writeRect = SkIRect::MakeXYWH(rect.fLeft, rect.fTop, 408 bmp.width(), bmp.height()); 409 bool expectSuccess = SkIRect::Intersects(canvasRect, writeRect) && 410 write_should_succeed(surfaceInfo, bmp.info(), isGPU); 411 REPORTER_ASSERT(reporter, expectSuccess == (idBefore != idAfter)); 412 } 413 } 414 } 415} 416 417DEF_TEST(WritePixels, reporter) { 418 const SkImageInfo info = SkImageInfo::MakeN32Premul(DEV_W, DEV_H); 419 for (auto& tightRowBytes : { true, false }) { 420 const size_t rowBytes = tightRowBytes ? info.minRowBytes() : 4 * DEV_W + 100; 421 const size_t size = info.computeByteSize(rowBytes); 422 void* pixels = sk_malloc_throw(size); 423 // if rowBytes isn't tight then set the padding to a known value 424 if (!tightRowBytes) { 425 memset(pixels, DEV_PAD, size); 426 } 427 auto surface(SkSurface::MakeRasterDirectReleaseProc(info, pixels, rowBytes, 428 free_pixels, nullptr)); 429 test_write_pixels(reporter, surface.get(), info); 430 } 431} 432 433static void test_write_pixels(skiatest::Reporter* reporter, 434 GrRecordingContext* rContext, 435 int sampleCnt) { 436 const SkImageInfo ii = SkImageInfo::MakeN32Premul(DEV_W, DEV_H); 437 for (auto& origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin }) { 438 sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(rContext, 439 SkBudgeted::kNo, ii, sampleCnt, 440 origin, nullptr)); 441 if (surface) { 442 test_write_pixels(reporter, surface.get(), ii); 443 } 444 } 445} 446 447DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WritePixels_Gpu, reporter, ctxInfo) { 448 test_write_pixels(reporter, ctxInfo.directContext(), 1); 449} 450 451DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WritePixelsMSAA_Gpu, reporter, ctxInfo) { 452 test_write_pixels(reporter, ctxInfo.directContext(), 1); 453} 454 455static void test_write_pixels_non_texture(skiatest::Reporter* reporter, 456 GrDirectContext* dContext, 457 int sampleCnt) { 458 // Dawn currently doesn't support writePixels to a texture-as-render-target. 459 // See http://skbug.com/10336. 460 if (GrBackendApi::kDawn == dContext->backend()) { 461 return; 462 } 463 for (auto& origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin }) { 464 SkColorType colorType = kN32_SkColorType; 465 auto surface = sk_gpu_test::MakeBackendRenderTargetSurface(dContext, 466 {DEV_W, DEV_H}, 467 origin, 468 sampleCnt, 469 colorType); 470 if (surface) { 471 auto ii = SkImageInfo::MakeN32Premul(DEV_W, DEV_H); 472 test_write_pixels(reporter, surface.get(), ii); 473 } 474 } 475} 476 477DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WritePixelsNonTexture_Gpu, reporter, ctxInfo) { 478 test_write_pixels_non_texture(reporter, ctxInfo.directContext(), 1); 479} 480 481DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WritePixelsNonTextureMSAA_Gpu, reporter, ctxInfo) { 482 test_write_pixels_non_texture(reporter, ctxInfo.directContext(), 4); 483} 484 485static sk_sp<SkSurface> create_surf(GrRecordingContext* rContext, int width, int height) { 486 const SkImageInfo ii = SkImageInfo::Make(width, height, 487 kRGBA_8888_SkColorType, kPremul_SkAlphaType); 488 489 sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(rContext, SkBudgeted::kYes, ii); 490 surf->flushAndSubmit(); 491 return surf; 492} 493 494static sk_sp<SkImage> upload(const sk_sp<SkSurface>& surf, SkColor color) { 495 const SkImageInfo smII = SkImageInfo::Make(16, 16, kRGBA_8888_SkColorType, kPremul_SkAlphaType); 496 SkBitmap bm; 497 bm.allocPixels(smII); 498 bm.eraseColor(color); 499 500 surf->writePixels(bm, 0, 0); 501 502 return surf->makeImageSnapshot(); 503} 504 505// This is tests whether the first writePixels is completed before the 506// second writePixels takes effect (i.e., that writePixels correctly flushes 507// in between uses of the shared backing resource). 508// The unit test fails on Nexus 6P/Android M with driver 129.0 without the 509// "DisallowTexSubImageForUnormConfigTexturesEverBoundToFBO" workaround enabled. 510// skbug.com/11834 511DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WritePixelsPendingIO, reporter, ctxInfo) { 512 auto context = ctxInfo.directContext(); 513 GrProxyProvider* proxyProvider = context->priv().proxyProvider(); 514 const GrCaps* caps = context->priv().caps(); 515 516 static const int kFullSize = 62; 517 static const int kHalfSize = 31; 518 519 static const uint32_t kLeftColor = 0xFF222222; 520 static const uint32_t kRightColor = 0xFFAAAAAA; 521 522 const SkImageInfo fullII = SkImageInfo::Make(kFullSize, kFullSize, 523 kRGBA_8888_SkColorType, kPremul_SkAlphaType); 524 const SkImageInfo halfII = SkImageInfo::Make(kHalfSize, kFullSize, 525 kRGBA_8888_SkColorType, kPremul_SkAlphaType); 526 527 sk_sp<SkSurface> dest = SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, fullII); 528 529 { 530 // Seed the resource cached with a scratch texture that will be reused by writePixels 531 static constexpr SkISize kDims = {32, 64}; 532 533 const GrBackendFormat format = caps->getDefaultBackendFormat(GrColorType::kRGBA_8888, 534 GrRenderable::kNo); 535 536 sk_sp<GrTextureProxy> temp = proxyProvider->createProxy( 537 format, kDims, GrRenderable::kNo, 1, GrMipmapped::kNo, SkBackingFit::kApprox, 538 SkBudgeted::kYes, GrProtected::kNo); 539 temp->instantiate(context->priv().resourceProvider()); 540 } 541 542 // Create the surfaces and flush them to ensure there is no lingering pendingIO 543 sk_sp<SkSurface> leftSurf = create_surf(context, kHalfSize, kFullSize); 544 sk_sp<SkSurface> rightSurf = create_surf(context, kHalfSize, kFullSize); 545 546 sk_sp<SkImage> leftImg = upload(std::move(leftSurf), kLeftColor); 547 dest->getCanvas()->drawImage(std::move(leftImg), 0, 0); 548 549 sk_sp<SkImage> rightImg = upload(std::move(rightSurf), kRightColor); 550 dest->getCanvas()->drawImage(std::move(rightImg), kHalfSize, 0); 551 552 SkBitmap bm; 553 bm.allocPixels(fullII); 554 SkAssertResult(dest->readPixels(bm, 0, 0)); 555 556 bool isCorrect = true; 557 for (int y = 0; isCorrect && y < 16; ++y) { 558 const uint32_t* sl = bm.getAddr32(0, y); 559 560 for (int x = 0; x < 16; ++x) { 561 if (kLeftColor != sl[x]) { 562 isCorrect = false; 563 break; 564 } 565 } 566 for (int x = kHalfSize; x < kHalfSize+16; ++x) { 567 if (kRightColor != sl[x]) { 568 isCorrect = false; 569 break; 570 } 571 } 572 } 573 574 REPORTER_ASSERT(reporter, isCorrect); 575} 576 577DEF_TEST(WritePixels_InvalidRowBytes, reporter) { 578 auto dstII = SkImageInfo::Make({10, 10}, kRGBA_8888_SkColorType, kPremul_SkAlphaType); 579 auto surf = SkSurface::MakeRaster(dstII); 580 for (int ct = 0; ct < kLastEnum_SkColorType + 1; ++ct) { 581 auto colorType = static_cast<SkColorType>(ct); 582 583 size_t bpp = SkColorTypeBytesPerPixel(colorType); 584 if (bpp <= 1) { 585 continue; 586 } 587 auto srcII = dstII.makeColorType(colorType); 588 size_t badRowBytes = (surf->width() + 1)*bpp - 1; 589 auto storage = std::make_unique<char[]>(badRowBytes*surf->height()); 590 memset(storage.get(), 0, badRowBytes * surf->height()); 591 // SkSurface::writePixels doesn't report bool, SkCanvas's does. 592 REPORTER_ASSERT(reporter, 593 !surf->getCanvas()->writePixels(srcII, storage.get(), badRowBytes, 0, 0)); 594 } 595} 596