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