SkBitmap.cpp revision 3296bee70d074bb8094b3229dbe12fa016657e90
1/* 2 * Copyright 2008 The Android Open Source Project 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 "SkAtomics.h" 9#include "SkBitmap.h" 10#include "SkColorPriv.h" 11#include "SkData.h" 12#include "SkFilterQuality.h" 13#include "SkMallocPixelRef.h" 14#include "SkMask.h" 15#include "SkMath.h" 16#include "SkPixelRef.h" 17#include "SkReadBuffer.h" 18#include "SkRect.h" 19#include "SkScalar.h" 20#include "SkTemplates.h" 21#include "SkUnPreMultiply.h" 22#include "SkWriteBuffer.h" 23 24#include <string.h> 25 26static bool reset_return_false(SkBitmap* bm) { 27 bm->reset(); 28 return false; 29} 30 31SkBitmap::SkBitmap() { 32 sk_bzero(this, sizeof(*this)); 33} 34 35SkBitmap::SkBitmap(const SkBitmap& src) { 36 SkDEBUGCODE(src.validate();) 37 sk_bzero(this, sizeof(*this)); 38 *this = src; 39 SkDEBUGCODE(this->validate();) 40} 41 42SkBitmap::SkBitmap(SkBitmap&& other) : SkBitmap() { this->swap(other); } 43 44SkBitmap::~SkBitmap() { 45 SkDEBUGCODE(this->validate();) 46 this->freePixels(); 47} 48 49SkBitmap& SkBitmap::operator=(const SkBitmap& src) { 50 if (this != &src) { 51 this->freePixels(); 52 this->fPixelRef = SkSafeRef(src.fPixelRef); 53 if (this->fPixelRef) { 54 // ignore the values if we have a pixelRef 55 this->fPixels = nullptr; 56 this->fColorTable = nullptr; 57 } else { 58 this->fPixels = src.fPixels; 59 this->fColorTable = src.fColorTable; 60 } 61 // we reset our locks if we get blown away 62 this->fPixelLockCount = 0; 63 64 this->fPixelRefOrigin = src.fPixelRefOrigin; 65 this->fInfo = src.fInfo; 66 this->fRowBytes = src.fRowBytes; 67 this->fFlags = src.fFlags; 68 } 69 70 SkDEBUGCODE(this->validate();) 71 return *this; 72} 73 74SkBitmap& SkBitmap::operator=(SkBitmap&& other) { 75 if (this != &other) { 76 this->swap(other); 77 other.reset(); 78 } 79 return *this; 80} 81 82void SkBitmap::swap(SkBitmap& other) { 83 SkTSwap(fColorTable, other.fColorTable); 84 SkTSwap(fPixelRef, other.fPixelRef); 85 SkTSwap(fPixelRefOrigin, other.fPixelRefOrigin); 86 SkTSwap(fPixelLockCount, other.fPixelLockCount); 87 SkTSwap(fPixels, other.fPixels); 88 SkTSwap(fInfo, other.fInfo); 89 SkTSwap(fRowBytes, other.fRowBytes); 90 SkTSwap(fFlags, other.fFlags); 91 92 SkDEBUGCODE(this->validate();) 93} 94 95void SkBitmap::reset() { 96 this->freePixels(); 97 this->fInfo.reset(); 98 sk_bzero(this, sizeof(*this)); 99} 100 101void SkBitmap::getBounds(SkRect* bounds) const { 102 SkASSERT(bounds); 103 bounds->set(0, 0, 104 SkIntToScalar(fInfo.width()), SkIntToScalar(fInfo.height())); 105} 106 107void SkBitmap::getBounds(SkIRect* bounds) const { 108 SkASSERT(bounds); 109 bounds->set(0, 0, fInfo.width(), fInfo.height()); 110} 111 112/////////////////////////////////////////////////////////////////////////////// 113 114bool SkBitmap::setInfo(const SkImageInfo& info, size_t rowBytes) { 115 SkAlphaType newAT = info.alphaType(); 116 if (!SkColorTypeValidateAlphaType(info.colorType(), info.alphaType(), &newAT)) { 117 return reset_return_false(this); 118 } 119 // don't look at info.alphaType(), since newAT is the real value... 120 121 // require that rowBytes fit in 31bits 122 int64_t mrb = info.minRowBytes64(); 123 if ((int32_t)mrb != mrb) { 124 return reset_return_false(this); 125 } 126 if ((int64_t)rowBytes != (int32_t)rowBytes) { 127 return reset_return_false(this); 128 } 129 130 if (info.width() < 0 || info.height() < 0) { 131 return reset_return_false(this); 132 } 133 134 if (kUnknown_SkColorType == info.colorType()) { 135 rowBytes = 0; 136 } else if (0 == rowBytes) { 137 rowBytes = (size_t)mrb; 138 } else if (!info.validRowBytes(rowBytes)) { 139 return reset_return_false(this); 140 } 141 142 this->freePixels(); 143 144 fInfo = info.makeAlphaType(newAT); 145 fRowBytes = SkToU32(rowBytes); 146 return true; 147} 148 149bool SkBitmap::setAlphaType(SkAlphaType newAlphaType) { 150 if (!SkColorTypeValidateAlphaType(fInfo.colorType(), newAlphaType, &newAlphaType)) { 151 return false; 152 } 153 if (fInfo.alphaType() != newAlphaType) { 154 fInfo = fInfo.makeAlphaType(newAlphaType); 155 if (fPixelRef) { 156 fPixelRef->changeAlphaType(newAlphaType); 157 } 158 } 159 return true; 160} 161 162void SkBitmap::updatePixelsFromRef() const { 163 if (fPixelRef) { 164 if (fPixelLockCount > 0) { 165 SkASSERT(fPixelRef->isLocked()); 166 167 void* p = fPixelRef->pixels(); 168 if (p) { 169 p = (char*)p 170 + fPixelRefOrigin.fY * fRowBytes 171 + fPixelRefOrigin.fX * fInfo.bytesPerPixel(); 172 } 173 fPixels = p; 174 fColorTable = fPixelRef->colorTable(); 175 } else { 176 SkASSERT(0 == fPixelLockCount); 177 fPixels = nullptr; 178 fColorTable = nullptr; 179 } 180 } 181} 182 183SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, int dx, int dy) { 184#ifdef SK_DEBUG 185 if (pr) { 186 if (kUnknown_SkColorType != fInfo.colorType()) { 187 const SkImageInfo& prInfo = pr->info(); 188 SkASSERT(fInfo.width() <= prInfo.width()); 189 SkASSERT(fInfo.height() <= prInfo.height()); 190 SkASSERT(fInfo.colorType() == prInfo.colorType()); 191 switch (prInfo.alphaType()) { 192 case kUnknown_SkAlphaType: 193 SkASSERT(fInfo.alphaType() == kUnknown_SkAlphaType); 194 break; 195 case kOpaque_SkAlphaType: 196 case kPremul_SkAlphaType: 197 SkASSERT(fInfo.alphaType() == kOpaque_SkAlphaType || 198 fInfo.alphaType() == kPremul_SkAlphaType); 199 break; 200 case kUnpremul_SkAlphaType: 201 SkASSERT(fInfo.alphaType() == kOpaque_SkAlphaType || 202 fInfo.alphaType() == kUnpremul_SkAlphaType); 203 break; 204 } 205 } 206 } 207#endif 208 209 if (pr) { 210 const SkImageInfo& info = pr->info(); 211 fPixelRefOrigin.set(SkTPin(dx, 0, info.width()), SkTPin(dy, 0, info.height())); 212 } else { 213 // ignore dx,dy if there is no pixelref 214 fPixelRefOrigin.setZero(); 215 } 216 217 if (fPixelRef != pr) { 218 this->freePixels(); 219 SkASSERT(nullptr == fPixelRef); 220 221 SkSafeRef(pr); 222 fPixelRef = pr; 223 this->updatePixelsFromRef(); 224 } 225 226 SkDEBUGCODE(this->validate();) 227 return pr; 228} 229 230void SkBitmap::lockPixels() const { 231 if (fPixelRef && 0 == sk_atomic_inc(&fPixelLockCount)) { 232 fPixelRef->lockPixels(); 233 this->updatePixelsFromRef(); 234 } 235 SkDEBUGCODE(this->validate();) 236} 237 238void SkBitmap::unlockPixels() const { 239 SkASSERT(nullptr == fPixelRef || fPixelLockCount > 0); 240 241 if (fPixelRef && 1 == sk_atomic_dec(&fPixelLockCount)) { 242 fPixelRef->unlockPixels(); 243 this->updatePixelsFromRef(); 244 } 245 SkDEBUGCODE(this->validate();) 246} 247 248bool SkBitmap::lockPixelsAreWritable() const { 249 return (fPixelRef) ? fPixelRef->lockPixelsAreWritable() : false; 250} 251 252void SkBitmap::setPixels(void* p, SkColorTable* ctable) { 253 if (nullptr == p) { 254 this->setPixelRef(nullptr); 255 return; 256 } 257 258 if (kUnknown_SkColorType == fInfo.colorType()) { 259 this->setPixelRef(nullptr); 260 return; 261 } 262 263 SkPixelRef* pr = SkMallocPixelRef::NewDirect(fInfo, p, fRowBytes, ctable); 264 if (nullptr == pr) { 265 this->setPixelRef(nullptr); 266 return; 267 } 268 269 this->setPixelRef(pr)->unref(); 270 271 // since we're already allocated, we lockPixels right away 272 this->lockPixels(); 273 SkDEBUGCODE(this->validate();) 274} 275 276bool SkBitmap::tryAllocPixels(Allocator* allocator, SkColorTable* ctable) { 277 HeapAllocator stdalloc; 278 279 if (nullptr == allocator) { 280 allocator = &stdalloc; 281 } 282 return allocator->allocPixelRef(this, ctable); 283} 284 285/////////////////////////////////////////////////////////////////////////////// 286 287bool SkBitmap::tryAllocPixels(const SkImageInfo& requestedInfo, size_t rowBytes) { 288 if (kIndex_8_SkColorType == requestedInfo.colorType()) { 289 return reset_return_false(this); 290 } 291 if (!this->setInfo(requestedInfo, rowBytes)) { 292 return reset_return_false(this); 293 } 294 295 // setInfo may have corrected info (e.g. 565 is always opaque). 296 const SkImageInfo& correctedInfo = this->info(); 297 // setInfo may have computed a valid rowbytes if 0 were passed in 298 rowBytes = this->rowBytes(); 299 300 SkMallocPixelRef::PRFactory defaultFactory; 301 302 SkPixelRef* pr = defaultFactory.create(correctedInfo, rowBytes, nullptr); 303 if (nullptr == pr) { 304 return reset_return_false(this); 305 } 306 this->setPixelRef(pr)->unref(); 307 308 // TODO: lockPixels could/should return bool or void*/nullptr 309 this->lockPixels(); 310 if (nullptr == this->getPixels()) { 311 return reset_return_false(this); 312 } 313 return true; 314} 315 316bool SkBitmap::tryAllocPixels(const SkImageInfo& requestedInfo, SkPixelRefFactory* factory, 317 SkColorTable* ctable) { 318 if (kIndex_8_SkColorType == requestedInfo.colorType() && nullptr == ctable) { 319 return reset_return_false(this); 320 } 321 if (!this->setInfo(requestedInfo)) { 322 return reset_return_false(this); 323 } 324 325 // setInfo may have corrected info (e.g. 565 is always opaque). 326 const SkImageInfo& correctedInfo = this->info(); 327 328 SkMallocPixelRef::PRFactory defaultFactory; 329 if (nullptr == factory) { 330 factory = &defaultFactory; 331 } 332 333 SkPixelRef* pr = factory->create(correctedInfo, correctedInfo.minRowBytes(), ctable); 334 if (nullptr == pr) { 335 return reset_return_false(this); 336 } 337 this->setPixelRef(pr)->unref(); 338 339 // TODO: lockPixels could/should return bool or void*/nullptr 340 this->lockPixels(); 341 if (nullptr == this->getPixels()) { 342 return reset_return_false(this); 343 } 344 return true; 345} 346 347static void invoke_release_proc(void (*proc)(void* pixels, void* ctx), void* pixels, void* ctx) { 348 if (proc) { 349 proc(pixels, ctx); 350 } 351} 352 353bool SkBitmap::installPixels(const SkImageInfo& requestedInfo, void* pixels, size_t rb, 354 SkColorTable* ct, void (*releaseProc)(void* addr, void* context), 355 void* context) { 356 if (!this->setInfo(requestedInfo, rb)) { 357 invoke_release_proc(releaseProc, pixels, context); 358 this->reset(); 359 return false; 360 } 361 if (nullptr == pixels) { 362 invoke_release_proc(releaseProc, pixels, context); 363 return true; // we behaved as if they called setInfo() 364 } 365 366 // setInfo may have corrected info (e.g. 565 is always opaque). 367 const SkImageInfo& correctedInfo = this->info(); 368 369 SkPixelRef* pr = SkMallocPixelRef::NewWithProc(correctedInfo, rb, ct, pixels, releaseProc, 370 context); 371 if (!pr) { 372 this->reset(); 373 return false; 374 } 375 376 this->setPixelRef(pr)->unref(); 377 378 // since we're already allocated, we lockPixels right away 379 this->lockPixels(); 380 SkDEBUGCODE(this->validate();) 381 return true; 382} 383 384bool SkBitmap::installPixels(const SkPixmap& pixmap) { 385 return this->installPixels(pixmap.info(), pixmap.writable_addr(), 386 pixmap.rowBytes(), pixmap.ctable(), 387 nullptr, nullptr); 388} 389 390bool SkBitmap::installMaskPixels(const SkMask& mask) { 391 if (SkMask::kA8_Format != mask.fFormat) { 392 this->reset(); 393 return false; 394 } 395 return this->installPixels(SkImageInfo::MakeA8(mask.fBounds.width(), 396 mask.fBounds.height()), 397 mask.fImage, mask.fRowBytes); 398} 399 400/////////////////////////////////////////////////////////////////////////////// 401 402void SkBitmap::freePixels() { 403 if (fPixelRef) { 404 if (fPixelLockCount > 0) { 405 fPixelRef->unlockPixels(); 406 } 407 fPixelRef->unref(); 408 fPixelRef = nullptr; 409 fPixelRefOrigin.setZero(); 410 } 411 fPixelLockCount = 0; 412 fPixels = nullptr; 413 fColorTable = nullptr; 414} 415 416uint32_t SkBitmap::getGenerationID() const { 417 return (fPixelRef) ? fPixelRef->getGenerationID() : 0; 418} 419 420void SkBitmap::notifyPixelsChanged() const { 421 SkASSERT(!this->isImmutable()); 422 if (fPixelRef) { 423 fPixelRef->notifyPixelsChanged(); 424 } 425} 426 427GrTexture* SkBitmap::getTexture() const { 428 return fPixelRef ? fPixelRef->getTexture() : nullptr; 429} 430 431/////////////////////////////////////////////////////////////////////////////// 432 433/** We explicitly use the same allocator for our pixels that SkMask does, 434 so that we can freely assign memory allocated by one class to the other. 435 */ 436bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst, 437 SkColorTable* ctable) { 438 const SkImageInfo info = dst->info(); 439 if (kUnknown_SkColorType == info.colorType()) { 440// SkDebugf("unsupported config for info %d\n", dst->config()); 441 return false; 442 } 443 444 SkPixelRef* pr = SkMallocPixelRef::NewAllocate(info, dst->rowBytes(), ctable); 445 if (nullptr == pr) { 446 return false; 447 } 448 449 dst->setPixelRef(pr)->unref(); 450 // since we're already allocated, we lockPixels right away 451 dst->lockPixels(); 452 return true; 453} 454 455/////////////////////////////////////////////////////////////////////////////// 456 457static bool copy_pixels_to(const SkPixmap& src, void* const dst, size_t dstSize, 458 size_t dstRowBytes, bool preserveDstPad) { 459 const SkImageInfo& info = src.info(); 460 461 if (0 == dstRowBytes) { 462 dstRowBytes = src.rowBytes(); 463 } 464 if (dstRowBytes < info.minRowBytes()) { 465 return false; 466 } 467 468 if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == src.rowBytes()) { 469 size_t safeSize = src.getSafeSize(); 470 if (safeSize > dstSize || safeSize == 0) 471 return false; 472 else { 473 // This implementation will write bytes beyond the end of each row, 474 // excluding the last row, if the bitmap's stride is greater than 475 // strictly required by the current config. 476 memcpy(dst, src.addr(), safeSize); 477 return true; 478 } 479 } else { 480 // If destination has different stride than us, then copy line by line. 481 if (info.getSafeSize(dstRowBytes) > dstSize) { 482 return false; 483 } else { 484 // Just copy what we need on each line. 485 size_t rowBytes = info.minRowBytes(); 486 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(src.addr()); 487 uint8_t* dstP = reinterpret_cast<uint8_t*>(dst); 488 for (int row = 0; row < info.height(); ++row) { 489 memcpy(dstP, srcP, rowBytes); 490 srcP += src.rowBytes(); 491 dstP += dstRowBytes; 492 } 493 494 return true; 495 } 496 } 497} 498 499bool SkBitmap::copyPixelsTo(void* dst, size_t dstSize, size_t dstRB, bool preserveDstPad) const { 500 if (nullptr == dst) { 501 return false; 502 } 503 SkAutoPixmapUnlock result; 504 if (!this->requestLock(&result)) { 505 return false; 506 } 507 return copy_pixels_to(result.pixmap(), dst, dstSize, dstRB, preserveDstPad); 508} 509 510/////////////////////////////////////////////////////////////////////////////// 511 512bool SkBitmap::isImmutable() const { 513 return fPixelRef ? fPixelRef->isImmutable() : false; 514} 515 516void SkBitmap::setImmutable() { 517 if (fPixelRef) { 518 fPixelRef->setImmutable(); 519 } 520} 521 522bool SkBitmap::isVolatile() const { 523 return (fFlags & kImageIsVolatile_Flag) != 0; 524} 525 526void SkBitmap::setIsVolatile(bool isVolatile) { 527 if (isVolatile) { 528 fFlags |= kImageIsVolatile_Flag; 529 } else { 530 fFlags &= ~kImageIsVolatile_Flag; 531 } 532} 533 534void* SkBitmap::getAddr(int x, int y) const { 535 SkASSERT((unsigned)x < (unsigned)this->width()); 536 SkASSERT((unsigned)y < (unsigned)this->height()); 537 538 char* base = (char*)this->getPixels(); 539 if (base) { 540 base += y * this->rowBytes(); 541 switch (this->colorType()) { 542 case kRGBA_8888_SkColorType: 543 case kBGRA_8888_SkColorType: 544 base += x << 2; 545 break; 546 case kARGB_4444_SkColorType: 547 case kRGB_565_SkColorType: 548 base += x << 1; 549 break; 550 case kAlpha_8_SkColorType: 551 case kIndex_8_SkColorType: 552 case kGray_8_SkColorType: 553 base += x; 554 break; 555 default: 556 SkDEBUGFAIL("Can't return addr for config"); 557 base = nullptr; 558 break; 559 } 560 } 561 return base; 562} 563 564#include "SkHalf.h" 565 566SkColor SkBitmap::getColor(int x, int y) const { 567 SkASSERT((unsigned)x < (unsigned)this->width()); 568 SkASSERT((unsigned)y < (unsigned)this->height()); 569 570 switch (this->colorType()) { 571 case kGray_8_SkColorType: { 572 uint8_t* addr = this->getAddr8(x, y); 573 return SkColorSetRGB(*addr, *addr, *addr); 574 } 575 case kAlpha_8_SkColorType: { 576 uint8_t* addr = this->getAddr8(x, y); 577 return SkColorSetA(0, addr[0]); 578 } 579 case kIndex_8_SkColorType: { 580 SkPMColor c = this->getIndex8Color(x, y); 581 return SkUnPreMultiply::PMColorToColor(c); 582 } 583 case kRGB_565_SkColorType: { 584 uint16_t* addr = this->getAddr16(x, y); 585 return SkPixel16ToColor(addr[0]); 586 } 587 case kARGB_4444_SkColorType: { 588 uint16_t* addr = this->getAddr16(x, y); 589 SkPMColor c = SkPixel4444ToPixel32(addr[0]); 590 return SkUnPreMultiply::PMColorToColor(c); 591 } 592 case kBGRA_8888_SkColorType: { 593 uint32_t* addr = this->getAddr32(x, y); 594 SkPMColor c = SkSwizzle_BGRA_to_PMColor(addr[0]); 595 return SkUnPreMultiply::PMColorToColor(c); 596 } 597 case kRGBA_8888_SkColorType: { 598 uint32_t* addr = this->getAddr32(x, y); 599 SkPMColor c = SkSwizzle_RGBA_to_PMColor(addr[0]); 600 return SkUnPreMultiply::PMColorToColor(c); 601 } 602 case kRGBA_F16_SkColorType: { 603 const uint64_t* addr = (const uint64_t*)fPixels + y * (fRowBytes >> 3) + x; 604 Sk4f p4 = SkHalfToFloat_finite(addr[0]); 605 if (p4[3]) { 606 float inva = 1 / p4[3]; 607 p4 = p4 * Sk4f(inva, inva, inva, 1); 608 } 609 SkColor c; 610 SkNx_cast<uint8_t>(p4 * Sk4f(255) + Sk4f(0.5f)).store(&c); 611 // p4 is RGBA, but we want BGRA, so we need to swap next 612 return SkSwizzle_RB(c); 613 } 614 default: 615 SkASSERT(false); 616 return 0; 617 } 618 SkASSERT(false); // Not reached. 619 return 0; 620} 621 622static bool compute_is_opaque(const SkPixmap& pmap) { 623 const int height = pmap.height(); 624 const int width = pmap.width(); 625 626 switch (pmap.colorType()) { 627 case kAlpha_8_SkColorType: { 628 unsigned a = 0xFF; 629 for (int y = 0; y < height; ++y) { 630 const uint8_t* row = pmap.addr8(0, y); 631 for (int x = 0; x < width; ++x) { 632 a &= row[x]; 633 } 634 if (0xFF != a) { 635 return false; 636 } 637 } 638 return true; 639 } break; 640 case kIndex_8_SkColorType: { 641 const SkColorTable* ctable = pmap.ctable(); 642 if (nullptr == ctable) { 643 return false; 644 } 645 const SkPMColor* table = ctable->readColors(); 646 SkPMColor c = (SkPMColor)~0; 647 for (int i = ctable->count() - 1; i >= 0; --i) { 648 c &= table[i]; 649 } 650 return 0xFF == SkGetPackedA32(c); 651 } break; 652 case kRGB_565_SkColorType: 653 case kGray_8_SkColorType: 654 return true; 655 break; 656 case kARGB_4444_SkColorType: { 657 unsigned c = 0xFFFF; 658 for (int y = 0; y < height; ++y) { 659 const SkPMColor16* row = pmap.addr16(0, y); 660 for (int x = 0; x < width; ++x) { 661 c &= row[x]; 662 } 663 if (0xF != SkGetPackedA4444(c)) { 664 return false; 665 } 666 } 667 return true; 668 } break; 669 case kBGRA_8888_SkColorType: 670 case kRGBA_8888_SkColorType: { 671 SkPMColor c = (SkPMColor)~0; 672 for (int y = 0; y < height; ++y) { 673 const SkPMColor* row = pmap.addr32(0, y); 674 for (int x = 0; x < width; ++x) { 675 c &= row[x]; 676 } 677 if (0xFF != SkGetPackedA32(c)) { 678 return false; 679 } 680 } 681 return true; 682 } 683 default: 684 break; 685 } 686 return false; 687} 688 689bool SkBitmap::ComputeIsOpaque(const SkBitmap& bm) { 690 SkAutoPixmapUnlock result; 691 if (!bm.requestLock(&result)) { 692 return false; 693 } 694 return compute_is_opaque(result.pixmap()); 695} 696 697 698/////////////////////////////////////////////////////////////////////////////// 699/////////////////////////////////////////////////////////////////////////////// 700 701void SkBitmap::erase(SkColor c, const SkIRect& area) const { 702 SkDEBUGCODE(this->validate();) 703 704 switch (fInfo.colorType()) { 705 case kUnknown_SkColorType: 706 case kIndex_8_SkColorType: 707 // TODO: can we ASSERT that we never get here? 708 return; // can't erase. Should we bzero so the memory is not uninitialized? 709 default: 710 break; 711 } 712 713 SkAutoPixmapUnlock result; 714 if (!this->requestLock(&result)) { 715 return; 716 } 717 718 if (result.pixmap().erase(c, area)) { 719 this->notifyPixelsChanged(); 720 } 721} 722 723void SkBitmap::eraseColor(SkColor c) const { 724 this->erase(c, SkIRect::MakeWH(this->width(), this->height())); 725} 726 727////////////////////////////////////////////////////////////////////////////////////// 728////////////////////////////////////////////////////////////////////////////////////// 729 730bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const { 731 SkDEBUGCODE(this->validate();) 732 733 if (nullptr == result || nullptr == fPixelRef) { 734 return false; // no src pixels 735 } 736 737 SkIRect srcRect, r; 738 srcRect.set(0, 0, this->width(), this->height()); 739 if (!r.intersect(srcRect, subset)) { 740 return false; // r is empty (i.e. no intersection) 741 } 742 743 if (fPixelRef->getTexture() != nullptr) { 744 // Do a deep copy 745 SkPixelRef* pixelRef = fPixelRef->deepCopy(this->colorType(), this->colorSpace(), &subset); 746 if (pixelRef != nullptr) { 747 SkBitmap dst; 748 dst.setInfo(this->info().makeWH(subset.width(), subset.height())); 749 dst.setIsVolatile(this->isVolatile()); 750 dst.setPixelRef(pixelRef)->unref(); 751 SkDEBUGCODE(dst.validate()); 752 result->swap(dst); 753 return true; 754 } 755 } 756 757 // If the upper left of the rectangle was outside the bounds of this SkBitmap, we should have 758 // exited above. 759 SkASSERT(static_cast<unsigned>(r.fLeft) < static_cast<unsigned>(this->width())); 760 SkASSERT(static_cast<unsigned>(r.fTop) < static_cast<unsigned>(this->height())); 761 762 SkBitmap dst; 763 dst.setInfo(this->info().makeWH(r.width(), r.height()), this->rowBytes()); 764 dst.setIsVolatile(this->isVolatile()); 765 766 if (fPixelRef) { 767 SkIPoint origin = fPixelRefOrigin; 768 origin.fX += r.fLeft; 769 origin.fY += r.fTop; 770 // share the pixelref with a custom offset 771 dst.setPixelRef(fPixelRef, origin); 772 } 773 SkDEBUGCODE(dst.validate();) 774 775 // we know we're good, so commit to result 776 result->swap(dst); 777 return true; 778} 779 780/////////////////////////////////////////////////////////////////////////////// 781 782bool SkBitmap::canCopyTo(SkColorType dstColorType) const { 783 const SkColorType srcCT = this->colorType(); 784 785 if (srcCT == kUnknown_SkColorType) { 786 return false; 787 } 788 789 bool sameConfigs = (srcCT == dstColorType); 790 switch (dstColorType) { 791 case kAlpha_8_SkColorType: 792 case kRGB_565_SkColorType: 793 case kRGBA_8888_SkColorType: 794 case kBGRA_8888_SkColorType: 795 break; 796 case kIndex_8_SkColorType: 797 if (!sameConfigs) { 798 return false; 799 } 800 break; 801 case kARGB_4444_SkColorType: 802 return sameConfigs || kN32_SkColorType == srcCT || kIndex_8_SkColorType == srcCT; 803 case kGray_8_SkColorType: 804 switch (srcCT) { 805 case kGray_8_SkColorType: 806 case kRGBA_8888_SkColorType: 807 case kBGRA_8888_SkColorType: 808 return true; 809 default: 810 break; 811 } 812 return false; 813 default: 814 return false; 815 } 816 return true; 817} 818 819bool SkBitmap::readPixels(const SkImageInfo& requestedDstInfo, void* dstPixels, size_t dstRB, 820 int x, int y) const { 821 SkAutoPixmapUnlock src; 822 if (!this->requestLock(&src)) { 823 return false; 824 } 825 return src.pixmap().readPixels(requestedDstInfo, dstPixels, dstRB, x, y); 826} 827 828bool SkBitmap::copyTo(SkBitmap* dst, SkColorType dstColorType, Allocator* alloc) const { 829 if (!this->canCopyTo(dstColorType)) { 830 return false; 831 } 832 833 // if we have a texture, first get those pixels 834 SkBitmap tmpSrc; 835 const SkBitmap* src = this; 836 837 if (fPixelRef) { 838 SkIRect subset; 839 subset.setXYWH(fPixelRefOrigin.fX, fPixelRefOrigin.fY, 840 fInfo.width(), fInfo.height()); 841 if (fPixelRef->readPixels(&tmpSrc, dstColorType, &subset)) { 842 if (fPixelRef->info().alphaType() == kUnpremul_SkAlphaType) { 843 // FIXME: The only meaningful implementation of readPixels 844 // (GrPixelRef) assumes premultiplied pixels. 845 return false; 846 } 847 SkASSERT(tmpSrc.width() == this->width()); 848 SkASSERT(tmpSrc.height() == this->height()); 849 850 // did we get lucky and we can just return tmpSrc? 851 if (tmpSrc.colorType() == dstColorType && nullptr == alloc) { 852 dst->swap(tmpSrc); 853 // If the result is an exact copy, clone the gen ID. 854 if (dst->pixelRef() && dst->pixelRef()->info() == fPixelRef->info()) { 855 dst->pixelRef()->cloneGenID(*fPixelRef); 856 } 857 return true; 858 } 859 860 // fall through to the raster case 861 src = &tmpSrc; 862 } 863 } 864 865 SkAutoPixmapUnlock srcUnlocker; 866 if (!src->requestLock(&srcUnlocker)) { 867 return false; 868 } 869 const SkPixmap& srcPM = srcUnlocker.pixmap(); 870 871 const SkImageInfo dstInfo = srcPM.info().makeColorType(dstColorType); 872 SkBitmap tmpDst; 873 if (!tmpDst.setInfo(dstInfo)) { 874 return false; 875 } 876 877 // allocate colortable if srcConfig == kIndex8_Config 878 SkAutoTUnref<SkColorTable> ctable; 879 if (dstColorType == kIndex_8_SkColorType) { 880 ctable.reset(SkRef(srcPM.ctable())); 881 } 882 if (!tmpDst.tryAllocPixels(alloc, ctable)) { 883 return false; 884 } 885 886 SkAutoPixmapUnlock dstUnlocker; 887 if (!tmpDst.requestLock(&dstUnlocker)) { 888 return false; 889 } 890 891 if (!srcPM.readPixels(dstUnlocker.pixmap())) { 892 return false; 893 } 894 895 // (for BitmapHeap) Clone the pixelref genID even though we have a new pixelref. 896 // The old copyTo impl did this, so we continue it for now. 897 // 898 // TODO: should we ignore rowbytes (i.e. getSize)? Then it could just be 899 // if (src_pixelref->info == dst_pixelref->info) 900 // 901 if (srcPM.colorType() == dstColorType && tmpDst.getSize() == srcPM.getSize64()) { 902 SkPixelRef* dstPixelRef = tmpDst.pixelRef(); 903 if (dstPixelRef->info() == fPixelRef->info()) { 904 dstPixelRef->cloneGenID(*fPixelRef); 905 } 906 } 907 908 dst->swap(tmpDst); 909 return true; 910} 911 912bool SkBitmap::deepCopyTo(SkBitmap* dst) const { 913 const SkColorType dstCT = this->colorType(); 914 SkColorSpace* dstCS = this->colorSpace(); 915 916 if (!this->canCopyTo(dstCT)) { 917 return false; 918 } 919 920 // If we have a PixelRef, and it supports deep copy, use it. 921 // Currently supported only by texture-backed bitmaps. 922 if (fPixelRef) { 923 SkPixelRef* pixelRef = fPixelRef->deepCopy(dstCT, dstCS, nullptr); 924 if (pixelRef) { 925 uint32_t rowBytes; 926 if (this->colorType() == dstCT && this->colorSpace() == dstCS) { 927 // Since there is no subset to pass to deepCopy, and deepCopy 928 // succeeded, the new pixel ref must be identical. 929 SkASSERT(fPixelRef->info() == pixelRef->info()); 930 pixelRef->cloneGenID(*fPixelRef); 931 // Use the same rowBytes as the original. 932 rowBytes = fRowBytes; 933 } else { 934 // With the new config, an appropriate fRowBytes will be computed by setInfo. 935 rowBytes = 0; 936 } 937 938 const SkImageInfo info = fInfo.makeColorType(dstCT); 939 if (!dst->setInfo(info, rowBytes)) { 940 return false; 941 } 942 dst->setPixelRef(pixelRef, fPixelRefOrigin)->unref(); 943 return true; 944 } 945 } 946 947 if (this->getTexture()) { 948 return false; 949 } else { 950 return this->copyTo(dst, dstCT, nullptr); 951 } 952} 953 954/////////////////////////////////////////////////////////////////////////////// 955 956static void rect_memset(uint8_t* array, U8CPU value, SkISize size, size_t rowBytes) { 957 for (int y = 0; y < size.height(); ++y) { 958 memset(array, value, size.width()); 959 array += rowBytes; 960 } 961} 962 963static void get_bitmap_alpha(const SkPixmap& pmap, uint8_t* SK_RESTRICT alpha, int alphaRowBytes) { 964 SkColorType colorType = pmap.colorType(); 965 int w = pmap.width(); 966 int h = pmap.height(); 967 size_t rb = pmap.rowBytes(); 968 969 if (kAlpha_8_SkColorType == colorType && !pmap.isOpaque()) { 970 const uint8_t* s = pmap.addr8(0, 0); 971 while (--h >= 0) { 972 memcpy(alpha, s, w); 973 s += rb; 974 alpha += alphaRowBytes; 975 } 976 } else if (kN32_SkColorType == colorType && !pmap.isOpaque()) { 977 const SkPMColor* SK_RESTRICT s = pmap.addr32(0, 0); 978 while (--h >= 0) { 979 for (int x = 0; x < w; x++) { 980 alpha[x] = SkGetPackedA32(s[x]); 981 } 982 s = (const SkPMColor*)((const char*)s + rb); 983 alpha += alphaRowBytes; 984 } 985 } else if (kARGB_4444_SkColorType == colorType && !pmap.isOpaque()) { 986 const SkPMColor16* SK_RESTRICT s = pmap.addr16(0, 0); 987 while (--h >= 0) { 988 for (int x = 0; x < w; x++) { 989 alpha[x] = SkPacked4444ToA32(s[x]); 990 } 991 s = (const SkPMColor16*)((const char*)s + rb); 992 alpha += alphaRowBytes; 993 } 994 } else if (kIndex_8_SkColorType == colorType && !pmap.isOpaque()) { 995 const SkColorTable* ct = pmap.ctable(); 996 if (ct) { 997 const SkPMColor* SK_RESTRICT table = ct->readColors(); 998 const uint8_t* SK_RESTRICT s = pmap.addr8(0, 0); 999 while (--h >= 0) { 1000 for (int x = 0; x < w; x++) { 1001 alpha[x] = SkGetPackedA32(table[s[x]]); 1002 } 1003 s += rb; 1004 alpha += alphaRowBytes; 1005 } 1006 } 1007 } else { // src is opaque, so just fill alpha[] with 0xFF 1008 rect_memset(alpha, 0xFF, pmap.info().dimensions(), alphaRowBytes); 1009 } 1010} 1011 1012static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha, int alphaRowBytes) { 1013 SkASSERT(alpha != nullptr); 1014 SkASSERT(alphaRowBytes >= src.width()); 1015 1016 SkAutoPixmapUnlock apl; 1017 if (!src.requestLock(&apl)) { 1018 rect_memset(alpha, 0, src.info().dimensions(), alphaRowBytes); 1019 return false; 1020 } 1021 get_bitmap_alpha(apl.pixmap(), alpha, alphaRowBytes); 1022 return true; 1023} 1024 1025#include "SkPaint.h" 1026#include "SkMaskFilter.h" 1027#include "SkMatrix.h" 1028 1029bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint, 1030 Allocator *allocator, SkIPoint* offset) const { 1031 SkDEBUGCODE(this->validate();) 1032 1033 SkBitmap tmpBitmap; 1034 SkMatrix identity; 1035 SkMask srcM, dstM; 1036 1037 srcM.fBounds.set(0, 0, this->width(), this->height()); 1038 srcM.fRowBytes = SkAlign4(this->width()); 1039 srcM.fFormat = SkMask::kA8_Format; 1040 1041 SkMaskFilter* filter = paint ? paint->getMaskFilter() : nullptr; 1042 1043 // compute our (larger?) dst bounds if we have a filter 1044 if (filter) { 1045 identity.reset(); 1046 if (!filter->filterMask(&dstM, srcM, identity, nullptr)) { 1047 goto NO_FILTER_CASE; 1048 } 1049 dstM.fRowBytes = SkAlign4(dstM.fBounds.width()); 1050 } else { 1051 NO_FILTER_CASE: 1052 tmpBitmap.setInfo(SkImageInfo::MakeA8(this->width(), this->height()), srcM.fRowBytes); 1053 if (!tmpBitmap.tryAllocPixels(allocator, nullptr)) { 1054 // Allocation of pixels for alpha bitmap failed. 1055 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n", 1056 tmpBitmap.width(), tmpBitmap.height()); 1057 return false; 1058 } 1059 GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes); 1060 if (offset) { 1061 offset->set(0, 0); 1062 } 1063 tmpBitmap.swap(*dst); 1064 return true; 1065 } 1066 srcM.fImage = SkMask::AllocImage(srcM.computeImageSize()); 1067 SkAutoMaskFreeImage srcCleanup(srcM.fImage); 1068 1069 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes); 1070 if (!filter->filterMask(&dstM, srcM, identity, nullptr)) { 1071 goto NO_FILTER_CASE; 1072 } 1073 SkAutoMaskFreeImage dstCleanup(dstM.fImage); 1074 1075 tmpBitmap.setInfo(SkImageInfo::MakeA8(dstM.fBounds.width(), dstM.fBounds.height()), 1076 dstM.fRowBytes); 1077 if (!tmpBitmap.tryAllocPixels(allocator, nullptr)) { 1078 // Allocation of pixels for alpha bitmap failed. 1079 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n", 1080 tmpBitmap.width(), tmpBitmap.height()); 1081 return false; 1082 } 1083 memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize()); 1084 if (offset) { 1085 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop); 1086 } 1087 SkDEBUGCODE(tmpBitmap.validate();) 1088 1089 tmpBitmap.swap(*dst); 1090 return true; 1091} 1092 1093/////////////////////////////////////////////////////////////////////////////// 1094 1095static void write_raw_pixels(SkWriteBuffer* buffer, const SkPixmap& pmap) { 1096 const SkImageInfo& info = pmap.info(); 1097 const size_t snugRB = info.width() * info.bytesPerPixel(); 1098 const char* src = (const char*)pmap.addr(); 1099 const size_t ramRB = pmap.rowBytes(); 1100 1101 buffer->write32(SkToU32(snugRB)); 1102 info.flatten(*buffer); 1103 1104 const size_t size = snugRB * info.height(); 1105 SkAutoTMalloc<char> storage(size); 1106 char* dst = storage.get(); 1107 for (int y = 0; y < info.height(); ++y) { 1108 memcpy(dst, src, snugRB); 1109 dst += snugRB; 1110 src += ramRB; 1111 } 1112 buffer->writeByteArray(storage.get(), size); 1113 1114 const SkColorTable* ct = pmap.ctable(); 1115 if (kIndex_8_SkColorType == info.colorType() && ct) { 1116 buffer->writeBool(true); 1117 ct->writeToBuffer(*buffer); 1118 } else { 1119 buffer->writeBool(false); 1120 } 1121} 1122 1123void SkBitmap::WriteRawPixels(SkWriteBuffer* buffer, const SkBitmap& bitmap) { 1124 const SkImageInfo info = bitmap.info(); 1125 if (0 == info.width() || 0 == info.height() || nullptr == bitmap.pixelRef()) { 1126 buffer->writeUInt(0); // instead of snugRB, signaling no pixels 1127 return; 1128 } 1129 1130 SkAutoPixmapUnlock result; 1131 if (!bitmap.requestLock(&result)) { 1132 buffer->writeUInt(0); // instead of snugRB, signaling no pixels 1133 return; 1134 } 1135 1136 write_raw_pixels(buffer, result.pixmap()); 1137} 1138 1139bool SkBitmap::ReadRawPixels(SkReadBuffer* buffer, SkBitmap* bitmap) { 1140 const size_t snugRB = buffer->readUInt(); 1141 if (0 == snugRB) { // no pixels 1142 return false; 1143 } 1144 1145 SkImageInfo info; 1146 info.unflatten(*buffer); 1147 1148 // If there was an error reading "info" or if it is bogus, 1149 // don't use it to compute minRowBytes() 1150 if (!buffer->validate(SkColorTypeValidateAlphaType(info.colorType(), 1151 info.alphaType()))) { 1152 return false; 1153 } 1154 1155 const size_t ramRB = info.minRowBytes(); 1156 const int height = SkMax32(info.height(), 0); 1157 const uint64_t snugSize = sk_64_mul(snugRB, height); 1158 const uint64_t ramSize = sk_64_mul(ramRB, height); 1159 static const uint64_t max_size_t = (size_t)(-1); 1160 if (!buffer->validate((snugSize <= ramSize) && (ramSize <= max_size_t))) { 1161 return false; 1162 } 1163 1164 sk_sp<SkData> data(SkData::MakeUninitialized(SkToSizeT(ramSize))); 1165 unsigned char* dst = (unsigned char*)data->writable_data(); 1166 buffer->readByteArray(dst, SkToSizeT(snugSize)); 1167 1168 if (snugSize != ramSize) { 1169 const unsigned char* srcRow = dst + snugRB * (height - 1); 1170 unsigned char* dstRow = dst + ramRB * (height - 1); 1171 for (int y = height - 1; y >= 1; --y) { 1172 memmove(dstRow, srcRow, snugRB); 1173 srcRow -= snugRB; 1174 dstRow -= ramRB; 1175 } 1176 SkASSERT(srcRow == dstRow); // first row does not need to be moved 1177 } 1178 1179 SkAutoTUnref<SkColorTable> ctable; 1180 if (buffer->readBool()) { 1181 ctable.reset(SkColorTable::Create(*buffer)); 1182 if (!ctable) { 1183 return false; 1184 } 1185 1186 if (info.isEmpty()) { 1187 // require an empty ctable 1188 if (ctable->count() != 0) { 1189 buffer->validate(false); 1190 return false; 1191 } 1192 } else { 1193 // require a non-empty ctable 1194 if (ctable->count() == 0) { 1195 buffer->validate(false); 1196 return false; 1197 } 1198 unsigned char maxIndex = ctable->count() - 1; 1199 for (uint64_t i = 0; i < ramSize; ++i) { 1200 dst[i] = SkTMin(dst[i], maxIndex); 1201 } 1202 } 1203 } 1204 1205 SkAutoTUnref<SkPixelRef> pr(SkMallocPixelRef::NewWithData(info, info.minRowBytes(), 1206 ctable.get(), data.get())); 1207 if (!pr.get()) { 1208 return false; 1209 } 1210 bitmap->setInfo(pr->info()); 1211 bitmap->setPixelRef(pr, 0, 0); 1212 return true; 1213} 1214 1215enum { 1216 SERIALIZE_PIXELTYPE_NONE, 1217 SERIALIZE_PIXELTYPE_REF_DATA 1218}; 1219 1220/////////////////////////////////////////////////////////////////////////////// 1221 1222SkBitmap::RLEPixels::RLEPixels(int width, int height) { 1223 fHeight = height; 1224 fYPtrs = (uint8_t**)sk_calloc_throw(height * sizeof(uint8_t*)); 1225} 1226 1227SkBitmap::RLEPixels::~RLEPixels() { 1228 sk_free(fYPtrs); 1229} 1230 1231/////////////////////////////////////////////////////////////////////////////// 1232 1233#ifdef SK_DEBUG 1234void SkBitmap::validate() const { 1235 fInfo.validate(); 1236 1237 // ImageInfo may not require this, but Bitmap ensures that opaque-only 1238 // colorTypes report opaque for their alphatype 1239 if (kRGB_565_SkColorType == fInfo.colorType()) { 1240 SkASSERT(kOpaque_SkAlphaType == fInfo.alphaType()); 1241 } 1242 1243 SkASSERT(fInfo.validRowBytes(fRowBytes)); 1244 uint8_t allFlags = kImageIsVolatile_Flag; 1245#ifdef SK_BUILD_FOR_ANDROID 1246 allFlags |= kHasHardwareMipMap_Flag; 1247#endif 1248 SkASSERT((~allFlags & fFlags) == 0); 1249 SkASSERT(fPixelLockCount >= 0); 1250 1251 if (fPixels) { 1252 SkASSERT(fPixelRef); 1253 SkASSERT(fPixelLockCount > 0); 1254 SkASSERT(fPixelRef->isLocked()); 1255 SkASSERT(fPixelRef->rowBytes() == fRowBytes); 1256 SkASSERT(fPixelRefOrigin.fX >= 0); 1257 SkASSERT(fPixelRefOrigin.fY >= 0); 1258 SkASSERT(fPixelRef->info().width() >= (int)this->width() + fPixelRefOrigin.fX); 1259 SkASSERT(fPixelRef->info().height() >= (int)this->height() + fPixelRefOrigin.fY); 1260 SkASSERT(fPixelRef->rowBytes() >= fInfo.minRowBytes()); 1261 } else { 1262 SkASSERT(nullptr == fColorTable); 1263 } 1264} 1265#endif 1266 1267#ifndef SK_IGNORE_TO_STRING 1268#include "SkString.h" 1269void SkBitmap::toString(SkString* str) const { 1270 1271 static const char* gColorTypeNames[kLastEnum_SkColorType + 1] = { 1272 "UNKNOWN", "A8", "565", "4444", "RGBA", "BGRA", "INDEX8", 1273 }; 1274 1275 str->appendf("bitmap: ((%d, %d) %s", this->width(), this->height(), 1276 gColorTypeNames[this->colorType()]); 1277 1278 str->append(" ("); 1279 if (this->isOpaque()) { 1280 str->append("opaque"); 1281 } else { 1282 str->append("transparent"); 1283 } 1284 if (this->isImmutable()) { 1285 str->append(", immutable"); 1286 } else { 1287 str->append(", not-immutable"); 1288 } 1289 str->append(")"); 1290 1291 SkPixelRef* pr = this->pixelRef(); 1292 if (nullptr == pr) { 1293 // show null or the explicit pixel address (rare) 1294 str->appendf(" pixels:%p", this->getPixels()); 1295 } else { 1296 const char* uri = pr->getURI(); 1297 if (uri) { 1298 str->appendf(" uri:\"%s\"", uri); 1299 } else { 1300 str->appendf(" pixelref:%p", pr); 1301 } 1302 } 1303 1304 str->append(")"); 1305} 1306#endif 1307 1308/////////////////////////////////////////////////////////////////////////////// 1309 1310bool SkBitmap::requestLock(SkAutoPixmapUnlock* result) const { 1311 SkASSERT(result); 1312 1313 SkPixelRef* pr = fPixelRef; 1314 if (nullptr == pr) { 1315 return false; 1316 } 1317 1318 // We have to lock the whole thing (using the pixelref's dimensions) until the api supports 1319 // a partial lock (with offset/origin). Hence we can't use our fInfo. 1320 SkPixelRef::LockRequest req = { pr->info().dimensions(), kNone_SkFilterQuality }; 1321 SkPixelRef::LockResult res; 1322 if (pr->requestLock(req, &res)) { 1323 SkASSERT(res.fPixels); 1324 // The bitmap may be a subset of the pixelref's dimensions 1325 SkASSERT(fPixelRefOrigin.x() + fInfo.width() <= res.fSize.width()); 1326 SkASSERT(fPixelRefOrigin.y() + fInfo.height() <= res.fSize.height()); 1327 const void* addr = (const char*)res.fPixels + SkColorTypeComputeOffset(fInfo.colorType(), 1328 fPixelRefOrigin.x(), 1329 fPixelRefOrigin.y(), 1330 res.fRowBytes); 1331 1332 result->reset(SkPixmap(this->info(), addr, res.fRowBytes, res.fCTable), 1333 res.fUnlockProc, res.fUnlockContext); 1334 return true; 1335 } 1336 return false; 1337} 1338 1339bool SkBitmap::peekPixels(SkPixmap* pmap) const { 1340 if (fPixels) { 1341 if (pmap) { 1342 pmap->reset(fInfo, fPixels, fRowBytes, fColorTable); 1343 } 1344 return true; 1345 } 1346 return false; 1347} 1348 1349/////////////////////////////////////////////////////////////////////////////// 1350 1351#ifdef SK_DEBUG 1352void SkImageInfo::validate() const { 1353 SkASSERT(fWidth >= 0); 1354 SkASSERT(fHeight >= 0); 1355 SkASSERT(SkColorTypeIsValid(fColorType)); 1356 SkASSERT(SkAlphaTypeIsValid(fAlphaType)); 1357} 1358#endif 1359