• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
build_sum_buffer(uint32_t sum[],int srcW,int srcH,const uint8_t src[],int srcRB)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  */
apply_kernel(uint8_t dst[],int rx,int ry,const uint32_t sum[],int sw,int sh)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  */
apply_kernel_interp(uint8_t dst[],int rx,int ry,const uint32_t sum[],int sw,int sh,U8CPU outer_weight)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 
merge_src_with_blur(uint8_t dst[],int dstRB,const uint8_t src[],int srcRB,const uint8_t blur[],int blurRB,int sw,int sh)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 
clamp_with_orig(uint8_t dst[],int dstRowBytes,const uint8_t src[],int srcRowBytes,int sw,int sh,SkBlurMask::Style style)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);
SkMask_FreeImage(uint8_t * image)243 void SkMask_FreeImage(uint8_t* image)
244 {
245     SkMask::FreeImage(image);
246 }
247 
Blur(SkMask * dst,const SkMask & src,SkScalar radius,Style style,Quality quality)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