blob: 28ccd4589b327712169647bb6933dc1217299db8 [file] [log] [blame]
robertphillips@google.com736dd032013-07-15 15:06:54 +00001/*
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"
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000014#include "effects/GrTextureDomainEffect.h"
robertphillips@google.com736dd032013-07-15 15:06:54 +000015#include "GrContext.h"
16#endif
17
18namespace SkGpuBlurUtils {
19
20#if SK_SUPPORT_GPU
21
22#define MAX_BLUR_SIGMA 4.0f
23
24static void scale_rect(SkRect* rect, float xScale, float yScale) {
25 rect->fLeft = SkScalarMul(rect->fLeft, SkFloatToScalar(xScale));
26 rect->fTop = SkScalarMul(rect->fTop, SkFloatToScalar(yScale));
27 rect->fRight = SkScalarMul(rect->fRight, SkFloatToScalar(xScale));
28 rect->fBottom = SkScalarMul(rect->fBottom, SkFloatToScalar(yScale));
29}
30
31static float adjust_sigma(float sigma, int *scaleFactor, int *radius) {
32 *scaleFactor = 1;
33 while (sigma > MAX_BLUR_SIGMA) {
34 *scaleFactor *= 2;
35 sigma *= 0.5f;
36 }
37 *radius = static_cast<int>(ceilf(sigma * 3.0f));
38 GrAssert(*radius <= GrConvolutionEffect::kMaxKernelRadius);
39 return sigma;
40}
41
42static void convolve_gaussian(GrContext* context,
43 GrTexture* texture,
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000044 const SkRect& srcRect,
45 const SkRect& dstRect,
46 bool cropToSrcRect,
robertphillips@google.com736dd032013-07-15 15:06:54 +000047 float sigma,
48 int radius,
49 Gr1DKernelEffect::Direction direction) {
50 GrPaint paint;
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000051 paint.reset();
senorblanco@chromium.orge8232bc2013-07-29 18:45:44 +000052 float bounds[2] = { 0.0f, 1.0f };
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000053 if (cropToSrcRect) {
54 if (direction == Gr1DKernelEffect::kX_Direction) {
senorblanco@chromium.orge8232bc2013-07-29 18:45:44 +000055 bounds[0] = SkScalarToFloat(srcRect.left()) / texture->width();
56 bounds[1] = SkScalarToFloat(srcRect.right()) / texture->width();
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000057 } else {
senorblanco@chromium.orge8232bc2013-07-29 18:45:44 +000058 bounds[0] = SkScalarToFloat(srcRect.top()) / texture->height();
59 bounds[1] = SkScalarToFloat(srcRect.bottom()) / texture->height();
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000060 }
61 }
robertphillips@google.com736dd032013-07-15 15:06:54 +000062
63 SkAutoTUnref<GrEffectRef> conv(GrConvolutionEffect::CreateGaussian(texture,
64 direction,
65 radius,
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000066 sigma,
67 cropToSrcRect,
senorblanco@chromium.orge8232bc2013-07-29 18:45:44 +000068 bounds));
robertphillips@google.com736dd032013-07-15 15:06:54 +000069 paint.addColorEffect(conv);
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000070 context->drawRectToRect(paint, dstRect, srcRect);
robertphillips@google.com736dd032013-07-15 15:06:54 +000071}
72
73GrTexture* GaussianBlur(GrContext* context,
74 GrTexture* srcTexture,
75 bool canClobberSrc,
76 const SkRect& rect,
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000077 bool cropToRect,
skia.committer@gmail.com977409a2013-07-16 07:00:56 +000078 float sigmaX,
robertphillips@google.com736dd032013-07-15 15:06:54 +000079 float sigmaY) {
80 GrAssert(NULL != context);
81
82 GrContext::AutoRenderTarget art(context);
83
84 GrContext::AutoMatrix am;
85 am.setIdentity(context);
86
87 SkIRect clearRect;
88 int scaleFactorX, radiusX;
89 int scaleFactorY, radiusY;
90 sigmaX = adjust_sigma(sigmaX, &scaleFactorX, &radiusX);
91 sigmaY = adjust_sigma(sigmaY, &scaleFactorY, &radiusY);
92
93 SkRect srcRect(rect);
94 scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
95 srcRect.roundOut();
96 scale_rect(&srcRect, static_cast<float>(scaleFactorX),
97 static_cast<float>(scaleFactorY));
98
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000099 GrContext::AutoClip acs(context, SkRect::MakeWH(srcRect.width(), srcRect.height()));
robertphillips@google.com736dd032013-07-15 15:06:54 +0000100
101 GrAssert(kBGRA_8888_GrPixelConfig == srcTexture->config() ||
102 kRGBA_8888_GrPixelConfig == srcTexture->config() ||
103 kAlpha_8_GrPixelConfig == srcTexture->config());
104
105 GrTextureDesc desc;
106 desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
107 desc.fWidth = SkScalarFloorToInt(srcRect.width());
108 desc.fHeight = SkScalarFloorToInt(srcRect.height());
109 desc.fConfig = srcTexture->config();
110
111 GrAutoScratchTexture temp1, temp2;
112 GrTexture* dstTexture = temp1.set(context, desc);
113 GrTexture* tempTexture = canClobberSrc ? srcTexture : temp2.set(context, desc);
114 if (NULL == dstTexture || NULL == tempTexture) {
115 return NULL;
116 }
117
118 for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) {
119 GrPaint paint;
120 SkMatrix matrix;
121 matrix.setIDiv(srcTexture->width(), srcTexture->height());
122 context->setRenderTarget(dstTexture->asRenderTarget());
123 SkRect dstRect(srcRect);
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000124 if (cropToRect && i == 1) {
125 dstRect.offset(-dstRect.fLeft, -dstRect.fTop);
126 SkRect domain;
127 matrix.mapRect(&domain, rect);
128 domain.inset(i < scaleFactorX ? SK_ScalarHalf / srcTexture->width() : 0.0f,
129 i < scaleFactorY ? SK_ScalarHalf / srcTexture->height() : 0.0f);
130 SkAutoTUnref<GrEffectRef> effect(GrTextureDomainEffect::Create(
131 srcTexture,
132 matrix,
133 domain,
134 GrTextureDomainEffect::kDecal_WrapMode,
humper@google.comb86add12013-07-25 18:49:07 +0000135 GrTextureParams::kBilerp_FilterMode));
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000136 paint.addColorEffect(effect);
137 } else {
humper@google.comb86add12013-07-25 18:49:07 +0000138 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000139 paint.addColorTextureEffect(srcTexture, matrix, params);
140 }
robertphillips@google.com736dd032013-07-15 15:06:54 +0000141 scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f,
142 i < scaleFactorY ? 0.5f : 1.0f);
robertphillips@google.com736dd032013-07-15 15:06:54 +0000143 context->drawRectToRect(paint, dstRect, srcRect);
144 srcRect = dstRect;
145 srcTexture = dstTexture;
146 SkTSwap(dstTexture, tempTexture);
147 }
148
149 SkIRect srcIRect;
150 srcRect.roundOut(&srcIRect);
151
152 if (sigmaX > 0.0f) {
153 if (scaleFactorX > 1) {
154 // Clear out a radius to the right of the srcRect to prevent the
155 // X convolution from reading garbage.
156 clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop,
157 radiusX, srcIRect.height());
158 context->clear(&clearRect, 0x0);
159 }
160 context->setRenderTarget(dstTexture->asRenderTarget());
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000161 SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
162 convolve_gaussian(context, srcTexture, srcRect, dstRect, cropToRect,
163 sigmaX, radiusX, Gr1DKernelEffect::kX_Direction);
robertphillips@google.com736dd032013-07-15 15:06:54 +0000164 srcTexture = dstTexture;
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000165 srcRect = dstRect;
robertphillips@google.com736dd032013-07-15 15:06:54 +0000166 SkTSwap(dstTexture, tempTexture);
167 }
168
169 if (sigmaY > 0.0f) {
170 if (scaleFactorY > 1 || sigmaX > 0.0f) {
171 // Clear out a radius below the srcRect to prevent the Y
172 // convolution from reading garbage.
173 clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom,
174 srcIRect.width(), radiusY);
175 context->clear(&clearRect, 0x0);
176 }
177
178 context->setRenderTarget(dstTexture->asRenderTarget());
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000179 SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
180 convolve_gaussian(context, srcTexture, srcRect, dstRect, cropToRect,
181 sigmaY, radiusY, Gr1DKernelEffect::kY_Direction);
robertphillips@google.com736dd032013-07-15 15:06:54 +0000182 srcTexture = dstTexture;
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000183 srcRect = dstRect;
robertphillips@google.com736dd032013-07-15 15:06:54 +0000184 SkTSwap(dstTexture, tempTexture);
185 }
186
187 if (scaleFactorX > 1 || scaleFactorY > 1) {
188 // Clear one pixel to the right and below, to accommodate bilinear
189 // upsampling.
190 clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom,
191 srcIRect.width() + 1, 1);
192 context->clear(&clearRect, 0x0);
193 clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop,
194 1, srcIRect.height());
195 context->clear(&clearRect, 0x0);
196 SkMatrix matrix;
197 matrix.setIDiv(srcTexture->width(), srcTexture->height());
198 context->setRenderTarget(dstTexture->asRenderTarget());
199
200 GrPaint paint;
201 // FIXME: this should be mitchell, not bilinear.
humper@google.comb86add12013-07-25 18:49:07 +0000202 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
robertphillips@google.com736dd032013-07-15 15:06:54 +0000203 paint.addColorTextureEffect(srcTexture, matrix, params);
204
205 SkRect dstRect(srcRect);
206 scale_rect(&dstRect, (float) scaleFactorX, (float) scaleFactorY);
207 context->drawRectToRect(paint, dstRect, srcRect);
208 srcRect = dstRect;
209 srcTexture = dstTexture;
210 SkTSwap(dstTexture, tempTexture);
211 }
212 if (srcTexture == temp1.texture()) {
213 return temp1.detach();
214 } else if (srcTexture == temp2.texture()) {
215 return temp2.detach();
216 } else {
217 srcTexture->ref();
218 return srcTexture;
219 }
220}
221#endif
222
robertphillips@google.comcce41022013-07-15 15:47:10 +0000223}