|
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/GrTextureDomain.h" |
|
15 #include "GrContext.h" |
|
16 #endif |
|
17 |
|
18 namespace SkGpuBlurUtils { |
|
19 |
|
20 #if SK_SUPPORT_GPU |
|
21 |
|
22 #define MAX_BLUR_SIGMA 4.0f |
|
23 |
|
24 static void scale_rect(SkRect* rect, float xScale, float yScale) { |
|
25 rect->fLeft = SkScalarMul(rect->fLeft, xScale); |
|
26 rect->fTop = SkScalarMul(rect->fTop, yScale); |
|
27 rect->fRight = SkScalarMul(rect->fRight, xScale); |
|
28 rect->fBottom = SkScalarMul(rect->fBottom, yScale); |
|
29 } |
|
30 |
|
31 static 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 SkASSERT(*radius <= GrConvolutionEffect::kMaxKernelRadius); |
|
39 return sigma; |
|
40 } |
|
41 |
|
42 static void convolve_gaussian_pass(GrContext* context, |
|
43 const SkRect& srcRect, |
|
44 const SkRect& dstRect, |
|
45 GrTexture* texture, |
|
46 Gr1DKernelEffect::Direction direction, |
|
47 int radius, |
|
48 float sigma, |
|
49 bool useBounds, |
|
50 float bounds[2]) { |
|
51 GrPaint paint; |
|
52 paint.reset(); |
|
53 SkAutoTUnref<GrEffectRef> conv(GrConvolutionEffect::CreateGaussian( |
|
54 texture, direction, radius, sigma, useBounds, bounds)); |
|
55 paint.reset(); |
|
56 paint.addColorEffect(conv); |
|
57 context->drawRectToRect(paint, dstRect, srcRect); |
|
58 } |
|
59 |
|
60 static void convolve_gaussian(GrContext* context, |
|
61 const SkRect& srcRect, |
|
62 const SkRect& dstRect, |
|
63 GrTexture* texture, |
|
64 Gr1DKernelEffect::Direction direction, |
|
65 int radius, |
|
66 float sigma, |
|
67 bool cropToSrcRect) { |
|
68 float bounds[2] = { 0.0f, 1.0f }; |
|
69 if (!cropToSrcRect) { |
|
70 convolve_gaussian_pass(context, srcRect, dstRect, texture, |
|
71 direction, radius, sigma, false, bounds); |
|
72 return; |
|
73 } |
|
74 SkRect lowerSrcRect = srcRect, lowerDstRect = dstRect; |
|
75 SkRect middleSrcRect = srcRect, middleDstRect = dstRect; |
|
76 SkRect upperSrcRect = srcRect, upperDstRect = dstRect; |
|
77 SkScalar size; |
|
78 SkScalar rad = SkIntToScalar(radius); |
|
79 if (direction == Gr1DKernelEffect::kX_Direction) { |
|
80 bounds[0] = SkScalarToFloat(srcRect.left()) / texture->width(); |
|
81 bounds[1] = SkScalarToFloat(srcRect.right()) / texture->width(); |
|
82 size = srcRect.width(); |
|
83 lowerSrcRect.fRight = srcRect.left() + rad; |
|
84 lowerDstRect.fRight = dstRect.left() + rad; |
|
85 upperSrcRect.fLeft = srcRect.right() - rad; |
|
86 upperDstRect.fLeft = dstRect.right() - rad; |
|
87 middleSrcRect.inset(rad, 0); |
|
88 middleDstRect.inset(rad, 0); |
|
89 } else { |
|
90 bounds[0] = SkScalarToFloat(srcRect.top()) / texture->height(); |
|
91 bounds[1] = SkScalarToFloat(srcRect.bottom()) / texture->height(); |
|
92 size = srcRect.height(); |
|
93 lowerSrcRect.fBottom = srcRect.top() + rad; |
|
94 lowerDstRect.fBottom = dstRect.top() + rad; |
|
95 upperSrcRect.fTop = srcRect.bottom() - rad; |
|
96 upperDstRect.fTop = dstRect.bottom() - rad; |
|
97 middleSrcRect.inset(0, rad); |
|
98 middleDstRect.inset(0, rad); |
|
99 } |
|
100 if (radius >= size * SK_ScalarHalf) { |
|
101 // Blur radius covers srcRect; use bounds over entire draw |
|
102 convolve_gaussian_pass(context, srcRect, dstRect, texture, |
|
103 direction, radius, sigma, true, bounds); |
|
104 } else { |
|
105 // Draw upper and lower margins with bounds; middle without. |
|
106 convolve_gaussian_pass(context, lowerSrcRect, lowerDstRect, texture, |
|
107 direction, radius, sigma, true, bounds); |
|
108 convolve_gaussian_pass(context, upperSrcRect, upperDstRect, texture, |
|
109 direction, radius, sigma, true, bounds); |
|
110 convolve_gaussian_pass(context, middleSrcRect, middleDstRect, texture, |
|
111 direction, radius, sigma, false, bounds); |
|
112 } |
|
113 } |
|
114 |
|
115 GrTexture* GaussianBlur(GrContext* context, |
|
116 GrTexture* srcTexture, |
|
117 bool canClobberSrc, |
|
118 const SkRect& rect, |
|
119 bool cropToRect, |
|
120 float sigmaX, |
|
121 float sigmaY) { |
|
122 SkASSERT(NULL != context); |
|
123 |
|
124 GrContext::AutoRenderTarget art(context); |
|
125 |
|
126 GrContext::AutoMatrix am; |
|
127 am.setIdentity(context); |
|
128 |
|
129 SkIRect clearRect; |
|
130 int scaleFactorX, radiusX; |
|
131 int scaleFactorY, radiusY; |
|
132 sigmaX = adjust_sigma(sigmaX, &scaleFactorX, &radiusX); |
|
133 sigmaY = adjust_sigma(sigmaY, &scaleFactorY, &radiusY); |
|
134 |
|
135 SkRect srcRect(rect); |
|
136 scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY); |
|
137 srcRect.roundOut(); |
|
138 scale_rect(&srcRect, static_cast<float>(scaleFactorX), |
|
139 static_cast<float>(scaleFactorY)); |
|
140 |
|
141 GrContext::AutoClip acs(context, SkRect::MakeWH(srcRect.width(), srcRect.height())); |
|
142 |
|
143 SkASSERT(kBGRA_8888_GrPixelConfig == srcTexture->config() || |
|
144 kRGBA_8888_GrPixelConfig == srcTexture->config() || |
|
145 kAlpha_8_GrPixelConfig == srcTexture->config()); |
|
146 |
|
147 GrTextureDesc desc; |
|
148 desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; |
|
149 desc.fWidth = SkScalarFloorToInt(srcRect.width()); |
|
150 desc.fHeight = SkScalarFloorToInt(srcRect.height()); |
|
151 desc.fConfig = srcTexture->config(); |
|
152 |
|
153 GrAutoScratchTexture temp1, temp2; |
|
154 GrTexture* dstTexture = temp1.set(context, desc); |
|
155 GrTexture* tempTexture = canClobberSrc ? srcTexture : temp2.set(context, desc); |
|
156 if (NULL == dstTexture || NULL == tempTexture) { |
|
157 return NULL; |
|
158 } |
|
159 |
|
160 for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) { |
|
161 GrPaint paint; |
|
162 SkMatrix matrix; |
|
163 matrix.setIDiv(srcTexture->width(), srcTexture->height()); |
|
164 context->setRenderTarget(dstTexture->asRenderTarget()); |
|
165 SkRect dstRect(srcRect); |
|
166 if (cropToRect && i == 1) { |
|
167 dstRect.offset(-dstRect.fLeft, -dstRect.fTop); |
|
168 SkRect domain; |
|
169 matrix.mapRect(&domain, rect); |
|
170 domain.inset(i < scaleFactorX ? SK_ScalarHalf / srcTexture->width() : 0.0f, |
|
171 i < scaleFactorY ? SK_ScalarHalf / srcTexture->height() : 0.0f); |
|
172 SkAutoTUnref<GrEffectRef> effect(GrTextureDomainEffect::Create( |
|
173 srcTexture, |
|
174 matrix, |
|
175 domain, |
|
176 GrTextureDomain::kDecal_Mode, |
|
177 GrTextureParams::kBilerp_FilterMode)); |
|
178 paint.addColorEffect(effect); |
|
179 } else { |
|
180 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode); |
|
181 paint.addColorTextureEffect(srcTexture, matrix, params); |
|
182 } |
|
183 scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f, |
|
184 i < scaleFactorY ? 0.5f : 1.0f); |
|
185 context->drawRectToRect(paint, dstRect, srcRect); |
|
186 srcRect = dstRect; |
|
187 srcTexture = dstTexture; |
|
188 SkTSwap(dstTexture, tempTexture); |
|
189 } |
|
190 |
|
191 SkIRect srcIRect; |
|
192 srcRect.roundOut(&srcIRect); |
|
193 |
|
194 if (sigmaX > 0.0f) { |
|
195 if (scaleFactorX > 1) { |
|
196 // Clear out a radius to the right of the srcRect to prevent the |
|
197 // X convolution from reading garbage. |
|
198 clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop, |
|
199 radiusX, srcIRect.height()); |
|
200 context->clear(&clearRect, 0x0, false); |
|
201 } |
|
202 context->setRenderTarget(dstTexture->asRenderTarget()); |
|
203 SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); |
|
204 convolve_gaussian(context, srcRect, dstRect, srcTexture, |
|
205 Gr1DKernelEffect::kX_Direction, radiusX, sigmaX, cropToRect); |
|
206 srcTexture = dstTexture; |
|
207 srcRect = dstRect; |
|
208 SkTSwap(dstTexture, tempTexture); |
|
209 } |
|
210 |
|
211 if (sigmaY > 0.0f) { |
|
212 if (scaleFactorY > 1 || sigmaX > 0.0f) { |
|
213 // Clear out a radius below the srcRect to prevent the Y |
|
214 // convolution from reading garbage. |
|
215 clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom, |
|
216 srcIRect.width(), radiusY); |
|
217 context->clear(&clearRect, 0x0, false); |
|
218 } |
|
219 |
|
220 context->setRenderTarget(dstTexture->asRenderTarget()); |
|
221 SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); |
|
222 convolve_gaussian(context, srcRect, dstRect, srcTexture, |
|
223 Gr1DKernelEffect::kY_Direction, radiusY, sigmaY, cropToRect); |
|
224 srcTexture = dstTexture; |
|
225 srcRect = dstRect; |
|
226 SkTSwap(dstTexture, tempTexture); |
|
227 } |
|
228 |
|
229 if (scaleFactorX > 1 || scaleFactorY > 1) { |
|
230 // Clear one pixel to the right and below, to accommodate bilinear |
|
231 // upsampling. |
|
232 clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom, |
|
233 srcIRect.width() + 1, 1); |
|
234 context->clear(&clearRect, 0x0, false); |
|
235 clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop, |
|
236 1, srcIRect.height()); |
|
237 context->clear(&clearRect, 0x0, false); |
|
238 SkMatrix matrix; |
|
239 matrix.setIDiv(srcTexture->width(), srcTexture->height()); |
|
240 context->setRenderTarget(dstTexture->asRenderTarget()); |
|
241 |
|
242 GrPaint paint; |
|
243 // FIXME: this should be mitchell, not bilinear. |
|
244 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode); |
|
245 paint.addColorTextureEffect(srcTexture, matrix, params); |
|
246 |
|
247 SkRect dstRect(srcRect); |
|
248 scale_rect(&dstRect, (float) scaleFactorX, (float) scaleFactorY); |
|
249 context->drawRectToRect(paint, dstRect, srcRect); |
|
250 srcRect = dstRect; |
|
251 srcTexture = dstTexture; |
|
252 SkTSwap(dstTexture, tempTexture); |
|
253 } |
|
254 if (srcTexture == temp1.texture()) { |
|
255 return temp1.detach(); |
|
256 } else if (srcTexture == temp2.texture()) { |
|
257 return temp2.detach(); |
|
258 } else { |
|
259 srcTexture->ref(); |
|
260 return srcTexture; |
|
261 } |
|
262 } |
|
263 #endif |
|
264 |
|
265 } |