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" |
11 #include "SkPaint.h" | 11 #include "SkPaint.h" |
12 #include "SkShader.h" // for tilemodes | 12 #include "SkShader.h" // for tilemodes |
13 #include "SkUtilsArm.h" | 13 #include "SkUtilsArm.h" |
14 #include "SkBitmapScaler.h" | 14 #include "SkBitmapScaler.h" |
15 #include "SkMipMap.h" | 15 #include "SkMipMap.h" |
16 #include "SkPixelRef.h" | |
16 #include "SkScaledImageCache.h" | 17 #include "SkScaledImageCache.h" |
17 | 18 |
18 #if !SK_ARM_NEON_IS_NONE | 19 #if !SK_ARM_NEON_IS_NONE |
19 // These are defined in src/opts/SkBitmapProcState_arm_neon.cpp | 20 // These are defined in src/opts/SkBitmapProcState_arm_neon.cpp |
20 extern const SkBitmapProcState::SampleProc16 gSkBitmapProcStateSample16_neon[]; | 21 extern const SkBitmapProcState::SampleProc16 gSkBitmapProcStateSample16_neon[]; |
21 extern const SkBitmapProcState::SampleProc32 gSkBitmapProcStateSample32_neon[]; | 22 extern const SkBitmapProcState::SampleProc32 gSkBitmapProcStateSample32_neon[]; |
22 extern void S16_D16_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, i nt, uint16_t*); | 23 extern void S16_D16_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, i nt, uint16_t*); |
23 extern void Clamp_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, i nt, int, uint16_t*, int); | 24 extern void Clamp_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, i nt, int, uint16_t*, int); |
24 extern void Repeat_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint16_t*, int); | 25 extern void Repeat_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint16_t*, int); |
25 extern void SI8_opaque_D32_filter_DX_neon(const SkBitmapProcState&, const uint3 2_t*, int, SkPMColor*); | 26 extern void SI8_opaque_D32_filter_DX_neon(const SkBitmapProcState&, const uint3 2_t*, int, SkPMColor*); |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
102 v2.fX = mat.getSkewX(); | 103 v2.fX = mat.getSkewX(); |
103 v2.fY = mat.getScaleY(); | 104 v2.fY = mat.getScaleY(); |
104 | 105 |
105 return SkMaxScalar(v1.lengthSqd(), v2.lengthSqd()); | 106 return SkMaxScalar(v1.lengthSqd(), v2.lengthSqd()); |
106 } | 107 } |
107 | 108 |
108 // TODO -- we may want to pass the clip into this function so we only scale | 109 // TODO -- we may want to pass the clip into this function so we only scale |
109 // the portion of the image that we're going to need. This will complicate | 110 // the portion of the image that we're going to need. This will complicate |
110 // the interface to the cache, but might be well worth it. | 111 // the interface to the cache, but might be well worth it. |
111 | 112 |
112 void SkBitmapProcState::possiblyScaleImage() { | 113 bool SkBitmapProcState::possiblyScaleImage() { |
114 SkASSERT(NULL == fBitmap); | |
115 SkASSERT(NULL == fScaledCacheID); | |
113 | 116 |
114 if (fFilterLevel <= SkPaint::kLow_FilterLevel) { | 117 if (fFilterLevel <= SkPaint::kLow_FilterLevel) { |
115 // none or low (bilerp) does not need to look any further | 118 return false; |
116 return; | |
117 } | 119 } |
118 | 120 |
119 // STEP 1: Highest quality direct scale? | |
120 | |
121 // Check to see if the transformation matrix is simple, and if we're | 121 // 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 | 122 // doing high quality scaling. If so, do the bitmap scale here and |
123 // remove the scaling component from the matrix. | 123 // remove the scaling component from the matrix. |
124 | 124 |
125 if (SkPaint::kHigh_FilterLevel == fFilterLevel && | 125 if (SkPaint::kHigh_FilterLevel == fFilterLevel && |
126 fInvMatrix.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Ma sk) && | 126 fInvMatrix.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Ma sk) && |
127 fOrigBitmap.config() == SkBitmap::kARGB_8888_Config) { | 127 fOrigBitmap.config() == SkBitmap::kARGB_8888_Config) { |
128 | 128 |
129 SkScalar invScaleX = fInvMatrix.getScaleX(); | 129 SkScalar invScaleX = fInvMatrix.getScaleX(); |
130 SkScalar invScaleY = fInvMatrix.getScaleY(); | 130 SkScalar invScaleY = fInvMatrix.getScaleY(); |
131 | 131 |
132 SkASSERT(NULL == fScaledCacheID); | |
133 fScaledCacheID = SkScaledImageCache::FindAndLock(fOrigBitmap, | 132 fScaledCacheID = SkScaledImageCache::FindAndLock(fOrigBitmap, |
134 invScaleX, invScaleY, | 133 invScaleX, invScaleY, |
135 &fScaledBitmap); | 134 &fScaledBitmap); |
136 if (NULL == fScaledCacheID) { | 135 if (NULL == fScaledCacheID) { |
137 int dest_width = SkScalarCeilToInt(fOrigBitmap.width() / invScaleX) ; | 136 int dest_width = SkScalarCeilToInt(fOrigBitmap.width() / invScaleX) ; |
138 int dest_height = SkScalarCeilToInt(fOrigBitmap.height() / invScaleY ); | 137 int dest_height = SkScalarCeilToInt(fOrigBitmap.height() / invScaleY ); |
139 | 138 |
140 // All the criteria are met; let's make a new bitmap. | 139 // All the criteria are met; let's make a new bitmap. |
141 | 140 |
142 SkConvolutionProcs simd; | 141 SkConvolutionProcs simd; |
143 sk_bzero(&simd, sizeof(simd)); | 142 sk_bzero(&simd, sizeof(simd)); |
144 this->platformConvolutionProcs(&simd); | 143 this->platformConvolutionProcs(&simd); |
145 | 144 |
146 if (!SkBitmapScaler::Resize(&fScaledBitmap, | 145 if (!SkBitmapScaler::Resize(&fScaledBitmap, |
147 fOrigBitmap, | 146 fOrigBitmap, |
148 SkBitmapScaler::RESIZE_BEST, | 147 SkBitmapScaler::RESIZE_BEST, |
149 dest_width, | 148 dest_width, |
150 dest_height, | 149 dest_height, |
151 simd)) { | 150 simd)) { |
152 // we failed to create fScaledBitmap, so just return and let | 151 // we failed to create fScaledBitmap, so just return and let |
153 // the scanline proc handle it. | 152 // the scanline proc handle it. |
154 return; | 153 return true; |
155 | 154 |
156 } | 155 } |
157 fScaledCacheID = SkScaledImageCache::AddAndLock(fOrigBitmap, | 156 fScaledCacheID = SkScaledImageCache::AddAndLock(fOrigBitmap, |
158 invScaleX, | 157 invScaleX, |
159 invScaleY, | 158 invScaleY, |
160 fScaledBitmap); | 159 fScaledBitmap); |
161 } | 160 } |
162 fScaledBitmap.lockPixels(); | 161 fScaledBitmap.lockPixels(); // wonder if Resize() should have locked thi s |
163 | |
164 fBitmap = &fScaledBitmap; | 162 fBitmap = &fScaledBitmap; |
165 | 163 |
166 // set the inv matrix type to translate-only; | 164 // set the inv matrix type to translate-only; |
167 | |
168 fInvMatrix.setTranslate(fInvMatrix.getTranslateX() / fInvMatrix.getScale X(), | 165 fInvMatrix.setTranslate(fInvMatrix.getTranslateX() / fInvMatrix.getScale X(), |
169 fInvMatrix.getTranslateY() / fInvMatrix.getScale Y()); | 166 fInvMatrix.getTranslateY() / fInvMatrix.getScale Y()); |
170 | 167 |
171 // no need for any further filtering; we just did it! | 168 // no need for any further filtering; we just did it! |
172 | |
173 fFilterLevel = SkPaint::kNone_FilterLevel; | 169 fFilterLevel = SkPaint::kNone_FilterLevel; |
174 | 170 return true; |
175 return; | |
176 } | 171 } |
177 | 172 |
178 /* | 173 /* |
179 * If we get here, the caller has requested either Med or High filter-level | |
scroggo
2013/09/10 18:08:31
Do these comments no longer apply?
| |
180 * | |
181 * If High, then our special-case for scale-only did not take, and so we | 174 * If High, then our special-case for scale-only did not take, and so we |
182 * have to make a choice: | 175 * have to make a choice: |
183 * 1. fall back on mipmaps + bilerp | 176 * 1. fall back on mipmaps + bilerp |
184 * 2. fall back on scanline bicubic filter | 177 * 2. fall back on scanline bicubic filter |
185 * For now, we compute the "scale" value from the matrix, and have a | 178 * 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. | 179 * threshold to decide when bicubic is better, and when mips are better. |
187 * No doubt a fancier decision tree could be used uere. | 180 * No doubt a fancier decision tree could be used uere. |
188 * | 181 * |
189 * If Medium, then we just try to build a mipmap and select a level, | 182 * If Medium, then we just try to build a mipmap and select a level, |
190 * setting the filter-level to kLow to signal that we just need bilerp | 183 * setting the filter-level to kLow to signal that we just need bilerp |
191 * to process the selected level. | 184 * to process the selected level. |
192 */ | 185 */ |
193 | 186 |
194 SkScalar scaleSqd = effective_matrix_scale_sqrd(fInvMatrix); | 187 SkScalar scaleSqd = effective_matrix_scale_sqrd(fInvMatrix); |
195 | 188 |
196 if (SkPaint::kHigh_FilterLevel == fFilterLevel) { | 189 if (SkPaint::kHigh_FilterLevel == fFilterLevel) { |
197 // Set the limit at 0.25 for the CTM... if the CTM is scaling smaller | 190 // Set the limit at 0.25 for the CTM... if the CTM is scaling smaller |
198 // than this, then the mipmaps quality may be greater (certainly faster) | 191 // than this, then the mipmaps quality may be greater (certainly faster) |
199 // so we only keep High quality if the scale is greater than this. | 192 // so we only keep High quality if the scale is greater than this. |
200 // | 193 // |
201 // Since we're dealing with the inverse, we compare against its inverse. | 194 // Since we're dealing with the inverse, we compare against its inverse. |
202 const SkScalar bicubicLimit = SkFloatToScalar(4.0f); | 195 const SkScalar bicubicLimit = SkFloatToScalar(4.0f); |
203 const SkScalar bicubicLimitSqd = bicubicLimit * bicubicLimit; | 196 const SkScalar bicubicLimitSqd = bicubicLimit * bicubicLimit; |
204 if (scaleSqd < bicubicLimitSqd) { // use bicubic scanline | 197 if (scaleSqd < bicubicLimitSqd) { // use bicubic scanline |
205 return; | 198 return false; |
206 } | 199 } |
207 | 200 |
208 // else set the filter-level to Medium, since we're scaling down and | 201 // else set the filter-level to Medium, since we're scaling down and |
209 // want to reqeust mipmaps | 202 // want to reqeust mipmaps |
210 fFilterLevel = SkPaint::kMedium_FilterLevel; | 203 fFilterLevel = SkPaint::kMedium_FilterLevel; |
211 } | 204 } |
212 | 205 |
213 SkASSERT(SkPaint::kMedium_FilterLevel == fFilterLevel); | 206 SkASSERT(SkPaint::kMedium_FilterLevel == fFilterLevel); |
214 | 207 |
215 /** | 208 /** |
(...skipping 24 matching lines...) Expand all Loading... | |
240 SkMipMap::Level level; | 233 SkMipMap::Level level; |
241 if (mip->extractLevel(levelScale, &level)) { | 234 if (mip->extractLevel(levelScale, &level)) { |
242 SkScalar invScaleFixup = level.fScale; | 235 SkScalar invScaleFixup = level.fScale; |
243 fInvMatrix.postScale(invScaleFixup, invScaleFixup); | 236 fInvMatrix.postScale(invScaleFixup, invScaleFixup); |
244 | 237 |
245 fScaledBitmap.setConfig(fOrigBitmap.config(), | 238 fScaledBitmap.setConfig(fOrigBitmap.config(), |
246 level.fWidth, level.fHeight, | 239 level.fWidth, level.fHeight, |
247 level.fRowBytes); | 240 level.fRowBytes); |
248 fScaledBitmap.setPixels(level.fPixels); | 241 fScaledBitmap.setPixels(level.fPixels); |
249 fBitmap = &fScaledBitmap; | 242 fBitmap = &fScaledBitmap; |
243 fFilterLevel = SkPaint::kLow_FilterLevel; | |
244 return true; | |
250 } | 245 } |
251 } | 246 } |
252 } | 247 } |
253 | 248 |
254 /* | 249 return false; |
255 * At this point, we may or may not have built a mipmap. Regardless, we | 250 } |
256 * now fall back on Low so will bilerp whatever fBitmap now points at. | 251 |
257 */ | 252 static bool decodeInto(const SkBitmap& src, int pow2, SkBitmap* dst) { |
scroggo
2013/09/10 18:08:31
Style nit: decode_into
reed1
2013/09/10 20:28:11
Done.
| |
258 fFilterLevel = SkPaint::kLow_FilterLevel; | 253 // new way to decodeInto... |
scroggo
2013/09/10 18:08:31
This appears to be a change in behavior: if !mip |
|
scroggo
2013/09/10 18:08:31
Should there be some more explicit comments, eg FI
reed1
2013/09/10 20:28:11
Done.
|
254 SkPixelRef* pr = src.pixelRef(); | |
255 if (pr && pr->decodeInto(pow2, dst)) { | |
256 return true; | |
257 } | |
258 | |
259 // olde school impl... ignores pow2 | |
260 src.lockPixels(); | |
261 if (!src.getPixels()) { | |
262 return false; | |
263 } | |
264 *dst = src; | |
265 dst->lockPixels(); | |
266 src.unlockPixels(); | |
267 return SkToBool(dst->getPixels()); | |
268 } | |
269 | |
270 bool SkBitmapProcState::lockBaseBitmap() { | |
scroggo
2013/09/10 18:08:31
Will this function become more general to lock oth
reed1
2013/09/10 20:28:11
I think not. I think that functionality belongs in
| |
271 fScaledCacheID = SkScaledImageCache::FindAndLock(fOrigBitmap, | |
272 SK_Scalar1, SK_Scalar1, | |
273 &fScaledBitmap); | |
274 if (NULL == fScaledCacheID) { | |
275 if (!decodeInto(fOrigBitmap, 0, &fScaledBitmap)) { | |
276 return false; | |
277 } | |
278 | |
279 fScaledCacheID = SkScaledImageCache::AddAndLock(fOrigBitmap, | |
280 SK_Scalar1, SK_Scalar1, | |
281 fScaledBitmap); | |
282 if (!fScaledCacheID) { | |
283 fScaledBitmap.reset(); | |
284 return false; | |
285 } | |
286 } | |
287 | |
288 fScaledBitmap.lockPixels(); // just 'cause the cache made a copy :( | |
289 fBitmap = &fScaledBitmap; | |
290 return true; | |
259 } | 291 } |
260 | 292 |
261 void SkBitmapProcState::endContext() { | 293 void SkBitmapProcState::endContext() { |
262 SkDELETE(fBitmapFilter); | 294 SkDELETE(fBitmapFilter); |
263 fBitmapFilter = NULL; | 295 fBitmapFilter = NULL; |
264 fScaledBitmap.reset(); | 296 fScaledBitmap.reset(); |
265 | 297 |
266 if (fScaledCacheID) { | 298 if (fScaledCacheID) { |
267 SkScaledImageCache::Unlock(fScaledCacheID); | 299 SkScaledImageCache::Unlock(fScaledCacheID); |
268 fScaledCacheID = NULL; | 300 fScaledCacheID = NULL; |
269 } | 301 } |
270 } | 302 } |
271 | 303 |
272 SkBitmapProcState::~SkBitmapProcState() { | 304 SkBitmapProcState::~SkBitmapProcState() { |
273 if (fScaledCacheID) { | 305 if (fScaledCacheID) { |
274 SkScaledImageCache::Unlock(fScaledCacheID); | 306 SkScaledImageCache::Unlock(fScaledCacheID); |
275 } | 307 } |
276 SkDELETE(fBitmapFilter); | 308 SkDELETE(fBitmapFilter); |
277 } | 309 } |
278 | 310 |
279 bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) { | 311 bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) { |
280 if (fOrigBitmap.width() == 0 || fOrigBitmap.height() == 0) { | 312 SkASSERT(fOrigBitmap.width() && fOrigBitmap.height()); |
281 return false; | |
282 } | |
283 | 313 |
284 fBitmap = &fOrigBitmap; | 314 fBitmap = NULL; |
285 fInvMatrix = inv; | 315 fInvMatrix = inv; |
286 | |
287 // initialize our filter quality to the one requested by the caller. | |
288 // We may downgrade it later if we determine that we either don't need | |
289 // or can't provide as high a quality filtering as the user requested. | |
290 | |
291 fFilterLevel = paint.getFilterLevel(); | 316 fFilterLevel = paint.getFilterLevel(); |
292 | 317 |
293 // possiblyScaleImage will look to see if it can rescale the image as a | 318 // possiblyScaleImage will look to see if it can rescale the image as a |
294 // preprocess; either by scaling up to the target size, or by selecting | 319 // preprocess; either by scaling up to the target size, or by selecting |
295 // a nearby mipmap level. If it does, it will adjust the working | 320 // a nearby mipmap level. If it does, it will adjust the working |
296 // matrix as well as the working bitmap. It may also adjust the filter | 321 // matrix as well as the working bitmap. It may also adjust the filter |
297 // quality to avoid re-filtering an already perfectly scaled image. | 322 // quality to avoid re-filtering an already perfectly scaled image. |
298 | 323 if (!this->possiblyScaleImage()) { |
299 this->possiblyScaleImage(); | 324 if (!this->lockBaseBitmap()) { |
325 return false; | |
326 } | |
327 } | |
328 | |
329 SkASSERT(fBitmap); | |
300 | 330 |
301 bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0; | 331 bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0; |
302 bool clampClamp = SkShader::kClamp_TileMode == fTileModeX && | 332 bool clampClamp = SkShader::kClamp_TileMode == fTileModeX && |
303 SkShader::kClamp_TileMode == fTileModeY; | 333 SkShader::kClamp_TileMode == fTileModeY; |
304 | 334 |
305 if (!(clampClamp || trivialMatrix)) { | 335 if (!(clampClamp || trivialMatrix)) { |
306 fInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height()); | 336 fInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height()); |
307 } | 337 } |
308 | 338 |
309 // Now that all possible changes to the matrix have taken place, check | 339 // Now that all possible changes to the matrix have taken place, check |
310 // to see if we're really close to a no-scale matrix. If so, explicitly | 340 // to see if we're really close to a no-scale matrix. If so, explicitly |
311 // set it to be so. Subsequent code may inspect this matrix to choose | 341 // set it to be so. Subsequent code may inspect this matrix to choose |
312 // a faster path in this case. | 342 // a faster path in this case. |
313 | 343 |
314 // This code will only execute if the matrix has some scale component; | 344 // This code will only execute if the matrix has some scale component; |
315 // if it's already pure translate then we won't do this inversion. | 345 // if it's already pure translate then we won't do this inversion. |
316 | 346 |
317 if (matrix_only_scale_translate(fInvMatrix)) { | 347 if (matrix_only_scale_translate(fInvMatrix)) { |
318 SkMatrix forward; | 348 SkMatrix forward; |
319 if (fInvMatrix.invert(&forward)) { | 349 if (fInvMatrix.invert(&forward)) { |
320 if (clampClamp ? just_trans_clamp(forward, *fBitmap) | 350 if (clampClamp ? just_trans_clamp(forward, *fBitmap) |
321 : just_trans_general(forward)) { | 351 : just_trans_general(forward)) { |
322 SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX()); | 352 SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX()); |
323 SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY()); | 353 SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY()); |
324 fInvMatrix.setTranslate(tx, ty); | 354 fInvMatrix.setTranslate(tx, ty); |
325 | |
326 } | 355 } |
327 } | 356 } |
328 } | 357 } |
329 | 358 |
330 fInvProc = fInvMatrix.getMapXYProc(); | 359 fInvProc = fInvMatrix.getMapXYProc(); |
331 fInvType = fInvMatrix.getType(); | 360 fInvType = fInvMatrix.getType(); |
332 fInvSx = SkScalarToFixed(fInvMatrix.getScaleX()); | 361 fInvSx = SkScalarToFixed(fInvMatrix.getScaleX()); |
333 fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX()); | 362 fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX()); |
334 fInvKy = SkScalarToFixed(fInvMatrix.getSkewY()); | 363 fInvKy = SkScalarToFixed(fInvMatrix.getSkewY()); |
335 fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY()); | 364 fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY()); |
(...skipping 596 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
932 } else { | 961 } else { |
933 size >>= 2; | 962 size >>= 2; |
934 } | 963 } |
935 | 964 |
936 if (fFilterLevel != SkPaint::kNone_FilterLevel) { | 965 if (fFilterLevel != SkPaint::kNone_FilterLevel) { |
937 size >>= 1; | 966 size >>= 1; |
938 } | 967 } |
939 | 968 |
940 return size; | 969 return size; |
941 } | 970 } |
OLD | NEW |