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