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 | |
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 |
| 249 return false; |
| 250 } |
| 251 |
| 252 static bool get_locked_pixels(const SkBitmap& src, int pow2, SkBitmap* dst) { |
| 253 SkPixelRef* pr = src.pixelRef(); |
| 254 if (pr && pr->decodeInto(pow2, dst)) { |
| 255 return true; |
| 256 } |
| 257 |
254 /* | 258 /* |
255 * At this point, we may or may not have built a mipmap. Regardless, we | 259 * If decodeInto() fails, it is possibe that we have an old subclass that |
256 * now fall back on Low so will bilerp whatever fBitmap now points at. | 260 * does not, or cannot, implement that. In that case we fall back to the |
| 261 * older protocol of having the pixelRef handle the caching for us. |
257 */ | 262 */ |
258 fFilterLevel = SkPaint::kLow_FilterLevel; | 263 *dst = src; |
| 264 dst->lockPixels(); |
| 265 return SkToBool(dst->getPixels()); |
| 266 } |
| 267 |
| 268 bool SkBitmapProcState::lockBaseBitmap() { |
| 269 SkPixelRef* pr = fOrigBitmap.pixelRef(); |
| 270 |
| 271 if (pr->isLocked() || !pr->implementsDecodeInto()) { |
| 272 // fast-case, no need to look in our cache |
| 273 fScaledBitmap = fOrigBitmap; |
| 274 } else { |
| 275 fScaledCacheID = SkScaledImageCache::FindAndLock(fOrigBitmap, |
| 276 SK_Scalar1, SK_Scalar1, |
| 277 &fScaledBitmap); |
| 278 if (NULL == fScaledCacheID) { |
| 279 if (!get_locked_pixels(fOrigBitmap, 0, &fScaledBitmap)) { |
| 280 return false; |
| 281 } |
| 282 |
| 283 // TODO: if fScaled comes back at a different width/height than fOri
g, |
| 284 // we need to update the matrix we are using to sample from this guy
. |
| 285 |
| 286 fScaledCacheID = SkScaledImageCache::AddAndLock(fOrigBitmap, |
| 287 SK_Scalar1, SK_Scala
r1, |
| 288 fScaledBitmap); |
| 289 if (!fScaledCacheID) { |
| 290 fScaledBitmap.reset(); |
| 291 return false; |
| 292 } |
| 293 } |
| 294 } |
| 295 fScaledBitmap.lockPixels(); // just 'cause the cache made a copy :( |
| 296 fBitmap = &fScaledBitmap; |
| 297 return true; |
259 } | 298 } |
260 | 299 |
261 void SkBitmapProcState::endContext() { | 300 void SkBitmapProcState::endContext() { |
262 SkDELETE(fBitmapFilter); | 301 SkDELETE(fBitmapFilter); |
263 fBitmapFilter = NULL; | 302 fBitmapFilter = NULL; |
264 fScaledBitmap.reset(); | 303 fScaledBitmap.reset(); |
265 | 304 |
266 if (fScaledCacheID) { | 305 if (fScaledCacheID) { |
267 SkScaledImageCache::Unlock(fScaledCacheID); | 306 SkScaledImageCache::Unlock(fScaledCacheID); |
268 fScaledCacheID = NULL; | 307 fScaledCacheID = NULL; |
269 } | 308 } |
270 } | 309 } |
271 | 310 |
272 SkBitmapProcState::~SkBitmapProcState() { | 311 SkBitmapProcState::~SkBitmapProcState() { |
273 if (fScaledCacheID) { | 312 if (fScaledCacheID) { |
274 SkScaledImageCache::Unlock(fScaledCacheID); | 313 SkScaledImageCache::Unlock(fScaledCacheID); |
275 } | 314 } |
276 SkDELETE(fBitmapFilter); | 315 SkDELETE(fBitmapFilter); |
277 } | 316 } |
278 | 317 |
279 bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) { | 318 bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) { |
280 if (fOrigBitmap.width() == 0 || fOrigBitmap.height() == 0) { | 319 SkASSERT(fOrigBitmap.width() && fOrigBitmap.height()); |
281 return false; | |
282 } | |
283 | 320 |
284 fBitmap = &fOrigBitmap; | 321 fBitmap = NULL; |
285 fInvMatrix = inv; | 322 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(); | 323 fFilterLevel = paint.getFilterLevel(); |
292 | 324 |
293 // possiblyScaleImage will look to see if it can rescale the image as a | 325 // 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 | 326 // 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 | 327 // 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 | 328 // matrix as well as the working bitmap. It may also adjust the filter |
297 // quality to avoid re-filtering an already perfectly scaled image. | 329 // quality to avoid re-filtering an already perfectly scaled image. |
298 | 330 if (!this->possiblyScaleImage()) { |
299 this->possiblyScaleImage(); | 331 if (!this->lockBaseBitmap()) { |
| 332 return false; |
| 333 } |
| 334 } |
| 335 |
| 336 SkASSERT(fBitmap); |
300 | 337 |
301 bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) ==
0; | 338 bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) ==
0; |
302 bool clampClamp = SkShader::kClamp_TileMode == fTileModeX && | 339 bool clampClamp = SkShader::kClamp_TileMode == fTileModeX && |
303 SkShader::kClamp_TileMode == fTileModeY; | 340 SkShader::kClamp_TileMode == fTileModeY; |
304 | 341 |
305 if (!(clampClamp || trivialMatrix)) { | 342 if (!(clampClamp || trivialMatrix)) { |
306 fInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height()); | 343 fInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height()); |
307 } | 344 } |
308 | 345 |
309 // Now that all possible changes to the matrix have taken place, check | 346 // 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 | 347 // 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 | 348 // set it to be so. Subsequent code may inspect this matrix to choose |
312 // a faster path in this case. | 349 // a faster path in this case. |
313 | 350 |
314 // This code will only execute if the matrix has some scale component; | 351 // 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. | 352 // if it's already pure translate then we won't do this inversion. |
316 | 353 |
317 if (matrix_only_scale_translate(fInvMatrix)) { | 354 if (matrix_only_scale_translate(fInvMatrix)) { |
318 SkMatrix forward; | 355 SkMatrix forward; |
319 if (fInvMatrix.invert(&forward)) { | 356 if (fInvMatrix.invert(&forward)) { |
320 if (clampClamp ? just_trans_clamp(forward, *fBitmap) | 357 if (clampClamp ? just_trans_clamp(forward, *fBitmap) |
321 : just_trans_general(forward)) { | 358 : just_trans_general(forward)) { |
322 SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX()); | 359 SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX()); |
323 SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY()); | 360 SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY()); |
324 fInvMatrix.setTranslate(tx, ty); | 361 fInvMatrix.setTranslate(tx, ty); |
325 | |
326 } | 362 } |
327 } | 363 } |
328 } | 364 } |
329 | 365 |
330 fInvProc = fInvMatrix.getMapXYProc(); | 366 fInvProc = fInvMatrix.getMapXYProc(); |
331 fInvType = fInvMatrix.getType(); | 367 fInvType = fInvMatrix.getType(); |
332 fInvSx = SkScalarToFixed(fInvMatrix.getScaleX()); | 368 fInvSx = SkScalarToFixed(fInvMatrix.getScaleX()); |
333 fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX()); | 369 fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX()); |
334 fInvKy = SkScalarToFixed(fInvMatrix.getSkewY()); | 370 fInvKy = SkScalarToFixed(fInvMatrix.getSkewY()); |
335 fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY()); | 371 fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY()); |
(...skipping 596 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
932 } else { | 968 } else { |
933 size >>= 2; | 969 size >>= 2; |
934 } | 970 } |
935 | 971 |
936 if (fFilterLevel != SkPaint::kNone_FilterLevel) { | 972 if (fFilterLevel != SkPaint::kNone_FilterLevel) { |
937 size >>= 1; | 973 size >>= 1; |
938 } | 974 } |
939 | 975 |
940 return size; | 976 return size; |
941 } | 977 } |
OLD | NEW |