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 |