gfx/skia/trunk/src/effects/SkBlurMask.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1
michael@0 2 /*
michael@0 3 * Copyright 2006 The Android Open Source Project
michael@0 4 *
michael@0 5 * Use of this source code is governed by a BSD-style license that can be
michael@0 6 * found in the LICENSE file.
michael@0 7 */
michael@0 8
michael@0 9
michael@0 10 #include "SkBlurMask.h"
michael@0 11 #include "SkMath.h"
michael@0 12 #include "SkTemplates.h"
michael@0 13 #include "SkEndian.h"
michael@0 14
michael@0 15
michael@0 16 SkScalar SkBlurMask::ConvertRadiusToSigma(SkScalar radius) {
michael@0 17 // This constant approximates the scaling done in the software path's
michael@0 18 // "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)).
michael@0 19 // IMHO, it actually should be 1: we blur "less" than we should do
michael@0 20 // according to the CSS and canvas specs, simply because Safari does the same.
michael@0 21 // Firefox used to do the same too, until 4.0 where they fixed it. So at some
michael@0 22 // point we should probably get rid of these scaling constants and rebaseline
michael@0 23 // all the blur tests.
michael@0 24 static const SkScalar kBLUR_SIGMA_SCALE = 0.57735f;
michael@0 25
michael@0 26 return radius ? kBLUR_SIGMA_SCALE * radius + 0.5f : 0.0f;
michael@0 27 }
michael@0 28
michael@0 29 #define UNROLL_SEPARABLE_LOOPS
michael@0 30
michael@0 31 /**
michael@0 32 * This function performs a box blur in X, of the given radius. If the
michael@0 33 * "transpose" parameter is true, it will transpose the pixels on write,
michael@0 34 * such that X and Y are swapped. Reads are always performed from contiguous
michael@0 35 * memory in X, for speed. The destination buffer (dst) must be at least
michael@0 36 * (width + leftRadius + rightRadius) * height bytes in size.
michael@0 37 *
michael@0 38 * This is what the inner loop looks like before unrolling, and with the two
michael@0 39 * cases broken out separately (width < diameter, width >= diameter):
michael@0 40 *
michael@0 41 * if (width < diameter) {
michael@0 42 * for (int x = 0; x < width; ++x) {
michael@0 43 * sum += *right++;
michael@0 44 * *dptr = (sum * scale + half) >> 24;
michael@0 45 * dptr += dst_x_stride;
michael@0 46 * }
michael@0 47 * for (int x = width; x < diameter; ++x) {
michael@0 48 * *dptr = (sum * scale + half) >> 24;
michael@0 49 * dptr += dst_x_stride;
michael@0 50 * }
michael@0 51 * for (int x = 0; x < width; ++x) {
michael@0 52 * *dptr = (sum * scale + half) >> 24;
michael@0 53 * sum -= *left++;
michael@0 54 * dptr += dst_x_stride;
michael@0 55 * }
michael@0 56 * } else {
michael@0 57 * for (int x = 0; x < diameter; ++x) {
michael@0 58 * sum += *right++;
michael@0 59 * *dptr = (sum * scale + half) >> 24;
michael@0 60 * dptr += dst_x_stride;
michael@0 61 * }
michael@0 62 * for (int x = diameter; x < width; ++x) {
michael@0 63 * sum += *right++;
michael@0 64 * *dptr = (sum * scale + half) >> 24;
michael@0 65 * sum -= *left++;
michael@0 66 * dptr += dst_x_stride;
michael@0 67 * }
michael@0 68 * for (int x = 0; x < diameter; ++x) {
michael@0 69 * *dptr = (sum * scale + half) >> 24;
michael@0 70 * sum -= *left++;
michael@0 71 * dptr += dst_x_stride;
michael@0 72 * }
michael@0 73 * }
michael@0 74 */
michael@0 75 static int boxBlur(const uint8_t* src, int src_y_stride, uint8_t* dst,
michael@0 76 int leftRadius, int rightRadius, int width, int height,
michael@0 77 bool transpose)
michael@0 78 {
michael@0 79 int diameter = leftRadius + rightRadius;
michael@0 80 int kernelSize = diameter + 1;
michael@0 81 int border = SkMin32(width, diameter);
michael@0 82 uint32_t scale = (1 << 24) / kernelSize;
michael@0 83 int new_width = width + SkMax32(leftRadius, rightRadius) * 2;
michael@0 84 int dst_x_stride = transpose ? height : 1;
michael@0 85 int dst_y_stride = transpose ? 1 : new_width;
michael@0 86 uint32_t half = 1 << 23;
michael@0 87 for (int y = 0; y < height; ++y) {
michael@0 88 uint32_t sum = 0;
michael@0 89 uint8_t* dptr = dst + y * dst_y_stride;
michael@0 90 const uint8_t* right = src + y * src_y_stride;
michael@0 91 const uint8_t* left = right;
michael@0 92 for (int x = 0; x < rightRadius - leftRadius; x++) {
michael@0 93 *dptr = 0;
michael@0 94 dptr += dst_x_stride;
michael@0 95 }
michael@0 96 #define LEFT_BORDER_ITER \
michael@0 97 sum += *right++; \
michael@0 98 *dptr = (sum * scale + half) >> 24; \
michael@0 99 dptr += dst_x_stride;
michael@0 100
michael@0 101 int x = 0;
michael@0 102 #ifdef UNROLL_SEPARABLE_LOOPS
michael@0 103 for (; x < border - 16; x += 16) {
michael@0 104 LEFT_BORDER_ITER
michael@0 105 LEFT_BORDER_ITER
michael@0 106 LEFT_BORDER_ITER
michael@0 107 LEFT_BORDER_ITER
michael@0 108 LEFT_BORDER_ITER
michael@0 109 LEFT_BORDER_ITER
michael@0 110 LEFT_BORDER_ITER
michael@0 111 LEFT_BORDER_ITER
michael@0 112 LEFT_BORDER_ITER
michael@0 113 LEFT_BORDER_ITER
michael@0 114 LEFT_BORDER_ITER
michael@0 115 LEFT_BORDER_ITER
michael@0 116 LEFT_BORDER_ITER
michael@0 117 LEFT_BORDER_ITER
michael@0 118 LEFT_BORDER_ITER
michael@0 119 LEFT_BORDER_ITER
michael@0 120 }
michael@0 121 #endif
michael@0 122 for (; x < border; ++x) {
michael@0 123 LEFT_BORDER_ITER
michael@0 124 }
michael@0 125 #undef LEFT_BORDER_ITER
michael@0 126 #define TRIVIAL_ITER \
michael@0 127 *dptr = (sum * scale + half) >> 24; \
michael@0 128 dptr += dst_x_stride;
michael@0 129 x = width;
michael@0 130 #ifdef UNROLL_SEPARABLE_LOOPS
michael@0 131 for (; x < diameter - 16; x += 16) {
michael@0 132 TRIVIAL_ITER
michael@0 133 TRIVIAL_ITER
michael@0 134 TRIVIAL_ITER
michael@0 135 TRIVIAL_ITER
michael@0 136 TRIVIAL_ITER
michael@0 137 TRIVIAL_ITER
michael@0 138 TRIVIAL_ITER
michael@0 139 TRIVIAL_ITER
michael@0 140 TRIVIAL_ITER
michael@0 141 TRIVIAL_ITER
michael@0 142 TRIVIAL_ITER
michael@0 143 TRIVIAL_ITER
michael@0 144 TRIVIAL_ITER
michael@0 145 TRIVIAL_ITER
michael@0 146 TRIVIAL_ITER
michael@0 147 TRIVIAL_ITER
michael@0 148 }
michael@0 149 #endif
michael@0 150 for (; x < diameter; ++x) {
michael@0 151 TRIVIAL_ITER
michael@0 152 }
michael@0 153 #undef TRIVIAL_ITER
michael@0 154 #define CENTER_ITER \
michael@0 155 sum += *right++; \
michael@0 156 *dptr = (sum * scale + half) >> 24; \
michael@0 157 sum -= *left++; \
michael@0 158 dptr += dst_x_stride;
michael@0 159
michael@0 160 x = diameter;
michael@0 161 #ifdef UNROLL_SEPARABLE_LOOPS
michael@0 162 for (; x < width - 16; x += 16) {
michael@0 163 CENTER_ITER
michael@0 164 CENTER_ITER
michael@0 165 CENTER_ITER
michael@0 166 CENTER_ITER
michael@0 167 CENTER_ITER
michael@0 168 CENTER_ITER
michael@0 169 CENTER_ITER
michael@0 170 CENTER_ITER
michael@0 171 CENTER_ITER
michael@0 172 CENTER_ITER
michael@0 173 CENTER_ITER
michael@0 174 CENTER_ITER
michael@0 175 CENTER_ITER
michael@0 176 CENTER_ITER
michael@0 177 CENTER_ITER
michael@0 178 CENTER_ITER
michael@0 179 }
michael@0 180 #endif
michael@0 181 for (; x < width; ++x) {
michael@0 182 CENTER_ITER
michael@0 183 }
michael@0 184 #undef CENTER_ITER
michael@0 185 #define RIGHT_BORDER_ITER \
michael@0 186 *dptr = (sum * scale + half) >> 24; \
michael@0 187 sum -= *left++; \
michael@0 188 dptr += dst_x_stride;
michael@0 189
michael@0 190 x = 0;
michael@0 191 #ifdef UNROLL_SEPARABLE_LOOPS
michael@0 192 for (; x < border - 16; x += 16) {
michael@0 193 RIGHT_BORDER_ITER
michael@0 194 RIGHT_BORDER_ITER
michael@0 195 RIGHT_BORDER_ITER
michael@0 196 RIGHT_BORDER_ITER
michael@0 197 RIGHT_BORDER_ITER
michael@0 198 RIGHT_BORDER_ITER
michael@0 199 RIGHT_BORDER_ITER
michael@0 200 RIGHT_BORDER_ITER
michael@0 201 RIGHT_BORDER_ITER
michael@0 202 RIGHT_BORDER_ITER
michael@0 203 RIGHT_BORDER_ITER
michael@0 204 RIGHT_BORDER_ITER
michael@0 205 RIGHT_BORDER_ITER
michael@0 206 RIGHT_BORDER_ITER
michael@0 207 RIGHT_BORDER_ITER
michael@0 208 RIGHT_BORDER_ITER
michael@0 209 }
michael@0 210 #endif
michael@0 211 for (; x < border; ++x) {
michael@0 212 RIGHT_BORDER_ITER
michael@0 213 }
michael@0 214 #undef RIGHT_BORDER_ITER
michael@0 215 for (int x = 0; x < leftRadius - rightRadius; ++x) {
michael@0 216 *dptr = 0;
michael@0 217 dptr += dst_x_stride;
michael@0 218 }
michael@0 219 SkASSERT(sum == 0);
michael@0 220 }
michael@0 221 return new_width;
michael@0 222 }
michael@0 223
michael@0 224 /**
michael@0 225 * This variant of the box blur handles blurring of non-integer radii. It
michael@0 226 * keeps two running sums: an outer sum for the rounded-up kernel radius, and
michael@0 227 * an inner sum for the rounded-down kernel radius. For each pixel, it linearly
michael@0 228 * interpolates between them. In float this would be:
michael@0 229 * outer_weight * outer_sum / kernelSize +
michael@0 230 * (1.0 - outer_weight) * innerSum / (kernelSize - 2)
michael@0 231 *
michael@0 232 * This is what the inner loop looks like before unrolling, and with the two
michael@0 233 * cases broken out separately (width < diameter, width >= diameter):
michael@0 234 *
michael@0 235 * if (width < diameter) {
michael@0 236 * for (int x = 0; x < width; x++) {
michael@0 237 * inner_sum = outer_sum;
michael@0 238 * outer_sum += *right++;
michael@0 239 * *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24;
michael@0 240 * dptr += dst_x_stride;
michael@0 241 * }
michael@0 242 * for (int x = width; x < diameter; ++x) {
michael@0 243 * *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24;
michael@0 244 * dptr += dst_x_stride;
michael@0 245 * }
michael@0 246 * for (int x = 0; x < width; x++) {
michael@0 247 * inner_sum = outer_sum - *left++;
michael@0 248 * *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24;
michael@0 249 * dptr += dst_x_stride;
michael@0 250 * outer_sum = inner_sum;
michael@0 251 * }
michael@0 252 * } else {
michael@0 253 * for (int x = 0; x < diameter; x++) {
michael@0 254 * inner_sum = outer_sum;
michael@0 255 * outer_sum += *right++;
michael@0 256 * *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24;
michael@0 257 * dptr += dst_x_stride;
michael@0 258 * }
michael@0 259 * for (int x = diameter; x < width; ++x) {
michael@0 260 * inner_sum = outer_sum - *left;
michael@0 261 * outer_sum += *right++;
michael@0 262 * *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24;
michael@0 263 * dptr += dst_x_stride;
michael@0 264 * outer_sum -= *left++;
michael@0 265 * }
michael@0 266 * for (int x = 0; x < diameter; x++) {
michael@0 267 * inner_sum = outer_sum - *left++;
michael@0 268 * *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24;
michael@0 269 * dptr += dst_x_stride;
michael@0 270 * outer_sum = inner_sum;
michael@0 271 * }
michael@0 272 * }
michael@0 273 * }
michael@0 274 * return new_width;
michael@0 275 */
michael@0 276
michael@0 277 static int boxBlurInterp(const uint8_t* src, int src_y_stride, uint8_t* dst,
michael@0 278 int radius, int width, int height,
michael@0 279 bool transpose, uint8_t outer_weight)
michael@0 280 {
michael@0 281 int diameter = radius * 2;
michael@0 282 int kernelSize = diameter + 1;
michael@0 283 int border = SkMin32(width, diameter);
michael@0 284 int inner_weight = 255 - outer_weight;
michael@0 285 outer_weight += outer_weight >> 7;
michael@0 286 inner_weight += inner_weight >> 7;
michael@0 287 uint32_t outer_scale = (outer_weight << 16) / kernelSize;
michael@0 288 uint32_t inner_scale = (inner_weight << 16) / (kernelSize - 2);
michael@0 289 uint32_t half = 1 << 23;
michael@0 290 int new_width = width + diameter;
michael@0 291 int dst_x_stride = transpose ? height : 1;
michael@0 292 int dst_y_stride = transpose ? 1 : new_width;
michael@0 293 for (int y = 0; y < height; ++y) {
michael@0 294 uint32_t outer_sum = 0, inner_sum = 0;
michael@0 295 uint8_t* dptr = dst + y * dst_y_stride;
michael@0 296 const uint8_t* right = src + y * src_y_stride;
michael@0 297 const uint8_t* left = right;
michael@0 298 int x = 0;
michael@0 299
michael@0 300 #define LEFT_BORDER_ITER \
michael@0 301 inner_sum = outer_sum; \
michael@0 302 outer_sum += *right++; \
michael@0 303 *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24; \
michael@0 304 dptr += dst_x_stride;
michael@0 305
michael@0 306 #ifdef UNROLL_SEPARABLE_LOOPS
michael@0 307 for (;x < border - 16; x += 16) {
michael@0 308 LEFT_BORDER_ITER
michael@0 309 LEFT_BORDER_ITER
michael@0 310 LEFT_BORDER_ITER
michael@0 311 LEFT_BORDER_ITER
michael@0 312 LEFT_BORDER_ITER
michael@0 313 LEFT_BORDER_ITER
michael@0 314 LEFT_BORDER_ITER
michael@0 315 LEFT_BORDER_ITER
michael@0 316 LEFT_BORDER_ITER
michael@0 317 LEFT_BORDER_ITER
michael@0 318 LEFT_BORDER_ITER
michael@0 319 LEFT_BORDER_ITER
michael@0 320 LEFT_BORDER_ITER
michael@0 321 LEFT_BORDER_ITER
michael@0 322 LEFT_BORDER_ITER
michael@0 323 LEFT_BORDER_ITER
michael@0 324 }
michael@0 325 #endif
michael@0 326
michael@0 327 for (;x < border; ++x) {
michael@0 328 LEFT_BORDER_ITER
michael@0 329 }
michael@0 330 #undef LEFT_BORDER_ITER
michael@0 331 for (int x = width; x < diameter; ++x) {
michael@0 332 *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24;
michael@0 333 dptr += dst_x_stride;
michael@0 334 }
michael@0 335 x = diameter;
michael@0 336
michael@0 337 #define CENTER_ITER \
michael@0 338 inner_sum = outer_sum - *left; \
michael@0 339 outer_sum += *right++; \
michael@0 340 *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24; \
michael@0 341 dptr += dst_x_stride; \
michael@0 342 outer_sum -= *left++;
michael@0 343
michael@0 344 #ifdef UNROLL_SEPARABLE_LOOPS
michael@0 345 for (; x < width - 16; x += 16) {
michael@0 346 CENTER_ITER
michael@0 347 CENTER_ITER
michael@0 348 CENTER_ITER
michael@0 349 CENTER_ITER
michael@0 350 CENTER_ITER
michael@0 351 CENTER_ITER
michael@0 352 CENTER_ITER
michael@0 353 CENTER_ITER
michael@0 354 CENTER_ITER
michael@0 355 CENTER_ITER
michael@0 356 CENTER_ITER
michael@0 357 CENTER_ITER
michael@0 358 CENTER_ITER
michael@0 359 CENTER_ITER
michael@0 360 CENTER_ITER
michael@0 361 CENTER_ITER
michael@0 362 }
michael@0 363 #endif
michael@0 364 for (; x < width; ++x) {
michael@0 365 CENTER_ITER
michael@0 366 }
michael@0 367 #undef CENTER_ITER
michael@0 368
michael@0 369 #define RIGHT_BORDER_ITER \
michael@0 370 inner_sum = outer_sum - *left++; \
michael@0 371 *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24; \
michael@0 372 dptr += dst_x_stride; \
michael@0 373 outer_sum = inner_sum;
michael@0 374
michael@0 375 x = 0;
michael@0 376 #ifdef UNROLL_SEPARABLE_LOOPS
michael@0 377 for (; x < border - 16; x += 16) {
michael@0 378 RIGHT_BORDER_ITER
michael@0 379 RIGHT_BORDER_ITER
michael@0 380 RIGHT_BORDER_ITER
michael@0 381 RIGHT_BORDER_ITER
michael@0 382 RIGHT_BORDER_ITER
michael@0 383 RIGHT_BORDER_ITER
michael@0 384 RIGHT_BORDER_ITER
michael@0 385 RIGHT_BORDER_ITER
michael@0 386 RIGHT_BORDER_ITER
michael@0 387 RIGHT_BORDER_ITER
michael@0 388 RIGHT_BORDER_ITER
michael@0 389 RIGHT_BORDER_ITER
michael@0 390 RIGHT_BORDER_ITER
michael@0 391 RIGHT_BORDER_ITER
michael@0 392 RIGHT_BORDER_ITER
michael@0 393 RIGHT_BORDER_ITER
michael@0 394 }
michael@0 395 #endif
michael@0 396 for (; x < border; ++x) {
michael@0 397 RIGHT_BORDER_ITER
michael@0 398 }
michael@0 399 #undef RIGHT_BORDER_ITER
michael@0 400 SkASSERT(outer_sum == 0 && inner_sum == 0);
michael@0 401 }
michael@0 402 return new_width;
michael@0 403 }
michael@0 404
michael@0 405 static void get_adjusted_radii(SkScalar passRadius, int *loRadius, int *hiRadius)
michael@0 406 {
michael@0 407 *loRadius = *hiRadius = SkScalarCeilToInt(passRadius);
michael@0 408 if (SkIntToScalar(*hiRadius) - passRadius > 0.5f) {
michael@0 409 *loRadius = *hiRadius - 1;
michael@0 410 }
michael@0 411 }
michael@0 412
michael@0 413 #include "SkColorPriv.h"
michael@0 414
michael@0 415 static void merge_src_with_blur(uint8_t dst[], int dstRB,
michael@0 416 const uint8_t src[], int srcRB,
michael@0 417 const uint8_t blur[], int blurRB,
michael@0 418 int sw, int sh) {
michael@0 419 dstRB -= sw;
michael@0 420 srcRB -= sw;
michael@0 421 blurRB -= sw;
michael@0 422 while (--sh >= 0) {
michael@0 423 for (int x = sw - 1; x >= 0; --x) {
michael@0 424 *dst = SkToU8(SkAlphaMul(*blur, SkAlpha255To256(*src)));
michael@0 425 dst += 1;
michael@0 426 src += 1;
michael@0 427 blur += 1;
michael@0 428 }
michael@0 429 dst += dstRB;
michael@0 430 src += srcRB;
michael@0 431 blur += blurRB;
michael@0 432 }
michael@0 433 }
michael@0 434
michael@0 435 static void clamp_with_orig(uint8_t dst[], int dstRowBytes,
michael@0 436 const uint8_t src[], int srcRowBytes,
michael@0 437 int sw, int sh,
michael@0 438 SkBlurMask::Style style) {
michael@0 439 int x;
michael@0 440 while (--sh >= 0) {
michael@0 441 switch (style) {
michael@0 442 case SkBlurMask::kSolid_Style:
michael@0 443 for (x = sw - 1; x >= 0; --x) {
michael@0 444 int s = *src;
michael@0 445 int d = *dst;
michael@0 446 *dst = SkToU8(s + d - SkMulDiv255Round(s, d));
michael@0 447 dst += 1;
michael@0 448 src += 1;
michael@0 449 }
michael@0 450 break;
michael@0 451 case SkBlurMask::kOuter_Style:
michael@0 452 for (x = sw - 1; x >= 0; --x) {
michael@0 453 if (*src) {
michael@0 454 *dst = SkToU8(SkAlphaMul(*dst, SkAlpha255To256(255 - *src)));
michael@0 455 }
michael@0 456 dst += 1;
michael@0 457 src += 1;
michael@0 458 }
michael@0 459 break;
michael@0 460 default:
michael@0 461 SkDEBUGFAIL("Unexpected blur style here");
michael@0 462 break;
michael@0 463 }
michael@0 464 dst += dstRowBytes - sw;
michael@0 465 src += srcRowBytes - sw;
michael@0 466 }
michael@0 467 }
michael@0 468
michael@0 469 ///////////////////////////////////////////////////////////////////////////////
michael@0 470
michael@0 471 // we use a local function to wrap the class static method to work around
michael@0 472 // a bug in gcc98
michael@0 473 void SkMask_FreeImage(uint8_t* image);
michael@0 474 void SkMask_FreeImage(uint8_t* image) {
michael@0 475 SkMask::FreeImage(image);
michael@0 476 }
michael@0 477
michael@0 478 bool SkBlurMask::BoxBlur(SkMask* dst, const SkMask& src,
michael@0 479 SkScalar sigma, Style style, Quality quality,
michael@0 480 SkIPoint* margin) {
michael@0 481
michael@0 482 if (src.fFormat != SkMask::kA8_Format) {
michael@0 483 return false;
michael@0 484 }
michael@0 485
michael@0 486 // Force high quality off for small radii (performance)
michael@0 487 if (sigma <= SkIntToScalar(2)) {
michael@0 488 quality = kLow_Quality;
michael@0 489 }
michael@0 490
michael@0 491 SkScalar passRadius;
michael@0 492 if (kHigh_Quality == quality) {
michael@0 493 // For the high quality path the 3 pass box blur kernel width is
michael@0 494 // 6*rad+1 while the full Gaussian width is 6*sigma.
michael@0 495 passRadius = sigma - (1/6.0f);
michael@0 496 } else {
michael@0 497 // For the low quality path we only attempt to cover 3*sigma of the
michael@0 498 // Gaussian blur area (1.5*sigma on each side). The single pass box
michael@0 499 // blur's kernel size is 2*rad+1.
michael@0 500 passRadius = 1.5f*sigma - 0.5f;
michael@0 501 }
michael@0 502
michael@0 503 // highQuality: use three box blur passes as a cheap way
michael@0 504 // to approximate a Gaussian blur
michael@0 505 int passCount = (kHigh_Quality == quality) ? 3 : 1;
michael@0 506
michael@0 507 int rx = SkScalarCeilToInt(passRadius);
michael@0 508 int outerWeight = 255 - SkScalarRoundToInt((SkIntToScalar(rx) - passRadius) * 255);
michael@0 509
michael@0 510 SkASSERT(rx >= 0);
michael@0 511 SkASSERT((unsigned)outerWeight <= 255);
michael@0 512 if (rx <= 0) {
michael@0 513 return false;
michael@0 514 }
michael@0 515
michael@0 516 int ry = rx; // only do square blur for now
michael@0 517
michael@0 518 int padx = passCount * rx;
michael@0 519 int pady = passCount * ry;
michael@0 520
michael@0 521 if (margin) {
michael@0 522 margin->set(padx, pady);
michael@0 523 }
michael@0 524 dst->fBounds.set(src.fBounds.fLeft - padx, src.fBounds.fTop - pady,
michael@0 525 src.fBounds.fRight + padx, src.fBounds.fBottom + pady);
michael@0 526
michael@0 527 dst->fRowBytes = dst->fBounds.width();
michael@0 528 dst->fFormat = SkMask::kA8_Format;
michael@0 529 dst->fImage = NULL;
michael@0 530
michael@0 531 if (src.fImage) {
michael@0 532 size_t dstSize = dst->computeImageSize();
michael@0 533 if (0 == dstSize) {
michael@0 534 return false; // too big to allocate, abort
michael@0 535 }
michael@0 536
michael@0 537 int sw = src.fBounds.width();
michael@0 538 int sh = src.fBounds.height();
michael@0 539 const uint8_t* sp = src.fImage;
michael@0 540 uint8_t* dp = SkMask::AllocImage(dstSize);
michael@0 541 SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dp);
michael@0 542
michael@0 543 // build the blurry destination
michael@0 544 SkAutoTMalloc<uint8_t> tmpBuffer(dstSize);
michael@0 545 uint8_t* tp = tmpBuffer.get();
michael@0 546 int w = sw, h = sh;
michael@0 547
michael@0 548 if (outerWeight == 255) {
michael@0 549 int loRadius, hiRadius;
michael@0 550 get_adjusted_radii(passRadius, &loRadius, &hiRadius);
michael@0 551 if (kHigh_Quality == quality) {
michael@0 552 // Do three X blurs, with a transpose on the final one.
michael@0 553 w = boxBlur(sp, src.fRowBytes, tp, loRadius, hiRadius, w, h, false);
michael@0 554 w = boxBlur(tp, w, dp, hiRadius, loRadius, w, h, false);
michael@0 555 w = boxBlur(dp, w, tp, hiRadius, hiRadius, w, h, true);
michael@0 556 // Do three Y blurs, with a transpose on the final one.
michael@0 557 h = boxBlur(tp, h, dp, loRadius, hiRadius, h, w, false);
michael@0 558 h = boxBlur(dp, h, tp, hiRadius, loRadius, h, w, false);
michael@0 559 h = boxBlur(tp, h, dp, hiRadius, hiRadius, h, w, true);
michael@0 560 } else {
michael@0 561 w = boxBlur(sp, src.fRowBytes, tp, rx, rx, w, h, true);
michael@0 562 h = boxBlur(tp, h, dp, ry, ry, h, w, true);
michael@0 563 }
michael@0 564 } else {
michael@0 565 if (kHigh_Quality == quality) {
michael@0 566 // Do three X blurs, with a transpose on the final one.
michael@0 567 w = boxBlurInterp(sp, src.fRowBytes, tp, rx, w, h, false, outerWeight);
michael@0 568 w = boxBlurInterp(tp, w, dp, rx, w, h, false, outerWeight);
michael@0 569 w = boxBlurInterp(dp, w, tp, rx, w, h, true, outerWeight);
michael@0 570 // Do three Y blurs, with a transpose on the final one.
michael@0 571 h = boxBlurInterp(tp, h, dp, ry, h, w, false, outerWeight);
michael@0 572 h = boxBlurInterp(dp, h, tp, ry, h, w, false, outerWeight);
michael@0 573 h = boxBlurInterp(tp, h, dp, ry, h, w, true, outerWeight);
michael@0 574 } else {
michael@0 575 w = boxBlurInterp(sp, src.fRowBytes, tp, rx, w, h, true, outerWeight);
michael@0 576 h = boxBlurInterp(tp, h, dp, ry, h, w, true, outerWeight);
michael@0 577 }
michael@0 578 }
michael@0 579
michael@0 580 dst->fImage = dp;
michael@0 581 // if need be, alloc the "real" dst (same size as src) and copy/merge
michael@0 582 // the blur into it (applying the src)
michael@0 583 if (style == kInner_Style) {
michael@0 584 // now we allocate the "real" dst, mirror the size of src
michael@0 585 size_t srcSize = src.computeImageSize();
michael@0 586 if (0 == srcSize) {
michael@0 587 return false; // too big to allocate, abort
michael@0 588 }
michael@0 589 dst->fImage = SkMask::AllocImage(srcSize);
michael@0 590 merge_src_with_blur(dst->fImage, src.fRowBytes,
michael@0 591 sp, src.fRowBytes,
michael@0 592 dp + passCount * (rx + ry * dst->fRowBytes),
michael@0 593 dst->fRowBytes, sw, sh);
michael@0 594 SkMask::FreeImage(dp);
michael@0 595 } else if (style != kNormal_Style) {
michael@0 596 clamp_with_orig(dp + passCount * (rx + ry * dst->fRowBytes),
michael@0 597 dst->fRowBytes, sp, src.fRowBytes, sw, sh, style);
michael@0 598 }
michael@0 599 (void)autoCall.detach();
michael@0 600 }
michael@0 601
michael@0 602 if (style == kInner_Style) {
michael@0 603 dst->fBounds = src.fBounds; // restore trimmed bounds
michael@0 604 dst->fRowBytes = src.fRowBytes;
michael@0 605 }
michael@0 606
michael@0 607 return true;
michael@0 608 }
michael@0 609
michael@0 610 /* Convolving a box with itself three times results in a piecewise
michael@0 611 quadratic function:
michael@0 612
michael@0 613 0 x <= -1.5
michael@0 614 9/8 + 3/2 x + 1/2 x^2 -1.5 < x <= -.5
michael@0 615 3/4 - x^2 -.5 < x <= .5
michael@0 616 9/8 - 3/2 x + 1/2 x^2 0.5 < x <= 1.5
michael@0 617 0 1.5 < x
michael@0 618
michael@0 619 Mathematica:
michael@0 620
michael@0 621 g[x_] := Piecewise [ {
michael@0 622 {9/8 + 3/2 x + 1/2 x^2 , -1.5 < x <= -.5},
michael@0 623 {3/4 - x^2 , -.5 < x <= .5},
michael@0 624 {9/8 - 3/2 x + 1/2 x^2 , 0.5 < x <= 1.5}
michael@0 625 }, 0]
michael@0 626
michael@0 627 To get the profile curve of the blurred step function at the rectangle
michael@0 628 edge, we evaluate the indefinite integral, which is piecewise cubic:
michael@0 629
michael@0 630 0 x <= -1.5
michael@0 631 9/16 + 9/8 x + 3/4 x^2 + 1/6 x^3 -1.5 < x <= -0.5
michael@0 632 1/2 + 3/4 x - 1/3 x^3 -.5 < x <= .5
michael@0 633 7/16 + 9/8 x - 3/4 x^2 + 1/6 x^3 .5 < x <= 1.5
michael@0 634 1 1.5 < x
michael@0 635
michael@0 636 in Mathematica code:
michael@0 637
michael@0 638 gi[x_] := Piecewise[ {
michael@0 639 { 0 , x <= -1.5 },
michael@0 640 { 9/16 + 9/8 x + 3/4 x^2 + 1/6 x^3, -1.5 < x <= -0.5 },
michael@0 641 { 1/2 + 3/4 x - 1/3 x^3 , -.5 < x <= .5},
michael@0 642 { 7/16 + 9/8 x - 3/4 x^2 + 1/6 x^3, .5 < x <= 1.5}
michael@0 643 },1]
michael@0 644 */
michael@0 645
michael@0 646 static float gaussianIntegral(float x) {
michael@0 647 if (x > 1.5f) {
michael@0 648 return 0.0f;
michael@0 649 }
michael@0 650 if (x < -1.5f) {
michael@0 651 return 1.0f;
michael@0 652 }
michael@0 653
michael@0 654 float x2 = x*x;
michael@0 655 float x3 = x2*x;
michael@0 656
michael@0 657 if ( x > 0.5f ) {
michael@0 658 return 0.5625f - (x3 / 6.0f - 3.0f * x2 * 0.25f + 1.125f * x);
michael@0 659 }
michael@0 660 if ( x > -0.5f ) {
michael@0 661 return 0.5f - (0.75f * x - x3 / 3.0f);
michael@0 662 }
michael@0 663 return 0.4375f + (-x3 / 6.0f - 3.0f * x2 * 0.25f - 1.125f * x);
michael@0 664 }
michael@0 665
michael@0 666 /* ComputeBlurProfile allocates and fills in an array of floating
michael@0 667 point values between 0 and 255 for the profile signature of
michael@0 668 a blurred half-plane with the given blur radius. Since we're
michael@0 669 going to be doing screened multiplications (i.e., 1 - (1-x)(1-y))
michael@0 670 all the time, we actually fill in the profile pre-inverted
michael@0 671 (already done 255-x).
michael@0 672
michael@0 673 It's the responsibility of the caller to delete the
michael@0 674 memory returned in profile_out.
michael@0 675 */
michael@0 676
michael@0 677 void SkBlurMask::ComputeBlurProfile(SkScalar sigma, uint8_t **profile_out) {
michael@0 678 int size = SkScalarCeilToInt(6*sigma);
michael@0 679
michael@0 680 int center = size >> 1;
michael@0 681 uint8_t *profile = SkNEW_ARRAY(uint8_t, size);
michael@0 682
michael@0 683 float invr = 1.f/(2*sigma);
michael@0 684
michael@0 685 profile[0] = 255;
michael@0 686 for (int x = 1 ; x < size ; ++x) {
michael@0 687 float scaled_x = (center - x - .5f) * invr;
michael@0 688 float gi = gaussianIntegral(scaled_x);
michael@0 689 profile[x] = 255 - (uint8_t) (255.f * gi);
michael@0 690 }
michael@0 691
michael@0 692 *profile_out = profile;
michael@0 693 }
michael@0 694
michael@0 695 // TODO MAYBE: Maintain a profile cache to avoid recomputing this for
michael@0 696 // commonly used radii. Consider baking some of the most common blur radii
michael@0 697 // directly in as static data?
michael@0 698
michael@0 699 // Implementation adapted from Michael Herf's approach:
michael@0 700 // http://stereopsis.com/shadowrect/
michael@0 701
michael@0 702 uint8_t SkBlurMask::ProfileLookup(const uint8_t *profile, int loc, int blurred_width, int sharp_width) {
michael@0 703 int dx = SkAbs32(((loc << 1) + 1) - blurred_width) - sharp_width; // how far are we from the original edge?
michael@0 704 int ox = dx >> 1;
michael@0 705 if (ox < 0) {
michael@0 706 ox = 0;
michael@0 707 }
michael@0 708
michael@0 709 return profile[ox];
michael@0 710 }
michael@0 711
michael@0 712 void SkBlurMask::ComputeBlurredScanline(uint8_t *pixels, const uint8_t *profile,
michael@0 713 unsigned int width, SkScalar sigma) {
michael@0 714
michael@0 715 unsigned int profile_size = SkScalarCeilToInt(6*sigma);
michael@0 716 SkAutoTMalloc<uint8_t> horizontalScanline(width);
michael@0 717
michael@0 718 unsigned int sw = width - profile_size;
michael@0 719 // nearest odd number less than the profile size represents the center
michael@0 720 // of the (2x scaled) profile
michael@0 721 int center = ( profile_size & ~1 ) - 1;
michael@0 722
michael@0 723 int w = sw - center;
michael@0 724
michael@0 725 for (unsigned int x = 0 ; x < width ; ++x) {
michael@0 726 if (profile_size <= sw) {
michael@0 727 pixels[x] = ProfileLookup(profile, x, width, w);
michael@0 728 } else {
michael@0 729 float span = float(sw)/(2*sigma);
michael@0 730 float giX = 1.5f - (x+.5f)/(2*sigma);
michael@0 731 pixels[x] = (uint8_t) (255 * (gaussianIntegral(giX) - gaussianIntegral(giX + span)));
michael@0 732 }
michael@0 733 }
michael@0 734 }
michael@0 735
michael@0 736 bool SkBlurMask::BlurRect(SkScalar sigma, SkMask *dst,
michael@0 737 const SkRect &src, Style style,
michael@0 738 SkIPoint *margin, SkMask::CreateMode createMode) {
michael@0 739 int profile_size = SkScalarCeilToInt(6*sigma);
michael@0 740
michael@0 741 int pad = profile_size/2;
michael@0 742 if (margin) {
michael@0 743 margin->set( pad, pad );
michael@0 744 }
michael@0 745
michael@0 746 dst->fBounds.set(SkScalarRoundToInt(src.fLeft - pad),
michael@0 747 SkScalarRoundToInt(src.fTop - pad),
michael@0 748 SkScalarRoundToInt(src.fRight + pad),
michael@0 749 SkScalarRoundToInt(src.fBottom + pad));
michael@0 750
michael@0 751 dst->fRowBytes = dst->fBounds.width();
michael@0 752 dst->fFormat = SkMask::kA8_Format;
michael@0 753 dst->fImage = NULL;
michael@0 754
michael@0 755 int sw = SkScalarFloorToInt(src.width());
michael@0 756 int sh = SkScalarFloorToInt(src.height());
michael@0 757
michael@0 758 if (createMode == SkMask::kJustComputeBounds_CreateMode) {
michael@0 759 if (style == kInner_Style) {
michael@0 760 dst->fBounds.set(SkScalarRoundToInt(src.fLeft),
michael@0 761 SkScalarRoundToInt(src.fTop),
michael@0 762 SkScalarRoundToInt(src.fRight),
michael@0 763 SkScalarRoundToInt(src.fBottom)); // restore trimmed bounds
michael@0 764 dst->fRowBytes = sw;
michael@0 765 }
michael@0 766 return true;
michael@0 767 }
michael@0 768 uint8_t *profile = NULL;
michael@0 769
michael@0 770 ComputeBlurProfile(sigma, &profile);
michael@0 771 SkAutoTDeleteArray<uint8_t> ada(profile);
michael@0 772
michael@0 773 size_t dstSize = dst->computeImageSize();
michael@0 774 if (0 == dstSize) {
michael@0 775 return false; // too big to allocate, abort
michael@0 776 }
michael@0 777
michael@0 778 uint8_t* dp = SkMask::AllocImage(dstSize);
michael@0 779
michael@0 780 dst->fImage = dp;
michael@0 781
michael@0 782 int dstHeight = dst->fBounds.height();
michael@0 783 int dstWidth = dst->fBounds.width();
michael@0 784
michael@0 785 uint8_t *outptr = dp;
michael@0 786
michael@0 787 SkAutoTMalloc<uint8_t> horizontalScanline(dstWidth);
michael@0 788 SkAutoTMalloc<uint8_t> verticalScanline(dstHeight);
michael@0 789
michael@0 790 ComputeBlurredScanline(horizontalScanline, profile, dstWidth, sigma);
michael@0 791 ComputeBlurredScanline(verticalScanline, profile, dstHeight, sigma);
michael@0 792
michael@0 793 for (int y = 0 ; y < dstHeight ; ++y) {
michael@0 794 for (int x = 0 ; x < dstWidth ; x++) {
michael@0 795 unsigned int maskval = SkMulDiv255Round(horizontalScanline[x], verticalScanline[y]);
michael@0 796 *(outptr++) = maskval;
michael@0 797 }
michael@0 798 }
michael@0 799
michael@0 800 if (style == kInner_Style) {
michael@0 801 // now we allocate the "real" dst, mirror the size of src
michael@0 802 size_t srcSize = (size_t)(src.width() * src.height());
michael@0 803 if (0 == srcSize) {
michael@0 804 return false; // too big to allocate, abort
michael@0 805 }
michael@0 806 dst->fImage = SkMask::AllocImage(srcSize);
michael@0 807 for (int y = 0 ; y < sh ; y++) {
michael@0 808 uint8_t *blur_scanline = dp + (y+pad)*dstWidth + pad;
michael@0 809 uint8_t *inner_scanline = dst->fImage + y*sw;
michael@0 810 memcpy(inner_scanline, blur_scanline, sw);
michael@0 811 }
michael@0 812 SkMask::FreeImage(dp);
michael@0 813
michael@0 814 dst->fBounds.set(SkScalarRoundToInt(src.fLeft),
michael@0 815 SkScalarRoundToInt(src.fTop),
michael@0 816 SkScalarRoundToInt(src.fRight),
michael@0 817 SkScalarRoundToInt(src.fBottom)); // restore trimmed bounds
michael@0 818 dst->fRowBytes = sw;
michael@0 819
michael@0 820 } else if (style == kOuter_Style) {
michael@0 821 for (int y = pad ; y < dstHeight-pad ; y++) {
michael@0 822 uint8_t *dst_scanline = dp + y*dstWidth + pad;
michael@0 823 memset(dst_scanline, 0, sw);
michael@0 824 }
michael@0 825 } else if (style == kSolid_Style) {
michael@0 826 for (int y = pad ; y < dstHeight-pad ; y++) {
michael@0 827 uint8_t *dst_scanline = dp + y*dstWidth + pad;
michael@0 828 memset(dst_scanline, 0xff, sw);
michael@0 829 }
michael@0 830 }
michael@0 831 // normal and solid styles are the same for analytic rect blurs, so don't
michael@0 832 // need to handle solid specially.
michael@0 833
michael@0 834 return true;
michael@0 835 }
michael@0 836
michael@0 837 bool SkBlurMask::BlurRRect(SkScalar sigma, SkMask *dst,
michael@0 838 const SkRRect &src, Style style,
michael@0 839 SkIPoint *margin, SkMask::CreateMode createMode) {
michael@0 840 // Temporary for now -- always fail, should cause caller to fall back
michael@0 841 // to old path. Plumbing just to land API and parallelize effort.
michael@0 842
michael@0 843 return false;
michael@0 844 }
michael@0 845
michael@0 846 // The "simple" blur is a direct implementation of separable convolution with a discrete
michael@0 847 // gaussian kernel. It's "ground truth" in a sense; too slow to be used, but very
michael@0 848 // useful for correctness comparisons.
michael@0 849
michael@0 850 bool SkBlurMask::BlurGroundTruth(SkScalar sigma, SkMask* dst, const SkMask& src,
michael@0 851 Style style, SkIPoint* margin) {
michael@0 852
michael@0 853 if (src.fFormat != SkMask::kA8_Format) {
michael@0 854 return false;
michael@0 855 }
michael@0 856
michael@0 857 float variance = sigma * sigma;
michael@0 858
michael@0 859 int windowSize = SkScalarCeilToInt(sigma*6);
michael@0 860 // round window size up to nearest odd number
michael@0 861 windowSize |= 1;
michael@0 862
michael@0 863 SkAutoTMalloc<float> gaussWindow(windowSize);
michael@0 864
michael@0 865 int halfWindow = windowSize >> 1;
michael@0 866
michael@0 867 gaussWindow[halfWindow] = 1;
michael@0 868
michael@0 869 float windowSum = 1;
michael@0 870 for (int x = 1 ; x <= halfWindow ; ++x) {
michael@0 871 float gaussian = expf(-x*x / (2*variance));
michael@0 872 gaussWindow[halfWindow + x] = gaussWindow[halfWindow-x] = gaussian;
michael@0 873 windowSum += 2*gaussian;
michael@0 874 }
michael@0 875
michael@0 876 // leave the filter un-normalized for now; we will divide by the normalization
michael@0 877 // sum later;
michael@0 878
michael@0 879 int pad = halfWindow;
michael@0 880 if (margin) {
michael@0 881 margin->set( pad, pad );
michael@0 882 }
michael@0 883
michael@0 884 dst->fBounds = src.fBounds;
michael@0 885 dst->fBounds.outset(pad, pad);
michael@0 886
michael@0 887 dst->fRowBytes = dst->fBounds.width();
michael@0 888 dst->fFormat = SkMask::kA8_Format;
michael@0 889 dst->fImage = NULL;
michael@0 890
michael@0 891 if (src.fImage) {
michael@0 892
michael@0 893 size_t dstSize = dst->computeImageSize();
michael@0 894 if (0 == dstSize) {
michael@0 895 return false; // too big to allocate, abort
michael@0 896 }
michael@0 897
michael@0 898 int srcWidth = src.fBounds.width();
michael@0 899 int srcHeight = src.fBounds.height();
michael@0 900 int dstWidth = dst->fBounds.width();
michael@0 901
michael@0 902 const uint8_t* srcPixels = src.fImage;
michael@0 903 uint8_t* dstPixels = SkMask::AllocImage(dstSize);
michael@0 904 SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dstPixels);
michael@0 905
michael@0 906 // do the actual blur. First, make a padded copy of the source.
michael@0 907 // use double pad so we never have to check if we're outside anything
michael@0 908
michael@0 909 int padWidth = srcWidth + 4*pad;
michael@0 910 int padHeight = srcHeight;
michael@0 911 int padSize = padWidth * padHeight;
michael@0 912
michael@0 913 SkAutoTMalloc<uint8_t> padPixels(padSize);
michael@0 914 memset(padPixels, 0, padSize);
michael@0 915
michael@0 916 for (int y = 0 ; y < srcHeight; ++y) {
michael@0 917 uint8_t* padptr = padPixels + y * padWidth + 2*pad;
michael@0 918 const uint8_t* srcptr = srcPixels + y * srcWidth;
michael@0 919 memcpy(padptr, srcptr, srcWidth);
michael@0 920 }
michael@0 921
michael@0 922 // blur in X, transposing the result into a temporary floating point buffer.
michael@0 923 // also double-pad the intermediate result so that the second blur doesn't
michael@0 924 // have to do extra conditionals.
michael@0 925
michael@0 926 int tmpWidth = padHeight + 4*pad;
michael@0 927 int tmpHeight = padWidth - 2*pad;
michael@0 928 int tmpSize = tmpWidth * tmpHeight;
michael@0 929
michael@0 930 SkAutoTMalloc<float> tmpImage(tmpSize);
michael@0 931 memset(tmpImage, 0, tmpSize*sizeof(tmpImage[0]));
michael@0 932
michael@0 933 for (int y = 0 ; y < padHeight ; ++y) {
michael@0 934 uint8_t *srcScanline = padPixels + y*padWidth;
michael@0 935 for (int x = pad ; x < padWidth - pad ; ++x) {
michael@0 936 float *outPixel = tmpImage + (x-pad)*tmpWidth + y + 2*pad; // transposed output
michael@0 937 uint8_t *windowCenter = srcScanline + x;
michael@0 938 for (int i = -pad ; i <= pad ; ++i) {
michael@0 939 *outPixel += gaussWindow[pad+i]*windowCenter[i];
michael@0 940 }
michael@0 941 *outPixel /= windowSum;
michael@0 942 }
michael@0 943 }
michael@0 944
michael@0 945 // blur in Y; now filling in the actual desired destination. We have to do
michael@0 946 // the transpose again; these transposes guarantee that we read memory in
michael@0 947 // linear order.
michael@0 948
michael@0 949 for (int y = 0 ; y < tmpHeight ; ++y) {
michael@0 950 float *srcScanline = tmpImage + y*tmpWidth;
michael@0 951 for (int x = pad ; x < tmpWidth - pad ; ++x) {
michael@0 952 float *windowCenter = srcScanline + x;
michael@0 953 float finalValue = 0;
michael@0 954 for (int i = -pad ; i <= pad ; ++i) {
michael@0 955 finalValue += gaussWindow[pad+i]*windowCenter[i];
michael@0 956 }
michael@0 957 finalValue /= windowSum;
michael@0 958 uint8_t *outPixel = dstPixels + (x-pad)*dstWidth + y; // transposed output
michael@0 959 int integerPixel = int(finalValue + 0.5f);
michael@0 960 *outPixel = SkClampMax( SkClampPos(integerPixel), 255 );
michael@0 961 }
michael@0 962 }
michael@0 963
michael@0 964 dst->fImage = dstPixels;
michael@0 965 // if need be, alloc the "real" dst (same size as src) and copy/merge
michael@0 966 // the blur into it (applying the src)
michael@0 967 if (style == kInner_Style) {
michael@0 968 // now we allocate the "real" dst, mirror the size of src
michael@0 969 size_t srcSize = src.computeImageSize();
michael@0 970 if (0 == srcSize) {
michael@0 971 return false; // too big to allocate, abort
michael@0 972 }
michael@0 973 dst->fImage = SkMask::AllocImage(srcSize);
michael@0 974 merge_src_with_blur(dst->fImage, src.fRowBytes,
michael@0 975 srcPixels, src.fRowBytes,
michael@0 976 dstPixels + pad*dst->fRowBytes + pad,
michael@0 977 dst->fRowBytes, srcWidth, srcHeight);
michael@0 978 SkMask::FreeImage(dstPixels);
michael@0 979 } else if (style != kNormal_Style) {
michael@0 980 clamp_with_orig(dstPixels + pad*dst->fRowBytes + pad,
michael@0 981 dst->fRowBytes, srcPixels, src.fRowBytes, srcWidth, srcHeight, style);
michael@0 982 }
michael@0 983 (void)autoCall.detach();
michael@0 984 }
michael@0 985
michael@0 986 if (style == kInner_Style) {
michael@0 987 dst->fBounds = src.fBounds; // restore trimmed bounds
michael@0 988 dst->fRowBytes = src.fRowBytes;
michael@0 989 }
michael@0 990
michael@0 991 return true;
michael@0 992 }

mercurial