/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkPathOpsPoint.h"
#include "SkPathWriter.h"
// wrap path to keep track of whether the contour is initialized and non-empty
SkPathWriter::SkPathWriter(SkPath& path)
: fPathPtr(&path)
, fCloses(0)
, fMoves(0)
{
init();
}
void SkPathWriter::close() {
if (!fHasMove) {
return;
}
bool callClose = isClosed();
lineTo();
if (fEmpty) {
return;
}
if (callClose) {
#if DEBUG_PATH_CONSTRUCTION
SkDebugf("path.close();\n");
#endif
fPathPtr->close();
fCloses++;
}
init();
}
void SkPathWriter::conicTo(const SkPoint& pt1, const SkPoint& pt2, SkScalar weight) {
lineTo();
if (fEmpty && AlmostEqualUlps(fDefer[0], pt1) && AlmostEqualUlps(pt1, pt2)) {
deferredLine(pt2);
return;
}
moveTo();
fDefer[1] = pt2;
nudge();
fDefer[0] = fDefer[1];
#if DEBUG_PATH_CONSTRUCTION
SkDebugf("path.conicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g);\n",
pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY, weight);
#endif
fPathPtr->conicTo(pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY, weight);
fEmpty = false;
}
void SkPathWriter::cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) {
lineTo();
if (fEmpty && AlmostEqualUlps(fDefer[0], pt1) && AlmostEqualUlps(pt1, pt2)
&& AlmostEqualUlps(pt2, pt3)) {
deferredLine(pt3);
return;
}
moveTo();
fDefer[1] = pt3;
nudge();
fDefer[0] = fDefer[1];
#if DEBUG_PATH_CONSTRUCTION
SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
pt1.fX, pt1.fY, pt2.fX, pt2.fY, fDefer[1].fX, fDefer[1].fY);
#endif
fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, fDefer[1].fX, fDefer[1].fY);
fEmpty = false;
}
void SkPathWriter::deferredLine(const SkPoint& pt) {
if (pt == fDefer[1]) {
return;
}
if (changedSlopes(pt)) {
lineTo();
fDefer[0] = fDefer[1];
}
fDefer[1] = pt;
}
void SkPathWriter::deferredMove(const SkPoint& pt) {
fMoved = true;
fHasMove = true;
fEmpty = true;
fDefer[0] = fDefer[1] = pt;
}
void SkPathWriter::deferredMoveLine(const SkPoint& pt) {
if (!fHasMove) {
deferredMove(pt);
}
deferredLine(pt);
}
bool SkPathWriter::hasMove() const {
return fHasMove;
}
void SkPathWriter::init() {
fEmpty = true;
fHasMove = false;
fMoved = false;
}
bool SkPathWriter::isClosed() const {
return !fEmpty && SkDPoint::ApproximatelyEqual(fFirstPt, fDefer[1]);
}
void SkPathWriter::lineTo() {
if (fDefer[0] == fDefer[1]) {
return;
}
moveTo();
nudge();
fEmpty = false;
#if DEBUG_PATH_CONSTRUCTION
SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1].fX, fDefer[1].fY);
#endif
fPathPtr->lineTo(fDefer[1].fX, fDefer[1].fY);
fDefer[0] = fDefer[1];
}
const SkPath* SkPathWriter::nativePath() const {
return fPathPtr;
}
void SkPathWriter::nudge() {
if (fEmpty || !AlmostEqualUlps(fDefer[1].fX, fFirstPt.fX)
|| !AlmostEqualUlps(fDefer[1].fY, fFirstPt.fY)) {
return;
}
fDefer[1] = fFirstPt;
}
void SkPathWriter::quadTo(const SkPoint& pt1, const SkPoint& pt2) {
lineTo();
if (fEmpty && AlmostEqualUlps(fDefer[0], pt1) && AlmostEqualUlps(pt1, pt2)) {
deferredLine(pt2);
return;
}
moveTo();
fDefer[1] = pt2;
nudge();
fDefer[0] = fDefer[1];
#if DEBUG_PATH_CONSTRUCTION
SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY);
#endif
fPathPtr->quadTo(pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY);
fEmpty = false;
}
bool SkPathWriter::someAssemblyRequired() const {
return fCloses < fMoves;
}
bool SkPathWriter::changedSlopes(const SkPoint& pt) const {
if (fDefer[0] == fDefer[1]) {
return false;
}
SkScalar deferDx = fDefer[1].fX - fDefer[0].fX;
SkScalar deferDy = fDefer[1].fY - fDefer[0].fY;
SkScalar lineDx = pt.fX - fDefer[1].fX;
SkScalar lineDy = pt.fY - fDefer[1].fY;
return deferDx * lineDy != deferDy * lineDx;
}
void SkPathWriter::moveTo() {
if (!fMoved) {
return;
}
fFirstPt = fDefer[0];
#if DEBUG_PATH_CONSTRUCTION
SkDebugf("path.moveTo(%1.9g,%1.9g);\n", fDefer[0].fX, fDefer[0].fY);
#endif
fPathPtr->moveTo(fDefer[0].fX, fDefer[0].fY);
fMoved = false;
fMoves++;
}