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