lottie/vector: refactor vpath class to use vcow_ptr

Change-Id: Ib16da52a8a97bbee87766ee362e1d6e5678ac305
This commit is contained in:
subhransu mohanty 2018-07-23 09:33:56 +09:00 committed by Hermet Park
parent 8c20000b08
commit 84c0411929
2 changed files with 253 additions and 303 deletions

View File

@ -7,56 +7,34 @@
V_BEGIN_NAMESPACE
struct VPathData
{
VPathData():ref(-1),
m_points(),
m_elements(),
m_segments(0),
mStartPoint(),
mNewSegment(true){}
VPath::VPathData::VPathData():m_points(),
m_elements(),
m_segments(0),
mStartPoint(),
mNewSegment(true){}
void copy(VPathData *o);
void moveTo(const VPointF &pt);
void lineTo(const VPointF &pt);
void cubicTo(const VPointF &c1, const VPointF &c2, const VPointF &e);
void close();
void reset();
void checkNewSegment();
int segments() const;
void transform(const VMatrix &m);
RefCount ref;
std::vector<VPointF> m_points;
std::vector<VPath::Element> m_elements;
int m_segments;
VPointF mStartPoint;
bool mNewSegment;
};
VPath::VPathData::VPathData(const VPathData &o):m_points(o.m_points),
m_elements(o.m_elements),
m_segments(o.m_segments),
mStartPoint(o.mStartPoint),
mNewSegment(o.mNewSegment){}
void VPathData::transform(const VMatrix &m)
void VPath::VPathData::transform(const VMatrix &m)
{
for(auto &i : m_points) {
i = m.map(i);
}
}
void VPathData::checkNewSegment()
void VPath::VPathData::checkNewSegment()
{
if (mNewSegment) {
moveTo(VPointF(0,0));
mNewSegment = false;
}
}
void VPathData::copy(VPathData *o)
{
m_points = o->m_points;
m_elements = o->m_elements;
m_segments = o->m_segments;
mStartPoint = o->mStartPoint;
}
void VPathData::moveTo(const VPointF &p)
void VPath::VPathData::moveTo(const VPointF &p)
{
mStartPoint = p;
mNewSegment = false;
@ -64,13 +42,13 @@ void VPathData::moveTo(const VPointF &p)
m_points.push_back(p);
m_segments++;
}
void VPathData::lineTo(const VPointF &p)
void VPath::VPathData::lineTo(const VPointF &p)
{
checkNewSegment();
m_elements.push_back(VPath::Element::LineTo);
m_points.push_back(p);
}
void VPathData::cubicTo(const VPointF &c1, const VPointF &c2, const VPointF &e)
void VPath::VPathData::cubicTo(const VPointF &c1, const VPointF &c2, const VPointF &e)
{
checkNewSegment();
m_elements.push_back(VPath::Element::CubicTo);
@ -79,8 +57,10 @@ void VPathData::cubicTo(const VPointF &c1, const VPointF &c2, const VPointF &e)
m_points.push_back(e);
}
void VPathData::close()
void VPath::VPathData::close()
{
if (isEmpty()) return;
const VPointF &lastPt = m_points.back();
if (!fuzzyCompare(mStartPoint, lastPt)) {
lineTo(mStartPoint);
@ -89,144 +69,149 @@ void VPathData::close()
mNewSegment = true;
}
void VPathData::reset()
void VPath::VPathData::reset()
{
if (isEmpty()) return;
m_elements.clear();
m_points.clear();
m_segments = 0;
}
int VPathData::segments() const
int VPath::VPathData::segments() const
{
return m_segments;
return m_segments + 1;
}
static const struct VPathData shared_empty;
inline void VPath::cleanUp(VPathData *d)
void VPath::VPathData::reserve(int num_elm)
{
delete d;
m_elements.reserve(num_elm);
m_points.reserve(2 * num_elm);
}
void VPath::detach()
static VPointF curvesForArc(const VRectF &, float, float, VPointF *, int *);
static constexpr float PATH_KAPPA = 0.5522847498;
void VPath::VPathData::arcTo(const VRectF &rect, float startAngle, float sweepLength, bool forceMoveTo)
{
if (d->ref.isShared())
*this = copy();
int point_count = 0;
VPointF pts[15];
VPointF curve_start = curvesForArc(rect, startAngle, sweepLength, pts, &point_count);
if (isEmpty() || forceMoveTo) {
moveTo(curve_start);
} else {
lineTo(curve_start);
}
for (int i=0; i<point_count; i+=3) {
cubicTo(pts[i], pts[i+1], pts[i+2]);
}
}
VPath VPath::copy() const
void VPath::VPathData::addCircle(float cx, float cy, float radius, VPath::Direction dir)
{
VPath other;
other.d = new VPathData;
other.d->m_points = d->m_points;
other.d->m_elements = d->m_elements;
other.d->m_segments = d->m_segments;
other.d->ref.setOwned();
return other;
addOval(VRectF(cx-radius, cy-radius, 2*radius, 2*radius) , dir);
}
VPath::~VPath()
void VPath::VPathData::addOval(const VRectF &rect, VPath::Direction dir)
{
if (!d->ref.deref())
cleanUp(d);
if (rect.isNull()) return;
float x = rect.x();
float y = rect.y();
float w = rect.width();
float w2 = rect.width() / 2;
float w2k = w2 * PATH_KAPPA;
float h = rect.height();
float h2 = rect.height() / 2;
float h2k = h2 * PATH_KAPPA;
if (dir == VPath::Direction::CW) {
// moveto 12 o'clock.
moveTo(VPointF(x+w2, y));
// 12 -> 3 o'clock
cubicTo(VPointF(x + w2 + w2k, y), VPointF(x + w, y + h2 - h2k), VPointF(x + w, y + h2));
// 3 -> 6 o'clock
cubicTo(VPointF(x + w, y + h2 + h2k), VPointF(x + w2 + w2k, y + h), VPointF(x + w2, y + h));
// 6 -> 9 o'clock
cubicTo(VPointF(x + w2 - w2k, y + h), VPointF(x, y + h2 + h2k), VPointF(x , y + h2));
// 9 -> 12 o'clock
cubicTo(VPointF(x, y + h2 - h2k), VPointF(x + w2 - w2k, y), VPointF(x + w2, y));
} else {
// moveto 12 o'clock.
moveTo(VPointF(x+w2, y));
// 12 -> 9 o'clock
cubicTo(VPointF(x + w2 - w2k, y), VPointF(x, y + h2 - h2k), VPointF(x , y + h2));
// 9 -> 6 o'clock
cubicTo(VPointF(x, y + h2 + h2k), VPointF(x + w2 - w2k, y + h), VPointF(x + w2, y + h));
// 6 -> 3 o'clock
cubicTo(VPointF(x + w2 + w2k, y + h), VPointF(x + w, y + h2 + h2k), VPointF(x + w, y + h2));
// 3 -> 12 o'clock
cubicTo(VPointF(x + w, y + h2 - h2k), VPointF(x + w2 + w2k, y), VPointF(x+w2, y));
}
}
VPath::VPath()
: d(const_cast<VPathData*>(&shared_empty))
void VPath::VPathData::addRect(const VRectF &rect, VPath::Direction dir)
{
if (rect.isNull()) return;
float x = rect.x();
float y = rect.y();
float w = rect.width();
float h = rect.height();
if (dir == VPath::Direction::CW) {
moveTo(VPointF(x + w, y));
lineTo(VPointF(x + w, y + h));
lineTo(VPointF(x , y + h));
lineTo(VPointF(x , y));
close();
} else {
moveTo(VPointF(x + w, y));
lineTo(VPointF(x , y));
lineTo(VPointF(x , y + h));
lineTo(VPointF(x + w, y + h));
close();
}
}
VPath::VPath(const VPath &other)
void VPath::VPathData::addRoundRect(const VRectF &rect, float rx, float ry, VPath::Direction dir)
{
d = other.d;
d->ref.ref();
}
if (vCompare(rx, 0.f) || vCompare(ry, 0.f)) {
addRect(rect, dir);
return;
}
VPath::VPath(VPath &&other): d(other.d)
{
other.d = const_cast<VPathData*>(&shared_empty);
}
float x = rect.x();
float y = rect.y();
float w = rect.width();
float h = rect.height();
// clamp the rx and ry radius value.
rx = 2*rx;
ry = 2*ry;
if (rx > w) rx = w;
if (ry > h) ry = h;
VPath &VPath::operator=(const VPath &other)
{
other.d->ref.ref();
if (!d->ref.deref())
cleanUp(d);
d = other.d;
return *this;
if (dir == VPath::Direction::CW) {
moveTo(VPointF(x + w, y + ry/2.f));
arcTo(VRectF(x + w - rx, y + h - ry, rx, ry), 0 , -90, false);
arcTo(VRectF(x, y + h - ry, rx, ry), -90 , -90, false);
arcTo(VRectF(x, y, rx, ry), -180 , -90, false);
arcTo(VRectF(x + w - rx, y, rx, ry), -270 , -90, false);
close();
} else {
moveTo(VPointF(x + w, y + ry/2.f));
arcTo(VRectF(x + w - rx, y , rx, ry), 0 , 90, false);
arcTo(VRectF(x, y, rx, ry), 90 , 90, false);
arcTo(VRectF(x, y + h - ry, rx, ry), 180 , 90, false);
arcTo(VRectF(x + w - rx, y + h - ry, rx, ry), 270 , 90, false);
close();
}
}
inline VPath &VPath::operator=(VPath &&other)
{
if (!d->ref.deref())
cleanUp(d);
d = other.d;
other.d = const_cast<VPathData*>(&shared_empty);
return *this;
}
bool VPath::isEmpty()const
{
return d->m_elements.empty();
}
void VPath::close()
{
if (isEmpty()) return;
detach();
d->close();
}
void VPath::reset()
{
if (isEmpty()) return;
detach();
d->reset();
}
void VPath::moveTo(const VPointF &p)
{
detach();
d->moveTo(p);
}
void VPath::lineTo(const VPointF &p)
{
detach();
d->lineTo(p);
}
void VPath::cubicTo(const VPointF &c1, const VPointF &c2, const VPointF &e)
{
detach();
d->cubicTo(c1, c2, e);
}
void VPath::reserve(int num_elm)
{
detach();
d->m_elements.reserve(num_elm);
d->m_points.reserve(num_elm);
}
const std::vector<VPath::Element> &VPath::elements() const
{
return d->m_elements;
}
const std::vector<VPointF> &VPath::points() const
{
return d->m_points;
}
int VPath::segments() const
{
return d->m_segments + 1;
}
#define PATH_KAPPA 0.5522847498
static float tForArcAngle(float angle);
void findEllipseCoords(const VRectF &r, float angle, float length,
VPointF* startPoint, VPointF *endPoint)
@ -454,132 +439,6 @@ curvesForArc(const VRectF &rect, float startAngle, float sweepLength,
return startPoint;
}
void VPath::arcTo(const VRectF &rect, float startAngle, float sweepLength, bool forceMoveTo)
{
detach();
int point_count = 0;
VPointF pts[15];
VPointF curve_start = curvesForArc(rect, startAngle, sweepLength, pts, &point_count);
if (isEmpty() || forceMoveTo) {
d->moveTo(curve_start);
} else {
d->lineTo(curve_start);
}
for (int i=0; i<point_count; i+=3) {
d->cubicTo(pts[i], pts[i+1], pts[i+2]);
}
}
void VPath::addCircle(float cx, float cy, float radius, VPath::Direction dir)
{
addOval(VRectF(cx-radius, cy-radius, 2*radius, 2*radius) , dir);
}
void VPath::addOval(const VRectF &rect, VPath::Direction dir)
{
if (rect.isNull()) return;
detach();
float x = rect.x();
float y = rect.y();
float w = rect.width();
float w2 = rect.width() / 2;
float w2k = w2 * PATH_KAPPA;
float h = rect.height();
float h2 = rect.height() / 2;
float h2k = h2 * PATH_KAPPA;
if (dir == VPath::Direction::CW) {
// moveto 12 o'clock.
d->moveTo(VPointF(x+w2, y));
// 12 -> 3 o'clock
d->cubicTo(VPointF(x + w2 + w2k, y), VPointF(x + w, y + h2 - h2k), VPointF(x + w, y + h2));
// 3 -> 6 o'clock
d->cubicTo(VPointF(x + w, y + h2 + h2k), VPointF(x + w2 + w2k, y + h), VPointF(x + w2, y + h));
// 6 -> 9 o'clock
d->cubicTo(VPointF(x + w2 - w2k, y + h), VPointF(x, y + h2 + h2k), VPointF(x , y + h2));
// 9 -> 12 o'clock
d->cubicTo(VPointF(x, y + h2 - h2k), VPointF(x + w2 - w2k, y), VPointF(x + w2, y));
} else {
// moveto 12 o'clock.
d->moveTo(VPointF(x+w2, y));
// 12 -> 9 o'clock
d->cubicTo(VPointF(x + w2 - w2k, y), VPointF(x, y + h2 - h2k), VPointF(x , y + h2));
// 9 -> 6 o'clock
d->cubicTo(VPointF(x, y + h2 + h2k), VPointF(x + w2 - w2k, y + h), VPointF(x + w2, y + h));
// 6 -> 3 o'clock
d->cubicTo(VPointF(x + w2 + w2k, y + h), VPointF(x + w, y + h2 + h2k), VPointF(x + w, y + h2));
// 3 -> 12 o'clock
d->cubicTo(VPointF(x + w, y + h2 - h2k), VPointF(x + w2 + w2k, y), VPointF(x+w2, y));
}
}
void VPath::addRect(const VRectF &rect, VPath::Direction dir)
{
if (rect.isNull()) return;
detach();
float x = rect.x();
float y = rect.y();
float w = rect.width();
float h = rect.height();
if (dir == VPath::Direction::CW) {
moveTo(VPointF(x + w, y));
lineTo(VPointF(x + w, y + h));
lineTo(VPointF(x , y + h));
lineTo(VPointF(x , y));
close();
} else {
moveTo(VPointF(x + w, y));
lineTo(VPointF(x , y));
lineTo(VPointF(x , y + h));
lineTo(VPointF(x + w, y + h));
close();
}
}
void VPath::addRoundRect(const VRectF &rect, float rx, float ry, VPath::Direction dir)
{
if (vCompare(rx, 0.f) || vCompare(ry, 0.f)) {
addRect(rect, dir);
return;
}
float x = rect.x();
float y = rect.y();
float w = rect.width();
float h = rect.height();
// clamp the rx and ry radius value.
rx = 2*rx;
ry = 2*ry;
if (rx > w) rx = w;
if (ry > h) ry = h;
if (dir == VPath::Direction::CW) {
moveTo(VPointF(x + w, y + ry/2.f));
arcTo(VRectF(x + w - rx, y + h - ry, rx, ry), 0 , -90, false);
arcTo(VRectF(x, y + h - ry, rx, ry), -90 , -90, false);
arcTo(VRectF(x, y, rx, ry), -180 , -90, false);
arcTo(VRectF(x + w - rx, y, rx, ry), -270 , -90, false);
close();
} else {
moveTo(VPointF(x + w, y + ry/2.f));
arcTo(VRectF(x + w - rx, y , rx, ry), 0 , 90, false);
arcTo(VRectF(x, y, rx, ry), 90 , 90, false);
arcTo(VRectF(x, y + h - ry, rx, ry), 180 , 90, false);
arcTo(VRectF(x + w - rx, y + h - ry, rx, ry), 270 , 90, false);
close();
}
}
void VPath::addPolystarStar(float startAngle, float cx, float cy, float points,
float innerRadius, float outerRadius,
float innerRoundness, float outerRoundness,
@ -617,7 +476,9 @@ void VPath::addPolystarStar(float startAngle, float cx, float cy, float points,
currentAngle += halfAnglePerPoint * angleDir;
}
moveTo(VPointF(x + cx, y + cy));
VPathData &ref = d.write();
ref.moveTo(VPointF(x + cx, y + cy));
for (int i = 0; i < numPoints; i++) {
float radius = longSegment ? outerRadius : innerRadius;
@ -634,7 +495,7 @@ void VPath::addPolystarStar(float startAngle, float cx, float cy, float points,
y = (float) (radius * sin(currentAngle));
if (innerRoundness == 0 && outerRoundness == 0) {
lineTo(VPointF(x + cx, y + cy));
ref.lineTo(VPointF(x + cx, y + cy));
} else {
float cp1Theta = (float) (atan2(previousY, previousX) - M_PI / 2.0 * angleDir);
float cp1Dx = (float) cos(cp1Theta);
@ -661,9 +522,9 @@ void VPath::addPolystarStar(float startAngle, float cx, float cy, float points,
cp2y *= partialPointAmount;
}
cubicTo(VPointF(previousX - cp1x + cx, previousY - cp1y + cy),
VPointF(x + cp2x + cx, y + cp2y + cy),
VPointF(x + cx, y + cy));
ref.cubicTo(VPointF(previousX - cp1x + cx, previousY - cp1y + cy),
VPointF(x + cp2x + cx, y + cp2y + cy),
VPointF(x + cx, y + cy));
}
currentAngle += dTheta * angleDir;
@ -729,11 +590,4 @@ void VPath::addPolystarPolygon(float startAngle, float cx, float cy, float point
close();
}
void VPath::transform(const VMatrix &m)
{
if (isEmpty()) return;
detach();
d->transform(m);
}
V_END_NAMESPACE

View File

@ -4,6 +4,7 @@
#include "vrect.h"
#include "vmatrix.h"
#include<vector>
#include<vcowptr.h>
V_BEGIN_NAMESPACE
@ -22,24 +23,18 @@ public:
CubicTo,
Close
};
~VPath();
VPath();
VPath(const VPath &path);
VPath(VPath &&other);
VPath &operator=(const VPath &);
VPath &operator=(VPath &&other);
bool isEmpty()const;
void moveTo(const VPointF &p);
inline void moveTo(float x, float y);
void moveTo(float x, float y);
void lineTo(const VPointF &p);
inline void lineTo(float x, float y);
void lineTo(float x, float y);
void cubicTo(const VPointF &c1, const VPointF &c2, const VPointF &e);
inline void cubicTo(float c1x, float c1y, float c2x, float c2y, float ex, float ey);
void cubicTo(float c1x, float c1y, float c2x, float c2y, float ex, float ey);
void arcTo(const VRectF &rect, float startAngle, float sweepLength, bool forceMoveTo);
void close();
void reset();
void reserve(int num_elm);
int segments() const;
void addCircle(float cx, float cy, float radius, VPath::Direction dir = Direction::CW);
void addOval(const VRectF &rect, VPath::Direction dir = Direction::CW);
void addRoundRect(const VRectF &rect, float rx, float ry, VPath::Direction dir = Direction::CW);
@ -51,19 +46,80 @@ public:
void addPolystarPolygon(float startAngle, float cx, float cy, float points,
float radius, float roundness,
VPath::Direction dir = Direction::CW);
void transform(const VMatrix &m);
const std::vector<VPath::Element> &elements() const;
const std::vector<VPointF> &points() const;
private:
friend class VRaster;
int segments() const;
VPath copy() const;
void detach();
void cleanUp(VPathData *x);
VPathData *d;
struct VPathData {
VPathData();
VPathData(const VPathData &o);
bool isEmpty() const { return m_elements.empty();}
void moveTo(const VPointF &pt);
void lineTo(const VPointF &pt);
void cubicTo(const VPointF &c1, const VPointF &c2, const VPointF &e);
void close();
void reset();
void reserve(int num_elm);
void checkNewSegment();
int segments() const;
void transform(const VMatrix &m);
void addRoundRect(const VRectF &, float, float, VPath::Direction);
void addRect(const VRectF &, VPath::Direction);
void arcTo(const VRectF&, float, float, bool);
void addCircle(float, float, float, VPath::Direction);
void addOval(const VRectF &, VPath::Direction);
const std::vector<VPath::Element> &elements() const { return m_elements;}
const std::vector<VPointF> &points() const {return m_points;}
std::vector<VPointF> m_points;
std::vector<VPath::Element> m_elements;
int m_segments;
VPointF mStartPoint;
bool mNewSegment;
};
vcow_ptr<VPathData> d;
};
inline bool VPath::isEmpty()const
{
return d->isEmpty();
}
inline void VPath::moveTo(const VPointF &p)
{
d.write().moveTo(p);
}
inline void VPath::lineTo(const VPointF &p)
{
d.write().lineTo(p);
}
inline void VPath::close()
{
d.write().close();
}
inline void VPath::reset()
{
d.write().reset();
}
inline void VPath::reserve(int num_elm)
{
d.write().reserve(num_elm);
}
inline int VPath::segments() const
{
return d->segments();
}
inline void VPath::cubicTo(const VPointF &c1, const VPointF &c2, const VPointF &e)
{
d.write().cubicTo(c1, c2, e);
}
inline void VPath::lineTo(float x, float y)
{
lineTo(VPointF(x,y));
@ -76,7 +132,47 @@ inline void VPath::moveTo(float x, float y)
inline void VPath::cubicTo(float c1x, float c1y, float c2x, float c2y, float ex, float ey)
{
cubicTo(VPointF(c1x, c1y), VPointF(c2x, c2y), VPointF(ex, ey));
cubicTo(VPointF(c1x, c1y), VPointF(c2x, c2y), VPointF(ex, ey));
}
inline void VPath::transform(const VMatrix &m)
{
d.write().transform(m);
}
inline void VPath::arcTo(const VRectF &rect, float startAngle, float sweepLength, bool forceMoveTo)
{
d.write().arcTo(rect, startAngle, sweepLength, forceMoveTo);
}
inline void VPath::addRect(const VRectF &rect, VPath::Direction dir)
{
d.write().addRect(rect, dir);
}
inline void VPath::addRoundRect(const VRectF &rect, float rx, float ry, VPath::Direction dir)
{
d.write().addRoundRect(rect, rx, ry, dir);
}
inline void VPath::addCircle(float cx, float cy, float radius, VPath::Direction dir)
{
d.write().addCircle(cx, cy, radius, dir);
}
inline void VPath::addOval(const VRectF &rect, VPath::Direction dir)
{
d.write().addOval(rect, dir);
}
inline const std::vector<VPath::Element> &VPath::elements() const
{
return d->elements();
}
inline const std::vector<VPointF> &VPath::points() const
{
return d->points();
}
V_END_NAMESPACE