Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright 2006 The Android Open Source Project
      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 "SkBlurMask.h"
      9 
     10 #include "SkColorPriv.h"
     11 #include "SkEndian.h"
     12 #include "SkMaskBlurFilter.h"
     13 #include "SkMath.h"
     14 #include "SkMathPriv.h"
     15 #include "SkTemplates.h"
     16 #include "SkTo.h"
     17 
     18 // This constant approximates the scaling done in the software path's
     19 // "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)).
     20 // IMHO, it actually should be 1:  we blur "less" than we should do
     21 // according to the CSS and canvas specs, simply because Safari does the same.
     22 // Firefox used to do the same too, until 4.0 where they fixed it.  So at some
     23 // point we should probably get rid of these scaling constants and rebaseline
     24 // all the blur tests.
     25 static const SkScalar kBLUR_SIGMA_SCALE = 0.57735f;
     26 
     27 SkScalar SkBlurMask::ConvertRadiusToSigma(SkScalar radius) {
     28     return radius > 0 ? kBLUR_SIGMA_SCALE * radius + 0.5f : 0.0f;
     29 }
     30 
     31 SkScalar SkBlurMask::ConvertSigmaToRadius(SkScalar sigma) {
     32     return sigma > 0.5f ? (sigma - 0.5f) / kBLUR_SIGMA_SCALE : 0.0f;
     33 }
     34 
     35 
     36 template <typename AlphaIter>
     37 static void merge_src_with_blur(uint8_t dst[], int dstRB,
     38                                 AlphaIter src, int srcRB,
     39                                 const uint8_t blur[], int blurRB,
     40                                 int sw, int sh) {
     41     dstRB -= sw;
     42     blurRB -= sw;
     43     while (--sh >= 0) {
     44         AlphaIter rowSrc(src);
     45         for (int x = sw - 1; x >= 0; --x) {
     46             *dst = SkToU8(SkAlphaMul(*blur, SkAlpha255To256(*rowSrc)));
     47             ++dst;
     48             ++rowSrc;
     49             ++blur;
     50         }
     51         dst += dstRB;
     52         src >>= srcRB;
     53         blur += blurRB;
     54     }
     55 }
     56 
     57 template <typename AlphaIter>
     58 static void clamp_solid_with_orig(uint8_t dst[], int dstRowBytes,
     59                                   AlphaIter src, int srcRowBytes,
     60                                   int sw, int sh) {
     61     int x;
     62     while (--sh >= 0) {
     63         AlphaIter rowSrc(src);
     64         for (x = sw - 1; x >= 0; --x) {
     65             int s = *rowSrc;
     66             int d = *dst;
     67             *dst = SkToU8(s + d - SkMulDiv255Round(s, d));
     68             ++dst;
     69             ++rowSrc;
     70         }
     71         dst += dstRowBytes - sw;
     72         src >>= srcRowBytes;
     73     }
     74 }
     75 
     76 template <typename AlphaIter>
     77 static void clamp_outer_with_orig(uint8_t dst[], int dstRowBytes,
     78                                   AlphaIter src, int srcRowBytes,
     79                                   int sw, int sh) {
     80     int x;
     81     while (--sh >= 0) {
     82         AlphaIter rowSrc(src);
     83         for (x = sw - 1; x >= 0; --x) {
     84             int srcValue = *rowSrc;
     85             if (srcValue) {
     86                 *dst = SkToU8(SkAlphaMul(*dst, SkAlpha255To256(255 - srcValue)));
     87             }
     88             ++dst;
     89             ++rowSrc;
     90         }
     91         dst += dstRowBytes - sw;
     92         src >>= srcRowBytes;
     93     }
     94 }
     95 ///////////////////////////////////////////////////////////////////////////////
     96 
     97 // we use a local function to wrap the class static method to work around
     98 // a bug in gcc98
     99 void SkMask_FreeImage(uint8_t* image);
    100 void SkMask_FreeImage(uint8_t* image) {
    101     SkMask::FreeImage(image);
    102 }
    103 
    104 bool SkBlurMask::BoxBlur(SkMask* dst, const SkMask& src, SkScalar sigma, SkBlurStyle style,
    105                          SkIPoint* margin) {
    106     if (src.fFormat != SkMask::kBW_Format &&
    107         src.fFormat != SkMask::kA8_Format &&
    108         src.fFormat != SkMask::kARGB32_Format &&
    109         src.fFormat != SkMask::kLCD16_Format)
    110     {
    111         return false;
    112     }
    113 
    114     SkMaskBlurFilter blurFilter{sigma, sigma};
    115     if (blurFilter.hasNoBlur()) {
    116         // If there is no effective blur most styles will just produce the original mask.
    117         // However, kOuter_SkBlurStyle will produce an empty mask.
    118         if (style == kOuter_SkBlurStyle) {
    119             dst->fImage = nullptr;
    120             dst->fBounds = SkIRect::MakeEmpty();
    121             dst->fRowBytes = dst->fBounds.width();
    122             dst->fFormat = SkMask::kA8_Format;
    123             if (margin != nullptr) {
    124                 // This filter will disregard the src.fImage completely.
    125                 // The margin is actually {-(src.fBounds.width() / 2), -(src.fBounds.height() / 2)}
    126                 // but it is not clear if callers will fall over with negative margins.
    127                 *margin = SkIPoint{0,0};
    128             }
    129             return true;
    130         }
    131         return false;
    132     }
    133     const SkIPoint border = blurFilter.blur(src, dst);
    134     // If src.fImage is null, then this call is only to calculate the border.
    135     if (src.fImage != nullptr && dst->fImage == nullptr) {
    136         return false;
    137     }
    138 
    139     if (margin != nullptr) {
    140         *margin = border;
    141     }
    142 
    143     if (src.fImage == nullptr) {
    144         if (style == kInner_SkBlurStyle) {
    145             dst->fBounds = src.fBounds; // restore trimmed bounds
    146             dst->fRowBytes = dst->fBounds.width();
    147         }
    148         return true;
    149     }
    150 
    151     switch (style) {
    152         case kNormal_SkBlurStyle:
    153             break;
    154         case kSolid_SkBlurStyle: {
    155             auto dstStart = &dst->fImage[border.x() + border.y() * dst->fRowBytes];
    156             switch (src.fFormat) {
    157                 case SkMask::kBW_Format:
    158                     clamp_solid_with_orig(
    159                             dstStart, dst->fRowBytes,
    160                             SkMask::AlphaIter<SkMask::kBW_Format>(src.fImage, 0), src.fRowBytes,
    161                             src.fBounds.width(), src.fBounds.height());
    162                     break;
    163                 case SkMask::kA8_Format:
    164                     clamp_solid_with_orig(
    165                             dstStart, dst->fRowBytes,
    166                             SkMask::AlphaIter<SkMask::kA8_Format>(src.fImage), src.fRowBytes,
    167                             src.fBounds.width(), src.fBounds.height());
    168                     break;
    169                 case SkMask::kARGB32_Format: {
    170                     uint32_t* srcARGB = reinterpret_cast<uint32_t*>(src.fImage);
    171                     clamp_solid_with_orig(
    172                             dstStart, dst->fRowBytes,
    173                             SkMask::AlphaIter<SkMask::kARGB32_Format>(srcARGB), src.fRowBytes,
    174                             src.fBounds.width(), src.fBounds.height());
    175                 } break;
    176                 case SkMask::kLCD16_Format: {
    177                     uint16_t* srcLCD = reinterpret_cast<uint16_t*>(src.fImage);
    178                     clamp_solid_with_orig(
    179                             dstStart, dst->fRowBytes,
    180                             SkMask::AlphaIter<SkMask::kLCD16_Format>(srcLCD), src.fRowBytes,
    181                             src.fBounds.width(), src.fBounds.height());
    182                 } break;
    183                 default:
    184                     SK_ABORT("Unhandled format.");
    185             }
    186         } break;
    187         case kOuter_SkBlurStyle: {
    188             auto dstStart = &dst->fImage[border.x() + border.y() * dst->fRowBytes];
    189             switch (src.fFormat) {
    190                 case SkMask::kBW_Format:
    191                     clamp_outer_with_orig(
    192                             dstStart, dst->fRowBytes,
    193                             SkMask::AlphaIter<SkMask::kBW_Format>(src.fImage, 0), src.fRowBytes,
    194                             src.fBounds.width(), src.fBounds.height());
    195                     break;
    196                 case SkMask::kA8_Format:
    197                     clamp_outer_with_orig(
    198                             dstStart, dst->fRowBytes,
    199                             SkMask::AlphaIter<SkMask::kA8_Format>(src.fImage), src.fRowBytes,
    200                             src.fBounds.width(), src.fBounds.height());
    201                     break;
    202                 case SkMask::kARGB32_Format: {
    203                     uint32_t* srcARGB = reinterpret_cast<uint32_t*>(src.fImage);
    204                     clamp_outer_with_orig(
    205                             dstStart, dst->fRowBytes,
    206                             SkMask::AlphaIter<SkMask::kARGB32_Format>(srcARGB), src.fRowBytes,
    207                             src.fBounds.width(), src.fBounds.height());
    208                 } break;
    209                 case SkMask::kLCD16_Format: {
    210                     uint16_t* srcLCD = reinterpret_cast<uint16_t*>(src.fImage);
    211                     clamp_outer_with_orig(
    212                             dstStart, dst->fRowBytes,
    213                             SkMask::AlphaIter<SkMask::kLCD16_Format>(srcLCD), src.fRowBytes,
    214                             src.fBounds.width(), src.fBounds.height());
    215                 } break;
    216                 default:
    217                     SK_ABORT("Unhandled format.");
    218             }
    219         } break;
    220         case kInner_SkBlurStyle: {
    221             // now we allocate the "real" dst, mirror the size of src
    222             SkMask blur = *dst;
    223             SkAutoMaskFreeImage autoFreeBlurMask(blur.fImage);
    224             dst->fBounds = src.fBounds;
    225             dst->fRowBytes = dst->fBounds.width();
    226             size_t dstSize = dst->computeImageSize();
    227             if (0 == dstSize) {
    228                 return false;   // too big to allocate, abort
    229             }
    230             dst->fImage = SkMask::AllocImage(dstSize);
    231             auto blurStart = &blur.fImage[border.x() + border.y() * blur.fRowBytes];
    232             switch (src.fFormat) {
    233                 case SkMask::kBW_Format:
    234                     merge_src_with_blur(
    235                             dst->fImage, dst->fRowBytes,
    236                             SkMask::AlphaIter<SkMask::kBW_Format>(src.fImage, 0), src.fRowBytes,
    237                             blurStart, blur.fRowBytes,
    238                             src.fBounds.width(), src.fBounds.height());
    239                     break;
    240                 case SkMask::kA8_Format:
    241                     merge_src_with_blur(
    242                             dst->fImage, dst->fRowBytes,
    243                             SkMask::AlphaIter<SkMask::kA8_Format>(src.fImage), src.fRowBytes,
    244                             blurStart, blur.fRowBytes,
    245                             src.fBounds.width(), src.fBounds.height());
    246                     break;
    247                 case SkMask::kARGB32_Format: {
    248                     uint32_t* srcARGB = reinterpret_cast<uint32_t*>(src.fImage);
    249                     merge_src_with_blur(
    250                             dst->fImage, dst->fRowBytes,
    251                             SkMask::AlphaIter<SkMask::kARGB32_Format>(srcARGB), src.fRowBytes,
    252                             blurStart, blur.fRowBytes,
    253                             src.fBounds.width(), src.fBounds.height());
    254                 } break;
    255                 case SkMask::kLCD16_Format: {
    256                     uint16_t* srcLCD = reinterpret_cast<uint16_t*>(src.fImage);
    257                     merge_src_with_blur(
    258                             dst->fImage, dst->fRowBytes,
    259                             SkMask::AlphaIter<SkMask::kLCD16_Format>(srcLCD), src.fRowBytes,
    260                             blurStart, blur.fRowBytes,
    261                             src.fBounds.width(), src.fBounds.height());
    262                 } break;
    263                 default:
    264                     SK_ABORT("Unhandled format.");
    265             }
    266         } break;
    267     }
    268 
    269     return true;
    270 }
    271 
    272 /* Convolving a box with itself three times results in a piecewise
    273    quadratic function:
    274 
    275    0                              x <= -1.5
    276    9/8 + 3/2 x + 1/2 x^2   -1.5 < x <= -.5
    277    3/4 - x^2                -.5 < x <= .5
    278    9/8 - 3/2 x + 1/2 x^2    0.5 < x <= 1.5
    279    0                        1.5 < x
    280 
    281    Mathematica:
    282 
    283    g[x_] := Piecewise [ {
    284      {9/8 + 3/2 x + 1/2 x^2 ,  -1.5 < x <= -.5},
    285      {3/4 - x^2             ,   -.5 < x <= .5},
    286      {9/8 - 3/2 x + 1/2 x^2 ,   0.5 < x <= 1.5}
    287    }, 0]
    288 
    289    To get the profile curve of the blurred step function at the rectangle
    290    edge, we evaluate the indefinite integral, which is piecewise cubic:
    291 
    292    0                                        x <= -1.5
    293    9/16 + 9/8 x + 3/4 x^2 + 1/6 x^3   -1.5 < x <= -0.5
    294    1/2 + 3/4 x - 1/3 x^3              -.5 < x <= .5
    295    7/16 + 9/8 x - 3/4 x^2 + 1/6 x^3     .5 < x <= 1.5
    296    1                                  1.5 < x
    297 
    298    in Mathematica code:
    299 
    300    gi[x_] := Piecewise[ {
    301      { 0 , x <= -1.5 },
    302      { 9/16 + 9/8 x + 3/4 x^2 + 1/6 x^3, -1.5 < x <= -0.5 },
    303      { 1/2 + 3/4 x - 1/3 x^3          ,  -.5 < x <= .5},
    304      { 7/16 + 9/8 x - 3/4 x^2 + 1/6 x^3,   .5 < x <= 1.5}
    305    },1]
    306 */
    307 
    308 static float gaussianIntegral(float x) {
    309     if (x > 1.5f) {
    310         return 0.0f;
    311     }
    312     if (x < -1.5f) {
    313         return 1.0f;
    314     }
    315 
    316     float x2 = x*x;
    317     float x3 = x2*x;
    318 
    319     if ( x > 0.5f ) {
    320         return 0.5625f - (x3 / 6.0f - 3.0f * x2 * 0.25f + 1.125f * x);
    321     }
    322     if ( x > -0.5f ) {
    323         return 0.5f - (0.75f * x - x3 / 3.0f);
    324     }
    325     return 0.4375f + (-x3 / 6.0f - 3.0f * x2 * 0.25f - 1.125f * x);
    326 }
    327 
    328 /*  ComputeBlurProfile fills in an array of floating
    329     point values between 0 and 255 for the profile signature of
    330     a blurred half-plane with the given blur radius.  Since we're
    331     going to be doing screened multiplications (i.e., 1 - (1-x)(1-y))
    332     all the time, we actually fill in the profile pre-inverted
    333     (already done 255-x).
    334 */
    335 
    336 void SkBlurMask::ComputeBlurProfile(uint8_t* profile, int size, SkScalar sigma) {
    337     SkASSERT(SkScalarCeilToInt(6*sigma) == size);
    338 
    339     int center = size >> 1;
    340 
    341     float invr = 1.f/(2*sigma);
    342 
    343     profile[0] = 255;
    344     for (int x = 1 ; x < size ; ++x) {
    345         float scaled_x = (center - x - .5f) * invr;
    346         float gi = gaussianIntegral(scaled_x);
    347         profile[x] = 255 - (uint8_t) (255.f * gi);
    348     }
    349 }
    350 
    351 // TODO MAYBE: Maintain a profile cache to avoid recomputing this for
    352 // commonly used radii.  Consider baking some of the most common blur radii
    353 // directly in as static data?
    354 
    355 // Implementation adapted from Michael Herf's approach:
    356 // http://stereopsis.com/shadowrect/
    357 
    358 uint8_t SkBlurMask::ProfileLookup(const uint8_t *profile, int loc,
    359                                   int blurredWidth, int sharpWidth) {
    360     // how far are we from the original edge?
    361     int dx = SkAbs32(((loc << 1) + 1) - blurredWidth) - sharpWidth;
    362     int ox = dx >> 1;
    363     if (ox < 0) {
    364         ox = 0;
    365     }
    366 
    367     return profile[ox];
    368 }
    369 
    370 void SkBlurMask::ComputeBlurredScanline(uint8_t *pixels, const uint8_t *profile,
    371                                         unsigned int width, SkScalar sigma) {
    372 
    373     unsigned int profile_size = SkScalarCeilToInt(6*sigma);
    374     SkAutoTMalloc<uint8_t> horizontalScanline(width);
    375 
    376     unsigned int sw = width - profile_size;
    377     // nearest odd number less than the profile size represents the center
    378     // of the (2x scaled) profile
    379     int center = ( profile_size & ~1 ) - 1;
    380 
    381     int w = sw - center;
    382 
    383     for (unsigned int x = 0 ; x < width ; ++x) {
    384        if (profile_size <= sw) {
    385            pixels[x] = ProfileLookup(profile, x, width, w);
    386        } else {
    387            float span = float(sw)/(2*sigma);
    388            float giX = 1.5f - (x+.5f)/(2*sigma);
    389            pixels[x] = (uint8_t) (255 * (gaussianIntegral(giX) - gaussianIntegral(giX + span)));
    390        }
    391     }
    392 }
    393 
    394 bool SkBlurMask::BlurRect(SkScalar sigma, SkMask *dst,
    395                           const SkRect &src, SkBlurStyle style,
    396                           SkIPoint *margin, SkMask::CreateMode createMode) {
    397     int profileSize = SkScalarCeilToInt(6*sigma);
    398     if (profileSize <= 0) {
    399         return false;   // no blur to compute
    400     }
    401 
    402     int pad = profileSize/2;
    403     if (margin) {
    404         margin->set( pad, pad );
    405     }
    406 
    407     dst->fBounds.set(SkScalarRoundToInt(src.fLeft - pad),
    408                      SkScalarRoundToInt(src.fTop - pad),
    409                      SkScalarRoundToInt(src.fRight + pad),
    410                      SkScalarRoundToInt(src.fBottom + pad));
    411 
    412     dst->fRowBytes = dst->fBounds.width();
    413     dst->fFormat = SkMask::kA8_Format;
    414     dst->fImage = nullptr;
    415 
    416     int             sw = SkScalarFloorToInt(src.width());
    417     int             sh = SkScalarFloorToInt(src.height());
    418 
    419     if (createMode == SkMask::kJustComputeBounds_CreateMode) {
    420         if (style == kInner_SkBlurStyle) {
    421             dst->fBounds.set(SkScalarRoundToInt(src.fLeft),
    422                              SkScalarRoundToInt(src.fTop),
    423                              SkScalarRoundToInt(src.fRight),
    424                              SkScalarRoundToInt(src.fBottom)); // restore trimmed bounds
    425             dst->fRowBytes = sw;
    426         }
    427         return true;
    428     }
    429 
    430     SkAutoTMalloc<uint8_t> profile(profileSize);
    431 
    432     ComputeBlurProfile(profile, profileSize, sigma);
    433 
    434     size_t dstSize = dst->computeImageSize();
    435     if (0 == dstSize) {
    436         return false;   // too big to allocate, abort
    437     }
    438 
    439     uint8_t*        dp = SkMask::AllocImage(dstSize);
    440 
    441     dst->fImage = dp;
    442 
    443     int dstHeight = dst->fBounds.height();
    444     int dstWidth = dst->fBounds.width();
    445 
    446     uint8_t *outptr = dp;
    447 
    448     SkAutoTMalloc<uint8_t> horizontalScanline(dstWidth);
    449     SkAutoTMalloc<uint8_t> verticalScanline(dstHeight);
    450 
    451     ComputeBlurredScanline(horizontalScanline, profile, dstWidth, sigma);
    452     ComputeBlurredScanline(verticalScanline, profile, dstHeight, sigma);
    453 
    454     for (int y = 0 ; y < dstHeight ; ++y) {
    455         for (int x = 0 ; x < dstWidth ; x++) {
    456             unsigned int maskval = SkMulDiv255Round(horizontalScanline[x], verticalScanline[y]);
    457             *(outptr++) = maskval;
    458         }
    459     }
    460 
    461     if (style == kInner_SkBlurStyle) {
    462         // now we allocate the "real" dst, mirror the size of src
    463         size_t srcSize = (size_t)(src.width() * src.height());
    464         if (0 == srcSize) {
    465             return false;   // too big to allocate, abort
    466         }
    467         dst->fImage = SkMask::AllocImage(srcSize);
    468         for (int y = 0 ; y < sh ; y++) {
    469             uint8_t *blur_scanline = dp + (y+pad)*dstWidth + pad;
    470             uint8_t *inner_scanline = dst->fImage + y*sw;
    471             memcpy(inner_scanline, blur_scanline, sw);
    472         }
    473         SkMask::FreeImage(dp);
    474 
    475         dst->fBounds.set(SkScalarRoundToInt(src.fLeft),
    476                          SkScalarRoundToInt(src.fTop),
    477                          SkScalarRoundToInt(src.fRight),
    478                          SkScalarRoundToInt(src.fBottom)); // restore trimmed bounds
    479         dst->fRowBytes = sw;
    480 
    481     } else if (style == kOuter_SkBlurStyle) {
    482         for (int y = pad ; y < dstHeight-pad ; y++) {
    483             uint8_t *dst_scanline = dp + y*dstWidth + pad;
    484             memset(dst_scanline, 0, sw);
    485         }
    486     } else if (style == kSolid_SkBlurStyle) {
    487         for (int y = pad ; y < dstHeight-pad ; y++) {
    488             uint8_t *dst_scanline = dp + y*dstWidth + pad;
    489             memset(dst_scanline, 0xff, sw);
    490         }
    491     }
    492     // normal and solid styles are the same for analytic rect blurs, so don't
    493     // need to handle solid specially.
    494 
    495     return true;
    496 }
    497 
    498 bool SkBlurMask::BlurRRect(SkScalar sigma, SkMask *dst,
    499                            const SkRRect &src, SkBlurStyle style,
    500                            SkIPoint *margin, SkMask::CreateMode createMode) {
    501     // Temporary for now -- always fail, should cause caller to fall back
    502     // to old path.  Plumbing just to land API and parallelize effort.
    503 
    504     return false;
    505 }
    506 
    507 // The "simple" blur is a direct implementation of separable convolution with a discrete
    508 // gaussian kernel.  It's "ground truth" in a sense; too slow to be used, but very
    509 // useful for correctness comparisons.
    510 
    511 bool SkBlurMask::BlurGroundTruth(SkScalar sigma, SkMask* dst, const SkMask& src,
    512                                  SkBlurStyle style, SkIPoint* margin) {
    513 
    514     if (src.fFormat != SkMask::kA8_Format) {
    515         return false;
    516     }
    517 
    518     float variance = sigma * sigma;
    519 
    520     int windowSize = SkScalarCeilToInt(sigma*6);
    521     // round window size up to nearest odd number
    522     windowSize |= 1;
    523 
    524     SkAutoTMalloc<float> gaussWindow(windowSize);
    525 
    526     int halfWindow = windowSize >> 1;
    527 
    528     gaussWindow[halfWindow] = 1;
    529 
    530     float windowSum = 1;
    531     for (int x = 1 ; x <= halfWindow ; ++x) {
    532         float gaussian = expf(-x*x / (2*variance));
    533         gaussWindow[halfWindow + x] = gaussWindow[halfWindow-x] = gaussian;
    534         windowSum += 2*gaussian;
    535     }
    536 
    537     // leave the filter un-normalized for now; we will divide by the normalization
    538     // sum later;
    539 
    540     int pad = halfWindow;
    541     if (margin) {
    542         margin->set( pad, pad );
    543     }
    544 
    545     dst->fBounds = src.fBounds;
    546     dst->fBounds.outset(pad, pad);
    547 
    548     dst->fRowBytes = dst->fBounds.width();
    549     dst->fFormat = SkMask::kA8_Format;
    550     dst->fImage = nullptr;
    551 
    552     if (src.fImage) {
    553 
    554         size_t dstSize = dst->computeImageSize();
    555         if (0 == dstSize) {
    556             return false;   // too big to allocate, abort
    557         }
    558 
    559         int             srcWidth = src.fBounds.width();
    560         int             srcHeight = src.fBounds.height();
    561         int             dstWidth = dst->fBounds.width();
    562 
    563         const uint8_t*  srcPixels = src.fImage;
    564         uint8_t*        dstPixels = SkMask::AllocImage(dstSize);
    565         SkAutoMaskFreeImage autoFreeDstPixels(dstPixels);
    566 
    567         // do the actual blur.  First, make a padded copy of the source.
    568         // use double pad so we never have to check if we're outside anything
    569 
    570         int padWidth = srcWidth + 4*pad;
    571         int padHeight = srcHeight;
    572         int padSize = padWidth * padHeight;
    573 
    574         SkAutoTMalloc<uint8_t> padPixels(padSize);
    575         memset(padPixels, 0, padSize);
    576 
    577         for (int y = 0 ; y < srcHeight; ++y) {
    578             uint8_t* padptr = padPixels + y * padWidth + 2*pad;
    579             const uint8_t* srcptr = srcPixels + y * srcWidth;
    580             memcpy(padptr, srcptr, srcWidth);
    581         }
    582 
    583         // blur in X, transposing the result into a temporary floating point buffer.
    584         // also double-pad the intermediate result so that the second blur doesn't
    585         // have to do extra conditionals.
    586 
    587         int tmpWidth = padHeight + 4*pad;
    588         int tmpHeight = padWidth - 2*pad;
    589         int tmpSize = tmpWidth * tmpHeight;
    590 
    591         SkAutoTMalloc<float> tmpImage(tmpSize);
    592         memset(tmpImage, 0, tmpSize*sizeof(tmpImage[0]));
    593 
    594         for (int y = 0 ; y < padHeight ; ++y) {
    595             uint8_t *srcScanline = padPixels + y*padWidth;
    596             for (int x = pad ; x < padWidth - pad ; ++x) {
    597                 float *outPixel = tmpImage + (x-pad)*tmpWidth + y + 2*pad; // transposed output
    598                 uint8_t *windowCenter = srcScanline + x;
    599                 for (int i = -pad ; i <= pad ; ++i) {
    600                     *outPixel += gaussWindow[pad+i]*windowCenter[i];
    601                 }
    602                 *outPixel /= windowSum;
    603             }
    604         }
    605 
    606         // blur in Y; now filling in the actual desired destination.  We have to do
    607         // the transpose again; these transposes guarantee that we read memory in
    608         // linear order.
    609 
    610         for (int y = 0 ; y < tmpHeight ; ++y) {
    611             float *srcScanline = tmpImage + y*tmpWidth;
    612             for (int x = pad ; x < tmpWidth - pad ; ++x) {
    613                 float *windowCenter = srcScanline + x;
    614                 float finalValue = 0;
    615                 for (int i = -pad ; i <= pad ; ++i) {
    616                     finalValue += gaussWindow[pad+i]*windowCenter[i];
    617                 }
    618                 finalValue /= windowSum;
    619                 uint8_t *outPixel = dstPixels + (x-pad)*dstWidth + y; // transposed output
    620                 int integerPixel = int(finalValue + 0.5f);
    621                 *outPixel = SkClampMax( SkClampPos(integerPixel), 255 );
    622             }
    623         }
    624 
    625         dst->fImage = dstPixels;
    626         switch (style) {
    627             case kNormal_SkBlurStyle:
    628                 break;
    629             case kSolid_SkBlurStyle: {
    630                 clamp_solid_with_orig(
    631                         dstPixels + pad*dst->fRowBytes + pad, dst->fRowBytes,
    632                         SkMask::AlphaIter<SkMask::kA8_Format>(srcPixels), src.fRowBytes,
    633                         srcWidth, srcHeight);
    634             } break;
    635             case kOuter_SkBlurStyle: {
    636                 clamp_outer_with_orig(
    637                         dstPixels + pad*dst->fRowBytes + pad, dst->fRowBytes,
    638                         SkMask::AlphaIter<SkMask::kA8_Format>(srcPixels), src.fRowBytes,
    639                         srcWidth, srcHeight);
    640             } break;
    641             case kInner_SkBlurStyle: {
    642                 // now we allocate the "real" dst, mirror the size of src
    643                 size_t srcSize = src.computeImageSize();
    644                 if (0 == srcSize) {
    645                     return false;   // too big to allocate, abort
    646                 }
    647                 dst->fImage = SkMask::AllocImage(srcSize);
    648                 merge_src_with_blur(dst->fImage, src.fRowBytes,
    649                     SkMask::AlphaIter<SkMask::kA8_Format>(srcPixels), src.fRowBytes,
    650                     dstPixels + pad*dst->fRowBytes + pad,
    651                     dst->fRowBytes, srcWidth, srcHeight);
    652                 SkMask::FreeImage(dstPixels);
    653             } break;
    654         }
    655         autoFreeDstPixels.release();
    656     }
    657 
    658     if (style == kInner_SkBlurStyle) {
    659         dst->fBounds = src.fBounds; // restore trimmed bounds
    660         dst->fRowBytes = src.fRowBytes;
    661     }
    662 
    663     return true;
    664 }
    665