| OLD | NEW |
| 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 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 117 if (0 == maximumAllocation) { | 117 if (0 == maximumAllocation) { |
| 118 return true; | 118 return true; |
| 119 } | 119 } |
| 120 // float matrixScaleFactor = 1.0 / (invMat.scaleX * invMat.scaleY); | 120 // float matrixScaleFactor = 1.0 / (invMat.scaleX * invMat.scaleY); |
| 121 // return ((origBitmapSize * matrixScaleFactor) < maximumAllocationSize); | 121 // return ((origBitmapSize * matrixScaleFactor) < maximumAllocationSize); |
| 122 // Skip the division step: | 122 // Skip the division step: |
| 123 return bm.info().getSafeSize(bm.info().minRowBytes()) | 123 return bm.info().getSafeSize(bm.info().minRowBytes()) |
| 124 < (maximumAllocation * invMat.getScaleX() * invMat.getScaleY()); | 124 < (maximumAllocation * invMat.getScaleX() * invMat.getScaleY()); |
| 125 } | 125 } |
| 126 | 126 |
| 127 // TODO -- we may want to pass the clip into this function so we only scale | |
| 128 // the portion of the image that we're going to need. This will complicate | |
| 129 // the interface to the cache, but might be well worth it. | |
| 130 | |
| 131 bool SkBitmapProcState::possiblyScaleImage() { | |
| 132 SkASSERT(NULL == fBitmap); | |
| 133 | |
| 134 if (fFilterLevel <= SkPaint::kLow_FilterLevel) { | |
| 135 return false; | |
| 136 } | |
| 137 // Check to see if the transformation matrix is simple, and if we're | |
| 138 // doing high quality scaling. If so, do the bitmap scale here and | |
| 139 // remove the (non-fractional) scaling component from the matrix. | |
| 140 | |
| 141 SkScalar invScaleX = fInvMatrix.getScaleX(); | |
| 142 SkScalar invScaleY = fInvMatrix.getScaleY(); | |
| 143 | |
| 144 float trueDestWidth = fOrigBitmap.width() / invScaleX; | |
| 145 float trueDestHeight = fOrigBitmap.height() / invScaleY; | |
| 146 | |
| 147 float roundedDestWidth = SkScalarRoundToScalar(trueDestWidth); | |
| 148 float roundedDestHeight = SkScalarRoundToScalar(trueDestHeight); | |
| 149 | |
| 150 if (SkPaint::kHigh_FilterLevel == fFilterLevel && | |
| 151 fInvMatrix.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Ma
sk) && | |
| 152 kN32_SkColorType == fOrigBitmap.colorType() && | |
| 153 cache_size_okay(fOrigBitmap, fInvMatrix)) { | |
| 154 | |
| 155 if (SkScalarNearlyEqual(invScaleX,1.0f) && | |
| 156 SkScalarNearlyEqual(invScaleY,1.0f)) { | |
| 157 // short-circuit identity scaling; the output is supposed to | |
| 158 // be the same as the input, so we might as well go fast. | |
| 159 | |
| 160 // Note(humper): We could also probably do this if the scales | |
| 161 // are close to -1 as well, since the flip doesn't require | |
| 162 // any fancy re-sampling... | |
| 163 | |
| 164 // Set our filter level to low -- the only post-filtering this | |
| 165 // image might require is some interpolation if the translation | |
| 166 // is fractional. | |
| 167 fFilterLevel = SkPaint::kLow_FilterLevel; | |
| 168 return false; | |
| 169 } | |
| 170 | |
| 171 if (!SkBitmapCache::Find(fOrigBitmap, roundedDestWidth, roundedDestHeigh
t, &fScaledBitmap)) { | |
| 172 // All the criteria are met; let's make a new bitmap. | |
| 173 | |
| 174 if (!SkBitmapScaler::Resize(&fScaledBitmap, | |
| 175 fOrigBitmap, | |
| 176 SkBitmapScaler::RESIZE_BEST, | |
| 177 roundedDestWidth, | |
| 178 roundedDestHeight, | |
| 179 SkResourceCache::GetAllocator())) { | |
| 180 // we failed to create fScaledBitmap, so just return and let | |
| 181 // the scanline proc handle it. | |
| 182 return false; | |
| 183 | |
| 184 } | |
| 185 | |
| 186 SkASSERT(fScaledBitmap.getPixels()); | |
| 187 fScaledBitmap.setImmutable(); | |
| 188 SkBitmapCache::Add(fOrigBitmap, roundedDestWidth, roundedDestHeight,
fScaledBitmap); | |
| 189 } | |
| 190 | |
| 191 SkASSERT(fScaledBitmap.getPixels()); | |
| 192 fBitmap = &fScaledBitmap; | |
| 193 | |
| 194 // clean up the inverse matrix by incorporating the scale we just perfor
med. | |
| 195 | |
| 196 fInvMatrix.postScale(roundedDestWidth / fOrigBitmap.width(), | |
| 197 roundedDestHeight / fOrigBitmap.height()); | |
| 198 | |
| 199 // Set our filter level to low -- the only post-filtering this | |
| 200 // image might require is some interpolation if the translation | |
| 201 // is fractional or if there's any remaining scaling to be done. | |
| 202 fFilterLevel = SkPaint::kLow_FilterLevel; | |
| 203 return true; | |
| 204 } | |
| 205 | |
| 206 /* | |
| 207 * If High, then our special-case for scale-only did not take, and so we | |
| 208 * have to make a choice: | |
| 209 * 1. fall back on mipmaps + bilerp | |
| 210 * 2. fall back on scanline bicubic filter | |
| 211 * For now, we compute the "scale" value from the matrix, and have a | |
| 212 * threshold to decide when bicubic is better, and when mips are better. | |
| 213 * No doubt a fancier decision tree could be used uere. | |
| 214 * | |
| 215 * If Medium, then we just try to build a mipmap and select a level, | |
| 216 * setting the filter-level to kLow to signal that we just need bilerp | |
| 217 * to process the selected level. | |
| 218 */ | |
| 219 | |
| 220 SkScalar scaleSqd = effective_matrix_scale_sqrd(fInvMatrix); | |
| 221 | |
| 222 if (SkPaint::kHigh_FilterLevel == fFilterLevel) { | |
| 223 // Set the limit at 0.25 for the CTM... if the CTM is scaling smaller | |
| 224 // than this, then the mipmaps quality may be greater (certainly faster) | |
| 225 // so we only keep High quality if the scale is greater than this. | |
| 226 // | |
| 227 // Since we're dealing with the inverse, we compare against its inverse. | |
| 228 const SkScalar bicubicLimit = 4.0f; | |
| 229 const SkScalar bicubicLimitSqd = bicubicLimit * bicubicLimit; | |
| 230 if (scaleSqd < bicubicLimitSqd) { // use bicubic scanline | |
| 231 return false; | |
| 232 } | |
| 233 | |
| 234 // else set the filter-level to Medium, since we're scaling down and | |
| 235 // want to reqeust mipmaps | |
| 236 fFilterLevel = SkPaint::kMedium_FilterLevel; | |
| 237 } | |
| 238 | |
| 239 SkASSERT(SkPaint::kMedium_FilterLevel == fFilterLevel); | |
| 240 | |
| 241 /** | |
| 242 * Medium quality means use a mipmap for down-scaling, and just bilper | |
| 243 * for upscaling. Since we're examining the inverse matrix, we look for | |
| 244 * a scale > 1 to indicate down scaling by the CTM. | |
| 245 */ | |
| 246 if (scaleSqd > SK_Scalar1) { | |
| 247 fCurrMip.reset(SkMipMapCache::FindAndRef(fOrigBitmap)); | |
| 248 if (NULL == fCurrMip.get()) { | |
| 249 fCurrMip.reset(SkMipMapCache::AddAndRef(fOrigBitmap)); | |
| 250 if (NULL == fCurrMip.get()) { | |
| 251 return false; | |
| 252 } | |
| 253 } | |
| 254 // diagnostic for a crasher... | |
| 255 if (NULL == fCurrMip->data()) { | |
| 256 sk_throw(); | |
| 257 } | |
| 258 | |
| 259 SkScalar levelScale = SkScalarInvert(SkScalarSqrt(scaleSqd)); | |
| 260 SkMipMap::Level level; | |
| 261 if (fCurrMip->extractLevel(levelScale, &level)) { | |
| 262 SkScalar invScaleFixup = level.fScale; | |
| 263 fInvMatrix.postScale(invScaleFixup, invScaleFixup); | |
| 264 | |
| 265 const SkImageInfo info = fOrigBitmap.info().makeWH(level.fWidth, lev
el.fHeight); | |
| 266 // todo: if we could wrap the fCurrMip in a pixelref, then we could
just install | |
| 267 // that here, and not need to explicitly track it ourselves. | |
| 268 fScaledBitmap.installPixels(info, level.fPixels, level.fRowBytes); | |
| 269 fBitmap = &fScaledBitmap; | |
| 270 fFilterLevel = SkPaint::kLow_FilterLevel; | |
| 271 return true; | |
| 272 } else { | |
| 273 // failed to extract, so release the mipmap | |
| 274 fCurrMip.reset(NULL); | |
| 275 } | |
| 276 } | |
| 277 | |
| 278 return false; | |
| 279 } | |
| 280 | |
| 281 /* | 127 /* |
| 282 * Extract the "best" scale factors from a matrix. | 128 * Extract the "best" scale factors from a matrix. |
| 283 */ | 129 */ |
| 284 static bool extract_scale(const SkMatrix& matrix, SkVector* scale) { | 130 static bool extract_scale(const SkMatrix& matrix, SkVector* scale) { |
| 285 SkASSERT(!matrix.hasPerspective()); | 131 SkASSERT(!matrix.hasPerspective()); |
| 286 SkScalar sx = SkPoint::Length(matrix[SkMatrix::kMScaleX], matrix[SkMatrix::k
MSkewY]); | 132 SkScalar sx = SkPoint::Length(matrix[SkMatrix::kMScaleX], matrix[SkMatrix::k
MSkewY]); |
| 287 SkScalar sy = SkPoint::Length(matrix[SkMatrix::kMSkewX], matrix[SkMatrix::k
MScaleY]); | 133 SkScalar sy = SkPoint::Length(matrix[SkMatrix::kMSkewX], matrix[SkMatrix::k
MScaleY]); |
| 288 if (!SkScalarIsFinite(sx) || !SkScalarIsFinite(sy) || | 134 if (!SkScalarIsFinite(sx) || !SkScalarIsFinite(sy) || |
| 289 SkScalarNearlyZero(sx) || SkScalarNearlyZero(sy)) | 135 SkScalarNearlyZero(sx) || SkScalarNearlyZero(sy)) |
| 290 { | 136 { |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 432 // TODO: if fScaled comes back at a different width/height than fOri
g, | 278 // TODO: if fScaled comes back at a different width/height than fOri
g, |
| 433 // we need to update the matrix we are using to sample from this guy
. | 279 // we need to update the matrix we are using to sample from this guy
. |
| 434 | 280 |
| 435 SkBitmapCache::Add(fOrigBitmap, 1, 1, fScaledBitmap); | 281 SkBitmapCache::Add(fOrigBitmap, 1, 1, fScaledBitmap); |
| 436 } | 282 } |
| 437 } | 283 } |
| 438 fBitmap = &fScaledBitmap; | 284 fBitmap = &fScaledBitmap; |
| 439 return true; | 285 return true; |
| 440 } | 286 } |
| 441 | 287 |
| 442 SkBitmapProcState::~SkBitmapProcState() { | |
| 443 SkDELETE(fBitmapFilter); | |
| 444 } | |
| 445 | |
| 446 static bool valid_for_drawing(const SkBitmap& bm) { | 288 static bool valid_for_drawing(const SkBitmap& bm) { |
| 447 if (0 == bm.width() || 0 == bm.height()) { | 289 if (0 == bm.width() || 0 == bm.height()) { |
| 448 return false; // nothing to draw | 290 return false; // nothing to draw |
| 449 } | 291 } |
| 450 if (NULL == bm.pixelRef()) { | 292 if (NULL == bm.pixelRef()) { |
| 451 return false; // no pixels to read | 293 return false; // no pixels to read |
| 452 } | 294 } |
| 453 if (bm.getTexture()) { | 295 if (bm.getTexture()) { |
| 454 // we can handle texture (ugh) since lockPixels will perform a read-back | 296 // we can handle texture (ugh) since lockPixels will perform a read-back |
| 455 return true; | 297 return true; |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 546 fShaderProc32 = NULL; | 388 fShaderProc32 = NULL; |
| 547 fShaderProc16 = NULL; | 389 fShaderProc16 = NULL; |
| 548 fSampleProc32 = NULL; | 390 fSampleProc32 = NULL; |
| 549 fSampleProc16 = NULL; | 391 fSampleProc16 = NULL; |
| 550 | 392 |
| 551 // recompute the triviality of the matrix here because we may have | 393 // recompute the triviality of the matrix here because we may have |
| 552 // changed it! | 394 // changed it! |
| 553 | 395 |
| 554 trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0; | 396 trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0; |
| 555 | 397 |
| 556 if (SkPaint::kHigh_FilterLevel == fFilterLevel) { | |
| 557 // If this is still set, that means we wanted HQ sampling | |
| 558 // but couldn't do it as a preprocess. Let's try to install | |
| 559 // the scanline version of the HQ sampler. If that process fails, | |
| 560 // downgrade to bilerp. | |
| 561 | |
| 562 // NOTE: Might need to be careful here in the future when we want | |
| 563 // to have the platform proc have a shot at this; it's possible that | |
| 564 // the chooseBitmapFilterProc will fail to install a shader but a | |
| 565 // platform-specific one might succeed, so it might be premature here | |
| 566 // to fall back to bilerp. This needs thought. | |
| 567 | |
| 568 if (!this->setBitmapFilterProcs()) { | |
| 569 fFilterLevel = SkPaint::kLow_FilterLevel; | |
| 570 } | |
| 571 } | |
| 572 | |
| 573 if (SkPaint::kLow_FilterLevel == fFilterLevel) { | 398 if (SkPaint::kLow_FilterLevel == fFilterLevel) { |
| 574 // Only try bilerp if the matrix is "interesting" and | 399 // Only try bilerp if the matrix is "interesting" and |
| 575 // the image has a suitable size. | 400 // the image has a suitable size. |
| 576 | 401 |
| 577 if (fInvType <= SkMatrix::kTranslate_Mask || | 402 if (fInvType <= SkMatrix::kTranslate_Mask || |
| 578 !valid_for_filtering(fBitmap->width() | fBitmap->height())) | 403 !valid_for_filtering(fBitmap->width() | fBitmap->height())) |
| 579 { | 404 { |
| 580 fFilterLevel = SkPaint::kNone_FilterLevel; | 405 fFilterLevel = SkPaint::kNone_FilterLevel; |
| 581 } | 406 } |
| 582 } | 407 } |
| (...skipping 627 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1210 fx += dx; | 1035 fx += dx; |
| 1211 } | 1036 } |
| 1212 } else { | 1037 } else { |
| 1213 for (int i = 0; i < count; ++i) { | 1038 for (int i = 0; i < count; ++i) { |
| 1214 dst[i] = src[SkClampMax(SkFractionalIntToInt(fx), maxX)]; | 1039 dst[i] = src[SkClampMax(SkFractionalIntToInt(fx), maxX)]; |
| 1215 fx += dx; | 1040 fx += dx; |
| 1216 } | 1041 } |
| 1217 } | 1042 } |
| 1218 } | 1043 } |
| 1219 | 1044 |
| OLD | NEW |