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