BitmapCopyTest.cpp revision 149e9a107c356b0151433fb23c2b1c8d0634947c
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 "Test.h" 11 12static const char* boolStr(bool value) { 13 return value ? "true" : "false"; 14} 15 16// these are in the same order as the SkColorType enum 17static const char* gColorTypeName[] = { 18 "None", "A8", "565", "4444", "RGBA", "BGRA", "Index8" 19}; 20 21static void report_opaqueness(skiatest::Reporter* reporter, const SkBitmap& src, 22 const SkBitmap& dst) { 23 ERRORF(reporter, "src %s opaque:%d, dst %s opaque:%d", 24 gColorTypeName[src.colorType()], src.isOpaque(), 25 gColorTypeName[dst.colorType()], dst.isOpaque()); 26} 27 28static bool canHaveAlpha(SkColorType ct) { 29 return kRGB_565_SkColorType != ct; 30} 31 32// copyTo() should preserve isOpaque when it makes sense 33static void test_isOpaque(skiatest::Reporter* reporter, 34 const SkBitmap& srcOpaque, const SkBitmap& srcPremul, 35 SkColorType dstColorType) { 36 SkBitmap dst; 37 38 if (canHaveAlpha(srcPremul.colorType()) && canHaveAlpha(dstColorType)) { 39 REPORTER_ASSERT(reporter, srcPremul.copyTo(&dst, dstColorType)); 40 REPORTER_ASSERT(reporter, dst.colorType() == dstColorType); 41 if (srcPremul.isOpaque() != dst.isOpaque()) { 42 report_opaqueness(reporter, srcPremul, dst); 43 } 44 } 45 46 REPORTER_ASSERT(reporter, srcOpaque.copyTo(&dst, dstColorType)); 47 REPORTER_ASSERT(reporter, dst.colorType() == dstColorType); 48 if (srcOpaque.isOpaque() != dst.isOpaque()) { 49 report_opaqueness(reporter, srcOpaque, dst); 50 } 51} 52 53static void init_src(const SkBitmap& bitmap) { 54 SkAutoLockPixels lock(bitmap); 55 if (bitmap.getPixels()) { 56 if (bitmap.getColorTable()) { 57 sk_bzero(bitmap.getPixels(), bitmap.getSize()); 58 } else { 59 bitmap.eraseColor(SK_ColorWHITE); 60 } 61 } 62} 63 64static SkColorTable* init_ctable(SkAlphaType alphaType) { 65 static const SkColor colors[] = { 66 SK_ColorBLACK, SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE 67 }; 68 return new SkColorTable(colors, SK_ARRAY_COUNT(colors), alphaType); 69} 70 71struct Pair { 72 SkColorType fColorType; 73 const char* fValid; 74}; 75 76// Utility functions for copyPixelsTo()/copyPixelsFrom() tests. 77// getPixel() 78// setPixel() 79// getSkConfigName() 80// struct Coordinates 81// reportCopyVerification() 82// writeCoordPixels() 83 84// Utility function to read the value of a given pixel in bm. All 85// values converted to uint32_t for simplification of comparisons. 86static uint32_t getPixel(int x, int y, const SkBitmap& bm) { 87 uint32_t val = 0; 88 uint16_t val16; 89 uint8_t val8; 90 SkAutoLockPixels lock(bm); 91 const void* rawAddr = bm.getAddr(x,y); 92 93 switch (bm.bytesPerPixel()) { 94 case 4: 95 memcpy(&val, rawAddr, sizeof(uint32_t)); 96 break; 97 case 2: 98 memcpy(&val16, rawAddr, sizeof(uint16_t)); 99 val = val16; 100 break; 101 case 1: 102 memcpy(&val8, rawAddr, sizeof(uint8_t)); 103 val = val8; 104 break; 105 default: 106 break; 107 } 108 return val; 109} 110 111// Utility function to set value of any pixel in bm. 112// bm.getConfig() specifies what format 'val' must be 113// converted to, but at present uint32_t can handle all formats. 114static void setPixel(int x, int y, uint32_t val, SkBitmap& bm) { 115 uint16_t val16; 116 uint8_t val8; 117 SkAutoLockPixels lock(bm); 118 void* rawAddr = bm.getAddr(x,y); 119 120 switch (bm.bytesPerPixel()) { 121 case 4: 122 memcpy(rawAddr, &val, sizeof(uint32_t)); 123 break; 124 case 2: 125 val16 = val & 0xFFFF; 126 memcpy(rawAddr, &val16, sizeof(uint16_t)); 127 break; 128 case 1: 129 val8 = val & 0xFF; 130 memcpy(rawAddr, &val8, sizeof(uint8_t)); 131 break; 132 default: 133 // Ignore. 134 break; 135 } 136} 137 138// Helper struct to contain pixel locations, while avoiding need for STL. 139struct Coordinates { 140 141 const int length; 142 SkIPoint* const data; 143 144 explicit Coordinates(int _length): length(_length) 145 , data(new SkIPoint[length]) { } 146 147 ~Coordinates(){ 148 delete [] data; 149 } 150 151 SkIPoint* operator[](int i) const { 152 // Use with care, no bounds checking. 153 return data + i; 154 } 155}; 156 157// A function to verify that two bitmaps contain the same pixel values 158// at all coordinates indicated by coords. Simplifies verification of 159// copied bitmaps. 160static void reportCopyVerification(const SkBitmap& bm1, const SkBitmap& bm2, 161 Coordinates& coords, 162 const char* msg, 163 skiatest::Reporter* reporter){ 164 bool success = true; 165 166 // Confirm all pixels in the list match. 167 for (int i = 0; i < coords.length; ++i) { 168 success = success && 169 (getPixel(coords[i]->fX, coords[i]->fY, bm1) == 170 getPixel(coords[i]->fX, coords[i]->fY, bm2)); 171 } 172 173 if (!success) { 174 ERRORF(reporter, "%s [colortype = %s]", msg, 175 gColorTypeName[bm1.colorType()]); 176 } 177} 178 179// Writes unique pixel values at locations specified by coords. 180static void writeCoordPixels(SkBitmap& bm, const Coordinates& coords) { 181 for (int i = 0; i < coords.length; ++i) 182 setPixel(coords[i]->fX, coords[i]->fY, i, bm); 183} 184 185static const Pair gPairs[] = { 186 { kUnknown_SkColorType, "000000" }, 187 { kAlpha_8_SkColorType, "010101" }, 188 { kIndex_8_SkColorType, "011101" }, 189 { kRGB_565_SkColorType, "010101" }, 190 { kARGB_4444_SkColorType, "010111" }, 191 { kN32_SkColorType, "010111" }, 192}; 193 194static const int W = 20; 195static const int H = 33; 196 197static void setup_src_bitmaps(SkBitmap* srcOpaque, SkBitmap* srcPremul, 198 SkColorType ct) { 199 SkColorTable* ctOpaque = NULL; 200 SkColorTable* ctPremul = NULL; 201 if (kIndex_8_SkColorType == ct) { 202 ctOpaque = init_ctable(kOpaque_SkAlphaType); 203 ctPremul = init_ctable(kPremul_SkAlphaType); 204 } 205 206 srcOpaque->allocPixels(SkImageInfo::Make(W, H, ct, kOpaque_SkAlphaType), 207 NULL, ctOpaque); 208 srcPremul->allocPixels(SkImageInfo::Make(W, H, ct, kPremul_SkAlphaType), 209 NULL, ctPremul); 210 SkSafeUnref(ctOpaque); 211 SkSafeUnref(ctPremul); 212 init_src(*srcOpaque); 213 init_src(*srcPremul); 214} 215 216DEF_TEST(BitmapCopy_extractSubset, reporter) { 217 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) { 218 SkBitmap srcOpaque, srcPremul; 219 setup_src_bitmaps(&srcOpaque, &srcPremul, gPairs[i].fColorType); 220 221 SkBitmap bitmap(srcOpaque); 222 SkBitmap subset; 223 SkIRect r; 224 // Extract a subset which has the same width as the original. This 225 // catches a bug where we cloned the genID incorrectly. 226 r.set(0, 1, W, 3); 227 bitmap.setIsVolatile(true); 228 // Relies on old behavior of extractSubset failing if colortype is unknown 229 if (kUnknown_SkColorType != bitmap.colorType() && bitmap.extractSubset(&subset, r)) { 230 REPORTER_ASSERT(reporter, subset.width() == W); 231 REPORTER_ASSERT(reporter, subset.height() == 2); 232 REPORTER_ASSERT(reporter, subset.alphaType() == bitmap.alphaType()); 233 REPORTER_ASSERT(reporter, subset.isVolatile() == true); 234 235 // Test copying an extracted subset. 236 for (size_t j = 0; j < SK_ARRAY_COUNT(gPairs); j++) { 237 SkBitmap copy; 238 bool success = subset.copyTo(©, gPairs[j].fColorType); 239 if (!success) { 240 // Skip checking that success matches fValid, which is redundant 241 // with the code below. 242 REPORTER_ASSERT(reporter, gPairs[i].fColorType != gPairs[j].fColorType); 243 continue; 244 } 245 246 // When performing a copy of an extracted subset, the gen id should 247 // change. 248 REPORTER_ASSERT(reporter, copy.getGenerationID() != subset.getGenerationID()); 249 250 REPORTER_ASSERT(reporter, copy.width() == W); 251 REPORTER_ASSERT(reporter, copy.height() == 2); 252 253 if (gPairs[i].fColorType == gPairs[j].fColorType) { 254 SkAutoLockPixels alp0(subset); 255 SkAutoLockPixels alp1(copy); 256 // they should both have, or both not-have, a colortable 257 bool hasCT = subset.getColorTable() != NULL; 258 REPORTER_ASSERT(reporter, (copy.getColorTable() != NULL) == hasCT); 259 } 260 } 261 } 262 263 bitmap = srcPremul; 264 bitmap.setIsVolatile(false); 265 if (bitmap.extractSubset(&subset, r)) { 266 REPORTER_ASSERT(reporter, subset.alphaType() == bitmap.alphaType()); 267 REPORTER_ASSERT(reporter, subset.isVolatile() == false); 268 } 269 } 270} 271 272DEF_TEST(BitmapCopy, reporter) { 273 static const bool isExtracted[] = { 274 false, true 275 }; 276 277 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) { 278 SkBitmap srcOpaque, srcPremul; 279 setup_src_bitmaps(&srcOpaque, &srcPremul, gPairs[i].fColorType); 280 281 for (size_t j = 0; j < SK_ARRAY_COUNT(gPairs); j++) { 282 SkBitmap dst; 283 284 bool success = srcPremul.copyTo(&dst, gPairs[j].fColorType); 285 bool expected = gPairs[i].fValid[j] != '0'; 286 if (success != expected) { 287 ERRORF(reporter, "SkBitmap::copyTo from %s to %s. expected %s " 288 "returned %s", gColorTypeName[i], gColorTypeName[j], 289 boolStr(expected), boolStr(success)); 290 } 291 292 bool canSucceed = srcPremul.canCopyTo(gPairs[j].fColorType); 293 if (success != canSucceed) { 294 ERRORF(reporter, "SkBitmap::copyTo from %s to %s. returned %s " 295 "canCopyTo %s", gColorTypeName[i], gColorTypeName[j], 296 boolStr(success), boolStr(canSucceed)); 297 } 298 299 if (success) { 300 REPORTER_ASSERT(reporter, srcPremul.width() == dst.width()); 301 REPORTER_ASSERT(reporter, srcPremul.height() == dst.height()); 302 REPORTER_ASSERT(reporter, dst.colorType() == gPairs[j].fColorType); 303 test_isOpaque(reporter, srcOpaque, srcPremul, dst.colorType()); 304 if (srcPremul.colorType() == dst.colorType()) { 305 SkAutoLockPixels srcLock(srcPremul); 306 SkAutoLockPixels dstLock(dst); 307 REPORTER_ASSERT(reporter, srcPremul.readyToDraw()); 308 REPORTER_ASSERT(reporter, dst.readyToDraw()); 309 const char* srcP = (const char*)srcPremul.getAddr(0, 0); 310 const char* dstP = (const char*)dst.getAddr(0, 0); 311 REPORTER_ASSERT(reporter, srcP != dstP); 312 REPORTER_ASSERT(reporter, !memcmp(srcP, dstP, 313 srcPremul.getSize())); 314 REPORTER_ASSERT(reporter, srcPremul.getGenerationID() == dst.getGenerationID()); 315 } else { 316 REPORTER_ASSERT(reporter, srcPremul.getGenerationID() != dst.getGenerationID()); 317 } 318 } else { 319 // dst should be unchanged from its initial state 320 REPORTER_ASSERT(reporter, dst.colorType() == kUnknown_SkColorType); 321 REPORTER_ASSERT(reporter, dst.width() == 0); 322 REPORTER_ASSERT(reporter, dst.height() == 0); 323 } 324 } // for (size_t j = ... 325 326 // Tests for getSafeSize(), getSafeSize64(), copyPixelsTo(), 327 // copyPixelsFrom(). 328 // 329 for (size_t copyCase = 0; copyCase < SK_ARRAY_COUNT(isExtracted); 330 ++copyCase) { 331 // Test copying to/from external buffer. 332 // Note: the tests below have hard-coded values --- 333 // Please take care if modifying. 334 335 // Tests for getSafeSize64(). 336 // Test with a very large configuration without pixel buffer 337 // attached. 338 SkBitmap tstSafeSize; 339 tstSafeSize.setConfig(SkImageInfo::Make(100000000U, 100000000U, 340 gPairs[i].fColorType, 341 kPremul_SkAlphaType)); 342 int64_t safeSize = tstSafeSize.computeSafeSize64(); 343 if (safeSize < 0) { 344 ERRORF(reporter, "getSafeSize64() negative: %s", 345 gColorTypeName[tstSafeSize.colorType()]); 346 } 347 bool sizeFail = false; 348 // Compare against hand-computed values. 349 switch (gPairs[i].fColorType) { 350 case kUnknown_SkColorType: 351 break; 352 353 case kAlpha_8_SkColorType: 354 case kIndex_8_SkColorType: 355 if (safeSize != 0x2386F26FC10000LL) { 356 sizeFail = true; 357 } 358 break; 359 360 case kRGB_565_SkColorType: 361 case kARGB_4444_SkColorType: 362 if (safeSize != 0x470DE4DF820000LL) { 363 sizeFail = true; 364 } 365 break; 366 367 case kN32_SkColorType: 368 if (safeSize != 0x8E1BC9BF040000LL) { 369 sizeFail = true; 370 } 371 break; 372 373 default: 374 break; 375 } 376 if (sizeFail) { 377 ERRORF(reporter, "computeSafeSize64() wrong size: %s", 378 gColorTypeName[tstSafeSize.colorType()]); 379 } 380 381 int subW = 2; 382 int subH = 2; 383 384 // Create bitmap to act as source for copies and subsets. 385 SkBitmap src, subset; 386 SkColorTable* ct = NULL; 387 if (kIndex_8_SkColorType == src.colorType()) { 388 ct = init_ctable(kPremul_SkAlphaType); 389 } 390 391 if (isExtracted[copyCase]) { // A larger image to extract from. 392 src.allocPixels(SkImageInfo::Make(2 * subW + 1, subH, 393 gPairs[i].fColorType, 394 kPremul_SkAlphaType)); 395 } else { // Tests expect a 2x2 bitmap, so make smaller. 396 src.allocPixels(SkImageInfo::Make(subW, subH, 397 gPairs[i].fColorType, 398 kPremul_SkAlphaType)); 399 } 400 SkSafeUnref(ct); 401 402 // Either copy src or extract into 'subset', which is used 403 // for subsequent calls to copyPixelsTo/From. 404 bool srcReady = false; 405 // Test relies on older behavior that extractSubset will fail on 406 // kUnknown_SkColorType 407 if (kUnknown_SkColorType != src.colorType() && 408 isExtracted[copyCase]) { 409 // The extractedSubset() test case allows us to test copy- 410 // ing when src and dst mave possibly different strides. 411 SkIRect r; 412 r.set(1, 0, 1 + subW, subH); // 2x2 extracted bitmap 413 414 srcReady = src.extractSubset(&subset, r); 415 } else { 416 srcReady = src.copyTo(&subset); 417 } 418 419 // Not all configurations will generate a valid 'subset'. 420 if (srcReady) { 421 422 // Allocate our target buffer 'buf' for all copies. 423 // To simplify verifying correctness of copies attach 424 // buf to a SkBitmap, but copies are done using the 425 // raw buffer pointer. 426 const size_t bufSize = subH * 427 SkColorTypeMinRowBytes(src.colorType(), subW) * 2; 428 SkAutoMalloc autoBuf (bufSize); 429 uint8_t* buf = static_cast<uint8_t*>(autoBuf.get()); 430 431 SkBitmap bufBm; // Attach buf to this bitmap. 432 bool successExpected; 433 434 // Set up values for each pixel being copied. 435 Coordinates coords(subW * subH); 436 for (int x = 0; x < subW; ++x) 437 for (int y = 0; y < subH; ++y) 438 { 439 int index = y * subW + x; 440 SkASSERT(index < coords.length); 441 coords[index]->fX = x; 442 coords[index]->fY = y; 443 } 444 445 writeCoordPixels(subset, coords); 446 447 // Test #1 //////////////////////////////////////////// 448 449 const SkImageInfo info = SkImageInfo::Make(subW, subH, 450 gPairs[i].fColorType, 451 kPremul_SkAlphaType); 452 // Before/after comparisons easier if we attach buf 453 // to an appropriately configured SkBitmap. 454 memset(buf, 0xFF, bufSize); 455 // Config with stride greater than src but that fits in buf. 456 bufBm.installPixels(info, buf, info.minRowBytes() * 2); 457 successExpected = false; 458 // Then attempt to copy with a stride that is too large 459 // to fit in the buffer. 460 REPORTER_ASSERT(reporter, 461 subset.copyPixelsTo(buf, bufSize, bufBm.rowBytes() * 3) 462 == successExpected); 463 464 if (successExpected) 465 reportCopyVerification(subset, bufBm, coords, 466 "copyPixelsTo(buf, bufSize, 1.5*maxRowBytes)", 467 reporter); 468 469 // Test #2 //////////////////////////////////////////// 470 // This test should always succeed, but in the case 471 // of extracted bitmaps only because we handle the 472 // issue of getSafeSize(). Without getSafeSize() 473 // buffer overrun/read would occur. 474 memset(buf, 0xFF, bufSize); 475 bufBm.installPixels(info, buf, subset.rowBytes()); 476 successExpected = subset.getSafeSize() <= bufSize; 477 REPORTER_ASSERT(reporter, 478 subset.copyPixelsTo(buf, bufSize) == 479 successExpected); 480 if (successExpected) 481 reportCopyVerification(subset, bufBm, coords, 482 "copyPixelsTo(buf, bufSize)", reporter); 483 484 // Test #3 //////////////////////////////////////////// 485 // Copy with different stride between src and dst. 486 memset(buf, 0xFF, bufSize); 487 bufBm.installPixels(info, buf, subset.rowBytes()+1); 488 successExpected = true; // Should always work. 489 REPORTER_ASSERT(reporter, 490 subset.copyPixelsTo(buf, bufSize, 491 subset.rowBytes()+1) == successExpected); 492 if (successExpected) 493 reportCopyVerification(subset, bufBm, coords, 494 "copyPixelsTo(buf, bufSize, rowBytes+1)", reporter); 495 496 // Test #4 //////////////////////////////////////////// 497 // Test copy with stride too small. 498 memset(buf, 0xFF, bufSize); 499 bufBm.installPixels(info, buf, info.minRowBytes()); 500 successExpected = false; 501 // Request copy with stride too small. 502 REPORTER_ASSERT(reporter, 503 subset.copyPixelsTo(buf, bufSize, bufBm.rowBytes()-1) 504 == successExpected); 505 if (successExpected) 506 reportCopyVerification(subset, bufBm, coords, 507 "copyPixelsTo(buf, bufSize, rowBytes()-1)", reporter); 508 509#if 0 // copyPixelsFrom is gone 510 // Test #5 //////////////////////////////////////////// 511 // Tests the case where the source stride is too small 512 // for the source configuration. 513 memset(buf, 0xFF, bufSize); 514 bufBm.installPixels(info, buf, info.minRowBytes()); 515 writeCoordPixels(bufBm, coords); 516 REPORTER_ASSERT(reporter, 517 subset.copyPixelsFrom(buf, bufSize, 1) == false); 518 519 // Test #6 /////////////////////////////////////////// 520 // Tests basic copy from an external buffer to the bitmap. 521 // If the bitmap is "extracted", this also tests the case 522 // where the source stride is different from the dest. 523 // stride. 524 // We've made the buffer large enough to always succeed. 525 bufBm.installPixels(info, buf, info.minRowBytes()); 526 writeCoordPixels(bufBm, coords); 527 REPORTER_ASSERT(reporter, 528 subset.copyPixelsFrom(buf, bufSize, bufBm.rowBytes()) == 529 true); 530 reportCopyVerification(bufBm, subset, coords, 531 "copyPixelsFrom(buf, bufSize)", 532 reporter); 533 534 // Test #7 //////////////////////////////////////////// 535 // Tests the case where the source buffer is too small 536 // for the transfer. 537 REPORTER_ASSERT(reporter, 538 subset.copyPixelsFrom(buf, 1, subset.rowBytes()) == 539 false); 540 541#endif 542 } 543 } // for (size_t copyCase ... 544 } 545} 546