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