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 V_BEGIN_NAMESPACE
struct VPathData VPath::VPathData::VPathData():m_points(),
{
VPathData():ref(-1),
m_points(),
m_elements(), m_elements(),
m_segments(0), m_segments(0),
mStartPoint(), mStartPoint(),
mNewSegment(true){} mNewSegment(true){}
void copy(VPathData *o); VPath::VPathData::VPathData(const VPathData &o):m_points(o.m_points),
void moveTo(const VPointF &pt); m_elements(o.m_elements),
void lineTo(const VPointF &pt); m_segments(o.m_segments),
void cubicTo(const VPointF &c1, const VPointF &c2, const VPointF &e); mStartPoint(o.mStartPoint),
void close(); mNewSegment(o.mNewSegment){}
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;
};
void VPath::VPathData::transform(const VMatrix &m)
void VPathData::transform(const VMatrix &m)
{ {
for(auto &i : m_points) { for(auto &i : m_points) {
i = m.map(i); i = m.map(i);
} }
} }
void VPathData::checkNewSegment() void VPath::VPathData::checkNewSegment()
{ {
if (mNewSegment) { if (mNewSegment) {
moveTo(VPointF(0,0)); moveTo(VPointF(0,0));
mNewSegment = false; 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; mStartPoint = p;
mNewSegment = false; mNewSegment = false;
@ -64,13 +42,13 @@ void VPathData::moveTo(const VPointF &p)
m_points.push_back(p); m_points.push_back(p);
m_segments++; m_segments++;
} }
void VPathData::lineTo(const VPointF &p) void VPath::VPathData::lineTo(const VPointF &p)
{ {
checkNewSegment(); checkNewSegment();
m_elements.push_back(VPath::Element::LineTo); m_elements.push_back(VPath::Element::LineTo);
m_points.push_back(p); 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(); checkNewSegment();
m_elements.push_back(VPath::Element::CubicTo); 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); m_points.push_back(e);
} }
void VPathData::close() void VPath::VPathData::close()
{ {
if (isEmpty()) return;
const VPointF &lastPt = m_points.back(); const VPointF &lastPt = m_points.back();
if (!fuzzyCompare(mStartPoint, lastPt)) { if (!fuzzyCompare(mStartPoint, lastPt)) {
lineTo(mStartPoint); lineTo(mStartPoint);
@ -89,144 +69,149 @@ void VPathData::close()
mNewSegment = true; mNewSegment = true;
} }
void VPathData::reset() void VPath::VPathData::reset()
{ {
if (isEmpty()) return;
m_elements.clear(); m_elements.clear();
m_points.clear(); m_points.clear();
m_segments = 0; m_segments = 0;
} }
int VPathData::segments() const int VPath::VPathData::segments() const
{ {
return m_segments; return m_segments + 1;
} }
void VPath::VPathData::reserve(int num_elm)
static const struct VPathData shared_empty;
inline void VPath::cleanUp(VPathData *d)
{ {
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()) int point_count = 0;
*this = copy(); 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; addOval(VRectF(cx-radius, cy-radius, 2*radius, 2*radius) , dir);
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;
} }
VPath::~VPath() void VPath::VPathData::addOval(const VRectF &rect, VPath::Direction dir)
{ {
if (!d->ref.deref()) if (rect.isNull()) return;
cleanUp(d);
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() void VPath::VPathData::addRect(const VRectF &rect, VPath::Direction dir)
: d(const_cast<VPathData*>(&shared_empty))
{ {
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; if (vCompare(rx, 0.f) || vCompare(ry, 0.f)) {
d->ref.ref(); addRect(rect, dir);
} return;
}
VPath::VPath(VPath &&other): d(other.d) float x = rect.x();
{ float y = rect.y();
other.d = const_cast<VPathData*>(&shared_empty); 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) if (dir == VPath::Direction::CW) {
{ moveTo(VPointF(x + w, y + ry/2.f));
other.d->ref.ref(); arcTo(VRectF(x + w - rx, y + h - ry, rx, ry), 0 , -90, false);
if (!d->ref.deref()) arcTo(VRectF(x, y + h - ry, rx, ry), -90 , -90, false);
cleanUp(d); arcTo(VRectF(x, y, rx, ry), -180 , -90, false);
arcTo(VRectF(x + w - rx, y, rx, ry), -270 , -90, false);
d = other.d; close();
return *this; } 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); static float tForArcAngle(float angle);
void findEllipseCoords(const VRectF &r, float angle, float length, void findEllipseCoords(const VRectF &r, float angle, float length,
VPointF* startPoint, VPointF *endPoint) VPointF* startPoint, VPointF *endPoint)
@ -454,132 +439,6 @@ curvesForArc(const VRectF &rect, float startAngle, float sweepLength,
return startPoint; 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, void VPath::addPolystarStar(float startAngle, float cx, float cy, float points,
float innerRadius, float outerRadius, float innerRadius, float outerRadius,
float innerRoundness, float outerRoundness, float innerRoundness, float outerRoundness,
@ -617,7 +476,9 @@ void VPath::addPolystarStar(float startAngle, float cx, float cy, float points,
currentAngle += halfAnglePerPoint * angleDir; 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++) { for (int i = 0; i < numPoints; i++) {
float radius = longSegment ? outerRadius : innerRadius; 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)); y = (float) (radius * sin(currentAngle));
if (innerRoundness == 0 && outerRoundness == 0) { if (innerRoundness == 0 && outerRoundness == 0) {
lineTo(VPointF(x + cx, y + cy)); ref.lineTo(VPointF(x + cx, y + cy));
} else { } else {
float cp1Theta = (float) (atan2(previousY, previousX) - M_PI / 2.0 * angleDir); float cp1Theta = (float) (atan2(previousY, previousX) - M_PI / 2.0 * angleDir);
float cp1Dx = (float) cos(cp1Theta); float cp1Dx = (float) cos(cp1Theta);
@ -661,7 +522,7 @@ void VPath::addPolystarStar(float startAngle, float cx, float cy, float points,
cp2y *= partialPointAmount; cp2y *= partialPointAmount;
} }
cubicTo(VPointF(previousX - cp1x + cx, previousY - cp1y + cy), ref.cubicTo(VPointF(previousX - cp1x + cx, previousY - cp1y + cy),
VPointF(x + cp2x + cx, y + cp2y + cy), VPointF(x + cp2x + cx, y + cp2y + cy),
VPointF(x + cx, y + cy)); VPointF(x + cx, y + cy));
} }
@ -729,11 +590,4 @@ void VPath::addPolystarPolygon(float startAngle, float cx, float cy, float point
close(); close();
} }
void VPath::transform(const VMatrix &m)
{
if (isEmpty()) return;
detach();
d->transform(m);
}
V_END_NAMESPACE V_END_NAMESPACE

View File

@ -4,6 +4,7 @@
#include "vrect.h" #include "vrect.h"
#include "vmatrix.h" #include "vmatrix.h"
#include<vector> #include<vector>
#include<vcowptr.h>
V_BEGIN_NAMESPACE V_BEGIN_NAMESPACE
@ -22,24 +23,18 @@ public:
CubicTo, CubicTo,
Close Close
}; };
~VPath();
VPath();
VPath(const VPath &path);
VPath(VPath &&other);
VPath &operator=(const VPath &);
VPath &operator=(VPath &&other);
bool isEmpty()const; bool isEmpty()const;
void moveTo(const VPointF &p); void moveTo(const VPointF &p);
inline void moveTo(float x, float y); void moveTo(float x, float y);
void lineTo(const VPointF &p); 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); 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 arcTo(const VRectF &rect, float startAngle, float sweepLength, bool forceMoveTo);
void close(); void close();
void reset(); void reset();
void reserve(int num_elm); void reserve(int num_elm);
int segments() const;
void addCircle(float cx, float cy, float radius, VPath::Direction dir = Direction::CW); void addCircle(float cx, float cy, float radius, VPath::Direction dir = Direction::CW);
void addOval(const VRectF &rect, 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); 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, void addPolystarPolygon(float startAngle, float cx, float cy, float points,
float radius, float roundness, float radius, float roundness,
VPath::Direction dir = Direction::CW); VPath::Direction dir = Direction::CW);
void transform(const VMatrix &m); void transform(const VMatrix &m);
const std::vector<VPath::Element> &elements() const; const std::vector<VPath::Element> &elements() const;
const std::vector<VPointF> &points() const; const std::vector<VPointF> &points() const;
private: private:
friend class VRaster; 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; int segments() const;
VPath copy() const; void transform(const VMatrix &m);
void detach(); void addRoundRect(const VRectF &, float, float, VPath::Direction);
void cleanUp(VPathData *x); void addRect(const VRectF &, VPath::Direction);
VPathData *d; 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) inline void VPath::lineTo(float x, float y)
{ {
lineTo(VPointF(x,y)); lineTo(VPointF(x,y));
@ -79,6 +135,46 @@ inline void VPath::cubicTo(float c1x, float c1y, float c2x, float c2y, float ex,
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 V_END_NAMESPACE
#endif // VPATH_H #endif // VPATH_H