OLD | NEW |
---|---|
1 | 1 |
2 /* | 2 /* |
3 * Copyright 2011 Google Inc. | 3 * Copyright 2011 Google Inc. |
4 * | 4 * |
5 * Use of this source code is governed by a BSD-style license that can be | 5 * Use of this source code is governed by a BSD-style license that can be |
6 * found in the LICENSE file. | 6 * found in the LICENSE file. |
7 */ | 7 */ |
8 #include "SkBitmapProcState.h" | 8 #include "SkBitmapProcState.h" |
9 #include "SkColorPriv.h" | 9 #include "SkColorPriv.h" |
10 #include "SkFilterProc.h" | 10 #include "SkFilterProc.h" |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
99 | 99 |
100 v1.fX = mat.getScaleX(); | 100 v1.fX = mat.getScaleX(); |
101 v1.fY = mat.getSkewY(); | 101 v1.fY = mat.getSkewY(); |
102 | 102 |
103 v2.fX = mat.getSkewX(); | 103 v2.fX = mat.getSkewX(); |
104 v2.fY = mat.getScaleY(); | 104 v2.fY = mat.getScaleY(); |
105 | 105 |
106 return SkMaxScalar(v1.lengthSqd(), v2.lengthSqd()); | 106 return SkMaxScalar(v1.lengthSqd(), v2.lengthSqd()); |
107 } | 107 } |
108 | 108 |
109 class AutoScaledCacheUnlocker { | |
scroggo
2013/12/12 20:34:55
Maybe not necessary, since this is hidden in a sou
reed1
2013/12/12 21:13:49
Done.
| |
110 public: | |
111 AutoScaledCacheUnlocker(SkScaledImageCache::ID** idPtr) : fIDPtr(idPtr) {} | |
112 ~AutoScaledCacheUnlocker() { | |
113 if (fIDPtr && *fIDPtr) { | |
114 SkScaledImageCache::Unlock(*fIDPtr); | |
115 *fIDPtr = NULL; | |
116 } | |
117 } | |
118 | |
119 void keepLock() { | |
scroggo
2013/12/12 20:34:55
How about reset? That keeps it consistent with our
hal.canary
2013/12/12 20:48:43
it's more of a detach() than a reset()
reed1
2013/12/12 21:13:49
How about release() -- release, since detach usual
| |
120 fIDPtr = NULL; | |
121 } | |
122 | |
123 private: | |
124 SkScaledImageCache::ID** fIDPtr; | |
125 }; | |
126 | |
109 // TODO -- we may want to pass the clip into this function so we only scale | 127 // TODO -- we may want to pass the clip into this function so we only scale |
110 // the portion of the image that we're going to need. This will complicate | 128 // the portion of the image that we're going to need. This will complicate |
111 // the interface to the cache, but might be well worth it. | 129 // the interface to the cache, but might be well worth it. |
112 | 130 |
113 bool SkBitmapProcState::possiblyScaleImage() { | 131 bool SkBitmapProcState::possiblyScaleImage() { |
132 AutoScaledCacheUnlocker unlocker(&fScaledCacheID); | |
133 | |
114 SkASSERT(NULL == fBitmap); | 134 SkASSERT(NULL == fBitmap); |
115 SkASSERT(NULL == fScaledCacheID); | 135 SkASSERT(NULL == fScaledCacheID); |
116 | 136 |
117 if (fFilterLevel <= SkPaint::kLow_FilterLevel) { | 137 if (fFilterLevel <= SkPaint::kLow_FilterLevel) { |
118 return false; | 138 return false; |
119 } | 139 } |
120 | 140 |
121 // Check to see if the transformation matrix is simple, and if we're | 141 // Check to see if the transformation matrix is simple, and if we're |
122 // doing high quality scaling. If so, do the bitmap scale here and | 142 // doing high quality scaling. If so, do the bitmap scale here and |
123 // remove the scaling component from the matrix. | 143 // remove the scaling component from the matrix. |
124 | 144 |
125 if (SkPaint::kHigh_FilterLevel == fFilterLevel && | 145 if (SkPaint::kHigh_FilterLevel == fFilterLevel && |
126 fInvMatrix.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Ma sk) && | 146 fInvMatrix.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Ma sk) && |
127 fOrigBitmap.config() == SkBitmap::kARGB_8888_Config) { | 147 fOrigBitmap.config() == SkBitmap::kARGB_8888_Config) { |
128 | 148 |
129 SkScalar invScaleX = fInvMatrix.getScaleX(); | 149 SkScalar invScaleX = fInvMatrix.getScaleX(); |
130 SkScalar invScaleY = fInvMatrix.getScaleY(); | 150 SkScalar invScaleY = fInvMatrix.getScaleY(); |
131 | 151 |
132 fScaledCacheID = SkScaledImageCache::FindAndLock(fOrigBitmap, | 152 fScaledCacheID = SkScaledImageCache::FindAndLock(fOrigBitmap, |
133 invScaleX, invScaleY, | 153 invScaleX, invScaleY, |
134 &fScaledBitmap); | 154 &fScaledBitmap); |
155 if (fScaledCacheID) { | |
156 fScaledBitmap.lockPixels(); | |
157 if (!fScaledBitmap.getPixels()) { | |
158 fScaledBitmap.unlockPixels(); | |
159 // found a purged entry (discardablememory?), release it | |
160 SkScaledImageCache::Unlock(fScaledCacheID); | |
161 fScaledCacheID = NULL; | |
162 // fall through to rebuild | |
163 } | |
164 } | |
165 | |
135 if (NULL == fScaledCacheID) { | 166 if (NULL == fScaledCacheID) { |
136 int dest_width = SkScalarCeilToInt(fOrigBitmap.width() / invScaleX) ; | 167 int dest_width = SkScalarCeilToInt(fOrigBitmap.width() / invScaleX) ; |
137 int dest_height = SkScalarCeilToInt(fOrigBitmap.height() / invScaleY ); | 168 int dest_height = SkScalarCeilToInt(fOrigBitmap.height() / invScaleY ); |
138 | 169 |
139 // All the criteria are met; let's make a new bitmap. | 170 // All the criteria are met; let's make a new bitmap. |
140 | 171 |
141 SkConvolutionProcs simd; | 172 SkConvolutionProcs simd; |
142 sk_bzero(&simd, sizeof(simd)); | 173 sk_bzero(&simd, sizeof(simd)); |
143 this->platformConvolutionProcs(&simd); | 174 this->platformConvolutionProcs(&simd); |
144 | 175 |
145 if (!SkBitmapScaler::Resize(&fScaledBitmap, | 176 if (!SkBitmapScaler::Resize(&fScaledBitmap, |
146 fOrigBitmap, | 177 fOrigBitmap, |
147 SkBitmapScaler::RESIZE_BEST, | 178 SkBitmapScaler::RESIZE_BEST, |
148 dest_width, | 179 dest_width, |
149 dest_height, | 180 dest_height, |
150 simd, | 181 simd, |
151 SkScaledImageCache::GetAllocator())) { | 182 SkScaledImageCache::GetAllocator())) { |
152 // we failed to create fScaledBitmap, so just return and let | 183 // we failed to create fScaledBitmap, so just return and let |
153 // the scanline proc handle it. | 184 // the scanline proc handle it. |
154 return false; | 185 return false; |
155 | 186 |
156 } | 187 } |
188 SkASSERT(NULL != fScaledBitmap.getPixels()); | |
157 fScaledCacheID = SkScaledImageCache::AddAndLock(fOrigBitmap, | 189 fScaledCacheID = SkScaledImageCache::AddAndLock(fOrigBitmap, |
158 invScaleX, | 190 invScaleX, |
159 invScaleY, | 191 invScaleY, |
160 fScaledBitmap); | 192 fScaledBitmap); |
161 } | 193 if (!fScaledCacheID) { |
162 fScaledBitmap.lockPixels(); // wonder if Resize() should have locked thi s | 194 fScaledBitmap.reset(); |
163 if (!fScaledBitmap.getPixels()) { | 195 return false; |
164 // TODO: find out how this can happen, and add a unittest to exercis e | 196 } |
165 // inspired by BUG=chromium:295895 | 197 SkASSERT(NULL != fScaledBitmap.getPixels()); |
166 return false; | |
167 } | 198 } |
168 | 199 |
200 SkASSERT(NULL != fScaledBitmap.getPixels()); | |
169 fBitmap = &fScaledBitmap; | 201 fBitmap = &fScaledBitmap; |
170 | 202 |
171 // set the inv matrix type to translate-only; | 203 // set the inv matrix type to translate-only; |
172 fInvMatrix.setTranslate(fInvMatrix.getTranslateX() / fInvMatrix.getScale X(), | 204 fInvMatrix.setTranslate(fInvMatrix.getTranslateX() / fInvMatrix.getScale X(), |
173 fInvMatrix.getTranslateY() / fInvMatrix.getScale Y()); | 205 fInvMatrix.getTranslateY() / fInvMatrix.getScale Y()); |
174 | 206 |
175 // no need for any further filtering; we just did it! | 207 // no need for any further filtering; we just did it! |
176 fFilterLevel = SkPaint::kNone_FilterLevel; | 208 fFilterLevel = SkPaint::kNone_FilterLevel; |
209 unlocker.keepLock(); | |
177 return true; | 210 return true; |
178 } | 211 } |
179 | 212 |
180 /* | 213 /* |
181 * If High, then our special-case for scale-only did not take, and so we | 214 * If High, then our special-case for scale-only did not take, and so we |
182 * have to make a choice: | 215 * have to make a choice: |
183 * 1. fall back on mipmaps + bilerp | 216 * 1. fall back on mipmaps + bilerp |
184 * 2. fall back on scanline bicubic filter | 217 * 2. fall back on scanline bicubic filter |
185 * For now, we compute the "scale" value from the matrix, and have a | 218 * For now, we compute the "scale" value from the matrix, and have a |
186 * threshold to decide when bicubic is better, and when mips are better. | 219 * threshold to decide when bicubic is better, and when mips are better. |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
241 if (mip->extractLevel(levelScale, &level)) { | 274 if (mip->extractLevel(levelScale, &level)) { |
242 SkScalar invScaleFixup = level.fScale; | 275 SkScalar invScaleFixup = level.fScale; |
243 fInvMatrix.postScale(invScaleFixup, invScaleFixup); | 276 fInvMatrix.postScale(invScaleFixup, invScaleFixup); |
244 | 277 |
245 fScaledBitmap.setConfig(fOrigBitmap.config(), | 278 fScaledBitmap.setConfig(fOrigBitmap.config(), |
246 level.fWidth, level.fHeight, | 279 level.fWidth, level.fHeight, |
247 level.fRowBytes); | 280 level.fRowBytes); |
248 fScaledBitmap.setPixels(level.fPixels); | 281 fScaledBitmap.setPixels(level.fPixels); |
249 fBitmap = &fScaledBitmap; | 282 fBitmap = &fScaledBitmap; |
250 fFilterLevel = SkPaint::kLow_FilterLevel; | 283 fFilterLevel = SkPaint::kLow_FilterLevel; |
284 unlocker.keepLock(); | |
251 return true; | 285 return true; |
252 } | 286 } |
253 } | 287 } |
254 } | 288 } |
255 | 289 |
256 return false; | 290 return false; |
257 } | 291 } |
258 | 292 |
259 static bool get_locked_pixels(const SkBitmap& src, int pow2, SkBitmap* dst) { | 293 static bool get_locked_pixels(const SkBitmap& src, int pow2, SkBitmap* dst) { |
260 SkPixelRef* pr = src.pixelRef(); | 294 SkPixelRef* pr = src.pixelRef(); |
261 if (pr && pr->decodeInto(pow2, dst)) { | 295 if (pr && pr->decodeInto(pow2, dst)) { |
262 return true; | 296 return true; |
263 } | 297 } |
264 | 298 |
265 /* | 299 /* |
266 * If decodeInto() fails, it is possibe that we have an old subclass that | 300 * If decodeInto() fails, it is possibe that we have an old subclass that |
267 * does not, or cannot, implement that. In that case we fall back to the | 301 * does not, or cannot, implement that. In that case we fall back to the |
268 * older protocol of having the pixelRef handle the caching for us. | 302 * older protocol of having the pixelRef handle the caching for us. |
269 */ | 303 */ |
270 *dst = src; | 304 *dst = src; |
271 dst->lockPixels(); | 305 dst->lockPixels(); |
272 return SkToBool(dst->getPixels()); | 306 return SkToBool(dst->getPixels()); |
273 } | 307 } |
274 | 308 |
275 bool SkBitmapProcState::lockBaseBitmap() { | 309 bool SkBitmapProcState::lockBaseBitmap() { |
310 AutoScaledCacheUnlocker unlocker(&fScaledCacheID); | |
311 | |
276 SkPixelRef* pr = fOrigBitmap.pixelRef(); | 312 SkPixelRef* pr = fOrigBitmap.pixelRef(); |
277 | 313 |
314 SkASSERT(NULL == fScaledCacheID); | |
315 | |
278 if (pr->isLocked() || !pr->implementsDecodeInto()) { | 316 if (pr->isLocked() || !pr->implementsDecodeInto()) { |
279 // fast-case, no need to look in our cache | 317 // fast-case, no need to look in our cache |
280 fScaledBitmap = fOrigBitmap; | 318 fScaledBitmap = fOrigBitmap; |
319 fScaledBitmap.lockPixels(); | |
320 SkASSERT(NULL != fScaledBitmap.getPixels()); | |
281 } else { | 321 } else { |
282 fScaledCacheID = SkScaledImageCache::FindAndLock(fOrigBitmap, | 322 fScaledCacheID = SkScaledImageCache::FindAndLock(fOrigBitmap, |
283 SK_Scalar1, SK_Scalar1, | 323 SK_Scalar1, SK_Scalar1, |
284 &fScaledBitmap); | 324 &fScaledBitmap); |
325 if (fScaledCacheID) { | |
326 fScaledBitmap.lockPixels(); | |
327 if (!fScaledBitmap.getPixels()) { | |
328 fScaledBitmap.unlockPixels(); | |
329 // found a purged entry (discardablememory?), release it | |
330 SkScaledImageCache::Unlock(fScaledCacheID); | |
331 fScaledCacheID = NULL; | |
332 // fall through to rebuild | |
333 } | |
334 } | |
335 | |
285 if (NULL == fScaledCacheID) { | 336 if (NULL == fScaledCacheID) { |
286 if (!get_locked_pixels(fOrigBitmap, 0, &fScaledBitmap)) { | 337 if (!get_locked_pixels(fOrigBitmap, 0, &fScaledBitmap)) { |
287 return false; | 338 return false; |
288 } | 339 } |
289 | 340 |
290 // TODO: if fScaled comes back at a different width/height than fOri g, | 341 // TODO: if fScaled comes back at a different width/height than fOri g, |
291 // we need to update the matrix we are using to sample from this guy . | 342 // we need to update the matrix we are using to sample from this guy . |
292 | 343 |
293 fScaledCacheID = SkScaledImageCache::AddAndLock(fOrigBitmap, | 344 fScaledCacheID = SkScaledImageCache::AddAndLock(fOrigBitmap, |
294 SK_Scalar1, SK_Scala r1, | 345 SK_Scalar1, SK_Scala r1, |
295 fScaledBitmap); | 346 fScaledBitmap); |
296 if (!fScaledCacheID) { | 347 if (!fScaledCacheID) { |
297 fScaledBitmap.reset(); | 348 fScaledBitmap.reset(); |
298 return false; | 349 return false; |
299 } | 350 } |
300 } | 351 } |
301 } | 352 } |
302 fScaledBitmap.lockPixels(); // just 'cause the cache made a copy :( | |
303 if (!fScaledBitmap.getPixels()) { | |
304 // TODO: find out how this can happen, and add a unittest to exercise | |
305 // inspired by BUG=chromium:295895 | |
306 return false; | |
307 } | |
308 fBitmap = &fScaledBitmap; | 353 fBitmap = &fScaledBitmap; |
354 unlocker.keepLock(); | |
309 return true; | 355 return true; |
310 } | 356 } |
311 | 357 |
312 void SkBitmapProcState::endContext() { | 358 void SkBitmapProcState::endContext() { |
313 SkDELETE(fBitmapFilter); | 359 SkDELETE(fBitmapFilter); |
314 fBitmapFilter = NULL; | 360 fBitmapFilter = NULL; |
315 fScaledBitmap.reset(); | 361 fScaledBitmap.reset(); |
316 | 362 |
317 if (fScaledCacheID) { | 363 if (fScaledCacheID) { |
318 SkScaledImageCache::Unlock(fScaledCacheID); | 364 SkScaledImageCache::Unlock(fScaledCacheID); |
319 fScaledCacheID = NULL; | 365 fScaledCacheID = NULL; |
320 } | 366 } |
321 } | 367 } |
322 | 368 |
323 SkBitmapProcState::~SkBitmapProcState() { | 369 SkBitmapProcState::~SkBitmapProcState() { |
324 if (fScaledCacheID) { | 370 if (fScaledCacheID) { |
325 SkScaledImageCache::Unlock(fScaledCacheID); | 371 SkScaledImageCache::Unlock(fScaledCacheID); |
326 } | 372 } |
327 SkDELETE(fBitmapFilter); | 373 SkDELETE(fBitmapFilter); |
328 } | 374 } |
329 | 375 |
330 bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) { | 376 bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) { |
331 SkASSERT(fOrigBitmap.width() && fOrigBitmap.height()); | 377 SkASSERT(fOrigBitmap.width() && fOrigBitmap.height()); |
332 | 378 |
333 fBitmap = NULL; | 379 fBitmap = NULL; |
334 fInvMatrix = inv; | 380 fInvMatrix = inv; |
335 fFilterLevel = paint.getFilterLevel(); | 381 fFilterLevel = paint.getFilterLevel(); |
336 | 382 |
383 SkASSERT(NULL == fScaledCacheID); | |
384 | |
337 // possiblyScaleImage will look to see if it can rescale the image as a | 385 // possiblyScaleImage will look to see if it can rescale the image as a |
338 // preprocess; either by scaling up to the target size, or by selecting | 386 // preprocess; either by scaling up to the target size, or by selecting |
339 // a nearby mipmap level. If it does, it will adjust the working | 387 // a nearby mipmap level. If it does, it will adjust the working |
340 // matrix as well as the working bitmap. It may also adjust the filter | 388 // matrix as well as the working bitmap. It may also adjust the filter |
341 // quality to avoid re-filtering an already perfectly scaled image. | 389 // quality to avoid re-filtering an already perfectly scaled image. |
342 if (!this->possiblyScaleImage()) { | 390 if (!this->possiblyScaleImage()) { |
343 if (!this->lockBaseBitmap()) { | 391 if (!this->lockBaseBitmap()) { |
344 return false; | 392 return false; |
345 } | 393 } |
346 } | 394 } |
(...skipping 634 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
981 } else { | 1029 } else { |
982 size >>= 2; | 1030 size >>= 2; |
983 } | 1031 } |
984 | 1032 |
985 if (fFilterLevel != SkPaint::kNone_FilterLevel) { | 1033 if (fFilterLevel != SkPaint::kNone_FilterLevel) { |
986 size >>= 1; | 1034 size >>= 1; |
987 } | 1035 } |
988 | 1036 |
989 return size; | 1037 return size; |
990 } | 1038 } |
OLD | NEW |