blob: ad60892ad1076e81873e0a10e6ad50d869cd6f4f [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"
14
scroggo@google.comcc690202013-03-04 19:56:21 +000015#if LAZY_CACHE_STATS
16#include "SkThread.h"
17
18int32_t SkLazyPixelRef::gCacheHits;
19int32_t SkLazyPixelRef::gCacheMisses;
20#endif
21
scroggo@google.comf8d7d272013-02-22 21:38:35 +000022SkLazyPixelRef::SkLazyPixelRef(SkData* data, SkBitmapFactory::DecodeProc proc, SkImageCache* cache)
23 // Pass NULL for the Mutex so that the default (ring buffer) will be used.
24 : INHERITED(NULL)
25 , fDecodeProc(proc)
26 , fImageCache(cache)
scroggo@google.combb281f72013-03-18 21:37:39 +000027 , fCacheId(SkImageCache::UNINITIALIZED_ID)
28 , fRowBytes(0) {
scroggo@google.comf8d7d272013-02-22 21:38:35 +000029 SkASSERT(fDecodeProc != NULL);
30 if (NULL == data) {
31 fData = SkData::NewEmpty();
32 fErrorInDecoding = true;
33 } else {
34 fData = data;
35 fData->ref();
36 fErrorInDecoding = data->size() == 0;
37 }
38 SkASSERT(cache != NULL);
39 cache->ref();
reed@google.com114038a2013-10-21 20:13:42 +000040
41 // mark as uninitialized -- all fields are -1
42 memset(&fLazilyCachedInfo, 0xFF, sizeof(fLazilyCachedInfo));
43
scroggo@google.comf8d7d272013-02-22 21:38:35 +000044 // Since this pixel ref bases its data on encoded data, it should never change.
45 this->setImmutable();
46}
47
48SkLazyPixelRef::~SkLazyPixelRef() {
49 SkASSERT(fData != NULL);
50 fData->unref();
51 SkASSERT(fImageCache);
52 if (fCacheId != SkImageCache::UNINITIALIZED_ID) {
53 fImageCache->throwAwayCache(fCacheId);
54 }
55 fImageCache->unref();
56}
57
58static size_t ComputeMinRowBytesAndSize(const SkImage::Info& info, size_t* rowBytes) {
59 *rowBytes = SkImageMinRowBytes(info);
60
61 Sk64 safeSize;
62 safeSize.setZero();
63 if (info.fHeight > 0) {
scroggo@google.come5f48242013-02-25 21:47:41 +000064 safeSize.setMul(info.fHeight, SkToS32(*rowBytes));
scroggo@google.comf8d7d272013-02-22 21:38:35 +000065 }
66 SkASSERT(!safeSize.isNeg());
67 return safeSize.is32() ? safeSize.get32() : 0;
68}
69
reed@google.com114038a2013-10-21 20:13:42 +000070const SkImage::Info* SkLazyPixelRef::getCachedInfo() {
71 if (fLazilyCachedInfo.fWidth < 0) {
72 SkImage::Info info;
73 fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, NULL);
74 if (fErrorInDecoding) {
75 return NULL;
76 }
77 fLazilyCachedInfo = info;
78 }
79 return &fLazilyCachedInfo;
80}
81
scroggo@google.comf8d7d272013-02-22 21:38:35 +000082void* SkLazyPixelRef::onLockPixels(SkColorTable**) {
83 if (fErrorInDecoding) {
84 return NULL;
85 }
86 SkBitmapFactory::Target target;
87 // Check to see if the pixels still exist in the cache.
scroggo@google.comcc690202013-03-04 19:56:21 +000088 if (SkImageCache::UNINITIALIZED_ID == fCacheId) {
89 target.fAddr = NULL;
90 } else {
scroggo@google.combb281f72013-03-18 21:37:39 +000091 SkImageCache::DataStatus status;
92 target.fAddr = fImageCache->pinCache(fCacheId, &status);
93 if (target.fAddr == NULL) {
94 fCacheId = SkImageCache::UNINITIALIZED_ID;
95 } else {
96 if (SkImageCache::kRetained_DataStatus == status) {
scroggo@google.comcc690202013-03-04 19:56:21 +000097#if LAZY_CACHE_STATS
scroggo@google.combb281f72013-03-18 21:37:39 +000098 sk_atomic_inc(&gCacheHits);
scroggo@google.comcc690202013-03-04 19:56:21 +000099#endif
scroggo@google.combb281f72013-03-18 21:37:39 +0000100 return target.fAddr;
101 }
102 SkASSERT(SkImageCache::kUninitialized_DataStatus == status);
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000103 }
scroggo@google.combb281f72013-03-18 21:37:39 +0000104 // Cache miss. Either pinCache returned NULL or it returned a memory address without the old
105 // data
scroggo@google.comcc690202013-03-04 19:56:21 +0000106#if LAZY_CACHE_STATS
107 sk_atomic_inc(&gCacheMisses);
108#endif
109 }
reed@google.com114038a2013-10-21 20:13:42 +0000110
scroggo@google.comcc690202013-03-04 19:56:21 +0000111 SkASSERT(fData != NULL && fData->size() > 0);
scroggo@google.comcc690202013-03-04 19:56:21 +0000112 if (NULL == target.fAddr) {
reed@google.com114038a2013-10-21 20:13:42 +0000113 const SkImage::Info* info = this->getCachedInfo();
114 if (NULL == info) {
scroggo@google.combb281f72013-03-18 21:37:39 +0000115 SkASSERT(SkImageCache::UNINITIALIZED_ID == fCacheId);
116 return NULL;
117 }
reed@google.com114038a2013-10-21 20:13:42 +0000118 size_t bytes = ComputeMinRowBytesAndSize(*info, &target.fRowBytes);
scroggo@google.combb281f72013-03-18 21:37:39 +0000119 target.fAddr = fImageCache->allocAndPinCache(bytes, &fCacheId);
120 if (NULL == target.fAddr) {
121 // Space could not be allocated.
122 // Just like the last assert, fCacheId must be UNINITIALIZED_ID.
123 SkASSERT(SkImageCache::UNINITIALIZED_ID == fCacheId);
124 return NULL;
125 }
126 } else {
127 // pinCache returned purged memory to which target.fAddr already points. Set
128 // target.fRowBytes properly.
129 target.fRowBytes = fRowBytes;
130 // Assume that the size is correct, since it was determined by this same function
131 // previously.
scroggo@google.comcc690202013-03-04 19:56:21 +0000132 }
scroggo@google.combb281f72013-03-18 21:37:39 +0000133 SkASSERT(target.fAddr != NULL);
scroggo@google.comcc690202013-03-04 19:56:21 +0000134 SkASSERT(SkImageCache::UNINITIALIZED_ID != fCacheId);
reed@google.com114038a2013-10-21 20:13:42 +0000135 fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), NULL, &target);
scroggo@google.comcc690202013-03-04 19:56:21 +0000136 if (fErrorInDecoding) {
137 fImageCache->throwAwayCache(fCacheId);
138 fCacheId = SkImageCache::UNINITIALIZED_ID;
139 return NULL;
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000140 }
scroggo@google.combb281f72013-03-18 21:37:39 +0000141 // Upon success, store fRowBytes so it can be used in case pinCache later returns purged memory.
142 fRowBytes = target.fRowBytes;
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000143 return target.fAddr;
144}
145
146void SkLazyPixelRef::onUnlockPixels() {
147 if (fErrorInDecoding) {
148 return;
149 }
150 if (fCacheId != SkImageCache::UNINITIALIZED_ID) {
151 fImageCache->releaseCache(fCacheId);
152 }
153}
154
155SkData* SkLazyPixelRef::onRefEncodedData() {
156 fData->ref();
157 return fData;
158}
reed@google.comcee9dcb2013-09-13 16:04:49 +0000159
160#include "SkImagePriv.h"
161
162static bool init_from_info(SkBitmap* bm, const SkImage::Info& info,
163 size_t rowBytes) {
reed@google.com383a6972013-10-21 14:00:07 +0000164 SkBitmap::Config config = SkImageInfoToBitmapConfig(info);
reed@google.comcee9dcb2013-09-13 16:04:49 +0000165 if (SkBitmap::kNo_Config == config) {
166 return false;
167 }
168
reed@google.com383a6972013-10-21 14:00:07 +0000169 return bm->setConfig(config, info.fWidth, info.fHeight, rowBytes, info.fAlphaType)
170 &&
171 bm->allocPixels();
reed@google.comcee9dcb2013-09-13 16:04:49 +0000172}
173
174bool SkLazyPixelRef::onImplementsDecodeInto() {
175 return true;
176}
177
178bool SkLazyPixelRef::onDecodeInto(int pow2, SkBitmap* bitmap) {
179 SkASSERT(fData != NULL && fData->size() > 0);
180 if (fErrorInDecoding) {
181 return false;
182 }
183
184 SkImage::Info info;
185 // Determine the size of the image in order to determine how much memory to allocate.
186 // FIXME: As an optimization, only do this part once.
187 fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, NULL);
188 if (fErrorInDecoding) {
189 return false;
190 }
skia.committer@gmail.coma604c4f2013-09-17 07:01:20 +0000191
reed@google.comcee9dcb2013-09-13 16:04:49 +0000192 SkBitmapFactory::Target target;
193 (void)ComputeMinRowBytesAndSize(info, &target.fRowBytes);
skia.committer@gmail.coma604c4f2013-09-17 07:01:20 +0000194
reed@google.comcee9dcb2013-09-13 16:04:49 +0000195 SkBitmap tmp;
196 if (!init_from_info(&tmp, info, target.fRowBytes)) {
197 return false;
198 }
skia.committer@gmail.coma604c4f2013-09-17 07:01:20 +0000199
reed@google.comcee9dcb2013-09-13 16:04:49 +0000200 target.fAddr = tmp.getPixels();
201 fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, &target);
202 if (fErrorInDecoding) {
203 return false;
204 }
205
206 *bitmap = tmp;
207 return true;
208}