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/SkBitmap.h"
9 #include "include/effects/SkImageFilters.h"
10 #include "include/private/SkColorData.h"
11 #include "include/private/SkTPin.h"
12 #include "src/core/SkImageFilter_Base.h"
13 #include "src/core/SkReadBuffer.h"
14 #include "src/core/SkSpecialImage.h"
15 #include "src/core/SkValidationUtils.h"
16 #include "src/core/SkWriteBuffer.h"
17 
18 ////////////////////////////////////////////////////////////////////////////////
19 #if SK_SUPPORT_GPU
20 #include "src/gpu/GrColorSpaceXform.h"
21 #include "src/gpu/effects/GrTextureEffect.h"
22 #include "src/gpu/effects/generated/GrMagnifierEffect.h"
23 #include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
24 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
25 #include "src/gpu/glsl/GrGLSLProgramDataManager.h"
26 #include "src/gpu/glsl/GrGLSLUniformHandler.h"
27 #endif
28 
29 namespace {
30 
31 class SkMagnifierImageFilter final : public SkImageFilter_Base {
32 public:
SkMagnifierImageFilter(const SkRect & srcRect,SkScalar inset,sk_sp<SkImageFilter> input,const SkRect * cropRect)33     SkMagnifierImageFilter(const SkRect& srcRect, SkScalar inset, sk_sp<SkImageFilter> input,
34                            const SkRect* cropRect)
35             : INHERITED(&input, 1, cropRect)
36             , fSrcRect(srcRect)
37             , fInset(inset) {
38         SkASSERT(srcRect.left() >= 0 && srcRect.top() >= 0 && inset >= 0);
39     }
40 
41 protected:
42     void flatten(SkWriteBuffer&) const override;
43 
44     sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
45 
46 private:
47     friend void ::SkRegisterMagnifierImageFilterFlattenable();
48     SK_FLATTENABLE_HOOKS(SkMagnifierImageFilter)
49 
50     SkRect   fSrcRect;
51     SkScalar fInset;
52 
53     using INHERITED = SkImageFilter_Base;
54 };
55 
56 } // end namespace
57 
Magnifier(const SkRect & srcRect,SkScalar inset,sk_sp<SkImageFilter> input,const CropRect & cropRect)58 sk_sp<SkImageFilter> SkImageFilters::Magnifier(
59         const SkRect& srcRect, SkScalar inset, sk_sp<SkImageFilter> input,
60         const CropRect& cropRect) {
61     if (!SkScalarIsFinite(inset) || !SkIsValidRect(srcRect)) {
62         return nullptr;
63     }
64     if (inset < 0) {
65         return nullptr;
66     }
67     // Negative numbers in src rect are not supported
68     if (srcRect.fLeft < 0 || srcRect.fTop < 0) {
69         return nullptr;
70     }
71     return sk_sp<SkImageFilter>(new SkMagnifierImageFilter(srcRect, inset, std::move(input),
72                                                            cropRect));
73 }
74 
SkRegisterMagnifierImageFilterFlattenable()75 void SkRegisterMagnifierImageFilterFlattenable() {
76     SK_REGISTER_FLATTENABLE(SkMagnifierImageFilter);
77     // TODO (michaelludwig) - Remove after grace period for SKPs to stop using old name
78     SkFlattenable::Register("SkMagnifierImageFilterImpl", SkMagnifierImageFilter::CreateProc);
79 }
80 
CreateProc(SkReadBuffer & buffer)81 sk_sp<SkFlattenable> SkMagnifierImageFilter::CreateProc(SkReadBuffer& buffer) {
82     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
83     SkRect src;
84     buffer.readRect(&src);
85     return SkImageFilters::Magnifier(src, buffer.readScalar(), common.getInput(0),
86                                      common.cropRect());
87 }
88 
flatten(SkWriteBuffer & buffer) const89 void SkMagnifierImageFilter::flatten(SkWriteBuffer& buffer) const {
90     this->INHERITED::flatten(buffer);
91     buffer.writeRect(fSrcRect);
92     buffer.writeScalar(fInset);
93 }
94 
95 ////////////////////////////////////////////////////////////////////////////////
96 
onFilterImage(const Context & ctx,SkIPoint * offset) const97 sk_sp<SkSpecialImage> SkMagnifierImageFilter::onFilterImage(const Context& ctx,
98                                                             SkIPoint* offset) const {
99     SkIPoint inputOffset = SkIPoint::Make(0, 0);
100     sk_sp<SkSpecialImage> input(this->filterInput(0, ctx, &inputOffset));
101     if (!input) {
102         return nullptr;
103     }
104 
105     const SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(),
106                                                   input->width(), input->height());
107 
108     SkIRect bounds;
109     if (!this->applyCropRect(ctx, inputBounds, &bounds)) {
110         return nullptr;
111     }
112 
113     SkScalar invInset = fInset > 0 ? SkScalarInvert(fInset) : SK_Scalar1;
114 
115     SkScalar invXZoom = fSrcRect.width() / bounds.width();
116     SkScalar invYZoom = fSrcRect.height() / bounds.height();
117 
118 
119 #if SK_SUPPORT_GPU
120     if (ctx.gpuBacked()) {
121         auto context = ctx.getContext();
122 
123         GrSurfaceProxyView inputView = input->view(context);
124         SkASSERT(inputView.asTextureProxy());
125 
126         const auto isProtected = inputView.proxy()->isProtected();
127 
128         offset->fX = bounds.left();
129         offset->fY = bounds.top();
130         bounds.offset(-inputOffset);
131 
132         // Map bounds and srcRect into the proxy space. Due to the zoom effect,
133         // it's not just an offset for fSrcRect.
134         bounds.offset(input->subset().x(), input->subset().y());
135         SkRect srcRect = fSrcRect.makeOffset((1.f - invXZoom) * input->subset().x(),
136                                              (1.f - invYZoom) * input->subset().y());
137         auto inputFP = GrTextureEffect::Make(std::move(inputView), kPremul_SkAlphaType);
138 
139         auto fp = GrMagnifierEffect::Make(std::move(inputFP),
140                                           bounds,
141                                           srcRect,
142                                           invXZoom,
143                                           invYZoom,
144                                           bounds.width() * invInset,
145                                           bounds.height() * invInset);
146         fp = GrColorSpaceXformEffect::Make(std::move(fp),
147                                            input->getColorSpace(), input->alphaType(),
148                                            ctx.colorSpace(), kPremul_SkAlphaType);
149         if (!fp) {
150             return nullptr;
151         }
152 
153         return DrawWithFP(context, std::move(fp), bounds, ctx.colorType(), ctx.colorSpace(),
154                           ctx.surfaceProps(), isProtected);
155     }
156 #endif
157 
158     SkBitmap inputBM;
159 
160     if (!input->getROPixels(&inputBM)) {
161         return nullptr;
162     }
163 
164     if ((inputBM.colorType() != kN32_SkColorType) ||
165         (fSrcRect.width() >= inputBM.width()) || (fSrcRect.height() >= inputBM.height())) {
166         return nullptr;
167     }
168 
169     SkASSERT(inputBM.getPixels());
170     if (!inputBM.getPixels() || inputBM.width() <= 0 || inputBM.height() <= 0) {
171         return nullptr;
172     }
173 
174     const SkImageInfo info = SkImageInfo::MakeN32Premul(bounds.width(), bounds.height());
175 
176     SkBitmap dst;
177     if (!dst.tryAllocPixels(info)) {
178         return nullptr;
179     }
180 
181     SkColor* dptr = dst.getAddr32(0, 0);
182     int dstWidth = dst.width(), dstHeight = dst.height();
183     for (int y = 0; y < dstHeight; ++y) {
184         for (int x = 0; x < dstWidth; ++x) {
185             SkScalar x_dist = std::min(x, dstWidth - x - 1) * invInset;
186             SkScalar y_dist = std::min(y, dstHeight - y - 1) * invInset;
187             SkScalar weight = 0;
188 
189             static const SkScalar kScalar2 = SkScalar(2);
190 
191             // To create a smooth curve at the corners, we need to work on
192             // a square twice the size of the inset.
193             if (x_dist < kScalar2 && y_dist < kScalar2) {
194                 x_dist = kScalar2 - x_dist;
195                 y_dist = kScalar2 - y_dist;
196 
197                 SkScalar dist = SkScalarSqrt(SkScalarSquare(x_dist) +
198                                              SkScalarSquare(y_dist));
199                 dist = std::max(kScalar2 - dist, 0.0f);
200                 // SkTPin rather than std::max to handle potential NaN
201                 weight = SkTPin(SkScalarSquare(dist), 0.0f, SK_Scalar1);
202             } else {
203                 SkScalar sqDist = std::min(SkScalarSquare(x_dist),
204                                            SkScalarSquare(y_dist));
205                 // SkTPin rather than std::max to handle potential NaN
206                 weight = SkTPin(sqDist, 0.0f, SK_Scalar1);
207             }
208 
209             SkScalar x_interp = weight * (fSrcRect.x() + x * invXZoom) + (1 - weight) * x;
210             SkScalar y_interp = weight * (fSrcRect.y() + y * invYZoom) + (1 - weight) * y;
211 
212             int x_val = SkTPin(bounds.x() + SkScalarFloorToInt(x_interp), 0, inputBM.width() - 1);
213             int y_val = SkTPin(bounds.y() + SkScalarFloorToInt(y_interp), 0, inputBM.height() - 1);
214 
215             *dptr = *inputBM.getAddr32(x_val, y_val);
216             dptr++;
217         }
218     }
219 
220     offset->fX = bounds.left();
221     offset->fY = bounds.top();
222     return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()),
223                                           dst, ctx.surfaceProps());
224 }
225