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 "SkMagnifierImageFilter.h"
9 
10 #include "SkBitmap.h"
11 #include "SkColorPriv.h"
12 #include "SkReadBuffer.h"
13 #include "SkSpecialImage.h"
14 #include "SkWriteBuffer.h"
15 #include "SkValidationUtils.h"
16 
17 ////////////////////////////////////////////////////////////////////////////////
18 #if SK_SUPPORT_GPU
19 #include "GrContext.h"
20 #include "effects/GrProxyMove.h"
21 #include "effects/GrSingleTextureEffect.h"
22 #include "glsl/GrGLSLColorSpaceXformHelper.h"
23 #include "glsl/GrGLSLFragmentProcessor.h"
24 #include "glsl/GrGLSLFragmentShaderBuilder.h"
25 #include "glsl/GrGLSLProgramDataManager.h"
26 #include "glsl/GrGLSLUniformHandler.h"
27 #include "../private/GrGLSL.h"
28 #endif
29 
Make(const SkRect & srcRect,SkScalar inset,sk_sp<SkImageFilter> input,const CropRect * cropRect)30 sk_sp<SkImageFilter> SkMagnifierImageFilter::Make(const SkRect& srcRect, SkScalar inset,
31                                                   sk_sp<SkImageFilter> input,
32                                                   const CropRect* cropRect) {
33     if (!SkScalarIsFinite(inset) || !SkIsValidRect(srcRect)) {
34         return nullptr;
35     }
36     if (inset < 0) {
37         return nullptr;
38     }
39     // Negative numbers in src rect are not supported
40     if (srcRect.fLeft < 0 || srcRect.fTop < 0) {
41         return nullptr;
42     }
43     return sk_sp<SkImageFilter>(new SkMagnifierImageFilter(srcRect, inset,
44                                                            std::move(input),
45                                                            cropRect));
46 }
47 
48 #if SK_SUPPORT_GPU
49 class GrMagnifierEffect : public GrSingleTextureEffect {
50 public:
Make(GrResourceProvider * resourceProvider,sk_sp<GrTextureProxy> proxy,sk_sp<GrColorSpaceXform> colorSpaceXform,const SkIRect & bounds,const SkRect & srcRect,float xInvZoom,float yInvZoom,float xInvInset,float yInvInset)51     static sk_sp<GrFragmentProcessor> Make(GrResourceProvider* resourceProvider,
52                                            sk_sp<GrTextureProxy> proxy,
53                                            sk_sp<GrColorSpaceXform> colorSpaceXform,
54                                            const SkIRect& bounds,
55                                            const SkRect& srcRect,
56                                            float xInvZoom,
57                                            float yInvZoom,
58                                            float xInvInset,
59                                            float yInvInset) {
60         return sk_sp<GrFragmentProcessor>(new GrMagnifierEffect(resourceProvider,
61                                                                 std::move(proxy),
62                                                                 std::move(colorSpaceXform),
63                                                                 bounds, srcRect,
64                                                                 xInvZoom, yInvZoom,
65                                                                 xInvInset, yInvInset));
66     }
67 
~GrMagnifierEffect()68     ~GrMagnifierEffect() override {}
69 
name() const70     const char* name() const override { return "Magnifier"; }
71 
bounds() const72     const SkIRect& bounds() const { return fBounds; }    // Bounds of source image.
srcRect() const73     const SkRect& srcRect() const { return fSrcRect; }
74 
75     // Scale to apply to zoomed pixels (srcRect size / bounds size).
xInvZoom() const76     float xInvZoom() const { return fXInvZoom; }
yInvZoom() const77     float yInvZoom() const { return fYInvZoom; }
78 
79     // 1/radius over which to transition from unzoomed to zoomed pixels (bounds size / inset).
xInvInset() const80     float xInvInset() const { return fXInvInset; }
yInvInset() const81     float yInvInset() const { return fYInvInset; }
82 
83 private:
GrMagnifierEffect(GrResourceProvider * resourceProvider,sk_sp<GrTextureProxy> proxy,sk_sp<GrColorSpaceXform> colorSpaceXform,const SkIRect & bounds,const SkRect & srcRect,float xInvZoom,float yInvZoom,float xInvInset,float yInvInset)84     GrMagnifierEffect(GrResourceProvider* resourceProvider,
85                       sk_sp<GrTextureProxy> proxy,
86                       sk_sp<GrColorSpaceXform> colorSpaceXform,
87                       const SkIRect& bounds,
88                       const SkRect& srcRect,
89                       float xInvZoom,
90                       float yInvZoom,
91                       float xInvInset,
92                       float yInvInset)
93             : INHERITED{resourceProvider,
94                         ModulationFlags(proxy->config()),
95                         GR_PROXY_MOVE(proxy),
96                         std::move(colorSpaceXform),
97                         SkMatrix::I()} // TODO: no GrSamplerParams::kBilerp_FilterMode?
98             , fBounds(bounds)
99             , fSrcRect(srcRect)
100             , fXInvZoom(xInvZoom)
101             , fYInvZoom(yInvZoom)
102             , fXInvInset(xInvInset)
103             , fYInvInset(yInvInset) {
104         this->initClassID<GrMagnifierEffect>();
105     }
106 
107     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
108 
109     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
110 
111     bool onIsEqual(const GrFragmentProcessor&) const override;
112 
113     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
114 
115     SkIRect fBounds;
116     SkRect  fSrcRect;
117     float fXInvZoom;
118     float fYInvZoom;
119     float fXInvInset;
120     float fYInvInset;
121 
122     typedef GrSingleTextureEffect INHERITED;
123 };
124 
125 // For brevity
126 typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
127 
128 class GrGLMagnifierEffect : public GrGLSLFragmentProcessor {
129 public:
130     void emitCode(EmitArgs&) override;
131 
GenKey(const GrProcessor & effect,const GrShaderCaps &,GrProcessorKeyBuilder * b)132     static inline void GenKey(const GrProcessor& effect, const GrShaderCaps&,
133                               GrProcessorKeyBuilder* b) {
134         const GrMagnifierEffect& zoom = effect.cast<GrMagnifierEffect>();
135         b->add32(GrColorSpaceXform::XformKey(zoom.colorSpaceXform()));
136     }
137 
138 protected:
139     void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
140 
141 private:
142     UniformHandle       fOffsetVar;
143     UniformHandle       fInvZoomVar;
144     UniformHandle       fInvInsetVar;
145     UniformHandle       fBoundsVar;
146     GrGLSLColorSpaceXformHelper fColorSpaceHelper;
147 
148     typedef GrGLSLFragmentProcessor INHERITED;
149 };
150 
emitCode(EmitArgs & args)151 void GrGLMagnifierEffect::emitCode(EmitArgs& args) {
152     GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
153     fOffsetVar = uniformHandler->addUniform(kFragment_GrShaderFlag,
154                                             kVec2f_GrSLType, kDefault_GrSLPrecision,
155                                             "Offset");
156     fInvZoomVar = uniformHandler->addUniform(kFragment_GrShaderFlag,
157                                              kVec2f_GrSLType, kDefault_GrSLPrecision,
158                                              "InvZoom");
159     fInvInsetVar = uniformHandler->addUniform(kFragment_GrShaderFlag,
160                                               kVec2f_GrSLType, kDefault_GrSLPrecision,
161                                               "InvInset");
162     fBoundsVar = uniformHandler->addUniform(kFragment_GrShaderFlag,
163                                             kVec4f_GrSLType, kDefault_GrSLPrecision,
164                                             "Bounds");
165 
166     const GrMagnifierEffect& zoom = args.fFp.cast<GrMagnifierEffect>();
167     fColorSpaceHelper.emitCode(uniformHandler, zoom.colorSpaceXform());
168 
169     GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
170     SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
171     fragBuilder->codeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str());
172     fragBuilder->codeAppendf("\t\tvec2 zoom_coord = %s + %s * %s;\n",
173                              uniformHandler->getUniformCStr(fOffsetVar),
174                              coords2D.c_str(),
175                              uniformHandler->getUniformCStr(fInvZoomVar));
176     const char* bounds = uniformHandler->getUniformCStr(fBoundsVar);
177     fragBuilder->codeAppendf("\t\tvec2 delta = (coord - %s.xy) * %s.zw;\n", bounds, bounds);
178     fragBuilder->codeAppendf("\t\tdelta = min(delta, vec2(1.0, 1.0) - delta);\n");
179     fragBuilder->codeAppendf("\t\tdelta = delta * %s;\n",
180                              uniformHandler->getUniformCStr(fInvInsetVar));
181 
182     fragBuilder->codeAppend("\t\tfloat weight = 0.0;\n");
183     fragBuilder->codeAppend("\t\tif (delta.s < 2.0 && delta.t < 2.0) {\n");
184     fragBuilder->codeAppend("\t\t\tdelta = vec2(2.0, 2.0) - delta;\n");
185     fragBuilder->codeAppend("\t\t\tfloat dist = length(delta);\n");
186     fragBuilder->codeAppend("\t\t\tdist = max(2.0 - dist, 0.0);\n");
187     fragBuilder->codeAppend("\t\t\tweight = min(dist * dist, 1.0);\n");
188     fragBuilder->codeAppend("\t\t} else {\n");
189     fragBuilder->codeAppend("\t\t\tvec2 delta_squared = delta * delta;\n");
190     fragBuilder->codeAppend("\t\t\tweight = min(min(delta_squared.x, delta_squared.y), 1.0);\n");
191     fragBuilder->codeAppend("\t\t}\n");
192 
193     fragBuilder->codeAppend("\t\tvec2 mix_coord = mix(coord, zoom_coord, weight);\n");
194     fragBuilder->codeAppend("\t\tvec4 output_color = ");
195     fragBuilder->appendTextureLookup(args.fTexSamplers[0], "mix_coord", kVec2f_GrSLType,
196                                      &fColorSpaceHelper);
197     fragBuilder->codeAppend(";\n");
198 
199     fragBuilder->codeAppendf("\t\t%s = output_color;", args.fOutputColor);
200     SkString modulate;
201     GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor);
202     fragBuilder->codeAppend(modulate.c_str());
203 }
204 
onSetData(const GrGLSLProgramDataManager & pdman,const GrProcessor & effect)205 void GrGLMagnifierEffect::onSetData(const GrGLSLProgramDataManager& pdman,
206                                     const GrProcessor& effect) {
207     const GrMagnifierEffect& zoom = effect.cast<GrMagnifierEffect>();
208 
209     GrTexture* tex = zoom.textureSampler(0).texture();
210     SkASSERT(tex);
211 
212     SkScalar invW = 1.0f / tex->width();
213     SkScalar invH = 1.0f / tex->height();
214 
215     {
216         SkScalar y = zoom.srcRect().y() * invH;
217         if (tex->origin() != kTopLeft_GrSurfaceOrigin) {
218             y = 1.0f - (zoom.srcRect().height() / zoom.bounds().height()) - y;
219         }
220 
221         pdman.set2f(fOffsetVar, zoom.srcRect().x() * invW, y);
222     }
223 
224     pdman.set2f(fInvZoomVar, zoom.xInvZoom(), zoom.yInvZoom());
225     pdman.set2f(fInvInsetVar, zoom.xInvInset(), zoom.yInvInset());
226 
227     {
228         SkScalar y = zoom.bounds().y() * invH;
229         if (tex->origin() != kTopLeft_GrSurfaceOrigin) {
230             y = 1.0f - zoom.bounds().height() * invH;
231         }
232 
233         pdman.set4f(fBoundsVar,
234                     zoom.bounds().x() * invW,
235                     y,
236                     SkIntToScalar(tex->width()) / zoom.bounds().width(),
237                     SkIntToScalar(tex->height()) / zoom.bounds().height());
238     }
239 
240     if (SkToBool(zoom.colorSpaceXform())) {
241         fColorSpaceHelper.setData(pdman, zoom.colorSpaceXform());
242     }
243 }
244 
245 /////////////////////////////////////////////////////////////////////
246 
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const247 void GrMagnifierEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
248                                               GrProcessorKeyBuilder* b) const {
249     GrGLMagnifierEffect::GenKey(*this, caps, b);
250 }
251 
onCreateGLSLInstance() const252 GrGLSLFragmentProcessor* GrMagnifierEffect::onCreateGLSLInstance() const {
253     return new GrGLMagnifierEffect;
254 }
255 
256 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrMagnifierEffect);
257 
258 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)259 sk_sp<GrFragmentProcessor> GrMagnifierEffect::TestCreate(GrProcessorTestData* d) {
260     sk_sp<GrTextureProxy> proxy = d->textureProxy(0);
261     const int kMaxWidth = 200;
262     const int kMaxHeight = 200;
263     const SkScalar kMaxInset = 20.0f;
264     uint32_t width = d->fRandom->nextULessThan(kMaxWidth);
265     uint32_t height = d->fRandom->nextULessThan(kMaxHeight);
266     SkScalar inset = d->fRandom->nextRangeScalar(1.0f, kMaxInset);
267     sk_sp<GrColorSpaceXform> colorSpaceXform = GrTest::TestColorXform(d->fRandom);
268 
269     SkIRect bounds = SkIRect::MakeWH(SkIntToScalar(kMaxWidth), SkIntToScalar(kMaxHeight));
270     SkRect srcRect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
271 
272     sk_sp<GrFragmentProcessor> effect(GrMagnifierEffect::Make(
273         d->resourceProvider(),
274         std::move(proxy),
275         std::move(colorSpaceXform),
276         bounds,
277         srcRect,
278         srcRect.width() / bounds.width(),
279         srcRect.height() / bounds.height(),
280         bounds.width() / inset,
281         bounds.height() / inset));
282     SkASSERT(effect);
283     return effect;
284 }
285 #endif
286 
287 ///////////////////////////////////////////////////////////////////////////////
288 
onIsEqual(const GrFragmentProcessor & sBase) const289 bool GrMagnifierEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
290     const GrMagnifierEffect& s = sBase.cast<GrMagnifierEffect>();
291     return (this->fBounds == s.fBounds &&
292             this->fSrcRect == s.fSrcRect &&
293             this->fXInvZoom == s.fXInvZoom &&
294             this->fYInvZoom == s.fYInvZoom &&
295             this->fXInvInset == s.fXInvInset &&
296             this->fYInvInset == s.fYInvInset);
297 }
298 
299 #endif
300 
301 ////////////////////////////////////////////////////////////////////////////////
302 
SkMagnifierImageFilter(const SkRect & srcRect,SkScalar inset,sk_sp<SkImageFilter> input,const CropRect * cropRect)303 SkMagnifierImageFilter::SkMagnifierImageFilter(const SkRect& srcRect,
304                                                SkScalar inset,
305                                                sk_sp<SkImageFilter> input,
306                                                const CropRect* cropRect)
307     : INHERITED(&input, 1, cropRect)
308     , fSrcRect(srcRect)
309     , fInset(inset) {
310     SkASSERT(srcRect.left() >= 0 && srcRect.top() >= 0 && inset >= 0);
311 }
312 
CreateProc(SkReadBuffer & buffer)313 sk_sp<SkFlattenable> SkMagnifierImageFilter::CreateProc(SkReadBuffer& buffer) {
314     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
315     SkRect src;
316     buffer.readRect(&src);
317     return Make(src, buffer.readScalar(), common.getInput(0), &common.cropRect());
318 }
319 
flatten(SkWriteBuffer & buffer) const320 void SkMagnifierImageFilter::flatten(SkWriteBuffer& buffer) const {
321     this->INHERITED::flatten(buffer);
322     buffer.writeRect(fSrcRect);
323     buffer.writeScalar(fInset);
324 }
325 
onFilterImage(SkSpecialImage * source,const Context & ctx,SkIPoint * offset) const326 sk_sp<SkSpecialImage> SkMagnifierImageFilter::onFilterImage(SkSpecialImage* source,
327                                                             const Context& ctx,
328                                                             SkIPoint* offset) const {
329     SkIPoint inputOffset = SkIPoint::Make(0, 0);
330     sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset));
331     if (!input) {
332         return nullptr;
333     }
334 
335     const SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(),
336                                                   input->width(), input->height());
337 
338     SkIRect bounds;
339     if (!this->applyCropRect(ctx, inputBounds, &bounds)) {
340         return nullptr;
341     }
342 
343     SkScalar invInset = fInset > 0 ? SkScalarInvert(fInset) : SK_Scalar1;
344 
345     SkScalar invXZoom = fSrcRect.width() / bounds.width();
346     SkScalar invYZoom = fSrcRect.height() / bounds.height();
347 
348 
349 #if SK_SUPPORT_GPU
350     if (source->isTextureBacked()) {
351         GrContext* context = source->getContext();
352 
353         sk_sp<GrTextureProxy> inputProxy(input->asTextureProxyRef(context));
354         SkASSERT(inputProxy);
355 
356         offset->fX = bounds.left();
357         offset->fY = bounds.top();
358         bounds.offset(-inputOffset);
359 
360         SkColorSpace* dstColorSpace = ctx.outputProperties().colorSpace();
361         sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(input->getColorSpace(),
362                                                                            dstColorSpace);
363         sk_sp<GrFragmentProcessor> fp(GrMagnifierEffect::Make(
364                                                         context->resourceProvider(),
365                                                         std::move(inputProxy),
366                                                         std::move(colorSpaceXform),
367                                                         bounds,
368                                                         fSrcRect,
369                                                         invXZoom,
370                                                         invYZoom,
371                                                         bounds.width() * invInset,
372                                                         bounds.height() * invInset));
373         if (!fp) {
374             return nullptr;
375         }
376 
377         return DrawWithFP(context, std::move(fp), bounds, ctx.outputProperties());
378     }
379 #endif
380 
381     SkBitmap inputBM;
382 
383     if (!input->getROPixels(&inputBM)) {
384         return nullptr;
385     }
386 
387     if ((inputBM.colorType() != kN32_SkColorType) ||
388         (fSrcRect.width() >= inputBM.width()) || (fSrcRect.height() >= inputBM.height())) {
389         return nullptr;
390     }
391 
392     SkAutoLockPixels alp(inputBM);
393     SkASSERT(inputBM.getPixels());
394     if (!inputBM.getPixels() || inputBM.width() <= 0 || inputBM.height() <= 0) {
395         return nullptr;
396     }
397 
398     const SkImageInfo info = SkImageInfo::MakeN32Premul(bounds.width(), bounds.height());
399 
400     SkBitmap dst;
401     if (!dst.tryAllocPixels(info)) {
402         return nullptr;
403     }
404 
405     SkAutoLockPixels dstLock(dst);
406 
407     SkColor* dptr = dst.getAddr32(0, 0);
408     int dstWidth = dst.width(), dstHeight = dst.height();
409     for (int y = 0; y < dstHeight; ++y) {
410         for (int x = 0; x < dstWidth; ++x) {
411             SkScalar x_dist = SkMin32(x, dstWidth - x - 1) * invInset;
412             SkScalar y_dist = SkMin32(y, dstHeight - y - 1) * invInset;
413             SkScalar weight = 0;
414 
415             static const SkScalar kScalar2 = SkScalar(2);
416 
417             // To create a smooth curve at the corners, we need to work on
418             // a square twice the size of the inset.
419             if (x_dist < kScalar2 && y_dist < kScalar2) {
420                 x_dist = kScalar2 - x_dist;
421                 y_dist = kScalar2 - y_dist;
422 
423                 SkScalar dist = SkScalarSqrt(SkScalarSquare(x_dist) +
424                                              SkScalarSquare(y_dist));
425                 dist = SkMaxScalar(kScalar2 - dist, 0);
426                 weight = SkMinScalar(SkScalarSquare(dist), SK_Scalar1);
427             } else {
428                 SkScalar sqDist = SkMinScalar(SkScalarSquare(x_dist),
429                                               SkScalarSquare(y_dist));
430                 weight = SkMinScalar(sqDist, SK_Scalar1);
431             }
432 
433             SkScalar x_interp = weight * (fSrcRect.x() + x * invXZoom) + (1 - weight) * x;
434             SkScalar y_interp = weight * (fSrcRect.y() + y * invYZoom) + (1 - weight) * y;
435 
436             int x_val = SkTPin(bounds.x() + SkScalarFloorToInt(x_interp), 0, inputBM.width() - 1);
437             int y_val = SkTPin(bounds.y() + SkScalarFloorToInt(y_interp), 0, inputBM.height() - 1);
438 
439             *dptr = *inputBM.getAddr32(x_val, y_val);
440             dptr++;
441         }
442     }
443 
444     offset->fX = bounds.left();
445     offset->fY = bounds.top();
446     return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()),
447                                           dst);
448 }
449 
450 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const451 void SkMagnifierImageFilter::toString(SkString* str) const {
452     str->appendf("SkMagnifierImageFilter: (");
453     str->appendf("src: (%f,%f,%f,%f) ",
454                  fSrcRect.fLeft, fSrcRect.fTop, fSrcRect.fRight, fSrcRect.fBottom);
455     str->appendf("inset: %f", fInset);
456     str->append(")");
457 }
458 #endif
459