• 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)248 bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
249                       SkScalar radius, Style style)
250 {
251     if (src.fFormat != SkMask::kA8_Format)
252         return false;
253 
254     int rx = SkScalarCeil(radius);
255     int outer_weight = 255 - SkScalarRound((SkIntToScalar(rx) - radius) * 255);
256 
257     SkASSERT(rx >= 0);
258     SkASSERT((unsigned)outer_weight <= 255);
259     if (rx <= 0) {
260         return false;
261     }
262 
263     int ry = rx;    // only do square blur for now
264 
265     dst->fBounds.set(src.fBounds.fLeft - rx, src.fBounds.fTop - ry,
266                         src.fBounds.fRight + rx, src.fBounds.fBottom + ry);
267     dst->fRowBytes = dst->fBounds.width();
268     dst->fFormat = SkMask::kA8_Format;
269     dst->fImage = NULL;
270 
271     if (src.fImage) {
272         size_t dstSize = dst->computeImageSize();
273         if (0 == dstSize) {
274             return false;   // too big to allocate, abort
275         }
276 
277         int             sw = src.fBounds.width();
278         int             sh = src.fBounds.height();
279         const uint8_t*  sp = src.fImage;
280         uint8_t*        dp = SkMask::AllocImage(dstSize);
281 
282         SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dp);
283 
284         // build the blurry destination
285         {
286             SkAutoTMalloc<uint32_t> storage((sw + 1) * (sh + 1));
287             uint32_t*               sumBuffer = storage.get();
288 
289             build_sum_buffer(sumBuffer, sw, sh, sp, src.fRowBytes);
290             dump_sum_buffer(sumBuffer, sw, sh);
291             if (outer_weight == 255)
292                 apply_kernel(dp, rx, ry, sumBuffer, sw, sh);
293             else
294                 apply_kernel_interp(dp, rx, ry, sumBuffer, sw, sh, outer_weight);
295         }
296 
297         dst->fImage = dp;
298         // if need be, alloc the "real" dst (same size as src) and copy/merge
299         // the blur into it (applying the src)
300         if (style == kInner_Style) {
301             // now we allocate the "real" dst, mirror the size of src
302             size_t srcSize = src.computeImageSize();
303             if (0 == srcSize) {
304                 return false;   // too big to allocate, abort
305             }
306             dst->fImage = SkMask::AllocImage(srcSize);
307             merge_src_with_blur(dst->fImage, src.fRowBytes,
308                                 sp, src.fRowBytes,
309                                 dp + rx + ry*dst->fRowBytes, dst->fRowBytes,
310                                 sw, sh);
311             SkMask::FreeImage(dp);
312         } else if (style != kNormal_Style) {
313             clamp_with_orig(dp + rx + ry*dst->fRowBytes, dst->fRowBytes,
314                             sp, src.fRowBytes, sw, sh,
315                             style);
316         }
317         (void)autoCall.detach();
318     }
319 
320     if (style == kInner_Style) {
321         dst->fBounds = src.fBounds; // restore trimmed bounds
322         dst->fRowBytes = src.fRowBytes;
323     }
324 
325 #if 0
326     if (gamma && dst->fImage) {
327         uint8_t*    image = dst->fImage;
328         uint8_t*    stop = image + dst->computeImageSize();
329 
330         for (; image < stop; image += 1) {
331             *image = gamma[*image];
332         }
333     }
334 #endif
335     return true;
336 }
337 
338 #if 0
339 void SkBlurMask::BuildSqrtGamma(uint8_t gamma[256], SkScalar percent)
340 {
341     SkASSERT(gamma);
342     SkASSERT(percent >= 0 && percent <= SK_Scalar1);
343 
344     int scale = SkScalarRound(percent * 256);
345 
346     for (int i = 0; i < 256; i++)
347     {
348         SkFixed n = i * 257;
349         n += n >> 15;
350         SkASSERT(n >= 0 && n <= SK_Fixed1);
351         n = SkFixedSqrt(n);
352         n = n * 255 >> 16;
353         n = SkAlphaBlend(n, i, scale);
354         gamma[i] = SkToU8(n);
355     }
356 }
357 
358 void SkBlurMask::BuildSqrGamma(uint8_t gamma[256], SkScalar percent)
359 {
360     SkASSERT(gamma);
361     SkASSERT(percent >= 0 && percent <= SK_Scalar1);
362 
363     int     scale = SkScalarRound(percent * 256);
364     SkFixed div255 = SK_Fixed1 / 255;
365 
366     for (int i = 0; i < 256; i++)
367     {
368         int square = i * i;
369         int linear = i * 255;
370         int n = SkAlphaBlend(square, linear, scale);
371         gamma[i] = SkToU8(n * div255 >> 16);
372     }
373 }
374 #endif
375