#include "config.h"
#include "Path.h"
#include "AffineTransform.h"
#include "FloatRect.h"
#include "GraphicsContext.h"
#include "NotImplemented.h"
#include "PainterOpenVG.h"
#include "PlatformPathOpenVG.h"
#include "PlatformString.h"
#include "StrokeStyleApplier.h"
#include "VGUtils.h"
#include <openvg.h>
#include <wtf/MathExtras.h>
#define WEBKIT_VG_PATH_CAPABILITIES VG_PATH_CAPABILITY_ALL
#define FUZZY_COMPARE(number, reference, delta) \
(number >= (reference - delta) && number <= (reference + delta))
namespace WebCore {
PlatformPathOpenVG::PlatformPathOpenVG()
: SharedResourceOpenVG()
{
createPath();
}
PlatformPathOpenVG::PlatformPathOpenVG(const PlatformPathOpenVG& other)
: SharedResourceOpenVG()
, m_currentPoint(other.m_currentPoint)
, m_subpathStartPoint(other.m_subpathStartPoint)
{
createPath();
vgAppendPath(m_vgPath, other.m_vgPath);
ASSERT_VG_NO_ERROR();
}
PlatformPathOpenVG& PlatformPathOpenVG::operator=(const PlatformPathOpenVG& other)
{
if (&other != this) {
clear();
vgAppendPath(m_vgPath, other.m_vgPath);
ASSERT_VG_NO_ERROR();
}
return *this;
}
PlatformPathOpenVG::~PlatformPathOpenVG()
{
makeCompatibleContextCurrent();
vgDestroyPath(m_vgPath);
ASSERT_VG_NO_ERROR();
}
void PlatformPathOpenVG::clear()
{
makeCompatibleContextCurrent();
vgClearPath(m_vgPath, WEBKIT_VG_PATH_CAPABILITIES);
ASSERT_VG_NO_ERROR();
m_subpathStartPoint.setX(0);
m_subpathStartPoint.setY(0);
m_currentPoint = m_subpathStartPoint;
}
void PlatformPathOpenVG::createPath()
{
makeSharedContextCurrent();
m_vgPath = vgCreatePath(
VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
1.0 , 0.0 ,
0 ,
0 ,
WEBKIT_VG_PATH_CAPABILITIES);
ASSERT_VG_NO_ERROR();
}
Path::Path()
{
m_path = new PlatformPathOpenVG();
}
Path::~Path()
{
delete m_path;
}
Path::Path(const Path& other)
{
m_path = new PlatformPathOpenVG(*(other.m_path));
}
Path& Path::operator=(const Path& other)
{
*m_path = *(other.m_path);
return *this;
}
FloatPoint Path::currentPoint() const
{
return m_currentPoint;
}
bool Path::contains(const FloatPoint& point, WindRule rule) const
{
notImplemented();
return boundingRect().contains(point);
}
bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const
{
notImplemented();
return (const_cast<Path*>(this))->strokeBoundingRect().contains(point);
}
void Path::translate(const FloatSize& size)
{
AffineTransform transformation;
transformation.translate(size.width(), size.height());
transform(transformation);
}
FloatRect Path::boundingRect() const
{
VGfloat minX;
VGfloat minY;
VGfloat width;
VGfloat height;
m_path->makeCompatibleContextCurrent();
vgPathBounds(m_path->vgPath(), &minX, &minY, &width, &height);
ASSERT_VG_NO_ERROR();
return FloatRect(FloatPoint(minX, minY), FloatSize(width, height));
}
FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) const
{
notImplemented();
return boundingRect();
}
void Path::moveTo(const FloatPoint& point)
{
static const VGubyte pathSegments[] = { VG_MOVE_TO_ABS };
const VGfloat pathData[] = { point.x(), point.y() };
m_path->makeCompatibleContextCurrent();
vgAppendPathData(m_path->vgPath(), 1, pathSegments, pathData);
ASSERT_VG_NO_ERROR();
m_path->m_currentPoint = m_path->m_subpathStartPoint = point;
}
void Path::addLineTo(const FloatPoint& point)
{
static const VGubyte pathSegments[] = { VG_LINE_TO_ABS };
const VGfloat pathData[] = { point.x(), point.y() };
m_path->makeCompatibleContextCurrent();
vgAppendPathData(m_path->vgPath(), 1, pathSegments, pathData);
ASSERT_VG_NO_ERROR();
m_path->m_currentPoint = point;
}
void Path::addQuadCurveTo(const FloatPoint& controlPoint, const FloatPoint& endPoint)
{
static const VGubyte pathSegments[] = { VG_QUAD_TO_ABS };
const VGfloat pathData[] = { controlPoint.x(), controlPoint.y(), endPoint.x(), endPoint.y() };
m_path->makeCompatibleContextCurrent();
vgAppendPathData(m_path->vgPath(), 1, pathSegments, pathData);
ASSERT_VG_NO_ERROR();
m_path->m_currentPoint = endPoint;
}
void Path::addBezierCurveTo(const FloatPoint& controlPoint1, const FloatPoint& controlPoint2, const FloatPoint& endPoint)
{
static const VGubyte pathSegments[] = { VG_CUBIC_TO_ABS };
const VGfloat pathData[] = { controlPoint1.x(), controlPoint1.y(), controlPoint2.x(), controlPoint2.y(), endPoint.x(), endPoint.y() };
m_path->makeCompatibleContextCurrent();
vgAppendPathData(m_path->vgPath(), 1, pathSegments, pathData);
ASSERT_VG_NO_ERROR();
m_path->m_currentPoint = endPoint;
}
void Path::addArcTo(const FloatPoint& point1, const FloatPoint& point2, float radius)
{
const FloatPoint& point0 = m_path->m_currentPoint;
if (!radius || point0 == point1 || point1 == point2) {
addLineTo(point1);
return;
}
FloatSize v01 = point0 - point1;
FloatSize v21 = point2 - point1;
double cross = v01.width() * v21.height() - v01.height() * v21.width();
if (fabs(cross) < 1E-10) {
addLineTo(point1);
return;
}
double d01 = hypot(v01.width(), v01.height());
double d21 = hypot(v21.width(), v21.height());
double angle = (piDouble - fabs(asin(cross / (d01 * d21)))) * 0.5;
double span = radius * tan(angle);
double rate = span / d01;
FloatPoint startPoint = FloatPoint(point1.x() + v01.width() * rate,
point1.y() + v01.height() * rate);
rate = span / d21;
FloatPoint endPoint = FloatPoint(point1.x() + v21.width() * rate,
point1.y() + v21.height() * rate);
const bool anticlockwise = cross < 0;
const VGubyte segmentCommand = anticlockwise ? VG_SCCWARC_TO_ABS : VG_SCWARC_TO_ABS;
const VGubyte pathSegments[] = {
VG_LINE_TO_ABS,
segmentCommand
};
const VGfloat pathData[] = {
startPoint.x(), startPoint.y(),
radius, radius, 0, endPoint.x(), endPoint.y()
};
m_path->makeCompatibleContextCurrent();
vgAppendPathData(m_path->vgPath(), 2, pathSegments, pathData);
ASSERT_VG_NO_ERROR();
m_path->m_currentPoint = endPoint;
}
void Path::closeSubpath()
{
static const VGubyte pathSegments[] = { VG_CLOSE_PATH };
static const VGfloat* pathData = reinterpret_cast<VGfloat*>(sizeof(VGfloat));
m_path->makeCompatibleContextCurrent();
vgAppendPathData(m_path->vgPath(), 1, pathSegments, pathData);
ASSERT_VG_NO_ERROR();
m_path->m_currentPoint = m_path->m_subpathStartPoint;
}
void Path::addArc(const FloatPoint& center, float radius, float startAngle, float endAngle, bool anticlockwise)
{
if (!isfinite(radius) || !isfinite(startAngle) || !isfinite(endAngle))
return;
startAngle = fmod((2.0 * piDouble) - startAngle, 2.0 * piDouble);
endAngle = fmod((2.0 * piDouble) - endAngle, 2.0 * piDouble);
if (endAngle <= startAngle)
endAngle += 2.0 * piDouble;
const VGfloat angleDelta = anticlockwise
? (endAngle - startAngle)
: (startAngle - endAngle + (2.0 * piDouble));
const VGfloat startX = radius * cos(startAngle) + center.x();
const VGfloat startY = -radius * sin(startAngle) + center.y();
const VGfloat endX = radius * cos(endAngle) + center.x();
const VGfloat endY = -radius * sin(endAngle) + center.y();
const bool largeArc = (angleDelta > piDouble);
const VGubyte segmentCommand = !anticlockwise
? (largeArc ? VG_LCCWARC_TO_ABS : VG_SCCWARC_TO_ABS)
: (largeArc ? VG_LCWARC_TO_ABS : VG_SCWARC_TO_ABS);
const VGubyte pathSegments[] = {
hasCurrentPoint() ? VG_LINE_TO_ABS : VG_MOVE_TO_ABS,
segmentCommand
};
const VGfloat pathData[] = {
startX, startY,
radius, radius, 0, endX, endY
};
m_path->makeCompatibleContextCurrent();
vgAppendPathData(m_path->vgPath(), 2, pathSegments, pathData);
ASSERT_VG_NO_ERROR();
m_path->m_currentPoint.setX(endX);
m_path->m_currentPoint.setY(endY);
}
void Path::addRect(const FloatRect& rect)
{
static const VGubyte pathSegments[] = {
VG_MOVE_TO_ABS,
VG_HLINE_TO_REL,
VG_VLINE_TO_REL,
VG_HLINE_TO_REL,
VG_CLOSE_PATH
};
const VGfloat pathData[] = {
rect.x(), rect.y(),
rect.width(),
rect.height(),
-rect.width()
};
m_path->makeCompatibleContextCurrent();
vgAppendPathData(m_path->vgPath(), 5, pathSegments, pathData);
ASSERT_VG_NO_ERROR();
m_path->m_currentPoint = m_path->m_subpathStartPoint = rect.location();
}
void Path::addEllipse(const FloatRect& rect)
{
static const VGubyte pathSegments[] = {
VG_MOVE_TO_ABS,
VG_SCCWARC_TO_REL,
VG_SCCWARC_TO_REL,
VG_CLOSE_PATH
};
const VGfloat pathData[] = {
rect.x() + rect.width() / 2.0, rect.y(),
rect.width() / 2.0, rect.height() / 2.0, 0, 0, rect.height(),
rect.width() / 2.0, rect.height() / 2.0, 0, 0, -rect.height()
};
m_path->makeCompatibleContextCurrent();
vgAppendPathData(m_path->vgPath(), 4, pathSegments, pathData);
ASSERT_VG_NO_ERROR();
}
void Path::clear()
{
m_path->clear();
}
bool Path::isEmpty() const
{
m_path->makeCompatibleContextCurrent();
return !vgGetParameteri(m_path->vgPath(), VG_PATH_NUM_SEGMENTS);
}
bool Path::hasCurrentPoint() const
{
m_path->makeCompatibleContextCurrent();
return vgGetParameteri(m_path->vgPath(), VG_PATH_NUM_SEGMENTS) > 0;
}
void Path::apply(void* info, PathApplierFunction function) const
{
notImplemented();
}
void Path::transform(const AffineTransform& transformation)
{
PlatformPathOpenVG* dst = new PlatformPathOpenVG();
PainterOpenVG::transformPath(dst->vgPath(), m_path->vgPath(), transformation);
delete m_path;
m_path = dst;
m_path->m_currentPoint = transformation.mapPoint(m_path->m_currentPoint);
m_path->m_subpathStartPoint = transformation.mapPoint(m_path->m_subpathStartPoint);
}
float Path::length() const
{
m_path->makeCompatibleContextCurrent();
VGfloat length = vgPathLength(m_path->vgPath(), 0, vgGetParameteri(m_path->vgPath(), VG_PATH_NUM_SEGMENTS));
ASSERT_VG_NO_ERROR();
return length;
}
FloatPoint Path::pointAtLength(float length, bool& ok) const
{
VGfloat x = 0, y = 0;
m_path->makeCompatibleContextCurrent();
vgPointAlongPath(m_path->vgPath(), 0, vgGetParameteri(m_path->vgPath(), VG_PATH_NUM_SEGMENTS),
length, &x, &y, 0, 0);
ok = (vgGetError() == VG_NO_ERROR);
return FloatPoint(x, y);
}
float Path::normalAngleAtLength(float length, bool& ok) const
{
VGfloat tangentX, tangentY;
m_path->makeCompatibleContextCurrent();
vgPointAlongPath(m_path->vgPath(), 0, vgGetParameteri(m_path->vgPath(), VG_PATH_NUM_SEGMENTS),
length, 0, 0, &tangentX, &tangentY);
ok = (vgGetError() == VG_NO_ERROR);
return atan2f(tangentY, tangentX) * 180.0 / piFloat; }
}