#include "config.h"
#include "Path.h"
#include "FloatPoint.h"
#include "FloatRect.h"
#include "FloatRoundedRect.h"
#include "PathTraversalState.h"
#include "RoundedRect.h"
#include "TextStream.h"
#include <math.h>
#include <wtf/MathExtras.h>
namespace WebCore {
float Path::length() const
{
PathTraversalState traversalState(PathTraversalState::Action::TotalLength);
apply([&traversalState](const PathElement& element) {
traversalState.processPathElement(element);
});
return traversalState.totalLength();
}
PathTraversalState Path::traversalStateAtLength(float length, bool& success) const
{
PathTraversalState traversalState(PathTraversalState::Action::VectorAtLength, length);
apply([&traversalState](const PathElement& element) {
traversalState.processPathElement(element);
});
success = traversalState.success();
return traversalState;
}
FloatPoint Path::pointAtLength(float length, bool& success) const
{
return traversalStateAtLength(length, success).current();
}
float Path::normalAngleAtLength(float length, bool& success) const
{
return traversalStateAtLength(length, success).normalAngle();
}
void Path::addRoundedRect(const FloatRect& rect, const FloatSize& roundingRadii, RoundedRectStrategy strategy)
{
if (rect.isEmpty())
return;
FloatSize radius(roundingRadii);
FloatSize halfSize(rect.width() / 2, rect.height() / 2);
if (radius.width() < 0)
radius.setWidth((radius.height() < 0) ? 0 : radius.height());
if (radius.height() < 0)
radius.setHeight(radius.width());
if (radius.width() > halfSize.width())
radius.setWidth(halfSize.width());
if (radius.height() > halfSize.height())
radius.setHeight(halfSize.height());
addRoundedRect(FloatRoundedRect(rect, radius, radius, radius, radius), strategy);
}
void Path::addRoundedRect(const FloatRoundedRect& r, RoundedRectStrategy strategy)
{
if (r.isEmpty())
return;
const FloatRoundedRect::Radii& radii = r.radii();
const FloatRect& rect = r.rect();
if (!r.isRenderable()) {
addRect(rect);
return;
}
if (strategy == PreferNativeRoundedRect) {
#if USE(CG)
platformAddPathForRoundedRect(rect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
return;
#endif
}
addBeziersForRoundedRect(rect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
}
void Path::addRoundedRect(const RoundedRect& r)
{
addRoundedRect(FloatRoundedRect(r));
}
void Path::addBeziersForRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius)
{
moveTo(FloatPoint(rect.x() + topLeftRadius.width(), rect.y()));
addLineTo(FloatPoint(rect.maxX() - topRightRadius.width(), rect.y()));
if (topRightRadius.width() > 0 || topRightRadius.height() > 0)
addBezierCurveTo(FloatPoint(rect.maxX() - topRightRadius.width() * circleControlPoint(), rect.y()),
FloatPoint(rect.maxX(), rect.y() + topRightRadius.height() * circleControlPoint()),
FloatPoint(rect.maxX(), rect.y() + topRightRadius.height()));
addLineTo(FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius.height()));
if (bottomRightRadius.width() > 0 || bottomRightRadius.height() > 0)
addBezierCurveTo(FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius.height() * circleControlPoint()),
FloatPoint(rect.maxX() - bottomRightRadius.width() * circleControlPoint(), rect.maxY()),
FloatPoint(rect.maxX() - bottomRightRadius.width(), rect.maxY()));
addLineTo(FloatPoint(rect.x() + bottomLeftRadius.width(), rect.maxY()));
if (bottomLeftRadius.width() > 0 || bottomLeftRadius.height() > 0)
addBezierCurveTo(FloatPoint(rect.x() + bottomLeftRadius.width() * circleControlPoint(), rect.maxY()),
FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height() * circleControlPoint()),
FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height()));
addLineTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height()));
if (topLeftRadius.width() > 0 || topLeftRadius.height() > 0)
addBezierCurveTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height() * circleControlPoint()),
FloatPoint(rect.x() + topLeftRadius.width() * circleControlPoint(), rect.y()),
FloatPoint(rect.x() + topLeftRadius.width(), rect.y()));
closeSubpath();
}
#if !USE(CG)
Path Path::polygonPathFromPoints(const Vector<FloatPoint>& points)
{
Path path;
if (points.size() < 2)
return path;
path.moveTo(points[0]);
for (size_t i = 1; i < points.size(); ++i)
path.addLineTo(points[i]);
path.closeSubpath();
return path;
}
FloatRect Path::fastBoundingRect() const
{
return boundingRect();
}
#endif
#ifndef NDEBUG
void Path::dump() const
{
TextStream stream;
stream << *this;
WTFLogAlways("%s", stream.release().utf8().data());
}
#endif
TextStream& operator<<(TextStream& stream, const Path& path)
{
bool isFirst = true;
path.apply([&stream, &isFirst](const PathElement& element) {
if (!isFirst)
stream << ", ";
isFirst = false;
switch (element.type) {
case PathElementMoveToPoint: stream << "move to " << element.points[0];
break;
case PathElementAddLineToPoint: stream << "add line to " << element.points[0];
break;
case PathElementAddQuadCurveToPoint: stream << "add quad curve to " << element.points[0] << " " << element.points[1];
break;
case PathElementAddCurveToPoint: stream << "add curve to " << element.points[0] << " " << element.points[1] << " " << element.points[2];
break;
case PathElementCloseSubpath: stream << "close subpath";
break;
}
});
return stream;
}
}