Home | History | Annotate | Download | only in effects
      1 /* libs/graphics/effects/SkBlurMask.cpp
      2 **
      3 ** Copyright 2006, The Android Open Source Project
      4 **
      5 ** Licensed under the Apache License, Version 2.0 (the "License");
      6 ** you may not use this file except in compliance with the License.
      7 ** You may obtain a copy of the License at
      8 **
      9 **     http://www.apache.org/licenses/LICENSE-2.0
     10 **
     11 ** Unless required by applicable law or agreed to in writing, software
     12 ** distributed under the License is distributed on an "AS IS" BASIS,
     13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 ** See the License for the specific language governing permissions and
     15 ** limitations under the License.
     16 */
     17 
     18 #include "SkBlurMask.h"
     19 #include "SkTemplates.h"
     20 
     21 #if 0
     22 static void dump_sum_buffer(const uint32_t sum[], const int w, const int h) {
     23     printf("---- sum buffer\n");
     24     for (int y = 0; y <= h; y++) {
     25         for (int x = 0; x <= w; x++) {
     26             printf(" %5d", sum[x]);
     27         }
     28         printf("\n");
     29         sum += w+1;
     30     }
     31 }
     32 #else
     33 #define dump_sum_buffer(sum, w, h)
     34 #endif
     35 
     36 /** The sum buffer is an array of u32 to hold the accumulated sum of all of the
     37     src values at their position, plus all values above and to the left.
     38     When we sample into this buffer, we need an initial row and column of 0s,
     39     so we have an index correspondence as follows:
     40 
     41     src[i, j] == sum[i+1, j+1]
     42     sum[0, j] == sum[i, 0] == 0
     43 
     44     We assume that the sum buffer's stride == its width
     45  */
     46 static void build_sum_buffer(uint32_t sum[], int srcW, int srcH, const uint8_t src[], int srcRB) {
     47     int sumW = srcW + 1;
     48 
     49     SkASSERT(srcRB >= srcW);
     50     // mod srcRB so we can apply it after each row
     51     srcRB -= srcW;
     52 
     53     int x, y;
     54 
     55     // zero out the top row and column
     56     memset(sum, 0, sumW * sizeof(sum[0]));
     57     sum += sumW;
     58 
     59     // special case first row
     60     uint32_t X = 0;
     61     *sum++ = 0; // initialze the first column to 0
     62     for (x = srcW - 1; x >= 0; --x)
     63     {
     64         X = *src++ + X;
     65         *sum++ = X;
     66     }
     67     src += srcRB;
     68 
     69     // now do the rest of the rows
     70     for (y = srcH - 1; y > 0; --y)
     71     {
     72         uint32_t L = 0;
     73         uint32_t C = 0;
     74         *sum++ = 0; // initialze the first column to 0
     75         for (x = srcW - 1; x >= 0; --x)
     76         {
     77             uint32_t T = sum[-sumW];
     78             X = *src++ + L + T - C;
     79             *sum++ = X;
     80             L = X;
     81             C = T;
     82         }
     83         src += srcRB;
     84     }
     85 }
     86 
     87 /*  sw and sh are the width and height of the src. Since the sum buffer
     88     matches that, but has an extra row and col at the beginning (with zeros),
     89     we can just use sw and sh as our "max" values for pinning coordinates
     90     when sampling into sum[][]
     91  */
     92 static void apply_kernel(uint8_t dst[], int rx, int ry, const uint32_t sum[],
     93                          int sw, int sh) {
     94     uint32_t scale = (1 << 24) / ((2*rx + 1)*(2*ry + 1));
     95 
     96     int sumStride = sw + 1;
     97 
     98     int dw = sw + 2*rx;
     99     int dh = sh + 2*ry;
    100 
    101     int prev_y = -2*ry;
    102     int next_y = 1;
    103 
    104     for (int y = 0; y < dh; y++) {
    105         int py = SkClampPos(prev_y) * sumStride;
    106         int ny = SkFastMin32(next_y, sh) * sumStride;
    107 
    108         int prev_x = -2*rx;
    109         int next_x = 1;
    110 
    111         for (int x = 0; x < dw; x++) {
    112             int px = SkClampPos(prev_x);
    113             int nx = SkFastMin32(next_x, sw);
    114 
    115             uint32_t tmp = sum[px+py] + sum[nx+ny] - sum[nx+py] - sum[px+ny];
    116             *dst++ = SkToU8(tmp * scale >> 24);
    117 
    118             prev_x += 1;
    119             next_x += 1;
    120         }
    121         prev_y += 1;
    122         next_y += 1;
    123     }
    124 }
    125 
    126 /*  sw and sh are the width and height of the src. Since the sum buffer
    127  matches that, but has an extra row and col at the beginning (with zeros),
    128  we can just use sw and sh as our "max" values for pinning coordinates
    129  when sampling into sum[][]
    130  */
    131 static void apply_kernel_interp(uint8_t dst[], int rx, int ry,
    132                 const uint32_t sum[], int sw, int sh, U8CPU outer_weight) {
    133     SkASSERT(rx > 0 && ry > 0);
    134     SkASSERT(outer_weight <= 255);
    135 
    136     int inner_weight = 255 - outer_weight;
    137 
    138     // round these guys up if they're bigger than 127
    139     outer_weight += outer_weight >> 7;
    140     inner_weight += inner_weight >> 7;
    141 
    142     uint32_t outer_scale = (outer_weight << 16) / ((2*rx + 1)*(2*ry + 1));
    143     uint32_t inner_scale = (inner_weight << 16) / ((2*rx - 1)*(2*ry - 1));
    144 
    145     int sumStride = sw + 1;
    146 
    147     int dw = sw + 2*rx;
    148     int dh = sh + 2*ry;
    149 
    150     int prev_y = -2*ry;
    151     int next_y = 1;
    152 
    153     for (int y = 0; y < dh; y++) {
    154         int py = SkClampPos(prev_y) * sumStride;
    155         int ny = SkFastMin32(next_y, sh) * sumStride;
    156 
    157         int ipy = SkClampPos(prev_y + 1) * sumStride;
    158         int iny = SkClampMax(next_y - 1, sh) * sumStride;
    159 
    160         int prev_x = -2*rx;
    161         int next_x = 1;
    162 
    163         for (int x = 0; x < dw; x++) {
    164             int px = SkClampPos(prev_x);
    165             int nx = SkFastMin32(next_x, sw);
    166 
    167             int ipx = SkClampPos(prev_x + 1);
    168             int inx = SkClampMax(next_x - 1, sw);
    169 
    170             uint32_t outer_sum = sum[px+py] + sum[nx+ny] - sum[nx+py] - sum[px+ny];
    171             uint32_t inner_sum = sum[ipx+ipy] + sum[inx+iny] - sum[inx+ipy] - sum[ipx+iny];
    172             *dst++ = SkToU8((outer_sum * outer_scale + inner_sum * inner_scale) >> 24);
    173 
    174             prev_x += 1;
    175             next_x += 1;
    176         }
    177         prev_y += 1;
    178         next_y += 1;
    179     }
    180 }
    181 
    182 #include "SkColorPriv.h"
    183 
    184 static void merge_src_with_blur(uint8_t dst[], int dstRB,
    185                                 const uint8_t src[], int srcRB,
    186                                 const uint8_t blur[], int blurRB,
    187                                 int sw, int sh) {
    188     dstRB -= sw;
    189     srcRB -= sw;
    190     blurRB -= sw;
    191     while (--sh >= 0) {
    192         for (int x = sw - 1; x >= 0; --x) {
    193             *dst = SkToU8(SkAlphaMul(*blur, SkAlpha255To256(*src)));
    194             dst += 1;
    195             src += 1;
    196             blur += 1;
    197         }
    198         dst += dstRB;
    199         src += srcRB;
    200         blur += blurRB;
    201     }
    202 }
    203 
    204 static void clamp_with_orig(uint8_t dst[], int dstRowBytes,
    205                             const uint8_t src[], int srcRowBytes,
    206                             int sw, int sh,
    207                             SkBlurMask::Style style) {
    208     int x;
    209     while (--sh >= 0) {
    210         switch (style) {
    211         case SkBlurMask::kSolid_Style:
    212             for (x = sw - 1; x >= 0; --x) {
    213                 int s = *src;
    214                 int d = *dst;
    215                 *dst = SkToU8(s + d - SkMulDiv255Round(s, d));
    216                 dst += 1;
    217                 src += 1;
    218             }
    219             break;
    220         case SkBlurMask::kOuter_Style:
    221             for (x = sw - 1; x >= 0; --x) {
    222                 if (*src) {
    223                     *dst = SkToU8(SkAlphaMul(*dst, SkAlpha255To256(255 - *src)));
    224                 }
    225                 dst += 1;
    226                 src += 1;
    227             }
    228             break;
    229         default:
    230             SkASSERT(!"Unexpected blur style here");
    231             break;
    232         }
    233         dst += dstRowBytes - sw;
    234         src += srcRowBytes - sw;
    235     }
    236 }
    237 
    238 ////////////////////////////////////////////////////////////////////////
    239 
    240 // we use a local funciton to wrap the class static method to work around
    241 // a bug in gcc98
    242 void SkMask_FreeImage(uint8_t* image);
    243 void SkMask_FreeImage(uint8_t* image)
    244 {
    245     SkMask::FreeImage(image);
    246 }
    247 
    248 bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
    249                       SkScalar radius, Style style, Quality quality)
    250 {
    251     if (src.fFormat != SkMask::kA8_Format)
    252         return false;
    253 
    254     // Force high quality off for small radii (performance)
    255     if (radius < SkIntToScalar(3)) quality = kLow_Quality;
    256 
    257     // highQuality: use three box blur passes as a cheap way to approximate a Gaussian blur
    258     int passCount = (quality == kHigh_Quality) ? 3 : 1;
    259     SkScalar passRadius = SkScalarDiv(radius, SkScalarSqrt(SkIntToScalar(passCount)));
    260 
    261     int rx = SkScalarCeil(passRadius);
    262     int outer_weight = 255 - SkScalarRound((SkIntToScalar(rx) - passRadius) * 255);
    263 
    264     SkASSERT(rx >= 0);
    265     SkASSERT((unsigned)outer_weight <= 255);
    266     if (rx <= 0) {
    267         return false;
    268     }
    269 
    270     int ry = rx;    // only do square blur for now
    271 
    272     int padx = passCount * rx;
    273     int pady = passCount * ry;
    274     dst->fBounds.set(src.fBounds.fLeft - padx, src.fBounds.fTop - pady,
    275         src.fBounds.fRight + padx, src.fBounds.fBottom + pady);
    276     dst->fRowBytes = dst->fBounds.width();
    277     dst->fFormat = SkMask::kA8_Format;
    278     dst->fImage = NULL;
    279 
    280     if (src.fImage) {
    281         size_t dstSize = dst->computeImageSize();
    282         if (0 == dstSize) {
    283             return false;   // too big to allocate, abort
    284         }
    285 
    286         int             sw = src.fBounds.width();
    287         int             sh = src.fBounds.height();
    288         const uint8_t*  sp = src.fImage;
    289         uint8_t*        dp = SkMask::AllocImage(dstSize);
    290 
    291         SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dp);
    292 
    293         // build the blurry destination
    294         {
    295             SkAutoTMalloc<uint32_t> storage((sw + 2 * (passCount - 1) * rx + 1) * (sh + 2 * (passCount - 1) * ry + 1));
    296             uint32_t*               sumBuffer = storage.get();
    297 
    298             //pass1: sp is source, dp is destination
    299             build_sum_buffer(sumBuffer, sw, sh, sp, src.fRowBytes);
    300             dump_sum_buffer(sumBuffer, sw, sh);
    301             if (outer_weight == 255)
    302                 apply_kernel(dp, rx, ry, sumBuffer, sw, sh);
    303             else
    304                 apply_kernel_interp(dp, rx, ry, sumBuffer, sw, sh, outer_weight);
    305 
    306             if (quality == kHigh_Quality)
    307             {
    308                 //pass2: dp is source, tmpBuffer is destination
    309                 int tmp_sw = sw + 2 * rx;
    310                 int tmp_sh = sh + 2 * ry;
    311                 SkAutoTMalloc<uint8_t>  tmpBuffer(dstSize);
    312                 build_sum_buffer(sumBuffer, tmp_sw, tmp_sh, dp, tmp_sw);
    313                 if (outer_weight == 255)
    314                     apply_kernel(tmpBuffer.get(), rx, ry, sumBuffer, tmp_sw, tmp_sh);
    315                 else
    316                     apply_kernel_interp(tmpBuffer.get(), rx, ry, sumBuffer, tmp_sw, tmp_sh, outer_weight);
    317 
    318                 //pass3: tmpBuffer is source, dp is destination
    319                 tmp_sw += 2 * rx;
    320                 tmp_sh += 2 * ry;
    321                 build_sum_buffer(sumBuffer, tmp_sw, tmp_sh, tmpBuffer.get(), tmp_sw);
    322                 if (outer_weight == 255)
    323                     apply_kernel(dp, rx, ry, sumBuffer, tmp_sw, tmp_sh);
    324                 else
    325                     apply_kernel_interp(dp, rx, ry, sumBuffer, tmp_sw, tmp_sh, outer_weight);
    326             }
    327         }
    328 
    329         dst->fImage = dp;
    330         // if need be, alloc the "real" dst (same size as src) and copy/merge
    331         // the blur into it (applying the src)
    332         if (style == kInner_Style) {
    333             // now we allocate the "real" dst, mirror the size of src
    334             size_t srcSize = src.computeImageSize();
    335             if (0 == srcSize) {
    336                 return false;   // too big to allocate, abort
    337             }
    338             dst->fImage = SkMask::AllocImage(srcSize);
    339             merge_src_with_blur(dst->fImage, src.fRowBytes,
    340                                 sp, src.fRowBytes,
    341                                 dp + passCount * (rx + ry * dst->fRowBytes), dst->fRowBytes,
    342                                 sw, sh);
    343             SkMask::FreeImage(dp);
    344         } else if (style != kNormal_Style) {
    345             clamp_with_orig(dp + passCount * (rx + ry * dst->fRowBytes), dst->fRowBytes,
    346                             sp, src.fRowBytes, sw, sh,
    347                             style);
    348         }
    349         (void)autoCall.detach();
    350     }
    351 
    352     if (style == kInner_Style) {
    353         dst->fBounds = src.fBounds; // restore trimmed bounds
    354         dst->fRowBytes = src.fRowBytes;
    355     }
    356 
    357 #if 0
    358     if (gamma && dst->fImage) {
    359         uint8_t*    image = dst->fImage;
    360         uint8_t*    stop = image + dst->computeImageSize();
    361 
    362         for (; image < stop; image += 1) {
    363             *image = gamma[*image];
    364         }
    365     }
    366 #endif
    367     return true;
    368 }
    369 
    370 #if 0
    371 void SkBlurMask::BuildSqrtGamma(uint8_t gamma[256], SkScalar percent)
    372 {
    373     SkASSERT(gamma);
    374     SkASSERT(percent >= 0 && percent <= SK_Scalar1);
    375 
    376     int scale = SkScalarRound(percent * 256);
    377 
    378     for (int i = 0; i < 256; i++)
    379     {
    380         SkFixed n = i * 257;
    381         n += n >> 15;
    382         SkASSERT(n >= 0 && n <= SK_Fixed1);
    383         n = SkFixedSqrt(n);
    384         n = n * 255 >> 16;
    385         n = SkAlphaBlend(n, i, scale);
    386         gamma[i] = SkToU8(n);
    387     }
    388 }
    389 
    390 void SkBlurMask::BuildSqrGamma(uint8_t gamma[256], SkScalar percent)
    391 {
    392     SkASSERT(gamma);
    393     SkASSERT(percent >= 0 && percent <= SK_Scalar1);
    394 
    395     int     scale = SkScalarRound(percent * 256);
    396     SkFixed div255 = SK_Fixed1 / 255;
    397 
    398     for (int i = 0; i < 256; i++)
    399     {
    400         int square = i * i;
    401         int linear = i * 255;
    402         int n = SkAlphaBlend(square, linear, scale);
    403         gamma[i] = SkToU8(n * div255 >> 16);
    404     }
    405 }
    406 #endif
    407