[FMIFS]
[reactos.git] / reactos / dll / opengl / opengl32 / wgl_font.c
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 }