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