SkGpuBlurUtils.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. /*
  2. * Copyright 2013 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/SkGpuBlurUtils.h"
  8. #include "include/core/SkRect.h"
  9. #if SK_SUPPORT_GPU
  10. #include "include/private/GrRecordingContext.h"
  11. #include "src/gpu/GrCaps.h"
  12. #include "src/gpu/GrFixedClip.h"
  13. #include "src/gpu/GrRecordingContextPriv.h"
  14. #include "src/gpu/GrRenderTargetContext.h"
  15. #include "src/gpu/GrRenderTargetContextPriv.h"
  16. #include "src/gpu/effects/GrGaussianConvolutionFragmentProcessor.h"
  17. #include "src/gpu/effects/GrMatrixConvolutionEffect.h"
  18. #include "src/gpu/SkGr.h"
  19. #define MAX_BLUR_SIGMA 4.0f
  20. using Direction = GrGaussianConvolutionFragmentProcessor::Direction;
  21. static void scale_irect_roundout(SkIRect* rect, float xScale, float yScale) {
  22. rect->fLeft = SkScalarFloorToInt(rect->fLeft * xScale);
  23. rect->fTop = SkScalarFloorToInt(rect->fTop * yScale);
  24. rect->fRight = SkScalarCeilToInt(rect->fRight * xScale);
  25. rect->fBottom = SkScalarCeilToInt(rect->fBottom * yScale);
  26. }
  27. static void scale_irect(SkIRect* rect, int xScale, int yScale) {
  28. rect->fLeft *= xScale;
  29. rect->fTop *= yScale;
  30. rect->fRight *= xScale;
  31. rect->fBottom *= yScale;
  32. }
  33. #ifdef SK_DEBUG
  34. static inline int is_even(int x) { return !(x & 1); }
  35. #endif
  36. static void shrink_irect_by_2(SkIRect* rect, bool xAxis, bool yAxis) {
  37. if (xAxis) {
  38. SkASSERT(is_even(rect->fLeft) && is_even(rect->fRight));
  39. rect->fLeft /= 2;
  40. rect->fRight /= 2;
  41. }
  42. if (yAxis) {
  43. SkASSERT(is_even(rect->fTop) && is_even(rect->fBottom));
  44. rect->fTop /= 2;
  45. rect->fBottom /= 2;
  46. }
  47. }
  48. static float adjust_sigma(float sigma, int maxTextureSize, int *scaleFactor, int *radius) {
  49. *scaleFactor = 1;
  50. while (sigma > MAX_BLUR_SIGMA) {
  51. *scaleFactor *= 2;
  52. sigma *= 0.5f;
  53. if (*scaleFactor > maxTextureSize) {
  54. *scaleFactor = maxTextureSize;
  55. sigma = MAX_BLUR_SIGMA;
  56. }
  57. }
  58. *radius = static_cast<int>(ceilf(sigma * 3.0f));
  59. SkASSERT(*radius <= GrGaussianConvolutionFragmentProcessor::kMaxKernelRadius);
  60. return sigma;
  61. }
  62. static void convolve_gaussian_1d(GrRenderTargetContext* renderTargetContext,
  63. const GrClip& clip,
  64. const SkIRect& dstRect,
  65. const SkIPoint& srcOffset,
  66. sk_sp<GrTextureProxy> proxy,
  67. Direction direction,
  68. int radius,
  69. float sigma,
  70. GrTextureDomain::Mode mode,
  71. int bounds[2]) {
  72. GrPaint paint;
  73. std::unique_ptr<GrFragmentProcessor> conv(GrGaussianConvolutionFragmentProcessor::Make(
  74. std::move(proxy), direction, radius, sigma, mode, bounds));
  75. paint.addColorFragmentProcessor(std::move(conv));
  76. paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
  77. SkMatrix localMatrix = SkMatrix::MakeTrans(-SkIntToScalar(srcOffset.x()),
  78. -SkIntToScalar(srcOffset.y()));
  79. renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
  80. SkRect::Make(dstRect), localMatrix);
  81. }
  82. static GrColorType get_blur_color_type(GrTextureProxy* proxy) {
  83. GrPixelConfig config = proxy->config();
  84. SkASSERT(kBGRA_8888_GrPixelConfig == config || kRGBA_8888_GrPixelConfig == config ||
  85. kRGB_888_GrPixelConfig == config || kRGBA_4444_GrPixelConfig == config ||
  86. kRGB_565_GrPixelConfig == config || kSRGBA_8888_GrPixelConfig == config ||
  87. kRGBA_half_GrPixelConfig == config || kAlpha_8_GrPixelConfig == config ||
  88. kAlpha_8_as_Alpha_GrPixelConfig == config || kAlpha_8_as_Red_GrPixelConfig == config ||
  89. kRGBA_1010102_GrPixelConfig == config || kRGBA_half_Clamped_GrPixelConfig == config);
  90. return GrPixelConfigToColorType(config);
  91. }
  92. static sk_sp<GrRenderTargetContext> convolve_gaussian_2d(GrRecordingContext* context,
  93. sk_sp<GrTextureProxy> proxy,
  94. const SkIRect& srcBounds,
  95. const SkIPoint& srcOffset,
  96. int radiusX,
  97. int radiusY,
  98. SkScalar sigmaX,
  99. SkScalar sigmaY,
  100. GrTextureDomain::Mode mode,
  101. int finalW,
  102. int finalH,
  103. sk_sp<SkColorSpace> finalCS,
  104. SkBackingFit dstFit) {
  105. // TODO: Once GrPixelConfig is gone, we need will need the source's color type.
  106. GrColorType colorType = get_blur_color_type(proxy.get());
  107. sk_sp<GrRenderTargetContext> renderTargetContext;
  108. renderTargetContext = context->priv().makeDeferredRenderTargetContext(
  109. dstFit,
  110. finalW,
  111. finalH,
  112. colorType,
  113. std::move(finalCS),
  114. 1,
  115. GrMipMapped::kNo,
  116. proxy->origin(),
  117. nullptr,
  118. SkBudgeted::kYes,
  119. proxy->isProtected() ? GrProtected::kYes : GrProtected::kNo);
  120. if (!renderTargetContext) {
  121. return nullptr;
  122. }
  123. SkMatrix localMatrix = SkMatrix::MakeTrans(-SkIntToScalar(srcOffset.x()),
  124. -SkIntToScalar(srcOffset.y()));
  125. SkISize size = SkISize::Make(2 * radiusX + 1, 2 * radiusY + 1);
  126. SkIPoint kernelOffset = SkIPoint::Make(radiusX, radiusY);
  127. GrPaint paint;
  128. auto conv = GrMatrixConvolutionEffect::MakeGaussian(std::move(proxy), srcBounds, size, 1.0, 0.0,
  129. kernelOffset, mode, true, sigmaX, sigmaY);
  130. paint.addColorFragmentProcessor(std::move(conv));
  131. paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
  132. GrFixedClip clip(SkIRect::MakeWH(finalW, finalH));
  133. renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
  134. SkRect::MakeWH(finalW, finalH), localMatrix);
  135. return renderTargetContext;
  136. }
  137. // NOTE: Both convolve_gaussian or decimate accept a proxyOffset. This is separate from the
  138. // srcBounds and srcOffset, which are relative to the content rect of the image, whereas proxyOffset
  139. // maps from the content rect to the proxy's coordinate space. Due to how the destination bounds are
  140. // calculated, it is more convenient to have the proxy offset kept separate from the logical bounds
  141. // (which do impact destination decisions). Both functions incorporate the proxy offset into the
  142. // geometry they submit or before calling convolve_gaussian_1d.
  143. static sk_sp<GrRenderTargetContext> convolve_gaussian(GrRecordingContext* context,
  144. sk_sp<GrTextureProxy> proxy,
  145. const SkIPoint& proxyOffset,
  146. const SkIRect& srcRect,
  147. const SkIPoint& srcOffset,
  148. Direction direction,
  149. int radius,
  150. float sigma,
  151. SkIRect* contentRect,
  152. GrTextureDomain::Mode mode,
  153. int finalW,
  154. int finalH,
  155. sk_sp<SkColorSpace> finalCS,
  156. SkBackingFit fit) {
  157. SkASSERT(srcRect.width() <= finalW && srcRect.height() <= finalH);
  158. // TODO: Once GrPixelConfig is gone we'll need the source's color type here.
  159. GrColorType colorType = get_blur_color_type(proxy.get());
  160. sk_sp<GrRenderTargetContext> dstRenderTargetContext;
  161. dstRenderTargetContext = context->priv().makeDeferredRenderTargetContext(
  162. fit,
  163. srcRect.width(),
  164. srcRect.height(),
  165. colorType,
  166. std::move(finalCS),
  167. 1,
  168. GrMipMapped::kNo,
  169. proxy->origin(),
  170. nullptr,
  171. SkBudgeted::kYes,
  172. proxy->isProtected() ? GrProtected::kYes : GrProtected::kNo);
  173. if (!dstRenderTargetContext) {
  174. return nullptr;
  175. }
  176. GrFixedClip clip(SkIRect::MakeWH(finalW, finalH));
  177. int bounds[2] = { 0, 0 };
  178. SkIRect dstRect = SkIRect::MakeWH(srcRect.width(), srcRect.height());
  179. SkIPoint netOffset = srcOffset - proxyOffset;
  180. if (GrTextureDomain::kIgnore_Mode == mode) {
  181. *contentRect = dstRect;
  182. convolve_gaussian_1d(dstRenderTargetContext.get(), clip, dstRect, netOffset,
  183. std::move(proxy), direction, radius, sigma,
  184. GrTextureDomain::kIgnore_Mode, bounds);
  185. return dstRenderTargetContext;
  186. }
  187. // These destination rects need to be adjusted by srcOffset, but should *not* be adjusted by
  188. // the proxyOffset, which is why keeping them separate is convenient.
  189. SkIRect midRect = *contentRect, leftRect, rightRect;
  190. midRect.offset(srcOffset);
  191. SkIRect topRect, bottomRect;
  192. if (Direction::kX == direction) {
  193. bounds[0] = contentRect->left() + proxyOffset.x();
  194. bounds[1] = contentRect->right() + proxyOffset.x();
  195. topRect = SkIRect::MakeLTRB(0, 0, dstRect.right(), midRect.top());
  196. bottomRect = SkIRect::MakeLTRB(0, midRect.bottom(), dstRect.right(), dstRect.bottom());
  197. midRect.inset(radius, 0);
  198. leftRect = SkIRect::MakeLTRB(0, midRect.top(), midRect.left(), midRect.bottom());
  199. rightRect =
  200. SkIRect::MakeLTRB(midRect.right(), midRect.top(), dstRect.width(), midRect.bottom());
  201. dstRect.fTop = midRect.top();
  202. dstRect.fBottom = midRect.bottom();
  203. contentRect->fLeft = dstRect.fLeft;
  204. contentRect->fTop = midRect.fTop;
  205. contentRect->fRight = dstRect.fRight;
  206. contentRect->fBottom = midRect.fBottom;
  207. } else {
  208. bounds[0] = contentRect->top() + proxyOffset.y();
  209. bounds[1] = contentRect->bottom() + proxyOffset.y();
  210. topRect = SkIRect::MakeLTRB(0, 0, midRect.left(), dstRect.bottom());
  211. bottomRect = SkIRect::MakeLTRB(midRect.right(), 0, dstRect.right(), dstRect.bottom());
  212. midRect.inset(0, radius);
  213. leftRect = SkIRect::MakeLTRB(midRect.left(), 0, midRect.right(), midRect.top());
  214. rightRect =
  215. SkIRect::MakeLTRB(midRect.left(), midRect.bottom(), midRect.right(), dstRect.height());
  216. dstRect.fLeft = midRect.left();
  217. dstRect.fRight = midRect.right();
  218. contentRect->fLeft = midRect.fLeft;
  219. contentRect->fTop = dstRect.fTop;
  220. contentRect->fRight = midRect.fRight;
  221. contentRect->fBottom = dstRect.fBottom;
  222. }
  223. if (!topRect.isEmpty()) {
  224. dstRenderTargetContext->clear(&topRect, SK_PMColor4fTRANSPARENT,
  225. GrRenderTargetContext::CanClearFullscreen::kNo);
  226. }
  227. if (!bottomRect.isEmpty()) {
  228. dstRenderTargetContext->clear(&bottomRect, SK_PMColor4fTRANSPARENT,
  229. GrRenderTargetContext::CanClearFullscreen::kNo);
  230. }
  231. if (midRect.isEmpty()) {
  232. // Blur radius covers srcBounds; use bounds over entire draw
  233. convolve_gaussian_1d(dstRenderTargetContext.get(), clip, dstRect, netOffset,
  234. std::move(proxy), direction, radius, sigma, mode, bounds);
  235. } else {
  236. // Draw right and left margins with bounds; middle without.
  237. convolve_gaussian_1d(dstRenderTargetContext.get(), clip, leftRect, netOffset,
  238. proxy, direction, radius, sigma, mode, bounds);
  239. convolve_gaussian_1d(dstRenderTargetContext.get(), clip, rightRect, netOffset,
  240. proxy, direction, radius, sigma, mode, bounds);
  241. convolve_gaussian_1d(dstRenderTargetContext.get(), clip, midRect, netOffset,
  242. std::move(proxy), direction, radius, sigma,
  243. GrTextureDomain::kIgnore_Mode, bounds);
  244. }
  245. return dstRenderTargetContext;
  246. }
  247. // Note: despite its name this function does not kill every tenth legionnaire.
  248. static sk_sp<GrTextureProxy> decimate(GrRecordingContext* context,
  249. sk_sp<GrTextureProxy> src,
  250. const SkIPoint& proxyOffset,
  251. SkIPoint* srcOffset,
  252. SkIRect* contentRect,
  253. int scaleFactorX, int scaleFactorY,
  254. bool willBeXFiltering, bool willBeYFiltering,
  255. int radiusX, int radiusY,
  256. GrTextureDomain::Mode mode,
  257. int finalW,
  258. int finalH,
  259. sk_sp<SkColorSpace> finalCS) {
  260. SkASSERT(SkIsPow2(scaleFactorX) && SkIsPow2(scaleFactorY));
  261. SkASSERT(scaleFactorX > 1 || scaleFactorY > 1);
  262. // TODO: Once GrPixelConfig is gone we'll need the source's color type here.
  263. GrColorType colorType = get_blur_color_type(src.get());
  264. SkIRect srcRect;
  265. if (GrTextureDomain::kIgnore_Mode == mode) {
  266. srcRect = SkIRect::MakeWH(finalW, finalH);
  267. } else {
  268. srcRect = *contentRect;
  269. srcRect.offset(*srcOffset);
  270. }
  271. scale_irect_roundout(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
  272. scale_irect(&srcRect, scaleFactorX, scaleFactorY);
  273. SkIRect dstRect(srcRect);
  274. sk_sp<GrRenderTargetContext> dstRenderTargetContext;
  275. for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) {
  276. shrink_irect_by_2(&dstRect, i < scaleFactorX, i < scaleFactorY);
  277. // We know this will not be the final draw so we are free to make it an approx match.
  278. dstRenderTargetContext = context->priv().makeDeferredRenderTargetContext(
  279. SkBackingFit::kApprox,
  280. dstRect.fRight,
  281. dstRect.fBottom,
  282. colorType,
  283. finalCS,
  284. 1,
  285. GrMipMapped::kNo,
  286. src->origin(),
  287. nullptr,
  288. SkBudgeted::kYes,
  289. src->isProtected() ? GrProtected::kYes : GrProtected::kNo);
  290. if (!dstRenderTargetContext) {
  291. return nullptr;
  292. }
  293. GrPaint paint;
  294. if (GrTextureDomain::kIgnore_Mode != mode && i == 1) {
  295. // GrTextureDomainEffect does not support kRepeat_Mode with GrSamplerState::Filter.
  296. GrTextureDomain::Mode modeForScaling = GrTextureDomain::kRepeat_Mode == mode
  297. ? GrTextureDomain::kDecal_Mode
  298. : mode;
  299. SkRect domain = SkRect::Make(*contentRect);
  300. domain.inset((i < scaleFactorX) ? SK_ScalarHalf + SK_ScalarNearlyZero : 0.0f,
  301. (i < scaleFactorY) ? SK_ScalarHalf + SK_ScalarNearlyZero : 0.0f);
  302. // Ensure that the insetting doesn't invert the domain rectangle.
  303. if (domain.fRight < domain.fLeft) {
  304. domain.fLeft = domain.fRight = SkScalarAve(domain.fLeft, domain.fRight);
  305. }
  306. if (domain.fBottom < domain.fTop) {
  307. domain.fTop = domain.fBottom = SkScalarAve(domain.fTop, domain.fBottom);
  308. }
  309. domain.offset(proxyOffset.x(), proxyOffset.y());
  310. auto fp = GrTextureDomainEffect::Make(std::move(src),
  311. SkMatrix::I(),
  312. domain,
  313. modeForScaling,
  314. GrSamplerState::Filter::kBilerp);
  315. paint.addColorFragmentProcessor(std::move(fp));
  316. srcRect.offset(-(*srcOffset));
  317. // TODO: consume the srcOffset in both first draws and always set it to zero
  318. // back in GaussianBlur
  319. srcOffset->set(0, 0);
  320. } else {
  321. paint.addColorTextureProcessor(std::move(src), SkMatrix::I(),
  322. GrSamplerState::ClampBilerp());
  323. }
  324. paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
  325. dstRenderTargetContext->fillRectToRect(GrFixedClip::Disabled(), std::move(paint), GrAA::kNo,
  326. SkMatrix::I(), SkRect::Make(dstRect),
  327. SkRect::Make(srcRect.makeOffset(proxyOffset.x(),
  328. proxyOffset.y())));
  329. src = dstRenderTargetContext->asTextureProxyRef();
  330. if (!src) {
  331. return nullptr;
  332. }
  333. srcRect = dstRect;
  334. }
  335. *contentRect = dstRect;
  336. SkASSERT(dstRenderTargetContext);
  337. if (willBeXFiltering) {
  338. if (scaleFactorX > 1) {
  339. // Clear out a radius to the right of the contentRect to prevent the
  340. // X convolution from reading garbage.
  341. SkIRect clearRect = SkIRect::MakeXYWH(contentRect->fRight, contentRect->fTop,
  342. radiusX, contentRect->height());
  343. dstRenderTargetContext->priv().absClear(&clearRect, SK_PMColor4fTRANSPARENT);
  344. }
  345. } else {
  346. if (scaleFactorY > 1) {
  347. // Clear out a radius below the contentRect to prevent the Y
  348. // convolution from reading garbage.
  349. SkIRect clearRect = SkIRect::MakeXYWH(contentRect->fLeft, contentRect->fBottom,
  350. contentRect->width(), radiusY);
  351. dstRenderTargetContext->priv().absClear(&clearRect, SK_PMColor4fTRANSPARENT);
  352. }
  353. }
  354. return dstRenderTargetContext->asTextureProxyRef();
  355. }
  356. // Expand the contents of 'srcRenderTargetContext' to fit in 'dstII'. At this point, we are
  357. // expanding an intermediate image, so there's no need to account for a proxy offset from the
  358. // original input.
  359. static sk_sp<GrRenderTargetContext> reexpand(GrRecordingContext* context,
  360. sk_sp<GrRenderTargetContext> srcRenderTargetContext,
  361. const SkIRect& localSrcBounds,
  362. int scaleFactorX, int scaleFactorY,
  363. GrTextureDomain::Mode mode,
  364. int finalW,
  365. int finalH,
  366. sk_sp<SkColorSpace> finalCS,
  367. SkBackingFit fit) {
  368. const SkIRect srcRect = SkIRect::MakeWH(srcRenderTargetContext->width(),
  369. srcRenderTargetContext->height());
  370. // Clear one pixel to the right and below, to accommodate bilinear upsampling.
  371. // TODO: it seems like we should actually be clamping here rather than darkening
  372. // the bottom right edges.
  373. SkIRect clearRect = SkIRect::MakeXYWH(srcRect.fLeft, srcRect.fBottom, srcRect.width() + 1, 1);
  374. srcRenderTargetContext->priv().absClear(&clearRect, SK_PMColor4fTRANSPARENT);
  375. clearRect = SkIRect::MakeXYWH(srcRect.fRight, srcRect.fTop, 1, srcRect.height());
  376. srcRenderTargetContext->priv().absClear(&clearRect, SK_PMColor4fTRANSPARENT);
  377. sk_sp<GrTextureProxy> srcProxy = srcRenderTargetContext->asTextureProxyRef();
  378. if (!srcProxy) {
  379. return nullptr;
  380. }
  381. srcRenderTargetContext = nullptr; // no longer needed
  382. // TODO: Once GrPixelConfig is gone we'll need the source's color type here.
  383. GrColorType colorType = get_blur_color_type(srcProxy.get());
  384. sk_sp<GrRenderTargetContext> dstRenderTargetContext =
  385. context->priv().makeDeferredRenderTargetContext(fit, finalW, finalH, colorType,
  386. std::move(finalCS), 1, GrMipMapped::kNo,
  387. srcProxy->origin());
  388. if (!dstRenderTargetContext) {
  389. return nullptr;
  390. }
  391. GrPaint paint;
  392. if (GrTextureDomain::kIgnore_Mode != mode) {
  393. // GrTextureDomainEffect does not support kRepeat_Mode with GrSamplerState::Filter.
  394. GrTextureDomain::Mode modeForScaling = GrTextureDomain::kRepeat_Mode == mode
  395. ? GrTextureDomain::kDecal_Mode
  396. : mode;
  397. SkRect domain = SkRect::Make(localSrcBounds);
  398. auto fp = GrTextureDomainEffect::Make(std::move(srcProxy),
  399. SkMatrix::I(),
  400. domain,
  401. modeForScaling,
  402. GrSamplerState::Filter::kBilerp);
  403. paint.addColorFragmentProcessor(std::move(fp));
  404. } else {
  405. // FIXME: this should be mitchell, not bilinear.
  406. paint.addColorTextureProcessor(std::move(srcProxy), SkMatrix::I(),
  407. GrSamplerState::ClampBilerp());
  408. }
  409. paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
  410. GrFixedClip clip(SkIRect::MakeWH(finalW, finalH));
  411. // TODO: using dstII as dstRect results in some image diffs - why?
  412. SkIRect dstRect(srcRect);
  413. scale_irect(&dstRect, scaleFactorX, scaleFactorY);
  414. dstRenderTargetContext->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
  415. SkRect::Make(dstRect), SkRect::Make(srcRect));
  416. return dstRenderTargetContext;
  417. }
  418. namespace SkGpuBlurUtils {
  419. sk_sp<GrRenderTargetContext> GaussianBlur(GrRecordingContext* context,
  420. sk_sp<GrTextureProxy> srcProxy,
  421. const SkIPoint& proxyOffset,
  422. sk_sp<SkColorSpace> colorSpace,
  423. const SkIRect& dstBounds,
  424. const SkIRect& srcBounds,
  425. float sigmaX,
  426. float sigmaY,
  427. GrTextureDomain::Mode mode,
  428. SkAlphaType at,
  429. SkBackingFit fit) {
  430. SkASSERT(context);
  431. int finalW = dstBounds.width();
  432. int finalH = dstBounds.height();
  433. int scaleFactorX, radiusX;
  434. int scaleFactorY, radiusY;
  435. int maxTextureSize = context->priv().caps()->maxTextureSize();
  436. sigmaX = adjust_sigma(sigmaX, maxTextureSize, &scaleFactorX, &radiusX);
  437. sigmaY = adjust_sigma(sigmaY, maxTextureSize, &scaleFactorY, &radiusY);
  438. SkASSERT(sigmaX || sigmaY);
  439. SkIPoint srcOffset = SkIPoint::Make(-dstBounds.x(), -dstBounds.y());
  440. SkIRect localSrcBounds = srcBounds;
  441. SkIPoint localProxyOffset = proxyOffset;
  442. // For really small blurs (certainly no wider than 5x5 on desktop gpus) it is faster to just
  443. // launch a single non separable kernel vs two launches
  444. if (sigmaX > 0.0f && sigmaY > 0.0f &&
  445. (2 * radiusX + 1) * (2 * radiusY + 1) <= MAX_KERNEL_SIZE) {
  446. // We shouldn't be scaling because this is a small size blur
  447. SkASSERT((1 == scaleFactorX) && (1 == scaleFactorY));
  448. // Apply the proxy offset to src bounds and offset directly
  449. srcOffset -= proxyOffset;
  450. localSrcBounds.offset(proxyOffset);
  451. return convolve_gaussian_2d(context, std::move(srcProxy), localSrcBounds, srcOffset,
  452. radiusX, radiusY, sigmaX, sigmaY, mode, finalW, finalH,
  453. colorSpace, fit);
  454. }
  455. // Only the last rendered renderTargetContext needs to match the supplied 'fit'
  456. SkBackingFit xFit = fit, yFit = fit;
  457. if (scaleFactorX > 1 || scaleFactorY > 1) {
  458. xFit = yFit = SkBackingFit::kApprox; // reexpand will be last
  459. } else if (sigmaY > 0.0f) {
  460. xFit = SkBackingFit::kApprox; // the y-pass will be last
  461. }
  462. if (scaleFactorX > 1 || scaleFactorY > 1) {
  463. srcProxy = decimate(context, std::move(srcProxy), localProxyOffset, &srcOffset,
  464. &localSrcBounds, scaleFactorX, scaleFactorY, sigmaX > 0.0f,
  465. sigmaY > 0.0f, radiusX, radiusY, mode, finalW, finalH, colorSpace);
  466. localProxyOffset.set(0, 0);
  467. if (!srcProxy) {
  468. return nullptr;
  469. }
  470. }
  471. sk_sp<GrRenderTargetContext> dstRenderTargetContext;
  472. auto srcRect = SkIRect::MakeWH(finalW, finalH);
  473. scale_irect_roundout(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
  474. if (sigmaX > 0.0f) {
  475. dstRenderTargetContext = convolve_gaussian(
  476. context, std::move(srcProxy), localProxyOffset, srcRect, srcOffset, Direction::kX,
  477. radiusX, sigmaX, &localSrcBounds, mode, finalW, finalH, colorSpace, xFit);
  478. if (!dstRenderTargetContext) {
  479. return nullptr;
  480. }
  481. if (sigmaY > 0.0f) {
  482. // Clear out a radius below the srcRect to prevent the Y
  483. // convolution from reading garbage.
  484. SkIRect clearRect = SkIRect::MakeXYWH(srcRect.fLeft, srcRect.fBottom,
  485. srcRect.width(), radiusY);
  486. dstRenderTargetContext->priv().absClear(&clearRect, SK_PMColor4fTRANSPARENT);
  487. }
  488. srcProxy = dstRenderTargetContext->asTextureProxyRef();
  489. if (!srcProxy) {
  490. return nullptr;
  491. }
  492. srcRect.offsetTo(0, 0);
  493. srcOffset.set(0, 0);
  494. localProxyOffset.set(0, 0);
  495. }
  496. if (sigmaY > 0.0f) {
  497. dstRenderTargetContext = convolve_gaussian(
  498. context, std::move(srcProxy), localProxyOffset, srcRect, srcOffset, Direction::kY,
  499. radiusY, sigmaY, &localSrcBounds, mode, finalW, finalH, colorSpace, yFit);
  500. if (!dstRenderTargetContext) {
  501. return nullptr;
  502. }
  503. srcProxy = dstRenderTargetContext->asTextureProxyRef();
  504. if (!srcProxy) {
  505. return nullptr;
  506. }
  507. srcRect.offsetTo(0, 0);
  508. srcOffset.set(0, 0);
  509. localProxyOffset.set(0, 0);
  510. }
  511. SkASSERT(dstRenderTargetContext);
  512. SkASSERT(srcProxy.get() == dstRenderTargetContext->asTextureProxy());
  513. SkASSERT(localProxyOffset.x() == 0 && localProxyOffset.y() == 0);
  514. if (scaleFactorX > 1 || scaleFactorY > 1) {
  515. dstRenderTargetContext =
  516. reexpand(context, std::move(dstRenderTargetContext), localSrcBounds, scaleFactorX,
  517. scaleFactorY, mode, finalW, finalH, colorSpace, fit);
  518. }
  519. SkASSERT(!dstRenderTargetContext || dstRenderTargetContext->origin() == srcProxy->origin());
  520. return dstRenderTargetContext;
  521. }
  522. }
  523. #endif