• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "include/core/SkAlphaType.h"
9 #include "include/core/SkBitmap.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkColorType.h"
12 #include "include/core/SkFlattenable.h"
13 #include "include/core/SkImageFilter.h"
14 #include "include/core/SkImageInfo.h"
15 #include "include/core/SkM44.h"
16 #include "include/core/SkPoint.h"
17 #include "include/core/SkRect.h"
18 #include "include/core/SkRefCnt.h"
19 #include "include/core/SkScalar.h"
20 #include "include/core/SkTypes.h"
21 #include "include/effects/SkImageFilters.h"
22 #include "include/effects/SkRuntimeEffect.h"
23 #include "include/private/base/SkTPin.h"
24 #include "src/core/SkImageFilter_Base.h"
25 #include "src/core/SkReadBuffer.h"
26 #include "src/core/SkSpecialImage.h"
27 #include "src/core/SkValidationUtils.h"
28 #include "src/core/SkWriteBuffer.h"
29 
30 #include <algorithm>
31 #include <memory>
32 #include <utility>
33 
34 #if defined(SK_GANESH)
35 #include "src/core/SkRuntimeEffectPriv.h"
36 #include "src/gpu/ganesh/GrColorSpaceXform.h"
37 #include "src/gpu/ganesh/GrFragmentProcessor.h"
38 #include "src/gpu/ganesh/GrSurfaceProxy.h"
39 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
40 #include "src/gpu/ganesh/effects/GrSkSLFP.h"
41 #include "src/gpu/ganesh/effects/GrTextureEffect.h"
42 #endif
43 
44 namespace {
45 
46 class SkMagnifierImageFilter final : public SkImageFilter_Base {
47 public:
SkMagnifierImageFilter(const SkRect & srcRect,SkScalar inset,sk_sp<SkImageFilter> input,const SkRect * cropRect)48     SkMagnifierImageFilter(const SkRect& srcRect, SkScalar inset, sk_sp<SkImageFilter> input,
49                            const SkRect* cropRect)
50             : INHERITED(&input, 1, cropRect)
51             , fSrcRect(srcRect)
52             , fInset(inset) {
53         SkASSERT(srcRect.left() >= 0 && srcRect.top() >= 0 && inset >= 0);
54     }
55 
56 protected:
57     void flatten(SkWriteBuffer&) const override;
58 
59     sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
60 
61 private:
62     friend void ::SkRegisterMagnifierImageFilterFlattenable();
63     SK_FLATTENABLE_HOOKS(SkMagnifierImageFilter)
64 
65     SkRect   fSrcRect;
66     SkScalar fInset;
67 
68     using INHERITED = SkImageFilter_Base;
69 };
70 
71 } // end namespace
72 
Magnifier(const SkRect & srcRect,SkScalar inset,sk_sp<SkImageFilter> input,const CropRect & cropRect)73 sk_sp<SkImageFilter> SkImageFilters::Magnifier(
74         const SkRect& srcRect, SkScalar inset, sk_sp<SkImageFilter> input,
75         const CropRect& cropRect) {
76     if (!SkScalarIsFinite(inset) || !SkIsValidRect(srcRect)) {
77         return nullptr;
78     }
79     if (inset < 0) {
80         return nullptr;
81     }
82     // Negative numbers in src rect are not supported
83     if (srcRect.fLeft < 0 || srcRect.fTop < 0) {
84         return nullptr;
85     }
86     return sk_sp<SkImageFilter>(new SkMagnifierImageFilter(srcRect, inset, std::move(input),
87                                                            cropRect));
88 }
89 
SkRegisterMagnifierImageFilterFlattenable()90 void SkRegisterMagnifierImageFilterFlattenable() {
91     SK_REGISTER_FLATTENABLE(SkMagnifierImageFilter);
92     // TODO (michaelludwig) - Remove after grace period for SKPs to stop using old name
93     SkFlattenable::Register("SkMagnifierImageFilterImpl", SkMagnifierImageFilter::CreateProc);
94 }
95 
CreateProc(SkReadBuffer & buffer)96 sk_sp<SkFlattenable> SkMagnifierImageFilter::CreateProc(SkReadBuffer& buffer) {
97     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
98     SkRect src;
99     buffer.readRect(&src);
100     return SkImageFilters::Magnifier(src, buffer.readScalar(), common.getInput(0),
101                                      common.cropRect());
102 }
103 
flatten(SkWriteBuffer & buffer) const104 void SkMagnifierImageFilter::flatten(SkWriteBuffer& buffer) const {
105     this->INHERITED::flatten(buffer);
106     buffer.writeRect(fSrcRect);
107     buffer.writeScalar(fInset);
108 }
109 
110 ////////////////////////////////////////////////////////////////////////////////
111 
112 #if defined(SK_GANESH)
make_magnifier_fp(std::unique_ptr<GrFragmentProcessor> input,SkIRect bounds,SkRect srcRect,float xInvZoom,float yInvZoom,float xInvInset,float yInvInset)113 static std::unique_ptr<GrFragmentProcessor> make_magnifier_fp(
114         std::unique_ptr<GrFragmentProcessor> input,
115         SkIRect bounds,
116         SkRect srcRect,
117         float xInvZoom,
118         float yInvZoom,
119         float xInvInset,
120         float yInvInset) {
121     static const SkRuntimeEffect* effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
122         "uniform shader src;"
123         "uniform float4 boundsUniform;"
124         "uniform float  xInvZoom;"
125         "uniform float  yInvZoom;"
126         "uniform float  xInvInset;"
127         "uniform float  yInvInset;"
128         "uniform half2  offset;"
129 
130         "half4 main(float2 coord) {"
131             "float2 zoom_coord = offset + coord * float2(xInvZoom, yInvZoom);"
132             "float2 delta = (coord - boundsUniform.xy) * boundsUniform.zw;"
133             "delta = min(delta, float2(1.0) - delta);"
134             "delta *= float2(xInvInset, yInvInset);"
135 
136             "float weight = 0.0;"
137             "if (delta.s < 2.0 && delta.t < 2.0) {"
138                 "delta = float2(2.0) - delta;"
139                 "float dist = length(delta);"
140                 "dist = max(2.0 - dist, 0.0);"
141                 "weight = min(dist * dist, 1.0);"
142             "} else {"
143                 "float2 delta_squared = delta * delta;"
144                 "weight = min(min(delta_squared.x, delta_squared.y), 1.0);"
145             "}"
146 
147             "return src.eval(mix(coord, zoom_coord, weight));"
148         "}"
149     );
150 
151     SkV4 boundsUniform = {static_cast<float>(bounds.x()),
152                           static_cast<float>(bounds.y()),
153                           1.f / bounds.width(),
154                           1.f / bounds.height()};
155 
156     return GrSkSLFP::Make(effect, "magnifier_fp", /*inputFP=*/nullptr, GrSkSLFP::OptFlags::kNone,
157                           "src", std::move(input),
158                           "boundsUniform", boundsUniform,
159                           "xInvZoom", xInvZoom,
160                           "yInvZoom", yInvZoom,
161                           "xInvInset", xInvInset,
162                           "yInvInset", yInvInset,
163                           "offset", SkV2{srcRect.x(), srcRect.y()});
164 }
165 #endif
166 
onFilterImage(const Context & ctx,SkIPoint * offset) const167 sk_sp<SkSpecialImage> SkMagnifierImageFilter::onFilterImage(const Context& ctx,
168                                                             SkIPoint* offset) const {
169     SkIPoint inputOffset = SkIPoint::Make(0, 0);
170     sk_sp<SkSpecialImage> input(this->filterInput(0, ctx, &inputOffset));
171     if (!input) {
172         return nullptr;
173     }
174 
175     const SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(),
176                                                   input->width(), input->height());
177 
178     SkIRect bounds;
179     if (!this->applyCropRect(ctx, inputBounds, &bounds)) {
180         return nullptr;
181     }
182 
183     SkScalar invInset = fInset > 0 ? SkScalarInvert(fInset) : SK_Scalar1;
184 
185     SkScalar invXZoom = fSrcRect.width() / bounds.width();
186     SkScalar invYZoom = fSrcRect.height() / bounds.height();
187 
188 
189 #if defined(SK_GANESH)
190     if (ctx.gpuBacked()) {
191         auto context = ctx.getContext();
192 
193         GrSurfaceProxyView inputView = input->view(context);
194         SkASSERT(inputView.asTextureProxy());
195 
196         const auto isProtected = inputView.proxy()->isProtected();
197         const auto origin = inputView.origin();
198 
199         offset->fX = bounds.left();
200         offset->fY = bounds.top();
201         bounds.offset(-inputOffset);
202 
203         // Map bounds and srcRect into the proxy space. Due to the zoom effect,
204         // it's not just an offset for fSrcRect.
205         bounds.offset(input->subset().x(), input->subset().y());
206         SkRect srcRect = fSrcRect.makeOffset((1.f - invXZoom) * input->subset().x(),
207                                              (1.f - invYZoom) * input->subset().y());
208         auto inputFP = GrTextureEffect::Make(std::move(inputView), kPremul_SkAlphaType);
209 
210         auto fp = make_magnifier_fp(std::move(inputFP),
211                                     bounds,
212                                     srcRect,
213                                     invXZoom,
214                                     invYZoom,
215                                     bounds.width() * invInset,
216                                     bounds.height() * invInset);
217 
218         fp = GrColorSpaceXformEffect::Make(std::move(fp),
219                                            input->getColorSpace(), input->alphaType(),
220                                            ctx.colorSpace(), kPremul_SkAlphaType);
221         if (!fp) {
222             return nullptr;
223         }
224 
225         return DrawWithFP(context, std::move(fp), bounds, ctx.colorType(), ctx.colorSpace(),
226                           ctx.surfaceProps(), origin, isProtected);
227     }
228 #endif
229 
230     SkBitmap inputBM;
231 
232     if (!input->getROPixels(&inputBM)) {
233         return nullptr;
234     }
235 
236     if ((inputBM.colorType() != kN32_SkColorType) ||
237         (fSrcRect.width() >= inputBM.width()) || (fSrcRect.height() >= inputBM.height())) {
238         return nullptr;
239     }
240 
241     SkASSERT(inputBM.getPixels());
242     if (!inputBM.getPixels() || inputBM.width() <= 0 || inputBM.height() <= 0) {
243         return nullptr;
244     }
245 
246     const SkImageInfo info = SkImageInfo::MakeN32Premul(bounds.width(), bounds.height());
247 
248     SkBitmap dst;
249     if (!dst.tryAllocPixels(info)) {
250         return nullptr;
251     }
252 
253     SkColor* dptr = dst.getAddr32(0, 0);
254     int dstWidth = dst.width(), dstHeight = dst.height();
255     for (int y = 0; y < dstHeight; ++y) {
256         for (int x = 0; x < dstWidth; ++x) {
257             SkScalar x_dist = std::min(x, dstWidth - x - 1) * invInset;
258             SkScalar y_dist = std::min(y, dstHeight - y - 1) * invInset;
259             SkScalar weight = 0;
260 
261             static const SkScalar kScalar2 = SkScalar(2);
262 
263             // To create a smooth curve at the corners, we need to work on
264             // a square twice the size of the inset.
265             if (x_dist < kScalar2 && y_dist < kScalar2) {
266                 x_dist = kScalar2 - x_dist;
267                 y_dist = kScalar2 - y_dist;
268 
269                 SkScalar dist = SkScalarSqrt(SkScalarSquare(x_dist) +
270                                              SkScalarSquare(y_dist));
271                 dist = std::max(kScalar2 - dist, 0.0f);
272                 // SkTPin rather than std::max to handle potential NaN
273                 weight = SkTPin(SkScalarSquare(dist), 0.0f, SK_Scalar1);
274             } else {
275                 SkScalar sqDist = std::min(SkScalarSquare(x_dist),
276                                            SkScalarSquare(y_dist));
277                 // SkTPin rather than std::max to handle potential NaN
278                 weight = SkTPin(sqDist, 0.0f, SK_Scalar1);
279             }
280 
281             SkScalar x_interp = weight * (fSrcRect.x() + x * invXZoom) + (1 - weight) * x;
282             SkScalar y_interp = weight * (fSrcRect.y() + y * invYZoom) + (1 - weight) * y;
283 
284             int x_val = SkTPin(bounds.x() + SkScalarFloorToInt(x_interp), 0, inputBM.width() - 1);
285             int y_val = SkTPin(bounds.y() + SkScalarFloorToInt(y_interp), 0, inputBM.height() - 1);
286 
287             *dptr = *inputBM.getAddr32(x_val, y_val);
288             dptr++;
289         }
290     }
291 
292     offset->fX = bounds.left();
293     offset->fY = bounds.top();
294     return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()),
295                                           dst, ctx.surfaceProps());
296 }
297