Style Change: SkNEW->new; SkDELETE->delete
[platform/upstream/libSkiaSharp.git] / src / effects / SkMagnifierImageFilter.cpp
1 /*
2  * Copyright 2012 The Android Open Source Project
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 "SkBitmap.h"
9 #include "SkMagnifierImageFilter.h"
10 #include "SkColorPriv.h"
11 #include "SkReadBuffer.h"
12 #include "SkWriteBuffer.h"
13 #include "SkValidationUtils.h"
14
15 ////////////////////////////////////////////////////////////////////////////////
16 #if SK_SUPPORT_GPU
17 #include "GrInvariantOutput.h"
18 #include "effects/GrSingleTextureEffect.h"
19 #include "gl/GrGLFragmentProcessor.h"
20 #include "gl/GrGLTexture.h"
21 #include "gl/builders/GrGLProgramBuilder.h"
22
23 class GrMagnifierEffect : public GrSingleTextureEffect {
24
25 public:
26     static GrFragmentProcessor* Create(GrProcessorDataManager* procDataManager,
27                                        GrTexture* texture,
28                                        const SkRect& bounds,
29                                        float xOffset,
30                                        float yOffset,
31                                        float xInvZoom,
32                                        float yInvZoom,
33                                        float xInvInset,
34                                        float yInvInset) {
35         return new GrMagnifierEffect(procDataManager, texture, bounds, xOffset, yOffset, xInvZoom,
36                                      yInvZoom, xInvInset, yInvInset);
37     }
38
39     virtual ~GrMagnifierEffect() {};
40
41     const char* name() const override { return "Magnifier"; }
42
43     const SkRect& bounds() const { return fBounds; }    // Bounds of source image.
44     // Offset to apply to zoomed pixels, (srcRect position / texture size).
45     float x_offset() const { return fXOffset; }
46     float y_offset() const { return fYOffset; }
47
48     // Scale to apply to zoomed pixels (srcRect size / bounds size).
49     float x_inv_zoom() const { return fXInvZoom; }
50     float y_inv_zoom() const { return fYInvZoom; }
51
52     // 1/radius over which to transition from unzoomed to zoomed pixels (bounds size / inset).
53     float x_inv_inset() const { return fXInvInset; }
54     float y_inv_inset() const { return fYInvInset; }
55
56 private:
57     GrMagnifierEffect(GrProcessorDataManager* procDataManager,
58                       GrTexture* texture,
59                       const SkRect& bounds,
60                       float xOffset,
61                       float yOffset,
62                       float xInvZoom,
63                       float yInvZoom,
64                       float xInvInset,
65                       float yInvInset)
66         : INHERITED(procDataManager, texture, GrCoordTransform::MakeDivByTextureWHMatrix(texture))
67         , fBounds(bounds)
68         , fXOffset(xOffset)
69         , fYOffset(yOffset)
70         , fXInvZoom(xInvZoom)
71         , fYInvZoom(yInvZoom)
72         , fXInvInset(xInvInset)
73         , fYInvInset(yInvInset) {
74         this->initClassID<GrMagnifierEffect>();
75     }
76
77     GrGLFragmentProcessor* onCreateGLInstance() const override;
78
79     void onGetGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
80
81     bool onIsEqual(const GrFragmentProcessor&) const override;
82
83     void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
84
85     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
86
87     SkRect fBounds;
88     float fXOffset;
89     float fYOffset;
90     float fXInvZoom;
91     float fYInvZoom;
92     float fXInvInset;
93     float fYInvInset;
94
95     typedef GrSingleTextureEffect INHERITED;
96 };
97
98 // For brevity
99 typedef GrGLProgramDataManager::UniformHandle UniformHandle;
100
101 class GrGLMagnifierEffect : public GrGLFragmentProcessor {
102 public:
103     GrGLMagnifierEffect(const GrProcessor&);
104
105     virtual void emitCode(EmitArgs&) override;
106
107 protected:
108     void onSetData(const GrGLProgramDataManager&, const GrProcessor&) override;
109
110 private:
111     UniformHandle       fOffsetVar;
112     UniformHandle       fInvZoomVar;
113     UniformHandle       fInvInsetVar;
114     UniformHandle       fBoundsVar;
115
116     typedef GrGLFragmentProcessor INHERITED;
117 };
118
119 GrGLMagnifierEffect::GrGLMagnifierEffect(const GrProcessor&) {
120 }
121
122 void GrGLMagnifierEffect::emitCode(EmitArgs& args) {
123     fOffsetVar = args.fBuilder->addUniform(
124         GrGLProgramBuilder::kFragment_Visibility |
125         GrGLProgramBuilder::kVertex_Visibility,
126         kVec2f_GrSLType, kDefault_GrSLPrecision, "Offset");
127     fInvZoomVar = args.fBuilder->addUniform(
128         GrGLProgramBuilder::kFragment_Visibility |
129         GrGLProgramBuilder::kVertex_Visibility,
130         kVec2f_GrSLType, kDefault_GrSLPrecision, "InvZoom");
131     fInvInsetVar = args.fBuilder->addUniform(
132         GrGLProgramBuilder::kFragment_Visibility |
133         GrGLProgramBuilder::kVertex_Visibility,
134         kVec2f_GrSLType, kDefault_GrSLPrecision, "InvInset");
135     fBoundsVar = args.fBuilder->addUniform(
136         GrGLProgramBuilder::kFragment_Visibility |
137         GrGLProgramBuilder::kVertex_Visibility,
138         kVec4f_GrSLType, kDefault_GrSLPrecision, "Bounds");
139
140     GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder();
141     SkString coords2D = fsBuilder->ensureFSCoords2D(args.fCoords, 0);
142     fsBuilder->codeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str());
143     fsBuilder->codeAppendf("\t\tvec2 zoom_coord = %s + %s * %s;\n",
144                            args.fBuilder->getUniformCStr(fOffsetVar),
145                            coords2D.c_str(),
146                            args.fBuilder->getUniformCStr(fInvZoomVar));
147     const char* bounds = args.fBuilder->getUniformCStr(fBoundsVar);
148     fsBuilder->codeAppendf("\t\tvec2 delta = (coord - %s.xy) * %s.zw;\n", bounds, bounds);
149     fsBuilder->codeAppendf("\t\tdelta = min(delta, vec2(1.0, 1.0) - delta);\n");
150     fsBuilder->codeAppendf("\t\tdelta = delta * %s;\n",
151                            args.fBuilder->getUniformCStr(fInvInsetVar));
152
153     fsBuilder->codeAppend("\t\tfloat weight = 0.0;\n");
154     fsBuilder->codeAppend("\t\tif (delta.s < 2.0 && delta.t < 2.0) {\n");
155     fsBuilder->codeAppend("\t\t\tdelta = vec2(2.0, 2.0) - delta;\n");
156     fsBuilder->codeAppend("\t\t\tfloat dist = length(delta);\n");
157     fsBuilder->codeAppend("\t\t\tdist = max(2.0 - dist, 0.0);\n");
158     fsBuilder->codeAppend("\t\t\tweight = min(dist * dist, 1.0);\n");
159     fsBuilder->codeAppend("\t\t} else {\n");
160     fsBuilder->codeAppend("\t\t\tvec2 delta_squared = delta * delta;\n");
161     fsBuilder->codeAppend("\t\t\tweight = min(min(delta_squared.x, delta_squared.y), 1.0);\n");
162     fsBuilder->codeAppend("\t\t}\n");
163
164     fsBuilder->codeAppend("\t\tvec2 mix_coord = mix(coord, zoom_coord, weight);\n");
165     fsBuilder->codeAppend("\t\tvec4 output_color = ");
166     fsBuilder->appendTextureLookup(args.fSamplers[0], "mix_coord");
167     fsBuilder->codeAppend(";\n");
168
169     fsBuilder->codeAppendf("\t\t%s = output_color;", args.fOutputColor);
170     SkString modulate;
171     GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor);
172     fsBuilder->codeAppend(modulate.c_str());
173 }
174
175 void GrGLMagnifierEffect::onSetData(const GrGLProgramDataManager& pdman,
176                                   const GrProcessor& effect) {
177     const GrMagnifierEffect& zoom = effect.cast<GrMagnifierEffect>();
178     pdman.set2f(fOffsetVar, zoom.x_offset(), zoom.y_offset());
179     pdman.set2f(fInvZoomVar, zoom.x_inv_zoom(), zoom.y_inv_zoom());
180     pdman.set2f(fInvInsetVar, zoom.x_inv_inset(), zoom.y_inv_inset());
181     pdman.set4f(fBoundsVar, zoom.bounds().x(), zoom.bounds().y(),
182                             zoom.bounds().width(), zoom.bounds().height());
183 }
184
185 /////////////////////////////////////////////////////////////////////
186
187 void GrMagnifierEffect::onGetGLProcessorKey(const GrGLSLCaps& caps,
188                                           GrProcessorKeyBuilder* b) const {
189     GrGLMagnifierEffect::GenKey(*this, caps, b);
190 }
191
192 GrGLFragmentProcessor* GrMagnifierEffect::onCreateGLInstance() const {
193     return new GrGLMagnifierEffect(*this);
194 }
195
196 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrMagnifierEffect);
197
198 GrFragmentProcessor* GrMagnifierEffect::TestCreate(GrProcessorTestData* d) {
199     GrTexture* texture = d->fTextures[0];
200     const int kMaxWidth = 200;
201     const int kMaxHeight = 200;
202     const int kMaxInset = 20;
203     uint32_t width = d->fRandom->nextULessThan(kMaxWidth);
204     uint32_t height = d->fRandom->nextULessThan(kMaxHeight);
205     uint32_t x = d->fRandom->nextULessThan(kMaxWidth - width);
206     uint32_t y = d->fRandom->nextULessThan(kMaxHeight - height);
207     uint32_t inset = d->fRandom->nextULessThan(kMaxInset);
208
209     GrFragmentProcessor* effect = GrMagnifierEffect::Create(
210         d->fProcDataManager,
211         texture,
212         SkRect::MakeWH(SkIntToScalar(kMaxWidth), SkIntToScalar(kMaxHeight)),
213         (float) width / texture->width(),
214         (float) height / texture->height(),
215         texture->width() / (float) x,
216         texture->height() / (float) y,
217         (float) inset / texture->width(),
218         (float) inset / texture->height());
219     SkASSERT(effect);
220     return effect;
221 }
222
223 ///////////////////////////////////////////////////////////////////////////////
224
225 bool GrMagnifierEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
226     const GrMagnifierEffect& s = sBase.cast<GrMagnifierEffect>();
227     return (this->fBounds == s.fBounds &&
228             this->fXOffset == s.fXOffset &&
229             this->fYOffset == s.fYOffset &&
230             this->fXInvZoom == s.fXInvZoom &&
231             this->fYInvZoom == s.fYInvZoom &&
232             this->fXInvInset == s.fXInvInset &&
233             this->fYInvInset == s.fYInvInset);
234 }
235
236 void GrMagnifierEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
237     this->updateInvariantOutputForModulation(inout);
238 }
239
240 #endif
241
242 ////////////////////////////////////////////////////////////////////////////////
243
244 SkImageFilter* SkMagnifierImageFilter::Create(const SkRect& srcRect, SkScalar inset,
245                                               SkImageFilter* input) {
246
247     if (!SkScalarIsFinite(inset) || !SkIsValidRect(srcRect)) {
248         return NULL;
249     }
250     // Negative numbers in src rect are not supported
251     if (srcRect.fLeft < 0 || srcRect.fTop < 0) {
252         return NULL;
253     }
254     return new SkMagnifierImageFilter(srcRect, inset, input);
255 }
256
257
258 SkMagnifierImageFilter::SkMagnifierImageFilter(const SkRect& srcRect, SkScalar inset,
259                                                SkImageFilter* input)
260     : INHERITED(1, &input), fSrcRect(srcRect), fInset(inset) {
261     SkASSERT(srcRect.x() >= 0 && srcRect.y() >= 0 && inset >= 0);
262 }
263
264 #if SK_SUPPORT_GPU
265 bool SkMagnifierImageFilter::asFragmentProcessor(GrFragmentProcessor** fp,
266                                                  GrProcessorDataManager* procDataManager,
267                                                  GrTexture* texture, const SkMatrix&,
268                                                  const SkIRect&bounds) const {
269     if (fp) {
270         SkScalar yOffset = texture->origin() == kTopLeft_GrSurfaceOrigin ? fSrcRect.y() :
271            texture->height() - fSrcRect.height() * texture->height() / bounds.height()
272                              - fSrcRect.y();
273         int boundsY = (texture->origin() == kTopLeft_GrSurfaceOrigin) ? bounds.y() :
274                       (texture->height() - bounds.height());
275         SkRect effectBounds = SkRect::MakeXYWH(
276             SkIntToScalar(bounds.x()) / texture->width(),
277             SkIntToScalar(boundsY) / texture->height(),
278             SkIntToScalar(texture->width()) / bounds.width(),
279             SkIntToScalar(texture->height()) / bounds.height());
280         SkScalar invInset = fInset > 0 ? SkScalarInvert(fInset) : SK_Scalar1;
281         *fp = GrMagnifierEffect::Create(procDataManager,
282                                         texture,
283                                         effectBounds,
284                                         fSrcRect.x() / texture->width(),
285                                         yOffset / texture->height(),
286                                         fSrcRect.width() / bounds.width(),
287                                         fSrcRect.height() / bounds.height(),
288                                         bounds.width() * invInset,
289                                         bounds.height() * invInset);
290     }
291     return true;
292 }
293 #endif
294
295 SkFlattenable* SkMagnifierImageFilter::CreateProc(SkReadBuffer& buffer) {
296     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
297     SkRect src;
298     buffer.readRect(&src);
299     return Create(src, buffer.readScalar(), common.getInput(0));
300 }
301
302 void SkMagnifierImageFilter::flatten(SkWriteBuffer& buffer) const {
303     this->INHERITED::flatten(buffer);
304     buffer.writeRect(fSrcRect);
305     buffer.writeScalar(fInset);
306 }
307
308 bool SkMagnifierImageFilter::onFilterImage(Proxy*, const SkBitmap& src,
309                                            const Context&, SkBitmap* dst,
310                                            SkIPoint* offset) const {
311     if ((src.colorType() != kN32_SkColorType) ||
312         (fSrcRect.width() >= src.width()) ||
313         (fSrcRect.height() >= src.height())) {
314       return false;
315     }
316
317     SkAutoLockPixels alp(src);
318     SkASSERT(src.getPixels());
319     if (!src.getPixels() || src.width() <= 0 || src.height() <= 0) {
320       return false;
321     }
322
323     if (!dst->tryAllocPixels(src.info())) {
324         return false;
325     }
326
327     SkScalar inv_inset = fInset > 0 ? SkScalarInvert(fInset) : SK_Scalar1;
328
329     SkScalar inv_x_zoom = fSrcRect.width() / src.width();
330     SkScalar inv_y_zoom = fSrcRect.height() / src.height();
331
332     SkColor* sptr = src.getAddr32(0, 0);
333     SkColor* dptr = dst->getAddr32(0, 0);
334     int width = src.width(), height = src.height();
335     for (int y = 0; y < height; ++y) {
336         for (int x = 0; x < width; ++x) {
337             SkScalar x_dist = SkMin32(x, width - x - 1) * inv_inset;
338             SkScalar y_dist = SkMin32(y, height - y - 1) * inv_inset;
339             SkScalar weight = 0;
340
341             static const SkScalar kScalar2 = SkScalar(2);
342
343             // To create a smooth curve at the corners, we need to work on
344             // a square twice the size of the inset.
345             if (x_dist < kScalar2 && y_dist < kScalar2) {
346                 x_dist = kScalar2 - x_dist;
347                 y_dist = kScalar2 - y_dist;
348
349                 SkScalar dist = SkScalarSqrt(SkScalarSquare(x_dist) +
350                                              SkScalarSquare(y_dist));
351                 dist = SkMaxScalar(kScalar2 - dist, 0);
352                 weight = SkMinScalar(SkScalarSquare(dist), SK_Scalar1);
353             } else {
354                 SkScalar sqDist = SkMinScalar(SkScalarSquare(x_dist),
355                                               SkScalarSquare(y_dist));
356                 weight = SkMinScalar(sqDist, SK_Scalar1);
357             }
358
359             SkScalar x_interp = SkScalarMul(weight, (fSrcRect.x() + x * inv_x_zoom)) +
360                            (SK_Scalar1 - weight) * x;
361             SkScalar y_interp = SkScalarMul(weight, (fSrcRect.y() + y * inv_y_zoom)) +
362                            (SK_Scalar1 - weight) * y;
363
364             int x_val = SkPin32(SkScalarFloorToInt(x_interp), 0, width - 1);
365             int y_val = SkPin32(SkScalarFloorToInt(y_interp), 0, height - 1);
366
367             *dptr = sptr[y_val * width + x_val];
368             dptr++;
369         }
370     }
371     return true;
372 }
373
374 #ifndef SK_IGNORE_TO_STRING
375 void SkMagnifierImageFilter::toString(SkString* str) const {
376     str->appendf("SkMagnifierImageFilter: (");
377     str->appendf("src: (%f,%f,%f,%f) ",
378                  fSrcRect.fLeft, fSrcRect.fTop, fSrcRect.fRight, fSrcRect.fBottom);
379     str->appendf("inset: %f", fInset);
380     str->append(")");
381 }
382 #endif