| /* | |
| * Copyright 2018 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "include/core/SkStrokeRec.h" | |
| #include "include/effects/SkOpPathEffect.h" | |
| #include "src/core/SkReadBuffer.h" | |
| #include "src/core/SkRectPriv.h" | |
| #include "src/core/SkWriteBuffer.h" | |
| #include "src/effects/SkOpPE.h" | |
| sk_sp<SkPathEffect> SkMergePathEffect::Make(sk_sp<SkPathEffect> one, sk_sp<SkPathEffect> two, | |
| SkPathOp op) { | |
| return sk_sp<SkPathEffect>(new SkOpPE(std::move(one), std::move(two), op)); | |
| } | |
| SkOpPE::SkOpPE(sk_sp<SkPathEffect> one, sk_sp<SkPathEffect> two, SkPathOp op) | |
| : fOne(std::move(one)), fTwo(std::move(two)), fOp(op) {} | |
| bool SkOpPE::onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec, | |
| const SkRect* cull, const SkMatrix& ctm) const { | |
| SkPath one, two; | |
| if (fOne) { | |
| if (!fOne->filterPath(&one, src, rec, cull, ctm)) { | |
| return false; | |
| } | |
| } else { | |
| one = src; | |
| } | |
| if (fTwo) { | |
| if (!fTwo->filterPath(&two, src, rec, cull, ctm)) { | |
| return false; | |
| } | |
| } else { | |
| two = src; | |
| } | |
| return Op(one, two, fOp, dst); | |
| } | |
| bool SkOpPE::computeFastBounds(SkRect* bounds) const { | |
| if (!bounds) { | |
| return (!SkToBool(fOne) || as_PEB(fOne)->computeFastBounds(nullptr)) && | |
| (!SkToBool(fTwo) || as_PEB(fTwo)->computeFastBounds(nullptr)); | |
| } | |
| // bounds will hold the result of the fOne while b2 holds the result of fTwo's fast bounds | |
| SkRect b2 = *bounds; | |
| if (fOne && !as_PEB(fOne)->computeFastBounds(bounds)) { | |
| return false; | |
| } | |
| if (fTwo && !as_PEB(fTwo)->computeFastBounds(&b2)) { | |
| return false; | |
| } | |
| switch (fOp) { | |
| case SkPathOp::kIntersect_SkPathOp: | |
| if (!bounds->intersect(b2)) { | |
| bounds->setEmpty(); | |
| } | |
| break; | |
| case SkPathOp::kDifference_SkPathOp: | |
| // (one - two) conservatively leaves one's bounds unmodified | |
| break; | |
| case SkPathOp::kReverseDifference_SkPathOp: | |
| // (two - one) conservatively leaves two's bounds unmodified | |
| *bounds = b2; | |
| break; | |
| case SkPathOp::kXOR_SkPathOp: | |
| // fall through to union since XOR computes a subset of regular OR | |
| case SkPathOp::kUnion_SkPathOp: | |
| bounds->join(b2); | |
| break; | |
| } | |
| return true; | |
| } | |
| void SkOpPE::flatten(SkWriteBuffer& buffer) const { | |
| buffer.writeFlattenable(fOne.get()); | |
| buffer.writeFlattenable(fTwo.get()); | |
| buffer.write32(fOp); | |
| } | |
| sk_sp<SkFlattenable> SkOpPE::CreateProc(SkReadBuffer& buffer) { | |
| auto one = buffer.readPathEffect(); | |
| auto two = buffer.readPathEffect(); | |
| SkPathOp op = buffer.read32LE(kReverseDifference_SkPathOp); | |
| return buffer.isValid() ? SkMergePathEffect::Make(std::move(one), std::move(two), op) : nullptr; | |
| } | |
| ////////////////////////////////////////////////////////////////////////////////////////////////// | |
| sk_sp<SkPathEffect> SkMatrixPathEffect::MakeTranslate(SkScalar dx, SkScalar dy) { | |
| if (!SkScalarsAreFinite(dx, dy)) { | |
| return nullptr; | |
| } | |
| return sk_sp<SkPathEffect>(new SkMatrixPE(SkMatrix::Translate(dx, dy))); | |
| } | |
| sk_sp<SkPathEffect> SkMatrixPathEffect::Make(const SkMatrix& matrix) { | |
| if (!matrix.isFinite()) { | |
| return nullptr; | |
| } | |
| return sk_sp<SkPathEffect>(new SkMatrixPE(matrix)); | |
| } | |
| SkMatrixPE::SkMatrixPE(const SkMatrix& matrix) : fMatrix(matrix) { | |
| SkASSERT(matrix.isFinite()); | |
| } | |
| bool SkMatrixPE::onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*, | |
| const SkMatrix&) const { | |
| src.transform(fMatrix, dst); | |
| return true; | |
| } | |
| void SkMatrixPE::flatten(SkWriteBuffer& buffer) const { | |
| buffer.writeMatrix(fMatrix); | |
| } | |
| sk_sp<SkFlattenable> SkMatrixPE::CreateProc(SkReadBuffer& buffer) { | |
| SkMatrix mx; | |
| buffer.readMatrix(&mx); | |
| return buffer.isValid() ? SkMatrixPathEffect::Make(mx) : nullptr; | |
| } | |
| ////////////////////////////////////////////////////////////////////////////////////////////////// | |
| sk_sp<SkPathEffect> SkStrokePathEffect::Make(SkScalar width, SkPaint::Join join, SkPaint::Cap cap, | |
| SkScalar miter) { | |
| if (!SkScalarsAreFinite(width, miter) || width < 0 || miter < 0) { | |
| return nullptr; | |
| } | |
| return sk_sp<SkPathEffect>(new SkStrokePE(width, join, cap, miter)); | |
| } | |
| SkStrokePE::SkStrokePE(SkScalar width, SkPaint::Join join, SkPaint::Cap cap, SkScalar miter) | |
| : fWidth(width), fMiter(miter), fJoin(join), fCap(cap) {} | |
| bool SkStrokePE::onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*, | |
| const SkMatrix&) const { | |
| SkStrokeRec rec(SkStrokeRec::kFill_InitStyle); | |
| rec.setStrokeStyle(fWidth); | |
| rec.setStrokeParams(fCap, fJoin, fMiter); | |
| return rec.applyToPath(dst, src); | |
| } | |
| bool SkStrokePE::computeFastBounds(SkRect* bounds) const { | |
| if (bounds) { | |
| SkStrokeRec rec(SkStrokeRec::kFill_InitStyle); | |
| rec.setStrokeStyle(fWidth); | |
| rec.setStrokeParams(fCap, fJoin, fMiter); | |
| bounds->outset(rec.getInflationRadius(), rec.getInflationRadius()); | |
| } | |
| return true; | |
| } | |
| void SkStrokePE::flatten(SkWriteBuffer& buffer) const { | |
| buffer.writeScalar(fWidth); | |
| buffer.writeScalar(fMiter); | |
| buffer.write32(fJoin); | |
| buffer.write32(fCap); | |
| } | |
| sk_sp<SkFlattenable> SkStrokePE::CreateProc(SkReadBuffer& buffer) { | |
| SkScalar width = buffer.readScalar(); | |
| SkScalar miter = buffer.readScalar(); | |
| SkPaint::Join join = buffer.read32LE(SkPaint::kLast_Join); | |
| SkPaint::Cap cap = buffer.read32LE(SkPaint::kLast_Cap); | |
| return buffer.isValid() ? SkStrokePathEffect::Make(width, join, cap, miter) : nullptr; | |
| } | |
| ////////////////////////////////////////////////////////////////////////////////////////////////// | |
| #include "include/effects/SkStrokeAndFillPathEffect.h" | |
| #include "src/core/SkPathPriv.h" | |
| sk_sp<SkPathEffect> SkStrokeAndFillPathEffect::Make() { | |
| static SkPathEffect* strokeAndFill = new SkStrokeAndFillPE; | |
| return sk_ref_sp(strokeAndFill); | |
| } | |
| void SkStrokeAndFillPE::flatten(SkWriteBuffer&) const {} | |
| static bool known_to_be_opposite_directions(const SkPath& a, const SkPath& b) { | |
| auto a_dir = SkPathPriv::ComputeFirstDirection(a), | |
| b_dir = SkPathPriv::ComputeFirstDirection(b); | |
| return (a_dir == SkPathFirstDirection::kCCW && | |
| b_dir == SkPathFirstDirection::kCW) | |
| || | |
| (a_dir == SkPathFirstDirection::kCW && | |
| b_dir == SkPathFirstDirection::kCCW); | |
| } | |
| bool SkStrokeAndFillPE::onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec, | |
| const SkRect*, const SkMatrix&) const { | |
| // This one is weird, since we exist to allow this paint-style to go away. If we see it, | |
| // just let the normal machine run its course. | |
| if (rec->getStyle() == SkStrokeRec::kStrokeAndFill_Style) { | |
| *dst = src; | |
| return true; | |
| } | |
| if (rec->getStyle() == SkStrokeRec::kStroke_Style) { | |
| if (!rec->applyToPath(dst, src)) { | |
| return false; | |
| } | |
| if (known_to_be_opposite_directions(src, *dst)) { | |
| dst->reverseAddPath(src); | |
| } else { | |
| dst->addPath(src); | |
| } | |
| } else { | |
| *dst = src; | |
| } | |
| rec->setFillStyle(); | |
| return true; | |
| } | |
| sk_sp<SkFlattenable> SkStrokeAndFillPE::CreateProc(SkReadBuffer& buffer) { | |
| return SkStrokeAndFillPathEffect::Make(); | |
| } |