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