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