[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