| /* |
| * Copyright 2013 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "SkCachingPixelRef.h" |
| #include "SkScaledImageCache.h" |
| |
| SkCachingPixelRef::SkCachingPixelRef() |
| : fErrorInDecoding(false) |
| , fScaledCacheId(NULL) { |
| memset(&fInfo, 0xFF, sizeof(fInfo)); |
| } |
| SkCachingPixelRef::~SkCachingPixelRef() { |
| SkASSERT(NULL == fScaledCacheId); |
| // Assert always unlock before unref. |
| } |
| |
| bool SkCachingPixelRef::getInfo(SkImageInfo* info) { |
| SkASSERT(info != NULL); |
| if (fErrorInDecoding) { |
| return false; // Don't try again. |
| } |
| if (fInfo.fWidth < 0) { |
| SkImageInfo tmp; |
| if (!this->onDecodeInfo(&tmp)) { |
| fErrorInDecoding = true; |
| return false; |
| } |
| SkASSERT(tmp.fWidth >= 0); |
| fInfo = tmp; |
| } |
| *info = fInfo; |
| return true; |
| } |
| |
| bool SkCachingPixelRef::configure(SkBitmap* bitmap) { |
| SkASSERT(bitmap != NULL); |
| SkImageInfo info; |
| if (!this->getInfo(&info)) { |
| return false; |
| } |
| return bitmap->setConfig(info, 0); |
| } |
| |
| void* SkCachingPixelRef::onLockPixels(SkColorTable** colorTable) { |
| (void)colorTable; |
| SkImageInfo info; |
| if (!this->getInfo(&info)) { |
| return NULL; |
| } |
| SkBitmap bitmap; |
| |
| fScaledCacheId = SkScaledImageCache::FindAndLock(this->getGenerationID(), |
| info.fWidth, |
| info.fHeight, |
| &bitmap); |
| if (NULL == fScaledCacheId) { |
| // Cache has been purged, must re-decode. |
| if (!this->onDecodeInto(0, &bitmap)) { |
| return NULL; |
| } |
| fScaledCacheId = SkScaledImageCache::AddAndLock(this->getGenerationID(), |
| info.fWidth, |
| info.fHeight, |
| bitmap); |
| SkASSERT(fScaledCacheId != NULL); |
| } |
| // Now bitmap should contain a concrete PixelRef of the decoded |
| // image. |
| SkAutoLockPixels autoLockPixels(bitmap); |
| void* pixels = bitmap.getPixels(); |
| SkASSERT(pixels != NULL); |
| // At this point, the autoLockPixels will unlockPixels() |
| // to remove bitmap's lock on the pixels. We will then |
| // destroy bitmap. The *only* guarantee that this pointer |
| // remains valid is the guarantee made by |
| // SkScaledImageCache that it will not destroy the *other* |
| // bitmap (SkScaledImageCache::Rec.fBitmap) that holds a |
| // reference to the concrete PixelRef while this record is |
| // locked. |
| return pixels; |
| } |
| |
| void SkCachingPixelRef::onUnlockPixels() { |
| if (fScaledCacheId != NULL) { |
| SkScaledImageCache::Unlock( |
| static_cast<SkScaledImageCache::ID*>(fScaledCacheId)); |
| fScaledCacheId = NULL; |
| } |
| } |
| |
| bool SkCachingPixelRef::onDecodeInto(int pow2, SkBitmap* bitmap) { |
| SkASSERT(bitmap != NULL); |
| SkBitmap tmp; |
| SkImageInfo info; |
| // TODO(halcanary) - Enable SkCachingPixelRef to use a custom |
| // allocator. `tmp.allocPixels(fAllocator, NULL)` |
| if (!(this->configure(&tmp) && tmp.allocPixels())) { |
| return false; |
| } |
| SkAssertResult(this->getInfo(&info)); // since configure() succeeded. |
| if (!this->onDecodePixels(info, tmp.getPixels(), tmp.rowBytes())) { |
| fErrorInDecoding = true; |
| return false; |
| } |
| *bitmap = tmp; |
| return true; |
| } |
| |