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 #include "Test.h" 9 #include "SkBitmap.h" 10 #include "SkRect.h" 11 12 static const char* boolStr(bool value) { 13 return value ? "true" : "false"; 14 } 15 16 // these are in the same order as the SkBitmap::Config enum 17 static const char* gConfigName[] = { 18 "None", "A1", "A8", "Index8", "565", "4444", "8888", "RLE_Index8" 19 }; 20 21 static void report_opaqueness(skiatest::Reporter* reporter, const SkBitmap& src, 22 const SkBitmap& dst) { 23 SkString str; 24 str.printf("src %s opaque:%d, dst %s opaque:%d", 25 gConfigName[src.config()], src.isOpaque(), 26 gConfigName[dst.config()], dst.isOpaque()); 27 reporter->reportFailed(str); 28 } 29 30 static bool canHaveAlpha(SkBitmap::Config config) { 31 return config != SkBitmap::kRGB_565_Config; 32 } 33 34 // copyTo() should preserve isOpaque when it makes sense 35 static void test_isOpaque(skiatest::Reporter* reporter, const SkBitmap& src, 36 SkBitmap::Config dstConfig) { 37 SkBitmap bitmap(src); 38 SkBitmap dst; 39 40 // we need the lock so that we get a valid colorTable (when available) 41 SkAutoLockPixels alp(bitmap); 42 SkColorTable* ctable = bitmap.getColorTable(); 43 unsigned ctableFlags = ctable ? ctable->getFlags() : 0; 44 45 if (canHaveAlpha(bitmap.config()) && canHaveAlpha(dstConfig)) { 46 bitmap.setIsOpaque(false); 47 if (ctable) { 48 ctable->setFlags(ctableFlags & ~SkColorTable::kColorsAreOpaque_Flag); 49 } 50 REPORTER_ASSERT(reporter, bitmap.copyTo(&dst, dstConfig)); 51 REPORTER_ASSERT(reporter, dst.config() == dstConfig); 52 if (bitmap.isOpaque() != dst.isOpaque()) { 53 report_opaqueness(reporter, bitmap, dst); 54 } 55 } 56 57 bitmap.setIsOpaque(true); 58 if (ctable) { 59 ctable->setFlags(ctableFlags | SkColorTable::kColorsAreOpaque_Flag); 60 } 61 REPORTER_ASSERT(reporter, bitmap.copyTo(&dst, dstConfig)); 62 REPORTER_ASSERT(reporter, dst.config() == dstConfig); 63 if (bitmap.isOpaque() != dst.isOpaque()) { 64 report_opaqueness(reporter, bitmap, dst); 65 } 66 67 if (ctable) { 68 ctable->setFlags(ctableFlags); 69 } 70 } 71 72 static void init_src(const SkBitmap& bitmap, const SkColorTable* ct) { 73 SkAutoLockPixels lock(bitmap); 74 if (bitmap.getPixels()) { 75 if (ct) { 76 sk_bzero(bitmap.getPixels(), bitmap.getSize()); 77 } else { 78 bitmap.eraseColor(SK_ColorWHITE); 79 } 80 } 81 } 82 83 SkColorTable* init_ctable() { 84 static const SkColor colors[] = { 85 SK_ColorBLACK, SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE 86 }; 87 return new SkColorTable(colors, SK_ARRAY_COUNT(colors)); 88 } 89 90 struct Pair { 91 SkBitmap::Config fConfig; 92 const char* fValid; 93 }; 94 95 // Utility functions for copyPixelsTo()/copyPixelsFrom() tests. 96 // getPixel() 97 // setPixel() 98 // getSkConfigName() 99 // struct Coordinates 100 // reportCopyVerification() 101 // writeCoordPixels() 102 103 // Utility function to read the value of a given pixel in bm. All 104 // values converted to uint32_t for simplification of comparisons. 105 uint32_t getPixel(int x, int y, const SkBitmap& bm) { 106 uint32_t val = 0; 107 uint16_t val16; 108 uint8_t val8, shift; 109 SkAutoLockPixels lock(bm); 110 const void* rawAddr = bm.getAddr(x,y); 111 112 switch (bm.getConfig()) { 113 case SkBitmap::kARGB_8888_Config: 114 memcpy(&val, rawAddr, sizeof(uint32_t)); 115 break; 116 case SkBitmap::kARGB_4444_Config: 117 case SkBitmap::kRGB_565_Config: 118 memcpy(&val16, rawAddr, sizeof(uint16_t)); 119 val = val16; 120 break; 121 case SkBitmap::kA8_Config: 122 case SkBitmap::kIndex8_Config: 123 memcpy(&val8, rawAddr, sizeof(uint8_t)); 124 val = val8; 125 break; 126 case SkBitmap::kA1_Config: 127 memcpy(&val8, rawAddr, sizeof(uint8_t)); 128 shift = x % 8; 129 val = (val8 >> shift) & 0x1 ; 130 break; 131 default: 132 break; 133 } 134 return val; 135 } 136 137 // Utility function to set value of any pixel in bm. 138 // bm.getConfig() specifies what format 'val' must be 139 // converted to, but at present uint32_t can handle all formats. 140 void setPixel(int x, int y, uint32_t val, SkBitmap& bm) { 141 uint16_t val16; 142 uint8_t val8, shift; 143 SkAutoLockPixels lock(bm); 144 void* rawAddr = bm.getAddr(x,y); 145 146 switch (bm.getConfig()) { 147 case SkBitmap::kARGB_8888_Config: 148 memcpy(rawAddr, &val, sizeof(uint32_t)); 149 break; 150 case SkBitmap::kARGB_4444_Config: 151 case SkBitmap::kRGB_565_Config: 152 val16 = val & 0xFFFF; 153 memcpy(rawAddr, &val16, sizeof(uint16_t)); 154 break; 155 case SkBitmap::kA8_Config: 156 case SkBitmap::kIndex8_Config: 157 val8 = val & 0xFF; 158 memcpy(rawAddr, &val8, sizeof(uint8_t)); 159 break; 160 case SkBitmap::kA1_Config: 161 shift = x % 8; // We assume we're in the right byte. 162 memcpy(&val8, rawAddr, sizeof(uint8_t)); 163 if (val & 0x1) // Turn bit on. 164 val8 |= (0x1 << shift); 165 else // Turn bit off. 166 val8 &= ~(0x1 << shift); 167 memcpy(rawAddr, &val8, sizeof(uint8_t)); 168 break; 169 default: 170 // Ignore. 171 break; 172 } 173 } 174 175 // Utility to return string containing name of each format, to 176 // simplify diagnostic output. 177 const char* getSkConfigName(const SkBitmap& bm) { 178 switch (bm.getConfig()) { 179 case SkBitmap::kNo_Config: return "SkBitmap::kNo_Config"; 180 case SkBitmap::kA1_Config: return "SkBitmap::kA1_Config"; 181 case SkBitmap::kA8_Config: return "SkBitmap::kA8_Config"; 182 case SkBitmap::kIndex8_Config: return "SkBitmap::kIndex8_Config"; 183 case SkBitmap::kRGB_565_Config: return "SkBitmap::kRGB_565_Config"; 184 case SkBitmap::kARGB_4444_Config: return "SkBitmap::kARGB_4444_Config"; 185 case SkBitmap::kARGB_8888_Config: return "SkBitmap::kARGB_8888_Config"; 186 case SkBitmap::kRLE_Index8_Config: 187 return "SkBitmap::kRLE_Index8_Config,"; 188 default: return "Unknown SkBitmap configuration."; 189 } 190 } 191 192 // Helper struct to contain pixel locations, while avoiding need for STL. 193 struct Coordinates { 194 195 const int length; 196 SkIPoint* const data; 197 198 explicit Coordinates(int _length): length(_length) 199 , data(new SkIPoint[length]) { } 200 201 ~Coordinates(){ 202 delete [] data; 203 } 204 205 SkIPoint* operator[](int i) const { 206 // Use with care, no bounds checking. 207 return data + i; 208 } 209 }; 210 211 // A function to verify that two bitmaps contain the same pixel values 212 // at all coordinates indicated by coords. Simplifies verification of 213 // copied bitmaps. 214 void reportCopyVerification(const SkBitmap& bm1, const SkBitmap& bm2, 215 Coordinates& coords, 216 const char* msg, 217 skiatest::Reporter* reporter){ 218 bool success = true; 219 220 // Confirm all pixels in the list match. 221 for (int i = 0; i < coords.length; ++i) 222 success = success && 223 (getPixel(coords[i]->fX, coords[i]->fY, bm1) == 224 getPixel(coords[i]->fX, coords[i]->fY, bm2)); 225 226 if (!success) { 227 SkString str; 228 str.printf("%s [config = %s]", 229 msg, getSkConfigName(bm1)); 230 reporter->reportFailed(str); 231 } 232 } 233 234 // Writes unique pixel values at locations specified by coords. 235 void writeCoordPixels(SkBitmap& bm, const Coordinates& coords) { 236 for (int i = 0; i < coords.length; ++i) 237 setPixel(coords[i]->fX, coords[i]->fY, i, bm); 238 } 239 240 static void TestBitmapCopy(skiatest::Reporter* reporter) { 241 static const Pair gPairs[] = { 242 { SkBitmap::kNo_Config, "00000000" }, 243 { SkBitmap::kA1_Config, "01000000" }, 244 { SkBitmap::kA8_Config, "00101110" }, 245 { SkBitmap::kIndex8_Config, "00111110" }, 246 { SkBitmap::kRGB_565_Config, "00101110" }, 247 { SkBitmap::kARGB_4444_Config, "00101110" }, 248 { SkBitmap::kARGB_8888_Config, "00101110" }, 249 // TODO: create valid RLE bitmap to test with 250 // { SkBitmap::kRLE_Index8_Config, "00101111" } 251 }; 252 253 static const bool isExtracted[] = { 254 false, true 255 }; 256 257 const int W = 20; 258 const int H = 33; 259 260 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) { 261 for (size_t j = 0; j < SK_ARRAY_COUNT(gPairs); j++) { 262 SkBitmap src, dst; 263 SkColorTable* ct = NULL; 264 265 src.setConfig(gPairs[i].fConfig, W, H); 266 if (SkBitmap::kIndex8_Config == src.config() || 267 SkBitmap::kRLE_Index8_Config == src.config()) { 268 ct = init_ctable(); 269 } 270 src.allocPixels(ct); 271 SkSafeUnref(ct); 272 273 init_src(src, ct); 274 bool success = src.copyTo(&dst, gPairs[j].fConfig); 275 bool expected = gPairs[i].fValid[j] != '0'; 276 if (success != expected) { 277 SkString str; 278 str.printf("SkBitmap::copyTo from %s to %s. expected %s returned %s", 279 gConfigName[i], gConfigName[j], boolStr(expected), 280 boolStr(success)); 281 reporter->reportFailed(str); 282 } 283 284 bool canSucceed = src.canCopyTo(gPairs[j].fConfig); 285 if (success != canSucceed) { 286 SkString str; 287 str.printf("SkBitmap::copyTo from %s to %s. returned %s canCopyTo %s", 288 gConfigName[i], gConfigName[j], boolStr(success), 289 boolStr(canSucceed)); 290 reporter->reportFailed(str); 291 } 292 293 if (success) { 294 REPORTER_ASSERT(reporter, src.width() == dst.width()); 295 REPORTER_ASSERT(reporter, src.height() == dst.height()); 296 REPORTER_ASSERT(reporter, dst.config() == gPairs[j].fConfig); 297 test_isOpaque(reporter, src, dst.config()); 298 if (src.config() == dst.config()) { 299 SkAutoLockPixels srcLock(src); 300 SkAutoLockPixels dstLock(dst); 301 REPORTER_ASSERT(reporter, src.readyToDraw()); 302 REPORTER_ASSERT(reporter, dst.readyToDraw()); 303 const char* srcP = (const char*)src.getAddr(0, 0); 304 const char* dstP = (const char*)dst.getAddr(0, 0); 305 REPORTER_ASSERT(reporter, srcP != dstP); 306 REPORTER_ASSERT(reporter, !memcmp(srcP, dstP, 307 src.getSize())); 308 } 309 // test extractSubset 310 { 311 SkBitmap bitmap(src); 312 SkBitmap subset; 313 SkIRect r; 314 r.set(1, 1, 2, 2); 315 bitmap.setIsVolatile(true); 316 if (bitmap.extractSubset(&subset, r)) { 317 REPORTER_ASSERT(reporter, subset.width() == 1); 318 REPORTER_ASSERT(reporter, subset.height() == 1); 319 REPORTER_ASSERT(reporter, 320 subset.isVolatile() == true); 321 322 SkBitmap copy; 323 REPORTER_ASSERT(reporter, 324 subset.copyTo(©, subset.config())); 325 REPORTER_ASSERT(reporter, copy.width() == 1); 326 REPORTER_ASSERT(reporter, copy.height() == 1); 327 REPORTER_ASSERT(reporter, copy.rowBytes() <= 4); 328 329 SkAutoLockPixels alp0(subset); 330 SkAutoLockPixels alp1(copy); 331 // they should both have, or both not-have, a colortable 332 bool hasCT = subset.getColorTable() != NULL; 333 REPORTER_ASSERT(reporter, 334 (copy.getColorTable() != NULL) == hasCT); 335 } 336 bitmap.setIsVolatile(false); 337 if (bitmap.extractSubset(&subset, r)) { 338 REPORTER_ASSERT(reporter, 339 subset.isVolatile() == false); 340 } 341 } 342 } else { 343 // dst should be unchanged from its initial state 344 REPORTER_ASSERT(reporter, dst.config() == SkBitmap::kNo_Config); 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 if (gPairs[i].fConfig != SkBitmap::kRLE_Index8_Config) { 359 360 // Tests for getSafeSize64(). 361 // Test with a very large configuration without pixel buffer 362 // attached. 363 SkBitmap tstSafeSize; 364 tstSafeSize.setConfig(gPairs[i].fConfig, 100000000U, 365 100000000U); 366 Sk64 safeSize = tstSafeSize.getSafeSize64(); 367 if (safeSize.isNeg()) { 368 SkString str; 369 str.printf("getSafeSize64() negative: %s", 370 getSkConfigName(tstSafeSize)); 371 reporter->reportFailed(str); 372 } 373 bool sizeFail = false; 374 // Compare against hand-computed values. 375 switch (gPairs[i].fConfig) { 376 case SkBitmap::kNo_Config: 377 break; 378 379 case SkBitmap::kA1_Config: 380 if (safeSize.fHi != 0x470DE || 381 safeSize.fLo != 0x4DF82000) 382 sizeFail = true; 383 break; 384 385 case SkBitmap::kA8_Config: 386 case SkBitmap::kIndex8_Config: 387 if (safeSize.fHi != 0x2386F2 || 388 safeSize.fLo != 0x6FC10000) 389 sizeFail = true; 390 break; 391 392 case SkBitmap::kRGB_565_Config: 393 case SkBitmap::kARGB_4444_Config: 394 if (safeSize.fHi != 0x470DE4 || 395 safeSize.fLo != 0xDF820000) 396 sizeFail = true; 397 break; 398 399 case SkBitmap::kARGB_8888_Config: 400 if (safeSize.fHi != 0x8E1BC9 || 401 safeSize.fLo != 0xBF040000) 402 sizeFail = true; 403 break; 404 405 case SkBitmap::kRLE_Index8_Config: 406 break; 407 408 default: 409 break; 410 } 411 if (sizeFail) { 412 SkString str; 413 str.printf("getSafeSize64() wrong size: %s", 414 getSkConfigName(tstSafeSize)); 415 reporter->reportFailed(str); 416 } 417 418 size_t subW, subH; 419 // Set sizes to be height = 2 to force the last row of the 420 // source to be used, thus verifying correct operation if 421 // the bitmap is an extracted subset. 422 if (gPairs[i].fConfig == SkBitmap::kA1_Config) { 423 // If one-bit per pixel, use 9 pixels to force more than 424 // one byte per row. 425 subW = 9; 426 subH = 2; 427 } else { 428 // All other configurations are at least one byte per pixel, 429 // and different configs will test copying different numbers 430 // of bytes. 431 subW = subH = 2; 432 } 433 434 // Create bitmap to act as source for copies and subsets. 435 SkBitmap src, subset; 436 SkColorTable* ct = NULL; 437 if (isExtracted[copyCase]) { // A larger image to extract from. 438 src.setConfig(gPairs[i].fConfig, 2 * subW + 1, subH); 439 } else // Tests expect a 2x2 bitmap, so make smaller. 440 src.setConfig(gPairs[i].fConfig, subW, subH); 441 if (SkBitmap::kIndex8_Config == src.config() || 442 SkBitmap::kRLE_Index8_Config == src.config()) { 443 ct = init_ctable(); 444 } 445 446 src.allocPixels(ct); 447 SkSafeUnref(ct); 448 449 // Either copy src or extract into 'subset', which is used 450 // for subsequent calls to copyPixelsTo/From. 451 bool srcReady = false; 452 if (isExtracted[copyCase]) { 453 // The extractedSubset() test case allows us to test copy- 454 // ing when src and dst mave possibly different strides. 455 SkIRect r; 456 if (gPairs[i].fConfig == SkBitmap::kA1_Config) 457 // This config seems to need byte-alignment of 458 // extracted subset bits. 459 r.set(0, 0, subW, subH); 460 else 461 r.set(1, 0, 1 + subW, subH); // 2x2 extracted bitmap 462 463 srcReady = src.extractSubset(&subset, r); 464 } else { 465 srcReady = src.copyTo(&subset, src.getConfig()); 466 } 467 468 // Not all configurations will generate a valid 'subset'. 469 if (srcReady) { 470 471 // Allocate our target buffer 'buf' for all copies. 472 // To simplify verifying correctness of copies attach 473 // buf to a SkBitmap, but copies are done using the 474 // raw buffer pointer. 475 const uint32_t bufSize = subH * 476 SkBitmap::ComputeRowBytes(src.getConfig(), subW) * 2; 477 uint8_t* buf = new uint8_t[bufSize]; 478 479 SkBitmap bufBm; // Attach buf to this bitmap. 480 bool successExpected; 481 482 // Set up values for each pixel being copied. 483 Coordinates coords(subW * subH); 484 for (size_t x = 0; x < subW; ++x) 485 for (size_t y = 0; y < subH; ++y) 486 { 487 int index = y * subW + x; 488 SkASSERT(index < coords.length); 489 coords[index]->fX = x; 490 coords[index]->fY = y; 491 } 492 493 writeCoordPixels(subset, coords); 494 495 // Test #1 //////////////////////////////////////////// 496 497 // Before/after comparisons easier if we attach buf 498 // to an appropriately configured SkBitmap. 499 memset(buf, 0xFF, bufSize); 500 // Config with stride greater than src but that fits in buf. 501 bufBm.setConfig(gPairs[i].fConfig, subW, subH, 502 SkBitmap::ComputeRowBytes(subset.getConfig(), subW) 503 * 2); 504 bufBm.setPixels(buf); 505 successExpected = false; 506 // Then attempt to copy with a stride that is too large 507 // to fit in the buffer. 508 REPORTER_ASSERT(reporter, 509 subset.copyPixelsTo(buf, bufSize, bufBm.rowBytes() * 3) 510 == successExpected); 511 512 if (successExpected) 513 reportCopyVerification(subset, bufBm, coords, 514 "copyPixelsTo(buf, bufSize, 1.5*maxRowBytes)", 515 reporter); 516 517 // Test #2 //////////////////////////////////////////// 518 // This test should always succeed, but in the case 519 // of extracted bitmaps only because we handle the 520 // issue of getSafeSize(). Without getSafeSize() 521 // buffer overrun/read would occur. 522 memset(buf, 0xFF, bufSize); 523 bufBm.setConfig(gPairs[i].fConfig, subW, subH, 524 subset.rowBytes()); 525 bufBm.setPixels(buf); 526 successExpected = subset.getSafeSize() <= bufSize; 527 REPORTER_ASSERT(reporter, 528 subset.copyPixelsTo(buf, bufSize) == 529 successExpected); 530 if (successExpected) 531 reportCopyVerification(subset, bufBm, coords, 532 "copyPixelsTo(buf, bufSize)", reporter); 533 534 // Test #3 //////////////////////////////////////////// 535 // Copy with different stride between src and dst. 536 memset(buf, 0xFF, bufSize); 537 bufBm.setConfig(gPairs[i].fConfig, subW, subH, 538 subset.rowBytes()+1); 539 bufBm.setPixels(buf); 540 successExpected = true; // Should always work. 541 REPORTER_ASSERT(reporter, 542 subset.copyPixelsTo(buf, bufSize, 543 subset.rowBytes()+1) == successExpected); 544 if (successExpected) 545 reportCopyVerification(subset, bufBm, coords, 546 "copyPixelsTo(buf, bufSize, rowBytes+1)", reporter); 547 548 // Test #4 //////////////////////////////////////////// 549 // Test copy with stride too small. 550 memset(buf, 0xFF, bufSize); 551 bufBm.setConfig(gPairs[i].fConfig, subW, subH); 552 bufBm.setPixels(buf); 553 successExpected = false; 554 // Request copy with stride too small. 555 REPORTER_ASSERT(reporter, 556 subset.copyPixelsTo(buf, bufSize, bufBm.rowBytes()-1) 557 == successExpected); 558 if (successExpected) 559 reportCopyVerification(subset, bufBm, coords, 560 "copyPixelsTo(buf, bufSize, rowBytes()-1)", reporter); 561 562 #if 0 // copyPixelsFrom is gone 563 // Test #5 //////////////////////////////////////////// 564 // Tests the case where the source stride is too small 565 // for the source configuration. 566 memset(buf, 0xFF, bufSize); 567 bufBm.setConfig(gPairs[i].fConfig, subW, subH); 568 bufBm.setPixels(buf); 569 writeCoordPixels(bufBm, coords); 570 REPORTER_ASSERT(reporter, 571 subset.copyPixelsFrom(buf, bufSize, 1) == false); 572 573 // Test #6 /////////////////////////////////////////// 574 // Tests basic copy from an external buffer to the bitmap. 575 // If the bitmap is "extracted", this also tests the case 576 // where the source stride is different from the dest. 577 // stride. 578 // We've made the buffer large enough to always succeed. 579 bufBm.setConfig(gPairs[i].fConfig, subW, subH); 580 bufBm.setPixels(buf); 581 writeCoordPixels(bufBm, coords); 582 REPORTER_ASSERT(reporter, 583 subset.copyPixelsFrom(buf, bufSize, bufBm.rowBytes()) == 584 true); 585 reportCopyVerification(bufBm, subset, coords, 586 "copyPixelsFrom(buf, bufSize)", 587 reporter); 588 589 // Test #7 //////////////////////////////////////////// 590 // Tests the case where the source buffer is too small 591 // for the transfer. 592 REPORTER_ASSERT(reporter, 593 subset.copyPixelsFrom(buf, 1, subset.rowBytes()) == 594 false); 595 596 delete [] buf; 597 #endif 598 } 599 } 600 } // for (size_t copyCase ... 601 } 602 } 603 604 #include "TestClassDef.h" 605 DEFINE_TESTCLASS("BitmapCopy", TestBitmapCopyClass, TestBitmapCopy) 606