blob: 5b52ad3cea9748bb1b4a14127aa98cef63c2a100 [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.orgccf225c2013-07-22 20:03:22 +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.orgccf225c2013-07-22 20:03:22 +000044 const SkRect& srcRect,
45 const SkRect& dstRect,
robertphillips@google.com736dd032013-07-15 15:06:54 +000046 float sigma,
47 int radius,
48 Gr1DKernelEffect::Direction direction) {
49 GrPaint paint;
senorblanco@chromium.orgccf225c2013-07-22 20:03:22 +000050 paint.reset();
51 float cropRect[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
52 if (direction == Gr1DKernelEffect::kX_Direction) {
53 cropRect[0] = SkScalarToFloat(srcRect.left()) / texture->width();
54 cropRect[1] = SkScalarToFloat(srcRect.right()) / texture->width();
55 } else {
56 cropRect[2] = SkScalarToFloat(srcRect.top()) / texture->height();
57 cropRect[3] = SkScalarToFloat(srcRect.bottom()) / texture->height();
58 }
robertphillips@google.com736dd032013-07-15 15:06:54 +000059
60 SkAutoTUnref<GrEffectRef> conv(GrConvolutionEffect::CreateGaussian(texture,
61 direction,
62 radius,
senorblanco@chromium.orgccf225c2013-07-22 20:03:22 +000063 sigma,
64 cropRect));
robertphillips@google.com736dd032013-07-15 15:06:54 +000065 paint.addColorEffect(conv);
senorblanco@chromium.orgccf225c2013-07-22 20:03:22 +000066 context->drawRectToRect(paint, dstRect, srcRect);
robertphillips@google.com736dd032013-07-15 15:06:54 +000067}
68
69GrTexture* GaussianBlur(GrContext* context,
70 GrTexture* srcTexture,
71 bool canClobberSrc,
72 const SkRect& rect,
skia.committer@gmail.com977409a2013-07-16 07:00:56 +000073 float sigmaX,
robertphillips@google.com736dd032013-07-15 15:06:54 +000074 float sigmaY) {
75 GrAssert(NULL != context);
76
77 GrContext::AutoRenderTarget art(context);
78
79 GrContext::AutoMatrix am;
80 am.setIdentity(context);
81
82 SkIRect clearRect;
83 int scaleFactorX, radiusX;
84 int scaleFactorY, radiusY;
85 sigmaX = adjust_sigma(sigmaX, &scaleFactorX, &radiusX);
86 sigmaY = adjust_sigma(sigmaY, &scaleFactorY, &radiusY);
87
88 SkRect srcRect(rect);
89 scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
90 srcRect.roundOut();
91 scale_rect(&srcRect, static_cast<float>(scaleFactorX),
92 static_cast<float>(scaleFactorY));
93
senorblanco@chromium.orgccf225c2013-07-22 20:03:22 +000094 GrContext::AutoClip acs(context, SkRect::MakeWH(srcRect.width(), srcRect.height()));
robertphillips@google.com736dd032013-07-15 15:06:54 +000095
96 GrAssert(kBGRA_8888_GrPixelConfig == srcTexture->config() ||
97 kRGBA_8888_GrPixelConfig == srcTexture->config() ||
98 kAlpha_8_GrPixelConfig == srcTexture->config());
99
100 GrTextureDesc desc;
101 desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
102 desc.fWidth = SkScalarFloorToInt(srcRect.width());
103 desc.fHeight = SkScalarFloorToInt(srcRect.height());
104 desc.fConfig = srcTexture->config();
105
106 GrAutoScratchTexture temp1, temp2;
107 GrTexture* dstTexture = temp1.set(context, desc);
108 GrTexture* tempTexture = canClobberSrc ? srcTexture : temp2.set(context, desc);
109 if (NULL == dstTexture || NULL == tempTexture) {
110 return NULL;
111 }
112
113 for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) {
114 GrPaint paint;
115 SkMatrix matrix;
116 matrix.setIDiv(srcTexture->width(), srcTexture->height());
117 context->setRenderTarget(dstTexture->asRenderTarget());
118 SkRect dstRect(srcRect);
senorblanco@chromium.orgccf225c2013-07-22 20:03:22 +0000119 if (i == 1) {
120 dstRect.offset(-dstRect.fLeft, -dstRect.fTop);
121 SkRect domain;
122 matrix.mapRect(&domain, rect);
123 domain.inset(i < scaleFactorX ? SK_ScalarHalf / srcTexture->width() : 0.0f,
124 i < scaleFactorY ? SK_ScalarHalf / srcTexture->height() : 0.0f);
125 SkAutoTUnref<GrEffectRef> effect(GrTextureDomainEffect::Create(
126 srcTexture,
127 matrix,
128 domain,
129 GrTextureDomainEffect::kDecal_WrapMode,
130 true));
131 paint.addColorEffect(effect);
132 } else {
133 GrTextureParams params(SkShader::kClamp_TileMode, true);
134 paint.addColorTextureEffect(srcTexture, matrix, params);
135 }
robertphillips@google.com736dd032013-07-15 15:06:54 +0000136 scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f,
137 i < scaleFactorY ? 0.5f : 1.0f);
robertphillips@google.com736dd032013-07-15 15:06:54 +0000138 context->drawRectToRect(paint, dstRect, srcRect);
139 srcRect = dstRect;
140 srcTexture = dstTexture;
141 SkTSwap(dstTexture, tempTexture);
142 }
143
144 SkIRect srcIRect;
145 srcRect.roundOut(&srcIRect);
146
147 if (sigmaX > 0.0f) {
148 if (scaleFactorX > 1) {
149 // Clear out a radius to the right of the srcRect to prevent the
150 // X convolution from reading garbage.
151 clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop,
152 radiusX, srcIRect.height());
153 context->clear(&clearRect, 0x0);
154 }
155 context->setRenderTarget(dstTexture->asRenderTarget());
senorblanco@chromium.orgccf225c2013-07-22 20:03:22 +0000156 SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
157 convolve_gaussian(context, srcTexture, srcRect, dstRect, sigmaX, radiusX,
robertphillips@google.com736dd032013-07-15 15:06:54 +0000158 Gr1DKernelEffect::kX_Direction);
159 srcTexture = dstTexture;
senorblanco@chromium.orgccf225c2013-07-22 20:03:22 +0000160 srcRect = dstRect;
robertphillips@google.com736dd032013-07-15 15:06:54 +0000161 SkTSwap(dstTexture, tempTexture);
162 }
163
164 if (sigmaY > 0.0f) {
165 if (scaleFactorY > 1 || sigmaX > 0.0f) {
166 // Clear out a radius below the srcRect to prevent the Y
167 // convolution from reading garbage.
168 clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom,
169 srcIRect.width(), radiusY);
170 context->clear(&clearRect, 0x0);
171 }
172
173 context->setRenderTarget(dstTexture->asRenderTarget());
senorblanco@chromium.orgccf225c2013-07-22 20:03:22 +0000174 SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
175 convolve_gaussian(context, srcTexture, srcRect, dstRect, sigmaY, radiusY,
robertphillips@google.com736dd032013-07-15 15:06:54 +0000176 Gr1DKernelEffect::kY_Direction);
177 srcTexture = dstTexture;
senorblanco@chromium.orgccf225c2013-07-22 20:03:22 +0000178 srcRect = dstRect;
robertphillips@google.com736dd032013-07-15 15:06:54 +0000179 SkTSwap(dstTexture, tempTexture);
180 }
181
182 if (scaleFactorX > 1 || scaleFactorY > 1) {
183 // Clear one pixel to the right and below, to accommodate bilinear
184 // upsampling.
185 clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom,
186 srcIRect.width() + 1, 1);
187 context->clear(&clearRect, 0x0);
188 clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop,
189 1, srcIRect.height());
190 context->clear(&clearRect, 0x0);
191 SkMatrix matrix;
192 matrix.setIDiv(srcTexture->width(), srcTexture->height());
193 context->setRenderTarget(dstTexture->asRenderTarget());
194
195 GrPaint paint;
196 // FIXME: this should be mitchell, not bilinear.
197 GrTextureParams params(SkShader::kClamp_TileMode, true);
198 paint.addColorTextureEffect(srcTexture, matrix, params);
199
200 SkRect dstRect(srcRect);
201 scale_rect(&dstRect, (float) scaleFactorX, (float) scaleFactorY);
202 context->drawRectToRect(paint, dstRect, srcRect);
203 srcRect = dstRect;
204 srcTexture = dstTexture;
205 SkTSwap(dstTexture, tempTexture);
206 }
207 if (srcTexture == temp1.texture()) {
208 return temp1.detach();
209 } else if (srcTexture == temp2.texture()) {
210 return temp2.detach();
211 } else {
212 srcTexture->ref();
213 return srcTexture;
214 }
215}
216#endif
217
robertphillips@google.comcce41022013-07-15 15:47:10 +0000218}