Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright 2011 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkBitmapCache.h"
      9 #include "SkBitmapController.h"
     10 #include "SkBitmapProcState.h"
     11 #include "SkColorData.h"
     12 #include "SkPaint.h"
     13 #include "SkShader.h"   // for tilemodes
     14 #include "SkUtilsArm.h"
     15 #include "SkMipMap.h"
     16 #include "SkPixelRef.h"
     17 #include "SkImageEncoder.h"
     18 #include "SkResourceCache.h"
     19 
     20 #if defined(SK_ARM_HAS_NEON)
     21 // These are defined in src/opts/SkBitmapProcState_arm_neon.cpp
     22 extern const SkBitmapProcState::SampleProc32 gSkBitmapProcStateSample32_neon[];
     23 #endif
     24 
     25 extern void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const void*, int, int, uint32_t*, int);
     26 
     27 #define   NAME_WRAP(x)  x
     28 #include "SkBitmapProcState_filter.h"
     29 #include "SkBitmapProcState_procs.h"
     30 
     31 SkBitmapProcInfo::SkBitmapProcInfo(const SkBitmapProvider& provider,
     32                                    SkShader::TileMode tmx, SkShader::TileMode tmy)
     33     : fProvider(provider)
     34     , fTileModeX(tmx)
     35     , fTileModeY(tmy)
     36     , fBMState(nullptr)
     37 {}
     38 
     39 SkBitmapProcInfo::~SkBitmapProcInfo() {
     40     SkInPlaceDeleteCheck(fBMState, fBMStateStorage.get());
     41 }
     42 
     43 ///////////////////////////////////////////////////////////////////////////////
     44 
     45 // true iff the matrix has a scale and no more than an optional translate.
     46 static bool matrix_only_scale_translate(const SkMatrix& m) {
     47     return (m.getType() & ~SkMatrix::kTranslate_Mask) == SkMatrix::kScale_Mask;
     48 }
     49 
     50 /**
     51  *  For the purposes of drawing bitmaps, if a matrix is "almost" translate
     52  *  go ahead and treat it as if it were, so that subsequent code can go fast.
     53  */
     54 static bool just_trans_general(const SkMatrix& matrix) {
     55     SkASSERT(matrix_only_scale_translate(matrix));
     56 
     57     const SkScalar tol = SK_Scalar1 / 32768;
     58 
     59     return SkScalarNearlyZero(matrix[SkMatrix::kMScaleX] - SK_Scalar1, tol)
     60         && SkScalarNearlyZero(matrix[SkMatrix::kMScaleY] - SK_Scalar1, tol);
     61 }
     62 
     63 /**
     64  *  Determine if the matrix can be treated as integral-only-translate,
     65  *  for the purpose of filtering.
     66  */
     67 static bool just_trans_integral(const SkMatrix& m) {
     68     static constexpr SkScalar tol = SK_Scalar1 / 256;
     69 
     70     return m.getType() <= SkMatrix::kTranslate_Mask
     71         && SkScalarNearlyEqual(m.getTranslateX(), SkScalarRoundToScalar(m.getTranslateX()), tol)
     72         && SkScalarNearlyEqual(m.getTranslateY(), SkScalarRoundToScalar(m.getTranslateY()), tol);
     73 }
     74 
     75 static bool valid_for_filtering(unsigned dimension) {
     76     // for filtering, width and height must fit in 14bits, since we use steal
     77     // 2 bits from each to store our 4bit subpixel data
     78     return (dimension & ~0x3FFF) == 0;
     79 }
     80 
     81 bool SkBitmapProcInfo::init(const SkMatrix& inv, const SkPaint& paint) {
     82     SkASSERT(inv.isScaleTranslate());
     83 
     84     fPixmap.reset();
     85     fInvMatrix = inv;
     86     fFilterQuality = paint.getFilterQuality();
     87 
     88     SkDefaultBitmapController controller;
     89     fBMState = controller.requestBitmap(fProvider, inv, paint.getFilterQuality(),
     90                                         fBMStateStorage.get(), fBMStateStorage.size());
     91     // Note : we allow the controller to return an empty (zero-dimension) result. Should we?
     92     if (nullptr == fBMState || fBMState->pixmap().info().isEmpty()) {
     93         return false;
     94     }
     95     fPixmap = fBMState->pixmap();
     96     fInvMatrix = fBMState->invMatrix();
     97     fRealInvMatrix = fBMState->invMatrix();
     98     fPaintColor = paint.getColor();
     99     fFilterQuality = fBMState->quality();
    100     SkASSERT(fFilterQuality <= kLow_SkFilterQuality);
    101     SkASSERT(fPixmap.addr());
    102 
    103     bool integral_translate_only = just_trans_integral(fInvMatrix);
    104     if (!integral_translate_only) {
    105         // Most of the scanline procs deal with "unit" texture coordinates, as this
    106         // makes it easy to perform tiling modes (repeat = (x & 0xFFFF)). To generate
    107         // those, we divide the matrix by its dimensions here.
    108         //
    109         // We don't do this if we're either trivial (can ignore the matrix) or clamping
    110         // in both X and Y since clamping to width,height is just as easy as to 0xFFFF.
    111 
    112         if (fTileModeX != SkShader::kClamp_TileMode ||
    113             fTileModeY != SkShader::kClamp_TileMode) {
    114             fInvMatrix.postIDiv(fPixmap.width(), fPixmap.height());
    115         }
    116 
    117         // Now that all possible changes to the matrix have taken place, check
    118         // to see if we're really close to a no-scale matrix.  If so, explicitly
    119         // set it to be so.  Subsequent code may inspect this matrix to choose
    120         // a faster path in this case.
    121 
    122         // This code will only execute if the matrix has some scale component;
    123         // if it's already pure translate then we won't do this inversion.
    124 
    125         if (matrix_only_scale_translate(fInvMatrix)) {
    126             SkMatrix forward;
    127             if (fInvMatrix.invert(&forward) && just_trans_general(forward)) {
    128                 fInvMatrix.setTranslate(-forward.getTranslateX(), -forward.getTranslateY());
    129             }
    130         }
    131 
    132         // Recompute the flag after matrix adjustments.
    133         integral_translate_only = just_trans_integral(fInvMatrix);
    134     }
    135 
    136     fInvType = fInvMatrix.getType();
    137 
    138     if (kLow_SkFilterQuality == fFilterQuality &&
    139         (!valid_for_filtering(fPixmap.width() | fPixmap.height()) ||
    140          integral_translate_only)) {
    141         fFilterQuality = kNone_SkFilterQuality;
    142     }
    143 
    144     return true;
    145 }
    146 
    147 /*
    148  *  Analyze filter-quality and matrix, and decide how to implement that.
    149  *
    150  *  In general, we cascade down the request level [ High ... None ]
    151  *  - for a given level, if we can fulfill it, fine, else
    152  *    - else we downgrade to the next lower level and try again.
    153  *  We can always fulfill requests for Low and None
    154  *  - sometimes we will "ignore" Low and give None, but this is likely a legacy perf hack
    155  *    and may be removed.
    156  */
    157 bool SkBitmapProcState::chooseProcs() {
    158     fInvProc            = SkMatrixPriv::GetMapXYProc(fInvMatrix);
    159     fInvSx              = SkScalarToFixed(fInvMatrix.getScaleX());
    160     fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX());
    161     fInvKy              = SkScalarToFixed(fInvMatrix.getSkewY());
    162     fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY());
    163 
    164     fAlphaScale = SkAlpha255To256(SkColorGetA(fPaintColor));
    165 
    166     fShaderProc32 = nullptr;
    167     fShaderProc16 = nullptr;
    168     fSampleProc32 = nullptr;
    169 
    170     const bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
    171     const bool clampClamp = SkShader::kClamp_TileMode == fTileModeX &&
    172                             SkShader::kClamp_TileMode == fTileModeY;
    173 
    174     return this->chooseScanlineProcs(trivialMatrix, clampClamp);
    175 }
    176 
    177 bool SkBitmapProcState::chooseScanlineProcs(bool trivialMatrix, bool clampClamp) {
    178     SkASSERT(fPixmap.colorType() == kN32_SkColorType);
    179 
    180     fMatrixProc = this->chooseMatrixProc(trivialMatrix);
    181     // TODO(dominikg): SkASSERT(fMatrixProc) instead? chooseMatrixProc never returns nullptr.
    182     if (nullptr == fMatrixProc) {
    183         return false;
    184     }
    185 
    186     const SkAlphaType at = fPixmap.alphaType();
    187     if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
    188         return false;
    189     }
    190 
    191     // No need to do this if we're doing HQ sampling; if filter quality is
    192     // still set to HQ by the time we get here, then we must have installed
    193     // the shader procs above and can skip all this.
    194 
    195     if (fFilterQuality < kHigh_SkFilterQuality) {
    196         int index = 0;
    197         if (fAlphaScale < 256) {  // note: this distinction is not used for D16
    198             index |= 1;
    199         }
    200         if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
    201             index |= 2;
    202         }
    203         if (fFilterQuality > kNone_SkFilterQuality) {
    204             index |= 4;
    205         }
    206 
    207 #if !defined(SK_ARM_HAS_NEON)
    208         static const SampleProc32 gSkBitmapProcStateSample32[] = {
    209             S32_opaque_D32_nofilter_DXDY,
    210             S32_alpha_D32_nofilter_DXDY,
    211             S32_opaque_D32_nofilter_DX,
    212             S32_alpha_D32_nofilter_DX,
    213             S32_opaque_D32_filter_DXDY,
    214             S32_alpha_D32_filter_DXDY,
    215             S32_opaque_D32_filter_DX,
    216             S32_alpha_D32_filter_DX,
    217         };
    218 #endif
    219 
    220         fSampleProc32 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index];
    221 
    222         // our special-case shaderprocs
    223         if (S32_opaque_D32_nofilter_DX == fSampleProc32 && clampClamp) {
    224             fShaderProc32 = Clamp_S32_opaque_D32_nofilter_DX_shaderproc;
    225         }
    226 
    227         if (nullptr == fShaderProc32) {
    228             fShaderProc32 = this->chooseShaderProc32();
    229         }
    230     }
    231 
    232     // see if our platform has any accelerated overrides
    233     this->platformProcs();
    234 
    235     return true;
    236 }
    237 
    238 static void Clamp_S32_D32_nofilter_trans_shaderproc(const void* sIn,
    239                                                     int x, int y,
    240                                                     SkPMColor* SK_RESTRICT colors,
    241                                                     int count) {
    242     const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn);
    243     SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
    244     SkASSERT(s.fInvKy == 0);
    245     SkASSERT(count > 0 && colors != nullptr);
    246     SkASSERT(kNone_SkFilterQuality == s.fFilterQuality);
    247 
    248     const int maxX = s.fPixmap.width() - 1;
    249     const int maxY = s.fPixmap.height() - 1;
    250     int ix = s.fFilterOneX + x;
    251     int iy = SkClampMax(s.fFilterOneY + y, maxY);
    252     const SkPMColor* row = s.fPixmap.addr32(0, iy);
    253 
    254     // clamp to the left
    255     if (ix < 0) {
    256         int n = SkMin32(-ix, count);
    257         sk_memset32(colors, row[0], n);
    258         count -= n;
    259         if (0 == count) {
    260             return;
    261         }
    262         colors += n;
    263         SkASSERT(-ix == n);
    264         ix = 0;
    265     }
    266     // copy the middle
    267     if (ix <= maxX) {
    268         int n = SkMin32(maxX - ix + 1, count);
    269         memcpy(colors, row + ix, n * sizeof(SkPMColor));
    270         count -= n;
    271         if (0 == count) {
    272             return;
    273         }
    274         colors += n;
    275     }
    276     SkASSERT(count > 0);
    277     // clamp to the right
    278     sk_memset32(colors, row[maxX], count);
    279 }
    280 
    281 static inline int sk_int_mod(int x, int n) {
    282     SkASSERT(n > 0);
    283     if ((unsigned)x >= (unsigned)n) {
    284         if (x < 0) {
    285             x = n + ~(~x % n);
    286         } else {
    287             x = x % n;
    288         }
    289     }
    290     return x;
    291 }
    292 
    293 static inline int sk_int_mirror(int x, int n) {
    294     x = sk_int_mod(x, 2 * n);
    295     if (x >= n) {
    296         x = n + ~(x - n);
    297     }
    298     return x;
    299 }
    300 
    301 static void Repeat_S32_D32_nofilter_trans_shaderproc(const void* sIn,
    302                                                      int x, int y,
    303                                                      SkPMColor* SK_RESTRICT colors,
    304                                                      int count) {
    305     const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn);
    306     SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
    307     SkASSERT(s.fInvKy == 0);
    308     SkASSERT(count > 0 && colors != nullptr);
    309     SkASSERT(kNone_SkFilterQuality == s.fFilterQuality);
    310 
    311     const int stopX = s.fPixmap.width();
    312     const int stopY = s.fPixmap.height();
    313     int ix = s.fFilterOneX + x;
    314     int iy = sk_int_mod(s.fFilterOneY + y, stopY);
    315     const SkPMColor* row = s.fPixmap.addr32(0, iy);
    316 
    317     ix = sk_int_mod(ix, stopX);
    318     for (;;) {
    319         int n = SkMin32(stopX - ix, count);
    320         memcpy(colors, row + ix, n * sizeof(SkPMColor));
    321         count -= n;
    322         if (0 == count) {
    323             return;
    324         }
    325         colors += n;
    326         ix = 0;
    327     }
    328 }
    329 
    330 static void S32_D32_constX_shaderproc(const void* sIn,
    331                                       int x, int y,
    332                                       SkPMColor* SK_RESTRICT colors,
    333                                       int count) {
    334     const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn);
    335     SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) == 0);
    336     SkASSERT(s.fInvKy == 0);
    337     SkASSERT(count > 0 && colors != nullptr);
    338     SkASSERT(1 == s.fPixmap.width());
    339 
    340     int iY0;
    341     int iY1   SK_INIT_TO_AVOID_WARNING;
    342     int iSubY SK_INIT_TO_AVOID_WARNING;
    343 
    344     if (kNone_SkFilterQuality != s.fFilterQuality) {
    345         SkBitmapProcState::MatrixProc mproc = s.getMatrixProc();
    346         uint32_t xy[2];
    347 
    348         mproc(s, xy, 1, x, y);
    349 
    350         iY0 = xy[0] >> 18;
    351         iY1 = xy[0] & 0x3FFF;
    352         iSubY = (xy[0] >> 14) & 0xF;
    353     } else {
    354         int yTemp;
    355 
    356         if (s.fInvType > SkMatrix::kTranslate_Mask) {
    357             const SkBitmapProcStateAutoMapper mapper(s, x, y);
    358 
    359             // When the matrix has a scale component the setup code in
    360             // chooseProcs multiples the inverse matrix by the inverse of the
    361             // bitmap's width and height. Since this method is going to do
    362             // its own tiling and sampling we need to undo that here.
    363             if (SkShader::kClamp_TileMode != s.fTileModeX ||
    364                 SkShader::kClamp_TileMode != s.fTileModeY) {
    365                 yTemp = SkFractionalIntToInt(mapper.fractionalIntY() * s.fPixmap.height());
    366             } else {
    367                 yTemp = mapper.intY();
    368             }
    369         } else {
    370             yTemp = s.fFilterOneY + y;
    371         }
    372 
    373         const int stopY = s.fPixmap.height();
    374         switch (s.fTileModeY) {
    375             case SkShader::kClamp_TileMode:
    376                 iY0 = SkClampMax(yTemp, stopY-1);
    377                 break;
    378             case SkShader::kRepeat_TileMode:
    379                 iY0 = sk_int_mod(yTemp, stopY);
    380                 break;
    381             case SkShader::kMirror_TileMode:
    382             default:
    383                 iY0 = sk_int_mirror(yTemp, stopY);
    384                 break;
    385         }
    386 
    387 #ifdef SK_DEBUG
    388         {
    389             const SkBitmapProcStateAutoMapper mapper(s, x, y);
    390             int iY2;
    391 
    392             if (s.fInvType > SkMatrix::kTranslate_Mask &&
    393                 (SkShader::kClamp_TileMode != s.fTileModeX ||
    394                  SkShader::kClamp_TileMode != s.fTileModeY)) {
    395                 iY2 = SkFractionalIntToInt(mapper.fractionalIntY() * s.fPixmap.height());
    396             } else {
    397                 iY2 = mapper.intY();
    398             }
    399 
    400             switch (s.fTileModeY) {
    401             case SkShader::kClamp_TileMode:
    402                 iY2 = SkClampMax(iY2, stopY-1);
    403                 break;
    404             case SkShader::kRepeat_TileMode:
    405                 iY2 = sk_int_mod(iY2, stopY);
    406                 break;
    407             case SkShader::kMirror_TileMode:
    408             default:
    409                 iY2 = sk_int_mirror(iY2, stopY);
    410                 break;
    411             }
    412 
    413             SkASSERT(iY0 == iY2);
    414         }
    415 #endif
    416     }
    417 
    418     const SkPMColor* row0 = s.fPixmap.addr32(0, iY0);
    419     SkPMColor color;
    420 
    421     if (kNone_SkFilterQuality != s.fFilterQuality) {
    422         const SkPMColor* row1 = s.fPixmap.addr32(0, iY1);
    423 
    424         if (s.fAlphaScale < 256) {
    425             Filter_32_alpha(iSubY, *row0, *row1, &color, s.fAlphaScale);
    426         } else {
    427             Filter_32_opaque(iSubY, *row0, *row1, &color);
    428         }
    429     } else {
    430         if (s.fAlphaScale < 256) {
    431             color = SkAlphaMulQ(*row0, s.fAlphaScale);
    432         } else {
    433             color = *row0;
    434         }
    435     }
    436 
    437     sk_memset32(colors, color, count);
    438 }
    439 
    440 static void DoNothing_shaderproc(const void*, int x, int y,
    441                                  SkPMColor* SK_RESTRICT colors, int count) {
    442     // if we get called, the matrix is too tricky, so we just draw nothing
    443     sk_memset32(colors, 0, count);
    444 }
    445 
    446 bool SkBitmapProcState::setupForTranslate() {
    447     SkPoint pt;
    448     const SkBitmapProcStateAutoMapper mapper(*this, 0, 0, &pt);
    449 
    450     /*
    451      *  if the translate is larger than our ints, we can get random results, or
    452      *  worse, we might get 0x80000000, which wreaks havoc on us, since we can't
    453      *  negate it.
    454      */
    455     const SkScalar too_big = SkIntToScalar(1 << 30);
    456     if (SkScalarAbs(pt.fX) > too_big || SkScalarAbs(pt.fY) > too_big) {
    457         return false;
    458     }
    459 
    460     // Since we know we're not filtered, we re-purpose these fields allow
    461     // us to go from device -> src coordinates w/ just an integer add,
    462     // rather than running through the inverse-matrix
    463     fFilterOneX = mapper.intX();
    464     fFilterOneY = mapper.intY();
    465 
    466     return true;
    467 }
    468 
    469 SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() {
    470 
    471     if (kN32_SkColorType != fPixmap.colorType()) {
    472         return nullptr;
    473     }
    474 
    475     static const unsigned kMask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
    476 
    477     if (1 == fPixmap.width() && 0 == (fInvType & ~kMask)) {
    478         if (kNone_SkFilterQuality == fFilterQuality &&
    479             fInvType <= SkMatrix::kTranslate_Mask &&
    480             !this->setupForTranslate()) {
    481             return DoNothing_shaderproc;
    482         }
    483         return S32_D32_constX_shaderproc;
    484     }
    485 
    486     if (fAlphaScale < 256) {
    487         return nullptr;
    488     }
    489     if (fInvType > SkMatrix::kTranslate_Mask) {
    490         return nullptr;
    491     }
    492     if (kNone_SkFilterQuality != fFilterQuality) {
    493         return nullptr;
    494     }
    495 
    496     SkShader::TileMode tx = (SkShader::TileMode)fTileModeX;
    497     SkShader::TileMode ty = (SkShader::TileMode)fTileModeY;
    498 
    499     if (SkShader::kClamp_TileMode == tx && SkShader::kClamp_TileMode == ty) {
    500         if (this->setupForTranslate()) {
    501             return Clamp_S32_D32_nofilter_trans_shaderproc;
    502         }
    503         return DoNothing_shaderproc;
    504     }
    505     if (SkShader::kRepeat_TileMode == tx && SkShader::kRepeat_TileMode == ty) {
    506         if (this->setupForTranslate()) {
    507             return Repeat_S32_D32_nofilter_trans_shaderproc;
    508         }
    509         return DoNothing_shaderproc;
    510     }
    511     return nullptr;
    512 }
    513 
    514 ///////////////////////////////////////////////////////////////////////////////
    515 
    516 #ifdef SK_DEBUG
    517 
    518 static void check_scale_nofilter(uint32_t bitmapXY[], int count,
    519                                  unsigned mx, unsigned my) {
    520     unsigned y = *bitmapXY++;
    521     SkASSERT(y < my);
    522 
    523     const uint16_t* xptr = reinterpret_cast<const uint16_t*>(bitmapXY);
    524     for (int i = 0; i < count; ++i) {
    525         SkASSERT(xptr[i] < mx);
    526     }
    527 }
    528 
    529 static void check_scale_filter(uint32_t bitmapXY[], int count,
    530                                  unsigned mx, unsigned my) {
    531     uint32_t YY = *bitmapXY++;
    532     unsigned y0 = YY >> 18;
    533     unsigned y1 = YY & 0x3FFF;
    534     SkASSERT(y0 < my);
    535     SkASSERT(y1 < my);
    536 
    537     for (int i = 0; i < count; ++i) {
    538         uint32_t XX = bitmapXY[i];
    539         unsigned x0 = XX >> 18;
    540         unsigned x1 = XX & 0x3FFF;
    541         SkASSERT(x0 < mx);
    542         SkASSERT(x1 < mx);
    543     }
    544 }
    545 
    546 void SkBitmapProcState::DebugMatrixProc(const SkBitmapProcState& state,
    547                                         uint32_t bitmapXY[], int count,
    548                                         int x, int y) {
    549     SkASSERT(bitmapXY);
    550     SkASSERT(count > 0);
    551 
    552     state.fMatrixProc(state, bitmapXY, count, x, y);
    553 
    554     void (*proc)(uint32_t bitmapXY[], int count, unsigned mx, unsigned my);
    555 
    556     // There are two formats possible:
    557     //  filter -vs- nofilter
    558     SkASSERT(state.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask));
    559     proc = state.fFilterQuality != kNone_SkFilterQuality ?
    560                 check_scale_filter : check_scale_nofilter;
    561     proc(bitmapXY, count, state.fPixmap.width(), state.fPixmap.height());
    562 }
    563 
    564 SkBitmapProcState::MatrixProc SkBitmapProcState::getMatrixProc() const {
    565     return DebugMatrixProc;
    566 }
    567 
    568 #endif
    569 
    570 ///////////////////////////////////////////////////////////////////////////////
    571 /*
    572     The storage requirements for the different matrix procs are as follows,
    573     where each X or Y is 2 bytes, and N is the number of pixels/elements:
    574 
    575     scale/translate     nofilter      Y(4bytes) + N * X
    576     affine/perspective  nofilter      N * (X Y)
    577     scale/translate     filter        Y Y + N * (X X)
    578     affine              filter        N * (Y Y X X)
    579  */
    580 int SkBitmapProcState::maxCountForBufferSize(size_t bufferSize) const {
    581     int32_t size = static_cast<int32_t>(bufferSize);
    582 
    583     size &= ~3; // only care about 4-byte aligned chunks
    584     if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
    585         size -= 4;   // the shared Y (or YY) coordinate
    586         if (size < 0) {
    587             size = 0;
    588         }
    589         size >>= 1;
    590     } else {
    591         size >>= 2;
    592     }
    593 
    594     if (fFilterQuality != kNone_SkFilterQuality) {
    595         size >>= 1;
    596     }
    597 
    598     return size;
    599 }
    600 
    601 ///////////////////////
    602 
    603 void  Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const void* sIn, int x, int y,
    604                                                   SkPMColor* SK_RESTRICT dst, int count) {
    605     const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn);
    606     SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
    607                              SkMatrix::kScale_Mask)) == 0);
    608 
    609     const unsigned maxX = s.fPixmap.width() - 1;
    610     SkFractionalInt fx;
    611     int dstY;
    612     {
    613         const SkBitmapProcStateAutoMapper mapper(s, x, y);
    614         const unsigned maxY = s.fPixmap.height() - 1;
    615         dstY = SkClampMax(mapper.intY(), maxY);
    616         fx = mapper.fractionalIntX();
    617     }
    618 
    619     const SkPMColor* SK_RESTRICT src = s.fPixmap.addr32(0, dstY);
    620     const SkFractionalInt dx = s.fInvSxFractionalInt;
    621 
    622     // Check if we're safely inside [0...maxX] so no need to clamp each computed index.
    623     //
    624     if ((uint64_t)SkFractionalIntToInt(fx) <= maxX &&
    625         (uint64_t)SkFractionalIntToInt(fx + dx * (count - 1)) <= maxX)
    626     {
    627         int count4 = count >> 2;
    628         for (int i = 0; i < count4; ++i) {
    629             SkPMColor src0 = src[SkFractionalIntToInt(fx)]; fx += dx;
    630             SkPMColor src1 = src[SkFractionalIntToInt(fx)]; fx += dx;
    631             SkPMColor src2 = src[SkFractionalIntToInt(fx)]; fx += dx;
    632             SkPMColor src3 = src[SkFractionalIntToInt(fx)]; fx += dx;
    633             dst[0] = src0;
    634             dst[1] = src1;
    635             dst[2] = src2;
    636             dst[3] = src3;
    637             dst += 4;
    638         }
    639         for (int i = (count4 << 2); i < count; ++i) {
    640             unsigned index = SkFractionalIntToInt(fx);
    641             SkASSERT(index <= maxX);
    642             *dst++ = src[index];
    643             fx += dx;
    644         }
    645     } else {
    646         for (int i = 0; i < count; ++i) {
    647             dst[i] = src[SkClampMax(SkFractionalIntToInt(fx), maxX)];
    648             fx += dx;
    649         }
    650     }
    651 }
    652