1 /*
2 * Copyright 2013 Google Inc.
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 "SkAlphaThresholdFilter.h"
9
10 #include "SkBitmap.h"
11 #include "SkReadBuffer.h"
12 #include "SkSpecialImage.h"
13 #include "SkWriteBuffer.h"
14 #include "SkRegion.h"
15
16 #if SK_SUPPORT_GPU
17 #include "GrAlphaThresholdFragmentProcessor.h"
18 #include "GrContext.h"
19 #include "GrFixedClip.h"
20 #include "GrRenderTargetContext.h"
21 #include "GrTextureProxy.h"
22 #endif
23
24 class SK_API SkAlphaThresholdFilterImpl : public SkImageFilter {
25 public:
26 SkAlphaThresholdFilterImpl(const SkRegion& region, SkScalar innerThreshold,
27 SkScalar outerThreshold, sk_sp<SkImageFilter> input,
28 const CropRect* cropRect = nullptr);
29
30 SK_TO_STRING_OVERRIDE()
31 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkAlphaThresholdFilterImpl)
32 friend void SkAlphaThresholdFilter::InitializeFlattenables();
33
34 protected:
35 void flatten(SkWriteBuffer&) const override;
36
37 sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
38 SkIPoint* offset) const override;
39
40 #if SK_SUPPORT_GPU
41 sk_sp<GrTextureProxy> createMaskTexture(GrContext*,
42 const SkMatrix&,
43 const SkIRect& bounds) const;
44 #endif
45
46 private:
47 SkRegion fRegion;
48 SkScalar fInnerThreshold;
49 SkScalar fOuterThreshold;
50 typedef SkImageFilter INHERITED;
51 };
52
53 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkAlphaThresholdFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAlphaThresholdFilterImpl)54 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAlphaThresholdFilterImpl)
55 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
56
57 static SkScalar pin_0_1(SkScalar x) {
58 return SkMinScalar(SkMaxScalar(x, 0), 1);
59 }
60
Make(const SkRegion & region,SkScalar innerThreshold,SkScalar outerThreshold,sk_sp<SkImageFilter> input,const SkImageFilter::CropRect * cropRect)61 sk_sp<SkImageFilter> SkAlphaThresholdFilter::Make(const SkRegion& region,
62 SkScalar innerThreshold,
63 SkScalar outerThreshold,
64 sk_sp<SkImageFilter> input,
65 const SkImageFilter::CropRect* cropRect) {
66 innerThreshold = pin_0_1(innerThreshold);
67 outerThreshold = pin_0_1(outerThreshold);
68 if (!SkScalarIsFinite(innerThreshold) || !SkScalarIsFinite(outerThreshold)) {
69 return nullptr;
70 }
71 return sk_sp<SkImageFilter>(new SkAlphaThresholdFilterImpl(region, innerThreshold,
72 outerThreshold,
73 std::move(input),
74 cropRect));
75 }
76
CreateProc(SkReadBuffer & buffer)77 sk_sp<SkFlattenable> SkAlphaThresholdFilterImpl::CreateProc(SkReadBuffer& buffer) {
78 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
79 SkScalar inner = buffer.readScalar();
80 SkScalar outer = buffer.readScalar();
81 SkRegion rgn;
82 buffer.readRegion(&rgn);
83 return SkAlphaThresholdFilter::Make(rgn, inner, outer, common.getInput(0),
84 &common.cropRect());
85 }
86
SkAlphaThresholdFilterImpl(const SkRegion & region,SkScalar innerThreshold,SkScalar outerThreshold,sk_sp<SkImageFilter> input,const CropRect * cropRect)87 SkAlphaThresholdFilterImpl::SkAlphaThresholdFilterImpl(const SkRegion& region,
88 SkScalar innerThreshold,
89 SkScalar outerThreshold,
90 sk_sp<SkImageFilter> input,
91 const CropRect* cropRect)
92 : INHERITED(&input, 1, cropRect)
93 , fRegion(region)
94 , fInnerThreshold(innerThreshold)
95 , fOuterThreshold(outerThreshold) {
96 }
97
98 #if SK_SUPPORT_GPU
createMaskTexture(GrContext * context,const SkMatrix & inMatrix,const SkIRect & bounds) const99 sk_sp<GrTextureProxy> SkAlphaThresholdFilterImpl::createMaskTexture(GrContext* context,
100 const SkMatrix& inMatrix,
101 const SkIRect& bounds) const {
102
103 sk_sp<GrRenderTargetContext> rtContext(context->makeDeferredRenderTargetContextWithFallback(
104 SkBackingFit::kApprox, bounds.width(), bounds.height(), kAlpha_8_GrPixelConfig, nullptr));
105 if (!rtContext) {
106 return nullptr;
107 }
108
109 GrPaint paint;
110 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
111 SkRegion::Iterator iter(fRegion);
112 rtContext->clear(nullptr, 0x0, true);
113
114 GrFixedClip clip(SkIRect::MakeWH(bounds.width(), bounds.height()));
115 while (!iter.done()) {
116 SkRect rect = SkRect::Make(iter.rect());
117 rtContext->drawRect(clip, std::move(paint), GrAA::kNo, inMatrix, rect);
118 iter.next();
119 }
120
121 return rtContext->asTextureProxyRef();
122 }
123 #endif
124
flatten(SkWriteBuffer & buffer) const125 void SkAlphaThresholdFilterImpl::flatten(SkWriteBuffer& buffer) const {
126 this->INHERITED::flatten(buffer);
127 buffer.writeScalar(fInnerThreshold);
128 buffer.writeScalar(fOuterThreshold);
129 buffer.writeRegion(fRegion);
130 }
131
onFilterImage(SkSpecialImage * source,const Context & ctx,SkIPoint * offset) const132 sk_sp<SkSpecialImage> SkAlphaThresholdFilterImpl::onFilterImage(SkSpecialImage* source,
133 const Context& ctx,
134 SkIPoint* offset) const {
135 SkIPoint inputOffset = SkIPoint::Make(0, 0);
136 sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset));
137 if (!input) {
138 return nullptr;
139 }
140
141 const SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(),
142 input->width(), input->height());
143
144 SkIRect bounds;
145 if (!this->applyCropRect(ctx, inputBounds, &bounds)) {
146 return nullptr;
147 }
148
149 #if SK_SUPPORT_GPU
150 if (source->isTextureBacked()) {
151 GrContext* context = source->getContext();
152
153 sk_sp<GrTextureProxy> inputProxy(input->asTextureProxyRef(context));
154 SkASSERT(inputProxy);
155
156 offset->fX = bounds.left();
157 offset->fY = bounds.top();
158
159 bounds.offset(-inputOffset);
160
161 SkMatrix matrix(ctx.ctm());
162 matrix.postTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
163
164 sk_sp<GrTextureProxy> maskProxy(this->createMaskTexture(context, matrix, bounds));
165 if (!maskProxy) {
166 return nullptr;
167 }
168
169 const OutputProperties& outProps = ctx.outputProperties();
170 sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(input->getColorSpace(),
171 outProps.colorSpace());
172
173 sk_sp<GrFragmentProcessor> fp(GrAlphaThresholdFragmentProcessor::Make(
174 context->resourceProvider(),
175 std::move(inputProxy),
176 std::move(colorSpaceXform),
177 std::move(maskProxy),
178 fInnerThreshold,
179 fOuterThreshold,
180 bounds));
181 if (!fp) {
182 return nullptr;
183 }
184
185 return DrawWithFP(context, std::move(fp), bounds, outProps);
186 }
187 #endif
188
189 SkBitmap inputBM;
190
191 if (!input->getROPixels(&inputBM)) {
192 return nullptr;
193 }
194
195 if (inputBM.colorType() != kN32_SkColorType) {
196 return nullptr;
197 }
198
199 SkAutoLockPixels inputLock(inputBM);
200
201 if (!inputBM.getPixels() || inputBM.width() <= 0 || inputBM.height() <= 0) {
202 return nullptr;
203 }
204
205
206 SkMatrix localInverse;
207 if (!ctx.ctm().invert(&localInverse)) {
208 return nullptr;
209 }
210
211 SkImageInfo info = SkImageInfo::MakeN32(bounds.width(), bounds.height(),
212 kPremul_SkAlphaType);
213
214 SkBitmap dst;
215 if (!dst.tryAllocPixels(info)) {
216 return nullptr;
217 }
218
219 SkAutoLockPixels dstLock(dst);
220
221 U8CPU innerThreshold = (U8CPU)(fInnerThreshold * 0xFF);
222 U8CPU outerThreshold = (U8CPU)(fOuterThreshold * 0xFF);
223 SkColor* dptr = dst.getAddr32(0, 0);
224 int dstWidth = dst.width(), dstHeight = dst.height();
225 SkIPoint srcOffset = { bounds.fLeft - inputOffset.fX, bounds.fTop - inputOffset.fY };
226 for (int y = 0; y < dstHeight; ++y) {
227 const SkColor* sptr = inputBM.getAddr32(srcOffset.fX, srcOffset.fY+y);
228
229 for (int x = 0; x < dstWidth; ++x) {
230 const SkColor& source = sptr[x];
231 SkColor outputColor(source);
232 SkPoint position;
233 localInverse.mapXY((SkScalar)x + bounds.fLeft, (SkScalar)y + bounds.fTop, &position);
234 if (fRegion.contains((int32_t)position.x(), (int32_t)position.y())) {
235 if (SkColorGetA(source) < innerThreshold) {
236 U8CPU alpha = SkColorGetA(source);
237 if (alpha == 0) {
238 alpha = 1;
239 }
240 float scale = (float)innerThreshold / alpha;
241 outputColor = SkColorSetARGB(innerThreshold,
242 (U8CPU)(SkColorGetR(source) * scale),
243 (U8CPU)(SkColorGetG(source) * scale),
244 (U8CPU)(SkColorGetB(source) * scale));
245 }
246 } else {
247 if (SkColorGetA(source) > outerThreshold) {
248 float scale = (float)outerThreshold / SkColorGetA(source);
249 outputColor = SkColorSetARGB(outerThreshold,
250 (U8CPU)(SkColorGetR(source) * scale),
251 (U8CPU)(SkColorGetG(source) * scale),
252 (U8CPU)(SkColorGetB(source) * scale));
253 }
254 }
255 dptr[y * dstWidth + x] = outputColor;
256 }
257 }
258
259 offset->fX = bounds.left();
260 offset->fY = bounds.top();
261 return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()),
262 dst);
263 }
264
265 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const266 void SkAlphaThresholdFilterImpl::toString(SkString* str) const {
267 str->appendf("SkAlphaThresholdImageFilter: (");
268 str->appendf("inner: %f outer: %f", fInnerThreshold, fOuterThreshold);
269 str->append(")");
270 }
271 #endif
272