Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(94)

Side by Side Diff: src/core/SkBitmapProcState.cpp

Issue 507483002: retool image cache to be generic cache (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: rebase + add comment in unlock Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/core/SkBitmapProcState.h ('k') | src/core/SkScaledImageCache.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2011 Google Inc. 2 * Copyright 2011 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkBitmapCache.h" 8 #include "SkBitmapCache.h"
9 #include "SkBitmapProcState.h" 9 #include "SkBitmapProcState.h"
10 #include "SkColorPriv.h" 10 #include "SkColorPriv.h"
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
102 v1.fY = mat.getSkewY(); 102 v1.fY = mat.getSkewY();
103 103
104 v2.fX = mat.getSkewX(); 104 v2.fX = mat.getSkewX();
105 v2.fY = mat.getScaleY(); 105 v2.fY = mat.getScaleY();
106 106
107 return SkMaxScalar(v1.lengthSqd(), v2.lengthSqd()); 107 return SkMaxScalar(v1.lengthSqd(), v2.lengthSqd());
108 } 108 }
109 109
110 class AutoScaledCacheUnlocker { 110 class AutoScaledCacheUnlocker {
111 public: 111 public:
112 AutoScaledCacheUnlocker(SkScaledImageCache::ID** idPtr) : fIDPtr(idPtr) {} 112 AutoScaledCacheUnlocker(SkScaledImageCache::ID* idPtr) : fIDPtr(idPtr) {}
113 ~AutoScaledCacheUnlocker() { 113 ~AutoScaledCacheUnlocker() {
114 if (fIDPtr && *fIDPtr) { 114 if (fIDPtr && *fIDPtr) {
115 SkScaledImageCache::Unlock(*fIDPtr); 115 SkScaledImageCache::Unlock(*fIDPtr);
116 *fIDPtr = NULL; 116 *fIDPtr = NULL;
117 } 117 }
118 } 118 }
119 119
120 // forgets the ID, so it won't call Unlock 120 // forgets the ID, so it won't call Unlock
121 void release() { 121 void release() {
122 fIDPtr = NULL; 122 fIDPtr = NULL;
123 } 123 }
124 124
125 private: 125 private:
126 SkScaledImageCache::ID** fIDPtr; 126 SkScaledImageCache::ID* fIDPtr;
127 }; 127 };
128 #define AutoScaledCacheUnlocker(...) SK_REQUIRE_LOCAL_VAR(AutoScaledCacheUnlocke r) 128 #define AutoScaledCacheUnlocker(...) SK_REQUIRE_LOCAL_VAR(AutoScaledCacheUnlocke r)
129 129
130 // Check to see that the size of the bitmap that would be produced by 130 // Check to see that the size of the bitmap that would be produced by
131 // scaling by the given inverted matrix is less than the maximum allowed. 131 // scaling by the given inverted matrix is less than the maximum allowed.
132 static inline bool cache_size_okay(const SkBitmap& bm, const SkMatrix& invMat) { 132 static inline bool cache_size_okay(const SkBitmap& bm, const SkMatrix& invMat) {
133 size_t maximumAllocation 133 size_t maximumAllocation
134 = SkScaledImageCache::GetSingleAllocationByteLimit(); 134 = SkScaledImageCache::GetSingleAllocationByteLimit();
135 if (0 == maximumAllocation) { 135 if (0 == maximumAllocation) {
136 return true; 136 return true;
137 } 137 }
138 // float matrixScaleFactor = 1.0 / (invMat.scaleX * invMat.scaleY); 138 // float matrixScaleFactor = 1.0 / (invMat.scaleX * invMat.scaleY);
139 // return ((origBitmapSize * matrixScaleFactor) < maximumAllocationSize); 139 // return ((origBitmapSize * matrixScaleFactor) < maximumAllocationSize);
140 // Skip the division step: 140 // Skip the division step:
141 return bm.info().getSafeSize(bm.info().minRowBytes()) 141 return bm.info().getSafeSize(bm.info().minRowBytes())
142 < (maximumAllocation * invMat.getScaleX() * invMat.getScaleY()); 142 < (maximumAllocation * invMat.getScaleX() * invMat.getScaleY());
143 } 143 }
144 144
145 // TODO -- we may want to pass the clip into this function so we only scale 145 // TODO -- we may want to pass the clip into this function so we only scale
146 // the portion of the image that we're going to need. This will complicate 146 // the portion of the image that we're going to need. This will complicate
147 // the interface to the cache, but might be well worth it. 147 // the interface to the cache, but might be well worth it.
148 148
149 bool SkBitmapProcState::possiblyScaleImage() { 149 bool SkBitmapProcState::possiblyScaleImage() {
150 AutoScaledCacheUnlocker unlocker(&fScaledCacheID);
151
152 SkASSERT(NULL == fBitmap); 150 SkASSERT(NULL == fBitmap);
153 SkASSERT(NULL == fScaledCacheID);
154 151
155 if (fFilterLevel <= SkPaint::kLow_FilterLevel) { 152 if (fFilterLevel <= SkPaint::kLow_FilterLevel) {
156 return false; 153 return false;
157 } 154 }
158 // Check to see if the transformation matrix is simple, and if we're 155 // Check to see if the transformation matrix is simple, and if we're
159 // doing high quality scaling. If so, do the bitmap scale here and 156 // doing high quality scaling. If so, do the bitmap scale here and
160 // remove the scaling component from the matrix. 157 // remove the scaling component from the matrix.
161 158
162 if (SkPaint::kHigh_FilterLevel == fFilterLevel && 159 if (SkPaint::kHigh_FilterLevel == fFilterLevel &&
163 fInvMatrix.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Ma sk) && 160 fInvMatrix.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Ma sk) &&
(...skipping 12 matching lines...) Expand all
176 // are close to -1 as well, since the flip doesn't require 173 // are close to -1 as well, since the flip doesn't require
177 // any fancy re-sampling... 174 // any fancy re-sampling...
178 175
179 // Set our filter level to low -- the only post-filtering this 176 // Set our filter level to low -- the only post-filtering this
180 // image might require is some interpolation if the translation 177 // image might require is some interpolation if the translation
181 // is fractional. 178 // is fractional.
182 fFilterLevel = SkPaint::kLow_FilterLevel; 179 fFilterLevel = SkPaint::kLow_FilterLevel;
183 return false; 180 return false;
184 } 181 }
185 182
186 fScaledCacheID = SkBitmapCache::FindAndLock(fOrigBitmap, invScaleX, invS caleY, 183 if (!SkBitmapCache::Find(fOrigBitmap, invScaleX, invScaleY, &fScaledBitm ap)) {
187 &fScaledBitmap);
188 if (fScaledCacheID) {
189 fScaledBitmap.lockPixels();
190 if (!fScaledBitmap.getPixels()) {
191 fScaledBitmap.unlockPixels();
192 // found a purged entry (discardablememory?), release it
193 SkScaledImageCache::Unlock(fScaledCacheID);
194 fScaledCacheID = NULL;
195 // fall through to rebuild
196 }
197 }
198
199 if (NULL == fScaledCacheID) {
200 float dest_width = fOrigBitmap.width() / invScaleX; 184 float dest_width = fOrigBitmap.width() / invScaleX;
201 float dest_height = fOrigBitmap.height() / invScaleY; 185 float dest_height = fOrigBitmap.height() / invScaleY;
202 186
203 // All the criteria are met; let's make a new bitmap. 187 // All the criteria are met; let's make a new bitmap.
204 188
205 if (!SkBitmapScaler::Resize(&fScaledBitmap, 189 if (!SkBitmapScaler::Resize(&fScaledBitmap,
206 fOrigBitmap, 190 fOrigBitmap,
207 SkBitmapScaler::RESIZE_BEST, 191 SkBitmapScaler::RESIZE_BEST,
208 dest_width, 192 dest_width,
209 dest_height, 193 dest_height,
210 SkScaledImageCache::GetAllocator())) { 194 SkScaledImageCache::GetAllocator())) {
211 // we failed to create fScaledBitmap, so just return and let 195 // we failed to create fScaledBitmap, so just return and let
212 // the scanline proc handle it. 196 // the scanline proc handle it.
213 return false; 197 return false;
214 198
215 } 199 }
216 200
217 SkASSERT(NULL != fScaledBitmap.getPixels()); 201 SkASSERT(NULL != fScaledBitmap.getPixels());
218 fScaledCacheID = SkBitmapCache::AddAndLock(fOrigBitmap, invScaleX, i nvScaleY, 202 SkBitmapCache::Add(fOrigBitmap, invScaleX, invScaleY, fScaledBitmap) ;
219 fScaledBitmap);
220 if (!fScaledCacheID) {
221 fScaledBitmap.reset();
222 return false;
223 }
224 SkASSERT(NULL != fScaledBitmap.getPixels());
225 } 203 }
226 204
227 SkASSERT(NULL != fScaledBitmap.getPixels()); 205 SkASSERT(NULL != fScaledBitmap.getPixels());
228 fBitmap = &fScaledBitmap; 206 fBitmap = &fScaledBitmap;
229 207
230 // set the inv matrix type to translate-only; 208 // set the inv matrix type to translate-only;
231 fInvMatrix.setTranslate(fInvMatrix.getTranslateX() / fInvMatrix.getScale X(), 209 fInvMatrix.setTranslate(fInvMatrix.getTranslateX() / fInvMatrix.getScale X(),
232 fInvMatrix.getTranslateY() / fInvMatrix.getScale Y()); 210 fInvMatrix.getTranslateY() / fInvMatrix.getScale Y());
233 211
234 // Set our filter level to low -- the only post-filtering this 212 // Set our filter level to low -- the only post-filtering this
235 // image might require is some interpolation if the translation 213 // image might require is some interpolation if the translation
236 // is fractional. 214 // is fractional.
237 fFilterLevel = SkPaint::kLow_FilterLevel; 215 fFilterLevel = SkPaint::kLow_FilterLevel;
238 unlocker.release();
239 return true; 216 return true;
240 } 217 }
241 218
242 /* 219 /*
243 * If High, then our special-case for scale-only did not take, and so we 220 * If High, then our special-case for scale-only did not take, and so we
244 * have to make a choice: 221 * have to make a choice:
245 * 1. fall back on mipmaps + bilerp 222 * 1. fall back on mipmaps + bilerp
246 * 2. fall back on scanline bicubic filter 223 * 2. fall back on scanline bicubic filter
247 * For now, we compute the "scale" value from the matrix, and have a 224 * For now, we compute the "scale" value from the matrix, and have a
248 * threshold to decide when bicubic is better, and when mips are better. 225 * threshold to decide when bicubic is better, and when mips are better.
(...skipping 24 matching lines...) Expand all
273 } 250 }
274 251
275 SkASSERT(SkPaint::kMedium_FilterLevel == fFilterLevel); 252 SkASSERT(SkPaint::kMedium_FilterLevel == fFilterLevel);
276 253
277 /** 254 /**
278 * Medium quality means use a mipmap for down-scaling, and just bilper 255 * Medium quality means use a mipmap for down-scaling, and just bilper
279 * for upscaling. Since we're examining the inverse matrix, we look for 256 * for upscaling. Since we're examining the inverse matrix, we look for
280 * a scale > 1 to indicate down scaling by the CTM. 257 * a scale > 1 to indicate down scaling by the CTM.
281 */ 258 */
282 if (scaleSqd > SK_Scalar1) { 259 if (scaleSqd > SK_Scalar1) {
283 const SkMipMap* mip = NULL; 260 fCurrMip.reset(SkMipMapCache::FindAndRef(fOrigBitmap));
284 261 if (NULL == fCurrMip.get()) {
285 SkASSERT(NULL == fScaledCacheID); 262 fCurrMip.reset(SkMipMap::Build(fOrigBitmap));
286 fScaledCacheID = SkMipMapCache::FindAndLock(fOrigBitmap, &mip); 263 if (NULL == fCurrMip.get()) {
287 if (!fScaledCacheID) { 264 return false;
288 SkASSERT(NULL == mip);
289 mip = SkMipMap::Build(fOrigBitmap);
290 if (mip) {
291 fScaledCacheID = SkMipMapCache::AddAndLock(fOrigBitmap, mip);
292 SkASSERT(mip->getRefCnt() > 1);
293 mip->unref(); // the cache took a ref
294 SkASSERT(fScaledCacheID);
295 } 265 }
296 } else { 266 SkMipMapCache::Add(fOrigBitmap, fCurrMip);
297 SkASSERT(mip);
298 } 267 }
299 268
300 if (mip) { 269 SkScalar levelScale = SkScalarInvert(SkScalarSqrt(scaleSqd));
301 SkScalar levelScale = SkScalarInvert(SkScalarSqrt(scaleSqd)); 270 SkMipMap::Level level;
302 SkMipMap::Level level; 271 if (fCurrMip->extractLevel(levelScale, &level)) {
303 if (mip->extractLevel(levelScale, &level)) { 272 SkScalar invScaleFixup = level.fScale;
304 SkScalar invScaleFixup = level.fScale; 273 fInvMatrix.postScale(invScaleFixup, invScaleFixup);
305 fInvMatrix.postScale(invScaleFixup, invScaleFixup);
306 274
307 SkImageInfo info = fOrigBitmap.info(); 275 SkImageInfo info = fOrigBitmap.info();
308 info.fWidth = level.fWidth; 276 info.fWidth = level.fWidth;
309 info.fHeight = level.fHeight; 277 info.fHeight = level.fHeight;
310 fScaledBitmap.installPixels(info, level.fPixels, level.fRowBytes ); 278 // todo: if we could wrap the fCurrMip in a pixelref, then we could just install
311 fBitmap = &fScaledBitmap; 279 // that here, and not need to explicitly track it ourselves.
312 fFilterLevel = SkPaint::kLow_FilterLevel; 280 fScaledBitmap.installPixels(info, level.fPixels, level.fRowBytes);
313 unlocker.release(); 281 fBitmap = &fScaledBitmap;
314 return true; 282 fFilterLevel = SkPaint::kLow_FilterLevel;
315 } 283 return true;
316 } 284 }
317 } 285 }
318 286
319 return false; 287 return false;
320 } 288 }
321 289
322 static bool get_locked_pixels(const SkBitmap& src, int pow2, SkBitmap* dst) { 290 static bool get_locked_pixels(const SkBitmap& src, int pow2, SkBitmap* dst) {
323 SkPixelRef* pr = src.pixelRef(); 291 SkPixelRef* pr = src.pixelRef();
324 if (pr && pr->decodeInto(pow2, dst)) { 292 if (pr && pr->decodeInto(pow2, dst)) {
325 return true; 293 return true;
326 } 294 }
327 295
328 /* 296 /*
329 * If decodeInto() fails, it is possibe that we have an old subclass that 297 * If decodeInto() fails, it is possibe that we have an old subclass that
330 * does not, or cannot, implement that. In that case we fall back to the 298 * does not, or cannot, implement that. In that case we fall back to the
331 * older protocol of having the pixelRef handle the caching for us. 299 * older protocol of having the pixelRef handle the caching for us.
332 */ 300 */
333 *dst = src; 301 *dst = src;
334 dst->lockPixels(); 302 dst->lockPixels();
335 return SkToBool(dst->getPixels()); 303 return SkToBool(dst->getPixels());
336 } 304 }
337 305
338 bool SkBitmapProcState::lockBaseBitmap() { 306 bool SkBitmapProcState::lockBaseBitmap() {
339 AutoScaledCacheUnlocker unlocker(&fScaledCacheID);
340
341 SkPixelRef* pr = fOrigBitmap.pixelRef(); 307 SkPixelRef* pr = fOrigBitmap.pixelRef();
342 308
343 SkASSERT(NULL == fScaledCacheID);
344
345 if (pr->isLocked() || !pr->implementsDecodeInto()) { 309 if (pr->isLocked() || !pr->implementsDecodeInto()) {
346 // fast-case, no need to look in our cache 310 // fast-case, no need to look in our cache
347 fScaledBitmap = fOrigBitmap; 311 fScaledBitmap = fOrigBitmap;
348 fScaledBitmap.lockPixels(); 312 fScaledBitmap.lockPixels();
349 if (NULL == fScaledBitmap.getPixels()) { 313 if (NULL == fScaledBitmap.getPixels()) {
350 return false; 314 return false;
351 } 315 }
352 } else { 316 } else {
353 fScaledCacheID = SkBitmapCache::FindAndLock(fOrigBitmap, 1, 1, &fScaledB itmap); 317 if (!SkBitmapCache::Find(fOrigBitmap, 1, 1, &fScaledBitmap)) {
354 if (fScaledCacheID) {
355 fScaledBitmap.lockPixels();
356 if (!fScaledBitmap.getPixels()) {
357 fScaledBitmap.unlockPixels();
358 // found a purged entry (discardablememory?), release it
359 SkScaledImageCache::Unlock(fScaledCacheID);
360 fScaledCacheID = NULL;
361 // fall through to rebuild
362 }
363 }
364
365 if (NULL == fScaledCacheID) {
366 if (!get_locked_pixels(fOrigBitmap, 0, &fScaledBitmap)) { 318 if (!get_locked_pixels(fOrigBitmap, 0, &fScaledBitmap)) {
367 return false; 319 return false;
368 } 320 }
369 321
370 // TODO: if fScaled comes back at a different width/height than fOri g, 322 // TODO: if fScaled comes back at a different width/height than fOri g,
371 // we need to update the matrix we are using to sample from this guy . 323 // we need to update the matrix we are using to sample from this guy .
372 324
373 fScaledCacheID = SkBitmapCache::AddAndLock(fOrigBitmap, 1, 1, fScale dBitmap); 325 SkBitmapCache::Add(fOrigBitmap, 1, 1, fScaledBitmap);
374 if (!fScaledCacheID) {
375 fScaledBitmap.reset();
376 return false;
377 }
378 } 326 }
379 } 327 }
380 fBitmap = &fScaledBitmap; 328 fBitmap = &fScaledBitmap;
381 unlocker.release();
382 return true; 329 return true;
383 } 330 }
384 331
385 SkBitmapProcState::~SkBitmapProcState() { 332 SkBitmapProcState::~SkBitmapProcState() {
386 if (fScaledCacheID) {
387 SkScaledImageCache::Unlock(fScaledCacheID);
388 }
389 SkDELETE(fBitmapFilter); 333 SkDELETE(fBitmapFilter);
390 } 334 }
391 335
392 bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) { 336 bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
393 SkASSERT(fOrigBitmap.width() && fOrigBitmap.height()); 337 SkASSERT(fOrigBitmap.width() && fOrigBitmap.height());
394 338
395 fBitmap = NULL; 339 fBitmap = NULL;
396 fInvMatrix = inv; 340 fInvMatrix = inv;
397 fFilterLevel = paint.getFilterLevel(); 341 fFilterLevel = paint.getFilterLevel();
398 342
399 SkASSERT(NULL == fScaledCacheID);
400
401 // possiblyScaleImage will look to see if it can rescale the image as a 343 // possiblyScaleImage will look to see if it can rescale the image as a
402 // preprocess; either by scaling up to the target size, or by selecting 344 // preprocess; either by scaling up to the target size, or by selecting
403 // a nearby mipmap level. If it does, it will adjust the working 345 // a nearby mipmap level. If it does, it will adjust the working
404 // matrix as well as the working bitmap. It may also adjust the filter 346 // matrix as well as the working bitmap. It may also adjust the filter
405 // quality to avoid re-filtering an already perfectly scaled image. 347 // quality to avoid re-filtering an already perfectly scaled image.
406 if (!this->possiblyScaleImage()) { 348 if (!this->possiblyScaleImage()) {
407 if (!this->lockBaseBitmap()) { 349 if (!this->lockBaseBitmap()) {
408 return false; 350 return false;
409 } 351 }
410 } 352 }
(...skipping 654 matching lines...) Expand 10 before | Expand all | Expand 10 after
1065 } else { 1007 } else {
1066 size >>= 2; 1008 size >>= 2;
1067 } 1009 }
1068 1010
1069 if (fFilterLevel != SkPaint::kNone_FilterLevel) { 1011 if (fFilterLevel != SkPaint::kNone_FilterLevel) {
1070 size >>= 1; 1012 size >>= 1;
1071 } 1013 }
1072 1014
1073 return size; 1015 return size;
1074 } 1016 }
OLDNEW
« no previous file with comments | « src/core/SkBitmapProcState.h ('k') | src/core/SkScaledImageCache.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698