/* * 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 SkMergePathEffect::Make(sk_sp one, sk_sp two, SkPathOp op) { return sk_sp(new SkOpPE(std::move(one), std::move(two), op)); } SkOpPE::SkOpPE(sk_sp one, sk_sp 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 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 SkMatrixPathEffect::MakeTranslate(SkScalar dx, SkScalar dy) { if (!SkScalarsAreFinite(dx, dy)) { return nullptr; } return sk_sp(new SkMatrixPE(SkMatrix::Translate(dx, dy))); } sk_sp SkMatrixPathEffect::Make(const SkMatrix& matrix) { if (!matrix.isFinite()) { return nullptr; } return sk_sp(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 SkMatrixPE::CreateProc(SkReadBuffer& buffer) { SkMatrix mx; buffer.readMatrix(&mx); return buffer.isValid() ? SkMatrixPathEffect::Make(mx) : nullptr; } ////////////////////////////////////////////////////////////////////////////////////////////////// sk_sp 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(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 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 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 SkStrokeAndFillPE::CreateProc(SkReadBuffer& buffer) { return SkStrokeAndFillPathEffect::Make(); }