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