SkBlurMask.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. /*
  2. * Copyright 2006 The Android Open Source Project
  3. *
  4. * Use of this source code is governed by a BSD-style license that can be
  5. * found in the LICENSE file.
  6. */
  7. #include "src/core/SkBlurMask.h"
  8. #include "include/core/SkColorPriv.h"
  9. #include "include/core/SkMath.h"
  10. #include "include/private/SkTemplates.h"
  11. #include "include/private/SkTo.h"
  12. #include "src/core/SkEndian.h"
  13. #include "src/core/SkMaskBlurFilter.h"
  14. #include "src/core/SkMathPriv.h"
  15. // This constant approximates the scaling done in the software path's
  16. // "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)).
  17. // IMHO, it actually should be 1: we blur "less" than we should do
  18. // according to the CSS and canvas specs, simply because Safari does the same.
  19. // Firefox used to do the same too, until 4.0 where they fixed it. So at some
  20. // point we should probably get rid of these scaling constants and rebaseline
  21. // all the blur tests.
  22. static const SkScalar kBLUR_SIGMA_SCALE = 0.57735f;
  23. SkScalar SkBlurMask::ConvertRadiusToSigma(SkScalar radius) {
  24. return radius > 0 ? kBLUR_SIGMA_SCALE * radius + 0.5f : 0.0f;
  25. }
  26. SkScalar SkBlurMask::ConvertSigmaToRadius(SkScalar sigma) {
  27. return sigma > 0.5f ? (sigma - 0.5f) / kBLUR_SIGMA_SCALE : 0.0f;
  28. }
  29. template <typename AlphaIter>
  30. static void merge_src_with_blur(uint8_t dst[], int dstRB,
  31. AlphaIter src, int srcRB,
  32. const uint8_t blur[], int blurRB,
  33. int sw, int sh) {
  34. dstRB -= sw;
  35. blurRB -= sw;
  36. while (--sh >= 0) {
  37. AlphaIter rowSrc(src);
  38. for (int x = sw - 1; x >= 0; --x) {
  39. *dst = SkToU8(SkAlphaMul(*blur, SkAlpha255To256(*rowSrc)));
  40. ++dst;
  41. ++rowSrc;
  42. ++blur;
  43. }
  44. dst += dstRB;
  45. src >>= srcRB;
  46. blur += blurRB;
  47. }
  48. }
  49. template <typename AlphaIter>
  50. static void clamp_solid_with_orig(uint8_t dst[], int dstRowBytes,
  51. AlphaIter src, int srcRowBytes,
  52. int sw, int sh) {
  53. int x;
  54. while (--sh >= 0) {
  55. AlphaIter rowSrc(src);
  56. for (x = sw - 1; x >= 0; --x) {
  57. int s = *rowSrc;
  58. int d = *dst;
  59. *dst = SkToU8(s + d - SkMulDiv255Round(s, d));
  60. ++dst;
  61. ++rowSrc;
  62. }
  63. dst += dstRowBytes - sw;
  64. src >>= srcRowBytes;
  65. }
  66. }
  67. template <typename AlphaIter>
  68. static void clamp_outer_with_orig(uint8_t dst[], int dstRowBytes,
  69. AlphaIter src, int srcRowBytes,
  70. int sw, int sh) {
  71. int x;
  72. while (--sh >= 0) {
  73. AlphaIter rowSrc(src);
  74. for (x = sw - 1; x >= 0; --x) {
  75. int srcValue = *rowSrc;
  76. if (srcValue) {
  77. *dst = SkToU8(SkAlphaMul(*dst, SkAlpha255To256(255 - srcValue)));
  78. }
  79. ++dst;
  80. ++rowSrc;
  81. }
  82. dst += dstRowBytes - sw;
  83. src >>= srcRowBytes;
  84. }
  85. }
  86. ///////////////////////////////////////////////////////////////////////////////
  87. // we use a local function to wrap the class static method to work around
  88. // a bug in gcc98
  89. void SkMask_FreeImage(uint8_t* image);
  90. void SkMask_FreeImage(uint8_t* image) {
  91. SkMask::FreeImage(image);
  92. }
  93. bool SkBlurMask::BoxBlur(SkMask* dst, const SkMask& src, SkScalar sigma, SkBlurStyle style,
  94. SkIPoint* margin) {
  95. if (src.fFormat != SkMask::kBW_Format &&
  96. src.fFormat != SkMask::kA8_Format &&
  97. src.fFormat != SkMask::kARGB32_Format &&
  98. src.fFormat != SkMask::kLCD16_Format)
  99. {
  100. return false;
  101. }
  102. SkMaskBlurFilter blurFilter{sigma, sigma};
  103. if (blurFilter.hasNoBlur()) {
  104. // If there is no effective blur most styles will just produce the original mask.
  105. // However, kOuter_SkBlurStyle will produce an empty mask.
  106. if (style == kOuter_SkBlurStyle) {
  107. dst->fImage = nullptr;
  108. dst->fBounds = SkIRect::MakeEmpty();
  109. dst->fRowBytes = dst->fBounds.width();
  110. dst->fFormat = SkMask::kA8_Format;
  111. if (margin != nullptr) {
  112. // This filter will disregard the src.fImage completely.
  113. // The margin is actually {-(src.fBounds.width() / 2), -(src.fBounds.height() / 2)}
  114. // but it is not clear if callers will fall over with negative margins.
  115. *margin = SkIPoint{0,0};
  116. }
  117. return true;
  118. }
  119. return false;
  120. }
  121. const SkIPoint border = blurFilter.blur(src, dst);
  122. // If src.fImage is null, then this call is only to calculate the border.
  123. if (src.fImage != nullptr && dst->fImage == nullptr) {
  124. return false;
  125. }
  126. if (margin != nullptr) {
  127. *margin = border;
  128. }
  129. if (src.fImage == nullptr) {
  130. if (style == kInner_SkBlurStyle) {
  131. dst->fBounds = src.fBounds; // restore trimmed bounds
  132. dst->fRowBytes = dst->fBounds.width();
  133. }
  134. return true;
  135. }
  136. switch (style) {
  137. case kNormal_SkBlurStyle:
  138. break;
  139. case kSolid_SkBlurStyle: {
  140. auto dstStart = &dst->fImage[border.x() + border.y() * dst->fRowBytes];
  141. switch (src.fFormat) {
  142. case SkMask::kBW_Format:
  143. clamp_solid_with_orig(
  144. dstStart, dst->fRowBytes,
  145. SkMask::AlphaIter<SkMask::kBW_Format>(src.fImage, 0), src.fRowBytes,
  146. src.fBounds.width(), src.fBounds.height());
  147. break;
  148. case SkMask::kA8_Format:
  149. clamp_solid_with_orig(
  150. dstStart, dst->fRowBytes,
  151. SkMask::AlphaIter<SkMask::kA8_Format>(src.fImage), src.fRowBytes,
  152. src.fBounds.width(), src.fBounds.height());
  153. break;
  154. case SkMask::kARGB32_Format: {
  155. uint32_t* srcARGB = reinterpret_cast<uint32_t*>(src.fImage);
  156. clamp_solid_with_orig(
  157. dstStart, dst->fRowBytes,
  158. SkMask::AlphaIter<SkMask::kARGB32_Format>(srcARGB), src.fRowBytes,
  159. src.fBounds.width(), src.fBounds.height());
  160. } break;
  161. case SkMask::kLCD16_Format: {
  162. uint16_t* srcLCD = reinterpret_cast<uint16_t*>(src.fImage);
  163. clamp_solid_with_orig(
  164. dstStart, dst->fRowBytes,
  165. SkMask::AlphaIter<SkMask::kLCD16_Format>(srcLCD), src.fRowBytes,
  166. src.fBounds.width(), src.fBounds.height());
  167. } break;
  168. default:
  169. SK_ABORT("Unhandled format.");
  170. }
  171. } break;
  172. case kOuter_SkBlurStyle: {
  173. auto dstStart = &dst->fImage[border.x() + border.y() * dst->fRowBytes];
  174. switch (src.fFormat) {
  175. case SkMask::kBW_Format:
  176. clamp_outer_with_orig(
  177. dstStart, dst->fRowBytes,
  178. SkMask::AlphaIter<SkMask::kBW_Format>(src.fImage, 0), src.fRowBytes,
  179. src.fBounds.width(), src.fBounds.height());
  180. break;
  181. case SkMask::kA8_Format:
  182. clamp_outer_with_orig(
  183. dstStart, dst->fRowBytes,
  184. SkMask::AlphaIter<SkMask::kA8_Format>(src.fImage), src.fRowBytes,
  185. src.fBounds.width(), src.fBounds.height());
  186. break;
  187. case SkMask::kARGB32_Format: {
  188. uint32_t* srcARGB = reinterpret_cast<uint32_t*>(src.fImage);
  189. clamp_outer_with_orig(
  190. dstStart, dst->fRowBytes,
  191. SkMask::AlphaIter<SkMask::kARGB32_Format>(srcARGB), src.fRowBytes,
  192. src.fBounds.width(), src.fBounds.height());
  193. } break;
  194. case SkMask::kLCD16_Format: {
  195. uint16_t* srcLCD = reinterpret_cast<uint16_t*>(src.fImage);
  196. clamp_outer_with_orig(
  197. dstStart, dst->fRowBytes,
  198. SkMask::AlphaIter<SkMask::kLCD16_Format>(srcLCD), src.fRowBytes,
  199. src.fBounds.width(), src.fBounds.height());
  200. } break;
  201. default:
  202. SK_ABORT("Unhandled format.");
  203. }
  204. } break;
  205. case kInner_SkBlurStyle: {
  206. // now we allocate the "real" dst, mirror the size of src
  207. SkMask blur = *dst;
  208. SkAutoMaskFreeImage autoFreeBlurMask(blur.fImage);
  209. dst->fBounds = src.fBounds;
  210. dst->fRowBytes = dst->fBounds.width();
  211. size_t dstSize = dst->computeImageSize();
  212. if (0 == dstSize) {
  213. return false; // too big to allocate, abort
  214. }
  215. dst->fImage = SkMask::AllocImage(dstSize);
  216. auto blurStart = &blur.fImage[border.x() + border.y() * blur.fRowBytes];
  217. switch (src.fFormat) {
  218. case SkMask::kBW_Format:
  219. merge_src_with_blur(
  220. dst->fImage, dst->fRowBytes,
  221. SkMask::AlphaIter<SkMask::kBW_Format>(src.fImage, 0), src.fRowBytes,
  222. blurStart, blur.fRowBytes,
  223. src.fBounds.width(), src.fBounds.height());
  224. break;
  225. case SkMask::kA8_Format:
  226. merge_src_with_blur(
  227. dst->fImage, dst->fRowBytes,
  228. SkMask::AlphaIter<SkMask::kA8_Format>(src.fImage), src.fRowBytes,
  229. blurStart, blur.fRowBytes,
  230. src.fBounds.width(), src.fBounds.height());
  231. break;
  232. case SkMask::kARGB32_Format: {
  233. uint32_t* srcARGB = reinterpret_cast<uint32_t*>(src.fImage);
  234. merge_src_with_blur(
  235. dst->fImage, dst->fRowBytes,
  236. SkMask::AlphaIter<SkMask::kARGB32_Format>(srcARGB), src.fRowBytes,
  237. blurStart, blur.fRowBytes,
  238. src.fBounds.width(), src.fBounds.height());
  239. } break;
  240. case SkMask::kLCD16_Format: {
  241. uint16_t* srcLCD = reinterpret_cast<uint16_t*>(src.fImage);
  242. merge_src_with_blur(
  243. dst->fImage, dst->fRowBytes,
  244. SkMask::AlphaIter<SkMask::kLCD16_Format>(srcLCD), src.fRowBytes,
  245. blurStart, blur.fRowBytes,
  246. src.fBounds.width(), src.fBounds.height());
  247. } break;
  248. default:
  249. SK_ABORT("Unhandled format.");
  250. }
  251. } break;
  252. }
  253. return true;
  254. }
  255. /* Convolving a box with itself three times results in a piecewise
  256. quadratic function:
  257. 0 x <= -1.5
  258. 9/8 + 3/2 x + 1/2 x^2 -1.5 < x <= -.5
  259. 3/4 - x^2 -.5 < x <= .5
  260. 9/8 - 3/2 x + 1/2 x^2 0.5 < x <= 1.5
  261. 0 1.5 < x
  262. Mathematica:
  263. g[x_] := Piecewise [ {
  264. {9/8 + 3/2 x + 1/2 x^2 , -1.5 < x <= -.5},
  265. {3/4 - x^2 , -.5 < x <= .5},
  266. {9/8 - 3/2 x + 1/2 x^2 , 0.5 < x <= 1.5}
  267. }, 0]
  268. To get the profile curve of the blurred step function at the rectangle
  269. edge, we evaluate the indefinite integral, which is piecewise cubic:
  270. 0 x <= -1.5
  271. 9/16 + 9/8 x + 3/4 x^2 + 1/6 x^3 -1.5 < x <= -0.5
  272. 1/2 + 3/4 x - 1/3 x^3 -.5 < x <= .5
  273. 7/16 + 9/8 x - 3/4 x^2 + 1/6 x^3 .5 < x <= 1.5
  274. 1 1.5 < x
  275. in Mathematica code:
  276. gi[x_] := Piecewise[ {
  277. { 0 , x <= -1.5 },
  278. { 9/16 + 9/8 x + 3/4 x^2 + 1/6 x^3, -1.5 < x <= -0.5 },
  279. { 1/2 + 3/4 x - 1/3 x^3 , -.5 < x <= .5},
  280. { 7/16 + 9/8 x - 3/4 x^2 + 1/6 x^3, .5 < x <= 1.5}
  281. },1]
  282. */
  283. static float gaussianIntegral(float x) {
  284. if (x > 1.5f) {
  285. return 0.0f;
  286. }
  287. if (x < -1.5f) {
  288. return 1.0f;
  289. }
  290. float x2 = x*x;
  291. float x3 = x2*x;
  292. if ( x > 0.5f ) {
  293. return 0.5625f - (x3 / 6.0f - 3.0f * x2 * 0.25f + 1.125f * x);
  294. }
  295. if ( x > -0.5f ) {
  296. return 0.5f - (0.75f * x - x3 / 3.0f);
  297. }
  298. return 0.4375f + (-x3 / 6.0f - 3.0f * x2 * 0.25f - 1.125f * x);
  299. }
  300. /* ComputeBlurProfile fills in an array of floating
  301. point values between 0 and 255 for the profile signature of
  302. a blurred half-plane with the given blur radius. Since we're
  303. going to be doing screened multiplications (i.e., 1 - (1-x)(1-y))
  304. all the time, we actually fill in the profile pre-inverted
  305. (already done 255-x).
  306. */
  307. void SkBlurMask::ComputeBlurProfile(uint8_t* profile, int size, SkScalar sigma) {
  308. SkASSERT(SkScalarCeilToInt(6*sigma) == size);
  309. int center = size >> 1;
  310. float invr = 1.f/(2*sigma);
  311. profile[0] = 255;
  312. for (int x = 1 ; x < size ; ++x) {
  313. float scaled_x = (center - x - .5f) * invr;
  314. float gi = gaussianIntegral(scaled_x);
  315. profile[x] = 255 - (uint8_t) (255.f * gi);
  316. }
  317. }
  318. // TODO MAYBE: Maintain a profile cache to avoid recomputing this for
  319. // commonly used radii. Consider baking some of the most common blur radii
  320. // directly in as static data?
  321. // Implementation adapted from Michael Herf's approach:
  322. // http://stereopsis.com/shadowrect/
  323. uint8_t SkBlurMask::ProfileLookup(const uint8_t *profile, int loc,
  324. int blurredWidth, int sharpWidth) {
  325. // how far are we from the original edge?
  326. int dx = SkAbs32(((loc << 1) + 1) - blurredWidth) - sharpWidth;
  327. int ox = dx >> 1;
  328. if (ox < 0) {
  329. ox = 0;
  330. }
  331. return profile[ox];
  332. }
  333. void SkBlurMask::ComputeBlurredScanline(uint8_t *pixels, const uint8_t *profile,
  334. unsigned int width, SkScalar sigma) {
  335. unsigned int profile_size = SkScalarCeilToInt(6*sigma);
  336. SkAutoTMalloc<uint8_t> horizontalScanline(width);
  337. unsigned int sw = width - profile_size;
  338. // nearest odd number less than the profile size represents the center
  339. // of the (2x scaled) profile
  340. int center = ( profile_size & ~1 ) - 1;
  341. int w = sw - center;
  342. for (unsigned int x = 0 ; x < width ; ++x) {
  343. if (profile_size <= sw) {
  344. pixels[x] = ProfileLookup(profile, x, width, w);
  345. } else {
  346. float span = float(sw)/(2*sigma);
  347. float giX = 1.5f - (x+.5f)/(2*sigma);
  348. pixels[x] = (uint8_t) (255 * (gaussianIntegral(giX) - gaussianIntegral(giX + span)));
  349. }
  350. }
  351. }
  352. bool SkBlurMask::BlurRect(SkScalar sigma, SkMask *dst,
  353. const SkRect &src, SkBlurStyle style,
  354. SkIPoint *margin, SkMask::CreateMode createMode) {
  355. int profileSize = SkScalarCeilToInt(6*sigma);
  356. if (profileSize <= 0) {
  357. return false; // no blur to compute
  358. }
  359. int pad = profileSize/2;
  360. if (margin) {
  361. margin->set( pad, pad );
  362. }
  363. dst->fBounds.set(SkScalarRoundToInt(src.fLeft - pad),
  364. SkScalarRoundToInt(src.fTop - pad),
  365. SkScalarRoundToInt(src.fRight + pad),
  366. SkScalarRoundToInt(src.fBottom + pad));
  367. dst->fRowBytes = dst->fBounds.width();
  368. dst->fFormat = SkMask::kA8_Format;
  369. dst->fImage = nullptr;
  370. int sw = SkScalarFloorToInt(src.width());
  371. int sh = SkScalarFloorToInt(src.height());
  372. if (createMode == SkMask::kJustComputeBounds_CreateMode) {
  373. if (style == kInner_SkBlurStyle) {
  374. dst->fBounds.set(SkScalarRoundToInt(src.fLeft),
  375. SkScalarRoundToInt(src.fTop),
  376. SkScalarRoundToInt(src.fRight),
  377. SkScalarRoundToInt(src.fBottom)); // restore trimmed bounds
  378. dst->fRowBytes = sw;
  379. }
  380. return true;
  381. }
  382. SkAutoTMalloc<uint8_t> profile(profileSize);
  383. ComputeBlurProfile(profile, profileSize, sigma);
  384. size_t dstSize = dst->computeImageSize();
  385. if (0 == dstSize) {
  386. return false; // too big to allocate, abort
  387. }
  388. uint8_t* dp = SkMask::AllocImage(dstSize);
  389. dst->fImage = dp;
  390. int dstHeight = dst->fBounds.height();
  391. int dstWidth = dst->fBounds.width();
  392. uint8_t *outptr = dp;
  393. SkAutoTMalloc<uint8_t> horizontalScanline(dstWidth);
  394. SkAutoTMalloc<uint8_t> verticalScanline(dstHeight);
  395. ComputeBlurredScanline(horizontalScanline, profile, dstWidth, sigma);
  396. ComputeBlurredScanline(verticalScanline, profile, dstHeight, sigma);
  397. for (int y = 0 ; y < dstHeight ; ++y) {
  398. for (int x = 0 ; x < dstWidth ; x++) {
  399. unsigned int maskval = SkMulDiv255Round(horizontalScanline[x], verticalScanline[y]);
  400. *(outptr++) = maskval;
  401. }
  402. }
  403. if (style == kInner_SkBlurStyle) {
  404. // now we allocate the "real" dst, mirror the size of src
  405. size_t srcSize = (size_t)(src.width() * src.height());
  406. if (0 == srcSize) {
  407. return false; // too big to allocate, abort
  408. }
  409. dst->fImage = SkMask::AllocImage(srcSize);
  410. for (int y = 0 ; y < sh ; y++) {
  411. uint8_t *blur_scanline = dp + (y+pad)*dstWidth + pad;
  412. uint8_t *inner_scanline = dst->fImage + y*sw;
  413. memcpy(inner_scanline, blur_scanline, sw);
  414. }
  415. SkMask::FreeImage(dp);
  416. dst->fBounds.set(SkScalarRoundToInt(src.fLeft),
  417. SkScalarRoundToInt(src.fTop),
  418. SkScalarRoundToInt(src.fRight),
  419. SkScalarRoundToInt(src.fBottom)); // restore trimmed bounds
  420. dst->fRowBytes = sw;
  421. } else if (style == kOuter_SkBlurStyle) {
  422. for (int y = pad ; y < dstHeight-pad ; y++) {
  423. uint8_t *dst_scanline = dp + y*dstWidth + pad;
  424. memset(dst_scanline, 0, sw);
  425. }
  426. } else if (style == kSolid_SkBlurStyle) {
  427. for (int y = pad ; y < dstHeight-pad ; y++) {
  428. uint8_t *dst_scanline = dp + y*dstWidth + pad;
  429. memset(dst_scanline, 0xff, sw);
  430. }
  431. }
  432. // normal and solid styles are the same for analytic rect blurs, so don't
  433. // need to handle solid specially.
  434. return true;
  435. }
  436. bool SkBlurMask::BlurRRect(SkScalar sigma, SkMask *dst,
  437. const SkRRect &src, SkBlurStyle style,
  438. SkIPoint *margin, SkMask::CreateMode createMode) {
  439. // Temporary for now -- always fail, should cause caller to fall back
  440. // to old path. Plumbing just to land API and parallelize effort.
  441. return false;
  442. }
  443. // The "simple" blur is a direct implementation of separable convolution with a discrete
  444. // gaussian kernel. It's "ground truth" in a sense; too slow to be used, but very
  445. // useful for correctness comparisons.
  446. bool SkBlurMask::BlurGroundTruth(SkScalar sigma, SkMask* dst, const SkMask& src,
  447. SkBlurStyle style, SkIPoint* margin) {
  448. if (src.fFormat != SkMask::kA8_Format) {
  449. return false;
  450. }
  451. float variance = sigma * sigma;
  452. int windowSize = SkScalarCeilToInt(sigma*6);
  453. // round window size up to nearest odd number
  454. windowSize |= 1;
  455. SkAutoTMalloc<float> gaussWindow(windowSize);
  456. int halfWindow = windowSize >> 1;
  457. gaussWindow[halfWindow] = 1;
  458. float windowSum = 1;
  459. for (int x = 1 ; x <= halfWindow ; ++x) {
  460. float gaussian = expf(-x*x / (2*variance));
  461. gaussWindow[halfWindow + x] = gaussWindow[halfWindow-x] = gaussian;
  462. windowSum += 2*gaussian;
  463. }
  464. // leave the filter un-normalized for now; we will divide by the normalization
  465. // sum later;
  466. int pad = halfWindow;
  467. if (margin) {
  468. margin->set( pad, pad );
  469. }
  470. dst->fBounds = src.fBounds;
  471. dst->fBounds.outset(pad, pad);
  472. dst->fRowBytes = dst->fBounds.width();
  473. dst->fFormat = SkMask::kA8_Format;
  474. dst->fImage = nullptr;
  475. if (src.fImage) {
  476. size_t dstSize = dst->computeImageSize();
  477. if (0 == dstSize) {
  478. return false; // too big to allocate, abort
  479. }
  480. int srcWidth = src.fBounds.width();
  481. int srcHeight = src.fBounds.height();
  482. int dstWidth = dst->fBounds.width();
  483. const uint8_t* srcPixels = src.fImage;
  484. uint8_t* dstPixels = SkMask::AllocImage(dstSize);
  485. SkAutoMaskFreeImage autoFreeDstPixels(dstPixels);
  486. // do the actual blur. First, make a padded copy of the source.
  487. // use double pad so we never have to check if we're outside anything
  488. int padWidth = srcWidth + 4*pad;
  489. int padHeight = srcHeight;
  490. int padSize = padWidth * padHeight;
  491. SkAutoTMalloc<uint8_t> padPixels(padSize);
  492. memset(padPixels, 0, padSize);
  493. for (int y = 0 ; y < srcHeight; ++y) {
  494. uint8_t* padptr = padPixels + y * padWidth + 2*pad;
  495. const uint8_t* srcptr = srcPixels + y * srcWidth;
  496. memcpy(padptr, srcptr, srcWidth);
  497. }
  498. // blur in X, transposing the result into a temporary floating point buffer.
  499. // also double-pad the intermediate result so that the second blur doesn't
  500. // have to do extra conditionals.
  501. int tmpWidth = padHeight + 4*pad;
  502. int tmpHeight = padWidth - 2*pad;
  503. int tmpSize = tmpWidth * tmpHeight;
  504. SkAutoTMalloc<float> tmpImage(tmpSize);
  505. memset(tmpImage, 0, tmpSize*sizeof(tmpImage[0]));
  506. for (int y = 0 ; y < padHeight ; ++y) {
  507. uint8_t *srcScanline = padPixels + y*padWidth;
  508. for (int x = pad ; x < padWidth - pad ; ++x) {
  509. float *outPixel = tmpImage + (x-pad)*tmpWidth + y + 2*pad; // transposed output
  510. uint8_t *windowCenter = srcScanline + x;
  511. for (int i = -pad ; i <= pad ; ++i) {
  512. *outPixel += gaussWindow[pad+i]*windowCenter[i];
  513. }
  514. *outPixel /= windowSum;
  515. }
  516. }
  517. // blur in Y; now filling in the actual desired destination. We have to do
  518. // the transpose again; these transposes guarantee that we read memory in
  519. // linear order.
  520. for (int y = 0 ; y < tmpHeight ; ++y) {
  521. float *srcScanline = tmpImage + y*tmpWidth;
  522. for (int x = pad ; x < tmpWidth - pad ; ++x) {
  523. float *windowCenter = srcScanline + x;
  524. float finalValue = 0;
  525. for (int i = -pad ; i <= pad ; ++i) {
  526. finalValue += gaussWindow[pad+i]*windowCenter[i];
  527. }
  528. finalValue /= windowSum;
  529. uint8_t *outPixel = dstPixels + (x-pad)*dstWidth + y; // transposed output
  530. int integerPixel = int(finalValue + 0.5f);
  531. *outPixel = SkClampMax( SkClampPos(integerPixel), 255 );
  532. }
  533. }
  534. dst->fImage = dstPixels;
  535. switch (style) {
  536. case kNormal_SkBlurStyle:
  537. break;
  538. case kSolid_SkBlurStyle: {
  539. clamp_solid_with_orig(
  540. dstPixels + pad*dst->fRowBytes + pad, dst->fRowBytes,
  541. SkMask::AlphaIter<SkMask::kA8_Format>(srcPixels), src.fRowBytes,
  542. srcWidth, srcHeight);
  543. } break;
  544. case kOuter_SkBlurStyle: {
  545. clamp_outer_with_orig(
  546. dstPixels + pad*dst->fRowBytes + pad, dst->fRowBytes,
  547. SkMask::AlphaIter<SkMask::kA8_Format>(srcPixels), src.fRowBytes,
  548. srcWidth, srcHeight);
  549. } break;
  550. case kInner_SkBlurStyle: {
  551. // now we allocate the "real" dst, mirror the size of src
  552. size_t srcSize = src.computeImageSize();
  553. if (0 == srcSize) {
  554. return false; // too big to allocate, abort
  555. }
  556. dst->fImage = SkMask::AllocImage(srcSize);
  557. merge_src_with_blur(dst->fImage, src.fRowBytes,
  558. SkMask::AlphaIter<SkMask::kA8_Format>(srcPixels), src.fRowBytes,
  559. dstPixels + pad*dst->fRowBytes + pad,
  560. dst->fRowBytes, srcWidth, srcHeight);
  561. SkMask::FreeImage(dstPixels);
  562. } break;
  563. }
  564. autoFreeDstPixels.release();
  565. }
  566. if (style == kInner_SkBlurStyle) {
  567. dst->fBounds = src.fBounds; // restore trimmed bounds
  568. dst->fRowBytes = src.fRowBytes;
  569. }
  570. return true;
  571. }