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 static 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 static 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 static 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 static 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 default: return "Unknown SkBitmap configuration."; 187 } 188 } 189 190 // Helper struct to contain pixel locations, while avoiding need for STL. 191 struct Coordinates { 192 193 const int length; 194 SkIPoint* const data; 195 196 explicit Coordinates(int _length): length(_length) 197 , data(new SkIPoint[length]) { } 198 199 ~Coordinates(){ 200 delete [] data; 201 } 202 203 SkIPoint* operator[](int i) const { 204 // Use with care, no bounds checking. 205 return data + i; 206 } 207 }; 208 209 // A function to verify that two bitmaps contain the same pixel values 210 // at all coordinates indicated by coords. Simplifies verification of 211 // copied bitmaps. 212 static void reportCopyVerification(const SkBitmap& bm1, const SkBitmap& bm2, 213 Coordinates& coords, 214 const char* msg, 215 skiatest::Reporter* reporter){ 216 bool success = true; 217 218 // Confirm all pixels in the list match. 219 for (int i = 0; i < coords.length; ++i) { 220 success = success && 221 (getPixel(coords[i]->fX, coords[i]->fY, bm1) == 222 getPixel(coords[i]->fX, coords[i]->fY, bm2)); 223 } 224 225 if (!success) { 226 SkString str; 227 str.printf("%s [config = %s]", 228 msg, getSkConfigName(bm1)); 229 reporter->reportFailed(str); 230 } 231 } 232 233 // Writes unique pixel values at locations specified by coords. 234 static void writeCoordPixels(SkBitmap& bm, const Coordinates& coords) { 235 for (int i = 0; i < coords.length; ++i) 236 setPixel(coords[i]->fX, coords[i]->fY, i, bm); 237 } 238 239 static void TestBitmapCopy(skiatest::Reporter* reporter) { 240 static const Pair gPairs[] = { 241 { SkBitmap::kNo_Config, "00000000" }, 242 { SkBitmap::kA1_Config, "01000000" }, 243 { SkBitmap::kA8_Config, "00101010" }, 244 { SkBitmap::kIndex8_Config, "00111010" }, 245 { SkBitmap::kRGB_565_Config, "00101010" }, 246 { SkBitmap::kARGB_4444_Config, "00101110" }, 247 { SkBitmap::kARGB_8888_Config, "00101110" }, 248 }; 249 250 static const bool isExtracted[] = { 251 false, true 252 }; 253 254 const int W = 20; 255 const int H = 33; 256 257 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) { 258 for (size_t j = 0; j < SK_ARRAY_COUNT(gPairs); j++) { 259 SkBitmap src, dst; 260 SkColorTable* ct = NULL; 261 262 src.setConfig(gPairs[i].fConfig, W, H); 263 if (SkBitmap::kIndex8_Config == src.config()) { 264 ct = init_ctable(); 265 } 266 src.allocPixels(ct); 267 SkSafeUnref(ct); 268 269 init_src(src, ct); 270 bool success = src.copyTo(&dst, gPairs[j].fConfig); 271 bool expected = gPairs[i].fValid[j] != '0'; 272 if (success != expected) { 273 SkString str; 274 str.printf("SkBitmap::copyTo from %s to %s. expected %s returned %s", 275 gConfigName[i], gConfigName[j], boolStr(expected), 276 boolStr(success)); 277 reporter->reportFailed(str); 278 } 279 280 bool canSucceed = src.canCopyTo(gPairs[j].fConfig); 281 if (success != canSucceed) { 282 SkString str; 283 str.printf("SkBitmap::copyTo from %s to %s. returned %s canCopyTo %s", 284 gConfigName[i], gConfigName[j], boolStr(success), 285 boolStr(canSucceed)); 286 reporter->reportFailed(str); 287 } 288 289 if (success) { 290 REPORTER_ASSERT(reporter, src.width() == dst.width()); 291 REPORTER_ASSERT(reporter, src.height() == dst.height()); 292 REPORTER_ASSERT(reporter, dst.config() == gPairs[j].fConfig); 293 test_isOpaque(reporter, src, dst.config()); 294 if (src.config() == dst.config()) { 295 SkAutoLockPixels srcLock(src); 296 SkAutoLockPixels dstLock(dst); 297 REPORTER_ASSERT(reporter, src.readyToDraw()); 298 REPORTER_ASSERT(reporter, dst.readyToDraw()); 299 const char* srcP = (const char*)src.getAddr(0, 0); 300 const char* dstP = (const char*)dst.getAddr(0, 0); 301 REPORTER_ASSERT(reporter, srcP != dstP); 302 REPORTER_ASSERT(reporter, !memcmp(srcP, dstP, 303 src.getSize())); 304 REPORTER_ASSERT(reporter, src.getGenerationID() == dst.getGenerationID()); 305 } else { 306 REPORTER_ASSERT(reporter, src.getGenerationID() != dst.getGenerationID()); 307 } 308 // test extractSubset 309 { 310 SkBitmap bitmap(src); 311 SkBitmap subset; 312 SkIRect r; 313 r.set(1, 1, 2, 2); 314 bitmap.setIsOpaque(true); 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.isOpaque() == bitmap.isOpaque()); 321 REPORTER_ASSERT(reporter, 322 subset.isVolatile() == true); 323 324 SkBitmap copy; 325 REPORTER_ASSERT(reporter, 326 subset.copyTo(©, subset.config())); 327 REPORTER_ASSERT(reporter, copy.width() == 1); 328 REPORTER_ASSERT(reporter, copy.height() == 1); 329 REPORTER_ASSERT(reporter, copy.rowBytes() <= 4); 330 331 SkAutoLockPixels alp0(subset); 332 SkAutoLockPixels alp1(copy); 333 // they should both have, or both not-have, a colortable 334 bool hasCT = subset.getColorTable() != NULL; 335 REPORTER_ASSERT(reporter, 336 (copy.getColorTable() != NULL) == hasCT); 337 } 338 bitmap.setIsOpaque(false); 339 bitmap.setIsVolatile(false); 340 if (bitmap.extractSubset(&subset, r)) { 341 REPORTER_ASSERT(reporter, 342 subset.isOpaque() == bitmap.isOpaque()); 343 REPORTER_ASSERT(reporter, 344 subset.isVolatile() == false); 345 } 346 } 347 } else { 348 // dst should be unchanged from its initial state 349 REPORTER_ASSERT(reporter, dst.config() == SkBitmap::kNo_Config); 350 REPORTER_ASSERT(reporter, dst.width() == 0); 351 REPORTER_ASSERT(reporter, dst.height() == 0); 352 } 353 } // for (size_t j = ... 354 355 // Tests for getSafeSize(), getSafeSize64(), copyPixelsTo(), 356 // copyPixelsFrom(). 357 // 358 for (size_t copyCase = 0; copyCase < SK_ARRAY_COUNT(isExtracted); 359 ++copyCase) { 360 // Test copying to/from external buffer. 361 // Note: the tests below have hard-coded values --- 362 // Please take care if modifying. 363 364 // Tests for getSafeSize64(). 365 // Test with a very large configuration without pixel buffer 366 // attached. 367 SkBitmap tstSafeSize; 368 tstSafeSize.setConfig(gPairs[i].fConfig, 100000000U, 369 100000000U); 370 Sk64 safeSize = tstSafeSize.getSafeSize64(); 371 if (safeSize.isNeg()) { 372 SkString str; 373 str.printf("getSafeSize64() negative: %s", 374 getSkConfigName(tstSafeSize)); 375 reporter->reportFailed(str); 376 } 377 bool sizeFail = false; 378 // Compare against hand-computed values. 379 switch (gPairs[i].fConfig) { 380 case SkBitmap::kNo_Config: 381 break; 382 383 case SkBitmap::kA1_Config: 384 if (safeSize.fHi != 0x470DE || 385 safeSize.fLo != 0x4DF82000) 386 sizeFail = true; 387 break; 388 389 case SkBitmap::kA8_Config: 390 case SkBitmap::kIndex8_Config: 391 if (safeSize.fHi != 0x2386F2 || 392 safeSize.fLo != 0x6FC10000) 393 sizeFail = true; 394 break; 395 396 case SkBitmap::kRGB_565_Config: 397 case SkBitmap::kARGB_4444_Config: 398 if (safeSize.fHi != 0x470DE4 || 399 safeSize.fLo != 0xDF820000) 400 sizeFail = true; 401 break; 402 403 case SkBitmap::kARGB_8888_Config: 404 if (safeSize.fHi != 0x8E1BC9 || 405 safeSize.fLo != 0xBF040000) 406 sizeFail = true; 407 break; 408 409 default: 410 break; 411 } 412 if (sizeFail) { 413 SkString str; 414 str.printf("getSafeSize64() wrong size: %s", 415 getSkConfigName(tstSafeSize)); 416 reporter->reportFailed(str); 417 } 418 419 size_t subW, subH; 420 // Set sizes to be height = 2 to force the last row of the 421 // source to be used, thus verifying correct operation if 422 // the bitmap is an extracted subset. 423 if (gPairs[i].fConfig == SkBitmap::kA1_Config) { 424 // If one-bit per pixel, use 9 pixels to force more than 425 // one byte per row. 426 subW = 9; 427 subH = 2; 428 } else { 429 // All other configurations are at least one byte per pixel, 430 // and different configs will test copying different numbers 431 // of bytes. 432 subW = subH = 2; 433 } 434 435 // Create bitmap to act as source for copies and subsets. 436 SkBitmap src, subset; 437 SkColorTable* ct = NULL; 438 if (isExtracted[copyCase]) { // A larger image to extract from. 439 src.setConfig(gPairs[i].fConfig, 2 * subW + 1, subH); 440 } else { // Tests expect a 2x2 bitmap, so make smaller. 441 src.setConfig(gPairs[i].fConfig, subW, subH); 442 } 443 if (SkBitmap::kIndex8_Config == src.config()) { 444 ct = init_ctable(); 445 } 446 447 src.allocPixels(ct); 448 SkSafeUnref(ct); 449 450 // Either copy src or extract into 'subset', which is used 451 // for subsequent calls to copyPixelsTo/From. 452 bool srcReady = false; 453 if (isExtracted[copyCase]) { 454 // The extractedSubset() test case allows us to test copy- 455 // ing when src and dst mave possibly different strides. 456 SkIRect r; 457 if (gPairs[i].fConfig == SkBitmap::kA1_Config) 458 // This config seems to need byte-alignment of 459 // extracted subset bits. 460 r.set(0, 0, subW, subH); 461 else 462 r.set(1, 0, 1 + subW, subH); // 2x2 extracted bitmap 463 464 srcReady = src.extractSubset(&subset, r); 465 } else { 466 srcReady = src.copyTo(&subset, src.getConfig()); 467 } 468 469 // Not all configurations will generate a valid 'subset'. 470 if (srcReady) { 471 472 // Allocate our target buffer 'buf' for all copies. 473 // To simplify verifying correctness of copies attach 474 // buf to a SkBitmap, but copies are done using the 475 // raw buffer pointer. 476 const uint32_t bufSize = subH * 477 SkBitmap::ComputeRowBytes(src.getConfig(), subW) * 2; 478 SkAutoMalloc autoBuf (bufSize); 479 uint8_t* buf = static_cast<uint8_t*>(autoBuf.get()); 480 481 SkBitmap bufBm; // Attach buf to this bitmap. 482 bool successExpected; 483 484 // Set up values for each pixel being copied. 485 Coordinates coords(subW * subH); 486 for (size_t x = 0; x < subW; ++x) 487 for (size_t y = 0; y < subH; ++y) 488 { 489 int index = y * subW + x; 490 SkASSERT(index < coords.length); 491 coords[index]->fX = x; 492 coords[index]->fY = y; 493 } 494 495 writeCoordPixels(subset, coords); 496 497 // Test #1 //////////////////////////////////////////// 498 499 // Before/after comparisons easier if we attach buf 500 // to an appropriately configured SkBitmap. 501 memset(buf, 0xFF, bufSize); 502 // Config with stride greater than src but that fits in buf. 503 bufBm.setConfig(gPairs[i].fConfig, subW, subH, 504 SkBitmap::ComputeRowBytes(subset.getConfig(), subW) 505 * 2); 506 bufBm.setPixels(buf); 507 successExpected = false; 508 // Then attempt to copy with a stride that is too large 509 // to fit in the buffer. 510 REPORTER_ASSERT(reporter, 511 subset.copyPixelsTo(buf, bufSize, bufBm.rowBytes() * 3) 512 == successExpected); 513 514 if (successExpected) 515 reportCopyVerification(subset, bufBm, coords, 516 "copyPixelsTo(buf, bufSize, 1.5*maxRowBytes)", 517 reporter); 518 519 // Test #2 //////////////////////////////////////////// 520 // This test should always succeed, but in the case 521 // of extracted bitmaps only because we handle the 522 // issue of getSafeSize(). Without getSafeSize() 523 // buffer overrun/read would occur. 524 memset(buf, 0xFF, bufSize); 525 bufBm.setConfig(gPairs[i].fConfig, subW, subH, 526 subset.rowBytes()); 527 bufBm.setPixels(buf); 528 successExpected = subset.getSafeSize() <= bufSize; 529 REPORTER_ASSERT(reporter, 530 subset.copyPixelsTo(buf, bufSize) == 531 successExpected); 532 if (successExpected) 533 reportCopyVerification(subset, bufBm, coords, 534 "copyPixelsTo(buf, bufSize)", reporter); 535 536 // Test #3 //////////////////////////////////////////// 537 // Copy with different stride between src and dst. 538 memset(buf, 0xFF, bufSize); 539 bufBm.setConfig(gPairs[i].fConfig, subW, subH, 540 subset.rowBytes()+1); 541 bufBm.setPixels(buf); 542 successExpected = true; // Should always work. 543 REPORTER_ASSERT(reporter, 544 subset.copyPixelsTo(buf, bufSize, 545 subset.rowBytes()+1) == successExpected); 546 if (successExpected) 547 reportCopyVerification(subset, bufBm, coords, 548 "copyPixelsTo(buf, bufSize, rowBytes+1)", reporter); 549 550 // Test #4 //////////////////////////////////////////// 551 // Test copy with stride too small. 552 memset(buf, 0xFF, bufSize); 553 bufBm.setConfig(gPairs[i].fConfig, subW, subH); 554 bufBm.setPixels(buf); 555 successExpected = false; 556 // Request copy with stride too small. 557 REPORTER_ASSERT(reporter, 558 subset.copyPixelsTo(buf, bufSize, bufBm.rowBytes()-1) 559 == successExpected); 560 if (successExpected) 561 reportCopyVerification(subset, bufBm, coords, 562 "copyPixelsTo(buf, bufSize, rowBytes()-1)", reporter); 563 564 #if 0 // copyPixelsFrom is gone 565 // Test #5 //////////////////////////////////////////// 566 // Tests the case where the source stride is too small 567 // for the source configuration. 568 memset(buf, 0xFF, bufSize); 569 bufBm.setConfig(gPairs[i].fConfig, subW, subH); 570 bufBm.setPixels(buf); 571 writeCoordPixels(bufBm, coords); 572 REPORTER_ASSERT(reporter, 573 subset.copyPixelsFrom(buf, bufSize, 1) == false); 574 575 // Test #6 /////////////////////////////////////////// 576 // Tests basic copy from an external buffer to the bitmap. 577 // If the bitmap is "extracted", this also tests the case 578 // where the source stride is different from the dest. 579 // stride. 580 // We've made the buffer large enough to always succeed. 581 bufBm.setConfig(gPairs[i].fConfig, subW, subH); 582 bufBm.setPixels(buf); 583 writeCoordPixels(bufBm, coords); 584 REPORTER_ASSERT(reporter, 585 subset.copyPixelsFrom(buf, bufSize, bufBm.rowBytes()) == 586 true); 587 reportCopyVerification(bufBm, subset, coords, 588 "copyPixelsFrom(buf, bufSize)", 589 reporter); 590 591 // Test #7 //////////////////////////////////////////// 592 // Tests the case where the source buffer is too small 593 // for the transfer. 594 REPORTER_ASSERT(reporter, 595 subset.copyPixelsFrom(buf, 1, subset.rowBytes()) == 596 false); 597 598 #endif 599 } 600 } // for (size_t copyCase ... 601 } 602 } 603 604 #include "TestClassDef.h" 605 DEFINE_TESTCLASS("BitmapCopy", TestBitmapCopyClass, TestBitmapCopy) 606