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 |