#include "config.h"
#include "Path.h"
#include "AffineTransform.h"
#include "FloatRect.h"
#include "ImageBuffer.h"
#include "StrokeStyleApplier.h"
#include "SkPath.h"
#include "SkRegion.h"
#include "SkiaUtils.h"
#include <wtf/MathExtras.h>
namespace WebCore {
Path::Path()
{
m_path = new SkPath;
}
Path::Path(const Path& other)
{
m_path = new SkPath(*other.m_path);
}
Path::~Path()
{
delete m_path;
}
Path& Path::operator=(const Path& other)
{
*m_path = *other.m_path;
return *this;
}
bool Path::isEmpty() const
{
return m_path->isEmpty();
}
bool Path::hasCurrentPoint() const
{
return m_path->getPoints(NULL, 0) != 0;
}
bool Path::contains(const FloatPoint& point, WindRule rule) const
{
return SkPathContainsPoint(m_path, point,
rule == RULE_NONZERO ? SkPath::kWinding_FillType : SkPath::kEvenOdd_FillType);
}
void Path::translate(const FloatSize& size)
{
m_path->offset(WebCoreFloatToSkScalar(size.width()), WebCoreFloatToSkScalar(size.height()));
}
FloatRect Path::boundingRect() const
{
return m_path->getBounds();
}
void Path::moveTo(const FloatPoint& point)
{
m_path->moveTo(point);
}
void Path::addLineTo(const FloatPoint& point)
{
m_path->lineTo(point);
}
void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& ep)
{
m_path->quadTo(cp, ep);
}
void Path::addBezierCurveTo(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& ep)
{
m_path->cubicTo(p1, p2, ep);
}
void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
{
m_path->arcTo(p1, p2, WebCoreFloatToSkScalar(radius));
}
void Path::closeSubpath()
{
m_path->close();
}
void Path::addArc(const FloatPoint& p, float r, float sa, float ea, bool anticlockwise) {
SkScalar cx = WebCoreFloatToSkScalar(p.x());
SkScalar cy = WebCoreFloatToSkScalar(p.y());
SkScalar radius = WebCoreFloatToSkScalar(r);
SkScalar s360 = SkIntToScalar(360);
SkRect oval;
oval.set(cx - radius, cy - radius, cx + radius, cy + radius);
float sweep = ea - sa;
SkScalar startDegrees = WebCoreFloatToSkScalar(sa * 180 / piFloat);
SkScalar sweepDegrees = WebCoreFloatToSkScalar(sweep * 180 / piFloat);
if (sweepDegrees >= s360 || sweepDegrees <= -s360) {
m_path->arcTo(oval, startDegrees, 0, false);
m_path->addOval(oval);
m_path->arcTo(oval, startDegrees + sweepDegrees, 0, true);
} else {
if (anticlockwise && sweepDegrees > 0) {
sweepDegrees -= s360;
} else if (!anticlockwise && sweepDegrees < 0) {
sweepDegrees += s360;
}
m_path->arcTo(oval, startDegrees, sweepDegrees, false);
}
}
void Path::addRect(const FloatRect& rect)
{
m_path->addRect(rect);
}
void Path::addEllipse(const FloatRect& rect)
{
m_path->addOval(rect);
}
void Path::clear()
{
m_path->reset();
}
static FloatPoint* convertPathPoints(FloatPoint dst[], const SkPoint src[], int count)
{
for (int i = 0; i < count; i++) {
dst[i].setX(SkScalarToFloat(src[i].fX));
dst[i].setY(SkScalarToFloat(src[i].fY));
}
return dst;
}
void Path::apply(void* info, PathApplierFunction function) const
{
SkPath::Iter iter(*m_path, false);
SkPoint pts[4];
PathElement pathElement;
FloatPoint pathPoints[3];
for (;;) {
switch (iter.next(pts)) {
case SkPath::kMove_Verb:
pathElement.type = PathElementMoveToPoint;
pathElement.points = convertPathPoints(pathPoints, &pts[0], 1);
break;
case SkPath::kLine_Verb:
pathElement.type = PathElementAddLineToPoint;
pathElement.points = convertPathPoints(pathPoints, &pts[1], 1);
break;
case SkPath::kQuad_Verb:
pathElement.type = PathElementAddQuadCurveToPoint;
pathElement.points = convertPathPoints(pathPoints, &pts[1], 2);
break;
case SkPath::kCubic_Verb:
pathElement.type = PathElementAddCurveToPoint;
pathElement.points = convertPathPoints(pathPoints, &pts[1], 3);
break;
case SkPath::kClose_Verb:
pathElement.type = PathElementCloseSubpath;
pathElement.points = convertPathPoints(pathPoints, 0, 0);
break;
case SkPath::kDone_Verb:
return;
}
function(info, &pathElement);
}
}
void Path::transform(const AffineTransform& xform)
{
m_path->transform(xform);
}
String Path::debugString() const
{
String result;
SkPath::Iter iter(*m_path, false);
SkPoint pts[4];
int numPoints = m_path->getPoints(0, 0);
SkPath::Verb verb;
do {
verb = iter.next(pts);
switch (verb) {
case SkPath::kMove_Verb:
result += String::format("M%.2f,%.2f ", pts[0].fX, pts[0].fY);
numPoints -= 1;
break;
case SkPath::kLine_Verb:
if (!iter.isCloseLine()) {
result += String::format("L%.2f,%.2f ", pts[1].fX, pts[1].fY);
numPoints -= 1;
}
break;
case SkPath::kQuad_Verb:
result += String::format("Q%.2f,%.2f,%.2f,%.2f ",
pts[1].fX, pts[1].fY,
pts[2].fX, pts[2].fY);
numPoints -= 2;
break;
case SkPath::kCubic_Verb:
result += String::format("C%.2f,%.2f,%.2f,%.2f,%.2f,%.2f ",
pts[1].fX, pts[1].fY,
pts[2].fX, pts[2].fY,
pts[3].fX, pts[3].fY);
numPoints -= 3;
break;
case SkPath::kClose_Verb:
result += "Z ";
break;
case SkPath::kDone_Verb:
break;
}
} while (verb != SkPath::kDone_Verb);
if (numPoints) {
ASSERT(numPoints==1);
m_path->getLastPt(pts);
result += String::format("M%.2f,%.2f ", pts[0].fX, pts[0].fY);
}
return result.stripWhiteSpace();
}
static FloatRect boundingBoxForCurrentStroke(const GraphicsContext* context)
{
SkPaint paint;
context->platformContext()->setupPaintForStroking(&paint, 0, 0);
SkPath boundingPath;
paint.getFillPath(context->platformContext()->currentPathInLocalCoordinates(), &boundingPath);
return boundingPath.getBounds();
}
FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier)
{
GraphicsContext* scratch = scratchContext();
scratch->save();
scratch->beginPath();
scratch->addPath(*this);
if (applier)
applier->strokeStyle(scratch);
FloatRect r = boundingBoxForCurrentStroke(scratch);
scratch->restore();
return r;
}
bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const
{
ASSERT(applier);
GraphicsContext* scratch = scratchContext();
scratch->save();
applier->strokeStyle(scratch);
SkPaint paint;
scratch->platformContext()->setupPaintForStroking(&paint, 0, 0);
SkPath strokePath;
paint.getFillPath(*platformPath(), &strokePath);
bool contains = SkPathContainsPoint(&strokePath, point,
SkPath::kWinding_FillType);
scratch->restore();
return contains;
}
}