summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/graphics/openvg/PathOpenVG.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/platform/graphics/openvg/PathOpenVG.cpp')
-rw-r--r--Source/WebCore/platform/graphics/openvg/PathOpenVG.cpp497
1 files changed, 497 insertions, 0 deletions
diff --git a/Source/WebCore/platform/graphics/openvg/PathOpenVG.cpp b/Source/WebCore/platform/graphics/openvg/PathOpenVG.cpp
new file mode 100644
index 0000000..39a4b06
--- /dev/null
+++ b/Source/WebCore/platform/graphics/openvg/PathOpenVG.cpp
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#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();
+ // makeCompatibleContextCurrent() is called by createPath(), so not necessary here.
+ vgAppendPath(m_vgPath, other.m_vgPath);
+ ASSERT_VG_NO_ERROR();
+}
+
+PlatformPathOpenVG& PlatformPathOpenVG::operator=(const PlatformPathOpenVG& other)
+{
+ if (&other != this) {
+ clear();
+ // makeCompatibleContextCurrent() is called by clear(), so not necessary here.
+ 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 /* scale */, 0.0 /* bias */,
+ 0 /* expected number of segments */,
+ 0 /* expected number of total coordinates */,
+ 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
+{
+ // FIXME: is this the way to return the current point of the subpath?
+ return m_currentPoint;
+}
+
+
+bool Path::contains(const FloatPoint& point, WindRule rule) const
+{
+ notImplemented();
+
+ // OpenVG has no path-contains function, so for now we approximate by
+ // using the bounding rect of the path.
+ return boundingRect().contains(point);
+}
+
+bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const
+{
+ notImplemented();
+
+ // OpenVG has no path-contains function, so for now we approximate by
+ // using the stroke bounding rect of the path.
+ 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)
+{
+ notImplemented();
+
+ // vgPathBounds() ignores stroke parameters, and we don't currently have
+ // an approximation that takes stroke parameters into account.
+ 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)
+{
+ // See http://philip.html5.org/tests/canvas/suite/tests/spec.html#arcto.
+
+ const FloatPoint& point0 = m_path->m_currentPoint;
+ if (!radius || point0 == point1 || point1 == point2) {
+ addLineTo(point1);
+ return;
+ }
+
+ FloatSize v01 = point0 - point1;
+ FloatSize v21 = point2 - point1;
+
+ // sin(A - B) = sin(A) * cos(B) - sin(B) * cos(A)
+ double cross = v01.width() * v21.height() - v01.height() * v21.width();
+
+ if (fabs(cross) < 1E-10) {
+ // on one line
+ 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);
+
+ // Fa: large arc flag, makes the difference between SCWARC_TO and LCWARC_TO
+ // respectively SCCWARC_TO and LCCWARC_TO arcs. We always use small
+ // arcs for arcTo(), as the arc is defined as the "shortest arc" of the
+ // circle specified in HTML 5.
+
+ // Fs: sweep flag, specifying whether the arc is drawn in increasing (true)
+ // or decreasing (0) direction.
+ const bool anticlockwise = cross < 0;
+
+ // Translate the large arc and sweep flags into an OpenVG segment command.
+ 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 };
+ // pathData must not be 0, but certain compilers also don't create
+ // zero-size arrays. So let's use a random aligned value (sizeof(VGfloat)),
+ // it won't be accessed anyways as VG_CLOSE_PATH doesn't take coordinates.
+ 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)
+{
+ // The OpenVG spec says nothing about inf as radius or start/end angle.
+ // WebKit seems to pass those (e.g. https://bugs.webkit.org/show_bug.cgi?id=16449),
+ // so abort instead of risking undefined behavior.
+ if (!isfinite(radius) || !isfinite(startAngle) || !isfinite(endAngle))
+ return;
+
+ // For some reason, the HTML 5 spec defines the angle as going clockwise
+ // from the positive X axis instead of going standard anticlockwise.
+ // So let's make it a proper angle in order to keep sanity.
+ startAngle = fmod((2.0 * piDouble) - startAngle, 2.0 * piDouble);
+ endAngle = fmod((2.0 * piDouble) - endAngle, 2.0 * piDouble);
+
+ // Make it so that endAngle > startAngle. fmod() above takes care of
+ // keeping the difference below 360 degrees.
+ if (endAngle <= startAngle)
+ endAngle += 2.0 * piDouble;
+
+ const VGfloat angleDelta = anticlockwise
+ ? (endAngle - startAngle)
+ : (startAngle - endAngle + (2.0 * piDouble));
+
+ // OpenVG uses endpoint parameterization while this method receives its
+ // values in center parameterization. It lacks an ellipse rotation
+ // parameter so we use 0 for that, and also the radius is only a single
+ // value which makes for rh == rv. In order to convert from endpoint to
+ // center parameterization, we use the formulas from the OpenVG/SVG specs:
+
+ // (x,y) = (cos rot, -sin rot; sin rot, -cos rot) * (rh * cos angle, rv * sin angle) + (center.x, center.y)
+ // rot is 0, which simplifies this a bit:
+ // (x,y) = (1, 0; 0, -1) * (rh * cos angle, rv * sin angle) + (center.x, center.y)
+ // = (1 * rh * cos angle + 0 * rv * sin angle, 0 * rh * cos angle + -1 * rv * sin angle) + (center.x, center.y)
+ // = (rh * cos angle, -rv * sin angle) + (center.x, center.y)
+ // (Set angle = {startAngle, endAngle} to retrieve the respective endpoints.)
+
+ 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();
+
+ // Fa: large arc flag, makes the difference between SCWARC_TO and LCWARC_TO
+ // respectively SCCWARC_TO and LCCWARC_TO arcs.
+ const bool largeArc = (angleDelta > piDouble);
+
+ // Fs: sweep flag, specifying whether the arc is drawn in increasing (true)
+ // or decreasing (0) direction. No need to calculate this value, as it
+ // we already get it passed as a parameter (Fs == !anticlockwise).
+
+ // Translate the large arc and sweep flags into an OpenVG segment command.
+ // As OpenVG thinks of everything upside down, we need to reverse the
+ // anticlockwise parameter in order to get the specified rotation.
+ const VGubyte segmentCommand = !anticlockwise
+ ? (largeArc ? VG_LCCWARC_TO_ABS : VG_SCCWARC_TO_ABS)
+ : (largeArc ? VG_LCWARC_TO_ABS : VG_SCWARC_TO_ABS);
+
+ // So now, we've got all the parameters in endpoint parameterization format
+ // as OpenVG requires it. Which means we can just pass it like this.
+ 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
+{
+ // OpenVG provides no means to retrieve path segment information.
+ // This is *very* unfortunate, we might need to store the segments in
+ // memory if we want to implement this function properly.
+ // See http://www.khronos.org/message_boards/viewtopic.php?f=6&t=1887
+ notImplemented();
+}
+
+void Path::transform(const AffineTransform& transformation)
+{
+ PlatformPathOpenVG* dst = new PlatformPathOpenVG();
+ // dst->makeCompatibleContextCurrent() is called by the platform path
+ // constructor, therefore not necessary to call it again here.
+ 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);
+}
+
+
+// Path::length(), Path::pointAtLength() and Path::normalAngleAtLength() are
+// reimplemented here instead of in Path.cpp, because OpenVG has its own
+// functions and Path::apply() doesn't really work as long as we rely on VGPath
+// as primary path storage.
+
+float Path::length()
+{
+ 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)
+{
+ 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)
+{
+ 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; // convert to degrees
+}
+
+}