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