| 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 |