[1/2] opengl32: Implement wglUseFontOutlines curve smoothing

Sam Edwards cfsworks at gmail.com
Thu Nov 1 19:50:42 CDT 2012


Please review this one carefully; this makes the wglUseFontOutlines 
function a bit more complex, and may violate style I'm not aware of (for 
example, I define a struct in the middle of the wgl.c file).
-------------- next part --------------
>From 117aa47535024ebc11b7d40b7606dd30a024dcbb Mon Sep 17 00:00:00 2001
From: Sam Edwards <CFSworks at gmail.com>
Date: Thu, 1 Nov 2012 16:39:47 -0600
Subject: opengl32: Implement wglUseFontOutlines curve smoothing

---
 dlls/opengl32/wgl.c |  222 +++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 173 insertions(+), 49 deletions(-)

diff --git a/dlls/opengl32/wgl.c b/dlls/opengl32/wgl.c
index b6efdba..b5c61ce 100644
--- a/dlls/opengl32/wgl.c
+++ b/dlls/opengl32/wgl.c
@@ -1311,6 +1311,74 @@ static void WINAPI tess_callback_end(void)
     funcs->gl.p_glEnd();
 }
 
+typedef struct _bezier_vector {
+    GLdouble x;
+    GLdouble y;
+} bezier_vector;
+
+static double bezier_deviation_squared(const bezier_vector *p)
+{
+    bezier_vector deviation;
+    bezier_vector vertex;
+    bezier_vector base;
+    double base_length;
+    double dot;
+
+    vertex.x = (p[0].x + p[1].x*2 + p[2].x)/4 - p[0].x;
+    vertex.y = (p[0].y + p[1].y*2 + p[2].y)/4 - p[0].y;
+
+    base.x = p[2].x - p[0].x;
+    base.y = p[2].y - p[0].y;
+
+    base_length = sqrt(base.x*base.x + base.y*base.y);
+    base.x /= base_length;
+    base.y /= base_length;
+
+    dot = base.x*vertex.x + base.y*vertex.y;
+    dot = min(max(dot, 0.0), base_length);
+    base.x *= dot;
+    base.y *= dot;
+
+    deviation.x = vertex.x-base.x;
+    deviation.y = vertex.y-base.y;
+
+    return deviation.x*deviation.x + deviation.y*deviation.y;
+}
+
+static int bezier_approximate(const bezier_vector *p, bezier_vector *points, FLOAT deviation)
+{
+    bezier_vector first_curve[3];
+    bezier_vector second_curve[3];
+    bezier_vector vertex;
+    int total_vertices;
+
+    if(bezier_deviation_squared(p) <= deviation*deviation)
+    {
+        if(points)
+            *points = p[2];
+        return 1;
+    }
+
+    vertex.x = (p[0].x + p[1].x*2 + p[2].x)/4;
+    vertex.y = (p[0].y + p[1].y*2 + p[2].y)/4;
+
+    first_curve[0] = p[0];
+    first_curve[1].x = (p[0].x + p[1].x)/2;
+    first_curve[1].y = (p[0].y + p[1].y)/2;
+    first_curve[2] = vertex;
+
+    second_curve[0] = vertex;
+    second_curve[1].x = (p[2].x + p[1].x)/2;
+    second_curve[1].y = (p[2].y + p[1].y)/2;
+    second_curve[2] = p[2];
+
+    total_vertices = bezier_approximate(first_curve, points, deviation);
+    if(points)
+        points += total_vertices;
+    total_vertices += bezier_approximate(second_curve, points, deviation);
+    return total_vertices;
+}
+
 /***********************************************************************
  *		wglUseFontOutlines_common
  */
@@ -1336,6 +1404,9 @@ static BOOL wglUseFontOutlines_common(HDC hdc,
     TRACE("(%p, %d, %d, %d, %f, %f, %d, %p, %s)\n", hdc, first, count,
           listBase, deviation, extrusion, format, lpgmf, unicode ? "W" : "A");
 
+    if(deviation <= 0.0)
+        deviation = 1.0/em_size;
+
     if (!load_libglu())
     {
         ERR("glu32 is required for this function but isn't available\n");
@@ -1364,7 +1435,8 @@ static BOOL wglUseFontOutlines_common(HDC hdc,
         BYTE *buf;
         TTPOLYGONHEADER *pph;
         TTPOLYCURVE *ppc;
-        GLdouble *vertices;
+        GLdouble *vertices = NULL;
+        int vertex_total = -1;
 
         if(unicode)
             needed = GetGlyphOutlineW(hdc, glyph, GGO_NATIVE, &gm, 0, NULL, &identity);
@@ -1375,7 +1447,6 @@ static BOOL wglUseFontOutlines_common(HDC hdc,
             goto error;
 
         buf = HeapAlloc(GetProcessHeap(), 0, needed);
-        vertices = HeapAlloc(GetProcessHeap(), 0, needed / sizeof(POINTFX) * 3 * sizeof(GLdouble));
 
         if(unicode)
             GetGlyphOutlineW(hdc, glyph, GGO_NATIVE, &gm, needed, buf, &identity);
@@ -1398,63 +1469,116 @@ static BOOL wglUseFontOutlines_common(HDC hdc,
             lpgmf++;
         }
 
-	funcs->gl.p_glNewList(listBase++, GL_COMPILE);
+        funcs->gl.p_glNewList(listBase++, GL_COMPILE);
         funcs->gl.p_glFrontFace(GL_CW);
         pgluTessBeginPolygon(tess, NULL);
 
-        pph = (TTPOLYGONHEADER*)buf;
-        while((BYTE*)pph < buf + needed)
+        while(!vertices)
         {
-            TRACE("\tstart %d, %d\n", pph->pfxStart.x.value, pph->pfxStart.y.value);
+            if(vertex_total != -1)
+                vertices = HeapAlloc(GetProcessHeap(), 0, vertex_total * 3 * sizeof(GLdouble));
+            vertex_total = 0;
 
-            pgluTessBeginContour(tess);
+            pph = (TTPOLYGONHEADER*)buf;
+            while((BYTE*)pph < buf + needed)
+            {
+                GLdouble previous[3];
+                fixed_to_double(pph->pfxStart, em_size, previous);
 
-            fixed_to_double(pph->pfxStart, em_size, vertices);
-            pgluTessVertex(tess, vertices, vertices);
-            vertices += 3;
+                if(vertices)
+                    TRACE("\tstart %d, %d\n", pph->pfxStart.x.value, pph->pfxStart.y.value);
 
-            ppc = (TTPOLYCURVE*)((char*)pph + sizeof(*pph));
-            while((char*)ppc < (char*)pph + pph->cb)
-            {
-                int i;
-
-                switch(ppc->wType) {
-                case TT_PRIM_LINE:
-                    for(i = 0; i < ppc->cpfx; i++)
-                    {
-                        TRACE("\t\tline to %d, %d\n", ppc->apfx[i].x.value, ppc->apfx[i].y.value);
-                        fixed_to_double(ppc->apfx[i], em_size, vertices); 
-                        pgluTessVertex(tess, vertices, vertices);
-                        vertices += 3;
-                    }
-                    break;
-
-                case TT_PRIM_QSPLINE:
-                    for(i = 0; i < ppc->cpfx/2; i++)
-                    {
-                        /* FIXME: just connecting the control points for now */
-                        TRACE("\t\tcurve  %d,%d %d,%d\n",
-                              ppc->apfx[i * 2].x.value,     ppc->apfx[i * 3].y.value,
-                              ppc->apfx[i * 2 + 1].x.value, ppc->apfx[i * 3 + 1].y.value);
-                        fixed_to_double(ppc->apfx[i * 2], em_size, vertices); 
-                        pgluTessVertex(tess, vertices, vertices);
-                        vertices += 3;
-                        fixed_to_double(ppc->apfx[i * 2 + 1], em_size, vertices); 
-                        pgluTessVertex(tess, vertices, vertices);
-                        vertices += 3;
-                    }
-                    break;
-                default:
-                    ERR("\t\tcurve type = %d\n", ppc->wType);
-                    pgluTessEndContour(tess);
-                    goto error_in_list;
+                pgluTessBeginContour(tess);
+
+                if(vertices)
+                {
+                    fixed_to_double(pph->pfxStart, em_size, vertices);
+                    pgluTessVertex(tess, vertices, vertices);
+                    vertices += 3;
                 }
+                vertex_total++;
+
+                ppc = (TTPOLYCURVE*)((char*)pph + sizeof(*pph));
+                while((char*)ppc < (char*)pph + pph->cb)
+                {
+                    int i, j;
+                    int num;
+
+                    switch(ppc->wType) {
+                    case TT_PRIM_LINE:
+                        for(i = 0; i < ppc->cpfx; i++)
+                        {
+                            if(vertices)
+                            {
+                                TRACE("\t\tline to %d, %d\n",
+                                      ppc->apfx[i].x.value, ppc->apfx[i].y.value);
+                                fixed_to_double(ppc->apfx[i], em_size, vertices);
+                                pgluTessVertex(tess, vertices, vertices);
+                                vertices += 3;
+                            }
+                            fixed_to_double(ppc->apfx[i], em_size, previous);
+                            vertex_total++;
+                        }
+                        break;
+
+                    case TT_PRIM_QSPLINE:
+                        for(i = 0; i < ppc->cpfx-1; i++)
+                        {
+                            bezier_vector curve[3];
+                            bezier_vector *points;
+                            GLdouble curve_vertex[3];
+
+                            if(vertices)
+                                TRACE("\t\tcurve  %d,%d %d,%d\n",
+                                      ppc->apfx[i].x.value,     ppc->apfx[i].y.value,
+                                      ppc->apfx[i + 1].x.value, ppc->apfx[i + 1].y.value);
+
+                            curve[0].x = previous[0];
+                            curve[0].y = previous[1];
+                            fixed_to_double(ppc->apfx[i], em_size, curve_vertex);
+                            curve[1].x = curve_vertex[0];
+                            curve[1].y = curve_vertex[1];
+                            fixed_to_double(ppc->apfx[i + 1], em_size, curve_vertex);
+                            curve[2].x = curve_vertex[0];
+                            curve[2].y = curve_vertex[1];
+                            if(i < ppc->cpfx-2)
+                            {
+                                curve[2].x = (curve[1].x + curve[2].x)/2;
+                                curve[2].y = (curve[1].y + curve[2].y)/2;
+                            }
+                            num = bezier_approximate(curve, NULL, deviation);
+                            points = HeapAlloc(GetProcessHeap(), 0, num*sizeof(bezier_vector));
+                            num = bezier_approximate(curve, points, deviation);
+                            vertex_total += num;
+                            if(vertices)
+                            {
+                                for(j=0; j<num; j++)
+                                {
+                                    TRACE("\t\t\tvertex at %f,%f\n", points[j].x, points[j].y);
+                                    vertices[0] = points[j].x;
+                                    vertices[1] = points[j].y;
+                                    vertices[2] = 0.0;
+                                    pgluTessVertex(tess, vertices, vertices);
+                                    vertices += 3;
+                                }
+                            }
+                            HeapFree(GetProcessHeap(), 0, points);
+                            previous[0] = curve[2].x;
+                            previous[1] = curve[2].y;
+                        }
+                        break;
+                    default:
+                        ERR("\t\tcurve type = %d\n", ppc->wType);
+                        pgluTessEndContour(tess);
+                        goto error_in_list;
+                    }
 
-                ppc = (TTPOLYCURVE*)((char*)ppc + sizeof(*ppc) +
-                                     (ppc->cpfx - 1) * sizeof(POINTFX));
+                    ppc = (TTPOLYCURVE*)((char*)ppc + sizeof(*ppc) +
+                                         (ppc->cpfx - 1) * sizeof(POINTFX));
+                }
+                pgluTessEndContour(tess);
+                pph = (TTPOLYGONHEADER*)((char*)pph + pph->cb);
             }
-            pgluTessEndContour(tess);
-            pph = (TTPOLYGONHEADER*)((char*)pph + pph->cb);
         }
 
 error_in_list:
-- 
1.7.10.4



More information about the wine-patches mailing list