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