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