| OLD | NEW |
| (Empty) |
| 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 | |
| 8 #include "SkGpuBlurUtils.h" | |
| 9 | |
| 10 #include "SkRect.h" | |
| 11 | |
| 12 #if SK_SUPPORT_GPU | |
| 13 #include "effects/GrConvolutionEffect.h" | |
| 14 #include "effects/GrMatrixConvolutionEffect.h" | |
| 15 #include "GrContext.h" | |
| 16 #include "GrCaps.h" | |
| 17 #include "GrDrawContext.h" | |
| 18 #include "GrFixedClip.h" | |
| 19 | |
| 20 #define MAX_BLUR_SIGMA 4.0f | |
| 21 | |
| 22 static void scale_irect_roundout(SkIRect* rect, float xScale, float yScale) { | |
| 23 rect->fLeft = SkScalarFloorToInt(SkScalarMul(rect->fLeft, xScale)); | |
| 24 rect->fTop = SkScalarFloorToInt(SkScalarMul(rect->fTop, yScale)); | |
| 25 rect->fRight = SkScalarCeilToInt(SkScalarMul(rect->fRight, xScale)); | |
| 26 rect->fBottom = SkScalarCeilToInt(SkScalarMul(rect->fBottom, yScale)); | |
| 27 } | |
| 28 | |
| 29 static void scale_irect(SkIRect* rect, int xScale, int yScale) { | |
| 30 rect->fLeft *= xScale; | |
| 31 rect->fTop *= yScale; | |
| 32 rect->fRight *= xScale; | |
| 33 rect->fBottom *= yScale; | |
| 34 } | |
| 35 | |
| 36 #ifdef SK_DEBUG | |
| 37 static inline int is_even(int x) { return !(x & 1); } | |
| 38 #endif | |
| 39 | |
| 40 static void shrink_irect_by_2(SkIRect* rect, bool xAxis, bool yAxis) { | |
| 41 if (xAxis) { | |
| 42 SkASSERT(is_even(rect->fLeft) && is_even(rect->fRight)); | |
| 43 rect->fLeft /= 2; | |
| 44 rect->fRight /= 2; | |
| 45 } | |
| 46 if (yAxis) { | |
| 47 SkASSERT(is_even(rect->fTop) && is_even(rect->fBottom)); | |
| 48 rect->fTop /= 2; | |
| 49 rect->fBottom /= 2; | |
| 50 } | |
| 51 } | |
| 52 | |
| 53 static float adjust_sigma(float sigma, int maxTextureSize, int *scaleFactor, int
*radius) { | |
| 54 *scaleFactor = 1; | |
| 55 while (sigma > MAX_BLUR_SIGMA) { | |
| 56 *scaleFactor *= 2; | |
| 57 sigma *= 0.5f; | |
| 58 if (*scaleFactor > maxTextureSize) { | |
| 59 *scaleFactor = maxTextureSize; | |
| 60 sigma = MAX_BLUR_SIGMA; | |
| 61 } | |
| 62 } | |
| 63 *radius = static_cast<int>(ceilf(sigma * 3.0f)); | |
| 64 SkASSERT(*radius <= GrConvolutionEffect::kMaxKernelRadius); | |
| 65 return sigma; | |
| 66 } | |
| 67 | |
| 68 static void convolve_gaussian_1d(GrDrawContext* drawContext, | |
| 69 const GrClip& clip, | |
| 70 const SkIRect& dstRect, | |
| 71 const SkIPoint& srcOffset, | |
| 72 GrTexture* texture, | |
| 73 Gr1DKernelEffect::Direction direction, | |
| 74 int radius, | |
| 75 float sigma, | |
| 76 bool useBounds, | |
| 77 float bounds[2]) { | |
| 78 GrPaint paint; | |
| 79 paint.setGammaCorrect(drawContext->isGammaCorrect()); | |
| 80 sk_sp<GrFragmentProcessor> conv(GrConvolutionEffect::MakeGaussian( | |
| 81 texture, direction, radius, sigma, useBounds, bounds)); | |
| 82 paint.addColorFragmentProcessor(std::move(conv)); | |
| 83 paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); | |
| 84 SkMatrix localMatrix = SkMatrix::MakeTrans(-SkIntToScalar(srcOffset.x()), | |
| 85 -SkIntToScalar(srcOffset.y())); | |
| 86 drawContext->fillRectWithLocalMatrix(clip, paint, SkMatrix::I(), | |
| 87 SkRect::Make(dstRect), localMatrix); | |
| 88 } | |
| 89 | |
| 90 static void convolve_gaussian_2d(GrDrawContext* drawContext, | |
| 91 const GrClip& clip, | |
| 92 const SkIRect& dstRect, | |
| 93 const SkIPoint& srcOffset, | |
| 94 GrTexture* texture, | |
| 95 int radiusX, | |
| 96 int radiusY, | |
| 97 SkScalar sigmaX, | |
| 98 SkScalar sigmaY, | |
| 99 const SkIRect* srcBounds) { | |
| 100 SkMatrix localMatrix = SkMatrix::MakeTrans(-SkIntToScalar(srcOffset.x()), | |
| 101 -SkIntToScalar(srcOffset.y())); | |
| 102 SkISize size = SkISize::Make(2 * radiusX + 1, 2 * radiusY + 1); | |
| 103 SkIPoint kernelOffset = SkIPoint::Make(radiusX, radiusY); | |
| 104 GrPaint paint; | |
| 105 paint.setGammaCorrect(drawContext->isGammaCorrect()); | |
| 106 SkIRect bounds = srcBounds ? *srcBounds : SkIRect::EmptyIRect(); | |
| 107 | |
| 108 sk_sp<GrFragmentProcessor> conv(GrMatrixConvolutionEffect::MakeGaussian( | |
| 109 texture, bounds, size, 1.0, 0.0, kernelOffset, | |
| 110 srcBounds ? GrTextureDomain::kDecal_Mode : GrTextureDomain::kIgnore_
Mode, | |
| 111 true, sigmaX, sigmaY)); | |
| 112 paint.addColorFragmentProcessor(std::move(conv)); | |
| 113 paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); | |
| 114 drawContext->fillRectWithLocalMatrix(clip, paint, SkMatrix::I(), | |
| 115 SkRect::Make(dstRect), localMatrix); | |
| 116 } | |
| 117 | |
| 118 static void convolve_gaussian(GrDrawContext* drawContext, | |
| 119 const GrClip& clip, | |
| 120 const SkIRect& srcRect, | |
| 121 GrTexture* texture, | |
| 122 Gr1DKernelEffect::Direction direction, | |
| 123 int radius, | |
| 124 float sigma, | |
| 125 const SkIRect* srcBounds, | |
| 126 const SkIPoint& srcOffset) { | |
| 127 float bounds[2] = { 0.0f, 1.0f }; | |
| 128 SkIRect dstRect = SkIRect::MakeWH(srcRect.width(), srcRect.height()); | |
| 129 if (!srcBounds) { | |
| 130 convolve_gaussian_1d(drawContext, clip, dstRect, srcOffset, texture, | |
| 131 direction, radius, sigma, false, bounds); | |
| 132 return; | |
| 133 } | |
| 134 SkIRect midRect = *srcBounds, leftRect, rightRect; | |
| 135 midRect.offset(srcOffset); | |
| 136 SkIRect topRect, bottomRect; | |
| 137 if (direction == Gr1DKernelEffect::kX_Direction) { | |
| 138 bounds[0] = SkIntToFloat(srcBounds->left()) / texture->width(); | |
| 139 bounds[1] = SkIntToFloat(srcBounds->right()) / texture->width(); | |
| 140 topRect = SkIRect::MakeLTRB(0, 0, dstRect.right(), midRect.top()); | |
| 141 bottomRect = SkIRect::MakeLTRB(0, midRect.bottom(), dstRect.right(), dst
Rect.bottom()); | |
| 142 midRect.inset(radius, 0); | |
| 143 leftRect = SkIRect::MakeLTRB(0, midRect.top(), midRect.left(), midRect.b
ottom()); | |
| 144 rightRect = | |
| 145 SkIRect::MakeLTRB(midRect.right(), midRect.top(), dstRect.width(), m
idRect.bottom()); | |
| 146 dstRect.fTop = midRect.top(); | |
| 147 dstRect.fBottom = midRect.bottom(); | |
| 148 } else { | |
| 149 bounds[0] = SkIntToFloat(srcBounds->top()) / texture->height(); | |
| 150 bounds[1] = SkIntToFloat(srcBounds->bottom()) / texture->height(); | |
| 151 topRect = SkIRect::MakeLTRB(0, 0, midRect.left(), dstRect.bottom()); | |
| 152 bottomRect = SkIRect::MakeLTRB(midRect.right(), 0, dstRect.right(), dstR
ect.bottom()); | |
| 153 midRect.inset(0, radius); | |
| 154 leftRect = SkIRect::MakeLTRB(midRect.left(), 0, midRect.right(), midRect
.top()); | |
| 155 rightRect = | |
| 156 SkIRect::MakeLTRB(midRect.left(), midRect.bottom(), midRect.right(),
dstRect.height()); | |
| 157 dstRect.fLeft = midRect.left(); | |
| 158 dstRect.fRight = midRect.right(); | |
| 159 } | |
| 160 if (!topRect.isEmpty()) { | |
| 161 drawContext->clear(&topRect, 0, false); | |
| 162 } | |
| 163 | |
| 164 if (!bottomRect.isEmpty()) { | |
| 165 drawContext->clear(&bottomRect, 0, false); | |
| 166 } | |
| 167 if (midRect.isEmpty()) { | |
| 168 // Blur radius covers srcBounds; use bounds over entire draw | |
| 169 convolve_gaussian_1d(drawContext, clip, dstRect, srcOffset, texture, | |
| 170 direction, radius, sigma, true, bounds); | |
| 171 } else { | |
| 172 // Draw right and left margins with bounds; middle without. | |
| 173 convolve_gaussian_1d(drawContext, clip, leftRect, srcOffset, texture, | |
| 174 direction, radius, sigma, true, bounds); | |
| 175 convolve_gaussian_1d(drawContext, clip, rightRect, srcOffset, texture, | |
| 176 direction, radius, sigma, true, bounds); | |
| 177 convolve_gaussian_1d(drawContext, clip, midRect, srcOffset, texture, | |
| 178 direction, radius, sigma, false, bounds); | |
| 179 } | |
| 180 } | |
| 181 | |
| 182 namespace SkGpuBlurUtils { | |
| 183 | |
| 184 sk_sp<GrDrawContext> GaussianBlur(GrContext* context, | |
| 185 GrTexture* origSrc, | |
| 186 sk_sp<SkColorSpace> colorSpace, | |
| 187 const SkIRect& dstBounds, | |
| 188 const SkIRect* srcBounds, | |
| 189 float sigmaX, | |
| 190 float sigmaY, | |
| 191 SkBackingFit fit) { | |
| 192 SkASSERT(context); | |
| 193 SkIRect clearRect; | |
| 194 int scaleFactorX, radiusX; | |
| 195 int scaleFactorY, radiusY; | |
| 196 int maxTextureSize = context->caps()->maxTextureSize(); | |
| 197 sigmaX = adjust_sigma(sigmaX, maxTextureSize, &scaleFactorX, &radiusX); | |
| 198 sigmaY = adjust_sigma(sigmaY, maxTextureSize, &scaleFactorY, &radiusY); | |
| 199 SkASSERT(sigmaX || sigmaY); | |
| 200 | |
| 201 SkIPoint srcOffset = SkIPoint::Make(-dstBounds.x(), -dstBounds.y()); | |
| 202 SkIRect localDstBounds = SkIRect::MakeWH(dstBounds.width(), dstBounds.height
()); | |
| 203 SkIRect localSrcBounds; | |
| 204 SkIRect srcRect; | |
| 205 if (srcBounds) { | |
| 206 srcRect = localSrcBounds = *srcBounds; | |
| 207 srcRect.offset(srcOffset); | |
| 208 srcBounds = &localSrcBounds; | |
| 209 } else { | |
| 210 srcRect = localDstBounds; | |
| 211 } | |
| 212 | |
| 213 scale_irect_roundout(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY); | |
| 214 scale_irect(&srcRect, scaleFactorX, scaleFactorY); | |
| 215 | |
| 216 // setup new clip | |
| 217 GrFixedClip clip(localDstBounds); | |
| 218 | |
| 219 sk_sp<GrTexture> srcTexture(sk_ref_sp(origSrc)); | |
| 220 | |
| 221 SkASSERT(kBGRA_8888_GrPixelConfig == srcTexture->config() || | |
| 222 kRGBA_8888_GrPixelConfig == srcTexture->config() || | |
| 223 kSRGBA_8888_GrPixelConfig == srcTexture->config() || | |
| 224 kSBGRA_8888_GrPixelConfig == srcTexture->config() || | |
| 225 kAlpha_8_GrPixelConfig == srcTexture->config()); | |
| 226 | |
| 227 const int width = dstBounds.width(); | |
| 228 const int height = dstBounds.height(); | |
| 229 const GrPixelConfig config = srcTexture->config(); | |
| 230 | |
| 231 sk_sp<GrDrawContext> dstDrawContext(context->makeDrawContext(fit, | |
| 232 width, height,
config, colorSpace, | |
| 233 0, kDefault_GrS
urfaceOrigin)); | |
| 234 if (!dstDrawContext) { | |
| 235 return nullptr; | |
| 236 } | |
| 237 | |
| 238 // For really small blurs (certainly no wider than 5x5 on desktop gpus) it i
s faster to just | |
| 239 // launch a single non separable kernel vs two launches | |
| 240 if (sigmaX > 0.0f && sigmaY > 0.0f && | |
| 241 (2 * radiusX + 1) * (2 * radiusY + 1) <= MAX_KERNEL_SIZE) { | |
| 242 // We shouldn't be scaling because this is a small size blur | |
| 243 SkASSERT((1 == scaleFactorX) && (1 == scaleFactorY)); | |
| 244 | |
| 245 convolve_gaussian_2d(dstDrawContext.get(), clip, localDstBounds, srcOffs
et, | |
| 246 srcTexture.get(), radiusX, radiusY, sigmaX, sigmaY,
srcBounds); | |
| 247 | |
| 248 return dstDrawContext; | |
| 249 } | |
| 250 | |
| 251 sk_sp<GrDrawContext> tmpDrawContext(context->makeDrawContext(fit, | |
| 252 width, height,
config, colorSpace, | |
| 253 0, kDefault_GrS
urfaceOrigin)); | |
| 254 if (!tmpDrawContext) { | |
| 255 return nullptr; | |
| 256 } | |
| 257 | |
| 258 sk_sp<GrDrawContext> srcDrawContext; | |
| 259 | |
| 260 SkASSERT(SkIsPow2(scaleFactorX) && SkIsPow2(scaleFactorY)); | |
| 261 | |
| 262 for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) { | |
| 263 GrPaint paint; | |
| 264 paint.setGammaCorrect(dstDrawContext->isGammaCorrect()); | |
| 265 SkMatrix matrix; | |
| 266 matrix.setIDiv(srcTexture->width(), srcTexture->height()); | |
| 267 SkIRect dstRect(srcRect); | |
| 268 if (srcBounds && i == 1) { | |
| 269 SkRect domain; | |
| 270 matrix.mapRect(&domain, SkRect::Make(*srcBounds)); | |
| 271 domain.inset((i < scaleFactorX) ? SK_ScalarHalf / srcTexture->width(
) : 0.0f, | |
| 272 (i < scaleFactorY) ? SK_ScalarHalf / srcTexture->height
() : 0.0f); | |
| 273 sk_sp<GrFragmentProcessor> fp(GrTextureDomainEffect::Make( | |
| 274 srcTexture.get(), | |
| 275 nullptr, | |
| 276 matrix, | |
| 277 domain, | |
| 278 GrTextureDomain::kDecal_
Mode, | |
| 279 GrTextureParams::kBilerp
_FilterMode)); | |
| 280 paint.addColorFragmentProcessor(std::move(fp)); | |
| 281 srcRect.offset(-srcOffset); | |
| 282 srcOffset.set(0, 0); | |
| 283 } else { | |
| 284 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::k
Bilerp_FilterMode); | |
| 285 paint.addColorTextureProcessor(srcTexture.get(), nullptr, matrix, pa
rams); | |
| 286 } | |
| 287 paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); | |
| 288 shrink_irect_by_2(&dstRect, i < scaleFactorX, i < scaleFactorY); | |
| 289 | |
| 290 dstDrawContext->fillRectToRect(clip, paint, SkMatrix::I(), | |
| 291 SkRect::Make(dstRect), SkRect::Make(srcRe
ct)); | |
| 292 | |
| 293 srcDrawContext = dstDrawContext; | |
| 294 srcRect = dstRect; | |
| 295 srcTexture = srcDrawContext->asTexture(); | |
| 296 dstDrawContext.swap(tmpDrawContext); | |
| 297 localSrcBounds = srcRect; | |
| 298 } | |
| 299 | |
| 300 srcRect = localDstBounds; | |
| 301 scale_irect_roundout(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY); | |
| 302 if (sigmaX > 0.0f) { | |
| 303 if (scaleFactorX > 1) { | |
| 304 SkASSERT(srcDrawContext); | |
| 305 | |
| 306 // Clear out a radius to the right of the srcRect to prevent the | |
| 307 // X convolution from reading garbage. | |
| 308 clearRect = SkIRect::MakeXYWH(srcRect.fRight, srcRect.fTop, | |
| 309 radiusX, srcRect.height()); | |
| 310 srcDrawContext->clear(&clearRect, 0x0, false); | |
| 311 } | |
| 312 | |
| 313 convolve_gaussian(dstDrawContext.get(), clip, srcRect, | |
| 314 srcTexture.get(), Gr1DKernelEffect::kX_Direction, radi
usX, sigmaX, | |
| 315 srcBounds, srcOffset); | |
| 316 srcDrawContext = dstDrawContext; | |
| 317 srcTexture = srcDrawContext->asTexture(); | |
| 318 srcRect.offsetTo(0, 0); | |
| 319 dstDrawContext.swap(tmpDrawContext); | |
| 320 localSrcBounds = srcRect; | |
| 321 srcOffset.set(0, 0); | |
| 322 } | |
| 323 | |
| 324 if (sigmaY > 0.0f) { | |
| 325 if (scaleFactorY > 1 || sigmaX > 0.0f) { | |
| 326 SkASSERT(srcDrawContext); | |
| 327 | |
| 328 // Clear out a radius below the srcRect to prevent the Y | |
| 329 // convolution from reading garbage. | |
| 330 clearRect = SkIRect::MakeXYWH(srcRect.fLeft, srcRect.fBottom, | |
| 331 srcRect.width(), radiusY); | |
| 332 srcDrawContext->clear(&clearRect, 0x0, false); | |
| 333 } | |
| 334 | |
| 335 convolve_gaussian(dstDrawContext.get(), clip, srcRect, | |
| 336 srcTexture.get(), Gr1DKernelEffect::kY_Direction, radi
usY, sigmaY, | |
| 337 srcBounds, srcOffset); | |
| 338 | |
| 339 srcDrawContext = dstDrawContext; | |
| 340 srcRect.offsetTo(0, 0); | |
| 341 dstDrawContext.swap(tmpDrawContext); | |
| 342 } | |
| 343 | |
| 344 SkASSERT(srcDrawContext); | |
| 345 srcTexture = nullptr; // we don't use this from here on out | |
| 346 | |
| 347 if (scaleFactorX > 1 || scaleFactorY > 1) { | |
| 348 // Clear one pixel to the right and below, to accommodate bilinear upsam
pling. | |
| 349 clearRect = SkIRect::MakeXYWH(srcRect.fLeft, srcRect.fBottom, srcRect.wi
dth() + 1, 1); | |
| 350 srcDrawContext->clear(&clearRect, 0x0, false); | |
| 351 clearRect = SkIRect::MakeXYWH(srcRect.fRight, srcRect.fTop, 1, srcRect.h
eight()); | |
| 352 srcDrawContext->clear(&clearRect, 0x0, false); | |
| 353 | |
| 354 SkMatrix matrix; | |
| 355 matrix.setIDiv(srcDrawContext->width(), srcDrawContext->height()); | |
| 356 | |
| 357 GrPaint paint; | |
| 358 paint.setGammaCorrect(dstDrawContext->isGammaCorrect()); | |
| 359 // FIXME: this should be mitchell, not bilinear. | |
| 360 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBile
rp_FilterMode); | |
| 361 sk_sp<GrTexture> tex(srcDrawContext->asTexture()); | |
| 362 paint.addColorTextureProcessor(tex.get(), nullptr, matrix, params); | |
| 363 paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); | |
| 364 | |
| 365 SkIRect dstRect(srcRect); | |
| 366 scale_irect(&dstRect, scaleFactorX, scaleFactorY); | |
| 367 | |
| 368 dstDrawContext->fillRectToRect(clip, paint, SkMatrix::I(), | |
| 369 SkRect::Make(dstRect), SkRect::Make(srcRe
ct)); | |
| 370 | |
| 371 srcDrawContext = dstDrawContext; | |
| 372 srcRect = dstRect; | |
| 373 dstDrawContext.swap(tmpDrawContext); | |
| 374 } | |
| 375 | |
| 376 return srcDrawContext; | |
| 377 } | |
| 378 | |
| 379 } | |
| 380 | |
| 381 #endif | |
| 382 | |
| OLD | NEW |