Commit d5adb85bf101d9f9aa1f93267d7ca78af82e90e0

sammy 2008-05-08T17:07:07

* When a glyph is created, check that all its contours have the proper clockwise/counterclockwise orientation. This fixes a nasty display bug with some badly encoded fonts.

diff --git a/src/FTContour.cpp b/src/FTContour.cpp
index 3f6653f..a998c76 100644
--- a/src/FTContour.cpp
+++ b/src/FTContour.cpp
@@ -120,16 +120,31 @@ FTPoint FTContour::ComputeOutsetPoint(FTPoint A, FTPoint B, FTPoint C)
 }
 
 
-void FTContour::outsetContour()
+void FTContour::SetParity(int parity)
 {
     size_t size = PointCount();
     FTPoint vOutset;
-    for(unsigned int pointIndex = 0; pointIndex < size; ++pointIndex)
+
+    if(((parity & 1) && clockwise) || !(parity & 1) && !clockwise)
     {
-        int prev = (pointIndex%size + size - 1) % size;
-        int cur = pointIndex%size;
-        int next = (pointIndex%size + 1) % size;
-        /* Build the outset shape with d = 1.0f */
+        // Contour orientation is wrong! We must reverse all points.
+        // FIXME: could it be worth writing FTVector::reverse() for this?
+        for(size_t i = 0; i < size / 2; i++)
+        {
+            FTPoint tmp = pointList[i];
+            pointList[i] = pointList[size - 1 - i];
+            pointList[size - 1 -i] = tmp;
+        }
+
+        clockwise = !clockwise;
+    }
+
+    for(size_t i = 0; i < size; i++)
+    {
+        int prev = (i + size - 1) % size;
+        int cur = i;
+        int next = (i + size + 1) % size;
+
         vOutset = ComputeOutsetPoint(Point(prev), Point(cur), Point(next));
         AddOutsetPoint(vOutset);
     }
@@ -151,7 +166,7 @@ FTContour::FTContour(FT_Vector* contour, char* tags, unsigned int n)
         cur = next;
         next = FTPoint(contour[(i + 1) % n]);
         olddir = dir;
-        dir = atan2((next - cur).Y(), (next - cur).Y());
+        dir = atan2((next - cur).Y(), (next - cur).X());
 
         // Compute our path's new direction.
         double t = dir - olddir;
@@ -196,9 +211,6 @@ FTContour::FTContour(FT_Vector* contour, char* tags, unsigned int n)
     // If final angle is positive (+2PI), it's an anti-clockwise contour,
     // otherwise (-2PI) it's clockwise.
     clockwise = (angle < 0.0);
-
-    // Create (or not) front outset and/or back outset.
-    outsetContour();
 }
 
 
diff --git a/src/FTContour.h b/src/FTContour.h
index 36b16df..d1a3501 100644
--- a/src/FTContour.h
+++ b/src/FTContour.h
@@ -124,10 +124,14 @@ class FTContour
         size_t BackPointCount() const { return backPointList.size(); }
 
         /**
-         * Create the front/back outset contour
+         * Make sure the glyph has the proper parity and create the front/back
+         * outset contour.
          *
-         * @param outset The outset distance
+         * @param parity  The contour's parity within the glyph.
          */
+        void SetParity(int parity);
+
+        // FIXME: this should probably go away.
         void buildFrontOutset(float outset);
         void buildBackOutset(float outset);
 
@@ -170,11 +174,6 @@ class FTContour
         inline void evaluateCubicCurve(FTPoint, FTPoint, FTPoint, FTPoint);
 
         /**
-         * Create the list point of the outset contour.
-         */
-        inline void outsetContour();
-
-        /**
          * Compute the vector norm
          */
         inline FTGL_DOUBLE NormVector(const FTPoint &v);
diff --git a/src/FTVectoriser.cpp b/src/FTVectoriser.cpp
index 467a75e..db1d991 100644
--- a/src/FTVectoriser.cpp
+++ b/src/FTVectoriser.cpp
@@ -162,20 +162,76 @@ void FTVectoriser::ProcessContours()
 
     contourList = new FTContour*[ftContourCount];
 
-    for(short contourIndex = 0; contourIndex < ftContourCount; ++contourIndex)
+    for(int i = 0; i < ftContourCount; ++i)
     {
         FT_Vector* pointList = &outline.points[startIndex];
         char* tagList = &outline.tags[startIndex];
 
-        endIndex = outline.contours[contourIndex];
+        endIndex = outline.contours[i];
         contourLength =  (endIndex - startIndex) + 1;
 
         FTContour* contour = new FTContour(pointList, tagList, contourLength);
 
-        contourList[contourIndex] = contour;
+        contourList[i] = contour;
 
         startIndex = endIndex + 1;
     }
+
+    // Compute each contour's parity. FIXME: see if FT_Outline_Get_Orientation
+    // can do it for us.
+    for(int i = 0; i < ftContourCount; i++)
+    {
+        FTContour *c1 = contourList[i];
+
+        // 1. Find the leftmost point.
+        FTPoint leftmost(65536.0, 0.0);
+
+        for(size_t n = 0; n < c1->PointCount(); n++)
+        {
+            FTPoint p = c1->Point(n);
+            if(p.X() < leftmost.X())
+            {
+                leftmost = p;
+            }
+        }
+
+        // 2. Count how many other contours we cross when going further to
+        // the left.
+        int parity = 0;
+
+        for(int j = 0; j < ftContourCount; j++)
+        {
+            if(j == i)
+            {
+                continue;
+            }
+
+            FTContour *c2 = contourList[j];
+
+            for(size_t n = 0; n < c2->PointCount(); n++)
+            {
+                FTPoint p1 = c2->Point(n);
+                FTPoint p2 = c2->Point((n + 1) % c2->PointCount());
+
+                if((p1.Y() < leftmost.Y() && p2.Y() < leftmost.Y())
+                    || (p1.Y() >= leftmost.Y() && p2.Y() >= leftmost.Y()))
+                {
+                    continue;
+                }
+
+                float K = (p2.Y() - leftmost.Y()) / (p1.Y() - leftmost.Y());
+                float K2 = (p2.X() - p1.X()) / (K - 1.);
+
+                if(leftmost.X() > p1.X() - K2)
+                {
+                    parity++;
+                }
+            }
+        }
+
+        // 3. Make sure the glyph has the proper parity.
+        c1->SetParity(parity);
+    }
 }