2 * Copyright 2011 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "SkBitmapCache.h"
9 #include "SkBitmapProcState.h"
10 #include "SkColorPriv.h"
11 #include "SkFilterProc.h"
13 #include "SkShader.h" // for tilemodes
14 #include "SkUtilsArm.h"
15 #include "SkBitmapScaler.h"
17 #include "SkPixelRef.h"
18 #include "SkImageEncoder.h"
19 #include "SkResourceCache.h"
21 #if !SK_ARM_NEON_IS_NONE
22 // These are defined in src/opts/SkBitmapProcState_arm_neon.cpp
23 extern const SkBitmapProcState::SampleProc16 gSkBitmapProcStateSample16_neon[];
24 extern const SkBitmapProcState::SampleProc32 gSkBitmapProcStateSample32_neon[];
25 extern void S16_D16_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, uint16_t*);
26 extern void Clamp_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint16_t*, int);
27 extern void Repeat_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint16_t*, int);
28 extern void SI8_opaque_D32_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, SkPMColor*);
29 extern void SI8_opaque_D32_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint32_t*, int);
30 extern void Clamp_SI8_opaque_D32_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint32_t*, int);
33 #define NAME_WRAP(x) x
34 #include "SkBitmapProcState_filter.h"
35 #include "SkBitmapProcState_procs.h"
37 ///////////////////////////////////////////////////////////////////////////////
39 // true iff the matrix contains, at most, scale and translate elements
40 static bool matrix_only_scale_translate(const SkMatrix& m) {
41 return m.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask);
45 * For the purposes of drawing bitmaps, if a matrix is "almost" translate
46 * go ahead and treat it as if it were, so that subsequent code can go fast.
48 static bool just_trans_clamp(const SkMatrix& matrix, const SkBitmap& bitmap) {
49 SkASSERT(matrix_only_scale_translate(matrix));
51 if (matrix.getType() & SkMatrix::kScale_Mask) {
53 bitmap.getBounds(&src);
55 // Can't call mapRect(), since that will fix up inverted rectangles,
56 // e.g. when scale is negative, and we don't want to return true for
58 matrix.mapPoints(SkTCast<SkPoint*>(&dst),
59 SkTCast<const SkPoint*>(&src),
62 // Now round all 4 edges to device space, and then compare the device
63 // width/height to the original. Note: we must map all 4 and subtract
64 // rather than map the "width" and compare, since we care about the
65 // phase (in pixel space) that any translate in the matrix might impart.
68 return idst.width() == bitmap.width() && idst.height() == bitmap.height();
70 // if we got here, we're either kTranslate_Mask or identity
74 static bool just_trans_general(const SkMatrix& matrix) {
75 SkASSERT(matrix_only_scale_translate(matrix));
77 if (matrix.getType() & SkMatrix::kScale_Mask) {
78 const SkScalar tol = SK_Scalar1 / 32768;
80 if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleX] - SK_Scalar1, tol)) {
83 if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleY] - SK_Scalar1, tol)) {
87 // if we got here, treat us as either kTranslate_Mask or identity
91 ///////////////////////////////////////////////////////////////////////////////
93 static bool valid_for_filtering(unsigned dimension) {
94 // for filtering, width and height must fit in 14bits, since we use steal
95 // 2 bits from each to store our 4bit subpixel data
96 return (dimension & ~0x3FFF) == 0;
99 static SkScalar effective_matrix_scale_sqrd(const SkMatrix& mat) {
102 v1.fX = mat.getScaleX();
103 v1.fY = mat.getSkewY();
105 v2.fX = mat.getSkewX();
106 v2.fY = mat.getScaleY();
108 return SkMaxScalar(v1.lengthSqd(), v2.lengthSqd());
111 // Check to see that the size of the bitmap that would be produced by
112 // scaling by the given inverted matrix is less than the maximum allowed.
113 static inline bool cache_size_okay(const SkBitmap& bm, const SkMatrix& invMat) {
114 size_t maximumAllocation = SkResourceCache::GetSingleAllocationByteLimit();
115 if (0 == maximumAllocation) {
118 // float matrixScaleFactor = 1.0 / (invMat.scaleX * invMat.scaleY);
119 // return ((origBitmapSize * matrixScaleFactor) < maximumAllocationSize);
120 // Skip the division step:
121 return bm.info().getSafeSize(bm.info().minRowBytes())
122 < (maximumAllocation * invMat.getScaleX() * invMat.getScaleY());
125 // TODO -- we may want to pass the clip into this function so we only scale
126 // the portion of the image that we're going to need. This will complicate
127 // the interface to the cache, but might be well worth it.
129 bool SkBitmapProcState::possiblyScaleImage() {
130 SkASSERT(NULL == fBitmap);
132 fAdjustedMatrix = false;
134 if (fFilterLevel <= SkPaint::kLow_FilterLevel) {
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.
141 SkScalar invScaleX = fInvMatrix.getScaleX();
142 SkScalar invScaleY = fInvMatrix.getScaleY();
144 float trueDestWidth = fOrigBitmap.width() / invScaleX;
145 float trueDestHeight = fOrigBitmap.height() / invScaleY;
147 #ifndef SK_IGNORE_PROPER_FRACTIONAL_SCALING
148 float roundedDestWidth = SkScalarRoundToScalar(trueDestWidth);
149 float roundedDestHeight = SkScalarRoundToScalar(trueDestHeight);
151 float roundedDestWidth = trueDestWidth;
152 float roundedDestHeight = trueDestHeight;
155 if (SkPaint::kHigh_FilterLevel == fFilterLevel &&
156 fInvMatrix.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask) &&
157 kN32_SkColorType == fOrigBitmap.colorType() &&
158 cache_size_okay(fOrigBitmap, fInvMatrix)) {
160 if (SkScalarNearlyEqual(invScaleX,1.0f) &&
161 SkScalarNearlyEqual(invScaleY,1.0f)) {
162 // short-circuit identity scaling; the output is supposed to
163 // be the same as the input, so we might as well go fast.
165 // Note(humper): We could also probably do this if the scales
166 // are close to -1 as well, since the flip doesn't require
167 // any fancy re-sampling...
169 // Set our filter level to low -- the only post-filtering this
170 // image might require is some interpolation if the translation
172 fFilterLevel = SkPaint::kLow_FilterLevel;
176 if (!SkBitmapCache::Find(fOrigBitmap, roundedDestWidth, roundedDestHeight, &fScaledBitmap)) {
177 // All the criteria are met; let's make a new bitmap.
179 if (!SkBitmapScaler::Resize(&fScaledBitmap,
181 SkBitmapScaler::RESIZE_BEST,
184 SkResourceCache::GetAllocator())) {
185 // we failed to create fScaledBitmap, so just return and let
186 // the scanline proc handle it.
191 SkASSERT(fScaledBitmap.getPixels());
192 fScaledBitmap.setImmutable();
193 SkBitmapCache::Add(fOrigBitmap, roundedDestWidth, roundedDestHeight, fScaledBitmap);
196 SkASSERT(fScaledBitmap.getPixels());
197 fBitmap = &fScaledBitmap;
199 // set the inv matrix type to translate-only;
200 fInvMatrix.setTranslate(fInvMatrix.getTranslateX() / fInvMatrix.getScaleX(),
201 fInvMatrix.getTranslateY() / fInvMatrix.getScaleY());
203 #ifndef SK_IGNORE_PROPER_FRACTIONAL_SCALING
204 // reintroduce any fractional scaling missed by our integral scale done above.
206 float fractionalScaleX = roundedDestWidth/trueDestWidth;
207 float fractionalScaleY = roundedDestHeight/trueDestHeight;
209 fInvMatrix.postScale(fractionalScaleX, fractionalScaleY);
211 fAdjustedMatrix = true;
213 // Set our filter level to low -- the only post-filtering this
214 // image might require is some interpolation if the translation
215 // is fractional or if there's any remaining scaling to be done.
216 fFilterLevel = SkPaint::kLow_FilterLevel;
221 * If High, then our special-case for scale-only did not take, and so we
222 * have to make a choice:
223 * 1. fall back on mipmaps + bilerp
224 * 2. fall back on scanline bicubic filter
225 * For now, we compute the "scale" value from the matrix, and have a
226 * threshold to decide when bicubic is better, and when mips are better.
227 * No doubt a fancier decision tree could be used uere.
229 * If Medium, then we just try to build a mipmap and select a level,
230 * setting the filter-level to kLow to signal that we just need bilerp
231 * to process the selected level.
234 SkScalar scaleSqd = effective_matrix_scale_sqrd(fInvMatrix);
236 if (SkPaint::kHigh_FilterLevel == fFilterLevel) {
237 // Set the limit at 0.25 for the CTM... if the CTM is scaling smaller
238 // than this, then the mipmaps quality may be greater (certainly faster)
239 // so we only keep High quality if the scale is greater than this.
241 // Since we're dealing with the inverse, we compare against its inverse.
242 const SkScalar bicubicLimit = 4.0f;
243 const SkScalar bicubicLimitSqd = bicubicLimit * bicubicLimit;
244 if (scaleSqd < bicubicLimitSqd) { // use bicubic scanline
248 // else set the filter-level to Medium, since we're scaling down and
249 // want to reqeust mipmaps
250 fFilterLevel = SkPaint::kMedium_FilterLevel;
253 SkASSERT(SkPaint::kMedium_FilterLevel == fFilterLevel);
256 * Medium quality means use a mipmap for down-scaling, and just bilper
257 * for upscaling. Since we're examining the inverse matrix, we look for
258 * a scale > 1 to indicate down scaling by the CTM.
260 if (scaleSqd > SK_Scalar1) {
261 fCurrMip.reset(SkMipMapCache::FindAndRef(fOrigBitmap));
262 if (NULL == fCurrMip.get()) {
263 fCurrMip.reset(SkMipMap::Build(fOrigBitmap));
264 if (NULL == fCurrMip.get()) {
267 SkMipMapCache::Add(fOrigBitmap, fCurrMip);
270 SkScalar levelScale = SkScalarInvert(SkScalarSqrt(scaleSqd));
271 SkMipMap::Level level;
272 if (fCurrMip->extractLevel(levelScale, &level)) {
273 SkScalar invScaleFixup = level.fScale;
274 fInvMatrix.postScale(invScaleFixup, invScaleFixup);
276 const SkImageInfo info = fOrigBitmap.info().makeWH(level.fWidth, level.fHeight);
277 // todo: if we could wrap the fCurrMip in a pixelref, then we could just install
278 // that here, and not need to explicitly track it ourselves.
279 fScaledBitmap.installPixels(info, level.fPixels, level.fRowBytes);
280 fBitmap = &fScaledBitmap;
281 fFilterLevel = SkPaint::kLow_FilterLevel;
289 static bool get_locked_pixels(const SkBitmap& src, int pow2, SkBitmap* dst) {
290 SkPixelRef* pr = src.pixelRef();
291 if (pr && pr->decodeInto(pow2, dst)) {
296 * If decodeInto() fails, it is possibe that we have an old subclass that
297 * does not, or cannot, implement that. In that case we fall back to the
298 * older protocol of having the pixelRef handle the caching for us.
302 return SkToBool(dst->getPixels());
305 bool SkBitmapProcState::lockBaseBitmap() {
306 SkPixelRef* pr = fOrigBitmap.pixelRef();
308 if (pr->isLocked() || !pr->implementsDecodeInto()) {
309 // fast-case, no need to look in our cache
310 fScaledBitmap = fOrigBitmap;
311 fScaledBitmap.lockPixels();
312 if (NULL == fScaledBitmap.getPixels()) {
316 if (!SkBitmapCache::Find(fOrigBitmap, 1, 1, &fScaledBitmap)) {
317 if (!get_locked_pixels(fOrigBitmap, 0, &fScaledBitmap)) {
321 // TODO: if fScaled comes back at a different width/height than fOrig,
322 // we need to update the matrix we are using to sample from this guy.
324 SkBitmapCache::Add(fOrigBitmap, 1, 1, fScaledBitmap);
327 fBitmap = &fScaledBitmap;
331 SkBitmapProcState::~SkBitmapProcState() {
332 SkDELETE(fBitmapFilter);
335 bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
336 SkASSERT(fOrigBitmap.width() && fOrigBitmap.height());
340 fFilterLevel = paint.getFilterLevel();
342 // possiblyScaleImage will look to see if it can rescale the image as a
343 // preprocess; either by scaling up to the target size, or by selecting
344 // a nearby mipmap level. If it does, it will adjust the working
345 // matrix as well as the working bitmap. It may also adjust the filter
346 // quality to avoid re-filtering an already perfectly scaled image.
347 if (!this->possiblyScaleImage()) {
348 if (!this->lockBaseBitmap()) {
352 // The above logic should have always assigned fBitmap, but in case it
353 // didn't, we check for that now...
354 // TODO(dominikg): Ask humper@ if we can just use an SkASSERT(fBitmap)?
355 if (NULL == fBitmap) {
359 // If we are "still" kMedium_FilterLevel, then the request was not fulfilled by possiblyScale,
360 // so we downgrade to kLow (so the rest of the sniffing code can assume that)
361 if (SkPaint::kMedium_FilterLevel == fFilterLevel) {
362 fFilterLevel = SkPaint::kLow_FilterLevel;
365 bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
366 bool clampClamp = SkShader::kClamp_TileMode == fTileModeX &&
367 SkShader::kClamp_TileMode == fTileModeY;
369 if (!(fAdjustedMatrix || clampClamp || trivialMatrix)) {
370 fInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height());
373 // Now that all possible changes to the matrix have taken place, check
374 // to see if we're really close to a no-scale matrix. If so, explicitly
375 // set it to be so. Subsequent code may inspect this matrix to choose
376 // a faster path in this case.
378 // This code will only execute if the matrix has some scale component;
379 // if it's already pure translate then we won't do this inversion.
381 if (matrix_only_scale_translate(fInvMatrix)) {
383 if (fInvMatrix.invert(&forward)) {
384 if (clampClamp ? just_trans_clamp(forward, *fBitmap)
385 : just_trans_general(forward)) {
386 SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX());
387 SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY());
388 fInvMatrix.setTranslate(tx, ty);
393 fInvProc = fInvMatrix.getMapXYProc();
394 fInvType = fInvMatrix.getType();
395 fInvSx = SkScalarToFixed(fInvMatrix.getScaleX());
396 fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX());
397 fInvKy = SkScalarToFixed(fInvMatrix.getSkewY());
398 fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY());
400 fAlphaScale = SkAlpha255To256(paint.getAlpha());
402 fShaderProc32 = NULL;
403 fShaderProc16 = NULL;
404 fSampleProc32 = NULL;
405 fSampleProc16 = NULL;
407 // recompute the triviality of the matrix here because we may have
410 trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
412 if (SkPaint::kHigh_FilterLevel == fFilterLevel) {
413 // If this is still set, that means we wanted HQ sampling
414 // but couldn't do it as a preprocess. Let's try to install
415 // the scanline version of the HQ sampler. If that process fails,
416 // downgrade to bilerp.
418 // NOTE: Might need to be careful here in the future when we want
419 // to have the platform proc have a shot at this; it's possible that
420 // the chooseBitmapFilterProc will fail to install a shader but a
421 // platform-specific one might succeed, so it might be premature here
422 // to fall back to bilerp. This needs thought.
424 if (!this->setBitmapFilterProcs()) {
425 fFilterLevel = SkPaint::kLow_FilterLevel;
429 if (SkPaint::kLow_FilterLevel == fFilterLevel) {
430 // Only try bilerp if the matrix is "interesting" and
431 // the image has a suitable size.
433 if (fInvType <= SkMatrix::kTranslate_Mask ||
434 !valid_for_filtering(fBitmap->width() | fBitmap->height())) {
435 fFilterLevel = SkPaint::kNone_FilterLevel;
439 // At this point, we know exactly what kind of sampling the per-scanline
440 // shader will perform.
442 fMatrixProc = this->chooseMatrixProc(trivialMatrix);
443 // TODO(dominikg): SkASSERT(fMatrixProc) instead? chooseMatrixProc never returns NULL.
444 if (NULL == fMatrixProc) {
448 ///////////////////////////////////////////////////////////////////////
450 const SkAlphaType at = fBitmap->alphaType();
452 // No need to do this if we're doing HQ sampling; if filter quality is
453 // still set to HQ by the time we get here, then we must have installed
454 // the shader procs above and can skip all this.
456 if (fFilterLevel < SkPaint::kHigh_FilterLevel) {
459 if (fAlphaScale < 256) { // note: this distinction is not used for D16
462 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
465 if (fFilterLevel > SkPaint::kNone_FilterLevel) {
468 // bits 3,4,5 encoding the source bitmap format
469 switch (fBitmap->colorType()) {
470 case kN32_SkColorType:
471 if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
476 case kRGB_565_SkColorType:
479 case kIndex_8_SkColorType:
480 if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
485 case kARGB_4444_SkColorType:
486 if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
491 case kAlpha_8_SkColorType:
493 fPaintPMColor = SkPreMultiplyColor(paint.getColor());
496 // TODO(dominikg): Should we ever get here? SkASSERT(false) instead?
500 #if !SK_ARM_NEON_IS_ALWAYS
501 static const SampleProc32 gSkBitmapProcStateSample32[] = {
502 S32_opaque_D32_nofilter_DXDY,
503 S32_alpha_D32_nofilter_DXDY,
504 S32_opaque_D32_nofilter_DX,
505 S32_alpha_D32_nofilter_DX,
506 S32_opaque_D32_filter_DXDY,
507 S32_alpha_D32_filter_DXDY,
508 S32_opaque_D32_filter_DX,
509 S32_alpha_D32_filter_DX,
511 S16_opaque_D32_nofilter_DXDY,
512 S16_alpha_D32_nofilter_DXDY,
513 S16_opaque_D32_nofilter_DX,
514 S16_alpha_D32_nofilter_DX,
515 S16_opaque_D32_filter_DXDY,
516 S16_alpha_D32_filter_DXDY,
517 S16_opaque_D32_filter_DX,
518 S16_alpha_D32_filter_DX,
520 SI8_opaque_D32_nofilter_DXDY,
521 SI8_alpha_D32_nofilter_DXDY,
522 SI8_opaque_D32_nofilter_DX,
523 SI8_alpha_D32_nofilter_DX,
524 SI8_opaque_D32_filter_DXDY,
525 SI8_alpha_D32_filter_DXDY,
526 SI8_opaque_D32_filter_DX,
527 SI8_alpha_D32_filter_DX,
529 S4444_opaque_D32_nofilter_DXDY,
530 S4444_alpha_D32_nofilter_DXDY,
531 S4444_opaque_D32_nofilter_DX,
532 S4444_alpha_D32_nofilter_DX,
533 S4444_opaque_D32_filter_DXDY,
534 S4444_alpha_D32_filter_DXDY,
535 S4444_opaque_D32_filter_DX,
536 S4444_alpha_D32_filter_DX,
538 // A8 treats alpha/opaque the same (equally efficient)
539 SA8_alpha_D32_nofilter_DXDY,
540 SA8_alpha_D32_nofilter_DXDY,
541 SA8_alpha_D32_nofilter_DX,
542 SA8_alpha_D32_nofilter_DX,
543 SA8_alpha_D32_filter_DXDY,
544 SA8_alpha_D32_filter_DXDY,
545 SA8_alpha_D32_filter_DX,
546 SA8_alpha_D32_filter_DX
549 static const SampleProc16 gSkBitmapProcStateSample16[] = {
550 S32_D16_nofilter_DXDY,
555 S16_D16_nofilter_DXDY,
560 SI8_D16_nofilter_DXDY,
565 // Don't support 4444 -> 565
566 NULL, NULL, NULL, NULL,
567 // Don't support A8 -> 565
568 NULL, NULL, NULL, NULL
572 fSampleProc32 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index];
573 index >>= 1; // shift away any opaque/alpha distinction
574 fSampleProc16 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample16)[index];
576 // our special-case shaderprocs
577 if (SK_ARM_NEON_WRAP(S16_D16_filter_DX) == fSampleProc16) {
579 fShaderProc16 = SK_ARM_NEON_WRAP(Clamp_S16_D16_filter_DX_shaderproc);
580 } else if (SkShader::kRepeat_TileMode == fTileModeX &&
581 SkShader::kRepeat_TileMode == fTileModeY) {
582 fShaderProc16 = SK_ARM_NEON_WRAP(Repeat_S16_D16_filter_DX_shaderproc);
584 } else if (SK_ARM_NEON_WRAP(SI8_opaque_D32_filter_DX) == fSampleProc32 && clampClamp) {
585 fShaderProc32 = SK_ARM_NEON_WRAP(Clamp_SI8_opaque_D32_filter_DX_shaderproc);
588 if (NULL == fShaderProc32) {
589 fShaderProc32 = this->chooseShaderProc32();
593 // see if our platform has any accelerated overrides
594 this->platformProcs();
599 static void Clamp_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
601 SkPMColor* SK_RESTRICT colors,
603 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
604 SkASSERT(s.fInvKy == 0);
605 SkASSERT(count > 0 && colors != NULL);
606 SkASSERT(SkPaint::kNone_FilterLevel == s.fFilterLevel);
608 const int maxX = s.fBitmap->width() - 1;
609 const int maxY = s.fBitmap->height() - 1;
610 int ix = s.fFilterOneX + x;
611 int iy = SkClampMax(s.fFilterOneY + y, maxY);
615 s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
616 SkIntToScalar(y) + SK_ScalarHalf, &pt);
617 int iy2 = SkClampMax(SkScalarFloorToInt(pt.fY), maxY);
618 int ix2 = SkScalarFloorToInt(pt.fX);
624 const SkPMColor* row = s.fBitmap->getAddr32(0, iy);
628 int n = SkMin32(-ix, count);
629 sk_memset32(colors, row[0], n);
640 int n = SkMin32(maxX - ix + 1, count);
641 memcpy(colors, row + ix, n * sizeof(SkPMColor));
649 // clamp to the right
650 sk_memset32(colors, row[maxX], count);
653 static inline int sk_int_mod(int x, int n) {
655 if ((unsigned)x >= (unsigned)n) {
665 static inline int sk_int_mirror(int x, int n) {
666 x = sk_int_mod(x, 2 * n);
673 static void Repeat_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
675 SkPMColor* SK_RESTRICT colors,
677 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
678 SkASSERT(s.fInvKy == 0);
679 SkASSERT(count > 0 && colors != NULL);
680 SkASSERT(SkPaint::kNone_FilterLevel == s.fFilterLevel);
682 const int stopX = s.fBitmap->width();
683 const int stopY = s.fBitmap->height();
684 int ix = s.fFilterOneX + x;
685 int iy = sk_int_mod(s.fFilterOneY + y, stopY);
689 s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
690 SkIntToScalar(y) + SK_ScalarHalf, &pt);
691 int iy2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
692 int ix2 = SkScalarFloorToInt(pt.fX);
698 const SkPMColor* row = s.fBitmap->getAddr32(0, iy);
700 ix = sk_int_mod(ix, stopX);
702 int n = SkMin32(stopX - ix, count);
703 memcpy(colors, row + ix, n * sizeof(SkPMColor));
713 static void S32_D32_constX_shaderproc(const SkBitmapProcState& s,
715 SkPMColor* SK_RESTRICT colors,
717 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) == 0);
718 SkASSERT(s.fInvKy == 0);
719 SkASSERT(count > 0 && colors != NULL);
720 SkASSERT(1 == s.fBitmap->width());
723 int iY1 SK_INIT_TO_AVOID_WARNING;
724 int iSubY SK_INIT_TO_AVOID_WARNING;
726 if (SkPaint::kNone_FilterLevel != s.fFilterLevel) {
727 SkBitmapProcState::MatrixProc mproc = s.getMatrixProc();
730 mproc(s, xy, 1, x, y);
733 iY1 = xy[0] & 0x3FFF;
734 iSubY = (xy[0] >> 14) & 0xF;
738 if (s.fInvType > SkMatrix::kTranslate_Mask) {
740 s.fInvProc(s.fInvMatrix,
741 SkIntToScalar(x) + SK_ScalarHalf,
742 SkIntToScalar(y) + SK_ScalarHalf,
744 // When the matrix has a scale component the setup code in
745 // chooseProcs multiples the inverse matrix by the inverse of the
746 // bitmap's width and height. Since this method is going to do
747 // its own tiling and sampling we need to undo that here.
748 if (SkShader::kClamp_TileMode != s.fTileModeX ||
749 SkShader::kClamp_TileMode != s.fTileModeY) {
750 yTemp = SkScalarFloorToInt(pt.fY * s.fBitmap->height());
752 yTemp = SkScalarFloorToInt(pt.fY);
755 yTemp = s.fFilterOneY + y;
758 const int stopY = s.fBitmap->height();
759 switch (s.fTileModeY) {
760 case SkShader::kClamp_TileMode:
761 iY0 = SkClampMax(yTemp, stopY-1);
763 case SkShader::kRepeat_TileMode:
764 iY0 = sk_int_mod(yTemp, stopY);
766 case SkShader::kMirror_TileMode:
768 iY0 = sk_int_mirror(yTemp, stopY);
775 s.fInvProc(s.fInvMatrix,
776 SkIntToScalar(x) + SK_ScalarHalf,
777 SkIntToScalar(y) + SK_ScalarHalf,
779 if (s.fInvType > SkMatrix::kTranslate_Mask &&
780 (SkShader::kClamp_TileMode != s.fTileModeX ||
781 SkShader::kClamp_TileMode != s.fTileModeY)) {
782 pt.fY *= s.fBitmap->height();
786 switch (s.fTileModeY) {
787 case SkShader::kClamp_TileMode:
788 iY2 = SkClampMax(SkScalarFloorToInt(pt.fY), stopY-1);
790 case SkShader::kRepeat_TileMode:
791 iY2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
793 case SkShader::kMirror_TileMode:
795 iY2 = sk_int_mirror(SkScalarFloorToInt(pt.fY), stopY);
799 SkASSERT(iY0 == iY2);
804 const SkPMColor* row0 = s.fBitmap->getAddr32(0, iY0);
807 if (SkPaint::kNone_FilterLevel != s.fFilterLevel) {
808 const SkPMColor* row1 = s.fBitmap->getAddr32(0, iY1);
810 if (s.fAlphaScale < 256) {
811 Filter_32_alpha(iSubY, *row0, *row1, &color, s.fAlphaScale);
813 Filter_32_opaque(iSubY, *row0, *row1, &color);
816 if (s.fAlphaScale < 256) {
817 color = SkAlphaMulQ(*row0, s.fAlphaScale);
823 sk_memset32(colors, color, count);
826 static void DoNothing_shaderproc(const SkBitmapProcState&, int x, int y,
827 SkPMColor* SK_RESTRICT colors, int count) {
828 // if we get called, the matrix is too tricky, so we just draw nothing
829 sk_memset32(colors, 0, count);
832 bool SkBitmapProcState::setupForTranslate() {
834 fInvProc(fInvMatrix, SK_ScalarHalf, SK_ScalarHalf, &pt);
837 * if the translate is larger than our ints, we can get random results, or
838 * worse, we might get 0x80000000, which wreaks havoc on us, since we can't
841 const SkScalar too_big = SkIntToScalar(1 << 30);
842 if (SkScalarAbs(pt.fX) > too_big || SkScalarAbs(pt.fY) > too_big) {
846 // Since we know we're not filtered, we re-purpose these fields allow
847 // us to go from device -> src coordinates w/ just an integer add,
848 // rather than running through the inverse-matrix
849 fFilterOneX = SkScalarFloorToInt(pt.fX);
850 fFilterOneY = SkScalarFloorToInt(pt.fY);
854 SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() {
856 if (kN32_SkColorType != fBitmap->colorType()) {
860 static const unsigned kMask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
862 if (1 == fBitmap->width() && 0 == (fInvType & ~kMask)) {
863 if (SkPaint::kNone_FilterLevel == fFilterLevel &&
864 fInvType <= SkMatrix::kTranslate_Mask &&
865 !this->setupForTranslate()) {
866 return DoNothing_shaderproc;
868 return S32_D32_constX_shaderproc;
871 if (fAlphaScale < 256) {
874 if (fInvType > SkMatrix::kTranslate_Mask) {
877 if (SkPaint::kNone_FilterLevel != fFilterLevel) {
881 SkShader::TileMode tx = (SkShader::TileMode)fTileModeX;
882 SkShader::TileMode ty = (SkShader::TileMode)fTileModeY;
884 if (SkShader::kClamp_TileMode == tx && SkShader::kClamp_TileMode == ty) {
885 if (this->setupForTranslate()) {
886 return Clamp_S32_D32_nofilter_trans_shaderproc;
888 return DoNothing_shaderproc;
890 if (SkShader::kRepeat_TileMode == tx && SkShader::kRepeat_TileMode == ty) {
891 if (this->setupForTranslate()) {
892 return Repeat_S32_D32_nofilter_trans_shaderproc;
894 return DoNothing_shaderproc;
899 ///////////////////////////////////////////////////////////////////////////////
903 static void check_scale_nofilter(uint32_t bitmapXY[], int count,
904 unsigned mx, unsigned my) {
905 unsigned y = *bitmapXY++;
908 const uint16_t* xptr = reinterpret_cast<const uint16_t*>(bitmapXY);
909 for (int i = 0; i < count; ++i) {
910 SkASSERT(xptr[i] < mx);
914 static void check_scale_filter(uint32_t bitmapXY[], int count,
915 unsigned mx, unsigned my) {
916 uint32_t YY = *bitmapXY++;
917 unsigned y0 = YY >> 18;
918 unsigned y1 = YY & 0x3FFF;
922 for (int i = 0; i < count; ++i) {
923 uint32_t XX = bitmapXY[i];
924 unsigned x0 = XX >> 18;
925 unsigned x1 = XX & 0x3FFF;
931 static void check_affine_nofilter(uint32_t bitmapXY[], int count,
932 unsigned mx, unsigned my) {
933 for (int i = 0; i < count; ++i) {
934 uint32_t XY = bitmapXY[i];
935 unsigned x = XY & 0xFFFF;
936 unsigned y = XY >> 16;
942 static void check_affine_filter(uint32_t bitmapXY[], int count,
943 unsigned mx, unsigned my) {
944 for (int i = 0; i < count; ++i) {
945 uint32_t YY = *bitmapXY++;
946 unsigned y0 = YY >> 18;
947 unsigned y1 = YY & 0x3FFF;
951 uint32_t XX = *bitmapXY++;
952 unsigned x0 = XX >> 18;
953 unsigned x1 = XX & 0x3FFF;
959 void SkBitmapProcState::DebugMatrixProc(const SkBitmapProcState& state,
960 uint32_t bitmapXY[], int count,
965 state.fMatrixProc(state, bitmapXY, count, x, y);
967 void (*proc)(uint32_t bitmapXY[], int count, unsigned mx, unsigned my);
969 // There are four formats possible:
971 // filter -vs- nofilter
972 if (state.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
973 proc = state.fFilterLevel != SkPaint::kNone_FilterLevel ? check_scale_filter : check_scale_nofilter;
975 proc = state.fFilterLevel != SkPaint::kNone_FilterLevel ? check_affine_filter : check_affine_nofilter;
977 proc(bitmapXY, count, state.fBitmap->width(), state.fBitmap->height());
980 SkBitmapProcState::MatrixProc SkBitmapProcState::getMatrixProc() const {
981 return DebugMatrixProc;
986 ///////////////////////////////////////////////////////////////////////////////
988 The storage requirements for the different matrix procs are as follows,
989 where each X or Y is 2 bytes, and N is the number of pixels/elements:
991 scale/translate nofilter Y(4bytes) + N * X
992 affine/perspective nofilter N * (X Y)
993 scale/translate filter Y Y + N * (X X)
994 affine/perspective filter N * (Y Y X X)
996 int SkBitmapProcState::maxCountForBufferSize(size_t bufferSize) const {
997 int32_t size = static_cast<int32_t>(bufferSize);
999 size &= ~3; // only care about 4-byte aligned chunks
1000 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
1001 size -= 4; // the shared Y (or YY) coordinate
1010 if (fFilterLevel != SkPaint::kNone_FilterLevel) {