blob: a059b6588a4a6a352048e31d61ccb9d5582f9da1 [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"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050026#include "src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.h"
27#include "src/gpu/effects/generated/GrSimpleTextureEffect.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) {
70 innerThreshold = SkScalarPin(innerThreshold, 0.f, 1.f);
71 outerThreshold = SkScalarPin(outerThreshold, 0.f, 1.f);
72 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 {
Brian Salomonbf6b9792019-08-21 09:38:10 -0400106 auto rtContext = context->priv().makeDeferredRenderTargetContextWithFallback(
107 SkBackingFit::kApprox, bounds.width(), bounds.height(), GrColorType::kAlpha_8, nullptr);
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
Robert Phillips8e1c4e62017-02-19 12:27:01 -0500152 sk_sp<GrTextureProxy> inputProxy(input->asTextureProxyRef(context));
153 SkASSERT(inputProxy);
Emircan Uysaler23ca4e72019-06-24 10:53:09 -0400154 const bool isProtected = inputProxy->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 Salomon078e8fa2019-11-22 04:10:18 +0000169 GrColorType srcColorType = SkColorTypeToGrColorType(input->colorType());
170 auto textureFP = GrSimpleTextureEffect::Make(std::move(inputProxy), srcColorType,
171 SkMatrix::MakeTrans(input->subset().x(),
172 input->subset().y()));
Brian Osmana4aa1332017-10-19 12:54:28 -0400173 textureFP = GrColorSpaceXformEffect::Make(std::move(textureFP), input->getColorSpace(),
Michael Ludwig03f9ca32019-08-14 14:35:15 -0400174 input->alphaType(), ctx.colorSpace());
Brian Osmana4aa1332017-10-19 12:54:28 -0400175 if (!textureFP) {
robertphillipsaf9b8c82016-04-12 11:02:25 -0700176 return nullptr;
177 }
178
Brian Osmana4aa1332017-10-19 12:54:28 -0400179 auto thresholdFP = GrAlphaThresholdFragmentProcessor::Make(std::move(maskProxy),
180 fInnerThreshold,
181 fOuterThreshold,
182 bounds);
183 if (!thresholdFP) {
184 return nullptr;
185 }
186
187 std::unique_ptr<GrFragmentProcessor> fpSeries[] = { std::move(textureFP),
188 std::move(thresholdFP) };
189 auto fp = GrFragmentProcessor::RunInSeries(fpSeries, 2);
190
Michael Ludwig03f9ca32019-08-14 14:35:15 -0400191 return DrawWithFP(context, std::move(fp), bounds, ctx.colorType(), ctx.colorSpace(),
Emircan Uysaler23ca4e72019-06-24 10:53:09 -0400192 isProtected ? GrProtected::kYes : GrProtected::kNo);
robertphillipsaf9b8c82016-04-12 11:02:25 -0700193 }
194#endif
195
196 SkBitmap inputBM;
197
198 if (!input->getROPixels(&inputBM)) {
199 return nullptr;
200 }
201
202 if (inputBM.colorType() != kN32_SkColorType) {
203 return nullptr;
204 }
205
robertphillipsaf9b8c82016-04-12 11:02:25 -0700206 if (!inputBM.getPixels() || inputBM.width() <= 0 || inputBM.height() <= 0) {
207 return nullptr;
208 }
209
210
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000211 SkMatrix localInverse;
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000212 if (!ctx.ctm().invert(&localInverse)) {
robertphillipsaf9b8c82016-04-12 11:02:25 -0700213 return nullptr;
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000214 }
215
robertphillipsaf9b8c82016-04-12 11:02:25 -0700216 SkImageInfo info = SkImageInfo::MakeN32(bounds.width(), bounds.height(),
robertphillips247e5342016-04-12 12:39:26 -0700217 kPremul_SkAlphaType);
robertphillipsaf9b8c82016-04-12 11:02:25 -0700218
219 SkBitmap dst;
220 if (!dst.tryAllocPixels(info)) {
221 return nullptr;
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000222 }
223
commit-bot@chromium.org9109e182014-01-07 16:04:01 +0000224 U8CPU innerThreshold = (U8CPU)(fInnerThreshold * 0xFF);
225 U8CPU outerThreshold = (U8CPU)(fOuterThreshold * 0xFF);
robertphillipsaf9b8c82016-04-12 11:02:25 -0700226 SkColor* dptr = dst.getAddr32(0, 0);
227 int dstWidth = dst.width(), dstHeight = dst.height();
Robert Phillips22c57ab2016-12-19 16:51:53 -0500228 SkIPoint srcOffset = { bounds.fLeft - inputOffset.fX, bounds.fTop - inputOffset.fY };
robertphillipsaf9b8c82016-04-12 11:02:25 -0700229 for (int y = 0; y < dstHeight; ++y) {
Robert Phillips22c57ab2016-12-19 16:51:53 -0500230 const SkColor* sptr = inputBM.getAddr32(srcOffset.fX, srcOffset.fY+y);
robertphillipsaf9b8c82016-04-12 11:02:25 -0700231
232 for (int x = 0; x < dstWidth; ++x) {
233 const SkColor& source = sptr[x];
234 SkColor outputColor(source);
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000235 SkPoint position;
robertphillipsaf9b8c82016-04-12 11:02:25 -0700236 localInverse.mapXY((SkScalar)x + bounds.fLeft, (SkScalar)y + bounds.fTop, &position);
commit-bot@chromium.org9109e182014-01-07 16:04:01 +0000237 if (fRegion.contains((int32_t)position.x(), (int32_t)position.y())) {
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000238 if (SkColorGetA(source) < innerThreshold) {
239 U8CPU alpha = SkColorGetA(source);
robertphillipsaf9b8c82016-04-12 11:02:25 -0700240 if (alpha == 0) {
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000241 alpha = 1;
robertphillipsaf9b8c82016-04-12 11:02:25 -0700242 }
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000243 float scale = (float)innerThreshold / alpha;
robertphillipsaf9b8c82016-04-12 11:02:25 -0700244 outputColor = SkColorSetARGB(innerThreshold,
commit-bot@chromium.org9109e182014-01-07 16:04:01 +0000245 (U8CPU)(SkColorGetR(source) * scale),
246 (U8CPU)(SkColorGetG(source) * scale),
247 (U8CPU)(SkColorGetB(source) * scale));
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000248 }
249 } else {
250 if (SkColorGetA(source) > outerThreshold) {
251 float scale = (float)outerThreshold / SkColorGetA(source);
robertphillipsaf9b8c82016-04-12 11:02:25 -0700252 outputColor = SkColorSetARGB(outerThreshold,
commit-bot@chromium.org9109e182014-01-07 16:04:01 +0000253 (U8CPU)(SkColorGetR(source) * scale),
254 (U8CPU)(SkColorGetG(source) * scale),
255 (U8CPU)(SkColorGetB(source) * scale));
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000256 }
257 }
robertphillipsaf9b8c82016-04-12 11:02:25 -0700258 dptr[y * dstWidth + x] = outputColor;
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000259 }
260 }
261
robertphillipsaf9b8c82016-04-12 11:02:25 -0700262 offset->fX = bounds.left();
263 offset->fY = bounds.top();
robertphillips3e302272016-04-20 11:48:36 -0700264 return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()),
robertphillipsaf9b8c82016-04-12 11:02:25 -0700265 dst);
commit-bot@chromium.org40eb3c12014-01-06 23:41:14 +0000266}