xref: /reactos/dll/opengl/opengl32/wgl_font.c (revision 1734f297)
1 /* Window-specific OpenGL functions implementation.
2  *
3  * Copyright (c) 1999 Lionel Ulmer
4  * Copyright (c) 2005 Raphael Junqueira
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include "opengl32.h"
22 
23 #include <math.h>
24 
25 WINE_DEFAULT_DEBUG_CHANNEL(wgl);
26 
27 /***********************************************************************
28  *		wglUseFontBitmaps_common
29  */
30 static BOOL wglUseFontBitmaps_common( HDC hdc, DWORD first, DWORD count, DWORD listBase, BOOL unicode )
31 {
32     const GLDISPATCHTABLE * funcs = IntGetCurrentDispatchTable();
33     GLYPHMETRICS gm;
34     unsigned int glyph, size = 0;
35     void *bitmap = NULL, *gl_bitmap = NULL;
36     int org_alignment;
37     BOOL ret = TRUE;
38 
39     funcs->GetIntegerv(GL_UNPACK_ALIGNMENT, &org_alignment);
40     funcs->PixelStorei(GL_UNPACK_ALIGNMENT, 4);
41 
42     for (glyph = first; glyph < first + count; glyph++) {
43         static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
44         unsigned int needed_size, height, width, width_int;
45 
46         if (unicode)
47             needed_size = GetGlyphOutlineW(hdc, glyph, GGO_BITMAP, &gm, 0, NULL, &identity);
48         else
49             needed_size = GetGlyphOutlineA(hdc, glyph, GGO_BITMAP, &gm, 0, NULL, &identity);
50 
51         TRACE("Glyph: %3d / List: %d size %d\n", glyph, listBase, needed_size);
52         if (needed_size == GDI_ERROR) {
53             ret = FALSE;
54             break;
55         }
56 
57         if (needed_size > size) {
58             size = needed_size;
59             HeapFree(GetProcessHeap(), 0, bitmap);
60             HeapFree(GetProcessHeap(), 0, gl_bitmap);
61             bitmap = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
62             gl_bitmap = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
63         }
64         if (unicode)
65             ret = (GetGlyphOutlineW(hdc, glyph, GGO_BITMAP, &gm, size, bitmap, &identity) != GDI_ERROR);
66         else
67             ret = (GetGlyphOutlineA(hdc, glyph, GGO_BITMAP, &gm, size, bitmap, &identity) != GDI_ERROR);
68         if (!ret) break;
69 
70         if (TRACE_ON(wgl)) {
71             unsigned int bitmask;
72             unsigned char *bitmap_ = bitmap;
73 
74             TRACE("  - bbox: %d x %d\n", gm.gmBlackBoxX, gm.gmBlackBoxY);
75             TRACE("  - origin: (%d, %d)\n", gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
76             TRACE("  - increment: %d - %d\n", gm.gmCellIncX, gm.gmCellIncY);
77             if (needed_size != 0) {
78                 TRACE("  - bitmap:\n");
79                 for (height = 0; height < gm.gmBlackBoxY; height++) {
80                     TRACE("      ");
81                     for (width = 0, bitmask = 0x80; width < gm.gmBlackBoxX; width++, bitmask >>= 1) {
82                         if (bitmask == 0) {
83                             bitmap_ += 1;
84                             bitmask = 0x80;
85                         }
86                         if (*bitmap_ & bitmask)
87                             TRACE("*");
88                         else
89                             TRACE(" ");
90                     }
91                     bitmap_ += (4 - ((UINT_PTR)bitmap_ & 0x03));
92                     TRACE("\n");
93                 }
94             }
95         }
96 
97          /* In OpenGL, the bitmap is drawn from the bottom to the top... So we need to invert the
98          * glyph for it to be drawn properly.
99          */
100         if (needed_size != 0) {
101             width_int = (gm.gmBlackBoxX + 31) / 32;
102             for (height = 0; height < gm.gmBlackBoxY; height++) {
103                 for (width = 0; width < width_int; width++) {
104                     ((int *) gl_bitmap)[(gm.gmBlackBoxY - height - 1) * width_int + width] =
105                     ((int *) bitmap)[height * width_int + width];
106                 }
107             }
108         }
109 
110         funcs->NewList(listBase++, GL_COMPILE);
111         if (needed_size != 0) {
112             funcs->Bitmap(gm.gmBlackBoxX, gm.gmBlackBoxY,
113                     0 - gm.gmptGlyphOrigin.x, (int) gm.gmBlackBoxY - gm.gmptGlyphOrigin.y,
114                     gm.gmCellIncX, gm.gmCellIncY,
115                     gl_bitmap);
116         } else {
117             /* This is the case of 'empty' glyphs like the space character */
118             funcs->Bitmap(0, 0, 0, 0, gm.gmCellIncX, gm.gmCellIncY, NULL);
119         }
120         funcs->EndList();
121     }
122 
123     funcs->PixelStorei(GL_UNPACK_ALIGNMENT, org_alignment);
124     HeapFree(GetProcessHeap(), 0, bitmap);
125     HeapFree(GetProcessHeap(), 0, gl_bitmap);
126     return ret;
127 }
128 
129 /***********************************************************************
130  *		wglUseFontBitmapsA (OPENGL32.@)
131  */
132 BOOL WINAPI wglUseFontBitmapsA(HDC hdc, DWORD first, DWORD count, DWORD listBase)
133 {
134     return wglUseFontBitmaps_common( hdc, first, count, listBase, FALSE );
135 }
136 
137 /***********************************************************************
138  *		wglUseFontBitmapsW (OPENGL32.@)
139  */
140 BOOL WINAPI wglUseFontBitmapsW(HDC hdc, DWORD first, DWORD count, DWORD listBase)
141 {
142     return wglUseFontBitmaps_common( hdc, first, count, listBase, TRUE );
143 }
144 
145 /* FIXME: should probably have a glu.h header */
146 
147 typedef struct GLUtesselator GLUtesselator;
148 typedef void (WINAPI *_GLUfuncptr)(void);
149 
150 #define GLU_TESS_BEGIN  100100
151 #define GLU_TESS_VERTEX 100101
152 #define GLU_TESS_END    100102
153 
154 static GLUtesselator * (WINAPI *pgluNewTess)(void);
155 static void (WINAPI *pgluDeleteTess)(GLUtesselator *tess);
156 static void (WINAPI *pgluTessNormal)(GLUtesselator *tess, GLdouble x, GLdouble y, GLdouble z);
157 static void (WINAPI *pgluTessBeginPolygon)(GLUtesselator *tess, void *polygon_data);
158 static void (WINAPI *pgluTessEndPolygon)(GLUtesselator *tess);
159 static void (WINAPI *pgluTessCallback)(GLUtesselator *tess, GLenum which, _GLUfuncptr fn);
160 static void (WINAPI *pgluTessBeginContour)(GLUtesselator *tess);
161 static void (WINAPI *pgluTessEndContour)(GLUtesselator *tess);
162 static void (WINAPI *pgluTessVertex)(GLUtesselator *tess, GLdouble *location, GLvoid* data);
163 
164 static HMODULE load_libglu(void)
165 {
166     static const WCHAR glu32W[] = {'g','l','u','3','2','.','d','l','l',0};
167     static int already_loaded;
168     static HMODULE module;
169 
170     if (already_loaded) return module;
171     already_loaded = 1;
172 
173     TRACE("Trying to load GLU library\n");
174     module = LoadLibraryW( glu32W );
175     if (!module)
176     {
177         WARN("Failed to load glu32\n");
178         return NULL;
179     }
180 #define LOAD_FUNCPTR(f) p##f = (void *)GetProcAddress( module, #f )
181     LOAD_FUNCPTR(gluNewTess);
182     LOAD_FUNCPTR(gluDeleteTess);
183     LOAD_FUNCPTR(gluTessBeginContour);
184     LOAD_FUNCPTR(gluTessNormal);
185     LOAD_FUNCPTR(gluTessBeginPolygon);
186     LOAD_FUNCPTR(gluTessCallback);
187     LOAD_FUNCPTR(gluTessEndContour);
188     LOAD_FUNCPTR(gluTessEndPolygon);
189     LOAD_FUNCPTR(gluTessVertex);
190 #undef LOAD_FUNCPTR
191     return module;
192 }
193 
194 static void fixed_to_double(POINTFX fixed, UINT em_size, GLdouble vertex[3])
195 {
196     vertex[0] = (fixed.x.value + (GLdouble)fixed.x.fract / (1 << 16)) / em_size;
197     vertex[1] = (fixed.y.value + (GLdouble)fixed.y.fract / (1 << 16)) / em_size;
198     vertex[2] = 0.0;
199 }
200 
201 static void WINAPI tess_callback_vertex(GLvoid *vertex)
202 {
203     const GLDISPATCHTABLE * funcs = IntGetCurrentDispatchTable();
204     GLdouble *dbl = vertex;
205     TRACE("%f, %f, %f\n", dbl[0], dbl[1], dbl[2]);
206     funcs->Vertex3dv(vertex);
207 }
208 
209 static void WINAPI tess_callback_begin(GLenum which)
210 {
211     const GLDISPATCHTABLE * funcs = IntGetCurrentDispatchTable();
212     TRACE("%d\n", which);
213     funcs->Begin(which);
214 }
215 
216 static void WINAPI tess_callback_end(void)
217 {
218     const GLDISPATCHTABLE * funcs = IntGetCurrentDispatchTable();
219     TRACE("\n");
220     funcs->End();
221 }
222 
223 typedef struct _bezier_vector {
224     GLdouble x;
225     GLdouble y;
226 } bezier_vector;
227 
228 static double bezier_deviation_squared(const bezier_vector *p)
229 {
230     bezier_vector deviation;
231     bezier_vector vertex;
232     bezier_vector base;
233     double base_length;
234     double dot;
235 
236     vertex.x = (p[0].x + p[1].x*2 + p[2].x)/4 - p[0].x;
237     vertex.y = (p[0].y + p[1].y*2 + p[2].y)/4 - p[0].y;
238 
239     base.x = p[2].x - p[0].x;
240     base.y = p[2].y - p[0].y;
241 
242     base_length = sqrt(base.x*base.x + base.y*base.y);
243     base.x /= base_length;
244     base.y /= base_length;
245 
246     dot = base.x*vertex.x + base.y*vertex.y;
247     dot = min(max(dot, 0.0), base_length);
248     base.x *= dot;
249     base.y *= dot;
250 
251     deviation.x = vertex.x-base.x;
252     deviation.y = vertex.y-base.y;
253 
254     return deviation.x*deviation.x + deviation.y*deviation.y;
255 }
256 
257 static int bezier_approximate(const bezier_vector *p, bezier_vector *points, FLOAT deviation)
258 {
259     bezier_vector first_curve[3];
260     bezier_vector second_curve[3];
261     bezier_vector vertex;
262     int total_vertices;
263 
264     if(bezier_deviation_squared(p) <= deviation*deviation)
265     {
266         if(points)
267             *points = p[2];
268         return 1;
269     }
270 
271     vertex.x = (p[0].x + p[1].x*2 + p[2].x)/4;
272     vertex.y = (p[0].y + p[1].y*2 + p[2].y)/4;
273 
274     first_curve[0] = p[0];
275     first_curve[1].x = (p[0].x + p[1].x)/2;
276     first_curve[1].y = (p[0].y + p[1].y)/2;
277     first_curve[2] = vertex;
278 
279     second_curve[0] = vertex;
280     second_curve[1].x = (p[2].x + p[1].x)/2;
281     second_curve[1].y = (p[2].y + p[1].y)/2;
282     second_curve[2] = p[2];
283 
284     total_vertices = bezier_approximate(first_curve, points, deviation);
285     if(points)
286         points += total_vertices;
287     total_vertices += bezier_approximate(second_curve, points, deviation);
288     return total_vertices;
289 }
290 
291 /***********************************************************************
292  *		wglUseFontOutlines_common
293  */
294 static BOOL wglUseFontOutlines_common(HDC hdc,
295                                       DWORD first,
296                                       DWORD count,
297                                       DWORD listBase,
298                                       FLOAT deviation,
299                                       FLOAT extrusion,
300                                       int format,
301                                       LPGLYPHMETRICSFLOAT lpgmf,
302                                       BOOL unicode)
303 {
304     const GLDISPATCHTABLE * funcs = IntGetCurrentDispatchTable();
305     UINT glyph;
306     const MAT2 identity = {{0,1},{0,0},{0,0},{0,1}};
307     GLUtesselator *tess = NULL;
308     LOGFONTW lf;
309     HFONT old_font, unscaled_font;
310     UINT em_size = 1024;
311     RECT rc;
312 
313     TRACE("(%p, %d, %d, %d, %f, %f, %d, %p, %s)\n", hdc, first, count,
314           listBase, deviation, extrusion, format, lpgmf, unicode ? "W" : "A");
315 
316     if(deviation <= 0.0)
317         deviation = 1.0/em_size;
318 
319     if(format == WGL_FONT_POLYGONS)
320     {
321         if (!load_libglu())
322         {
323             ERR("glu32 is required for this function but isn't available\n");
324             return FALSE;
325         }
326 
327         tess = pgluNewTess();
328         if(!tess) return FALSE;
329         pgluTessCallback(tess, GLU_TESS_VERTEX, (_GLUfuncptr)tess_callback_vertex);
330         pgluTessCallback(tess, GLU_TESS_BEGIN, (_GLUfuncptr)tess_callback_begin);
331         pgluTessCallback(tess, GLU_TESS_END, tess_callback_end);
332     }
333 
334     GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf);
335     rc.left = rc.right = rc.bottom = 0;
336     rc.top = em_size;
337     DPtoLP(hdc, (POINT*)&rc, 2);
338     lf.lfHeight = -abs(rc.top - rc.bottom);
339     lf.lfOrientation = lf.lfEscapement = 0;
340     unscaled_font = CreateFontIndirectW(&lf);
341     old_font = SelectObject(hdc, unscaled_font);
342 
343     for (glyph = first; glyph < first + count; glyph++)
344     {
345         DWORD needed;
346         GLYPHMETRICS gm;
347         BYTE *buf;
348         TTPOLYGONHEADER *pph;
349         TTPOLYCURVE *ppc;
350         GLdouble *vertices = NULL, *vertices_temp = NULL;
351         int vertex_total = -1;
352 
353         if(unicode)
354             needed = GetGlyphOutlineW(hdc, glyph, GGO_NATIVE, &gm, 0, NULL, &identity);
355         else
356             needed = GetGlyphOutlineA(hdc, glyph, GGO_NATIVE, &gm, 0, NULL, &identity);
357 
358         if(needed == GDI_ERROR)
359             goto error;
360 
361         buf = HeapAlloc(GetProcessHeap(), 0, needed);
362 
363         if(!buf)
364             goto error;
365 
366         if(unicode)
367             GetGlyphOutlineW(hdc, glyph, GGO_NATIVE, &gm, needed, buf, &identity);
368         else
369             GetGlyphOutlineA(hdc, glyph, GGO_NATIVE, &gm, needed, buf, &identity);
370 
371         TRACE("glyph %d\n", glyph);
372 
373         if(lpgmf)
374         {
375             lpgmf->gmfBlackBoxX = (float)gm.gmBlackBoxX / em_size;
376             lpgmf->gmfBlackBoxY = (float)gm.gmBlackBoxY / em_size;
377             lpgmf->gmfptGlyphOrigin.x = (float)gm.gmptGlyphOrigin.x / em_size;
378             lpgmf->gmfptGlyphOrigin.y = (float)gm.gmptGlyphOrigin.y / em_size;
379             lpgmf->gmfCellIncX = (float)gm.gmCellIncX / em_size;
380             lpgmf->gmfCellIncY = (float)gm.gmCellIncY / em_size;
381 
382             TRACE("%fx%f at %f,%f inc %f,%f\n", lpgmf->gmfBlackBoxX, lpgmf->gmfBlackBoxY,
383                   lpgmf->gmfptGlyphOrigin.x, lpgmf->gmfptGlyphOrigin.y, lpgmf->gmfCellIncX, lpgmf->gmfCellIncY);
384             lpgmf++;
385         }
386 
387         funcs->NewList(listBase++, GL_COMPILE);
388         funcs->FrontFace(GL_CCW);
389         if(format == WGL_FONT_POLYGONS)
390         {
391             funcs->Normal3d(0.0, 0.0, 1.0);
392             pgluTessNormal(tess, 0, 0, 1);
393             pgluTessBeginPolygon(tess, NULL);
394         }
395 
396         while(!vertices)
397         {
398             if(vertex_total != -1)
399                 vertices_temp = vertices = HeapAlloc(GetProcessHeap(), 0, vertex_total * 3 * sizeof(GLdouble));
400             vertex_total = 0;
401 
402             pph = (TTPOLYGONHEADER*)buf;
403             while((BYTE*)pph < buf + needed)
404             {
405                 GLdouble previous[3];
406                 fixed_to_double(pph->pfxStart, em_size, previous);
407 
408                 if(vertices)
409                     TRACE("\tstart %d, %d\n", pph->pfxStart.x.value, pph->pfxStart.y.value);
410 
411                 if(format == WGL_FONT_POLYGONS)
412                     pgluTessBeginContour(tess);
413                 else
414                     funcs->Begin(GL_LINE_LOOP);
415 
416                 if(vertices)
417                 {
418                     fixed_to_double(pph->pfxStart, em_size, vertices);
419                     if(format == WGL_FONT_POLYGONS)
420                         pgluTessVertex(tess, vertices, vertices);
421                     else
422                         funcs->Vertex3d(vertices[0], vertices[1], vertices[2]);
423                     vertices += 3;
424                 }
425                 vertex_total++;
426 
427                 ppc = (TTPOLYCURVE*)((char*)pph + sizeof(*pph));
428                 while((char*)ppc < (char*)pph + pph->cb)
429                 {
430                     int i, j;
431                     int num;
432 
433                     switch(ppc->wType) {
434                     case TT_PRIM_LINE:
435                         for(i = 0; i < ppc->cpfx; i++)
436                         {
437                             if(vertices)
438                             {
439                                 TRACE("\t\tline to %d, %d\n",
440                                       ppc->apfx[i].x.value, ppc->apfx[i].y.value);
441                                 fixed_to_double(ppc->apfx[i], em_size, vertices);
442                                 if(format == WGL_FONT_POLYGONS)
443                                     pgluTessVertex(tess, vertices, vertices);
444                                 else
445                                     funcs->Vertex3d(vertices[0], vertices[1], vertices[2]);
446                                 vertices += 3;
447                             }
448                             fixed_to_double(ppc->apfx[i], em_size, previous);
449                             vertex_total++;
450                         }
451                         break;
452 
453                     case TT_PRIM_QSPLINE:
454                         for(i = 0; i < ppc->cpfx-1; i++)
455                         {
456                             bezier_vector curve[3];
457                             bezier_vector *points;
458                             GLdouble curve_vertex[3];
459 
460                             if(vertices)
461                                 TRACE("\t\tcurve  %d,%d %d,%d\n",
462                                       ppc->apfx[i].x.value,     ppc->apfx[i].y.value,
463                                       ppc->apfx[i + 1].x.value, ppc->apfx[i + 1].y.value);
464 
465                             curve[0].x = previous[0];
466                             curve[0].y = previous[1];
467                             fixed_to_double(ppc->apfx[i], em_size, curve_vertex);
468                             curve[1].x = curve_vertex[0];
469                             curve[1].y = curve_vertex[1];
470                             fixed_to_double(ppc->apfx[i + 1], em_size, curve_vertex);
471                             curve[2].x = curve_vertex[0];
472                             curve[2].y = curve_vertex[1];
473                             if(i < ppc->cpfx-2)
474                             {
475                                 curve[2].x = (curve[1].x + curve[2].x)/2;
476                                 curve[2].y = (curve[1].y + curve[2].y)/2;
477                             }
478                             num = bezier_approximate(curve, NULL, deviation);
479                             points = HeapAlloc(GetProcessHeap(), 0, num*sizeof(bezier_vector));
480                             num = bezier_approximate(curve, points, deviation);
481                             vertex_total += num;
482                             if(vertices)
483                             {
484                                 for(j=0; j<num; j++)
485                                 {
486                                     TRACE("\t\t\tvertex at %f,%f\n", points[j].x, points[j].y);
487                                     vertices[0] = points[j].x;
488                                     vertices[1] = points[j].y;
489                                     vertices[2] = 0.0;
490                                     if(format == WGL_FONT_POLYGONS)
491                                         pgluTessVertex(tess, vertices, vertices);
492                                     else
493                                         funcs->Vertex3d(vertices[0], vertices[1], vertices[2]);
494                                     vertices += 3;
495                                 }
496                             }
497                             HeapFree(GetProcessHeap(), 0, points);
498                             previous[0] = curve[2].x;
499                             previous[1] = curve[2].y;
500                         }
501                         break;
502                     default:
503                         ERR("\t\tcurve type = %d\n", ppc->wType);
504                         if(format == WGL_FONT_POLYGONS)
505                             pgluTessEndContour(tess);
506                         else
507                             funcs->End();
508                         goto error_in_list;
509                     }
510 
511                     ppc = (TTPOLYCURVE*)((char*)ppc + sizeof(*ppc) +
512                                          (ppc->cpfx - 1) * sizeof(POINTFX));
513                 }
514                 if(format == WGL_FONT_POLYGONS)
515                     pgluTessEndContour(tess);
516                 else
517                     funcs->End();
518                 pph = (TTPOLYGONHEADER*)((char*)pph + pph->cb);
519             }
520         }
521 
522 error_in_list:
523         if(format == WGL_FONT_POLYGONS)
524             pgluTessEndPolygon(tess);
525         funcs->Translated((GLdouble)gm.gmCellIncX / em_size, (GLdouble)gm.gmCellIncY / em_size, 0.0);
526         funcs->EndList();
527 
528         HeapFree(GetProcessHeap(), 0, buf);
529 
530         if(vertices_temp)
531             HeapFree(GetProcessHeap(), 0, vertices_temp);
532     }
533 
534  error:
535     DeleteObject(SelectObject(hdc, old_font));
536     if(format == WGL_FONT_POLYGONS)
537         pgluDeleteTess(tess);
538     return TRUE;
539 
540 }
541 
542 /***********************************************************************
543  *		wglUseFontOutlinesA (OPENGL32.@)
544  */
545 BOOL WINAPI wglUseFontOutlinesA(HDC hdc,
546 				DWORD first,
547 				DWORD count,
548 				DWORD listBase,
549 				FLOAT deviation,
550 				FLOAT extrusion,
551 				int format,
552 				LPGLYPHMETRICSFLOAT lpgmf)
553 {
554     return wglUseFontOutlines_common(hdc, first, count, listBase, deviation, extrusion, format, lpgmf, FALSE);
555 }
556 
557 /***********************************************************************
558  *		wglUseFontOutlinesW (OPENGL32.@)
559  */
560 BOOL WINAPI wglUseFontOutlinesW(HDC hdc,
561 				DWORD first,
562 				DWORD count,
563 				DWORD listBase,
564 				FLOAT deviation,
565 				FLOAT extrusion,
566 				int format,
567 				LPGLYPHMETRICSFLOAT lpgmf)
568 {
569     return wglUseFontOutlines_common(hdc, first, count, listBase, deviation, extrusion, format, lpgmf, TRUE);
570 }