Just expose factories for patheffects

bug: skia:11957
Change-Id: If2983fcd1b520a7ae77650d7e5ab226af9db52e0
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/410782
Commit-Queue: Mike Reed <reed@google.com>
Reviewed-by: Tyler Denniston <tdenniston@google.com>
This commit is contained in:
Mike Reed 2021-05-20 15:16:34 -04:00 committed by Skia Commit-Bot
parent 298de8a345
commit ec87dc1b76
7 changed files with 295 additions and 489 deletions

View File

@ -73,7 +73,6 @@ samples_sources = [
"$_samplecode/SampleStrokeRect.cpp",
"$_samplecode/SampleStrokeVerb.cpp",
"$_samplecode/SampleTextBox.cpp",
"$_samplecode/SampleTextEffects.cpp",
"$_samplecode/SampleTextureUpload.cpp",
"$_samplecode/SampleThinAA.cpp",
"$_samplecode/SampleTiming.cpp",

View File

@ -8,37 +8,9 @@
#ifndef Sk1DPathEffect_DEFINED
#define Sk1DPathEffect_DEFINED
#include "include/core/SkFlattenable.h"
#include "include/core/SkPath.h"
#include "include/core/SkPathEffect.h"
class SkPathMeasure;
// This class is not exported to java.
class SK_API Sk1DPathEffect : public SkPathEffect {
public:
protected:
bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*) const override;
/** Called at the start of each contour, returns the initial offset
into that contour.
*/
virtual SkScalar begin(SkScalar contourLength) const = 0;
/** Called with the current distance along the path, with the current matrix
for the point/tangent at the specified distance.
Return the distance to travel for the next call. If return <= 0, then that
contour is done.
*/
virtual SkScalar next(SkPath* dst, SkScalar dist, SkPathMeasure&) const = 0;
private:
// For simplicity, assume fast bounds cannot be computed
bool computeFastBounds(SkRect*) const override { return false; }
using INHERITED = SkPathEffect;
};
class SK_API SkPath1DPathEffect : public Sk1DPathEffect {
class SK_API SkPath1DPathEffect {
public:
enum Style {
kTranslate_Style, // translate the shape to each position
@ -57,24 +29,7 @@ public:
*/
static sk_sp<SkPathEffect> Make(const SkPath& path, SkScalar advance, SkScalar phase, Style);
protected:
SkPath1DPathEffect(const SkPath& path, SkScalar advance, SkScalar phase, Style);
void flatten(SkWriteBuffer&) const override;
bool onFilterPath(SkPath*, const SkPath&, SkStrokeRec*, const SkRect*) const override;
// overrides from Sk1DPathEffect
SkScalar begin(SkScalar contourLength) const override;
SkScalar next(SkPath*, SkScalar, SkPathMeasure&) const override;
private:
SK_FLATTENABLE_HOOKS(SkPath1DPathEffect)
SkPath fPath; // copied from constructor
SkScalar fAdvance; // copied from constructor
SkScalar fInitialOffset; // computed from phase
Style fStyle; // copied from constructor
using INHERITED = Sk1DPathEffect;
static void RegisterFlattenables();
};
#endif

View File

@ -8,101 +8,23 @@
#ifndef Sk2DPathEffect_DEFINED
#define Sk2DPathEffect_DEFINED
#include "include/core/SkFlattenable.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkPath.h"
#include "include/core/SkPathEffect.h"
class SK_API Sk2DPathEffect : public SkPathEffect {
protected:
/** New virtual, to be overridden by subclasses.
This is called once from filterPath, and provides the
uv parameter bounds for the path. Subsequent calls to
next() will receive u and v values within these bounds,
and then a call to end() will signal the end of processing.
*/
virtual void begin(const SkIRect& uvBounds, SkPath* dst) const;
virtual void next(const SkPoint& loc, int u, int v, SkPath* dst) const;
virtual void end(SkPath* dst) const;
class SkMatrix;
class SkPath;
/** Low-level virtual called per span of locations in the u-direction.
The default implementation calls next() repeatedly with each
location.
*/
virtual void nextSpan(int u, int v, int ucount, SkPath* dst) const;
class SK_API SkLine2DPathEffect {
public:
static sk_sp<SkPathEffect> Make(SkScalar width, const SkMatrix& matrix);
const SkMatrix& getMatrix() const { return fMatrix; }
// protected so that subclasses can call this during unflattening
explicit Sk2DPathEffect(const SkMatrix& mat);
void flatten(SkWriteBuffer&) const override;
bool onFilterPath(SkPath*, const SkPath&, SkStrokeRec*, const SkRect*) const override;
private:
SkMatrix fMatrix, fInverse;
bool fMatrixIsInvertible;
// For simplicity, assume fast bounds cannot be computed
bool computeFastBounds(SkRect*) const override { return false; }
// illegal
Sk2DPathEffect(const Sk2DPathEffect&);
Sk2DPathEffect& operator=(const Sk2DPathEffect&);
friend class Sk2DPathEffectBlitter;
using INHERITED = SkPathEffect;
static void RegisterFlattenables();
};
class SK_API SkLine2DPathEffect : public Sk2DPathEffect {
class SK_API SkPath2DPathEffect {
public:
static sk_sp<SkPathEffect> Make(SkScalar width, const SkMatrix& matrix) {
if (!(width >= 0)) {
return nullptr;
}
return sk_sp<SkPathEffect>(new SkLine2DPathEffect(width, matrix));
}
static sk_sp<SkPathEffect> Make(const SkMatrix& matrix, const SkPath& path);
protected:
SkLine2DPathEffect(SkScalar width, const SkMatrix& matrix)
: Sk2DPathEffect(matrix), fWidth(width) {
SkASSERT(width >= 0);
}
void flatten(SkWriteBuffer&) const override;
bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*) const override;
void nextSpan(int u, int v, int ucount, SkPath*) const override;
private:
SK_FLATTENABLE_HOOKS(SkLine2DPathEffect)
SkScalar fWidth;
using INHERITED = Sk2DPathEffect;
};
class SK_API SkPath2DPathEffect : public Sk2DPathEffect {
public:
/**
* Stamp the specified path to fill the shape, using the matrix to define
* the latice.
*/
static sk_sp<SkPathEffect> Make(const SkMatrix& matrix, const SkPath& path) {
return sk_sp<SkPathEffect>(new SkPath2DPathEffect(matrix, path));
}
protected:
SkPath2DPathEffect(const SkMatrix&, const SkPath&);
void flatten(SkWriteBuffer&) const override;
void next(const SkPoint&, int u, int v, SkPath*) const override;
private:
SK_FLATTENABLE_HOOKS(SkPath2DPathEffect)
SkPath fPath;
using INHERITED = Sk2DPathEffect;
static void RegisterFlattenables();
};
#endif

View File

@ -1,172 +0,0 @@
/*
* Copyright 2011 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/SkCanvas.h"
#include "include/core/SkColorFilter.h"
#include "include/core/SkColorPriv.h"
#include "include/core/SkPath.h"
#include "include/core/SkRegion.h"
#include "include/core/SkShader.h"
#include "include/core/SkStrokeRec.h"
#include "include/core/SkTypeface.h"
#include "include/effects/SkGradientShader.h"
#include "include/utils/SkTextUtils.h"
#include "samplecode/Sample.h"
#include "src/core/SkReadBuffer.h"
#include "src/core/SkWriteBuffer.h"
#include "src/utils/SkUTF.h"
#include "include/effects/SkGradientShader.h"
#include "include/effects/Sk2DPathEffect.h"
class Dot2DPathEffect : public Sk2DPathEffect {
public:
Dot2DPathEffect(SkScalar radius, const SkMatrix& matrix,
SkTDArray<SkPoint>* pts)
: Sk2DPathEffect(matrix), fRadius(radius), fPts(pts) {}
SK_FLATTENABLE_HOOKS(Dot2DPathEffect)
protected:
void begin(const SkIRect& uvBounds, SkPath* dst) const override {
if (fPts) {
fPts->reset();
}
this->INHERITED::begin(uvBounds, dst);
}
void next(const SkPoint& loc, int u, int v, SkPath* dst) const override {
if (fPts) {
*fPts->append() = loc;
}
dst->addCircle(loc.fX, loc.fY, fRadius);
}
void flatten(SkWriteBuffer& buffer) const override {
buffer.writeMatrix(this->getMatrix());
buffer.writeScalar(fRadius);
}
private:
SkScalar fRadius;
SkTDArray<SkPoint>* fPts;
using INHERITED = Sk2DPathEffect;
};
// Register this path effect as deserializable before main().
namespace {
static struct Initializer {
Initializer() {
SK_REGISTER_FLATTENABLE(Dot2DPathEffect);
}
} initializer;
} // namespace
sk_sp<SkFlattenable> Dot2DPathEffect::CreateProc(SkReadBuffer& buffer) {
SkMatrix matrix;
buffer.readMatrix(&matrix);
return sk_make_sp<Dot2DPathEffect>(buffer.readScalar(), matrix, nullptr);
}
class InverseFillPE : public SkPathEffect {
public:
InverseFillPE() {}
bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*) const override {
*dst = src;
dst->setFillType(SkPathFillType::kInverseWinding);
return true;
}
private:
SK_FLATTENABLE_HOOKS(InverseFillPE)
bool computeFastBounds(SkRect* bounds) const override { return false; }
using INHERITED = SkPathEffect;
};
sk_sp<SkFlattenable> InverseFillPE::CreateProc(SkReadBuffer& buffer) {
return sk_make_sp<InverseFillPE>();
}
static sk_sp<SkPathEffect> makepe(float interp, SkTDArray<SkPoint>* pts) {
SkMatrix lattice;
SkScalar rad = 3 + SkIntToScalar(4) * (1 - interp);
lattice.setScale(rad*2, rad*2, 0, 0);
lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
return sk_make_sp<Dot2DPathEffect>(rad, lattice, pts);
}
class TextEffectsView : public Sample {
sk_sp<SkTypeface> fFace;
SkScalar fInterp;
SkScalar fDx;
public:
TextEffectsView() {
fFace = SkTypeface::MakeFromFile("/Users/reed/Downloads/p052024l.pfb");
fInterp = 0;
fDx = SK_Scalar1/64;
}
protected:
SkString name() override { return SkString("Text Effects"); }
void drawBG(SkCanvas* canvas) {
canvas->drawColor(SK_ColorWHITE);
}
void drawdots(SkCanvas* canvas, SkString s, SkScalar x, SkScalar y, const SkFont& font) {
SkTDArray<SkPoint> pts;
auto pe = makepe(fInterp, &pts);
SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
SkPath path, dstPath;
SkTextUtils::GetPath(s.c_str(), s.size(), SkTextEncoding::kUTF8, x, y, font, &path);
pe->filterPath(&dstPath, path, &rec, nullptr);
SkPaint paint;
paint.setAntiAlias(true);
paint.setStrokeWidth(10);
paint.setColor(SK_ColorRED);
canvas->drawPoints(SkCanvas::kPoints_PointMode, pts.count(), pts.begin(), paint);
}
void onDrawContent(SkCanvas* canvas) override {
this->drawBG(canvas);
SkScalar x = SkIntToScalar(20);
SkScalar y = SkIntToScalar(300);
SkFont font(SkTypeface::MakeFromName("sans-serif", SkFontStyle::Bold()), 240);
SkString str("9");
canvas->drawString(str, x, y, font, SkPaint());
drawdots(canvas, str, x, y, font);
if (false) {
fInterp += fDx;
if (fInterp > 1) {
fInterp = 1;
fDx = -fDx;
} else if (fInterp < 0) {
fInterp = 0;
fDx = -fDx;
}
}
}
private:
using INHERITED = Sample;
};
//////////////////////////////////////////////////////////////////////////////
DEF_SAMPLE( return new TextEffectsView(); )

View File

@ -16,68 +16,119 @@
// Put in a governor to limit crash values from looping too long (and allocating too much ram).
#define MAX_REASONABLE_ITERATIONS 100000
bool Sk1DPathEffect::onFilterPath(SkPath* dst, const SkPath& src,
SkStrokeRec*, const SkRect*) const {
SkPathMeasure meas(src, false);
do {
int governor = MAX_REASONABLE_ITERATIONS;
SkScalar length = meas.getLength();
SkScalar distance = this->begin(length);
while (distance < length && --governor >= 0) {
SkScalar delta = this->next(dst, distance, meas);
if (delta <= 0) {
break;
class Sk1DPathEffect : public SkPathEffect {
public:
protected:
bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*) const override {
SkPathMeasure meas(src, false);
do {
int governor = MAX_REASONABLE_ITERATIONS;
SkScalar length = meas.getLength();
SkScalar distance = this->begin(length);
while (distance < length && --governor >= 0) {
SkScalar delta = this->next(dst, distance, meas);
if (delta <= 0) {
break;
}
distance += delta;
}
distance += delta;
}
} while (meas.nextContour());
return true;
}
} while (meas.nextContour());
return true;
}
/** Called at the start of each contour, returns the initial offset
into that contour.
*/
virtual SkScalar begin(SkScalar contourLength) const = 0;
/** Called with the current distance along the path, with the current matrix
for the point/tangent at the specified distance.
Return the distance to travel for the next call. If return <= 0, then that
contour is done.
*/
virtual SkScalar next(SkPath* dst, SkScalar dist, SkPathMeasure&) const = 0;
private:
// For simplicity, assume fast bounds cannot be computed
bool computeFastBounds(SkRect*) const override { return false; }
using INHERITED = SkPathEffect;
};
///////////////////////////////////////////////////////////////////////////////
SkPath1DPathEffect::SkPath1DPathEffect(const SkPath& path, SkScalar advance, SkScalar phase,
Style style) : fPath(path) {
SkASSERT(advance > 0 && !path.isEmpty());
SkASSERT((unsigned)style <= kMorph_Style);
class SkPath1DPathEffectImpl : public Sk1DPathEffect {
public:
SkPath1DPathEffectImpl(const SkPath& path, SkScalar advance, SkScalar phase,
SkPath1DPathEffect::Style style) : fPath(path) {
SkASSERT(advance > 0 && !path.isEmpty());
// Make the path thread-safe.
fPath.updateBoundsCache();
(void)fPath.getGenerationID();
// Make the path thread-safe.
fPath.updateBoundsCache();
(void)fPath.getGenerationID();
// cleanup their phase parameter, inverting it so that it becomes an
// offset along the path (to match the interpretation in PostScript)
if (phase < 0) {
phase = -phase;
if (phase > advance) {
phase = SkScalarMod(phase, advance);
// cleanup their phase parameter, inverting it so that it becomes an
// offset along the path (to match the interpretation in PostScript)
if (phase < 0) {
phase = -phase;
if (phase > advance) {
phase = SkScalarMod(phase, advance);
}
} else {
if (phase > advance) {
phase = SkScalarMod(phase, advance);
}
phase = advance - phase;
}
} else {
if (phase > advance) {
phase = SkScalarMod(phase, advance);
// now catch the edge case where phase == advance (within epsilon)
if (phase >= advance) {
phase = 0;
}
phase = advance - phase;
}
// now catch the edge case where phase == advance (within epsilon)
if (phase >= advance) {
phase = 0;
}
SkASSERT(phase >= 0);
SkASSERT(phase >= 0);
fAdvance = advance;
fInitialOffset = phase;
if ((unsigned)style > kMorph_Style) {
SkDEBUGF("SkPath1DPathEffect style enum out of range %d\n", style);
fAdvance = advance;
fInitialOffset = phase;
fStyle = style;
}
fStyle = style;
}
bool SkPath1DPathEffect::onFilterPath(SkPath* dst, const SkPath& src,
SkStrokeRec* rec, const SkRect* cullRect) const {
rec->setFillStyle();
return this->INHERITED::onFilterPath(dst, src, rec, cullRect);
}
bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
const SkRect* cullRect) const override {
rec->setFillStyle();
return this->INHERITED::onFilterPath(dst, src, rec, cullRect);
}
SkScalar begin(SkScalar contourLength) const override {
return fInitialOffset;
}
SkScalar next(SkPath*, SkScalar, SkPathMeasure&) const override;
static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) {
SkScalar advance = buffer.readScalar();
SkPath path;
buffer.readPath(&path);
SkScalar phase = buffer.readScalar();
SkPath1DPathEffect::Style style = buffer.read32LE(SkPath1DPathEffect::kLastEnum_Style);
return buffer.isValid() ? SkPath1DPathEffect::Make(path, advance, phase, style) : nullptr;
}
void flatten(SkWriteBuffer& buffer) const override {
buffer.writeScalar(fAdvance);
buffer.writePath(fPath);
buffer.writeScalar(fInitialOffset);
buffer.writeUInt(fStyle);
}
Factory getFactory() const override { return CreateProc; }
const char* getTypeName() const override { return "SkPath1DPathEffect"; }
private:
SkPath fPath; // copied from constructor
SkScalar fAdvance; // copied from constructor
SkScalar fInitialOffset; // computed from phase
SkPath1DPathEffect::Style fStyle; // copied from constructor
using INHERITED = Sk1DPathEffect;
};
static bool morphpoints(SkPoint dst[], const SkPoint src[], int count,
SkPathMeasure& meas, SkScalar dist) {
@ -153,52 +204,29 @@ static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas,
}
}
SkScalar SkPath1DPathEffect::begin(SkScalar contourLength) const {
return fInitialOffset;
}
sk_sp<SkFlattenable> SkPath1DPathEffect::CreateProc(SkReadBuffer& buffer) {
SkScalar advance = buffer.readScalar();
SkPath path;
buffer.readPath(&path);
SkScalar phase = buffer.readScalar();
Style style = buffer.read32LE(kLastEnum_Style);
return buffer.isValid() ? SkPath1DPathEffect::Make(path, advance, phase, style) : nullptr;
}
void SkPath1DPathEffect::flatten(SkWriteBuffer& buffer) const {
buffer.writeScalar(fAdvance);
buffer.writePath(fPath);
buffer.writeScalar(fInitialOffset);
buffer.writeUInt(fStyle);
}
SkScalar SkPath1DPathEffect::next(SkPath* dst, SkScalar distance,
SkPathMeasure& meas) const {
SkScalar SkPath1DPathEffectImpl::next(SkPath* dst, SkScalar distance,
SkPathMeasure& meas) const {
#if defined(SK_BUILD_FOR_FUZZER)
if (dst->countPoints() > 100000) {
return fAdvance;
}
#endif
switch (fStyle) {
case kTranslate_Style: {
case SkPath1DPathEffect::kTranslate_Style: {
SkPoint pos;
if (meas.getPosTan(distance, &pos, nullptr)) {
dst->addPath(fPath, pos.fX, pos.fY);
}
} break;
case kRotate_Style: {
case SkPath1DPathEffect::kRotate_Style: {
SkMatrix matrix;
if (meas.getMatrix(distance, &matrix)) {
dst->addPath(fPath, matrix);
}
} break;
case kMorph_Style:
case SkPath1DPathEffect::kMorph_Style:
morphpath(dst, fPath, meas, distance);
break;
default:
SkDEBUGFAIL("unknown Style enum");
break;
}
return fAdvance;
}
@ -210,5 +238,9 @@ sk_sp<SkPathEffect> SkPath1DPathEffect::Make(const SkPath& path, SkScalar advanc
if (advance <= 0 || !SkScalarIsFinite(advance) || !SkScalarIsFinite(phase) || path.isEmpty()) {
return nullptr;
}
return sk_sp<SkPathEffect>(new SkPath1DPathEffect(path, advance, phase, style));
return sk_sp<SkPathEffect>(new SkPath1DPathEffectImpl(path, advance, phase, style));
}
void SkPath1DPathEffect::RegisterFlattenables() {
SK_REGISTER_FLATTENABLE(SkPath1DPathEffectImpl);
}

View File

@ -5,7 +5,6 @@
* found in the LICENSE file.
*/
#include "include/core/SkPath.h"
#include "include/core/SkRegion.h"
#include "include/core/SkStrokeRec.h"
@ -13,128 +12,199 @@
#include "src/core/SkReadBuffer.h"
#include "src/core/SkWriteBuffer.h"
Sk2DPathEffect::Sk2DPathEffect(const SkMatrix& mat) : fMatrix(mat) {
// Calling invert will set the type mask on both matrices, making them thread safe.
fMatrixIsInvertible = fMatrix.invert(&fInverse);
}
class Sk2DPathEffect : public SkPathEffect {
public:
Sk2DPathEffect(const SkMatrix& mat) : fMatrix(mat) {
// Calling invert will set the type mask on both matrices, making them thread safe.
fMatrixIsInvertible = fMatrix.invert(&fInverse);
}
bool Sk2DPathEffect::onFilterPath(SkPath* dst, const SkPath& src,
SkStrokeRec*, const SkRect*) const {
if (!fMatrixIsInvertible) {
protected:
/** New virtual, to be overridden by subclasses.
This is called once from filterPath, and provides the
uv parameter bounds for the path. Subsequent calls to
next() will receive u and v values within these bounds,
and then a call to end() will signal the end of processing.
*/
virtual void begin(const SkIRect& uvBounds, SkPath* dst) const {}
virtual void next(const SkPoint& loc, int u, int v, SkPath* dst) const {}
virtual void end(SkPath* dst) const {}
/** Low-level virtual called per span of locations in the u-direction.
The default implementation calls next() repeatedly with each
location.
*/
virtual void nextSpan(int x, int y, int ucount, SkPath* path) const {
if (!fMatrixIsInvertible) {
return;
}
#if defined(SK_BUILD_FOR_FUZZER)
if (count > 100) {
return;
}
#endif
const SkMatrix& mat = this->getMatrix();
SkPoint src, dst;
src.set(SkIntToScalar(x) + SK_ScalarHalf, SkIntToScalar(y) + SK_ScalarHalf);
do {
mat.mapPoints(&dst, &src, 1);
this->next(dst, x++, y, path);
src.fX += SK_Scalar1;
} while (--ucount > 0);
}
const SkMatrix& getMatrix() const { return fMatrix; }
void flatten(SkWriteBuffer& buffer) const override {
this->INHERITED::flatten(buffer);
buffer.writeMatrix(fMatrix);
}
bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
const SkRect* cullRect) const override {
if (!fMatrixIsInvertible) {
return false;
}
SkPath tmp;
SkIRect ir;
src.transform(fInverse, &tmp);
tmp.getBounds().round(&ir);
if (!ir.isEmpty()) {
this->begin(ir, dst);
SkRegion rgn;
rgn.setPath(tmp, SkRegion(ir));
SkRegion::Iterator iter(rgn);
for (; !iter.done(); iter.next()) {
const SkIRect& rect = iter.rect();
for (int y = rect.fTop; y < rect.fBottom; ++y) {
this->nextSpan(rect.fLeft, y, rect.width(), dst);
}
}
this->end(dst);
}
return true;
}
private:
SkMatrix fMatrix, fInverse;
bool fMatrixIsInvertible;
// For simplicity, assume fast bounds cannot be computed
bool computeFastBounds(SkRect*) const override { return false; }
friend class Sk2DPathEffectBlitter;
using INHERITED = SkPathEffect;
};
///////////////////////////////////////////////////////////////////////////////
class SkLine2DPathEffectImpl : public Sk2DPathEffect {
public:
SkLine2DPathEffectImpl(SkScalar width, const SkMatrix& matrix)
: Sk2DPathEffect(matrix)
, fWidth(width)
{
SkASSERT(width >= 0);
}
bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
const SkRect* cullRect) const override {
if (this->INHERITED::onFilterPath(dst, src, rec, cullRect)) {
rec->setStrokeStyle(fWidth);
return true;
}
return false;
}
SkPath tmp;
SkIRect ir;
void nextSpan(int u, int v, int ucount, SkPath* dst) const override {
if (ucount > 1) {
SkPoint src[2], dstP[2];
src.transform(fInverse, &tmp);
tmp.getBounds().round(&ir);
if (!ir.isEmpty()) {
this->begin(ir, dst);
src[0].set(SkIntToScalar(u) + SK_ScalarHalf, SkIntToScalar(v) + SK_ScalarHalf);
src[1].set(SkIntToScalar(u+ucount) + SK_ScalarHalf, SkIntToScalar(v) + SK_ScalarHalf);
this->getMatrix().mapPoints(dstP, src, 2);
SkRegion rgn;
rgn.setPath(tmp, SkRegion(ir));
SkRegion::Iterator iter(rgn);
for (; !iter.done(); iter.next()) {
const SkIRect& rect = iter.rect();
for (int y = rect.fTop; y < rect.fBottom; ++y) {
this->nextSpan(rect.fLeft, y, rect.width(), dst);
}
dst->moveTo(dstP[0]);
dst->lineTo(dstP[1]);
}
this->end(dst);
}
return true;
}
void Sk2DPathEffect::nextSpan(int x, int y, int count, SkPath* path) const {
if (!fMatrixIsInvertible) {
return;
static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) {
SkMatrix matrix;
buffer.readMatrix(&matrix);
SkScalar width = buffer.readScalar();
return SkLine2DPathEffect::Make(width, matrix);
}
#if defined(SK_BUILD_FOR_FUZZER)
if (count > 100) {
return;
void flatten(SkWriteBuffer &buffer) const override {
buffer.writeMatrix(this->getMatrix());
buffer.writeScalar(fWidth);
}
#endif
const SkMatrix& mat = this->getMatrix();
SkPoint src, dst;
Factory getFactory() const override { return CreateProc; }
const char* getTypeName() const override { return "SkLine2DPathEffect"; }
src.set(SkIntToScalar(x) + SK_ScalarHalf, SkIntToScalar(y) + SK_ScalarHalf);
do {
mat.mapPoints(&dst, &src, 1);
this->next(dst, x++, y, path);
src.fX += SK_Scalar1;
} while (--count > 0);
}
private:
SkScalar fWidth;
void Sk2DPathEffect::begin(const SkIRect& uvBounds, SkPath* dst) const {}
void Sk2DPathEffect::next(const SkPoint& loc, int u, int v, SkPath* dst) const {}
void Sk2DPathEffect::end(SkPath* dst) const {}
using INHERITED = Sk2DPathEffect;
};
///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
void Sk2DPathEffect::flatten(SkWriteBuffer& buffer) const {
this->INHERITED::flatten(buffer);
buffer.writeMatrix(fMatrix);
}
class SK_API SkPath2DPathEffectImpl : public Sk2DPathEffect {
public:
SkPath2DPathEffectImpl(const SkMatrix& m, const SkPath& p) : INHERITED(m), fPath(p) {}
///////////////////////////////////////////////////////////////////////////////
bool SkLine2DPathEffect::onFilterPath(SkPath* dst, const SkPath& src,
SkStrokeRec* rec, const SkRect* cullRect) const {
if (this->INHERITED::onFilterPath(dst, src, rec, cullRect)) {
rec->setStrokeStyle(fWidth);
return true;
void next(const SkPoint& loc, int u, int v, SkPath* dst) const override {
dst->addPath(fPath, loc.fX, loc.fY);
}
return false;
}
void SkLine2DPathEffect::nextSpan(int u, int v, int ucount, SkPath* dst) const {
if (ucount > 1) {
SkPoint src[2], dstP[2];
src[0].set(SkIntToScalar(u) + SK_ScalarHalf, SkIntToScalar(v) + SK_ScalarHalf);
src[1].set(SkIntToScalar(u+ucount) + SK_ScalarHalf, SkIntToScalar(v) + SK_ScalarHalf);
this->getMatrix().mapPoints(dstP, src, 2);
dst->moveTo(dstP[0]);
dst->lineTo(dstP[1]);
static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) {
SkMatrix matrix;
buffer.readMatrix(&matrix);
SkPath path;
buffer.readPath(&path);
return SkPath2DPathEffect::Make(matrix, path);
}
void flatten(SkWriteBuffer& buffer) const override {
buffer.writeMatrix(this->getMatrix());
buffer.writePath(fPath);
}
Factory getFactory() const override { return CreateProc; }
const char* getTypeName() const override { return "SkPath2DPathEffect"; }
private:
SkPath fPath;
using INHERITED = Sk2DPathEffect;
};
//////////////////////////////////////////////////////////////////////////////////////////////////
sk_sp<SkPathEffect> SkLine2DPathEffect::Make(SkScalar width, const SkMatrix& matrix) {
if (!(width >= 0)) {
return nullptr;
}
return sk_sp<SkPathEffect>(new SkLine2DPathEffectImpl(width, matrix));
}
sk_sp<SkFlattenable> SkLine2DPathEffect::CreateProc(SkReadBuffer& buffer) {
SkMatrix matrix;
buffer.readMatrix(&matrix);
SkScalar width = buffer.readScalar();
return SkLine2DPathEffect::Make(width, matrix);
sk_sp<SkPathEffect> SkPath2DPathEffect::Make(const SkMatrix& matrix, const SkPath& path) {
return sk_sp<SkPathEffect>(new SkPath2DPathEffectImpl(matrix, path));
}
void SkLine2DPathEffect::flatten(SkWriteBuffer &buffer) const {
buffer.writeMatrix(this->getMatrix());
buffer.writeScalar(fWidth);
void SkLine2DPathEffect::RegisterFlattenables() {
SK_REGISTER_FLATTENABLE(SkLine2DPathEffectImpl);
}
///////////////////////////////////////////////////////////////////////////////
SkPath2DPathEffect::SkPath2DPathEffect(const SkMatrix& m, const SkPath& p)
: INHERITED(m), fPath(p) {
}
sk_sp<SkFlattenable> SkPath2DPathEffect::CreateProc(SkReadBuffer& buffer) {
SkMatrix matrix;
buffer.readMatrix(&matrix);
SkPath path;
buffer.readPath(&path);
return SkPath2DPathEffect::Make(matrix, path);
}
void SkPath2DPathEffect::flatten(SkWriteBuffer& buffer) const {
buffer.writeMatrix(this->getMatrix());
buffer.writePath(fPath);
}
void SkPath2DPathEffect::next(const SkPoint& loc, int u, int v,
SkPath* dst) const {
dst->addPath(fPath, loc.fX, loc.fY);
void SkPath2DPathEffect::RegisterFlattenables() {
SK_REGISTER_FLATTENABLE(SkPath2DPathEffectImpl);
}

View File

@ -89,11 +89,11 @@
SK_REGISTER_FLATTENABLE(SkCornerPathEffect);
SK_REGISTER_FLATTENABLE(SkDashImpl);
SK_REGISTER_FLATTENABLE(SkDiscretePathEffect);
SK_REGISTER_FLATTENABLE(SkLine2DPathEffect);
SkLine2DPathEffect::RegisterFlattenables();
SkPath2DPathEffect::RegisterFlattenables();
SK_REGISTER_FLATTENABLE(SkMatrixPE);
SK_REGISTER_FLATTENABLE(SkOpPE);
SK_REGISTER_FLATTENABLE(SkPath1DPathEffect);
SK_REGISTER_FLATTENABLE(SkPath2DPathEffect);
SkPath1DPathEffect::RegisterFlattenables();
SK_REGISTER_FLATTENABLE(SkStrokePE);
SK_REGISTER_FLATTENABLE(SkStrokeAndFillPE);
SK_REGISTER_FLATTENABLE(SkTrimPE);