1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/core/SkBitmapProcState.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1042 @@ 1.4 + 1.5 +/* 1.6 + * Copyright 2011 Google Inc. 1.7 + * 1.8 + * Use of this source code is governed by a BSD-style license that can be 1.9 + * found in the LICENSE file. 1.10 + */ 1.11 +#include "SkBitmapProcState.h" 1.12 +#include "SkColorPriv.h" 1.13 +#include "SkFilterProc.h" 1.14 +#include "SkPaint.h" 1.15 +#include "SkShader.h" // for tilemodes 1.16 +#include "SkUtilsArm.h" 1.17 +#include "SkBitmapScaler.h" 1.18 +#include "SkMipMap.h" 1.19 +#include "SkPixelRef.h" 1.20 +#include "SkScaledImageCache.h" 1.21 + 1.22 +#if !SK_ARM_NEON_IS_NONE 1.23 +// These are defined in src/opts/SkBitmapProcState_arm_neon.cpp 1.24 +extern const SkBitmapProcState::SampleProc16 gSkBitmapProcStateSample16_neon[]; 1.25 +extern const SkBitmapProcState::SampleProc32 gSkBitmapProcStateSample32_neon[]; 1.26 +extern void S16_D16_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, uint16_t*); 1.27 +extern void Clamp_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint16_t*, int); 1.28 +extern void Repeat_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint16_t*, int); 1.29 +extern void SI8_opaque_D32_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, SkPMColor*); 1.30 +extern void SI8_opaque_D32_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint32_t*, int); 1.31 +extern void Clamp_SI8_opaque_D32_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint32_t*, int); 1.32 +#endif 1.33 + 1.34 +#define NAME_WRAP(x) x 1.35 +#include "SkBitmapProcState_filter.h" 1.36 +#include "SkBitmapProcState_procs.h" 1.37 + 1.38 +/////////////////////////////////////////////////////////////////////////////// 1.39 + 1.40 +// true iff the matrix contains, at most, scale and translate elements 1.41 +static bool matrix_only_scale_translate(const SkMatrix& m) { 1.42 + return m.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask); 1.43 +} 1.44 + 1.45 +/** 1.46 + * For the purposes of drawing bitmaps, if a matrix is "almost" translate 1.47 + * go ahead and treat it as if it were, so that subsequent code can go fast. 1.48 + */ 1.49 +static bool just_trans_clamp(const SkMatrix& matrix, const SkBitmap& bitmap) { 1.50 + SkASSERT(matrix_only_scale_translate(matrix)); 1.51 + 1.52 + if (matrix.getType() & SkMatrix::kScale_Mask) { 1.53 + SkRect src, dst; 1.54 + bitmap.getBounds(&src); 1.55 + 1.56 + // Can't call mapRect(), since that will fix up inverted rectangles, 1.57 + // e.g. when scale is negative, and we don't want to return true for 1.58 + // those. 1.59 + matrix.mapPoints(SkTCast<SkPoint*>(&dst), 1.60 + SkTCast<const SkPoint*>(&src), 1.61 + 2); 1.62 + 1.63 + // Now round all 4 edges to device space, and then compare the device 1.64 + // width/height to the original. Note: we must map all 4 and subtract 1.65 + // rather than map the "width" and compare, since we care about the 1.66 + // phase (in pixel space) that any translate in the matrix might impart. 1.67 + SkIRect idst; 1.68 + dst.round(&idst); 1.69 + return idst.width() == bitmap.width() && idst.height() == bitmap.height(); 1.70 + } 1.71 + // if we got here, we're either kTranslate_Mask or identity 1.72 + return true; 1.73 +} 1.74 + 1.75 +static bool just_trans_general(const SkMatrix& matrix) { 1.76 + SkASSERT(matrix_only_scale_translate(matrix)); 1.77 + 1.78 + if (matrix.getType() & SkMatrix::kScale_Mask) { 1.79 + const SkScalar tol = SK_Scalar1 / 32768; 1.80 + 1.81 + if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleX] - SK_Scalar1, tol)) { 1.82 + return false; 1.83 + } 1.84 + if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleY] - SK_Scalar1, tol)) { 1.85 + return false; 1.86 + } 1.87 + } 1.88 + // if we got here, treat us as either kTranslate_Mask or identity 1.89 + return true; 1.90 +} 1.91 + 1.92 +/////////////////////////////////////////////////////////////////////////////// 1.93 + 1.94 +static bool valid_for_filtering(unsigned dimension) { 1.95 + // for filtering, width and height must fit in 14bits, since we use steal 1.96 + // 2 bits from each to store our 4bit subpixel data 1.97 + return (dimension & ~0x3FFF) == 0; 1.98 +} 1.99 + 1.100 +static SkScalar effective_matrix_scale_sqrd(const SkMatrix& mat) { 1.101 + SkPoint v1, v2; 1.102 + 1.103 + v1.fX = mat.getScaleX(); 1.104 + v1.fY = mat.getSkewY(); 1.105 + 1.106 + v2.fX = mat.getSkewX(); 1.107 + v2.fY = mat.getScaleY(); 1.108 + 1.109 + return SkMaxScalar(v1.lengthSqd(), v2.lengthSqd()); 1.110 +} 1.111 + 1.112 +class AutoScaledCacheUnlocker { 1.113 +public: 1.114 + AutoScaledCacheUnlocker(SkScaledImageCache::ID** idPtr) : fIDPtr(idPtr) {} 1.115 + ~AutoScaledCacheUnlocker() { 1.116 + if (fIDPtr && *fIDPtr) { 1.117 + SkScaledImageCache::Unlock(*fIDPtr); 1.118 + *fIDPtr = NULL; 1.119 + } 1.120 + } 1.121 + 1.122 + // forgets the ID, so it won't call Unlock 1.123 + void release() { 1.124 + fIDPtr = NULL; 1.125 + } 1.126 + 1.127 +private: 1.128 + SkScaledImageCache::ID** fIDPtr; 1.129 +}; 1.130 +#define AutoScaledCacheUnlocker(...) SK_REQUIRE_LOCAL_VAR(AutoScaledCacheUnlocker) 1.131 + 1.132 +// TODO -- we may want to pass the clip into this function so we only scale 1.133 +// the portion of the image that we're going to need. This will complicate 1.134 +// the interface to the cache, but might be well worth it. 1.135 + 1.136 +bool SkBitmapProcState::possiblyScaleImage() { 1.137 + AutoScaledCacheUnlocker unlocker(&fScaledCacheID); 1.138 + 1.139 + SkASSERT(NULL == fBitmap); 1.140 + SkASSERT(NULL == fScaledCacheID); 1.141 + 1.142 + if (fFilterLevel <= SkPaint::kLow_FilterLevel) { 1.143 + return false; 1.144 + } 1.145 + 1.146 + // Check to see if the transformation matrix is simple, and if we're 1.147 + // doing high quality scaling. If so, do the bitmap scale here and 1.148 + // remove the scaling component from the matrix. 1.149 + 1.150 + if (SkPaint::kHigh_FilterLevel == fFilterLevel && 1.151 + fInvMatrix.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask) && 1.152 + fOrigBitmap.config() == SkBitmap::kARGB_8888_Config) { 1.153 + 1.154 + SkScalar invScaleX = fInvMatrix.getScaleX(); 1.155 + SkScalar invScaleY = fInvMatrix.getScaleY(); 1.156 + 1.157 + fScaledCacheID = SkScaledImageCache::FindAndLock(fOrigBitmap, 1.158 + invScaleX, invScaleY, 1.159 + &fScaledBitmap); 1.160 + if (fScaledCacheID) { 1.161 + fScaledBitmap.lockPixels(); 1.162 + if (!fScaledBitmap.getPixels()) { 1.163 + fScaledBitmap.unlockPixels(); 1.164 + // found a purged entry (discardablememory?), release it 1.165 + SkScaledImageCache::Unlock(fScaledCacheID); 1.166 + fScaledCacheID = NULL; 1.167 + // fall through to rebuild 1.168 + } 1.169 + } 1.170 + 1.171 + if (NULL == fScaledCacheID) { 1.172 + int dest_width = SkScalarCeilToInt(fOrigBitmap.width() / invScaleX); 1.173 + int dest_height = SkScalarCeilToInt(fOrigBitmap.height() / invScaleY); 1.174 + 1.175 + // All the criteria are met; let's make a new bitmap. 1.176 + 1.177 + SkConvolutionProcs simd; 1.178 + sk_bzero(&simd, sizeof(simd)); 1.179 + this->platformConvolutionProcs(&simd); 1.180 + 1.181 + if (!SkBitmapScaler::Resize(&fScaledBitmap, 1.182 + fOrigBitmap, 1.183 + SkBitmapScaler::RESIZE_BEST, 1.184 + dest_width, 1.185 + dest_height, 1.186 + simd, 1.187 + SkScaledImageCache::GetAllocator())) { 1.188 + // we failed to create fScaledBitmap, so just return and let 1.189 + // the scanline proc handle it. 1.190 + return false; 1.191 + 1.192 + } 1.193 + SkASSERT(NULL != fScaledBitmap.getPixels()); 1.194 + fScaledCacheID = SkScaledImageCache::AddAndLock(fOrigBitmap, 1.195 + invScaleX, 1.196 + invScaleY, 1.197 + fScaledBitmap); 1.198 + if (!fScaledCacheID) { 1.199 + fScaledBitmap.reset(); 1.200 + return false; 1.201 + } 1.202 + SkASSERT(NULL != fScaledBitmap.getPixels()); 1.203 + } 1.204 + 1.205 + SkASSERT(NULL != fScaledBitmap.getPixels()); 1.206 + fBitmap = &fScaledBitmap; 1.207 + 1.208 + // set the inv matrix type to translate-only; 1.209 + fInvMatrix.setTranslate(fInvMatrix.getTranslateX() / fInvMatrix.getScaleX(), 1.210 + fInvMatrix.getTranslateY() / fInvMatrix.getScaleY()); 1.211 + 1.212 + // no need for any further filtering; we just did it! 1.213 + fFilterLevel = SkPaint::kNone_FilterLevel; 1.214 + unlocker.release(); 1.215 + return true; 1.216 + } 1.217 + 1.218 + /* 1.219 + * If High, then our special-case for scale-only did not take, and so we 1.220 + * have to make a choice: 1.221 + * 1. fall back on mipmaps + bilerp 1.222 + * 2. fall back on scanline bicubic filter 1.223 + * For now, we compute the "scale" value from the matrix, and have a 1.224 + * threshold to decide when bicubic is better, and when mips are better. 1.225 + * No doubt a fancier decision tree could be used uere. 1.226 + * 1.227 + * If Medium, then we just try to build a mipmap and select a level, 1.228 + * setting the filter-level to kLow to signal that we just need bilerp 1.229 + * to process the selected level. 1.230 + */ 1.231 + 1.232 + SkScalar scaleSqd = effective_matrix_scale_sqrd(fInvMatrix); 1.233 + 1.234 + if (SkPaint::kHigh_FilterLevel == fFilterLevel) { 1.235 + // Set the limit at 0.25 for the CTM... if the CTM is scaling smaller 1.236 + // than this, then the mipmaps quality may be greater (certainly faster) 1.237 + // so we only keep High quality if the scale is greater than this. 1.238 + // 1.239 + // Since we're dealing with the inverse, we compare against its inverse. 1.240 + const SkScalar bicubicLimit = 4.0f; 1.241 + const SkScalar bicubicLimitSqd = bicubicLimit * bicubicLimit; 1.242 + if (scaleSqd < bicubicLimitSqd) { // use bicubic scanline 1.243 + return false; 1.244 + } 1.245 + 1.246 + // else set the filter-level to Medium, since we're scaling down and 1.247 + // want to reqeust mipmaps 1.248 + fFilterLevel = SkPaint::kMedium_FilterLevel; 1.249 + } 1.250 + 1.251 + SkASSERT(SkPaint::kMedium_FilterLevel == fFilterLevel); 1.252 + 1.253 + /** 1.254 + * Medium quality means use a mipmap for down-scaling, and just bilper 1.255 + * for upscaling. Since we're examining the inverse matrix, we look for 1.256 + * a scale > 1 to indicate down scaling by the CTM. 1.257 + */ 1.258 + if (scaleSqd > SK_Scalar1) { 1.259 + const SkMipMap* mip = NULL; 1.260 + 1.261 + SkASSERT(NULL == fScaledCacheID); 1.262 + fScaledCacheID = SkScaledImageCache::FindAndLockMip(fOrigBitmap, &mip); 1.263 + if (!fScaledCacheID) { 1.264 + SkASSERT(NULL == mip); 1.265 + mip = SkMipMap::Build(fOrigBitmap); 1.266 + if (mip) { 1.267 + fScaledCacheID = SkScaledImageCache::AddAndLockMip(fOrigBitmap, 1.268 + mip); 1.269 + mip->unref(); // the cache took a ref 1.270 + SkASSERT(fScaledCacheID); 1.271 + } 1.272 + } else { 1.273 + SkASSERT(mip); 1.274 + } 1.275 + 1.276 + if (mip) { 1.277 + SkScalar levelScale = SkScalarInvert(SkScalarSqrt(scaleSqd)); 1.278 + SkMipMap::Level level; 1.279 + if (mip->extractLevel(levelScale, &level)) { 1.280 + SkScalar invScaleFixup = level.fScale; 1.281 + fInvMatrix.postScale(invScaleFixup, invScaleFixup); 1.282 + 1.283 + fScaledBitmap.setConfig(fOrigBitmap.config(), 1.284 + level.fWidth, level.fHeight, 1.285 + level.fRowBytes); 1.286 + fScaledBitmap.setPixels(level.fPixels); 1.287 + fBitmap = &fScaledBitmap; 1.288 + fFilterLevel = SkPaint::kLow_FilterLevel; 1.289 + unlocker.release(); 1.290 + return true; 1.291 + } 1.292 + } 1.293 + } 1.294 + 1.295 + return false; 1.296 +} 1.297 + 1.298 +static bool get_locked_pixels(const SkBitmap& src, int pow2, SkBitmap* dst) { 1.299 + SkPixelRef* pr = src.pixelRef(); 1.300 + if (pr && pr->decodeInto(pow2, dst)) { 1.301 + return true; 1.302 + } 1.303 + 1.304 + /* 1.305 + * If decodeInto() fails, it is possibe that we have an old subclass that 1.306 + * does not, or cannot, implement that. In that case we fall back to the 1.307 + * older protocol of having the pixelRef handle the caching for us. 1.308 + */ 1.309 + *dst = src; 1.310 + dst->lockPixels(); 1.311 + return SkToBool(dst->getPixels()); 1.312 +} 1.313 + 1.314 +bool SkBitmapProcState::lockBaseBitmap() { 1.315 + AutoScaledCacheUnlocker unlocker(&fScaledCacheID); 1.316 + 1.317 + SkPixelRef* pr = fOrigBitmap.pixelRef(); 1.318 + 1.319 + SkASSERT(NULL == fScaledCacheID); 1.320 + 1.321 + if (pr->isLocked() || !pr->implementsDecodeInto()) { 1.322 + // fast-case, no need to look in our cache 1.323 + fScaledBitmap = fOrigBitmap; 1.324 + fScaledBitmap.lockPixels(); 1.325 + if (NULL == fScaledBitmap.getPixels()) { 1.326 + return false; 1.327 + } 1.328 + } else { 1.329 + fScaledCacheID = SkScaledImageCache::FindAndLock(fOrigBitmap, 1.330 + SK_Scalar1, SK_Scalar1, 1.331 + &fScaledBitmap); 1.332 + if (fScaledCacheID) { 1.333 + fScaledBitmap.lockPixels(); 1.334 + if (!fScaledBitmap.getPixels()) { 1.335 + fScaledBitmap.unlockPixels(); 1.336 + // found a purged entry (discardablememory?), release it 1.337 + SkScaledImageCache::Unlock(fScaledCacheID); 1.338 + fScaledCacheID = NULL; 1.339 + // fall through to rebuild 1.340 + } 1.341 + } 1.342 + 1.343 + if (NULL == fScaledCacheID) { 1.344 + if (!get_locked_pixels(fOrigBitmap, 0, &fScaledBitmap)) { 1.345 + return false; 1.346 + } 1.347 + 1.348 + // TODO: if fScaled comes back at a different width/height than fOrig, 1.349 + // we need to update the matrix we are using to sample from this guy. 1.350 + 1.351 + fScaledCacheID = SkScaledImageCache::AddAndLock(fOrigBitmap, 1.352 + SK_Scalar1, SK_Scalar1, 1.353 + fScaledBitmap); 1.354 + if (!fScaledCacheID) { 1.355 + fScaledBitmap.reset(); 1.356 + return false; 1.357 + } 1.358 + } 1.359 + } 1.360 + fBitmap = &fScaledBitmap; 1.361 + unlocker.release(); 1.362 + return true; 1.363 +} 1.364 + 1.365 +void SkBitmapProcState::endContext() { 1.366 + SkDELETE(fBitmapFilter); 1.367 + fBitmapFilter = NULL; 1.368 + fScaledBitmap.reset(); 1.369 + 1.370 + if (fScaledCacheID) { 1.371 + SkScaledImageCache::Unlock(fScaledCacheID); 1.372 + fScaledCacheID = NULL; 1.373 + } 1.374 +} 1.375 + 1.376 +SkBitmapProcState::~SkBitmapProcState() { 1.377 + if (fScaledCacheID) { 1.378 + SkScaledImageCache::Unlock(fScaledCacheID); 1.379 + } 1.380 + SkDELETE(fBitmapFilter); 1.381 +} 1.382 + 1.383 +bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) { 1.384 + SkASSERT(fOrigBitmap.width() && fOrigBitmap.height()); 1.385 + 1.386 + fBitmap = NULL; 1.387 + fInvMatrix = inv; 1.388 + fFilterLevel = paint.getFilterLevel(); 1.389 + 1.390 + SkASSERT(NULL == fScaledCacheID); 1.391 + 1.392 + // possiblyScaleImage will look to see if it can rescale the image as a 1.393 + // preprocess; either by scaling up to the target size, or by selecting 1.394 + // a nearby mipmap level. If it does, it will adjust the working 1.395 + // matrix as well as the working bitmap. It may also adjust the filter 1.396 + // quality to avoid re-filtering an already perfectly scaled image. 1.397 + if (!this->possiblyScaleImage()) { 1.398 + if (!this->lockBaseBitmap()) { 1.399 + return false; 1.400 + } 1.401 + } 1.402 + // The above logic should have always assigned fBitmap, but in case it 1.403 + // didn't, we check for that now... 1.404 + if (NULL == fBitmap) { 1.405 + return false; 1.406 + } 1.407 + 1.408 + bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0; 1.409 + bool clampClamp = SkShader::kClamp_TileMode == fTileModeX && 1.410 + SkShader::kClamp_TileMode == fTileModeY; 1.411 + 1.412 + if (!(clampClamp || trivialMatrix)) { 1.413 + fInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height()); 1.414 + } 1.415 + 1.416 + // Now that all possible changes to the matrix have taken place, check 1.417 + // to see if we're really close to a no-scale matrix. If so, explicitly 1.418 + // set it to be so. Subsequent code may inspect this matrix to choose 1.419 + // a faster path in this case. 1.420 + 1.421 + // This code will only execute if the matrix has some scale component; 1.422 + // if it's already pure translate then we won't do this inversion. 1.423 + 1.424 + if (matrix_only_scale_translate(fInvMatrix)) { 1.425 + SkMatrix forward; 1.426 + if (fInvMatrix.invert(&forward)) { 1.427 + if (clampClamp ? just_trans_clamp(forward, *fBitmap) 1.428 + : just_trans_general(forward)) { 1.429 + SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX()); 1.430 + SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY()); 1.431 + fInvMatrix.setTranslate(tx, ty); 1.432 + } 1.433 + } 1.434 + } 1.435 + 1.436 + fInvProc = fInvMatrix.getMapXYProc(); 1.437 + fInvType = fInvMatrix.getType(); 1.438 + fInvSx = SkScalarToFixed(fInvMatrix.getScaleX()); 1.439 + fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX()); 1.440 + fInvKy = SkScalarToFixed(fInvMatrix.getSkewY()); 1.441 + fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY()); 1.442 + 1.443 + fAlphaScale = SkAlpha255To256(paint.getAlpha()); 1.444 + 1.445 + fShaderProc32 = NULL; 1.446 + fShaderProc16 = NULL; 1.447 + fSampleProc32 = NULL; 1.448 + fSampleProc16 = NULL; 1.449 + 1.450 + // recompute the triviality of the matrix here because we may have 1.451 + // changed it! 1.452 + 1.453 + trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0; 1.454 + 1.455 + if (SkPaint::kHigh_FilterLevel == fFilterLevel) { 1.456 + // If this is still set, that means we wanted HQ sampling 1.457 + // but couldn't do it as a preprocess. Let's try to install 1.458 + // the scanline version of the HQ sampler. If that process fails, 1.459 + // downgrade to bilerp. 1.460 + 1.461 + // NOTE: Might need to be careful here in the future when we want 1.462 + // to have the platform proc have a shot at this; it's possible that 1.463 + // the chooseBitmapFilterProc will fail to install a shader but a 1.464 + // platform-specific one might succeed, so it might be premature here 1.465 + // to fall back to bilerp. This needs thought. 1.466 + 1.467 + if (!this->setBitmapFilterProcs()) { 1.468 + fFilterLevel = SkPaint::kLow_FilterLevel; 1.469 + } 1.470 + } 1.471 + 1.472 + if (SkPaint::kLow_FilterLevel == fFilterLevel) { 1.473 + // Only try bilerp if the matrix is "interesting" and 1.474 + // the image has a suitable size. 1.475 + 1.476 + if (fInvType <= SkMatrix::kTranslate_Mask || 1.477 + !valid_for_filtering(fBitmap->width() | fBitmap->height())) { 1.478 + fFilterLevel = SkPaint::kNone_FilterLevel; 1.479 + } 1.480 + } 1.481 + 1.482 + // At this point, we know exactly what kind of sampling the per-scanline 1.483 + // shader will perform. 1.484 + 1.485 + fMatrixProc = this->chooseMatrixProc(trivialMatrix); 1.486 + if (NULL == fMatrixProc) { 1.487 + return false; 1.488 + } 1.489 + 1.490 + /////////////////////////////////////////////////////////////////////// 1.491 + 1.492 + // No need to do this if we're doing HQ sampling; if filter quality is 1.493 + // still set to HQ by the time we get here, then we must have installed 1.494 + // the shader procs above and can skip all this. 1.495 + 1.496 + if (fFilterLevel < SkPaint::kHigh_FilterLevel) { 1.497 + 1.498 + int index = 0; 1.499 + if (fAlphaScale < 256) { // note: this distinction is not used for D16 1.500 + index |= 1; 1.501 + } 1.502 + if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) { 1.503 + index |= 2; 1.504 + } 1.505 + if (fFilterLevel > SkPaint::kNone_FilterLevel) { 1.506 + index |= 4; 1.507 + } 1.508 + // bits 3,4,5 encoding the source bitmap format 1.509 + switch (fBitmap->config()) { 1.510 + case SkBitmap::kARGB_8888_Config: 1.511 + index |= 0; 1.512 + break; 1.513 + case SkBitmap::kRGB_565_Config: 1.514 + index |= 8; 1.515 + break; 1.516 + case SkBitmap::kIndex8_Config: 1.517 + index |= 16; 1.518 + break; 1.519 + case SkBitmap::kARGB_4444_Config: 1.520 + index |= 24; 1.521 + break; 1.522 + case SkBitmap::kA8_Config: 1.523 + index |= 32; 1.524 + fPaintPMColor = SkPreMultiplyColor(paint.getColor()); 1.525 + break; 1.526 + default: 1.527 + return false; 1.528 + } 1.529 + 1.530 + #if !SK_ARM_NEON_IS_ALWAYS 1.531 + static const SampleProc32 gSkBitmapProcStateSample32[] = { 1.532 + S32_opaque_D32_nofilter_DXDY, 1.533 + S32_alpha_D32_nofilter_DXDY, 1.534 + S32_opaque_D32_nofilter_DX, 1.535 + S32_alpha_D32_nofilter_DX, 1.536 + S32_opaque_D32_filter_DXDY, 1.537 + S32_alpha_D32_filter_DXDY, 1.538 + S32_opaque_D32_filter_DX, 1.539 + S32_alpha_D32_filter_DX, 1.540 + 1.541 + S16_opaque_D32_nofilter_DXDY, 1.542 + S16_alpha_D32_nofilter_DXDY, 1.543 + S16_opaque_D32_nofilter_DX, 1.544 + S16_alpha_D32_nofilter_DX, 1.545 + S16_opaque_D32_filter_DXDY, 1.546 + S16_alpha_D32_filter_DXDY, 1.547 + S16_opaque_D32_filter_DX, 1.548 + S16_alpha_D32_filter_DX, 1.549 + 1.550 + SI8_opaque_D32_nofilter_DXDY, 1.551 + SI8_alpha_D32_nofilter_DXDY, 1.552 + SI8_opaque_D32_nofilter_DX, 1.553 + SI8_alpha_D32_nofilter_DX, 1.554 + SI8_opaque_D32_filter_DXDY, 1.555 + SI8_alpha_D32_filter_DXDY, 1.556 + SI8_opaque_D32_filter_DX, 1.557 + SI8_alpha_D32_filter_DX, 1.558 + 1.559 + S4444_opaque_D32_nofilter_DXDY, 1.560 + S4444_alpha_D32_nofilter_DXDY, 1.561 + S4444_opaque_D32_nofilter_DX, 1.562 + S4444_alpha_D32_nofilter_DX, 1.563 + S4444_opaque_D32_filter_DXDY, 1.564 + S4444_alpha_D32_filter_DXDY, 1.565 + S4444_opaque_D32_filter_DX, 1.566 + S4444_alpha_D32_filter_DX, 1.567 + 1.568 + // A8 treats alpha/opaque the same (equally efficient) 1.569 + SA8_alpha_D32_nofilter_DXDY, 1.570 + SA8_alpha_D32_nofilter_DXDY, 1.571 + SA8_alpha_D32_nofilter_DX, 1.572 + SA8_alpha_D32_nofilter_DX, 1.573 + SA8_alpha_D32_filter_DXDY, 1.574 + SA8_alpha_D32_filter_DXDY, 1.575 + SA8_alpha_D32_filter_DX, 1.576 + SA8_alpha_D32_filter_DX 1.577 + }; 1.578 + 1.579 + static const SampleProc16 gSkBitmapProcStateSample16[] = { 1.580 + S32_D16_nofilter_DXDY, 1.581 + S32_D16_nofilter_DX, 1.582 + S32_D16_filter_DXDY, 1.583 + S32_D16_filter_DX, 1.584 + 1.585 + S16_D16_nofilter_DXDY, 1.586 + S16_D16_nofilter_DX, 1.587 + S16_D16_filter_DXDY, 1.588 + S16_D16_filter_DX, 1.589 + 1.590 + SI8_D16_nofilter_DXDY, 1.591 + SI8_D16_nofilter_DX, 1.592 + SI8_D16_filter_DXDY, 1.593 + SI8_D16_filter_DX, 1.594 + 1.595 + // Don't support 4444 -> 565 1.596 + NULL, NULL, NULL, NULL, 1.597 + // Don't support A8 -> 565 1.598 + NULL, NULL, NULL, NULL 1.599 + }; 1.600 + #endif 1.601 + 1.602 + fSampleProc32 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index]; 1.603 + index >>= 1; // shift away any opaque/alpha distinction 1.604 + fSampleProc16 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample16)[index]; 1.605 + 1.606 + // our special-case shaderprocs 1.607 + if (SK_ARM_NEON_WRAP(S16_D16_filter_DX) == fSampleProc16) { 1.608 + if (clampClamp) { 1.609 + fShaderProc16 = SK_ARM_NEON_WRAP(Clamp_S16_D16_filter_DX_shaderproc); 1.610 + } else if (SkShader::kRepeat_TileMode == fTileModeX && 1.611 + SkShader::kRepeat_TileMode == fTileModeY) { 1.612 + fShaderProc16 = SK_ARM_NEON_WRAP(Repeat_S16_D16_filter_DX_shaderproc); 1.613 + } 1.614 + } else if (SK_ARM_NEON_WRAP(SI8_opaque_D32_filter_DX) == fSampleProc32 && clampClamp) { 1.615 + fShaderProc32 = SK_ARM_NEON_WRAP(Clamp_SI8_opaque_D32_filter_DX_shaderproc); 1.616 + } 1.617 + 1.618 + if (NULL == fShaderProc32) { 1.619 + fShaderProc32 = this->chooseShaderProc32(); 1.620 + } 1.621 + } 1.622 + 1.623 + // see if our platform has any accelerated overrides 1.624 + this->platformProcs(); 1.625 + 1.626 + return true; 1.627 +} 1.628 + 1.629 +static void Clamp_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s, 1.630 + int x, int y, 1.631 + SkPMColor* SK_RESTRICT colors, 1.632 + int count) { 1.633 + SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0); 1.634 + SkASSERT(s.fInvKy == 0); 1.635 + SkASSERT(count > 0 && colors != NULL); 1.636 + SkASSERT(SkPaint::kNone_FilterLevel == s.fFilterLevel); 1.637 + 1.638 + const int maxX = s.fBitmap->width() - 1; 1.639 + const int maxY = s.fBitmap->height() - 1; 1.640 + int ix = s.fFilterOneX + x; 1.641 + int iy = SkClampMax(s.fFilterOneY + y, maxY); 1.642 +#ifdef SK_DEBUG 1.643 + { 1.644 + SkPoint pt; 1.645 + s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf, 1.646 + SkIntToScalar(y) + SK_ScalarHalf, &pt); 1.647 + int iy2 = SkClampMax(SkScalarFloorToInt(pt.fY), maxY); 1.648 + int ix2 = SkScalarFloorToInt(pt.fX); 1.649 + 1.650 + SkASSERT(iy == iy2); 1.651 + SkASSERT(ix == ix2); 1.652 + } 1.653 +#endif 1.654 + const SkPMColor* row = s.fBitmap->getAddr32(0, iy); 1.655 + 1.656 + // clamp to the left 1.657 + if (ix < 0) { 1.658 + int n = SkMin32(-ix, count); 1.659 + sk_memset32(colors, row[0], n); 1.660 + count -= n; 1.661 + if (0 == count) { 1.662 + return; 1.663 + } 1.664 + colors += n; 1.665 + SkASSERT(-ix == n); 1.666 + ix = 0; 1.667 + } 1.668 + // copy the middle 1.669 + if (ix <= maxX) { 1.670 + int n = SkMin32(maxX - ix + 1, count); 1.671 + memcpy(colors, row + ix, n * sizeof(SkPMColor)); 1.672 + count -= n; 1.673 + if (0 == count) { 1.674 + return; 1.675 + } 1.676 + colors += n; 1.677 + } 1.678 + SkASSERT(count > 0); 1.679 + // clamp to the right 1.680 + sk_memset32(colors, row[maxX], count); 1.681 +} 1.682 + 1.683 +static inline int sk_int_mod(int x, int n) { 1.684 + SkASSERT(n > 0); 1.685 + if ((unsigned)x >= (unsigned)n) { 1.686 + if (x < 0) { 1.687 + x = n + ~(~x % n); 1.688 + } else { 1.689 + x = x % n; 1.690 + } 1.691 + } 1.692 + return x; 1.693 +} 1.694 + 1.695 +static inline int sk_int_mirror(int x, int n) { 1.696 + x = sk_int_mod(x, 2 * n); 1.697 + if (x >= n) { 1.698 + x = n + ~(x - n); 1.699 + } 1.700 + return x; 1.701 +} 1.702 + 1.703 +static void Repeat_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s, 1.704 + int x, int y, 1.705 + SkPMColor* SK_RESTRICT colors, 1.706 + int count) { 1.707 + SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0); 1.708 + SkASSERT(s.fInvKy == 0); 1.709 + SkASSERT(count > 0 && colors != NULL); 1.710 + SkASSERT(SkPaint::kNone_FilterLevel == s.fFilterLevel); 1.711 + 1.712 + const int stopX = s.fBitmap->width(); 1.713 + const int stopY = s.fBitmap->height(); 1.714 + int ix = s.fFilterOneX + x; 1.715 + int iy = sk_int_mod(s.fFilterOneY + y, stopY); 1.716 +#ifdef SK_DEBUG 1.717 + { 1.718 + SkPoint pt; 1.719 + s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf, 1.720 + SkIntToScalar(y) + SK_ScalarHalf, &pt); 1.721 + int iy2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY); 1.722 + int ix2 = SkScalarFloorToInt(pt.fX); 1.723 + 1.724 + SkASSERT(iy == iy2); 1.725 + SkASSERT(ix == ix2); 1.726 + } 1.727 +#endif 1.728 + const SkPMColor* row = s.fBitmap->getAddr32(0, iy); 1.729 + 1.730 + ix = sk_int_mod(ix, stopX); 1.731 + for (;;) { 1.732 + int n = SkMin32(stopX - ix, count); 1.733 + memcpy(colors, row + ix, n * sizeof(SkPMColor)); 1.734 + count -= n; 1.735 + if (0 == count) { 1.736 + return; 1.737 + } 1.738 + colors += n; 1.739 + ix = 0; 1.740 + } 1.741 +} 1.742 + 1.743 +static void S32_D32_constX_shaderproc(const SkBitmapProcState& s, 1.744 + int x, int y, 1.745 + SkPMColor* SK_RESTRICT colors, 1.746 + int count) { 1.747 + SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) == 0); 1.748 + SkASSERT(s.fInvKy == 0); 1.749 + SkASSERT(count > 0 && colors != NULL); 1.750 + SkASSERT(1 == s.fBitmap->width()); 1.751 + 1.752 + int iY0; 1.753 + int iY1 SK_INIT_TO_AVOID_WARNING; 1.754 + int iSubY SK_INIT_TO_AVOID_WARNING; 1.755 + 1.756 + if (SkPaint::kNone_FilterLevel != s.fFilterLevel) { 1.757 + SkBitmapProcState::MatrixProc mproc = s.getMatrixProc(); 1.758 + uint32_t xy[2]; 1.759 + 1.760 + mproc(s, xy, 1, x, y); 1.761 + 1.762 + iY0 = xy[0] >> 18; 1.763 + iY1 = xy[0] & 0x3FFF; 1.764 + iSubY = (xy[0] >> 14) & 0xF; 1.765 + } else { 1.766 + int yTemp; 1.767 + 1.768 + if (s.fInvType > SkMatrix::kTranslate_Mask) { 1.769 + SkPoint pt; 1.770 + s.fInvProc(s.fInvMatrix, 1.771 + SkIntToScalar(x) + SK_ScalarHalf, 1.772 + SkIntToScalar(y) + SK_ScalarHalf, 1.773 + &pt); 1.774 + // When the matrix has a scale component the setup code in 1.775 + // chooseProcs multiples the inverse matrix by the inverse of the 1.776 + // bitmap's width and height. Since this method is going to do 1.777 + // its own tiling and sampling we need to undo that here. 1.778 + if (SkShader::kClamp_TileMode != s.fTileModeX || 1.779 + SkShader::kClamp_TileMode != s.fTileModeY) { 1.780 + yTemp = SkScalarFloorToInt(pt.fY * s.fBitmap->height()); 1.781 + } else { 1.782 + yTemp = SkScalarFloorToInt(pt.fY); 1.783 + } 1.784 + } else { 1.785 + yTemp = s.fFilterOneY + y; 1.786 + } 1.787 + 1.788 + const int stopY = s.fBitmap->height(); 1.789 + switch (s.fTileModeY) { 1.790 + case SkShader::kClamp_TileMode: 1.791 + iY0 = SkClampMax(yTemp, stopY-1); 1.792 + break; 1.793 + case SkShader::kRepeat_TileMode: 1.794 + iY0 = sk_int_mod(yTemp, stopY); 1.795 + break; 1.796 + case SkShader::kMirror_TileMode: 1.797 + default: 1.798 + iY0 = sk_int_mirror(yTemp, stopY); 1.799 + break; 1.800 + } 1.801 + 1.802 +#ifdef SK_DEBUG 1.803 + { 1.804 + SkPoint pt; 1.805 + s.fInvProc(s.fInvMatrix, 1.806 + SkIntToScalar(x) + SK_ScalarHalf, 1.807 + SkIntToScalar(y) + SK_ScalarHalf, 1.808 + &pt); 1.809 + if (s.fInvType > SkMatrix::kTranslate_Mask && 1.810 + (SkShader::kClamp_TileMode != s.fTileModeX || 1.811 + SkShader::kClamp_TileMode != s.fTileModeY)) { 1.812 + pt.fY *= s.fBitmap->height(); 1.813 + } 1.814 + int iY2; 1.815 + 1.816 + switch (s.fTileModeY) { 1.817 + case SkShader::kClamp_TileMode: 1.818 + iY2 = SkClampMax(SkScalarFloorToInt(pt.fY), stopY-1); 1.819 + break; 1.820 + case SkShader::kRepeat_TileMode: 1.821 + iY2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY); 1.822 + break; 1.823 + case SkShader::kMirror_TileMode: 1.824 + default: 1.825 + iY2 = sk_int_mirror(SkScalarFloorToInt(pt.fY), stopY); 1.826 + break; 1.827 + } 1.828 + 1.829 + SkASSERT(iY0 == iY2); 1.830 + } 1.831 +#endif 1.832 + } 1.833 + 1.834 + const SkPMColor* row0 = s.fBitmap->getAddr32(0, iY0); 1.835 + SkPMColor color; 1.836 + 1.837 + if (SkPaint::kNone_FilterLevel != s.fFilterLevel) { 1.838 + const SkPMColor* row1 = s.fBitmap->getAddr32(0, iY1); 1.839 + 1.840 + if (s.fAlphaScale < 256) { 1.841 + Filter_32_alpha(iSubY, *row0, *row1, &color, s.fAlphaScale); 1.842 + } else { 1.843 + Filter_32_opaque(iSubY, *row0, *row1, &color); 1.844 + } 1.845 + } else { 1.846 + if (s.fAlphaScale < 256) { 1.847 + color = SkAlphaMulQ(*row0, s.fAlphaScale); 1.848 + } else { 1.849 + color = *row0; 1.850 + } 1.851 + } 1.852 + 1.853 + sk_memset32(colors, color, count); 1.854 +} 1.855 + 1.856 +static void DoNothing_shaderproc(const SkBitmapProcState&, int x, int y, 1.857 + SkPMColor* SK_RESTRICT colors, int count) { 1.858 + // if we get called, the matrix is too tricky, so we just draw nothing 1.859 + sk_memset32(colors, 0, count); 1.860 +} 1.861 + 1.862 +bool SkBitmapProcState::setupForTranslate() { 1.863 + SkPoint pt; 1.864 + fInvProc(fInvMatrix, SK_ScalarHalf, SK_ScalarHalf, &pt); 1.865 + 1.866 + /* 1.867 + * if the translate is larger than our ints, we can get random results, or 1.868 + * worse, we might get 0x80000000, which wreaks havoc on us, since we can't 1.869 + * negate it. 1.870 + */ 1.871 + const SkScalar too_big = SkIntToScalar(1 << 30); 1.872 + if (SkScalarAbs(pt.fX) > too_big || SkScalarAbs(pt.fY) > too_big) { 1.873 + return false; 1.874 + } 1.875 + 1.876 + // Since we know we're not filtered, we re-purpose these fields allow 1.877 + // us to go from device -> src coordinates w/ just an integer add, 1.878 + // rather than running through the inverse-matrix 1.879 + fFilterOneX = SkScalarFloorToInt(pt.fX); 1.880 + fFilterOneY = SkScalarFloorToInt(pt.fY); 1.881 + return true; 1.882 +} 1.883 + 1.884 +SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() { 1.885 + 1.886 + if (SkBitmap::kARGB_8888_Config != fBitmap->config()) { 1.887 + return NULL; 1.888 + } 1.889 + 1.890 + static const unsigned kMask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask; 1.891 + 1.892 + if (1 == fBitmap->width() && 0 == (fInvType & ~kMask)) { 1.893 + if (SkPaint::kNone_FilterLevel == fFilterLevel && 1.894 + fInvType <= SkMatrix::kTranslate_Mask && 1.895 + !this->setupForTranslate()) { 1.896 + return DoNothing_shaderproc; 1.897 + } 1.898 + return S32_D32_constX_shaderproc; 1.899 + } 1.900 + 1.901 + if (fAlphaScale < 256) { 1.902 + return NULL; 1.903 + } 1.904 + if (fInvType > SkMatrix::kTranslate_Mask) { 1.905 + return NULL; 1.906 + } 1.907 + if (SkPaint::kNone_FilterLevel != fFilterLevel) { 1.908 + return NULL; 1.909 + } 1.910 + 1.911 + SkShader::TileMode tx = (SkShader::TileMode)fTileModeX; 1.912 + SkShader::TileMode ty = (SkShader::TileMode)fTileModeY; 1.913 + 1.914 + if (SkShader::kClamp_TileMode == tx && SkShader::kClamp_TileMode == ty) { 1.915 + if (this->setupForTranslate()) { 1.916 + return Clamp_S32_D32_nofilter_trans_shaderproc; 1.917 + } 1.918 + return DoNothing_shaderproc; 1.919 + } 1.920 + if (SkShader::kRepeat_TileMode == tx && SkShader::kRepeat_TileMode == ty) { 1.921 + if (this->setupForTranslate()) { 1.922 + return Repeat_S32_D32_nofilter_trans_shaderproc; 1.923 + } 1.924 + return DoNothing_shaderproc; 1.925 + } 1.926 + return NULL; 1.927 +} 1.928 + 1.929 +/////////////////////////////////////////////////////////////////////////////// 1.930 + 1.931 +#ifdef SK_DEBUG 1.932 + 1.933 +static void check_scale_nofilter(uint32_t bitmapXY[], int count, 1.934 + unsigned mx, unsigned my) { 1.935 + unsigned y = *bitmapXY++; 1.936 + SkASSERT(y < my); 1.937 + 1.938 + const uint16_t* xptr = reinterpret_cast<const uint16_t*>(bitmapXY); 1.939 + for (int i = 0; i < count; ++i) { 1.940 + SkASSERT(xptr[i] < mx); 1.941 + } 1.942 +} 1.943 + 1.944 +static void check_scale_filter(uint32_t bitmapXY[], int count, 1.945 + unsigned mx, unsigned my) { 1.946 + uint32_t YY = *bitmapXY++; 1.947 + unsigned y0 = YY >> 18; 1.948 + unsigned y1 = YY & 0x3FFF; 1.949 + SkASSERT(y0 < my); 1.950 + SkASSERT(y1 < my); 1.951 + 1.952 + for (int i = 0; i < count; ++i) { 1.953 + uint32_t XX = bitmapXY[i]; 1.954 + unsigned x0 = XX >> 18; 1.955 + unsigned x1 = XX & 0x3FFF; 1.956 + SkASSERT(x0 < mx); 1.957 + SkASSERT(x1 < mx); 1.958 + } 1.959 +} 1.960 + 1.961 +static void check_affine_nofilter(uint32_t bitmapXY[], int count, 1.962 + unsigned mx, unsigned my) { 1.963 + for (int i = 0; i < count; ++i) { 1.964 + uint32_t XY = bitmapXY[i]; 1.965 + unsigned x = XY & 0xFFFF; 1.966 + unsigned y = XY >> 16; 1.967 + SkASSERT(x < mx); 1.968 + SkASSERT(y < my); 1.969 + } 1.970 +} 1.971 + 1.972 +static void check_affine_filter(uint32_t bitmapXY[], int count, 1.973 + unsigned mx, unsigned my) { 1.974 + for (int i = 0; i < count; ++i) { 1.975 + uint32_t YY = *bitmapXY++; 1.976 + unsigned y0 = YY >> 18; 1.977 + unsigned y1 = YY & 0x3FFF; 1.978 + SkASSERT(y0 < my); 1.979 + SkASSERT(y1 < my); 1.980 + 1.981 + uint32_t XX = *bitmapXY++; 1.982 + unsigned x0 = XX >> 18; 1.983 + unsigned x1 = XX & 0x3FFF; 1.984 + SkASSERT(x0 < mx); 1.985 + SkASSERT(x1 < mx); 1.986 + } 1.987 +} 1.988 + 1.989 +void SkBitmapProcState::DebugMatrixProc(const SkBitmapProcState& state, 1.990 + uint32_t bitmapXY[], int count, 1.991 + int x, int y) { 1.992 + SkASSERT(bitmapXY); 1.993 + SkASSERT(count > 0); 1.994 + 1.995 + state.fMatrixProc(state, bitmapXY, count, x, y); 1.996 + 1.997 + void (*proc)(uint32_t bitmapXY[], int count, unsigned mx, unsigned my); 1.998 + 1.999 + // There are four formats possible: 1.1000 + // scale -vs- affine 1.1001 + // filter -vs- nofilter 1.1002 + if (state.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) { 1.1003 + proc = state.fFilterLevel != SkPaint::kNone_FilterLevel ? check_scale_filter : check_scale_nofilter; 1.1004 + } else { 1.1005 + proc = state.fFilterLevel != SkPaint::kNone_FilterLevel ? check_affine_filter : check_affine_nofilter; 1.1006 + } 1.1007 + proc(bitmapXY, count, state.fBitmap->width(), state.fBitmap->height()); 1.1008 +} 1.1009 + 1.1010 +SkBitmapProcState::MatrixProc SkBitmapProcState::getMatrixProc() const { 1.1011 + return DebugMatrixProc; 1.1012 +} 1.1013 + 1.1014 +#endif 1.1015 + 1.1016 +/////////////////////////////////////////////////////////////////////////////// 1.1017 +/* 1.1018 + The storage requirements for the different matrix procs are as follows, 1.1019 + where each X or Y is 2 bytes, and N is the number of pixels/elements: 1.1020 + 1.1021 + scale/translate nofilter Y(4bytes) + N * X 1.1022 + affine/perspective nofilter N * (X Y) 1.1023 + scale/translate filter Y Y + N * (X X) 1.1024 + affine/perspective filter N * (Y Y X X) 1.1025 + */ 1.1026 +int SkBitmapProcState::maxCountForBufferSize(size_t bufferSize) const { 1.1027 + int32_t size = static_cast<int32_t>(bufferSize); 1.1028 + 1.1029 + size &= ~3; // only care about 4-byte aligned chunks 1.1030 + if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) { 1.1031 + size -= 4; // the shared Y (or YY) coordinate 1.1032 + if (size < 0) { 1.1033 + size = 0; 1.1034 + } 1.1035 + size >>= 1; 1.1036 + } else { 1.1037 + size >>= 2; 1.1038 + } 1.1039 + 1.1040 + if (fFilterLevel != SkPaint::kNone_FilterLevel) { 1.1041 + size >>= 1; 1.1042 + } 1.1043 + 1.1044 + return size; 1.1045 +}