SkMaskBlurFilter.cpp 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049
  1. /*
  2. * Copyright 2017 Google Inc.
  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/SkMaskBlurFilter.h"
  8. #include "include/core/SkColorPriv.h"
  9. #include "include/private/SkMalloc.h"
  10. #include "include/private/SkNx.h"
  11. #include "include/private/SkTemplates.h"
  12. #include "include/private/SkTo.h"
  13. #include "src/core/SkArenaAlloc.h"
  14. #include "src/core/SkGaussFilter.h"
  15. #include <cmath>
  16. #include <climits>
  17. namespace {
  18. static const double kPi = 3.14159265358979323846264338327950288;
  19. class PlanGauss final {
  20. public:
  21. explicit PlanGauss(double sigma) {
  22. auto possibleWindow = static_cast<int>(floor(sigma * 3 * sqrt(2 * kPi) / 4 + 0.5));
  23. auto window = std::max(1, possibleWindow);
  24. fPass0Size = window - 1;
  25. fPass1Size = window - 1;
  26. fPass2Size = (window & 1) == 1 ? window - 1 : window;
  27. // Calculating the border is tricky. I will go through the odd case which is simpler, and
  28. // then through the even case. Given a stack of filters seven wide for the odd case of
  29. // three passes.
  30. //
  31. // S
  32. // aaaAaaa
  33. // bbbBbbb
  34. // cccCccc
  35. // D
  36. //
  37. // The furthest changed pixel is when the filters are in the following configuration.
  38. //
  39. // S
  40. // aaaAaaa
  41. // bbbBbbb
  42. // cccCccc
  43. // D
  44. //
  45. // The A pixel is calculated using the value S, the B uses A, and the C uses B, and
  46. // finally D is C. So, with a window size of seven the border is nine. In general, the
  47. // border is 3*((window - 1)/2).
  48. //
  49. // For even cases the filter stack is more complicated. The spec specifies two passes
  50. // of even filters and a final pass of odd filters. A stack for a width of six looks like
  51. // this.
  52. //
  53. // S
  54. // aaaAaa
  55. // bbBbbb
  56. // cccCccc
  57. // D
  58. //
  59. // The furthest pixel looks like this.
  60. //
  61. // S
  62. // aaaAaa
  63. // bbBbbb
  64. // cccCccc
  65. // D
  66. //
  67. // For a window of size, the border value is seven. In general the border is 3 *
  68. // (window/2) -1.
  69. fBorder = (window & 1) == 1 ? 3 * ((window - 1) / 2) : 3 * (window / 2) - 1;
  70. fSlidingWindow = 2 * fBorder + 1;
  71. // If the window is odd then the divisor is just window ^ 3 otherwise,
  72. // it is window * window * (window + 1) = window ^ 2 + window ^ 3;
  73. auto window2 = window * window;
  74. auto window3 = window2 * window;
  75. auto divisor = (window & 1) == 1 ? window3 : window3 + window2;
  76. fWeight = static_cast<uint64_t>(round(1.0 / divisor * (1ull << 32)));
  77. }
  78. size_t bufferSize() const { return fPass0Size + fPass1Size + fPass2Size; }
  79. int border() const { return fBorder; }
  80. public:
  81. class Scan {
  82. public:
  83. Scan(uint64_t weight, int noChangeCount,
  84. uint32_t* buffer0, uint32_t* buffer0End,
  85. uint32_t* buffer1, uint32_t* buffer1End,
  86. uint32_t* buffer2, uint32_t* buffer2End)
  87. : fWeight{weight}
  88. , fNoChangeCount{noChangeCount}
  89. , fBuffer0{buffer0}
  90. , fBuffer0End{buffer0End}
  91. , fBuffer1{buffer1}
  92. , fBuffer1End{buffer1End}
  93. , fBuffer2{buffer2}
  94. , fBuffer2End{buffer2End}
  95. { }
  96. template <typename AlphaIter> void blur(const AlphaIter srcBegin, const AlphaIter srcEnd,
  97. uint8_t* dst, int dstStride, uint8_t* dstEnd) const {
  98. auto buffer0Cursor = fBuffer0;
  99. auto buffer1Cursor = fBuffer1;
  100. auto buffer2Cursor = fBuffer2;
  101. std::memset(fBuffer0, 0x00, (fBuffer2End - fBuffer0) * sizeof(*fBuffer0));
  102. uint32_t sum0 = 0;
  103. uint32_t sum1 = 0;
  104. uint32_t sum2 = 0;
  105. // Consume the source generating pixels.
  106. for (AlphaIter src = srcBegin; src < srcEnd; ++src, dst += dstStride) {
  107. uint32_t leadingEdge = *src;
  108. sum0 += leadingEdge;
  109. sum1 += sum0;
  110. sum2 += sum1;
  111. *dst = this->finalScale(sum2);
  112. sum2 -= *buffer2Cursor;
  113. *buffer2Cursor = sum1;
  114. buffer2Cursor = (buffer2Cursor + 1) < fBuffer2End ? buffer2Cursor + 1 : fBuffer2;
  115. sum1 -= *buffer1Cursor;
  116. *buffer1Cursor = sum0;
  117. buffer1Cursor = (buffer1Cursor + 1) < fBuffer1End ? buffer1Cursor + 1 : fBuffer1;
  118. sum0 -= *buffer0Cursor;
  119. *buffer0Cursor = leadingEdge;
  120. buffer0Cursor = (buffer0Cursor + 1) < fBuffer0End ? buffer0Cursor + 1 : fBuffer0;
  121. }
  122. // The leading edge is off the right side of the mask.
  123. for (int i = 0; i < fNoChangeCount; i++) {
  124. uint32_t leadingEdge = 0;
  125. sum0 += leadingEdge;
  126. sum1 += sum0;
  127. sum2 += sum1;
  128. *dst = this->finalScale(sum2);
  129. sum2 -= *buffer2Cursor;
  130. *buffer2Cursor = sum1;
  131. buffer2Cursor = (buffer2Cursor + 1) < fBuffer2End ? buffer2Cursor + 1 : fBuffer2;
  132. sum1 -= *buffer1Cursor;
  133. *buffer1Cursor = sum0;
  134. buffer1Cursor = (buffer1Cursor + 1) < fBuffer1End ? buffer1Cursor + 1 : fBuffer1;
  135. sum0 -= *buffer0Cursor;
  136. *buffer0Cursor = leadingEdge;
  137. buffer0Cursor = (buffer0Cursor + 1) < fBuffer0End ? buffer0Cursor + 1 : fBuffer0;
  138. dst += dstStride;
  139. }
  140. // Starting from the right, fill in the rest of the buffer.
  141. std::memset(fBuffer0, 0, (fBuffer2End - fBuffer0) * sizeof(*fBuffer0));
  142. sum0 = sum1 = sum2 = 0;
  143. uint8_t* dstCursor = dstEnd;
  144. AlphaIter src = srcEnd;
  145. while (dstCursor > dst) {
  146. dstCursor -= dstStride;
  147. uint32_t leadingEdge = *(--src);
  148. sum0 += leadingEdge;
  149. sum1 += sum0;
  150. sum2 += sum1;
  151. *dstCursor = this->finalScale(sum2);
  152. sum2 -= *buffer2Cursor;
  153. *buffer2Cursor = sum1;
  154. buffer2Cursor = (buffer2Cursor + 1) < fBuffer2End ? buffer2Cursor + 1 : fBuffer2;
  155. sum1 -= *buffer1Cursor;
  156. *buffer1Cursor = sum0;
  157. buffer1Cursor = (buffer1Cursor + 1) < fBuffer1End ? buffer1Cursor + 1 : fBuffer1;
  158. sum0 -= *buffer0Cursor;
  159. *buffer0Cursor = leadingEdge;
  160. buffer0Cursor = (buffer0Cursor + 1) < fBuffer0End ? buffer0Cursor + 1 : fBuffer0;
  161. }
  162. }
  163. private:
  164. static constexpr uint64_t kHalf = static_cast<uint64_t>(1) << 31;
  165. uint8_t finalScale(uint32_t sum) const {
  166. return SkTo<uint8_t>((fWeight * sum + kHalf) >> 32);
  167. }
  168. uint64_t fWeight;
  169. int fNoChangeCount;
  170. uint32_t* fBuffer0;
  171. uint32_t* fBuffer0End;
  172. uint32_t* fBuffer1;
  173. uint32_t* fBuffer1End;
  174. uint32_t* fBuffer2;
  175. uint32_t* fBuffer2End;
  176. };
  177. Scan makeBlurScan(int width, uint32_t* buffer) const {
  178. uint32_t* buffer0, *buffer0End, *buffer1, *buffer1End, *buffer2, *buffer2End;
  179. buffer0 = buffer;
  180. buffer0End = buffer1 = buffer0 + fPass0Size;
  181. buffer1End = buffer2 = buffer1 + fPass1Size;
  182. buffer2End = buffer2 + fPass2Size;
  183. int noChangeCount = fSlidingWindow > width ? fSlidingWindow - width : 0;
  184. return Scan(
  185. fWeight, noChangeCount,
  186. buffer0, buffer0End,
  187. buffer1, buffer1End,
  188. buffer2, buffer2End);
  189. }
  190. uint64_t fWeight;
  191. int fBorder;
  192. int fSlidingWindow;
  193. int fPass0Size;
  194. int fPass1Size;
  195. int fPass2Size;
  196. };
  197. } // namespace
  198. // NB 136 is the largest sigma that will not cause a buffer full of 255 mask values to overflow
  199. // using the Gauss filter. It also limits the size of buffers used hold intermediate values.
  200. // Explanation of maximums:
  201. // sum0 = window * 255
  202. // sum1 = window * sum0 -> window * window * 255
  203. // sum2 = window * sum1 -> window * window * window * 255 -> window^3 * 255
  204. //
  205. // The value window^3 * 255 must fit in a uint32_t. So,
  206. // window^3 < 2^32. window = 255.
  207. //
  208. // window = floor(sigma * 3 * sqrt(2 * kPi) / 4 + 0.5)
  209. // For window <= 255, the largest value for sigma is 136.
  210. SkMaskBlurFilter::SkMaskBlurFilter(double sigmaW, double sigmaH)
  211. : fSigmaW{SkTPin(sigmaW, 0.0, 136.0)}
  212. , fSigmaH{SkTPin(sigmaH, 0.0, 136.0)}
  213. {
  214. SkASSERT(sigmaW >= 0);
  215. SkASSERT(sigmaH >= 0);
  216. }
  217. bool SkMaskBlurFilter::hasNoBlur() const {
  218. return (3 * fSigmaW <= 1) && (3 * fSigmaH <= 1);
  219. }
  220. // We favor A8 masks, and if we need to work with another format, we'll convert to A8 first.
  221. // Each of these converts width (up to 8) mask values to A8.
  222. static void bw_to_a8(uint8_t* a8, const uint8_t* from, int width) {
  223. SkASSERT(0 < width && width <= 8);
  224. uint8_t masks = *from;
  225. for (int i = 0; i < width; ++i) {
  226. a8[i] = (masks >> (7 - i)) & 1 ? 0xFF
  227. : 0x00;
  228. }
  229. }
  230. static void lcd_to_a8(uint8_t* a8, const uint8_t* from, int width) {
  231. SkASSERT(0 < width && width <= 8);
  232. for (int i = 0; i < width; ++i) {
  233. unsigned rgb = reinterpret_cast<const uint16_t*>(from)[i],
  234. r = SkPacked16ToR32(rgb),
  235. g = SkPacked16ToG32(rgb),
  236. b = SkPacked16ToB32(rgb);
  237. a8[i] = (r + g + b) / 3;
  238. }
  239. }
  240. static void argb32_to_a8(uint8_t* a8, const uint8_t* from, int width) {
  241. SkASSERT(0 < width && width <= 8);
  242. for (int i = 0; i < width; ++i) {
  243. uint32_t rgba = reinterpret_cast<const uint32_t*>(from)[i];
  244. a8[i] = SkGetPackedA32(rgba);
  245. }
  246. }
  247. using ToA8 = decltype(bw_to_a8);
  248. static Sk8h load(const uint8_t* from, int width, ToA8* toA8) {
  249. // Our fast path is a full 8-byte load of A8.
  250. // So we'll conditionally handle the two slow paths using tmp:
  251. // - if we have a function to convert another mask to A8, use it;
  252. // - if not but we have less than 8 bytes to load, load them one at a time.
  253. uint8_t tmp[8] = {0,0,0,0, 0,0,0,0};
  254. if (toA8) {
  255. toA8(tmp, from, width);
  256. from = tmp;
  257. } else if (width < 8) {
  258. for (int i = 0; i < width; ++i) {
  259. tmp[i] = from[i];
  260. }
  261. from = tmp;
  262. }
  263. // Load A8 and convert to 8.8 fixed-point.
  264. return SkNx_cast<uint16_t>(Sk8b::Load(from)) << 8;
  265. }
  266. static void store(uint8_t* to, const Sk8h& v, int width) {
  267. Sk8b b = SkNx_cast<uint8_t>(v >> 8);
  268. if (width == 8) {
  269. b.store(to);
  270. } else {
  271. uint8_t buffer[8];
  272. b.store(buffer);
  273. for (int i = 0; i < width; i++) {
  274. to[i] = buffer[i];
  275. }
  276. }
  277. };
  278. static constexpr uint16_t _____ = 0u;
  279. static constexpr uint16_t kHalf = 0x80u;
  280. // In all the blur_x_radius_N and blur_y_radius_N functions the gaussian values are encoded
  281. // in 0.16 format, none of the values is greater than one. The incoming mask values are in 8.8
  282. // format. The resulting multiply has a 8.24 format, by the mulhi truncates the lower 16 bits
  283. // resulting in a 8.8 format.
  284. //
  285. // The blur_x_radius_N function below blur along a row of pixels using a kernel with radius N. This
  286. // system is setup to minimize the number of multiplies needed.
  287. //
  288. // Explanation:
  289. // Blurring a specific mask value is given by the following equation where D_n is the resulting
  290. // mask value and S_n is the source value. The example below is for a filter with a radius of 1
  291. // and a width of 3 (radius == (width-1)/2). The indexes for the source and destination are
  292. // aligned. The filter is given by G_n where n is the symmetric filter value.
  293. //
  294. // D[n] = S[n-1]*G[1] + S[n]*G[0] + S[n+1]*G[1].
  295. //
  296. // We can start the source index at an offset relative to the destination separated by the
  297. // radius. This results in a non-traditional restating of the above filter.
  298. //
  299. // D[n] = S[n]*G[1] + S[n+1]*G[0] + S[n+2]*G[1]
  300. //
  301. // If we look at three specific consecutive destinations the following equations result:
  302. //
  303. // D[5] = S[5]*G[1] + S[6]*G[0] + S[7]*G[1]
  304. // D[7] = S[6]*G[1] + S[7]*G[0] + S[8]*G[1]
  305. // D[8] = S[7]*G[1] + S[8]*G[0] + S[9]*G[1].
  306. //
  307. // In the above equations, notice that S[7] is used in all three. In particular, two values are
  308. // used: S[7]*G[0] and S[7]*G[1]. So, S[7] is only multiplied twice, but used in D[5], D[6] and
  309. // D[7].
  310. //
  311. // From the point of view of a source value we end up with the following three equations.
  312. //
  313. // Given S[7]:
  314. // D[5] += S[7]*G[1]
  315. // D[6] += S[7]*G[0]
  316. // D[7] += S[7]*G[1]
  317. //
  318. // In General:
  319. // D[n] += S[n]*G[1]
  320. // D[n+1] += S[n]*G[0]
  321. // D[n+2] += S[n]*G[1]
  322. //
  323. // Now these equations can be ganged using SIMD to form:
  324. // D[n..n+7] += S[n..n+7]*G[1]
  325. // D[n+1..n+8] += S[n..n+7]*G[0]
  326. // D[n+2..n+9] += S[n..n+7]*G[1]
  327. // The next set of values becomes.
  328. // D[n+8..n+15] += S[n+8..n+15]*G[1]
  329. // D[n+9..n+16] += S[n+8..n+15]*G[0]
  330. // D[n+10..n+17] += S[n+8..n+15]*G[1]
  331. // You can see that the D[n+8] and D[n+9] values overlap the two sets, using parts of both
  332. // S[n..7] and S[n+8..n+15].
  333. //
  334. // Just one more transformation allows the code to maintain all working values in
  335. // registers. I introduce the notation {0, S[n..n+7] * G[k]} to mean that the value where 0 is
  336. // prepended to the array of values to form {0, S[n] * G[k], ..., S[n+7]*G[k]}.
  337. //
  338. // D[n..n+7] += S[n..n+7] * G[1]
  339. // D[n..n+8] += {0, S[n..n+7] * G[0]}
  340. // D[n..n+9] += {0, 0, S[n..n+7] * G[1]}
  341. //
  342. // Now we can encode D[n..n+7] in a single Sk8h register called d0, and D[n+8..n+15] in a
  343. // register d8. In addition, S[0..n+7] becomes s0.
  344. //
  345. // The translation of the {0, S[n..n+7] * G[k]} is translated in the following way below.
  346. //
  347. // Sk8h v0 = s0*G[0]
  348. // Sk8h v1 = s0*G[1]
  349. // /* D[n..n+7] += S[n..n+7] * G[1] */
  350. // d0 += v1;
  351. // /* D[n..n+8] += {0, S[n..n+7] * G[0]} */
  352. // d0 += {_____, v0[0], v0[1], v0[2], v0[3], v0[4], v0[5], v0[6]}
  353. // d1 += {v0[7], _____, _____, _____, _____, _____, _____, _____}
  354. // /* D[n..n+9] += {0, 0, S[n..n+7] * G[1]} */
  355. // d0 += {_____, _____, v1[0], v1[1], v1[2], v1[3], v1[4], v1[5]}
  356. // d1 += {v1[6], v1[7], _____, _____, _____, _____, _____, _____}
  357. // Where we rely on the compiler to generate efficient code for the {____, n, ....} notation.
  358. static void blur_x_radius_1(
  359. const Sk8h& s0,
  360. const Sk8h& g0, const Sk8h& g1, const Sk8h&, const Sk8h&, const Sk8h&,
  361. Sk8h* d0, Sk8h* d8) {
  362. auto v1 = s0.mulHi(g1);
  363. auto v0 = s0.mulHi(g0);
  364. // D[n..n+7] += S[n..n+7] * G[1]
  365. *d0 += v1;
  366. //D[n..n+8] += {0, S[n..n+7] * G[0]}
  367. *d0 += Sk8h{_____, v0[0], v0[1], v0[2], v0[3], v0[4], v0[5], v0[6]};
  368. *d8 += Sk8h{v0[7], _____, _____, _____, _____, _____, _____, _____};
  369. // D[n..n+9] += {0, 0, S[n..n+7] * G[1]}
  370. *d0 += Sk8h{_____, _____, v1[0], v1[1], v1[2], v1[3], v1[4], v1[5]};
  371. *d8 += Sk8h{v1[6], v1[7], _____, _____, _____, _____, _____, _____};
  372. }
  373. static void blur_x_radius_2(
  374. const Sk8h& s0,
  375. const Sk8h& g0, const Sk8h& g1, const Sk8h& g2, const Sk8h&, const Sk8h&,
  376. Sk8h* d0, Sk8h* d8) {
  377. auto v0 = s0.mulHi(g0);
  378. auto v1 = s0.mulHi(g1);
  379. auto v2 = s0.mulHi(g2);
  380. // D[n..n+7] += S[n..n+7] * G[2]
  381. *d0 += v2;
  382. // D[n..n+8] += {0, S[n..n+7] * G[1]}
  383. *d0 += Sk8h{_____, v1[0], v1[1], v1[2], v1[3], v1[4], v1[5], v1[6]};
  384. *d8 += Sk8h{v1[7], _____, _____, _____, _____, _____, _____, _____};
  385. // D[n..n+9] += {0, 0, S[n..n+7] * G[0]}
  386. *d0 += Sk8h{_____, _____, v0[0], v0[1], v0[2], v0[3], v0[4], v0[5]};
  387. *d8 += Sk8h{v0[6], v0[7], _____, _____, _____, _____, _____, _____};
  388. // D[n..n+10] += {0, 0, 0, S[n..n+7] * G[1]}
  389. *d0 += Sk8h{_____, _____, _____, v1[0], v1[1], v1[2], v1[3], v1[4]};
  390. *d8 += Sk8h{v1[5], v1[6], v1[7], _____, _____, _____, _____, _____};
  391. // D[n..n+11] += {0, 0, 0, 0, S[n..n+7] * G[2]}
  392. *d0 += Sk8h{_____, _____, _____, _____, v2[0], v2[1], v2[2], v2[3]};
  393. *d8 += Sk8h{v2[4], v2[5], v2[6], v2[7], _____, _____, _____, _____};
  394. }
  395. static void blur_x_radius_3(
  396. const Sk8h& s0,
  397. const Sk8h& gauss0, const Sk8h& gauss1, const Sk8h& gauss2, const Sk8h& gauss3, const Sk8h&,
  398. Sk8h* d0, Sk8h* d8) {
  399. auto v0 = s0.mulHi(gauss0);
  400. auto v1 = s0.mulHi(gauss1);
  401. auto v2 = s0.mulHi(gauss2);
  402. auto v3 = s0.mulHi(gauss3);
  403. // D[n..n+7] += S[n..n+7] * G[3]
  404. *d0 += v3;
  405. // D[n..n+8] += {0, S[n..n+7] * G[2]}
  406. *d0 += Sk8h{_____, v2[0], v2[1], v2[2], v2[3], v2[4], v2[5], v2[6]};
  407. *d8 += Sk8h{v2[7], _____, _____, _____, _____, _____, _____, _____};
  408. // D[n..n+9] += {0, 0, S[n..n+7] * G[1]}
  409. *d0 += Sk8h{_____, _____, v1[0], v1[1], v1[2], v1[3], v1[4], v1[5]};
  410. *d8 += Sk8h{v1[6], v1[7], _____, _____, _____, _____, _____, _____};
  411. // D[n..n+10] += {0, 0, 0, S[n..n+7] * G[0]}
  412. *d0 += Sk8h{_____, _____, _____, v0[0], v0[1], v0[2], v0[3], v0[4]};
  413. *d8 += Sk8h{v0[5], v0[6], v0[7], _____, _____, _____, _____, _____};
  414. // D[n..n+11] += {0, 0, 0, 0, S[n..n+7] * G[1]}
  415. *d0 += Sk8h{_____, _____, _____, _____, v1[0], v1[1], v1[2], v1[3]};
  416. *d8 += Sk8h{v1[4], v1[5], v1[6], v1[7], _____, _____, _____, _____};
  417. // D[n..n+12] += {0, 0, 0, 0, 0, S[n..n+7] * G[2]}
  418. *d0 += Sk8h{_____, _____, _____, _____, _____, v2[0], v2[1], v2[2]};
  419. *d8 += Sk8h{v2[3], v2[4], v2[5], v2[6], v2[7], _____, _____, _____};
  420. // D[n..n+13] += {0, 0, 0, 0, 0, 0, S[n..n+7] * G[3]}
  421. *d0 += Sk8h{_____, _____, _____, _____, _____, _____, v3[0], v3[1]};
  422. *d8 += Sk8h{v3[2], v3[3], v3[4], v3[5], v3[6], v3[7], _____, _____};
  423. }
  424. static void blur_x_radius_4(
  425. const Sk8h& s0,
  426. const Sk8h& gauss0,
  427. const Sk8h& gauss1,
  428. const Sk8h& gauss2,
  429. const Sk8h& gauss3,
  430. const Sk8h& gauss4,
  431. Sk8h* d0, Sk8h* d8) {
  432. auto v0 = s0.mulHi(gauss0);
  433. auto v1 = s0.mulHi(gauss1);
  434. auto v2 = s0.mulHi(gauss2);
  435. auto v3 = s0.mulHi(gauss3);
  436. auto v4 = s0.mulHi(gauss4);
  437. // D[n..n+7] += S[n..n+7] * G[4]
  438. *d0 += v4;
  439. // D[n..n+8] += {0, S[n..n+7] * G[3]}
  440. *d0 += Sk8h{_____, v3[0], v3[1], v3[2], v3[3], v3[4], v3[5], v3[6]};
  441. *d8 += Sk8h{v3[7], _____, _____, _____, _____, _____, _____, _____};
  442. // D[n..n+9] += {0, 0, S[n..n+7] * G[2]}
  443. *d0 += Sk8h{_____, _____, v2[0], v2[1], v2[2], v2[3], v2[4], v2[5]};
  444. *d8 += Sk8h{v2[6], v2[7], _____, _____, _____, _____, _____, _____};
  445. // D[n..n+10] += {0, 0, 0, S[n..n+7] * G[1]}
  446. *d0 += Sk8h{_____, _____, _____, v1[0], v1[1], v1[2], v1[3], v1[4]};
  447. *d8 += Sk8h{v1[5], v1[6], v1[7], _____, _____, _____, _____, _____};
  448. // D[n..n+11] += {0, 0, 0, 0, S[n..n+7] * G[0]}
  449. *d0 += Sk8h{_____, _____, _____, _____, v0[0], v0[1], v0[2], v0[3]};
  450. *d8 += Sk8h{v0[4], v0[5], v0[6], v0[7], _____, _____, _____, _____};
  451. // D[n..n+12] += {0, 0, 0, 0, 0, S[n..n+7] * G[1]}
  452. *d0 += Sk8h{_____, _____, _____, _____, _____, v1[0], v1[1], v1[2]};
  453. *d8 += Sk8h{v1[3], v1[4], v1[5], v1[6], v1[7], _____, _____, _____};
  454. // D[n..n+13] += {0, 0, 0, 0, 0, 0, S[n..n+7] * G[2]}
  455. *d0 += Sk8h{_____, _____, _____, _____, _____, _____, v2[0], v2[1]};
  456. *d8 += Sk8h{v2[2], v2[3], v2[4], v2[5], v2[6], v2[7], _____, _____};
  457. // D[n..n+14] += {0, 0, 0, 0, 0, 0, 0, S[n..n+7] * G[3]}
  458. *d0 += Sk8h{_____, _____, _____, _____, _____, _____, _____, v3[0]};
  459. *d8 += Sk8h{v3[1], v3[2], v3[3], v3[4], v3[5], v3[6], v3[7], _____};
  460. // D[n..n+15] += {0, 0, 0, 0, 0, 0, 0, 0, S[n..n+7] * G[4]}
  461. *d8 += v4;
  462. }
  463. using BlurX = decltype(blur_x_radius_1);
  464. // BlurX will only be one of the functions blur_x_radius_(1|2|3|4).
  465. static void blur_row(
  466. BlurX blur,
  467. const Sk8h& g0, const Sk8h& g1, const Sk8h& g2, const Sk8h& g3, const Sk8h& g4,
  468. const uint8_t* src, int srcW,
  469. uint8_t* dst, int dstW) {
  470. // Clear the buffer to handle summing wider than source.
  471. Sk8h d0{kHalf}, d8{kHalf};
  472. // Go by multiples of 8 in src.
  473. int x = 0;
  474. for (; x <= srcW - 8; x += 8) {
  475. blur(load(src, 8, nullptr), g0, g1, g2, g3, g4, &d0, &d8);
  476. store(dst, d0, 8);
  477. d0 = d8;
  478. d8 = Sk8h{kHalf};
  479. src += 8;
  480. dst += 8;
  481. }
  482. // There are src values left, but the remainder of src values is not a multiple of 8.
  483. int srcTail = srcW - x;
  484. if (srcTail > 0) {
  485. blur(load(src, srcTail, nullptr), g0, g1, g2, g3, g4, &d0, &d8);
  486. int dstTail = std::min(8, dstW - x);
  487. store(dst, d0, dstTail);
  488. d0 = d8;
  489. dst += dstTail;
  490. x += dstTail;
  491. }
  492. // There are dst mask values to complete.
  493. int dstTail = dstW - x;
  494. if (dstTail > 0) {
  495. store(dst, d0, dstTail);
  496. }
  497. }
  498. // BlurX will only be one of the functions blur_x_radius_(1|2|3|4).
  499. static void blur_x_rect(BlurX blur,
  500. uint16_t* gauss,
  501. const uint8_t* src, size_t srcStride, int srcW,
  502. uint8_t* dst, size_t dstStride, int dstW, int dstH) {
  503. Sk8h g0{gauss[0]},
  504. g1{gauss[1]},
  505. g2{gauss[2]},
  506. g3{gauss[3]},
  507. g4{gauss[4]};
  508. // Blur *ALL* the rows.
  509. for (int y = 0; y < dstH; y++) {
  510. blur_row(blur, g0, g1, g2, g3, g4, src, srcW, dst, dstW);
  511. src += srcStride;
  512. dst += dstStride;
  513. }
  514. }
  515. static void direct_blur_x(int radius, uint16_t* gauss,
  516. const uint8_t* src, size_t srcStride, int srcW,
  517. uint8_t* dst, size_t dstStride, int dstW, int dstH) {
  518. switch (radius) {
  519. case 1:
  520. blur_x_rect(blur_x_radius_1, gauss, src, srcStride, srcW, dst, dstStride, dstW, dstH);
  521. break;
  522. case 2:
  523. blur_x_rect(blur_x_radius_2, gauss, src, srcStride, srcW, dst, dstStride, dstW, dstH);
  524. break;
  525. case 3:
  526. blur_x_rect(blur_x_radius_3, gauss, src, srcStride, srcW, dst, dstStride, dstW, dstH);
  527. break;
  528. case 4:
  529. blur_x_rect(blur_x_radius_4, gauss, src, srcStride, srcW, dst, dstStride, dstW, dstH);
  530. break;
  531. default:
  532. SkASSERTF(false, "The radius %d is not handled\n", radius);
  533. }
  534. }
  535. // The operations of the blur_y_radius_N functions work on a theme similar to the blur_x_radius_N
  536. // functions, but end up being simpler because there is no complicated shift of registers. We
  537. // start with the non-traditional form of the gaussian filter. In the following r is the value
  538. // when added generates the next value in the column.
  539. //
  540. // D[n+0r] = S[n+0r]*G[1]
  541. // + S[n+1r]*G[0]
  542. // + S[n+2r]*G[1]
  543. //
  544. // Expanding out in a way similar to blur_x_radius_N for specific values of n.
  545. //
  546. // D[n+0r] = S[n-2r]*G[1] + S[n-1r]*G[0] + S[n+0r]*G[1]
  547. // D[n+1r] = S[n-1r]*G[1] + S[n+0r]*G[0] + S[n+1r]*G[1]
  548. // D[n+2r] = S[n+0r]*G[1] + S[n+1r]*G[0] + S[n+2r]*G[1]
  549. //
  550. // We can see that S[n+0r] is in all three D[] equations, but is only multiplied twice. Now we
  551. // can look at the calculation form the point of view of a source value.
  552. //
  553. // Given S[n+0r]:
  554. // D[n+0r] += S[n+0r]*G[1];
  555. // /* D[n+0r] is done and can be stored now. */
  556. // D[n+1r] += S[n+0r]*G[0];
  557. // D[n+2r] = S[n+0r]*G[1];
  558. //
  559. // Remember, by induction, that D[n+0r] == S[n-2r]*G[1] + S[n-1r]*G[0] before adding in
  560. // S[n+0r]*G[1]. So, after the addition D[n+0r] has finished calculation and can be stored. Also,
  561. // notice that D[n+2r] is receiving its first value from S[n+0r]*G[1] and is not added in. Notice
  562. // how values flow in the following two iterations in source.
  563. //
  564. // D[n+0r] += S[n+0r]*G[1]
  565. // D[n+1r] += S[n+0r]*G[0]
  566. // D[n+2r] = S[n+0r]*G[1]
  567. // /* ------- */
  568. // D[n+1r] += S[n+1r]*G[1]
  569. // D[n+2r] += S[n+1r]*G[0]
  570. // D[n+3r] = S[n+1r]*G[1]
  571. //
  572. // Instead of using memory we can introduce temporaries d01 and d12. The update step changes
  573. // to the following.
  574. //
  575. // answer = d01 + S[n+0r]*G[1]
  576. // d01 = d12 + S[n+0r]*G[0]
  577. // d12 = S[n+0r]*G[1]
  578. // return answer
  579. //
  580. // Finally, this can be ganged into SIMD style.
  581. // answer[0..7] = d01[0..7] + S[n+0r..n+0r+7]*G[1]
  582. // d01[0..7] = d12[0..7] + S[n+0r..n+0r+7]*G[0]
  583. // d12[0..7] = S[n+0r..n+0r+7]*G[1]
  584. // return answer[0..7]
  585. static Sk8h blur_y_radius_1(
  586. const Sk8h& s0,
  587. const Sk8h& g0, const Sk8h& g1, const Sk8h&, const Sk8h&, const Sk8h&,
  588. Sk8h* d01, Sk8h* d12, Sk8h*, Sk8h*, Sk8h*, Sk8h*, Sk8h*, Sk8h*) {
  589. auto v0 = s0.mulHi(g0);
  590. auto v1 = s0.mulHi(g1);
  591. Sk8h answer = *d01 + v1;
  592. *d01 = *d12 + v0;
  593. *d12 = v1 + kHalf;
  594. return answer;
  595. }
  596. static Sk8h blur_y_radius_2(
  597. const Sk8h& s0,
  598. const Sk8h& g0, const Sk8h& g1, const Sk8h& g2, const Sk8h&, const Sk8h&,
  599. Sk8h* d01, Sk8h* d12, Sk8h* d23, Sk8h* d34, Sk8h*, Sk8h*, Sk8h*, Sk8h*) {
  600. auto v0 = s0.mulHi(g0);
  601. auto v1 = s0.mulHi(g1);
  602. auto v2 = s0.mulHi(g2);
  603. Sk8h answer = *d01 + v2;
  604. *d01 = *d12 + v1;
  605. *d12 = *d23 + v0;
  606. *d23 = *d34 + v1;
  607. *d34 = v2 + kHalf;
  608. return answer;
  609. }
  610. static Sk8h blur_y_radius_3(
  611. const Sk8h& s0,
  612. const Sk8h& g0, const Sk8h& g1, const Sk8h& g2, const Sk8h& g3, const Sk8h&,
  613. Sk8h* d01, Sk8h* d12, Sk8h* d23, Sk8h* d34, Sk8h* d45, Sk8h* d56, Sk8h*, Sk8h*) {
  614. auto v0 = s0.mulHi(g0);
  615. auto v1 = s0.mulHi(g1);
  616. auto v2 = s0.mulHi(g2);
  617. auto v3 = s0.mulHi(g3);
  618. Sk8h answer = *d01 + v3;
  619. *d01 = *d12 + v2;
  620. *d12 = *d23 + v1;
  621. *d23 = *d34 + v0;
  622. *d34 = *d45 + v1;
  623. *d45 = *d56 + v2;
  624. *d56 = v3 + kHalf;
  625. return answer;
  626. }
  627. static Sk8h blur_y_radius_4(
  628. const Sk8h& s0,
  629. const Sk8h& g0, const Sk8h& g1, const Sk8h& g2, const Sk8h& g3, const Sk8h& g4,
  630. Sk8h* d01, Sk8h* d12, Sk8h* d23, Sk8h* d34, Sk8h* d45, Sk8h* d56, Sk8h* d67, Sk8h* d78) {
  631. auto v0 = s0.mulHi(g0);
  632. auto v1 = s0.mulHi(g1);
  633. auto v2 = s0.mulHi(g2);
  634. auto v3 = s0.mulHi(g3);
  635. auto v4 = s0.mulHi(g4);
  636. Sk8h answer = *d01 + v4;
  637. *d01 = *d12 + v3;
  638. *d12 = *d23 + v2;
  639. *d23 = *d34 + v1;
  640. *d34 = *d45 + v0;
  641. *d45 = *d56 + v1;
  642. *d56 = *d67 + v2;
  643. *d67 = *d78 + v3;
  644. *d78 = v4 + kHalf;
  645. return answer;
  646. }
  647. using BlurY = decltype(blur_y_radius_1);
  648. // BlurY will be one of blur_y_radius_(1|2|3|4).
  649. static void blur_column(
  650. ToA8 toA8,
  651. BlurY blur, int radius, int width,
  652. const Sk8h& g0, const Sk8h& g1, const Sk8h& g2, const Sk8h& g3, const Sk8h& g4,
  653. const uint8_t* src, size_t srcRB, int srcH,
  654. uint8_t* dst, size_t dstRB) {
  655. Sk8h d01{kHalf}, d12{kHalf}, d23{kHalf}, d34{kHalf},
  656. d45{kHalf}, d56{kHalf}, d67{kHalf}, d78{kHalf};
  657. auto flush = [&](uint8_t* to, const Sk8h& v0, const Sk8h& v1) {
  658. store(to, v0, width);
  659. to += dstRB;
  660. store(to, v1, width);
  661. return to + dstRB;
  662. };
  663. for (int y = 0; y < srcH; y += 1) {
  664. auto s = load(src, width, toA8);
  665. auto b = blur(s,
  666. g0, g1, g2, g3, g4,
  667. &d01, &d12, &d23, &d34, &d45, &d56, &d67, &d78);
  668. store(dst, b, width);
  669. src += srcRB;
  670. dst += dstRB;
  671. }
  672. if (radius >= 1) {
  673. dst = flush(dst, d01, d12);
  674. }
  675. if (radius >= 2) {
  676. dst = flush(dst, d23, d34);
  677. }
  678. if (radius >= 3) {
  679. dst = flush(dst, d45, d56);
  680. }
  681. if (radius >= 4) {
  682. flush(dst, d67, d78);
  683. }
  684. }
  685. // BlurY will be one of blur_y_radius_(1|2|3|4).
  686. static void blur_y_rect(ToA8 toA8, const int strideOf8,
  687. BlurY blur, int radius, uint16_t *gauss,
  688. const uint8_t *src, size_t srcRB, int srcW, int srcH,
  689. uint8_t *dst, size_t dstRB) {
  690. Sk8h g0{gauss[0]},
  691. g1{gauss[1]},
  692. g2{gauss[2]},
  693. g3{gauss[3]},
  694. g4{gauss[4]};
  695. int x = 0;
  696. for (; x <= srcW - 8; x += 8) {
  697. blur_column(toA8, blur, radius, 8,
  698. g0, g1, g2, g3, g4,
  699. src, srcRB, srcH,
  700. dst, dstRB);
  701. src += strideOf8;
  702. dst += 8;
  703. }
  704. int xTail = srcW - x;
  705. if (xTail > 0) {
  706. blur_column(toA8, blur, radius, xTail,
  707. g0, g1, g2, g3, g4,
  708. src, srcRB, srcH,
  709. dst, dstRB);
  710. }
  711. }
  712. static void direct_blur_y(ToA8 toA8, const int strideOf8,
  713. int radius, uint16_t* gauss,
  714. const uint8_t* src, size_t srcRB, int srcW, int srcH,
  715. uint8_t* dst, size_t dstRB) {
  716. switch (radius) {
  717. case 1:
  718. blur_y_rect(toA8, strideOf8, blur_y_radius_1, 1, gauss,
  719. src, srcRB, srcW, srcH,
  720. dst, dstRB);
  721. break;
  722. case 2:
  723. blur_y_rect(toA8, strideOf8, blur_y_radius_2, 2, gauss,
  724. src, srcRB, srcW, srcH,
  725. dst, dstRB);
  726. break;
  727. case 3:
  728. blur_y_rect(toA8, strideOf8, blur_y_radius_3, 3, gauss,
  729. src, srcRB, srcW, srcH,
  730. dst, dstRB);
  731. break;
  732. case 4:
  733. blur_y_rect(toA8, strideOf8, blur_y_radius_4, 4, gauss,
  734. src, srcRB, srcW, srcH,
  735. dst, dstRB);
  736. break;
  737. default:
  738. SkASSERTF(false, "The radius %d is not handled\n", radius);
  739. }
  740. }
  741. static SkIPoint small_blur(double sigmaX, double sigmaY, const SkMask& src, SkMask* dst) {
  742. SkASSERT(sigmaX == sigmaY); // TODO
  743. SkASSERT(0.01 <= sigmaX && sigmaX < 2);
  744. SkASSERT(0.01 <= sigmaY && sigmaY < 2);
  745. SkGaussFilter filterX{sigmaX},
  746. filterY{sigmaY};
  747. int radiusX = filterX.radius(),
  748. radiusY = filterY.radius();
  749. SkASSERT(radiusX <= 4 && radiusY <= 4);
  750. auto prepareGauss = [](const SkGaussFilter& filter, uint16_t* factors) {
  751. int i = 0;
  752. for (double d : filter) {
  753. factors[i++] = static_cast<uint16_t>(round(d * (1 << 16)));
  754. }
  755. };
  756. uint16_t gaussFactorsX[SkGaussFilter::kGaussArrayMax],
  757. gaussFactorsY[SkGaussFilter::kGaussArrayMax];
  758. prepareGauss(filterX, gaussFactorsX);
  759. prepareGauss(filterY, gaussFactorsY);
  760. *dst = SkMask::PrepareDestination(radiusX, radiusY, src);
  761. if (src.fImage == nullptr) {
  762. return {SkTo<int32_t>(radiusX), SkTo<int32_t>(radiusY)};
  763. }
  764. if (dst->fImage == nullptr) {
  765. dst->fBounds.setEmpty();
  766. return {0, 0};
  767. }
  768. int srcW = src.fBounds.width(),
  769. srcH = src.fBounds.height();
  770. int dstW = dst->fBounds.width(),
  771. dstH = dst->fBounds.height();
  772. size_t srcRB = src.fRowBytes,
  773. dstRB = dst->fRowBytes;
  774. //TODO: handle bluring in only one direction.
  775. // Blur vertically and copy to destination.
  776. switch (src.fFormat) {
  777. case SkMask::kBW_Format:
  778. direct_blur_y(bw_to_a8, 1,
  779. radiusY, gaussFactorsY,
  780. src.fImage, srcRB, srcW, srcH,
  781. dst->fImage + radiusX, dstRB);
  782. break;
  783. case SkMask::kA8_Format:
  784. direct_blur_y(nullptr, 8,
  785. radiusY, gaussFactorsY,
  786. src.fImage, srcRB, srcW, srcH,
  787. dst->fImage + radiusX, dstRB);
  788. break;
  789. case SkMask::kARGB32_Format:
  790. direct_blur_y(argb32_to_a8, 32,
  791. radiusY, gaussFactorsY,
  792. src.fImage, srcRB, srcW, srcH,
  793. dst->fImage + radiusX, dstRB);
  794. break;
  795. case SkMask::kLCD16_Format:
  796. direct_blur_y(lcd_to_a8, 16, radiusY, gaussFactorsY,
  797. src.fImage, srcRB, srcW, srcH,
  798. dst->fImage + radiusX, dstRB);
  799. break;
  800. default:
  801. SK_ABORT("Unhandled format.");
  802. }
  803. // Blur horizontally in place.
  804. direct_blur_x(radiusX, gaussFactorsX,
  805. dst->fImage + radiusX, dstRB, srcW,
  806. dst->fImage, dstRB, dstW, dstH);
  807. return {radiusX, radiusY};
  808. }
  809. // TODO: assuming sigmaW = sigmaH. Allow different sigmas. Right now the
  810. // API forces the sigmas to be the same.
  811. SkIPoint SkMaskBlurFilter::blur(const SkMask& src, SkMask* dst) const {
  812. if (fSigmaW < 2.0 && fSigmaH < 2.0) {
  813. return small_blur(fSigmaW, fSigmaH, src, dst);
  814. }
  815. // 1024 is a place holder guess until more analysis can be done.
  816. SkSTArenaAlloc<1024> alloc;
  817. PlanGauss planW(fSigmaW);
  818. PlanGauss planH(fSigmaH);
  819. int borderW = planW.border(),
  820. borderH = planH.border();
  821. SkASSERT(borderH >= 0 && borderW >= 0);
  822. *dst = SkMask::PrepareDestination(borderW, borderH, src);
  823. if (src.fImage == nullptr) {
  824. return {SkTo<int32_t>(borderW), SkTo<int32_t>(borderH)};
  825. }
  826. if (dst->fImage == nullptr) {
  827. dst->fBounds.setEmpty();
  828. return {0, 0};
  829. }
  830. int srcW = src.fBounds.width(),
  831. srcH = src.fBounds.height(),
  832. dstW = dst->fBounds.width(),
  833. dstH = dst->fBounds.height();
  834. SkASSERT(srcW >= 0 && srcH >= 0 && dstW >= 0 && dstH >= 0);
  835. auto bufferSize = std::max(planW.bufferSize(), planH.bufferSize());
  836. auto buffer = alloc.makeArrayDefault<uint32_t>(bufferSize);
  837. // Blur both directions.
  838. int tmpW = srcH,
  839. tmpH = dstW;
  840. auto tmp = alloc.makeArrayDefault<uint8_t>(tmpW * tmpH);
  841. // Blur horizontally, and transpose.
  842. const PlanGauss::Scan& scanW = planW.makeBlurScan(srcW, buffer);
  843. switch (src.fFormat) {
  844. case SkMask::kBW_Format: {
  845. const uint8_t* bwStart = src.fImage;
  846. auto start = SkMask::AlphaIter<SkMask::kBW_Format>(bwStart, 0);
  847. auto end = SkMask::AlphaIter<SkMask::kBW_Format>(bwStart + (srcW / 8), srcW % 8);
  848. for (int y = 0; y < srcH; ++y, start >>= src.fRowBytes, end >>= src.fRowBytes) {
  849. auto tmpStart = &tmp[y];
  850. scanW.blur(start, end, tmpStart, tmpW, tmpStart + tmpW * tmpH);
  851. }
  852. } break;
  853. case SkMask::kA8_Format: {
  854. const uint8_t* a8Start = src.fImage;
  855. auto start = SkMask::AlphaIter<SkMask::kA8_Format>(a8Start);
  856. auto end = SkMask::AlphaIter<SkMask::kA8_Format>(a8Start + srcW);
  857. for (int y = 0; y < srcH; ++y, start >>= src.fRowBytes, end >>= src.fRowBytes) {
  858. auto tmpStart = &tmp[y];
  859. scanW.blur(start, end, tmpStart, tmpW, tmpStart + tmpW * tmpH);
  860. }
  861. } break;
  862. case SkMask::kARGB32_Format: {
  863. const uint32_t* argbStart = reinterpret_cast<const uint32_t*>(src.fImage);
  864. auto start = SkMask::AlphaIter<SkMask::kARGB32_Format>(argbStart);
  865. auto end = SkMask::AlphaIter<SkMask::kARGB32_Format>(argbStart + srcW);
  866. for (int y = 0; y < srcH; ++y, start >>= src.fRowBytes, end >>= src.fRowBytes) {
  867. auto tmpStart = &tmp[y];
  868. scanW.blur(start, end, tmpStart, tmpW, tmpStart + tmpW * tmpH);
  869. }
  870. } break;
  871. case SkMask::kLCD16_Format: {
  872. const uint16_t* lcdStart = reinterpret_cast<const uint16_t*>(src.fImage);
  873. auto start = SkMask::AlphaIter<SkMask::kLCD16_Format>(lcdStart);
  874. auto end = SkMask::AlphaIter<SkMask::kLCD16_Format>(lcdStart + srcW);
  875. for (int y = 0; y < srcH; ++y, start >>= src.fRowBytes, end >>= src.fRowBytes) {
  876. auto tmpStart = &tmp[y];
  877. scanW.blur(start, end, tmpStart, tmpW, tmpStart + tmpW * tmpH);
  878. }
  879. } break;
  880. default:
  881. SK_ABORT("Unhandled format.");
  882. }
  883. // Blur vertically (scan in memory order because of the transposition),
  884. // and transpose back to the original orientation.
  885. const PlanGauss::Scan& scanH = planH.makeBlurScan(tmpW, buffer);
  886. for (int y = 0; y < tmpH; y++) {
  887. auto tmpStart = &tmp[y * tmpW];
  888. auto dstStart = &dst->fImage[y];
  889. scanH.blur(tmpStart, tmpStart + tmpW,
  890. dstStart, dst->fRowBytes, dstStart + dst->fRowBytes * dstH);
  891. }
  892. return {SkTo<int32_t>(borderW), SkTo<int32_t>(borderH)};
  893. }