GrRectBlurEffect.fp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. /*
  2. * Copyright 2018 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. @header {
  8. #include "include/core/SkScalar.h"
  9. #include "src/core/SkBlurMask.h"
  10. #include "src/gpu/GrProxyProvider.h"
  11. #include "src/gpu/GrShaderCaps.h"
  12. }
  13. in uniform float4 rect;
  14. in float sigma;
  15. in uniform sampler2D blurProfile;
  16. @constructorParams {
  17. GrSamplerState samplerParams
  18. }
  19. @samplerParams(blurProfile) {
  20. samplerParams
  21. }
  22. // in OpenGL ES, mediump floats have a minimum range of 2^14. If we have coordinates bigger than
  23. // that, the shader math will end up with infinities and result in the blur effect not working
  24. // correctly. To avoid this, we switch into highp when the coordinates are too big. As 2^14 is the
  25. // minimum range but the actual range can be bigger, we might end up switching to highp sooner than
  26. // strictly necessary, but most devices that have a bigger range for mediump also have mediump being
  27. // exactly the same as highp (e.g. all non-OpenGL ES devices), and thus incur no additional penalty
  28. // for the switch.
  29. layout(key) bool highPrecision = abs(rect.x) > 16000 || abs(rect.y) > 16000 ||
  30. abs(rect.z) > 16000 || abs(rect.w) > 16000 ||
  31. abs(rect.z - rect.x) > 16000 || abs(rect.w - rect.y) > 16000;
  32. layout(when=!highPrecision) uniform half4 proxyRectHalf;
  33. layout(when=highPrecision) uniform float4 proxyRectFloat;
  34. uniform half profileSize;
  35. @class {
  36. static sk_sp<GrTextureProxy> CreateBlurProfileTexture(GrProxyProvider* proxyProvider,
  37. float sigma) {
  38. unsigned int profileSize = SkScalarCeilToInt(6 * sigma);
  39. static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
  40. GrUniqueKey key;
  41. GrUniqueKey::Builder builder(&key, kDomain, 1, "Rect Blur Mask");
  42. builder[0] = profileSize;
  43. builder.finish();
  44. sk_sp<GrTextureProxy> blurProfile(proxyProvider->findOrCreateProxyByUniqueKey(
  45. key, kTopLeft_GrSurfaceOrigin));
  46. if (!blurProfile) {
  47. SkImageInfo ii = SkImageInfo::MakeA8(profileSize, 1);
  48. SkBitmap bitmap;
  49. if (!bitmap.tryAllocPixels(ii)) {
  50. return nullptr;
  51. }
  52. SkBlurMask::ComputeBlurProfile(bitmap.getAddr8(0, 0), profileSize, sigma);
  53. bitmap.setImmutable();
  54. sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);
  55. if (!image) {
  56. return nullptr;
  57. }
  58. blurProfile = proxyProvider->createTextureProxy(std::move(image), GrRenderable::kNo,
  59. 1, SkBudgeted::kYes,
  60. SkBackingFit::kExact);
  61. if (!blurProfile) {
  62. return nullptr;
  63. }
  64. SkASSERT(blurProfile->origin() == kTopLeft_GrSurfaceOrigin);
  65. proxyProvider->assignUniqueKeyToProxy(key, blurProfile.get());
  66. }
  67. return blurProfile;
  68. }
  69. }
  70. @make {
  71. static std::unique_ptr<GrFragmentProcessor> Make(GrProxyProvider* proxyProvider,
  72. const GrShaderCaps& caps,
  73. const SkRect& rect, float sigma) {
  74. if (!caps.floatIs32Bits()) {
  75. // We promote the rect uniform from half to float when it has large values for
  76. // precision. If we don't have full float then fail.
  77. if (SkScalarAbs(rect.fLeft) > 16000.f || SkScalarAbs(rect.fTop) > 16000.f ||
  78. SkScalarAbs(rect.fRight) > 16000.f || SkScalarAbs(rect.fBottom) > 16000.f ||
  79. SkScalarAbs(rect.width()) > 16000.f || SkScalarAbs(rect.height()) > 16000.f) {
  80. return nullptr;
  81. }
  82. }
  83. int doubleProfileSize = SkScalarCeilToInt(12*sigma);
  84. if (doubleProfileSize >= rect.width() || doubleProfileSize >= rect.height()) {
  85. // if the blur sigma is too large so the gaussian overlaps the whole
  86. // rect in either direction, fall back to CPU path for now.
  87. return nullptr;
  88. }
  89. sk_sp<GrTextureProxy> blurProfile(CreateBlurProfileTexture(proxyProvider, sigma));
  90. if (!blurProfile) {
  91. return nullptr;
  92. }
  93. return std::unique_ptr<GrFragmentProcessor>(new GrRectBlurEffect(
  94. rect, sigma, std::move(blurProfile),
  95. GrSamplerState(GrSamplerState::WrapMode::kClamp, GrSamplerState::Filter::kBilerp)));
  96. }
  97. }
  98. void main() {
  99. @if (highPrecision) {
  100. float2 translatedPos = sk_FragCoord.xy - rect.xy;
  101. float width = rect.z - rect.x;
  102. float height = rect.w - rect.y;
  103. float2 smallDims = float2(width - profileSize, height - profileSize);
  104. float center = 2 * floor(profileSize / 2 + 0.25) - 1;
  105. float2 wh = smallDims - float2(center, center);
  106. half hcoord = half(((abs(translatedPos.x - 0.5 * width) - 0.5 * wh.x)) / profileSize);
  107. half hlookup = texture(blurProfile, float2(hcoord, 0.5)).a;
  108. half vcoord = half(((abs(translatedPos.y - 0.5 * height) - 0.5 * wh.y)) / profileSize);
  109. half vlookup = texture(blurProfile, float2(vcoord, 0.5)).a;
  110. sk_OutColor = sk_InColor * hlookup * vlookup;
  111. } else {
  112. half2 translatedPos = half2(sk_FragCoord.xy - rect.xy);
  113. half width = half(rect.z - rect.x);
  114. half height = half(rect.w - rect.y);
  115. half2 smallDims = half2(width - profileSize, height - profileSize);
  116. half center = 2 * floor(profileSize / 2 + 0.25) - 1;
  117. half2 wh = smallDims - half2(center, center);
  118. half hcoord = ((half(abs(translatedPos.x - 0.5 * width)) - 0.5 * wh.x)) / profileSize;
  119. half hlookup = texture(blurProfile, float2(hcoord, 0.5)).a;
  120. half vcoord = ((half(abs(translatedPos.y - 0.5 * height)) - 0.5 * wh.y)) / profileSize;
  121. half vlookup = texture(blurProfile, float2(vcoord, 0.5)).a;
  122. sk_OutColor = sk_InColor * hlookup * vlookup;
  123. }
  124. }
  125. @setData(pdman) {
  126. pdman.set1f(profileSize, SkScalarCeilToScalar(6 * sigma));
  127. }
  128. @optimizationFlags { kCompatibleWithCoverageAsAlpha_OptimizationFlag }
  129. @test(data) {
  130. float sigma = data->fRandom->nextRangeF(3,8);
  131. float width = data->fRandom->nextRangeF(200,300);
  132. float height = data->fRandom->nextRangeF(200,300);
  133. return GrRectBlurEffect::Make(data->proxyProvider(), *data->caps()->shaderCaps(),
  134. SkRect::MakeWH(width, height), sigma);
  135. }