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 }