blob: 86ab4787de69942cef5c8d621b4ec2191475e2fc [file] [log] [blame]
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +00001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/effects/SkAlphaThresholdFilter.h"
robertphillipsf6242042016-04-01 10:34:43 -07009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkBitmap.h"
11#include "include/core/SkRegion.h"
Michael Ludwig8ee6cf32019-08-02 09:57:04 -040012#include "src/core/SkImageFilter_Base.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "src/core/SkReadBuffer.h"
14#include "src/core/SkSpecialImage.h"
15#include "src/core/SkWriteBuffer.h"
robertphillipsf7142e72016-04-18 07:20:05 -070016
robertphillipsea461502015-05-26 11:38:03 -070017#if SK_SUPPORT_GPU
Mike Kleinc0bd9f92019-04-23 12:05:21 -050018#include "include/gpu/GrContext.h"
19#include "include/private/GrRecordingContext.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050020#include "src/gpu/GrCaps.h"
21#include "src/gpu/GrColorSpaceXform.h"
22#include "src/gpu/GrFixedClip.h"
23#include "src/gpu/GrRecordingContextPriv.h"
24#include "src/gpu/GrRenderTargetContext.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040025#include "src/gpu/GrTextureProxy.h"
Brian Salomonb8f098d2020-01-07 11:15:44 -050026#include "src/gpu/effects/GrTextureEffect.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050027#include "src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.h"
robertphillipsea461502015-05-26 11:38:03 -070028#endif
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +000029
Michael Ludwigd668f7f2019-07-30 10:03:16 -040030namespace {
31
Michael Ludwig8ee6cf32019-08-02 09:57:04 -040032class SkAlphaThresholdFilterImpl final : public SkImageFilter_Base {
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +000033public:
senorblanco9ea3d572014-07-08 09:16:22 -070034 SkAlphaThresholdFilterImpl(const SkRegion& region, SkScalar innerThreshold,
robertphillipsaf9b8c82016-04-12 11:02:25 -070035 SkScalar outerThreshold, sk_sp<SkImageFilter> input,
Michael Ludwiga00318f2019-08-01 09:30:33 -040036 const CropRect* cropRect = nullptr)
37 : INHERITED(&input, 1, cropRect)
38 , fRegion(region)
39 , fInnerThreshold(innerThreshold)
40 , fOuterThreshold(outerThreshold) {}
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +000041
42protected:
mtklein36352bf2015-03-25 18:17:31 -070043 void flatten(SkWriteBuffer&) const override;
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +000044
Michael Ludwige30a4852019-08-14 14:35:42 -040045 sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
robertphillipsaf9b8c82016-04-12 11:02:25 -070046
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +000047#if SK_SUPPORT_GPU
Robert Phillipsbe9aff22019-02-15 11:33:22 -050048 sk_sp<GrTextureProxy> createMaskTexture(GrRecordingContext*,
robertphillipsd728f0c2016-11-21 11:05:03 -080049 const SkMatrix&,
50 const SkIRect& bounds) const;
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +000051#endif
52
53private:
Michael Ludwiga00318f2019-08-01 09:30:33 -040054 friend void SkAlphaThresholdFilter::RegisterFlattenables();
Mike Klein4fee3232018-10-18 17:27:16 -040055 SK_FLATTENABLE_HOOKS(SkAlphaThresholdFilterImpl)
56
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +000057 SkRegion fRegion;
58 SkScalar fInnerThreshold;
59 SkScalar fOuterThreshold;
Michael Ludwiga00318f2019-08-01 09:30:33 -040060
Michael Ludwig8ee6cf32019-08-02 09:57:04 -040061 typedef SkImageFilter_Base INHERITED;
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +000062};
63
Michael Ludwigd668f7f2019-07-30 10:03:16 -040064}; // end namespace
65
Michael Ludwiga00318f2019-08-01 09:30:33 -040066sk_sp<SkImageFilter> SkAlphaThresholdFilter::Make(const SkRegion& region, SkScalar innerThreshold,
67 SkScalar outerThreshold,
68 sk_sp<SkImageFilter> input,
69 const SkImageFilter::CropRect* cropRect) {
Brian Osmanaba642c2020-02-06 12:52:25 -050070 innerThreshold = SkTPin(innerThreshold, 0.f, 1.f);
71 outerThreshold = SkTPin(outerThreshold, 0.f, 1.f);
Michael Ludwiga00318f2019-08-01 09:30:33 -040072 if (!SkScalarIsFinite(innerThreshold) || !SkScalarIsFinite(outerThreshold)) {
73 return nullptr;
74 }
75 return sk_sp<SkImageFilter>(new SkAlphaThresholdFilterImpl(
76 region, innerThreshold, outerThreshold, std::move(input), cropRect));
77 }
78
Mike Kleinfa5f6ce2018-10-20 08:21:31 -040079void SkAlphaThresholdFilter::RegisterFlattenables() {
Brian Salomon23356442018-11-30 15:33:19 -050080 SK_REGISTER_FLATTENABLE(SkAlphaThresholdFilterImpl);
Mike Klein12956722018-10-19 10:00:21 -040081}
halcanary07456532015-12-07 10:29:54 -080082
Michael Ludwiga00318f2019-08-01 09:30:33 -040083///////////////////////////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +000084
reed60c9b582016-04-03 09:11:13 -070085sk_sp<SkFlattenable> SkAlphaThresholdFilterImpl::CreateProc(SkReadBuffer& buffer) {
reed9fa60da2014-08-21 07:59:51 -070086 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
87 SkScalar inner = buffer.readScalar();
88 SkScalar outer = buffer.readScalar();
89 SkRegion rgn;
90 buffer.readRegion(&rgn);
robertphillipsaf9b8c82016-04-12 11:02:25 -070091 return SkAlphaThresholdFilter::Make(rgn, inner, outer, common.getInput(0),
92 &common.cropRect());
reed9fa60da2014-08-21 07:59:51 -070093}
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +000094
Michael Ludwiga00318f2019-08-01 09:30:33 -040095void SkAlphaThresholdFilterImpl::flatten(SkWriteBuffer& buffer) const {
96 this->INHERITED::flatten(buffer);
97 buffer.writeScalar(fInnerThreshold);
98 buffer.writeScalar(fOuterThreshold);
99 buffer.writeRegion(fRegion);
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000100}
101
102#if SK_SUPPORT_GPU
Robert Phillipsbe9aff22019-02-15 11:33:22 -0500103sk_sp<GrTextureProxy> SkAlphaThresholdFilterImpl::createMaskTexture(GrRecordingContext* context,
robertphillipsd728f0c2016-11-21 11:05:03 -0800104 const SkMatrix& inMatrix,
105 const SkIRect& bounds) const {
Greg Daniele20fcad2020-01-08 11:52:34 -0500106 auto rtContext = GrRenderTargetContext::MakeWithFallback(
107 context, GrColorType::kAlpha_8, nullptr, SkBackingFit::kApprox, bounds.size());
robertphillipsd728f0c2016-11-21 11:05:03 -0800108 if (!rtContext) {
robertphillipsaf9b8c82016-04-12 11:02:25 -0700109 return nullptr;
110 }
111
robertphillipsaf9b8c82016-04-12 11:02:25 -0700112 SkRegion::Iterator iter(fRegion);
Brian Osman9a9baae2018-11-05 15:06:26 -0500113 rtContext->clear(nullptr, SK_PMColor4fTRANSPARENT,
114 GrRenderTargetContext::CanClearFullscreen::kYes);
robertphillipsaf9b8c82016-04-12 11:02:25 -0700115
cdalton846c0512016-05-13 10:25:00 -0700116 GrFixedClip clip(SkIRect::MakeWH(bounds.width(), bounds.height()));
robertphillipsaf9b8c82016-04-12 11:02:25 -0700117 while (!iter.done()) {
Mike Klein16885072018-12-11 09:54:31 -0500118 GrPaint paint;
119 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
120
robertphillipsaf9b8c82016-04-12 11:02:25 -0700121 SkRect rect = SkRect::Make(iter.rect());
Mike Klein16885072018-12-11 09:54:31 -0500122
Brian Salomon82f44312017-01-11 13:42:54 -0500123 rtContext->drawRect(clip, std::move(paint), GrAA::kNo, inMatrix, rect);
Mike Klein16885072018-12-11 09:54:31 -0500124
robertphillipsaf9b8c82016-04-12 11:02:25 -0700125 iter.next();
126 }
127
Robert Phillipsf200a902017-01-30 13:27:37 -0500128 return rtContext->asTextureProxyRef();
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000129}
130#endif
131
Michael Ludwige30a4852019-08-14 14:35:42 -0400132sk_sp<SkSpecialImage> SkAlphaThresholdFilterImpl::onFilterImage(const Context& ctx,
robertphillipsaf9b8c82016-04-12 11:02:25 -0700133 SkIPoint* offset) const {
134 SkIPoint inputOffset = SkIPoint::Make(0, 0);
Michael Ludwige30a4852019-08-14 14:35:42 -0400135 sk_sp<SkSpecialImage> input(this->filterInput(0, ctx, &inputOffset));
robertphillipsaf9b8c82016-04-12 11:02:25 -0700136 if (!input) {
137 return nullptr;
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000138 }
139
robertphillipsaf9b8c82016-04-12 11:02:25 -0700140 const SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(),
141 input->width(), input->height());
142
143 SkIRect bounds;
144 if (!this->applyCropRect(ctx, inputBounds, &bounds)) {
145 return nullptr;
146 }
147
148#if SK_SUPPORT_GPU
Michael Ludwige30a4852019-08-14 14:35:42 -0400149 if (ctx.gpuBacked()) {
150 auto context = ctx.getContext();
robertphillipsaf9b8c82016-04-12 11:02:25 -0700151
Greg Daniel37c127f2020-02-05 10:37:27 -0500152 GrSurfaceProxyView inputView = (input->view(context));
Greg Daniel83547172020-01-29 11:11:03 -0500153 SkASSERT(inputView.asTextureProxy());
154 const GrProtected isProtected = inputView.proxy()->isProtected();
robertphillipsaf9b8c82016-04-12 11:02:25 -0700155
156 offset->fX = bounds.left();
157 offset->fY = bounds.top();
158
159 bounds.offset(-inputOffset);
160
161 SkMatrix matrix(ctx.ctm());
Robert Phillipsdcf1fb42018-04-10 17:50:44 -0400162 matrix.postTranslate(SkIntToScalar(-offset->fX), SkIntToScalar(-offset->fY));
robertphillipsaf9b8c82016-04-12 11:02:25 -0700163
robertphillipsd728f0c2016-11-21 11:05:03 -0800164 sk_sp<GrTextureProxy> maskProxy(this->createMaskTexture(context, matrix, bounds));
165 if (!maskProxy) {
robertphillipsaf9b8c82016-04-12 11:02:25 -0700166 return nullptr;
167 }
168
Brian Salomonb8f098d2020-01-07 11:15:44 -0500169 auto textureFP = GrTextureEffect::Make(
Greg Danield2ccbb52020-02-05 10:45:39 -0500170 std::move(inputView), input->alphaType(),
Brian Salomonfc118442019-11-22 19:09:27 -0500171 SkMatrix::MakeTrans(input->subset().x(), input->subset().y()));
Brian Osmana4aa1332017-10-19 12:54:28 -0400172 textureFP = GrColorSpaceXformEffect::Make(std::move(textureFP), input->getColorSpace(),
Michael Ludwig03f9ca32019-08-14 14:35:15 -0400173 input->alphaType(), ctx.colorSpace());
Brian Osmana4aa1332017-10-19 12:54:28 -0400174 if (!textureFP) {
robertphillipsaf9b8c82016-04-12 11:02:25 -0700175 return nullptr;
176 }
177
Brian Osmana4aa1332017-10-19 12:54:28 -0400178 auto thresholdFP = GrAlphaThresholdFragmentProcessor::Make(std::move(maskProxy),
179 fInnerThreshold,
180 fOuterThreshold,
181 bounds);
182 if (!thresholdFP) {
183 return nullptr;
184 }
185
186 std::unique_ptr<GrFragmentProcessor> fpSeries[] = { std::move(textureFP),
187 std::move(thresholdFP) };
188 auto fp = GrFragmentProcessor::RunInSeries(fpSeries, 2);
189
Michael Ludwig03f9ca32019-08-14 14:35:15 -0400190 return DrawWithFP(context, std::move(fp), bounds, ctx.colorType(), ctx.colorSpace(),
Greg Danielca9dbe22020-01-02 13:44:30 -0500191 isProtected);
robertphillipsaf9b8c82016-04-12 11:02:25 -0700192 }
193#endif
194
195 SkBitmap inputBM;
196
197 if (!input->getROPixels(&inputBM)) {
198 return nullptr;
199 }
200
201 if (inputBM.colorType() != kN32_SkColorType) {
202 return nullptr;
203 }
204
robertphillipsaf9b8c82016-04-12 11:02:25 -0700205 if (!inputBM.getPixels() || inputBM.width() <= 0 || inputBM.height() <= 0) {
206 return nullptr;
207 }
208
209
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000210 SkMatrix localInverse;
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000211 if (!ctx.ctm().invert(&localInverse)) {
robertphillipsaf9b8c82016-04-12 11:02:25 -0700212 return nullptr;
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000213 }
214
robertphillipsaf9b8c82016-04-12 11:02:25 -0700215 SkImageInfo info = SkImageInfo::MakeN32(bounds.width(), bounds.height(),
robertphillips247e5342016-04-12 12:39:26 -0700216 kPremul_SkAlphaType);
robertphillipsaf9b8c82016-04-12 11:02:25 -0700217
218 SkBitmap dst;
219 if (!dst.tryAllocPixels(info)) {
220 return nullptr;
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000221 }
222
commit-bot@chromium.org9109e182014-01-07 16:04:01 +0000223 U8CPU innerThreshold = (U8CPU)(fInnerThreshold * 0xFF);
224 U8CPU outerThreshold = (U8CPU)(fOuterThreshold * 0xFF);
robertphillipsaf9b8c82016-04-12 11:02:25 -0700225 SkColor* dptr = dst.getAddr32(0, 0);
226 int dstWidth = dst.width(), dstHeight = dst.height();
Robert Phillips22c57ab2016-12-19 16:51:53 -0500227 SkIPoint srcOffset = { bounds.fLeft - inputOffset.fX, bounds.fTop - inputOffset.fY };
robertphillipsaf9b8c82016-04-12 11:02:25 -0700228 for (int y = 0; y < dstHeight; ++y) {
Robert Phillips22c57ab2016-12-19 16:51:53 -0500229 const SkColor* sptr = inputBM.getAddr32(srcOffset.fX, srcOffset.fY+y);
robertphillipsaf9b8c82016-04-12 11:02:25 -0700230
231 for (int x = 0; x < dstWidth; ++x) {
232 const SkColor& source = sptr[x];
233 SkColor outputColor(source);
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000234 SkPoint position;
robertphillipsaf9b8c82016-04-12 11:02:25 -0700235 localInverse.mapXY((SkScalar)x + bounds.fLeft, (SkScalar)y + bounds.fTop, &position);
commit-bot@chromium.org9109e182014-01-07 16:04:01 +0000236 if (fRegion.contains((int32_t)position.x(), (int32_t)position.y())) {
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000237 if (SkColorGetA(source) < innerThreshold) {
238 U8CPU alpha = SkColorGetA(source);
robertphillipsaf9b8c82016-04-12 11:02:25 -0700239 if (alpha == 0) {
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000240 alpha = 1;
robertphillipsaf9b8c82016-04-12 11:02:25 -0700241 }
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000242 float scale = (float)innerThreshold / alpha;
robertphillipsaf9b8c82016-04-12 11:02:25 -0700243 outputColor = SkColorSetARGB(innerThreshold,
commit-bot@chromium.org9109e182014-01-07 16:04:01 +0000244 (U8CPU)(SkColorGetR(source) * scale),
245 (U8CPU)(SkColorGetG(source) * scale),
246 (U8CPU)(SkColorGetB(source) * scale));
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000247 }
248 } else {
249 if (SkColorGetA(source) > outerThreshold) {
250 float scale = (float)outerThreshold / SkColorGetA(source);
robertphillipsaf9b8c82016-04-12 11:02:25 -0700251 outputColor = SkColorSetARGB(outerThreshold,
commit-bot@chromium.org9109e182014-01-07 16:04:01 +0000252 (U8CPU)(SkColorGetR(source) * scale),
253 (U8CPU)(SkColorGetG(source) * scale),
254 (U8CPU)(SkColorGetB(source) * scale));
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000255 }
256 }
robertphillipsaf9b8c82016-04-12 11:02:25 -0700257 dptr[y * dstWidth + x] = outputColor;
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000258 }
259 }
260
robertphillipsaf9b8c82016-04-12 11:02:25 -0700261 offset->fX = bounds.left();
262 offset->fY = bounds.top();
robertphillips3e302272016-04-20 11:48:36 -0700263 return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()),
robertphillipsaf9b8c82016-04-12 11:02:25 -0700264 dst);
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000265}