Bug 376927 - add path flattening API to thebes. r=vlad
authortor@cs.brown.edu
Mon, 16 Apr 2007 13:18:32 -0700
changeset 562 8b101a22b0f6da5a45d69915c831b5bef3bd7bf4
parent 561 424e88f5959e8e57479e374fad581cd068519043
child 563 6eb9346e61a01328d732d04bd6b829be6c8c5431
push idunknown
push userunknown
push dateunknown
reviewersvlad
bugs376927
milestone1.9a4pre
Bug 376927 - add path flattening API to thebes. r=vlad
gfx/thebes/public/Makefile.in
gfx/thebes/public/gfxContext.h
gfx/thebes/public/gfxPath.h
gfx/thebes/src/Makefile.in
gfx/thebes/src/gfxContext.cpp
gfx/thebes/src/gfxPath.cpp
--- a/gfx/thebes/public/Makefile.in
+++ b/gfx/thebes/public/Makefile.in
@@ -12,16 +12,17 @@ REQUIRES	= cairo
 
 EXPORTS		= 	gfxASurface.h \
 			gfxAlphaRecovery.h \
 			gfxColor.h \
 			gfxContext.h \
 			gfxFont.h \
 			gfxImageSurface.h \
 			gfxMatrix.h \
+			gfxPath.h \
 			gfxPattern.h \
 			gfxPlatform.h \
 			gfxPoint.h \
 			gfxRect.h \
 			gfxSkipChars.h \
 			gfxTypes.h \
 			gfxTextRunCache.h \
 			$(NULL)
--- a/gfx/thebes/public/gfxContext.h
+++ b/gfx/thebes/public/gfxContext.h
@@ -42,16 +42,17 @@
 #include "gfxTypes.h"
 
 #include "gfxASurface.h"
 #include "gfxColor.h"
 #include "gfxPoint.h"
 #include "gfxRect.h"
 #include "gfxMatrix.h"
 #include "gfxPattern.h"
+#include "gfxPath.h"
 
 typedef struct _cairo cairo_t;
 
 /**
  * This is the main class for doing actual drawing. It is initialized using
  * a surface and can be drawn on. It manages various state information like
  * a current transformation matrix (CTM), a current path, current color,
  * etc.
@@ -511,14 +512,19 @@ public:
     PRBool PointInStroke(const gfxPoint& pt);
 
     /**
      ** Extents - returns user space extent of current path
      **/
     gfxRect GetUserFillExtent();
     gfxRect GetUserStrokeExtent();
 
+    /**
+     ** Obtaining a "flattened" path - path converted to all line segments
+     **/
+    already_AddRefed<gfxFlattenedPath> GetFlattenedPath();
+
 private:
     cairo_t *mCairo;
     nsRefPtr<gfxASurface> mSurface;
 };
 
 #endif /* GFX_CONTEXT_H */
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/public/gfxPath.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is IBM Corporation code.
+ *
+ * The Initial Developer of the Original Code is IBM Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef GFX_PATH_H
+#define GFX_PATH_H
+
+#include "gfxTypes.h"
+
+struct gfxPoint;
+typedef struct cairo_path cairo_path_t;
+
+class THEBES_API gfxFlattenedPath {
+    THEBES_INLINE_DECL_REFCOUNTING(gfxPath)
+
+public:
+    gfxFlattenedPath(cairo_path_t *aPath);
+    ~gfxFlattenedPath();
+
+    /**
+     * Returns calculated total length of path
+     */
+    gfxFloat GetLength();
+
+    /**
+     * Returns a point a certain distance along the path.  Return is
+     * first or last point of the path if the requested length offset
+     * is outside the range for the path.
+     * @param aOffset offset inpath parameter space (x=length, y=normal offset)
+     * @param aAngle optional - output tangent
+     */
+    gfxPoint FindPoint(gfxPoint aOffset,
+                       gfxFloat *aAngle = nsnull);
+
+protected:
+    cairo_path_t *mPath;
+};
+
+#endif
--- a/gfx/thebes/src/Makefile.in
+++ b/gfx/thebes/src/Makefile.in
@@ -21,19 +21,20 @@ REQUIRES = \
 	$(NULL)
 
 CPPSRCS	= \
 	gfxASurface.cpp \
 	gfxAlphaRecovery.cpp \
 	gfxContext.cpp \
 	gfxImageSurface.cpp \
 	gfxFont.cpp \
+	gfxFontMissingGlyphs.cpp \
 	gfxFontTest.cpp \
 	gfxMatrix.cpp \
-	gfxFontMissingGlyphs.cpp \
+	gfxPath.cpp \
 	gfxPattern.cpp \
 	gfxPlatform.cpp \
 	gfxRect.cpp \
 	gfxSkipChars.cpp \
 	gfxTextRunCache.cpp \
 	$(NULL)
 
 ifdef MOZ_TREE_CAIRO
--- a/gfx/thebes/src/gfxContext.cpp
+++ b/gfx/thebes/src/gfxContext.cpp
@@ -699,8 +699,17 @@ gfxContext::GetUserFillExtent()
 
 gfxRect
 gfxContext::GetUserStrokeExtent()
 {
     double xmin, ymin, xmax, ymax;
     cairo_stroke_extents(mCairo, &xmin, &ymin, &xmax, &ymax);
     return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
 }
+
+already_AddRefed<gfxFlattenedPath>
+gfxContext::GetFlattenedPath()
+{
+    gfxFlattenedPath *path =
+        new gfxFlattenedPath(cairo_copy_path_flat(mCairo));
+    NS_IF_ADDREF(path);
+    return path;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/src/gfxPath.cpp
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is IBM Corporation code.
+ *
+ * The Initial Developer of the Original Code is IBM Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "gfxPath.h"
+#include "gfxPoint.h"
+
+#include "cairo.h"
+
+gfxFlattenedPath::gfxFlattenedPath(cairo_path_t *aPath) : mPath(aPath)
+{
+}
+
+gfxFlattenedPath::~gfxFlattenedPath()
+{
+    cairo_path_destroy(mPath);
+}
+
+static gfxFloat
+CalcSubLengthAndAdvance(cairo_path_data_t *aData,
+                        gfxPoint &aPathStart, gfxPoint &aCurrent)
+{
+    float sublength = 0;
+
+    switch (aData->header.type) {
+        case CAIRO_PATH_MOVE_TO:
+        {
+            aCurrent = aPathStart = gfxPoint(aData[1].point.x,
+                                             aData[1].point.y);
+            break;
+        }
+        case CAIRO_PATH_LINE_TO:
+        {
+            gfxPoint diff =
+                gfxPoint(aData[1].point.x, aData[1].point.y) - aCurrent;
+            sublength = sqrt(diff.x * diff.x + diff.y * diff.y);
+            aCurrent = gfxPoint(aData[1].point.x, aData[1].point.y);
+            break;
+        }
+        case CAIRO_PATH_CURVE_TO:
+            /* should never happen with a flattened path */
+            NS_WARNING("curve_to in flattened path");
+            break;
+        case CAIRO_PATH_CLOSE_PATH:
+        {
+            gfxPoint diff = aPathStart - aCurrent;
+            sublength = sqrt(diff.x * diff.x + diff.y * diff.y);
+            aCurrent = aPathStart;
+            break;
+        }
+    }
+    return sublength;
+}
+
+gfxFloat
+gfxFlattenedPath::GetLength()
+{
+    gfxPoint start(0, 0);     // start of current subpath
+    gfxPoint current(0, 0);   // current point
+    gfxFloat length = 0;      // current summed length
+
+    for (PRInt32 i = 0;
+         i < mPath->num_data;
+         i += mPath->data[i].header.length) {
+        length += CalcSubLengthAndAdvance(&mPath->data[i], start, current);
+    }
+    return length;
+}
+
+gfxPoint
+gfxFlattenedPath::FindPoint(gfxPoint aOffset, gfxFloat *aAngle)
+{
+    gfxPoint start(0, 0);     // start of current subpath
+    gfxPoint current(0, 0);   // current point
+    gfxFloat length = 0;      // current summed length
+
+    for (PRInt32 i = 0;
+         i < mPath->num_data;
+         i += mPath->data[i].header.length) {
+        gfxPoint prev = current;
+
+        gfxFloat sublength = CalcSubLengthAndAdvance(&mPath->data[i],
+                                                     start, current);
+
+        gfxPoint diff = current - prev;
+        if (aAngle)
+            *aAngle = atan2(diff.y, diff.x);
+
+        if (sublength != 0 && length + sublength >= aOffset.x) {
+            gfxFloat ratio = (aOffset.x - length) / sublength;
+            gfxFloat normalization =
+                1.0 / sqrt(diff.x * diff.x + diff.y * diff.y);
+
+            return prev * (1.0f - ratio) + current * ratio +
+                gfxPoint(-diff.y, diff.x) * aOffset.y * normalization;
+        }
+        length += sublength;
+    }
+
+    // requested offset is past the end of the path - return last point
+    return current;
+}