blob: c0f571da20ff9ba673348eab1d6b01d326137258 [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"
Weston Tracey074414f2020-05-29 12:55:06 +000022#include "src/gpu/GrFixedClip.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050023#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
Greg Daniel43956122020-02-11 15:49:27 -050048 GrSurfaceProxyView createMaskTexture(GrRecordingContext*,
49 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
Greg Daniel43956122020-02-11 15:49:27 -0500103GrSurfaceProxyView SkAlphaThresholdFilterImpl::createMaskTexture(GrRecordingContext* context,
104 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) {
Greg Daniel43956122020-02-11 15:49:27 -0500109 return {};
robertphillipsaf9b8c82016-04-12 11:02:25 -0700110 }
111
robertphillipsaf9b8c82016-04-12 11:02:25 -0700112 SkRegion::Iterator iter(fRegion);
Michael Ludwig81d41722020-05-26 16:57:38 -0400113 rtContext->clear(SK_PMColor4fTRANSPARENT);
robertphillipsaf9b8c82016-04-12 11:02:25 -0700114
Weston Tracey074414f2020-05-29 12:55:06 +0000115 GrFixedClip clip(SkIRect::MakeWH(bounds.width(), bounds.height()));
robertphillipsaf9b8c82016-04-12 11:02:25 -0700116 while (!iter.done()) {
Mike Klein16885072018-12-11 09:54:31 -0500117 GrPaint paint;
118 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
119
robertphillipsaf9b8c82016-04-12 11:02:25 -0700120 SkRect rect = SkRect::Make(iter.rect());
Mike Klein16885072018-12-11 09:54:31 -0500121
Weston Tracey074414f2020-05-29 12:55:06 +0000122 rtContext->drawRect(clip, std::move(paint), GrAA::kNo, inMatrix, rect);
Mike Klein16885072018-12-11 09:54:31 -0500123
robertphillipsaf9b8c82016-04-12 11:02:25 -0700124 iter.next();
125 }
126
Greg Daniel43956122020-02-11 15:49:27 -0500127 return rtContext->readSurfaceView();
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000128}
129#endif
130
Michael Ludwige30a4852019-08-14 14:35:42 -0400131sk_sp<SkSpecialImage> SkAlphaThresholdFilterImpl::onFilterImage(const Context& ctx,
robertphillipsaf9b8c82016-04-12 11:02:25 -0700132 SkIPoint* offset) const {
133 SkIPoint inputOffset = SkIPoint::Make(0, 0);
Michael Ludwige30a4852019-08-14 14:35:42 -0400134 sk_sp<SkSpecialImage> input(this->filterInput(0, ctx, &inputOffset));
robertphillipsaf9b8c82016-04-12 11:02:25 -0700135 if (!input) {
136 return nullptr;
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000137 }
138
robertphillipsaf9b8c82016-04-12 11:02:25 -0700139 const SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(),
140 input->width(), input->height());
141
142 SkIRect bounds;
143 if (!this->applyCropRect(ctx, inputBounds, &bounds)) {
144 return nullptr;
145 }
146
147#if SK_SUPPORT_GPU
Michael Ludwige30a4852019-08-14 14:35:42 -0400148 if (ctx.gpuBacked()) {
149 auto context = ctx.getContext();
robertphillipsaf9b8c82016-04-12 11:02:25 -0700150
Greg Daniel37c127f2020-02-05 10:37:27 -0500151 GrSurfaceProxyView inputView = (input->view(context));
Greg Daniel83547172020-01-29 11:11:03 -0500152 SkASSERT(inputView.asTextureProxy());
153 const GrProtected isProtected = inputView.proxy()->isProtected();
robertphillipsaf9b8c82016-04-12 11:02:25 -0700154
155 offset->fX = bounds.left();
156 offset->fY = bounds.top();
157
158 bounds.offset(-inputOffset);
159
160 SkMatrix matrix(ctx.ctm());
Robert Phillipsdcf1fb42018-04-10 17:50:44 -0400161 matrix.postTranslate(SkIntToScalar(-offset->fX), SkIntToScalar(-offset->fY));
robertphillipsaf9b8c82016-04-12 11:02:25 -0700162
Greg Daniel43956122020-02-11 15:49:27 -0500163 GrSurfaceProxyView maskView = this->createMaskTexture(context, matrix, bounds);
164 if (!maskView) {
robertphillipsaf9b8c82016-04-12 11:02:25 -0700165 return nullptr;
166 }
167
Brian Salomonb8f098d2020-01-07 11:15:44 -0500168 auto textureFP = GrTextureEffect::Make(
Greg Danield2ccbb52020-02-05 10:45:39 -0500169 std::move(inputView), input->alphaType(),
Mike Reed1f607332020-05-21 12:11:27 -0400170 SkMatrix::Translate(input->subset().x(), input->subset().y()));
Brian Osmana4aa1332017-10-19 12:54:28 -0400171 textureFP = GrColorSpaceXformEffect::Make(std::move(textureFP), input->getColorSpace(),
Michael Ludwig03f9ca32019-08-14 14:35:15 -0400172 input->alphaType(), ctx.colorSpace());
Brian Osmana4aa1332017-10-19 12:54:28 -0400173 if (!textureFP) {
robertphillipsaf9b8c82016-04-12 11:02:25 -0700174 return nullptr;
175 }
176
Greg Daniel43956122020-02-11 15:49:27 -0500177 auto thresholdFP = GrAlphaThresholdFragmentProcessor::Make(
178 std::move(maskView), fInnerThreshold, fOuterThreshold, bounds);
Brian Osmana4aa1332017-10-19 12:54:28 -0400179 if (!thresholdFP) {
180 return nullptr;
181 }
182
183 std::unique_ptr<GrFragmentProcessor> fpSeries[] = { std::move(textureFP),
184 std::move(thresholdFP) };
185 auto fp = GrFragmentProcessor::RunInSeries(fpSeries, 2);
186
Michael Ludwig03f9ca32019-08-14 14:35:15 -0400187 return DrawWithFP(context, std::move(fp), bounds, ctx.colorType(), ctx.colorSpace(),
Greg Danielca9dbe22020-01-02 13:44:30 -0500188 isProtected);
robertphillipsaf9b8c82016-04-12 11:02:25 -0700189 }
190#endif
191
192 SkBitmap inputBM;
193
194 if (!input->getROPixels(&inputBM)) {
195 return nullptr;
196 }
197
198 if (inputBM.colorType() != kN32_SkColorType) {
199 return nullptr;
200 }
201
robertphillipsaf9b8c82016-04-12 11:02:25 -0700202 if (!inputBM.getPixels() || inputBM.width() <= 0 || inputBM.height() <= 0) {
203 return nullptr;
204 }
205
206
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000207 SkMatrix localInverse;
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000208 if (!ctx.ctm().invert(&localInverse)) {
robertphillipsaf9b8c82016-04-12 11:02:25 -0700209 return nullptr;
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000210 }
211
robertphillipsaf9b8c82016-04-12 11:02:25 -0700212 SkImageInfo info = SkImageInfo::MakeN32(bounds.width(), bounds.height(),
robertphillips247e5342016-04-12 12:39:26 -0700213 kPremul_SkAlphaType);
robertphillipsaf9b8c82016-04-12 11:02:25 -0700214
215 SkBitmap dst;
216 if (!dst.tryAllocPixels(info)) {
217 return nullptr;
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000218 }
219
commit-bot@chromium.org9109e182014-01-07 16:04:01 +0000220 U8CPU innerThreshold = (U8CPU)(fInnerThreshold * 0xFF);
221 U8CPU outerThreshold = (U8CPU)(fOuterThreshold * 0xFF);
robertphillipsaf9b8c82016-04-12 11:02:25 -0700222 SkColor* dptr = dst.getAddr32(0, 0);
223 int dstWidth = dst.width(), dstHeight = dst.height();
Robert Phillips22c57ab2016-12-19 16:51:53 -0500224 SkIPoint srcOffset = { bounds.fLeft - inputOffset.fX, bounds.fTop - inputOffset.fY };
robertphillipsaf9b8c82016-04-12 11:02:25 -0700225 for (int y = 0; y < dstHeight; ++y) {
Robert Phillips22c57ab2016-12-19 16:51:53 -0500226 const SkColor* sptr = inputBM.getAddr32(srcOffset.fX, srcOffset.fY+y);
robertphillipsaf9b8c82016-04-12 11:02:25 -0700227
228 for (int x = 0; x < dstWidth; ++x) {
229 const SkColor& source = sptr[x];
230 SkColor outputColor(source);
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000231 SkPoint position;
robertphillipsaf9b8c82016-04-12 11:02:25 -0700232 localInverse.mapXY((SkScalar)x + bounds.fLeft, (SkScalar)y + bounds.fTop, &position);
commit-bot@chromium.org9109e182014-01-07 16:04:01 +0000233 if (fRegion.contains((int32_t)position.x(), (int32_t)position.y())) {
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000234 if (SkColorGetA(source) < innerThreshold) {
235 U8CPU alpha = SkColorGetA(source);
robertphillipsaf9b8c82016-04-12 11:02:25 -0700236 if (alpha == 0) {
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000237 alpha = 1;
robertphillipsaf9b8c82016-04-12 11:02:25 -0700238 }
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000239 float scale = (float)innerThreshold / alpha;
robertphillipsaf9b8c82016-04-12 11:02:25 -0700240 outputColor = SkColorSetARGB(innerThreshold,
commit-bot@chromium.org9109e182014-01-07 16:04:01 +0000241 (U8CPU)(SkColorGetR(source) * scale),
242 (U8CPU)(SkColorGetG(source) * scale),
243 (U8CPU)(SkColorGetB(source) * scale));
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000244 }
245 } else {
246 if (SkColorGetA(source) > outerThreshold) {
247 float scale = (float)outerThreshold / SkColorGetA(source);
robertphillipsaf9b8c82016-04-12 11:02:25 -0700248 outputColor = SkColorSetARGB(outerThreshold,
commit-bot@chromium.org9109e182014-01-07 16:04:01 +0000249 (U8CPU)(SkColorGetR(source) * scale),
250 (U8CPU)(SkColorGetG(source) * scale),
251 (U8CPU)(SkColorGetB(source) * scale));
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000252 }
253 }
robertphillipsaf9b8c82016-04-12 11:02:25 -0700254 dptr[y * dstWidth + x] = outputColor;
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000255 }
256 }
257
robertphillipsaf9b8c82016-04-12 11:02:25 -0700258 offset->fX = bounds.left();
259 offset->fY = bounds.top();
robertphillips3e302272016-04-20 11:48:36 -0700260 return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()),
robertphillipsaf9b8c82016-04-12 11:02:25 -0700261 dst);
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000262}