1/*
2 * Copyright 2006 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 "src/core/SkMaskFilterBase.h"
9
10#include "include/core/SkPath.h"
11#include "include/core/SkRRect.h"
12#include "src/core/SkAutoMalloc.h"
13#include "src/core/SkBlitter.h"
14#include "src/core/SkBlurPriv.h"
15#include "src/core/SkCachedData.h"
16#include "src/core/SkCoverageModePriv.h"
17#include "src/core/SkDraw.h"
18#include "src/core/SkPathPriv.h"
19#include "src/core/SkRasterClip.h"
20#include "src/core/SkReadBuffer.h"
21#include "src/core/SkWriteBuffer.h"
22
23#if SK_SUPPORT_GPU
24#include "src/gpu/GrFragmentProcessor.h"
25#include "src/gpu/GrTextureProxy.h"
26#include "src/gpu/effects/GrXfermodeFragmentProcessor.h"
27#include "src/gpu/text/GrSDFMaskFilter.h"
28#endif
29
30SkMaskFilterBase::NinePatch::~NinePatch() {
31 if (fCache) {
32 SkASSERT((const void*)fMask.fImage == fCache->data());
33 fCache->unref();
34 } else {
35 SkMask::FreeImage(fMask.fImage);
36 }
37}
38
39bool SkMaskFilterBase::asABlur(BlurRec*) const {
40 return false;
41}
42
43static void extractMaskSubset(const SkMask& src, SkMask* dst) {
44 SkASSERT(src.fBounds.contains(dst->fBounds));
45
46 const int dx = dst->fBounds.left() - src.fBounds.left();
47 const int dy = dst->fBounds.top() - src.fBounds.top();
48 dst->fImage = src.fImage + dy * src.fRowBytes + dx;
49 dst->fRowBytes = src.fRowBytes;
50 dst->fFormat = src.fFormat;
51}
52
53static void blitClippedMask(SkBlitter* blitter, const SkMask& mask,
54 const SkIRect& bounds, const SkIRect& clipR) {
55 SkIRect r;
56 if (r.intersect(bounds, clipR)) {
57 blitter->blitMask(mask, r);
58 }
59}
60
61static void blitClippedRect(SkBlitter* blitter, const SkIRect& rect, const SkIRect& clipR) {
62 SkIRect r;
63 if (r.intersect(rect, clipR)) {
64 blitter->blitRect(r.left(), r.top(), r.width(), r.height());
65 }
66}
67
68#if 0
69static void dump(const SkMask& mask) {
70 for (int y = mask.fBounds.top(); y < mask.fBounds.bottom(); ++y) {
71 for (int x = mask.fBounds.left(); x < mask.fBounds.right(); ++x) {
72 SkDebugf("%02X", *mask.getAddr8(x, y));
73 }
74 SkDebugf("\n");
75 }
76 SkDebugf("\n");
77}
78#endif
79
80static void draw_nine_clipped(const SkMask& mask, const SkIRect& outerR,
81 const SkIPoint& center, bool fillCenter,
82 const SkIRect& clipR, SkBlitter* blitter) {
83 int cx = center.x();
84 int cy = center.y();
85 SkMask m;
86
87 // top-left
88 m.fBounds = mask.fBounds;
89 m.fBounds.fRight = cx;
90 m.fBounds.fBottom = cy;
91 if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
92 extractMaskSubset(mask, &m);
93 m.fBounds.offsetTo(outerR.left(), outerR.top());
94 blitClippedMask(blitter, m, m.fBounds, clipR);
95 }
96
97 // top-right
98 m.fBounds = mask.fBounds;
99 m.fBounds.fLeft = cx + 1;
100 m.fBounds.fBottom = cy;
101 if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
102 extractMaskSubset(mask, &m);
103 m.fBounds.offsetTo(outerR.right() - m.fBounds.width(), outerR.top());
104 blitClippedMask(blitter, m, m.fBounds, clipR);
105 }
106
107 // bottom-left
108 m.fBounds = mask.fBounds;
109 m.fBounds.fRight = cx;
110 m.fBounds.fTop = cy + 1;
111 if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
112 extractMaskSubset(mask, &m);
113 m.fBounds.offsetTo(outerR.left(), outerR.bottom() - m.fBounds.height());
114 blitClippedMask(blitter, m, m.fBounds, clipR);
115 }
116
117 // bottom-right
118 m.fBounds = mask.fBounds;
119 m.fBounds.fLeft = cx + 1;
120 m.fBounds.fTop = cy + 1;
121 if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
122 extractMaskSubset(mask, &m);
123 m.fBounds.offsetTo(outerR.right() - m.fBounds.width(),
124 outerR.bottom() - m.fBounds.height());
125 blitClippedMask(blitter, m, m.fBounds, clipR);
126 }
127
128 SkIRect innerR;
129 innerR.setLTRB(outerR.left() + cx - mask.fBounds.left(),
130 outerR.top() + cy - mask.fBounds.top(),
131 outerR.right() + (cx + 1 - mask.fBounds.right()),
132 outerR.bottom() + (cy + 1 - mask.fBounds.bottom()));
133 if (fillCenter) {
134 blitClippedRect(blitter, innerR, clipR);
135 }
136
137 const int innerW = innerR.width();
138 size_t storageSize = (innerW + 1) * (sizeof(int16_t) + sizeof(uint8_t));
139 SkAutoSMalloc<4*1024> storage(storageSize);
140 int16_t* runs = (int16_t*)storage.get();
141 uint8_t* alpha = (uint8_t*)(runs + innerW + 1);
142
143 SkIRect r;
144 // top
145 r.setLTRB(innerR.left(), outerR.top(), innerR.right(), innerR.top());
146 if (r.intersect(clipR)) {
147 int startY = std::max(0, r.top() - outerR.top());
148 int stopY = startY + r.height();
149 int width = r.width();
150 for (int y = startY; y < stopY; ++y) {
151 runs[0] = width;
152 runs[width] = 0;
153 alpha[0] = *mask.getAddr8(cx, mask.fBounds.top() + y);
154 blitter->blitAntiH(r.left(), outerR.top() + y, alpha, runs);
155 }
156 }
157 // bottom
158 r.setLTRB(innerR.left(), innerR.bottom(), innerR.right(), outerR.bottom());
159 if (r.intersect(clipR)) {
160 int startY = outerR.bottom() - r.bottom();
161 int stopY = startY + r.height();
162 int width = r.width();
163 for (int y = startY; y < stopY; ++y) {
164 runs[0] = width;
165 runs[width] = 0;
166 alpha[0] = *mask.getAddr8(cx, mask.fBounds.bottom() - y - 1);
167 blitter->blitAntiH(r.left(), outerR.bottom() - y - 1, alpha, runs);
168 }
169 }
170 // left
171 r.setLTRB(outerR.left(), innerR.top(), innerR.left(), innerR.bottom());
172 if (r.intersect(clipR)) {
173 SkMask m;
174 m.fImage = mask.getAddr8(mask.fBounds.left() + r.left() - outerR.left(),
175 mask.fBounds.top() + cy);
176 m.fBounds = r;
177 m.fRowBytes = 0; // so we repeat the scanline for our height
178 m.fFormat = SkMask::kA8_Format;
179 blitter->blitMask(m, r);
180 }
181 // right
182 r.setLTRB(innerR.right(), innerR.top(), outerR.right(), innerR.bottom());
183 if (r.intersect(clipR)) {
184 SkMask m;
185 m.fImage = mask.getAddr8(mask.fBounds.right() - outerR.right() + r.left(),
186 mask.fBounds.top() + cy);
187 m.fBounds = r;
188 m.fRowBytes = 0; // so we repeat the scanline for our height
189 m.fFormat = SkMask::kA8_Format;
190 blitter->blitMask(m, r);
191 }
192}
193
194static void draw_nine(const SkMask& mask, const SkIRect& outerR, const SkIPoint& center,
195 bool fillCenter, const SkRasterClip& clip, SkBlitter* blitter) {
196 // if we get here, we need to (possibly) resolve the clip and blitter
197 SkAAClipBlitterWrapper wrapper(clip, blitter);
198 blitter = wrapper.getBlitter();
199
200 SkRegion::Cliperator clipper(wrapper.getRgn(), outerR);
201
202 if (!clipper.done()) {
203 const SkIRect& cr = clipper.rect();
204 do {
205 draw_nine_clipped(mask, outerR, center, fillCenter, cr, blitter);
206 clipper.next();
207 } while (!clipper.done());
208 }
209}
210
211static int countNestedRects(const SkPath& path, SkRect rects[2]) {
212 if (SkPathPriv::IsNestedFillRects(path, rects)) {
213 return 2;
214 }
215 return path.isRect(&rects[0]);
216}
217
218bool SkMaskFilterBase::filterRRect(const SkRRect& devRRect, const SkMatrix& matrix,
219 const SkRasterClip& clip, SkBlitter* blitter) const {
220 // Attempt to speed up drawing by creating a nine patch. If a nine patch
221 // cannot be used, return false to allow our caller to recover and perform
222 // the drawing another way.
223 NinePatch patch;
224 patch.fMask.fImage = nullptr;
225 if (kTrue_FilterReturn != this->filterRRectToNine(devRRect, matrix,
226 clip.getBounds(),
227 &patch)) {
228 SkASSERT(nullptr == patch.fMask.fImage);
229 return false;
230 }
231 draw_nine(patch.fMask, patch.fOuterRect, patch.fCenter, true, clip, blitter);
232 return true;
233}
234
235bool SkMaskFilterBase::filterPath(const SkPath& devPath, const SkMatrix& matrix,
236 const SkRasterClip& clip, SkBlitter* blitter,
237 SkStrokeRec::InitStyle style) const {
238 SkRect rects[2];
239 int rectCount = 0;
240 if (SkStrokeRec::kFill_InitStyle == style) {
241 rectCount = countNestedRects(devPath, rects);
242 }
243 if (rectCount > 0) {
244 NinePatch patch;
245
246 switch (this->filterRectsToNine(rects, rectCount, matrix, clip.getBounds(), &patch)) {
247 case kFalse_FilterReturn:
248 SkASSERT(nullptr == patch.fMask.fImage);
249 return false;
250
251 case kTrue_FilterReturn:
252 draw_nine(patch.fMask, patch.fOuterRect, patch.fCenter, 1 == rectCount, clip,
253 blitter);
254 return true;
255
256 case kUnimplemented_FilterReturn:
257 SkASSERT(nullptr == patch.fMask.fImage);
258 // fall through
259 break;
260 }
261 }
262
263 SkMask srcM, dstM;
264
265 if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), this, &matrix, &srcM,
266 SkMask::kComputeBoundsAndRenderImage_CreateMode,
267 style)) {
268 return false;
269 }
270 SkAutoMaskFreeImage autoSrc(srcM.fImage);
271
272 if (!this->filterMask(&dstM, srcM, matrix, nullptr)) {
273 return false;
274 }
275 SkAutoMaskFreeImage autoDst(dstM.fImage);
276
277 // if we get here, we need to (possibly) resolve the clip and blitter
278 SkAAClipBlitterWrapper wrapper(clip, blitter);
279 blitter = wrapper.getBlitter();
280
281 SkRegion::Cliperator clipper(wrapper.getRgn(), dstM.fBounds);
282
283 if (!clipper.done()) {
284 const SkIRect& cr = clipper.rect();
285 do {
286 blitter->blitMask(dstM, cr);
287 clipper.next();
288 } while (!clipper.done());
289 }
290
291 return true;
292}
293
294SkMaskFilterBase::FilterReturn
295SkMaskFilterBase::filterRRectToNine(const SkRRect&, const SkMatrix&,
296 const SkIRect& clipBounds, NinePatch*) const {
297 return kUnimplemented_FilterReturn;
298}
299
300SkMaskFilterBase::FilterReturn
301SkMaskFilterBase::filterRectsToNine(const SkRect[], int count, const SkMatrix&,
302 const SkIRect& clipBounds, NinePatch*) const {
303 return kUnimplemented_FilterReturn;
304}
305
306#if SK_SUPPORT_GPU
307std::unique_ptr<GrFragmentProcessor>
308SkMaskFilterBase::asFragmentProcessor(const GrFPArgs& args) const {
309 auto fp = this->onAsFragmentProcessor(args);
310 if (fp) {
311 SkASSERT(this->hasFragmentProcessor());
312 } else {
313 SkASSERT(!this->hasFragmentProcessor());
314 }
315 return fp;
316}
317bool SkMaskFilterBase::hasFragmentProcessor() const {
318 return this->onHasFragmentProcessor();
319}
320
321std::unique_ptr<GrFragmentProcessor>
322SkMaskFilterBase::onAsFragmentProcessor(const GrFPArgs&) const {
323 return nullptr;
324}
325bool SkMaskFilterBase::onHasFragmentProcessor() const { return false; }
326
327bool SkMaskFilterBase::canFilterMaskGPU(const GrShape& shape,
328 const SkIRect& devSpaceShapeBounds,
329 const SkIRect& clipBounds,
330 const SkMatrix& ctm,
331 SkIRect* maskRect) const {
332 return false;
333}
334
335bool SkMaskFilterBase::directFilterMaskGPU(GrRecordingContext*,
336 GrRenderTargetContext*,
337 GrPaint&&,
338 const GrClip&,
339 const SkMatrix& viewMatrix,
340 const GrShape&) const {
341 return false;
342}
343
344GrSurfaceProxyView SkMaskFilterBase::filterMaskGPU(GrRecordingContext*,
345 GrSurfaceProxyView view,
346 GrColorType srcColorType,
347 SkAlphaType srcAlphaType,
348 const SkMatrix& ctm,
349 const SkIRect& maskRect) const {
350 return {};
351}
352#endif
353
354void SkMaskFilterBase::computeFastBounds(const SkRect& src, SkRect* dst) const {
355 SkMask srcM, dstM;
356
357 srcM.fBounds = src.roundOut();
358 srcM.fRowBytes = 0;
359 srcM.fFormat = SkMask::kA8_Format;
360
361 SkIPoint margin; // ignored
362 if (this->filterMask(&dstM, srcM, SkMatrix::I(), &margin)) {
363 dst->set(dstM.fBounds);
364 } else {
365 dst->set(srcM.fBounds);
366 }
367}
368
369///////////////////////////////////////////////////////////////////////////////////////////////////
370
371template <typename T> static inline T join(const T& a, const T& b) {
372 T r = a;
373 r.join(b);
374 return r;
375}
376template <typename T> static inline T sect(const T& a, const T& b) {
377 T r = a;
378 return r.intersect(b) ? r : T::MakeEmpty();
379}
380
381class SkComposeMF : public SkMaskFilterBase {
382public:
383 SkComposeMF(sk_sp<SkMaskFilter> outer, sk_sp<SkMaskFilter> inner)
384 : fOuter(std::move(outer))
385 , fInner(std::move(inner))
386 {
387 SkASSERT(as_MFB(fOuter)->getFormat() == SkMask::kA8_Format);
388 SkASSERT(as_MFB(fInner)->getFormat() == SkMask::kA8_Format);
389 }
390
391 bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&, SkIPoint*) const override;
392
393 void computeFastBounds(const SkRect& src, SkRect* dst) const override {
394 SkRect tmp;
395 as_MFB(fInner)->computeFastBounds(src, &tmp);
396 as_MFB(fOuter)->computeFastBounds(tmp, dst);
397 }
398
399 SkMask::Format getFormat() const override { return SkMask::kA8_Format; }
400
401protected:
402#if SK_SUPPORT_GPU
403 std::unique_ptr<GrFragmentProcessor> onAsFragmentProcessor(const GrFPArgs& args) const override{
404 std::unique_ptr<GrFragmentProcessor> array[2] = {
405 as_MFB(fInner)->asFragmentProcessor(args),
406 as_MFB(fOuter)->asFragmentProcessor(args),
407 };
408 if (!array[0] || !array[1]) {
409 return nullptr;
410 }
411 return GrFragmentProcessor::RunInSeries(array, 2);
412 }
413
414 bool onHasFragmentProcessor() const override {
415 return as_MFB(fInner)->hasFragmentProcessor() && as_MFB(fOuter)->hasFragmentProcessor();
416 }
417#endif
418
419private:
420 SK_FLATTENABLE_HOOKS(SkComposeMF)
421
422 sk_sp<SkMaskFilter> fOuter;
423 sk_sp<SkMaskFilter> fInner;
424
425 void flatten(SkWriteBuffer&) const override;
426
427 friend class SkMaskFilter;
428
429 typedef SkMaskFilterBase INHERITED;
430};
431
432bool SkComposeMF::filterMask(SkMask* dst, const SkMask& src, const SkMatrix& ctm,
433 SkIPoint* margin) const {
434 SkIPoint innerMargin;
435 SkMask innerMask;
436
437 if (!as_MFB(fInner)->filterMask(&innerMask, src, ctm, &innerMargin)) {
438 return false;
439 }
440 if (!as_MFB(fOuter)->filterMask(dst, innerMask, ctm, margin)) {
441 return false;
442 }
443 if (margin) {
444 margin->fX += innerMargin.fX;
445 margin->fY += innerMargin.fY;
446 }
447 sk_free(innerMask.fImage);
448 return true;
449}
450
451void SkComposeMF::flatten(SkWriteBuffer & buffer) const {
452 buffer.writeFlattenable(fOuter.get());
453 buffer.writeFlattenable(fInner.get());
454}
455
456sk_sp<SkFlattenable> SkComposeMF::CreateProc(SkReadBuffer& buffer) {
457 auto outer = buffer.readMaskFilter();
458 auto inner = buffer.readMaskFilter();
459 if (!buffer.validate(outer && inner)) {
460 return nullptr;
461 }
462 return SkMaskFilter::MakeCompose(std::move(outer), std::move(inner));
463}
464
465///////////////////////////////////////////////////////////////////////////////////////////////////
466
467class SkCombineMF : public SkMaskFilterBase {
468public:
469 SkCombineMF(sk_sp<SkMaskFilter> dst, sk_sp<SkMaskFilter> src, SkCoverageMode mode)
470 : fDst(std::move(dst))
471 , fSrc(std::move(src))
472 , fMode(mode)
473 {
474 SkASSERT(as_MFB(fSrc)->getFormat() == SkMask::kA8_Format);
475 SkASSERT(as_MFB(fDst)->getFormat() == SkMask::kA8_Format);
476 }
477
478 bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&, SkIPoint*) const override;
479
480 void computeFastBounds(const SkRect& src, SkRect* dst) const override {
481 SkRect srcR, dstR;
482 as_MFB(fSrc)->computeFastBounds(src, &srcR);
483 as_MFB(fDst)->computeFastBounds(src, &dstR);
484 *dst = join(srcR, dstR);
485 }
486
487 SkMask::Format getFormat() const override { return SkMask::kA8_Format; }
488
489 SK_FLATTENABLE_HOOKS(SkCombineMF)
490
491protected:
492#if SK_SUPPORT_GPU
493 std::unique_ptr<GrFragmentProcessor> onAsFragmentProcessor(const GrFPArgs& args) const override{
494 auto src = as_MFB(fSrc)->asFragmentProcessor(args);
495 auto dst = as_MFB(fDst)->asFragmentProcessor(args);
496 if (!src || !dst) {
497 return nullptr;
498 }
499 return GrXfermodeFragmentProcessor::MakeFromTwoProcessors(std::move(src), std::move(dst),
500 SkUncorrelatedCoverageModeToBlendMode(fMode));
501 }
502
503 bool onHasFragmentProcessor() const override {
504 return as_MFB(fSrc)->hasFragmentProcessor() && as_MFB(fDst)->hasFragmentProcessor();
505 }
506#endif
507
508private:
509 sk_sp<SkMaskFilter> fDst;
510 sk_sp<SkMaskFilter> fSrc;
511 SkCoverageMode fMode;
512
513 void flatten(SkWriteBuffer&) const override;
514
515 friend class SkMaskFilter;
516
517 typedef SkMaskFilterBase INHERITED;
518};
519
520#include "src/core/SkSafeMath.h"
521
522class DrawIntoMask : public SkDraw {
523public:
524 // we ignore the offset of the mask->fBounds
525 DrawIntoMask(SkMask* mask) {
526 int w = mask->fBounds.width();
527 int h = mask->fBounds.height();
528 size_t size = SkSafeMath::Mul(w, h);
529 mask->fFormat = SkMask::kA8_Format;
530 mask->fImage = SkMask::AllocImage(size, SkMask::kZeroInit_Alloc);
531 mask->fRowBytes = w;
532
533 SkAssertResult(fDst.reset(*mask));
534
535 fMatrixStorage.reset();
536 fMatrix = &fMatrixStorage;
537
538 fRCStorage.setRect({ 0, 0, w, h });
539 fRC = &fRCStorage;
540 }
541
542 void drawAsBitmap(const SkMask& m, const SkPaint& p) {
543 SkBitmap b;
544 b.installMaskPixels(m);
545 this->drawSprite(b, m.fBounds.fLeft, m.fBounds.fTop, p);
546 }
547
548private:
549 SkMatrix fMatrixStorage;
550 SkRasterClip fRCStorage;
551};
552
553static SkIRect join(const SkIRect& src, const SkIRect& dst, SkCoverageMode mode) {
554 switch (mode) {
555 case SkCoverageMode::kUnion: return join(src, dst);
556 case SkCoverageMode::kIntersect: return sect(src, dst);
557 case SkCoverageMode::kDifference: return src;
558 case SkCoverageMode::kReverseDifference: return dst;
559 case SkCoverageMode::kXor: return join(src, dst);
560 }
561 // not reached
562 return { 0, 0, 0, 0 };
563}
564
565bool SkCombineMF::filterMask(SkMask* dst, const SkMask& src, const SkMatrix& ctm,
566 SkIPoint* margin) const {
567 SkIPoint srcP, dstP;
568 SkMask srcM, dstM;
569
570 if (!as_MFB(fSrc)->filterMask(&srcM, src, ctm, &srcP)) {
571 return false;
572 }
573 if (!as_MFB(fDst)->filterMask(&dstM, src, ctm, &dstP)) {
574 return false;
575 }
576
577 dst->fBounds = join(srcM.fBounds, dstM.fBounds, fMode);
578 dst->fFormat = SkMask::kA8_Format;
579 if (src.fImage == nullptr) {
580 dst->fImage = nullptr;
581 return true;
582 }
583
584 DrawIntoMask md(dst);
585 SkPaint p;
586
587 p.setBlendMode(SkBlendMode::kSrc);
588 dstM.fBounds.offset(-dst->fBounds.fLeft, -dst->fBounds.fTop);
589 md.drawAsBitmap(dstM, p);
590 p.setBlendMode(SkUncorrelatedCoverageModeToBlendMode(fMode));
591 srcM.fBounds.offset(-dst->fBounds.fLeft, -dst->fBounds.fTop);
592 md.drawAsBitmap(srcM, p);
593
594 sk_free(srcM.fImage);
595 sk_free(dstM.fImage);
596 return true;
597}
598
599void SkCombineMF::flatten(SkWriteBuffer & buffer) const {
600 buffer.writeFlattenable(fDst.get());
601 buffer.writeFlattenable(fSrc.get());
602 buffer.write32(static_cast<uint32_t>(fMode));
603}
604
605sk_sp<SkFlattenable> SkCombineMF::CreateProc(SkReadBuffer& buffer) {
606 auto dst = buffer.readMaskFilter();
607 auto src = buffer.readMaskFilter();
608 SkCoverageMode mode = buffer.read32LE(SkCoverageMode::kLast);
609 if (!buffer.validate(dst && src)) {
610 return nullptr;
611 }
612 return SkMaskFilter::MakeCombine(std::move(dst), std::move(src), mode);
613}
614
615///////////////////////////////////////////////////////////////////////////////////////////////////
616
617sk_sp<SkMaskFilter> SkMaskFilter::MakeCompose(sk_sp<SkMaskFilter> outer,
618 sk_sp<SkMaskFilter> inner) {
619 if (!outer) {
620 return inner;
621 }
622 if (!inner) {
623 return outer;
624 }
625 if (as_MFB(inner)->getFormat() != SkMask::kA8_Format ||
626 as_MFB(outer)->getFormat() != SkMask::kA8_Format) {
627 return nullptr;
628 }
629 return sk_sp<SkMaskFilter>(new SkComposeMF(std::move(outer), std::move(inner)));
630}
631
632sk_sp<SkMaskFilter> SkMaskFilter::MakeCombine(sk_sp<SkMaskFilter> dst, sk_sp<SkMaskFilter> src,
633 SkCoverageMode mode) {
634 if (!dst) {
635 return src;
636 }
637 if (!src) {
638 return dst;
639 }
640
641 if (as_MFB(dst)->getFormat() != SkMask::kA8_Format ||
642 as_MFB(src)->getFormat() != SkMask::kA8_Format) {
643 return nullptr;
644 }
645 return sk_sp<SkMaskFilter>(new SkCombineMF(std::move(dst), std::move(src), mode));
646}
647
648void SkMaskFilter::RegisterFlattenables() {
649 SK_REGISTER_FLATTENABLE(SkComposeMF);
650 SK_REGISTER_FLATTENABLE(SkCombineMF);
651 sk_register_blur_maskfilter_createproc();
652#if SK_SUPPORT_GPU
653 gr_register_sdf_maskfilter_createproc();
654#endif
655}
656