SkBlurImageFilter.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. /*
  2. * Copyright 2011 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 "include/effects/SkBlurImageFilter.h"
  8. #include <algorithm>
  9. #include "include/core/SkBitmap.h"
  10. #include "include/private/SkColorData.h"
  11. #include "include/private/SkNx.h"
  12. #include "include/private/SkTFitsIn.h"
  13. #include "src/core/SkArenaAlloc.h"
  14. #include "src/core/SkAutoPixmapStorage.h"
  15. #include "src/core/SkGpuBlurUtils.h"
  16. #include "src/core/SkImageFilterPriv.h"
  17. #include "src/core/SkOpts.h"
  18. #include "src/core/SkReadBuffer.h"
  19. #include "src/core/SkSpecialImage.h"
  20. #include "src/core/SkWriteBuffer.h"
  21. #if SK_SUPPORT_GPU
  22. #include "include/gpu/GrContext.h"
  23. #include "src/gpu/GrTextureProxy.h"
  24. #include "src/gpu/SkGr.h"
  25. #endif
  26. static constexpr double kPi = 3.14159265358979323846264338327950288;
  27. class SkBlurImageFilterImpl final : public SkImageFilter {
  28. public:
  29. SkBlurImageFilterImpl(SkScalar sigmaX,
  30. SkScalar sigmaY,
  31. sk_sp<SkImageFilter> input,
  32. const CropRect* cropRect,
  33. SkBlurImageFilter::TileMode tileMode);
  34. SkRect computeFastBounds(const SkRect&) const override;
  35. protected:
  36. void flatten(SkWriteBuffer&) const override;
  37. sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
  38. SkIPoint* offset) const override;
  39. SkIRect onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
  40. MapDirection, const SkIRect* inputRect) const override;
  41. private:
  42. SK_FLATTENABLE_HOOKS(SkBlurImageFilterImpl)
  43. typedef SkImageFilter INHERITED;
  44. friend class SkImageFilter;
  45. #if SK_SUPPORT_GPU
  46. sk_sp<SkSpecialImage> gpuFilter(
  47. SkSpecialImage *source, SkVector sigma, const sk_sp<SkSpecialImage> &input,
  48. SkIRect inputBounds, SkIRect dstBounds, SkIPoint inputOffset,
  49. const OutputProperties& outProps, SkIPoint* offset) const;
  50. #endif
  51. SkSize fSigma;
  52. SkBlurImageFilter::TileMode fTileMode;
  53. };
  54. void SkImageFilter::RegisterFlattenables() { SK_REGISTER_FLATTENABLE(SkBlurImageFilterImpl); }
  55. ///////////////////////////////////////////////////////////////////////////////
  56. sk_sp<SkImageFilter> SkBlurImageFilter::Make(SkScalar sigmaX, SkScalar sigmaY,
  57. sk_sp<SkImageFilter> input,
  58. const SkImageFilter::CropRect* cropRect,
  59. TileMode tileMode) {
  60. if (sigmaX < SK_ScalarNearlyZero && sigmaY < SK_ScalarNearlyZero && !cropRect) {
  61. return input;
  62. }
  63. return sk_sp<SkImageFilter>(
  64. new SkBlurImageFilterImpl(sigmaX, sigmaY, input, cropRect, tileMode));
  65. }
  66. // This rather arbitrary-looking value results in a maximum box blur kernel size
  67. // of 1000 pixels on the raster path, which matches the WebKit and Firefox
  68. // implementations. Since the GPU path does not compute a box blur, putting
  69. // the limit on sigma ensures consistent behaviour between the GPU and
  70. // raster paths.
  71. #define MAX_SIGMA SkIntToScalar(532)
  72. static SkVector map_sigma(const SkSize& localSigma, const SkMatrix& ctm) {
  73. SkVector sigma = SkVector::Make(localSigma.width(), localSigma.height());
  74. ctm.mapVectors(&sigma, 1);
  75. sigma.fX = SkMinScalar(SkScalarAbs(sigma.fX), MAX_SIGMA);
  76. sigma.fY = SkMinScalar(SkScalarAbs(sigma.fY), MAX_SIGMA);
  77. return sigma;
  78. }
  79. SkBlurImageFilterImpl::SkBlurImageFilterImpl(SkScalar sigmaX,
  80. SkScalar sigmaY,
  81. sk_sp<SkImageFilter> input,
  82. const CropRect* cropRect,
  83. SkBlurImageFilter::TileMode tileMode)
  84. : INHERITED(&input, 1, cropRect), fSigma{sigmaX, sigmaY}, fTileMode(tileMode) {}
  85. sk_sp<SkFlattenable> SkBlurImageFilterImpl::CreateProc(SkReadBuffer& buffer) {
  86. SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
  87. SkScalar sigmaX = buffer.readScalar();
  88. SkScalar sigmaY = buffer.readScalar();
  89. SkBlurImageFilter::TileMode tileMode;
  90. if (buffer.isVersionLT(SkReadBuffer::kTileModeInBlurImageFilter_Version)) {
  91. tileMode = SkBlurImageFilter::kClampToBlack_TileMode;
  92. } else {
  93. tileMode = buffer.read32LE(SkBlurImageFilter::kLast_TileMode);
  94. }
  95. static_assert(SkBlurImageFilter::kLast_TileMode == 2, "CreateProc");
  96. return SkBlurImageFilter::Make(
  97. sigmaX, sigmaY, common.getInput(0), &common.cropRect(), tileMode);
  98. }
  99. void SkBlurImageFilterImpl::flatten(SkWriteBuffer& buffer) const {
  100. this->INHERITED::flatten(buffer);
  101. buffer.writeScalar(fSigma.fWidth);
  102. buffer.writeScalar(fSigma.fHeight);
  103. static_assert(SkBlurImageFilter::kLast_TileMode == 2, "flatten");
  104. SkASSERT(fTileMode <= SkBlurImageFilter::kLast_TileMode);
  105. buffer.writeInt(static_cast<int>(fTileMode));
  106. }
  107. #if SK_SUPPORT_GPU
  108. static GrTextureDomain::Mode to_texture_domain_mode(SkBlurImageFilter::TileMode tileMode) {
  109. switch (tileMode) {
  110. case SkBlurImageFilter::TileMode::kClamp_TileMode:
  111. return GrTextureDomain::kClamp_Mode;
  112. case SkBlurImageFilter::TileMode::kClampToBlack_TileMode:
  113. return GrTextureDomain::kDecal_Mode;
  114. case SkBlurImageFilter::TileMode::kRepeat_TileMode:
  115. return GrTextureDomain::kRepeat_Mode;
  116. default:
  117. SK_ABORT("Unsupported tile mode.");
  118. return GrTextureDomain::kDecal_Mode;
  119. }
  120. }
  121. #endif
  122. // This is defined by the SVG spec:
  123. // https://drafts.fxtf.org/filter-effects/#feGaussianBlurElement
  124. static int calculate_window(double sigma) {
  125. // NB 136 is the largest sigma that will not cause a buffer full of 255 mask values to overflow
  126. // using the Gauss filter. It also limits the size of buffers used hold intermediate values.
  127. // Explanation of maximums:
  128. // sum0 = window * 255
  129. // sum1 = window * sum0 -> window * window * 255
  130. // sum2 = window * sum1 -> window * window * window * 255 -> window^3 * 255
  131. //
  132. // The value window^3 * 255 must fit in a uint32_t. So,
  133. // window^3 < 2^32. window = 255.
  134. //
  135. // window = floor(sigma * 3 * sqrt(2 * kPi) / 4 + 0.5)
  136. // For window <= 255, the largest value for sigma is 136.
  137. sigma = SkTPin(sigma, 0.0, 136.0);
  138. auto possibleWindow = static_cast<int>(floor(sigma * 3 * sqrt(2 * kPi) / 4 + 0.5));
  139. return std::max(1, possibleWindow);
  140. }
  141. // Calculating the border is tricky. The border is the distance in pixels between the first dst
  142. // pixel and the first src pixel (or the last src pixel and the last dst pixel).
  143. // I will go through the odd case which is simpler, and then through the even case. Given a
  144. // stack of filters seven wide for the odd case of three passes.
  145. //
  146. // S
  147. // aaaAaaa
  148. // bbbBbbb
  149. // cccCccc
  150. // D
  151. //
  152. // The furthest changed pixel is when the filters are in the following configuration.
  153. //
  154. // S
  155. // aaaAaaa
  156. // bbbBbbb
  157. // cccCccc
  158. // D
  159. //
  160. // The A pixel is calculated using the value S, the B uses A, and the C uses B, and
  161. // finally D is C. So, with a window size of seven the border is nine. In the odd case, the
  162. // border is 3*((window - 1)/2).
  163. //
  164. // For even cases the filter stack is more complicated. The spec specifies two passes
  165. // of even filters and a final pass of odd filters. A stack for a width of six looks like
  166. // this.
  167. //
  168. // S
  169. // aaaAaa
  170. // bbBbbb
  171. // cccCccc
  172. // D
  173. //
  174. // The furthest pixel looks like this.
  175. //
  176. // S
  177. // aaaAaa
  178. // bbBbbb
  179. // cccCccc
  180. // D
  181. //
  182. // For a window of six, the border value is eight. In the even case the border is 3 *
  183. // (window/2) - 1.
  184. static int calculate_border(int window) {
  185. return (window & 1) == 1 ? 3 * ((window - 1) / 2) : 3 * (window / 2) - 1;
  186. }
  187. static int calculate_buffer(int window) {
  188. int bufferSize = window - 1;
  189. return (window & 1) == 1 ? 3 * bufferSize : 3 * bufferSize + 1;
  190. }
  191. // blur_one_direction implements the common three pass box filter approximation of Gaussian blur,
  192. // but combines all three passes into a single pass. This approach is facilitated by three circular
  193. // buffers the width of the window which track values for trailing edges of each of the three
  194. // passes. This allows the algorithm to use more precision in the calculation because the values
  195. // are not rounded each pass. And this implementation also avoids a trap that's easy to fall
  196. // into resulting in blending in too many zeroes near the edge.
  197. //
  198. // In general, a window sum has the form:
  199. // sum_n+1 = sum_n + leading_edge - trailing_edge.
  200. // If instead we do the subtraction at the end of the previous iteration, we can just
  201. // calculate the sums instead of having to do the subtractions too.
  202. //
  203. // In previous iteration:
  204. // sum_n+1 = sum_n - trailing_edge.
  205. //
  206. // In this iteration:
  207. // sum_n+1 = sum_n + leading_edge.
  208. //
  209. // Now we can stack all three sums and do them at once. Sum0 gets its leading edge from the
  210. // actual data. Sum1's leading edge is just Sum0, and Sum2's leading edge is Sum1. So, doing the
  211. // three passes at the same time has the form:
  212. //
  213. // sum0_n+1 = sum0_n + leading edge
  214. // sum1_n+1 = sum1_n + sum0_n+1
  215. // sum2_n+1 = sum2_n + sum1_n+1
  216. //
  217. // sum2_n+1 / window^3 is the new value of the destination pixel.
  218. //
  219. // Reduce the sums by the trailing edges which were stored in the circular buffers,
  220. // for the next go around. This is the case for odd sized windows, even windows the the third
  221. // circular buffer is one larger then the first two circular buffers.
  222. //
  223. // sum2_n+2 = sum2_n+1 - buffer2[i];
  224. // buffer2[i] = sum1;
  225. // sum1_n+2 = sum1_n+1 - buffer1[i];
  226. // buffer1[i] = sum0;
  227. // sum0_n+2 = sum0_n+1 - buffer0[i];
  228. // buffer0[i] = leading edge
  229. //
  230. // This is all encapsulated in the processValue function below.
  231. //
  232. using Pass0And1 = Sk4u[2];
  233. // The would be dLeft parameter is assumed to be 0.
  234. static void blur_one_direction(Sk4u* buffer, int window,
  235. int srcLeft, int srcRight, int dstRight,
  236. const uint32_t* src, int srcXStride, int srcYStride, int srcH,
  237. uint32_t* dst, int dstXStride, int dstYStride) {
  238. // The circular buffers are one less than the window.
  239. auto pass0Count = window - 1,
  240. pass1Count = window - 1,
  241. pass2Count = (window & 1) == 1 ? window - 1 : window;
  242. Pass0And1* buffer01Start = (Pass0And1*)buffer;
  243. Sk4u* buffer2Start = buffer + pass0Count + pass1Count;
  244. Pass0And1* buffer01End = (Pass0And1*)buffer2Start;
  245. Sk4u* buffer2End = buffer2Start + pass2Count;
  246. // If the window is odd then the divisor is just window ^ 3 otherwise,
  247. // it is window * window * (window + 1) = window ^ 3 + window ^ 2;
  248. auto window2 = window * window;
  249. auto window3 = window2 * window;
  250. auto divisor = (window & 1) == 1 ? window3 : window3 + window2;
  251. // NB the sums in the blur code use the following technique to avoid
  252. // adding 1/2 to round the divide.
  253. //
  254. // Sum/d + 1/2 == (Sum + h) / d
  255. // Sum + d(1/2) == Sum + h
  256. // h == (1/2)d
  257. //
  258. // But the d/2 it self should be rounded.
  259. // h == d/2 + 1/2 == (d + 1) / 2
  260. //
  261. // weight = 1 / d * 2 ^ 32
  262. auto weight = static_cast<uint32_t>(round(1.0 / divisor * (1ull << 32)));
  263. auto half = static_cast<uint32_t>((divisor + 1) / 2);
  264. auto border = calculate_border(window);
  265. // Calculate the start and end of the source pixels with respect to the destination start.
  266. auto srcStart = srcLeft - border,
  267. srcEnd = srcRight - border,
  268. dstEnd = dstRight;
  269. for (auto y = 0; y < srcH; y++) {
  270. auto buffer01Cursor = buffer01Start;
  271. auto buffer2Cursor = buffer2Start;
  272. Sk4u sum0{0u};
  273. Sk4u sum1{0u};
  274. Sk4u sum2{half};
  275. sk_bzero(buffer01Start, (buffer2End - (Sk4u *) (buffer01Start)) * sizeof(*buffer2Start));
  276. // Given an expanded input pixel, move the window ahead using the leadingEdge value.
  277. auto processValue = [&](const Sk4u& leadingEdge) -> Sk4u {
  278. sum0 += leadingEdge;
  279. sum1 += sum0;
  280. sum2 += sum1;
  281. Sk4u value = sum2.mulHi(weight);
  282. sum2 -= *buffer2Cursor;
  283. *buffer2Cursor = sum1;
  284. buffer2Cursor = (buffer2Cursor + 1) < buffer2End ? buffer2Cursor + 1 : buffer2Start;
  285. sum1 -= (*buffer01Cursor)[1];
  286. (*buffer01Cursor)[1] = sum0;
  287. sum0 -= (*buffer01Cursor)[0];
  288. (*buffer01Cursor)[0] = leadingEdge;
  289. buffer01Cursor =
  290. (buffer01Cursor + 1) < buffer01End ? buffer01Cursor + 1 : buffer01Start;
  291. return value;
  292. };
  293. auto srcIdx = srcStart;
  294. auto dstIdx = 0;
  295. const uint32_t* srcCursor = src;
  296. uint32_t* dstCursor = dst;
  297. // The destination pixels are not effected by the src pixels,
  298. // change to zero as per the spec.
  299. // https://drafts.fxtf.org/filter-effects/#FilterPrimitivesOverviewIntro
  300. while (dstIdx < srcIdx) {
  301. *dstCursor = 0;
  302. dstCursor += dstXStride;
  303. SK_PREFETCH(dstCursor);
  304. dstIdx++;
  305. }
  306. // The edge of the source is before the edge of the destination. Calculate the sums for
  307. // the pixels before the start of the destination.
  308. while (dstIdx > srcIdx) {
  309. Sk4u leadingEdge = srcIdx < srcEnd ? SkNx_cast<uint32_t>(Sk4b::Load(srcCursor)) : 0;
  310. (void) processValue(leadingEdge);
  311. srcCursor += srcXStride;
  312. srcIdx++;
  313. }
  314. // The dstIdx and srcIdx are in sync now; the code just uses the dstIdx for both now.
  315. // Consume the source generating pixels to dst.
  316. auto loopEnd = std::min(dstEnd, srcEnd);
  317. while (dstIdx < loopEnd) {
  318. Sk4u leadingEdge = SkNx_cast<uint32_t>(Sk4b::Load(srcCursor));
  319. SkNx_cast<uint8_t>(processValue(leadingEdge)).store(dstCursor);
  320. srcCursor += srcXStride;
  321. dstCursor += dstXStride;
  322. SK_PREFETCH(dstCursor);
  323. dstIdx++;
  324. }
  325. // The leading edge is beyond the end of the source. Assume that the pixels
  326. // are now 0x0000 until the end of the destination.
  327. loopEnd = dstEnd;
  328. while (dstIdx < loopEnd) {
  329. SkNx_cast<uint8_t>(processValue(0u)).store(dstCursor);
  330. dstCursor += dstXStride;
  331. SK_PREFETCH(dstCursor);
  332. dstIdx++;
  333. }
  334. src += srcYStride;
  335. dst += dstYStride;
  336. }
  337. }
  338. static sk_sp<SkSpecialImage> copy_image_with_bounds(
  339. SkSpecialImage *source, const sk_sp<SkSpecialImage> &input,
  340. SkIRect srcBounds, SkIRect dstBounds) {
  341. SkBitmap inputBM;
  342. if (!input->getROPixels(&inputBM)) {
  343. return nullptr;
  344. }
  345. if (inputBM.colorType() != kN32_SkColorType) {
  346. return nullptr;
  347. }
  348. SkBitmap src;
  349. inputBM.extractSubset(&src, srcBounds);
  350. // Make everything relative to the destination bounds.
  351. srcBounds.offset(-dstBounds.x(), -dstBounds.y());
  352. dstBounds.offset(-dstBounds.x(), -dstBounds.y());
  353. auto srcW = srcBounds.width(),
  354. dstW = dstBounds.width(),
  355. dstH = dstBounds.height();
  356. SkImageInfo dstInfo = SkImageInfo::Make(dstW, dstH, inputBM.colorType(), inputBM.alphaType());
  357. SkBitmap dst;
  358. if (!dst.tryAllocPixels(dstInfo)) {
  359. return nullptr;
  360. }
  361. // There is no blurring to do, but we still need to copy the source while accounting for the
  362. // dstBounds. Remember that the src was intersected with the dst.
  363. int y = 0;
  364. size_t dstWBytes = dstW * sizeof(uint32_t);
  365. for (;y < srcBounds.top(); y++) {
  366. sk_bzero(dst.getAddr32(0, y), dstWBytes);
  367. }
  368. for (;y < srcBounds.bottom(); y++) {
  369. int x = 0;
  370. uint32_t* dstPtr = dst.getAddr32(0, y);
  371. for (;x < srcBounds.left(); x++) {
  372. *dstPtr++ = 0;
  373. }
  374. memcpy(dstPtr, src.getAddr32(x - srcBounds.left(), y - srcBounds.top()),
  375. srcW * sizeof(uint32_t));
  376. dstPtr += srcW;
  377. x += srcW;
  378. for (;x < dstBounds.right(); x++) {
  379. *dstPtr++ = 0;
  380. }
  381. }
  382. for (;y < dstBounds.bottom(); y++) {
  383. sk_bzero(dst.getAddr32(0, y), dstWBytes);
  384. }
  385. return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(dstBounds.width(),
  386. dstBounds.height()),
  387. dst, &source->props());
  388. }
  389. // TODO: Implement CPU backend for different fTileMode.
  390. static sk_sp<SkSpecialImage> cpu_blur(
  391. SkVector sigma,
  392. SkSpecialImage *source, const sk_sp<SkSpecialImage> &input,
  393. SkIRect srcBounds, SkIRect dstBounds) {
  394. auto windowW = calculate_window(sigma.x()),
  395. windowH = calculate_window(sigma.y());
  396. if (windowW <= 1 && windowH <= 1) {
  397. return copy_image_with_bounds(source, input, srcBounds, dstBounds);
  398. }
  399. SkBitmap inputBM;
  400. if (!input->getROPixels(&inputBM)) {
  401. return nullptr;
  402. }
  403. if (inputBM.colorType() != kN32_SkColorType) {
  404. return nullptr;
  405. }
  406. SkBitmap src;
  407. inputBM.extractSubset(&src, srcBounds);
  408. // Make everything relative to the destination bounds.
  409. srcBounds.offset(-dstBounds.x(), -dstBounds.y());
  410. dstBounds.offset(-dstBounds.x(), -dstBounds.y());
  411. auto srcW = srcBounds.width(),
  412. srcH = srcBounds.height(),
  413. dstW = dstBounds.width(),
  414. dstH = dstBounds.height();
  415. SkImageInfo dstInfo = inputBM.info().makeWH(dstW, dstH);
  416. SkBitmap dst;
  417. if (!dst.tryAllocPixels(dstInfo)) {
  418. return nullptr;
  419. }
  420. auto bufferSizeW = calculate_buffer(windowW),
  421. bufferSizeH = calculate_buffer(windowH);
  422. // The amount 1024 is enough for buffers up to 10 sigma. The tmp bitmap will be
  423. // allocated on the heap.
  424. SkSTArenaAlloc<1024> alloc;
  425. Sk4u* buffer = alloc.makeArrayDefault<Sk4u>(std::max(bufferSizeW, bufferSizeH));
  426. // Basic Plan: The three cases to handle
  427. // * Horizontal and Vertical - blur horizontally while copying values from the source to
  428. // the destination. Then, do an in-place vertical blur.
  429. // * Horizontal only - blur horizontally copying values from the source to the destination.
  430. // * Vertical only - blur vertically copying values from the source to the destination.
  431. // Default to vertical only blur case. If a horizontal blur is needed, then these values
  432. // will be adjusted while doing the horizontal blur.
  433. auto intermediateSrc = static_cast<uint32_t *>(src.getPixels());
  434. auto intermediateRowBytesAsPixels = src.rowBytesAsPixels();
  435. auto intermediateWidth = srcW;
  436. // Because the border is calculated before the fork of the GPU/CPU path. The border is
  437. // the maximum of the two rendering methods. In the case where sigma is zero, then the
  438. // src and dst left values are the same. If sigma is small resulting in a window size of
  439. // 1, then border calculations add some pixels which will always be zero. Inset the
  440. // destination by those zero pixels. This case is very rare.
  441. auto intermediateDst = dst.getAddr32(srcBounds.left(), 0);
  442. // The following code is executed very rarely, I have never seen it in a real web
  443. // page. If sigma is small but not zero then shared GPU/CPU border calculation
  444. // code adds extra pixels for the border. Just clear everything to clear those pixels.
  445. // This solution is overkill, but very simple.
  446. if (windowW == 1 || windowH == 1) {
  447. dst.eraseColor(0);
  448. }
  449. if (windowW > 1) {
  450. // Make int64 to avoid overflow in multiplication below.
  451. int64_t shift = srcBounds.top() - dstBounds.top();
  452. // For the horizontal blur, starts part way down in anticipation of the vertical blur.
  453. // For a vertical sigma of zero shift should be zero. But, for small sigma,
  454. // shift may be > 0 but the vertical window could be 1.
  455. intermediateSrc = static_cast<uint32_t *>(dst.getPixels())
  456. + (shift > 0 ? shift * dst.rowBytesAsPixels() : 0);
  457. intermediateRowBytesAsPixels = dst.rowBytesAsPixels();
  458. intermediateWidth = dstW;
  459. intermediateDst = static_cast<uint32_t *>(dst.getPixels());
  460. blur_one_direction(
  461. buffer, windowW,
  462. srcBounds.left(), srcBounds.right(), dstBounds.right(),
  463. static_cast<uint32_t *>(src.getPixels()), 1, src.rowBytesAsPixels(), srcH,
  464. intermediateSrc, 1, intermediateRowBytesAsPixels);
  465. }
  466. if (windowH > 1) {
  467. blur_one_direction(
  468. buffer, windowH,
  469. srcBounds.top(), srcBounds.bottom(), dstBounds.bottom(),
  470. intermediateSrc, intermediateRowBytesAsPixels, 1, intermediateWidth,
  471. intermediateDst, dst.rowBytesAsPixels(), 1);
  472. }
  473. return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(dstBounds.width(),
  474. dstBounds.height()),
  475. dst, &source->props());
  476. }
  477. sk_sp<SkSpecialImage> SkBlurImageFilterImpl::onFilterImage(SkSpecialImage* source,
  478. const Context& ctx,
  479. SkIPoint* offset) const {
  480. SkIPoint inputOffset = SkIPoint::Make(0, 0);
  481. sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset));
  482. if (!input) {
  483. return nullptr;
  484. }
  485. SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.fX, inputOffset.fY,
  486. input->width(), input->height());
  487. // Calculate the destination bounds.
  488. SkIRect dstBounds;
  489. if (!this->applyCropRect(this->mapContext(ctx), inputBounds, &dstBounds)) {
  490. return nullptr;
  491. }
  492. if (!inputBounds.intersect(dstBounds)) {
  493. return nullptr;
  494. }
  495. // Save the offset in preparation to make all rectangles relative to the inputOffset.
  496. SkIPoint resultOffset = SkIPoint::Make(dstBounds.fLeft, dstBounds.fTop);
  497. // Make all bounds relative to the inputOffset.
  498. inputBounds.offset(-inputOffset);
  499. dstBounds.offset(-inputOffset);
  500. const SkVector sigma = map_sigma(fSigma, ctx.ctm());
  501. if (sigma.x() < 0 || sigma.y() < 0) {
  502. return nullptr;
  503. }
  504. sk_sp<SkSpecialImage> result;
  505. #if SK_SUPPORT_GPU
  506. if (source->isTextureBacked()) {
  507. // Ensure the input is in the destination's gamut. This saves us from having to do the
  508. // xform during the filter itself.
  509. input = ImageToColorSpace(input.get(), ctx.outputProperties());
  510. result = this->gpuFilter(source, sigma, input, inputBounds, dstBounds, inputOffset,
  511. ctx.outputProperties(), &resultOffset);
  512. } else
  513. #endif
  514. {
  515. result = cpu_blur(sigma, source, input, inputBounds, dstBounds);
  516. }
  517. // Return the resultOffset if the blur succeeded.
  518. if (result != nullptr) {
  519. *offset = resultOffset;
  520. }
  521. return result;
  522. }
  523. #if SK_SUPPORT_GPU
  524. sk_sp<SkSpecialImage> SkBlurImageFilterImpl::gpuFilter(
  525. SkSpecialImage *source, SkVector sigma, const sk_sp<SkSpecialImage> &input,
  526. SkIRect inputBounds, SkIRect dstBounds, SkIPoint inputOffset,
  527. const OutputProperties& outProps, SkIPoint* offset) const
  528. {
  529. if (0 == sigma.x() && 0 == sigma.y()) {
  530. offset->fX = inputBounds.x() + inputOffset.fX;
  531. offset->fY = inputBounds.y() + inputOffset.fY;
  532. return input->makeSubset(inputBounds);
  533. }
  534. auto context = source->getContext();
  535. sk_sp<GrTextureProxy> inputTexture(input->asTextureProxyRef(context));
  536. if (!inputTexture) {
  537. return nullptr;
  538. }
  539. sk_sp<GrRenderTargetContext> renderTargetContext(SkGpuBlurUtils::GaussianBlur(
  540. context,
  541. std::move(inputTexture),
  542. input->subset().topLeft(),
  543. outProps.colorSpace() ? sk_ref_sp(input->getColorSpace()) : nullptr,
  544. dstBounds,
  545. inputBounds,
  546. sigma.x(),
  547. sigma.y(),
  548. to_texture_domain_mode(fTileMode),
  549. input->alphaType()));
  550. if (!renderTargetContext) {
  551. return nullptr;
  552. }
  553. return SkSpecialImage::MakeDeferredFromGpu(
  554. context,
  555. SkIRect::MakeWH(dstBounds.width(), dstBounds.height()),
  556. kNeedNewImageUniqueID_SpecialImage,
  557. renderTargetContext->asTextureProxyRef(),
  558. sk_ref_sp(input->getColorSpace()),
  559. &source->props());
  560. }
  561. #endif
  562. SkRect SkBlurImageFilterImpl::computeFastBounds(const SkRect& src) const {
  563. SkRect bounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src;
  564. bounds.outset(fSigma.width() * 3, fSigma.height() * 3);
  565. return bounds;
  566. }
  567. SkIRect SkBlurImageFilterImpl::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
  568. MapDirection, const SkIRect* inputRect) const {
  569. SkVector sigma = map_sigma(fSigma, ctm);
  570. return src.makeOutset(SkScalarCeilToInt(sigma.x() * 3), SkScalarCeilToInt(sigma.y() * 3));
  571. }