#include "PathBuilderNVpr.h"
#include "PathNVpr.h"
#include "Line.h"
#include "nvpr/ConvexPolygon.h"
#include <map>
using namespace mozilla::gfx::nvpr;
using namespace std;
namespace mozilla {
namespace gfx {
class PathCache
: public map<PathDescriptionNVpr, RefPtr<PathObjectNVpr> >
, public nvpr::UserData::Object
{};
PathBuilderNVpr::PathBuilderNVpr(FillRule aFillRule)
: mFillRule(aFillRule)
, mIsPolygon(true)
{
}
PathBuilderNVpr::PathBuilderNVpr(FillRule aFillRule,
TemporaryRef<PathObjectNVpr> aPathObject)
: mFillRule(aFillRule)
{
RefPtr<PathObjectNVpr> pathObject = aPathObject;
mPathObject = pathObject.forget();
mIsPolygon = mPathObject->Polygon().IsEmpty();
}
PathBuilderNVpr::PathBuilderNVpr(FillRule aFillRule,
TemporaryRef<PathObjectNVpr> aPathObject,
const Matrix& aTransform)
: mFillRule(aFillRule)
{
RefPtr<PathObjectNVpr> pathObject = aPathObject;
mPathObject = new PathObjectNVpr(*pathObject, aTransform);
mIsPolygon = mPathObject->Polygon().IsEmpty();
}
PathBuilderNVpr::~PathBuilderNVpr()
{
}
void
PathBuilderNVpr::MoveTo(const Point& aPoint)
{
MakeWritable();
if (!mDescription.IsEmpty()) {
mIsPolygon = false;
}
mDescription.AppendCommand(GL_MOVE_TO_NV);
mDescription.AppendPoint(aPoint);
mStartPoint = aPoint;
mCurrentPoint = aPoint;
}
void
PathBuilderNVpr::LineTo(const Point& aPoint)
{
MakeWritable();
if (mDescription.IsEmpty()) {
MoveTo(aPoint);
return;
}
if (mDescription.mCommands.back() != GL_MOVE_TO_NV
&& mDescription.mCommands.back() != GL_LINE_TO_NV) {
mIsPolygon = false;
}
mDescription.AppendCommand(GL_LINE_TO_NV);
mDescription.AppendPoint(aPoint);
mCurrentPoint = aPoint;
}
void
PathBuilderNVpr::BezierTo(const Point& aCP1,
const Point& aCP2,
const Point& aCP3)
{
MakeWritable();
if (mDescription.IsEmpty()) {
MoveTo(aCP1);
}
mDescription.AppendCommand(GL_CUBIC_CURVE_TO_NV);
mDescription.AppendPoint(aCP1);
mDescription.AppendPoint(aCP2);
mDescription.AppendPoint(aCP3);
mCurrentPoint = aCP3;
mIsPolygon = false;
}
void
PathBuilderNVpr::QuadraticBezierTo(const Point& aCP1,
const Point& aCP2)
{
MakeWritable();
if (mDescription.IsEmpty()) {
MoveTo(aCP1);
}
mDescription.AppendCommand(GL_QUADRATIC_CURVE_TO_NV);
mDescription.AppendPoint(aCP1);
mDescription.AppendPoint(aCP2);
mCurrentPoint = aCP2;
mIsPolygon = false;
}
void
PathBuilderNVpr::Close()
{
MakeWritable();
mDescription.AppendCommand(GL_CLOSE_PATH_NV);
mCurrentPoint = mStartPoint;
}
void
PathBuilderNVpr::Arc(const Point& aOrigin, Float aRadius, Float aStartAngle,
Float aEndAngle, bool aAntiClockwise)
{
MakeWritable();
const Point startPoint(aOrigin.x + cos(aStartAngle) * aRadius,
aOrigin.y + sin(aStartAngle) * aRadius);
LineTo(startPoint);
mIsPolygon = false;
if (fabs(aEndAngle - aStartAngle) > 2 * M_PI - 1e-5) {
mDescription.AppendCommand(GL_CIRCULAR_CCW_ARC_TO_NV);
mDescription.AppendPoint(aOrigin);
mDescription.AppendFloat(aRadius);
mDescription.AppendFloat(aStartAngle * 180 / M_PI);
mDescription.AppendFloat(360 + aStartAngle * 180 / M_PI);
return;
}
const Point endPoint(aOrigin.x + cos(aEndAngle) * aRadius,
aOrigin.y + sin(aEndAngle) * aRadius);
if (aAntiClockwise && aEndAngle < aStartAngle) {
aEndAngle += 2 * M_PI;
} else if (!aAntiClockwise && aEndAngle > aStartAngle) {
aEndAngle -= 2 * M_PI;
}
if (fabs(aEndAngle - aStartAngle) < M_PI) {
mDescription.AppendCommand(aAntiClockwise ? GL_LARGE_CW_ARC_TO_NV
: GL_LARGE_CCW_ARC_TO_NV);
} else {
mDescription.AppendCommand(aAntiClockwise ? GL_SMALL_CW_ARC_TO_NV
: GL_SMALL_CCW_ARC_TO_NV);
}
mDescription.AppendFloat(aRadius); mDescription.AppendFloat(aRadius); mDescription.AppendFloat(0);
mDescription.AppendPoint(endPoint);
mCurrentPoint = endPoint;
}
Point
PathBuilderNVpr::CurrentPoint() const
{
return mCurrentPoint;
}
TemporaryRef<Path>
PathBuilderNVpr::Finish()
{
if (mPathObject) {
MOZ_ASSERT(mDescription.IsEmpty());
return new PathNVpr(mFillRule, mPathObject.forget());
}
PathCache& pathCache = gl->GetUserObject<PathCache>(&nvpr::UserData::mPathCache);
RefPtr<PathObjectNVpr>& pathObject = pathCache[mDescription];
if (pathObject) {
return new PathNVpr(mFillRule, pathObject);
}
if (mIsPolygon) {
vector<Point> points(mDescription.mCoords.size() / 2);
for (size_t i = 0; i < mDescription.mCoords.size(); i += 2) {
points.push_back(Point(mDescription.mCoords[i],
mDescription.mCoords[i + 1]));
}
pathObject = new PathObjectNVpr(mDescription, mStartPoint, mCurrentPoint,
ConvexPolygon(points));
} else {
pathObject = new PathObjectNVpr(mDescription, mStartPoint, mCurrentPoint);
}
return new PathNVpr(mFillRule, pathObject);
}
void
PathBuilderNVpr::MakeWritable()
{
if (!mPathObject) {
return;
}
MOZ_ASSERT(mDescription.IsEmpty());
gl->MakeCurrent();
GLint commandCount;
gl->GetPathParameterivNV(*mPathObject, GL_PATH_COMMAND_COUNT_NV, &commandCount);
mDescription.mCommands.resize(commandCount);
gl->GetPathCommandsNV(*mPathObject, mDescription.mCommands.data());
GLint coordCount;
gl->GetPathParameterivNV(*mPathObject, GL_PATH_COORD_COUNT_NV, &coordCount);
mDescription.mCoords.resize(coordCount);
gl->GetPathCoordsNV(*mPathObject, mDescription.mCoords.data());
mStartPoint = mPathObject->StartPoint();
mCurrentPoint = mPathObject->CurrentPoint();
mPathObject = nullptr;
}
PathDescriptionNVpr::PathDescriptionNVpr(const PathDescriptionNVpr& aOther)
: mCommands(aOther.mCommands)
, mCoords(aOther.mCoords)
{
}
PathDescriptionNVpr::PathDescriptionNVpr(PathDescriptionNVpr&& aOther)
{
swap(mCommands, aOther.mCommands);
swap(mCoords, aOther.mCoords);
}
bool
PathDescriptionNVpr::operator <(const PathDescriptionNVpr& aOther) const
{
if (mCoords.size() != aOther.mCoords.size()) {
return mCoords.size() < aOther.mCoords.size();
}
if (mCommands.size() != aOther.mCommands.size()) {
return mCommands.size() < aOther.mCommands.size();
}
for (size_t i = 0; i < mCoords.size(); i++) {
if (mCoords[i] != aOther.mCoords[i]) {
return mCoords[i] < aOther.mCoords[i];
}
}
for (size_t i = 0; i < mCommands.size(); i++) {
if (mCommands[i] != aOther.mCommands[i]) {
return mCommands[i] < aOther.mCommands[i];
}
}
return false;
}
}
}