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 "SkBitmap.h" 9 #include "SkRect.h" 10 #include "SkTemplates.h" 11 #include "Test.h" 12 13 static const char* boolStr(bool value) { 14 return value ? "true" : "false"; 15 } 16 17 static const char* color_type_name(SkColorType colorType) { 18 switch (colorType) { 19 case kUnknown_SkColorType: 20 return "None"; 21 case kAlpha_8_SkColorType: 22 return "A8"; 23 case kRGB_565_SkColorType: 24 return "565"; 25 case kARGB_4444_SkColorType: 26 return "4444"; 27 case kRGBA_8888_SkColorType: 28 return "RGBA"; 29 case kBGRA_8888_SkColorType: 30 return "BGRA"; 31 case kIndex_8_SkColorType: 32 return "Index8"; 33 case kGray_8_SkColorType: 34 return "Gray8"; 35 case kRGBA_F16_SkColorType: 36 return "F16"; 37 } 38 return ""; 39 } 40 41 static void report_opaqueness(skiatest::Reporter* reporter, const SkBitmap& src, 42 const SkBitmap& dst) { 43 ERRORF(reporter, "src %s opaque:%d, dst %s opaque:%d", 44 color_type_name(src.colorType()), src.isOpaque(), 45 color_type_name(dst.colorType()), dst.isOpaque()); 46 } 47 48 static bool canHaveAlpha(SkColorType ct) { 49 return kRGB_565_SkColorType != ct; 50 } 51 52 // copyTo() should preserve isOpaque when it makes sense 53 static void test_isOpaque(skiatest::Reporter* reporter, 54 const SkBitmap& srcOpaque, const SkBitmap& srcPremul, 55 SkColorType dstColorType) { 56 SkBitmap dst; 57 58 if (canHaveAlpha(srcPremul.colorType()) && canHaveAlpha(dstColorType)) { 59 REPORTER_ASSERT(reporter, srcPremul.copyTo(&dst, dstColorType)); 60 REPORTER_ASSERT(reporter, dst.colorType() == dstColorType); 61 if (srcPremul.isOpaque() != dst.isOpaque()) { 62 report_opaqueness(reporter, srcPremul, dst); 63 } 64 } 65 66 REPORTER_ASSERT(reporter, srcOpaque.copyTo(&dst, dstColorType)); 67 REPORTER_ASSERT(reporter, dst.colorType() == dstColorType); 68 if (srcOpaque.isOpaque() != dst.isOpaque()) { 69 report_opaqueness(reporter, srcOpaque, dst); 70 } 71 } 72 73 static void init_src(const SkBitmap& bitmap) { 74 SkAutoLockPixels lock(bitmap); 75 if (bitmap.getPixels()) { 76 if (bitmap.getColorTable()) { 77 sk_bzero(bitmap.getPixels(), bitmap.getSize()); 78 } else { 79 bitmap.eraseColor(SK_ColorWHITE); 80 } 81 } 82 } 83 84 static SkColorTable* init_ctable() { 85 static const SkColor colors[] = { 86 SK_ColorBLACK, SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE 87 }; 88 return new SkColorTable(colors, SK_ARRAY_COUNT(colors)); 89 } 90 91 struct Pair { 92 SkColorType fColorType; 93 const char* fValid; 94 }; 95 96 // Utility functions for copyPixelsTo()/copyPixelsFrom() tests. 97 // getPixel() 98 // setPixel() 99 // getSkConfigName() 100 // struct Coordinates 101 // reportCopyVerification() 102 // writeCoordPixels() 103 104 // Utility function to read the value of a given pixel in bm. All 105 // values converted to uint32_t for simplification of comparisons. 106 static uint32_t getPixel(int x, int y, const SkBitmap& bm) { 107 uint32_t val = 0; 108 uint16_t val16; 109 uint8_t val8; 110 SkAutoLockPixels lock(bm); 111 const void* rawAddr = bm.getAddr(x,y); 112 113 switch (bm.bytesPerPixel()) { 114 case 4: 115 memcpy(&val, rawAddr, sizeof(uint32_t)); 116 break; 117 case 2: 118 memcpy(&val16, rawAddr, sizeof(uint16_t)); 119 val = val16; 120 break; 121 case 1: 122 memcpy(&val8, rawAddr, sizeof(uint8_t)); 123 val = val8; 124 break; 125 default: 126 break; 127 } 128 return val; 129 } 130 131 // Utility function to set value of any pixel in bm. 132 // bm.getConfig() specifies what format 'val' must be 133 // converted to, but at present uint32_t can handle all formats. 134 static void setPixel(int x, int y, uint32_t val, SkBitmap& bm) { 135 uint16_t val16; 136 uint8_t val8; 137 SkAutoLockPixels lock(bm); 138 void* rawAddr = bm.getAddr(x,y); 139 140 switch (bm.bytesPerPixel()) { 141 case 4: 142 memcpy(rawAddr, &val, sizeof(uint32_t)); 143 break; 144 case 2: 145 val16 = val & 0xFFFF; 146 memcpy(rawAddr, &val16, sizeof(uint16_t)); 147 break; 148 case 1: 149 val8 = val & 0xFF; 150 memcpy(rawAddr, &val8, sizeof(uint8_t)); 151 break; 152 default: 153 // Ignore. 154 break; 155 } 156 } 157 158 // Helper struct to contain pixel locations, while avoiding need for STL. 159 struct Coordinates { 160 161 const int length; 162 SkIPoint* const data; 163 164 explicit Coordinates(int _length): length(_length) 165 , data(new SkIPoint[length]) { } 166 167 ~Coordinates(){ 168 delete [] data; 169 } 170 171 SkIPoint* operator[](int i) const { 172 // Use with care, no bounds checking. 173 return data + i; 174 } 175 }; 176 177 // A function to verify that two bitmaps contain the same pixel values 178 // at all coordinates indicated by coords. Simplifies verification of 179 // copied bitmaps. 180 static void reportCopyVerification(const SkBitmap& bm1, const SkBitmap& bm2, 181 Coordinates& coords, 182 const char* msg, 183 skiatest::Reporter* reporter){ 184 // Confirm all pixels in the list match. 185 for (int i = 0; i < coords.length; ++i) { 186 uint32_t p1 = getPixel(coords[i]->fX, coords[i]->fY, bm1); 187 uint32_t p2 = getPixel(coords[i]->fX, coords[i]->fY, bm2); 188 // SkDebugf("[%d] (%d %d) p1=%x p2=%x\n", i, coords[i]->fX, coords[i]->fY, p1, p2); 189 if (p1 != p2) { 190 ERRORF(reporter, "%s [colortype = %s]", msg, color_type_name(bm1.colorType())); 191 break; 192 } 193 } 194 } 195 196 // Writes unique pixel values at locations specified by coords. 197 static void writeCoordPixels(SkBitmap& bm, const Coordinates& coords) { 198 for (int i = 0; i < coords.length; ++i) 199 setPixel(coords[i]->fX, coords[i]->fY, i, bm); 200 } 201 202 static const Pair gPairs[] = { 203 { kUnknown_SkColorType, "0000000" }, 204 { kAlpha_8_SkColorType, "0100000" }, 205 { kIndex_8_SkColorType, "0101111" }, 206 { kRGB_565_SkColorType, "0101011" }, 207 { kARGB_4444_SkColorType, "0101111" }, 208 { kN32_SkColorType, "0101111" }, 209 { kRGBA_F16_SkColorType, "0101011" }, 210 }; 211 212 static const int W = 20; 213 static const int H = 33; 214 215 static void setup_src_bitmaps(SkBitmap* srcOpaque, SkBitmap* srcPremul, 216 SkColorType ct) { 217 SkColorTable* ctable = nullptr; 218 if (kIndex_8_SkColorType == ct) { 219 ctable = init_ctable(); 220 } 221 222 sk_sp<SkColorSpace> colorSpace = nullptr; 223 if (kRGBA_F16_SkColorType == ct) { 224 colorSpace = SkColorSpace::MakeSRGBLinear(); 225 } 226 227 srcOpaque->allocPixels(SkImageInfo::Make(W, H, ct, kOpaque_SkAlphaType, colorSpace), 228 nullptr, ctable); 229 srcPremul->allocPixels(SkImageInfo::Make(W, H, ct, kPremul_SkAlphaType, colorSpace), 230 nullptr, ctable); 231 SkSafeUnref(ctable); 232 233 init_src(*srcOpaque); 234 init_src(*srcPremul); 235 } 236 237 DEF_TEST(BitmapCopy_extractSubset, reporter) { 238 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) { 239 SkBitmap srcOpaque, srcPremul; 240 setup_src_bitmaps(&srcOpaque, &srcPremul, gPairs[i].fColorType); 241 242 SkBitmap bitmap(srcOpaque); 243 SkBitmap subset; 244 SkIRect r; 245 // Extract a subset which has the same width as the original. This 246 // catches a bug where we cloned the genID incorrectly. 247 r.set(0, 1, W, 3); 248 bitmap.setIsVolatile(true); 249 // Relies on old behavior of extractSubset failing if colortype is unknown 250 if (kUnknown_SkColorType != bitmap.colorType() && bitmap.extractSubset(&subset, r)) { 251 REPORTER_ASSERT(reporter, subset.width() == W); 252 REPORTER_ASSERT(reporter, subset.height() == 2); 253 REPORTER_ASSERT(reporter, subset.alphaType() == bitmap.alphaType()); 254 REPORTER_ASSERT(reporter, subset.isVolatile() == true); 255 256 // Test copying an extracted subset. 257 for (size_t j = 0; j < SK_ARRAY_COUNT(gPairs); j++) { 258 SkBitmap copy; 259 bool success = subset.copyTo(©, gPairs[j].fColorType); 260 if (!success) { 261 // Skip checking that success matches fValid, which is redundant 262 // with the code below. 263 REPORTER_ASSERT(reporter, kIndex_8_SkColorType == gPairs[i].fColorType || 264 gPairs[i].fColorType != gPairs[j].fColorType); 265 continue; 266 } 267 268 // When performing a copy of an extracted subset, the gen id should 269 // change. 270 REPORTER_ASSERT(reporter, copy.getGenerationID() != subset.getGenerationID()); 271 272 REPORTER_ASSERT(reporter, copy.width() == W); 273 REPORTER_ASSERT(reporter, copy.height() == 2); 274 275 if (gPairs[i].fColorType == gPairs[j].fColorType) { 276 SkAutoLockPixels alp0(subset); 277 SkAutoLockPixels alp1(copy); 278 // they should both have, or both not-have, a colortable 279 bool hasCT = subset.getColorTable() != nullptr; 280 REPORTER_ASSERT(reporter, (copy.getColorTable() != nullptr) == hasCT); 281 } 282 } 283 } 284 285 bitmap = srcPremul; 286 bitmap.setIsVolatile(false); 287 if (bitmap.extractSubset(&subset, r)) { 288 REPORTER_ASSERT(reporter, subset.alphaType() == bitmap.alphaType()); 289 REPORTER_ASSERT(reporter, subset.isVolatile() == false); 290 } 291 } 292 } 293 294 DEF_TEST(BitmapCopy, reporter) { 295 static const bool isExtracted[] = { 296 false, true 297 }; 298 299 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) { 300 SkBitmap srcOpaque, srcPremul; 301 setup_src_bitmaps(&srcOpaque, &srcPremul, gPairs[i].fColorType); 302 303 for (size_t j = 0; j < SK_ARRAY_COUNT(gPairs); j++) { 304 SkBitmap dst; 305 306 bool success = srcPremul.copyTo(&dst, gPairs[j].fColorType); 307 bool expected = gPairs[i].fValid[j] != '0'; 308 if (success != expected) { 309 ERRORF(reporter, "SkBitmap::copyTo from %s to %s. expected %s " 310 "returned %s", color_type_name(gPairs[i].fColorType), 311 color_type_name(gPairs[j].fColorType), 312 boolStr(expected), boolStr(success)); 313 } 314 315 bool canSucceed = srcPremul.canCopyTo(gPairs[j].fColorType); 316 if (success != canSucceed) { 317 ERRORF(reporter, "SkBitmap::copyTo from %s to %s. returned %s " 318 "canCopyTo %s", color_type_name(gPairs[i].fColorType), 319 color_type_name(gPairs[j].fColorType), 320 boolStr(success), boolStr(canSucceed)); 321 } 322 323 if (success) { 324 REPORTER_ASSERT(reporter, srcPremul.width() == dst.width()); 325 REPORTER_ASSERT(reporter, srcPremul.height() == dst.height()); 326 REPORTER_ASSERT(reporter, dst.colorType() == gPairs[j].fColorType); 327 test_isOpaque(reporter, srcOpaque, srcPremul, dst.colorType()); 328 if (srcPremul.colorType() == dst.colorType()) { 329 SkAutoLockPixels srcLock(srcPremul); 330 SkAutoLockPixels dstLock(dst); 331 REPORTER_ASSERT(reporter, srcPremul.readyToDraw()); 332 REPORTER_ASSERT(reporter, dst.readyToDraw()); 333 const char* srcP = (const char*)srcPremul.getAddr(0, 0); 334 const char* dstP = (const char*)dst.getAddr(0, 0); 335 REPORTER_ASSERT(reporter, srcP != dstP); 336 REPORTER_ASSERT(reporter, !memcmp(srcP, dstP, 337 srcPremul.getSize())); 338 REPORTER_ASSERT(reporter, srcPremul.getGenerationID() == dst.getGenerationID()); 339 } else { 340 REPORTER_ASSERT(reporter, srcPremul.getGenerationID() != dst.getGenerationID()); 341 } 342 } else { 343 // dst should be unchanged from its initial state 344 REPORTER_ASSERT(reporter, dst.colorType() == kUnknown_SkColorType); 345 REPORTER_ASSERT(reporter, dst.width() == 0); 346 REPORTER_ASSERT(reporter, dst.height() == 0); 347 } 348 } // for (size_t j = ... 349 350 // Tests for getSafeSize(), getSafeSize64(), copyPixelsTo(), 351 // copyPixelsFrom(). 352 // 353 for (size_t copyCase = 0; copyCase < SK_ARRAY_COUNT(isExtracted); 354 ++copyCase) { 355 // Test copying to/from external buffer. 356 // Note: the tests below have hard-coded values --- 357 // Please take care if modifying. 358 359 // Tests for getSafeSize64(). 360 // Test with a very large configuration without pixel buffer 361 // attached. 362 SkBitmap tstSafeSize; 363 tstSafeSize.setInfo(SkImageInfo::Make(100000000U, 100000000U, 364 gPairs[i].fColorType, kPremul_SkAlphaType)); 365 int64_t safeSize = tstSafeSize.computeSafeSize64(); 366 if (safeSize < 0) { 367 ERRORF(reporter, "getSafeSize64() negative: %s", 368 color_type_name(tstSafeSize.colorType())); 369 } 370 bool sizeFail = false; 371 // Compare against hand-computed values. 372 switch (gPairs[i].fColorType) { 373 case kUnknown_SkColorType: 374 break; 375 376 case kAlpha_8_SkColorType: 377 case kIndex_8_SkColorType: 378 if (safeSize != 0x2386F26FC10000LL) { 379 sizeFail = true; 380 } 381 break; 382 383 case kRGB_565_SkColorType: 384 case kARGB_4444_SkColorType: 385 if (safeSize != 0x470DE4DF820000LL) { 386 sizeFail = true; 387 } 388 break; 389 390 case kN32_SkColorType: 391 if (safeSize != 0x8E1BC9BF040000LL) { 392 sizeFail = true; 393 } 394 break; 395 396 default: 397 break; 398 } 399 if (sizeFail) { 400 ERRORF(reporter, "computeSafeSize64() wrong size: %s", 401 color_type_name(tstSafeSize.colorType())); 402 } 403 404 int subW = 2; 405 int subH = 2; 406 407 // Create bitmap to act as source for copies and subsets. 408 SkBitmap src, subset; 409 SkColorTable* ct = nullptr; 410 if (kIndex_8_SkColorType == src.colorType()) { 411 ct = init_ctable(); 412 } 413 414 int localSubW; 415 if (isExtracted[copyCase]) { // A larger image to extract from. 416 localSubW = 2 * subW + 1; 417 } else { // Tests expect a 2x2 bitmap, so make smaller. 418 localSubW = subW; 419 } 420 // could fail if we pass kIndex_8 for the colortype 421 if (src.tryAllocPixels(SkImageInfo::Make(localSubW, subH, gPairs[i].fColorType, 422 kPremul_SkAlphaType))) { 423 // failure is fine, as we will notice later on 424 } 425 SkSafeUnref(ct); 426 427 // Either copy src or extract into 'subset', which is used 428 // for subsequent calls to copyPixelsTo/From. 429 bool srcReady = false; 430 // Test relies on older behavior that extractSubset will fail on 431 // kUnknown_SkColorType 432 if (kUnknown_SkColorType != src.colorType() && 433 isExtracted[copyCase]) { 434 // The extractedSubset() test case allows us to test copy- 435 // ing when src and dst mave possibly different strides. 436 SkIRect r; 437 r.set(1, 0, 1 + subW, subH); // 2x2 extracted bitmap 438 439 srcReady = src.extractSubset(&subset, r); 440 } else { 441 srcReady = src.copyTo(&subset); 442 } 443 444 // Not all configurations will generate a valid 'subset'. 445 if (srcReady) { 446 447 // Allocate our target buffer 'buf' for all copies. 448 // To simplify verifying correctness of copies attach 449 // buf to a SkBitmap, but copies are done using the 450 // raw buffer pointer. 451 const size_t bufSize = subH * 452 SkColorTypeMinRowBytes(src.colorType(), subW) * 2; 453 SkAutoTMalloc<uint8_t> autoBuf (bufSize); 454 uint8_t* buf = autoBuf.get(); 455 456 SkBitmap bufBm; // Attach buf to this bitmap. 457 bool successExpected; 458 459 // Set up values for each pixel being copied. 460 Coordinates coords(subW * subH); 461 for (int x = 0; x < subW; ++x) 462 for (int y = 0; y < subH; ++y) 463 { 464 int index = y * subW + x; 465 SkASSERT(index < coords.length); 466 coords[index]->fX = x; 467 coords[index]->fY = y; 468 } 469 470 writeCoordPixels(subset, coords); 471 472 // Test #1 //////////////////////////////////////////// 473 474 const SkImageInfo info = SkImageInfo::Make(subW, subH, 475 gPairs[i].fColorType, 476 kPremul_SkAlphaType); 477 // Before/after comparisons easier if we attach buf 478 // to an appropriately configured SkBitmap. 479 memset(buf, 0xFF, bufSize); 480 // Config with stride greater than src but that fits in buf. 481 bufBm.installPixels(info, buf, info.minRowBytes() * 2); 482 successExpected = false; 483 // Then attempt to copy with a stride that is too large 484 // to fit in the buffer. 485 REPORTER_ASSERT(reporter, 486 subset.copyPixelsTo(buf, bufSize, bufBm.rowBytes() * 3) 487 == successExpected); 488 489 if (successExpected) 490 reportCopyVerification(subset, bufBm, coords, 491 "copyPixelsTo(buf, bufSize, 1.5*maxRowBytes)", 492 reporter); 493 494 // Test #2 //////////////////////////////////////////// 495 // This test should always succeed, but in the case 496 // of extracted bitmaps only because we handle the 497 // issue of getSafeSize(). Without getSafeSize() 498 // buffer overrun/read would occur. 499 memset(buf, 0xFF, bufSize); 500 bufBm.installPixels(info, buf, subset.rowBytes()); 501 successExpected = subset.getSafeSize() <= bufSize; 502 REPORTER_ASSERT(reporter, 503 subset.copyPixelsTo(buf, bufSize) == 504 successExpected); 505 if (successExpected) 506 reportCopyVerification(subset, bufBm, coords, 507 "copyPixelsTo(buf, bufSize)", reporter); 508 509 // Test #3 //////////////////////////////////////////// 510 // Copy with different stride between src and dst. 511 memset(buf, 0xFF, bufSize); 512 bufBm.installPixels(info, buf, subset.rowBytes()+1); 513 successExpected = true; // Should always work. 514 REPORTER_ASSERT(reporter, 515 subset.copyPixelsTo(buf, bufSize, 516 subset.rowBytes()+1) == successExpected); 517 if (successExpected) 518 reportCopyVerification(subset, bufBm, coords, 519 "copyPixelsTo(buf, bufSize, rowBytes+1)", reporter); 520 521 // Test #4 //////////////////////////////////////////// 522 // Test copy with stride too small. 523 memset(buf, 0xFF, bufSize); 524 bufBm.installPixels(info, buf, info.minRowBytes()); 525 successExpected = false; 526 // Request copy with stride too small. 527 REPORTER_ASSERT(reporter, 528 subset.copyPixelsTo(buf, bufSize, bufBm.rowBytes()-1) 529 == successExpected); 530 if (successExpected) 531 reportCopyVerification(subset, bufBm, coords, 532 "copyPixelsTo(buf, bufSize, rowBytes()-1)", reporter); 533 534 #if 0 // copyPixelsFrom is gone 535 // Test #5 //////////////////////////////////////////// 536 // Tests the case where the source stride is too small 537 // for the source configuration. 538 memset(buf, 0xFF, bufSize); 539 bufBm.installPixels(info, buf, info.minRowBytes()); 540 writeCoordPixels(bufBm, coords); 541 REPORTER_ASSERT(reporter, 542 subset.copyPixelsFrom(buf, bufSize, 1) == false); 543 544 // Test #6 /////////////////////////////////////////// 545 // Tests basic copy from an external buffer to the bitmap. 546 // If the bitmap is "extracted", this also tests the case 547 // where the source stride is different from the dest. 548 // stride. 549 // We've made the buffer large enough to always succeed. 550 bufBm.installPixels(info, buf, info.minRowBytes()); 551 writeCoordPixels(bufBm, coords); 552 REPORTER_ASSERT(reporter, 553 subset.copyPixelsFrom(buf, bufSize, bufBm.rowBytes()) == 554 true); 555 reportCopyVerification(bufBm, subset, coords, 556 "copyPixelsFrom(buf, bufSize)", 557 reporter); 558 559 // Test #7 //////////////////////////////////////////// 560 // Tests the case where the source buffer is too small 561 // for the transfer. 562 REPORTER_ASSERT(reporter, 563 subset.copyPixelsFrom(buf, 1, subset.rowBytes()) == 564 false); 565 566 #endif 567 } 568 } // for (size_t copyCase ... 569 } 570 } 571 572 #include "SkColorPriv.h" 573 #include "SkUtils.h" 574 575 /** 576 * Construct 4x4 pixels where we can look at a color and determine where it should be in the grid. 577 * alpha = 0xFF, blue = 0x80, red = x, green = y 578 */ 579 static void fill_4x4_pixels(SkPMColor colors[16]) { 580 for (int y = 0; y < 4; ++y) { 581 for (int x = 0; x < 4; ++x) { 582 colors[y*4+x] = SkPackARGB32(0xFF, x, y, 0x80); 583 } 584 } 585 } 586 587 static bool check_4x4_pixel(SkPMColor color, unsigned x, unsigned y) { 588 SkASSERT(x < 4 && y < 4); 589 return 0xFF == SkGetPackedA32(color) && 590 x == SkGetPackedR32(color) && 591 y == SkGetPackedG32(color) && 592 0x80 == SkGetPackedB32(color); 593 } 594 595 /** 596 * Fill with all zeros, which will never match any value from fill_4x4_pixels 597 */ 598 static void clear_4x4_pixels(SkPMColor colors[16]) { 599 sk_memset32(colors, 0, 16); 600 } 601 602 // Much of readPixels is exercised by copyTo testing, since readPixels is the backend for that 603 // method. Here we explicitly test subset copies. 604 // 605 DEF_TEST(BitmapReadPixels, reporter) { 606 const int W = 4; 607 const int H = 4; 608 const size_t rowBytes = W * sizeof(SkPMColor); 609 const SkImageInfo srcInfo = SkImageInfo::MakeN32Premul(W, H); 610 SkPMColor srcPixels[16]; 611 fill_4x4_pixels(srcPixels); 612 SkBitmap srcBM; 613 srcBM.installPixels(srcInfo, srcPixels, rowBytes); 614 615 SkImageInfo dstInfo = SkImageInfo::MakeN32Premul(W, H); 616 SkPMColor dstPixels[16]; 617 618 const struct { 619 bool fExpectedSuccess; 620 SkIPoint fRequestedSrcLoc; 621 SkISize fRequestedDstSize; 622 // If fExpectedSuccess, check these, otherwise ignore 623 SkIPoint fExpectedDstLoc; 624 SkIRect fExpectedSrcR; 625 } gRec[] = { 626 { true, { 0, 0 }, { 4, 4 }, { 0, 0 }, { 0, 0, 4, 4 } }, 627 { true, { 1, 1 }, { 2, 2 }, { 0, 0 }, { 1, 1, 3, 3 } }, 628 { true, { 2, 2 }, { 4, 4 }, { 0, 0 }, { 2, 2, 4, 4 } }, 629 { true, {-1,-1 }, { 2, 2 }, { 1, 1 }, { 0, 0, 1, 1 } }, 630 { false, {-1,-1 }, { 1, 1 }, { 0, 0 }, { 0, 0, 0, 0 } }, 631 }; 632 633 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { 634 clear_4x4_pixels(dstPixels); 635 636 dstInfo = dstInfo.makeWH(gRec[i].fRequestedDstSize.width(), 637 gRec[i].fRequestedDstSize.height()); 638 bool success = srcBM.readPixels(dstInfo, dstPixels, rowBytes, 639 gRec[i].fRequestedSrcLoc.x(), gRec[i].fRequestedSrcLoc.y()); 640 641 REPORTER_ASSERT(reporter, gRec[i].fExpectedSuccess == success); 642 if (success) { 643 const SkIRect srcR = gRec[i].fExpectedSrcR; 644 const int dstX = gRec[i].fExpectedDstLoc.x(); 645 const int dstY = gRec[i].fExpectedDstLoc.y(); 646 // Walk the dst pixels, and check if we got what we expected 647 for (int y = 0; y < H; ++y) { 648 for (int x = 0; x < W; ++x) { 649 SkPMColor dstC = dstPixels[y*4+x]; 650 // get into src coordinates 651 int sx = x - dstX + srcR.x(); 652 int sy = y - dstY + srcR.y(); 653 if (srcR.contains(sx, sy)) { 654 REPORTER_ASSERT(reporter, check_4x4_pixel(dstC, sx, sy)); 655 } else { 656 REPORTER_ASSERT(reporter, 0 == dstC); 657 } 658 } 659 } 660 } 661 } 662 } 663 664 DEF_TEST(BitmapCopy_ColorSpaceMatch, r) { 665 // We should support matching color spaces, even if they are parametric. 666 SkColorSpaceTransferFn fn; 667 fn.fA = 1.f; fn.fB = 0.f; fn.fC = 0.f; fn.fD = 0.f; fn.fE = 0.f; fn.fF = 0.f; fn.fG = 1.8f; 668 sk_sp<SkColorSpace> cs = SkColorSpace::MakeRGB(fn, SkColorSpace::kRec2020_Gamut); 669 670 SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1, cs); 671 SkBitmap bitmap; 672 bitmap.allocPixels(info); 673 bitmap.eraseColor(0); 674 675 SkBitmap copy; 676 bool success = bitmap.copyTo(©, kN32_SkColorType, nullptr); 677 REPORTER_ASSERT(r, success); 678 REPORTER_ASSERT(r, cs.get() == copy.colorSpace()); 679 } 680