blob: c2ca041b41da71e372be13cbc9c0cc4591440046 [file] [log] [blame]
scroggo@google.comf8d7d272013-02-22 21:38:35 +00001/*
2 * Copyright 2012 Google Inc.
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 "Sk64.h"
9#include "SkLazyPixelRef.h"
10#include "SkColorTable.h"
11#include "SkData.h"
12#include "SkImageCache.h"
13#include "SkImagePriv.h"
commit-bot@chromium.org75854792013-10-29 19:55:00 +000014#include "SkScaledImageCache.h"
scroggo@google.comf8d7d272013-02-22 21:38:35 +000015
scroggo@google.comcc690202013-03-04 19:56:21 +000016#if LAZY_CACHE_STATS
17#include "SkThread.h"
18
19int32_t SkLazyPixelRef::gCacheHits;
20int32_t SkLazyPixelRef::gCacheMisses;
21#endif
22
scroggo@google.comf8d7d272013-02-22 21:38:35 +000023SkLazyPixelRef::SkLazyPixelRef(SkData* data, SkBitmapFactory::DecodeProc proc, SkImageCache* cache)
24 // Pass NULL for the Mutex so that the default (ring buffer) will be used.
25 : INHERITED(NULL)
commit-bot@chromium.org75854792013-10-29 19:55:00 +000026 , fErrorInDecoding(false)
scroggo@google.comf8d7d272013-02-22 21:38:35 +000027 , fDecodeProc(proc)
28 , fImageCache(cache)
scroggo@google.combb281f72013-03-18 21:37:39 +000029 , fRowBytes(0) {
scroggo@google.comf8d7d272013-02-22 21:38:35 +000030 SkASSERT(fDecodeProc != NULL);
31 if (NULL == data) {
32 fData = SkData::NewEmpty();
33 fErrorInDecoding = true;
34 } else {
35 fData = data;
36 fData->ref();
37 fErrorInDecoding = data->size() == 0;
38 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +000039 if (fImageCache != NULL) {
40 fImageCache->ref();
41 fCacheId = SkImageCache::UNINITIALIZED_ID;
42 } else {
43 fScaledCacheId = NULL;
44 }
reed@google.com114038a2013-10-21 20:13:42 +000045
46 // mark as uninitialized -- all fields are -1
47 memset(&fLazilyCachedInfo, 0xFF, sizeof(fLazilyCachedInfo));
skia.committer@gmail.comdb6fa172013-10-22 07:02:30 +000048
scroggo@google.comf8d7d272013-02-22 21:38:35 +000049 // Since this pixel ref bases its data on encoded data, it should never change.
50 this->setImmutable();
51}
52
53SkLazyPixelRef::~SkLazyPixelRef() {
54 SkASSERT(fData != NULL);
55 fData->unref();
commit-bot@chromium.org75854792013-10-29 19:55:00 +000056 if (NULL == fImageCache) {
57 if (fScaledCacheId != NULL) {
58 SkScaledImageCache::Unlock(fScaledCacheId);
59 // TODO(halcanary): SkScaledImageCache needs a
60 // throwAwayCache(id) method.
61 }
62 return;
63 }
scroggo@google.comf8d7d272013-02-22 21:38:35 +000064 SkASSERT(fImageCache);
65 if (fCacheId != SkImageCache::UNINITIALIZED_ID) {
66 fImageCache->throwAwayCache(fCacheId);
67 }
68 fImageCache->unref();
69}
70
reed@google.com2bd8b812013-11-01 13:46:54 +000071static size_t ComputeMinRowBytesAndSize(const SkImageInfo& info, size_t* rowBytes) {
scroggo@google.comf8d7d272013-02-22 21:38:35 +000072 *rowBytes = SkImageMinRowBytes(info);
73
74 Sk64 safeSize;
75 safeSize.setZero();
76 if (info.fHeight > 0) {
scroggo@google.come5f48242013-02-25 21:47:41 +000077 safeSize.setMul(info.fHeight, SkToS32(*rowBytes));
scroggo@google.comf8d7d272013-02-22 21:38:35 +000078 }
79 SkASSERT(!safeSize.isNeg());
80 return safeSize.is32() ? safeSize.get32() : 0;
81}
82
reed@google.com2bd8b812013-11-01 13:46:54 +000083const SkImageInfo* SkLazyPixelRef::getCachedInfo() {
reed@google.com114038a2013-10-21 20:13:42 +000084 if (fLazilyCachedInfo.fWidth < 0) {
reed@google.com2bd8b812013-11-01 13:46:54 +000085 SkImageInfo info;
reed@google.com114038a2013-10-21 20:13:42 +000086 fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, NULL);
87 if (fErrorInDecoding) {
88 return NULL;
89 }
90 fLazilyCachedInfo = info;
91 }
92 return &fLazilyCachedInfo;
93}
94
commit-bot@chromium.org75854792013-10-29 19:55:00 +000095/**
96 Returns bitmap->getPixels() on success; NULL on failure */
reed@google.com2bd8b812013-11-01 13:46:54 +000097static void* decode_into_bitmap(SkImageInfo* info,
commit-bot@chromium.org75854792013-10-29 19:55:00 +000098 SkBitmapFactory::DecodeProc decodeProc,
99 size_t* rowBytes,
100 SkData* data,
101 SkBitmap* bm) {
102 SkASSERT(info && decodeProc && rowBytes && data && bm);
103 if (!(bm->setConfig(SkImageInfoToBitmapConfig(*info), info->fWidth,
104 info->fHeight, *rowBytes, info->fAlphaType)
105 && bm->allocPixels(NULL, NULL))) {
106 // Use the default allocator. It may be necessary for the
107 // SkLazyPixelRef to have a allocator field which is passed
108 // into allocPixels().
109 return NULL;
110 }
111 SkBitmapFactory::Target target;
112 target.fAddr = bm->getPixels();
113 target.fRowBytes = bm->rowBytes();
114 *rowBytes = target.fRowBytes;
115 if (!decodeProc(data->data(), data->size(), info, &target)) {
116 return NULL;
117 }
118 return target.fAddr;
119}
120
121void* SkLazyPixelRef::lockScaledImageCachePixels() {
122 SkASSERT(!fErrorInDecoding);
123 SkASSERT(NULL == fImageCache);
124 SkBitmap bitmap;
reed@google.com2bd8b812013-11-01 13:46:54 +0000125 const SkImageInfo* info = this->getCachedInfo();
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000126 if (info == NULL) {
127 return NULL;
128 }
129 // If this is the first time though, this is guaranteed to fail.
130 // Maybe we should have a flag that says "don't even bother looking"
131 fScaledCacheId = SkScaledImageCache::FindAndLock(this->getGenerationID(),
132 info->fWidth,
133 info->fHeight,
134 &bitmap);
135 if (fScaledCacheId != NULL) {
136 SkAutoLockPixels autoLockPixels(bitmap);
137 void* pixels = bitmap.getPixels();
138 SkASSERT(NULL != pixels);
139 // At this point, the autoLockPixels will unlockPixels()
140 // to remove bitmap's lock on the pixels. We will then
141 // destroy bitmap. The *only* guarantee that this pointer
142 // remains valid is the guarantee made by
143 // SkScaledImageCache that it will not destroy the *other*
144 // bitmap (SkScaledImageCache::Rec.fBitmap) that holds a
145 // reference to the concrete PixelRef while this record is
146 // locked.
147 return pixels;
148 } else {
149 // Cache has been purged, must re-decode.
reed@google.com2bd8b812013-11-01 13:46:54 +0000150 void* pixels = decode_into_bitmap(const_cast<SkImageInfo*>(info),
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000151 fDecodeProc, &fRowBytes, fData,
152 &bitmap);
153 if (NULL == pixels) {
154 fErrorInDecoding = true;
155 return NULL;
156 }
157 fScaledCacheId = SkScaledImageCache::AddAndLock(this->getGenerationID(),
158 info->fWidth,
159 info->fHeight,
160 bitmap);
161 SkASSERT(fScaledCacheId != NULL);
162 return pixels;
163 }
164}
165
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000166void* SkLazyPixelRef::onLockPixels(SkColorTable**) {
167 if (fErrorInDecoding) {
168 return NULL;
169 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000170 if (NULL == fImageCache) {
171 return this->lockScaledImageCachePixels();
172 } else {
173 return this->lockImageCachePixels();
174 }
175}
176
177void* SkLazyPixelRef::lockImageCachePixels() {
178 SkASSERT(fImageCache != NULL);
179 SkASSERT(!fErrorInDecoding);
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000180 SkBitmapFactory::Target target;
181 // Check to see if the pixels still exist in the cache.
scroggo@google.comcc690202013-03-04 19:56:21 +0000182 if (SkImageCache::UNINITIALIZED_ID == fCacheId) {
183 target.fAddr = NULL;
184 } else {
scroggo@google.combb281f72013-03-18 21:37:39 +0000185 SkImageCache::DataStatus status;
186 target.fAddr = fImageCache->pinCache(fCacheId, &status);
187 if (target.fAddr == NULL) {
188 fCacheId = SkImageCache::UNINITIALIZED_ID;
189 } else {
190 if (SkImageCache::kRetained_DataStatus == status) {
scroggo@google.comcc690202013-03-04 19:56:21 +0000191#if LAZY_CACHE_STATS
scroggo@google.combb281f72013-03-18 21:37:39 +0000192 sk_atomic_inc(&gCacheHits);
scroggo@google.comcc690202013-03-04 19:56:21 +0000193#endif
scroggo@google.combb281f72013-03-18 21:37:39 +0000194 return target.fAddr;
195 }
196 SkASSERT(SkImageCache::kUninitialized_DataStatus == status);
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000197 }
scroggo@google.combb281f72013-03-18 21:37:39 +0000198 // Cache miss. Either pinCache returned NULL or it returned a memory address without the old
199 // data
scroggo@google.comcc690202013-03-04 19:56:21 +0000200#if LAZY_CACHE_STATS
201 sk_atomic_inc(&gCacheMisses);
202#endif
203 }
reed@google.com114038a2013-10-21 20:13:42 +0000204
scroggo@google.comcc690202013-03-04 19:56:21 +0000205 SkASSERT(fData != NULL && fData->size() > 0);
scroggo@google.comcc690202013-03-04 19:56:21 +0000206 if (NULL == target.fAddr) {
reed@google.com2bd8b812013-11-01 13:46:54 +0000207 const SkImageInfo* info = this->getCachedInfo();
reed@google.com114038a2013-10-21 20:13:42 +0000208 if (NULL == info) {
scroggo@google.combb281f72013-03-18 21:37:39 +0000209 SkASSERT(SkImageCache::UNINITIALIZED_ID == fCacheId);
210 return NULL;
211 }
reed@google.com114038a2013-10-21 20:13:42 +0000212 size_t bytes = ComputeMinRowBytesAndSize(*info, &target.fRowBytes);
scroggo@google.combb281f72013-03-18 21:37:39 +0000213 target.fAddr = fImageCache->allocAndPinCache(bytes, &fCacheId);
214 if (NULL == target.fAddr) {
215 // Space could not be allocated.
216 // Just like the last assert, fCacheId must be UNINITIALIZED_ID.
217 SkASSERT(SkImageCache::UNINITIALIZED_ID == fCacheId);
218 return NULL;
219 }
220 } else {
221 // pinCache returned purged memory to which target.fAddr already points. Set
222 // target.fRowBytes properly.
223 target.fRowBytes = fRowBytes;
224 // Assume that the size is correct, since it was determined by this same function
225 // previously.
scroggo@google.comcc690202013-03-04 19:56:21 +0000226 }
scroggo@google.combb281f72013-03-18 21:37:39 +0000227 SkASSERT(target.fAddr != NULL);
scroggo@google.comcc690202013-03-04 19:56:21 +0000228 SkASSERT(SkImageCache::UNINITIALIZED_ID != fCacheId);
reed@google.com114038a2013-10-21 20:13:42 +0000229 fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), NULL, &target);
scroggo@google.comcc690202013-03-04 19:56:21 +0000230 if (fErrorInDecoding) {
231 fImageCache->throwAwayCache(fCacheId);
232 fCacheId = SkImageCache::UNINITIALIZED_ID;
233 return NULL;
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000234 }
scroggo@google.combb281f72013-03-18 21:37:39 +0000235 // Upon success, store fRowBytes so it can be used in case pinCache later returns purged memory.
236 fRowBytes = target.fRowBytes;
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000237 return target.fAddr;
238}
239
240void SkLazyPixelRef::onUnlockPixels() {
241 if (fErrorInDecoding) {
242 return;
243 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000244 if (NULL == fImageCache) {
245 // onUnlockPixels() should never be called a second time from
246 // PixelRef::Unlock() without calling onLockPixels() first.
247 SkASSERT(NULL != fScaledCacheId);
248 if (NULL != fScaledCacheId) {
249 SkScaledImageCache::Unlock(fScaledCacheId);
250 fScaledCacheId = NULL;
251 }
252 } else { // use fImageCache
253 SkASSERT(SkImageCache::UNINITIALIZED_ID != fCacheId);
254 if (SkImageCache::UNINITIALIZED_ID != fCacheId) {
255 fImageCache->releaseCache(fCacheId);
256 }
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000257 }
258}
259
260SkData* SkLazyPixelRef::onRefEncodedData() {
261 fData->ref();
262 return fData;
263}
reed@google.comcee9dcb2013-09-13 16:04:49 +0000264
reed@google.com2bd8b812013-11-01 13:46:54 +0000265static bool init_from_info(SkBitmap* bm, const SkImageInfo& info,
reed@google.comcee9dcb2013-09-13 16:04:49 +0000266 size_t rowBytes) {
reed@google.com383a6972013-10-21 14:00:07 +0000267 SkBitmap::Config config = SkImageInfoToBitmapConfig(info);
reed@google.comcee9dcb2013-09-13 16:04:49 +0000268 if (SkBitmap::kNo_Config == config) {
269 return false;
270 }
271
reed@google.com383a6972013-10-21 14:00:07 +0000272 return bm->setConfig(config, info.fWidth, info.fHeight, rowBytes, info.fAlphaType)
273 &&
274 bm->allocPixels();
reed@google.comcee9dcb2013-09-13 16:04:49 +0000275}
276
277bool SkLazyPixelRef::onImplementsDecodeInto() {
278 return true;
279}
280
281bool SkLazyPixelRef::onDecodeInto(int pow2, SkBitmap* bitmap) {
282 SkASSERT(fData != NULL && fData->size() > 0);
283 if (fErrorInDecoding) {
284 return false;
285 }
286
reed@google.com2bd8b812013-11-01 13:46:54 +0000287 SkImageInfo info;
reed@google.comcee9dcb2013-09-13 16:04:49 +0000288 // Determine the size of the image in order to determine how much memory to allocate.
289 // FIXME: As an optimization, only do this part once.
290 fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, NULL);
291 if (fErrorInDecoding) {
292 return false;
293 }
skia.committer@gmail.coma604c4f2013-09-17 07:01:20 +0000294
reed@google.comcee9dcb2013-09-13 16:04:49 +0000295 SkBitmapFactory::Target target;
296 (void)ComputeMinRowBytesAndSize(info, &target.fRowBytes);
skia.committer@gmail.coma604c4f2013-09-17 07:01:20 +0000297
reed@google.comcee9dcb2013-09-13 16:04:49 +0000298 SkBitmap tmp;
299 if (!init_from_info(&tmp, info, target.fRowBytes)) {
300 return false;
301 }
skia.committer@gmail.coma604c4f2013-09-17 07:01:20 +0000302
reed@google.comcee9dcb2013-09-13 16:04:49 +0000303 target.fAddr = tmp.getPixels();
304 fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, &target);
305 if (fErrorInDecoding) {
306 return false;
307 }
308
309 *bitmap = tmp;
310 return true;
311}