diff --git a/reactos/dll/win32/opengl32/font.c b/reactos/dll/win32/opengl32/font.c new file mode 100644 index 00000000000..59040c10121 --- /dev/null +++ b/reactos/dll/win32/opengl32/font.c @@ -0,0 +1,1201 @@ +/**************************************************************************** +* Copyright (C) 1991-2004 SciTech Software, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* SCITECH SOFTWARE INC BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +****************************************************************************/ + +#include "opengl32.h" +#include + +#define LINE_BUF_QUANT 4000 +#define VERT_BUF_QUANT 4000 + +static HFONT hNewFont, hOldFont; +static FLOAT ScaleFactor; +static FLOAT* LineBuf; +static DWORD LineBufSize; +static DWORD LineBufIndex; +static FLOAT* VertBuf; +static DWORD VertBufSize; +static DWORD VertBufIndex; +static GLenum TessErrorOccurred; + + +/***************************************************************************** +* InitLineBuf +* +* Initializes the global LineBuf and its associated size and current-element +* counters. +*****************************************************************************/ + +INT InitLineBuf(VOID) +{ + if (!(LineBuf = (FLOAT*) + HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (LineBufSize = LINE_BUF_QUANT) * sizeof(FLOAT)))) + return 0; + LineBufIndex = 0; + return 1; +} + +/***************************************************************************** +* InitVertBuf +* +* Initializes the global VertBuf and its associated size and current-element +* counters. +*****************************************************************************/ + +INT InitVertBuf(VOID) +{ + if (!(VertBuf = (FLOAT*) + HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (VertBufSize = VERT_BUF_QUANT) * sizeof(FLOAT)))) + return 0; + VertBufIndex = 0; + return 1; +} + +/***************************************************************************** +* AppendToLineBuf +* +* Appends one floating-point value to the global LineBuf array. Return value +* is non-zero for success, zero for failure. +*****************************************************************************/ + +INT AppendToLineBuf(FLOAT value) +{ + if (LineBufIndex >= LineBufSize) + { + FLOAT* f; + + f = (FLOAT*) HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, LineBuf, (LineBufSize += LINE_BUF_QUANT) * sizeof(FLOAT)); + if (!f) + return 0; + LineBuf = f; + } + LineBuf[LineBufIndex++] = value; + return 1; +} + +/***************************************************************************** +* AppendToVertBuf +* +* Appends one floating-point value to the global VertBuf array. Return value +* is non-zero for success, zero for failure. +* +* Note that we can't realloc this one, because the tessellator is using +* pointers into it. +*****************************************************************************/ + +INT AppendToVertBuf(FLOAT value) +{ + if (VertBufIndex >= VertBufSize) + return 0; + VertBuf[VertBufIndex++] = value; + return 1; +} + +/***************************************************************************** +* FreeLineBuf +* +* Cleans up vertex buffer structure. +*****************************************************************************/ + +VOID FreeLineBuf(VOID) +{ + if (LineBuf) + { + HeapFree(GetProcessHeap(), 0, LineBuf); + LineBuf = NULL; + } +} + +/***************************************************************************** +* FreeVertBuf +* +* Cleans up vertex buffer structure. +*****************************************************************************/ + +VOID FreeVertBuf(VOID) +{ + if (VertBuf) + { + HeapFree(GetProcessHeap(), 0, VertBuf); + VertBuf = NULL; + } +} + +/***************************************************************************** +* GetWord +* +* Fetch the next 16-bit word from a little-endian byte stream, and increment +* the stream pointer to the next unscanned byte. +*****************************************************************************/ + +LONG GetWord(UCHAR** p) +{ + LONG value; + + value = ((*p)[1] << 8) + (*p)[0]; + *p += 2; + return value; +} + +/***************************************************************************** +* GetDWord +* +* Fetch the next 32-bit word from a little-endian byte stream, and increment +* the stream pointer to the next unscanned byte. +*****************************************************************************/ + +LONG GetDWord(UCHAR** p) +{ + LONG value; + + value = ((*p)[3] << 24) + ((*p)[2] << 16) + ((*p)[1] << 8) + (*p)[0]; + *p += 4; + return value; +} + +/***************************************************************************** +* GetFixed +* +* Fetch the next 32-bit fixed-point value from a little-endian byte stream, +* convert it to floating-point, and increment the stream pointer to the next +* unscanned byte. +*****************************************************************************/ +double GetFixed(UCHAR** p) +{ + LONG hiBits, loBits; + double value; + + loBits = GetWord(p); + hiBits = GetWord(p); + value = (double) ((hiBits << 16) | loBits) / 65536.0; + + return value * ScaleFactor; +} + + +/***************************************************************************** +** +** InvertGlyphBitmap. +** +** Invert the bitmap so that it suits OpenGL's representation. +** Each row starts on a double word boundary. +** +*****************************************************************************/ + +VOID InvertGlyphBitmap(INT w, INT h, DWORD *fptr, DWORD *tptr) +{ + INT dWordsInRow = (w+31)/32; + INT i, j; + + if (w <= 0 || h <= 0) { + return; + } + + tptr += ((h-1)*dWordsInRow); + for (i = 0; i < h; i++) { + for (j = 0; j < dWordsInRow; j++) { + *(tptr + j) = *(fptr + j); + } + tptr -= dWordsInRow; + fptr += dWordsInRow; + } +} + +/***************************************************************************** +* CreateHighResolutionFont +* +* Gets metrics for the current font and creates an equivalent font +* scaled to the design units of the font. +* +*****************************************************************************/ + +HFONT CreateHighResolutionFont(HDC hDC) +{ + UINT otmSize; + OUTLINETEXTMETRIC *otm; + LONG fontHeight, fontWidth, fontUnits; + LOGFONTW logFont, logFontFaceName; + + otmSize = GetOutlineTextMetricsW(hDC, 0, NULL); + if (!otmSize) + return NULL; + + otm = (OUTLINETEXTMETRIC *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, otmSize); + if (!otm) + return NULL; + + otm->otmSize = otmSize; + if (!GetOutlineTextMetricsW(hDC, otmSize, otm)) + return NULL; + + GetObjectW(GetCurrentObject(hDC, OBJ_FONT), sizeof(logFontFaceName), &logFontFaceName); + + fontHeight = otm->otmTextMetrics.tmHeight - + otm->otmTextMetrics.tmInternalLeading; + fontWidth = otm->otmTextMetrics.tmAveCharWidth; + fontUnits = (LONG) otm->otmEMSquare; + + ScaleFactor = 1.0F / (FLOAT) fontUnits; + + logFont.lfHeight = - ((LONG) fontUnits); + logFont.lfWidth = (LONG)((FLOAT) (fontWidth * fontUnits) / (FLOAT) fontHeight); + logFont.lfEscapement = 0; + logFont.lfOrientation = 0; + logFont.lfWeight = otm->otmTextMetrics.tmWeight; + logFont.lfItalic = otm->otmTextMetrics.tmItalic; + logFont.lfUnderline = otm->otmTextMetrics.tmUnderlined; + logFont.lfStrikeOut = otm->otmTextMetrics.tmStruckOut; + logFont.lfCharSet = otm->otmTextMetrics.tmCharSet; + logFont.lfOutPrecision = OUT_OUTLINE_PRECIS; + logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS; + logFont.lfQuality = DEFAULT_QUALITY; + logFont.lfPitchAndFamily = + otm->otmTextMetrics.tmPitchAndFamily & 0xf0; + wcscpy(logFont.lfFaceName, logFontFaceName.lfFaceName); + + hNewFont = CreateFontIndirectW(&logFont); + + HeapFree(GetProcessHeap(), 0, otm); + + return hNewFont; +} + +/***************************************************************************** +* MakeLinesFromArc +* +* Subdivides one arc of a quadratic spline until the chordal deviation +* tolerance requirement is met, then places the resulting set of line +* segments in the global LineBuf. +*****************************************************************************/ +INT MakeLinesFromArc(FLOAT x0, FLOAT y0, FLOAT x1, FLOAT y1, FLOAT x2, FLOAT y2, + DWORD vertexCountIndex, FLOAT chordalDeviationSquared) +{ + FLOAT x01; + FLOAT y01; + FLOAT x12; + FLOAT y12; + FLOAT midPointX; + FLOAT midPointY; + FLOAT deltaX; + FLOAT deltaY; + + /* + * Calculate midpoint of the curve by de Casteljau: + */ + x01 = 0.5F * (x0 + x1); + y01 = 0.5F * (y0 + y1); + x12 = 0.5F * (x1 + x2); + y12 = 0.5F * (y1 + y2); + midPointX = 0.5F * (x01 + x12); + midPointY = 0.5F * (y01 + y12); + + + /* + * Estimate chordal deviation by the distance from the midpoint + * of the curve to its non-pointpolated control point. If this + * distance is greater than the specified chordal deviation + * constraint, then subdivide. Otherwise, generate polylines + * from the three control points. + */ + deltaX = midPointX - x1; + deltaY = midPointY - y1; + + if (deltaX * deltaX + deltaY * deltaY > chordalDeviationSquared) + { + MakeLinesFromArc( x0, y0, + x01, y01, + midPointX, midPointY, + vertexCountIndex, + chordalDeviationSquared); + + MakeLinesFromArc( midPointX, midPointY, + x12, y12, + x2, y2, + vertexCountIndex, + chordalDeviationSquared); + } + else + { + /* + * The "pen" is already at (x0, y0), so we don't need to + * add that point to the LineBuf. + */ + if (!AppendToLineBuf(x1) + || !AppendToLineBuf(y1) + || !AppendToLineBuf(x2) + || !AppendToLineBuf(y2)) + return 0; + LineBuf[vertexCountIndex] += 2.0F; + } + + return 1; +} + +/***************************************************************************** +* MakeLinesFromTTQSpline +* +* Converts points from the poly quadratic spline in a TT_PRIM_QSPLINE +* structure to polyline points in the global LineBuf. +*****************************************************************************/ + +INT MakeLinesFromTTQSpline( UCHAR** pp, DWORD vertexCountIndex, WORD pointCount, FLOAT chordalDeviation) +{ + FLOAT x0, y0, x1, y1, x2, y2; + WORD point; + + /* + * Process each of the non-pointpolated points in the outline. + * To do this, we need to generate two pointpolated points (the + * start and end of the arc) for each non-pointpolated point. + * The first pointpolated point is always the one most recently + * stored in LineBuf, so we just extract it from there. The + * second pointpolated point is either the average of the next + * two points in the QSpline, or the last point in the QSpline + * if only one remains. + */ + for (point = 0; point < pointCount - 1; ++point) + { + x0 = LineBuf[LineBufIndex - 2]; + y0 = LineBuf[LineBufIndex - 1]; + + x1 = (FLOAT) GetFixed(pp); + y1 = (FLOAT) GetFixed(pp); + + if (point == pointCount - 2) + { + /* + * This is the last arc in the QSpline. The final + * point is the end of the arc. + */ + x2 = (FLOAT) GetFixed(pp); + y2 = (FLOAT) GetFixed(pp); + } + else + { + /* + * Peek at the next point in the input to compute + * the end of the arc: + */ + x2 = 0.5F * (x1 + (FLOAT) GetFixed(pp)); + y2 = 0.5F * (y1 + (FLOAT) GetFixed(pp)); + /* + * Push the point back onto the input so it will + * be reused as the next off-curve point: + */ + *pp -= 8; + } + + if (!MakeLinesFromArc( x0, y0, + x1, y1, + x2, y2, + vertexCountIndex, + chordalDeviation * chordalDeviation)) + return 0; + } + + return 1; +} + +/***************************************************************************** +* MakeLinesFromTTLine +* +* Converts points from the polyline in a TT_PRIM_LINE structure to +* equivalent points in the global LineBuf. +*****************************************************************************/ +INT MakeLinesFromTTLine(UCHAR** pp, DWORD vertexCountIndex, WORD pointCount) +{ + /* + * Just copy the line segments into the line buffer (converting + * type as we go): + */ + LineBuf[vertexCountIndex] += pointCount; + while (pointCount--) + { + if (!AppendToLineBuf((FLOAT) GetFixed(pp)) /* X coord */ + || !AppendToLineBuf((FLOAT) GetFixed(pp))) /* Y coord */ + return 0; + } + + return 1; +} + +/***************************************************************************** +* MakeLinesFromTTPolyCurve +* +* Converts the lines and splines in a single TTPOLYCURVE structure to points +* in the global LineBuf. +*****************************************************************************/ + +INT MakeLinesFromTTPolycurve(UCHAR** pp, DWORD vertexCountIndex, FLOAT chordalDeviation) +{ + WORD type; + WORD pointCount; + + /* + * Pick up the relevant fields of the TTPOLYCURVE structure: + */ + type = (WORD) GetWord(pp); + pointCount = (WORD) GetWord(pp); + + /* + * Convert the "curve" to line segments: + */ + if (type == TT_PRIM_LINE) + return MakeLinesFromTTLine( pp, + vertexCountIndex, + pointCount); + else if (type == TT_PRIM_QSPLINE) + return MakeLinesFromTTQSpline( pp, + vertexCountIndex, + pointCount, + chordalDeviation); + else + return 0; +} + +/***************************************************************************** +* MakeLinesFromTTPolygon +* +* Converts a TTPOLYGONHEADER and its associated curve structures into a +* single polyline loop in the global LineBuf. +*****************************************************************************/ + +INT MakeLinesFromTTPolygon(UCHAR** pp, FLOAT chordalDeviation) +{ + DWORD polySize; + UCHAR* polyStart; + DWORD vertexCountIndex; + + /* + * Record where the polygon data begins, and where the loop's + * vertex count resides: + */ + polyStart = *pp; + vertexCountIndex = LineBufIndex; + if (!AppendToLineBuf(0.0F)) + return 0; + + /* + * Extract relevant data from the TTPOLYGONHEADER: + */ + polySize = GetDWord(pp); + if (GetDWord(pp) != TT_POLYGON_TYPE) /* polygon type */ + return 0; + if (!AppendToLineBuf((FLOAT) GetFixed(pp))) /* first X coord */ + return 0; + if (!AppendToLineBuf((FLOAT) GetFixed(pp))) /* first Y coord */ + return 0; + LineBuf[vertexCountIndex] += 1.0F; + + /* + * Process each of the TTPOLYCURVE structures in the polygon: + */ + while (*pp < polyStart + polySize) + if (!MakeLinesFromTTPolycurve( pp, + vertexCountIndex, + chordalDeviation)) + return 0; + + return 1; +} + +/***************************************************************************** +* TessVertexOut +* +* Used by tessellator to handle output vertexes. +*****************************************************************************/ + +VOID CALLBACK TessVertexOutData(FLOAT p[3], GLfloat z) +{ + GLfloat v[3]; + v[0] = (GLfloat) p[0]; + v[1] = (GLfloat) p[1]; + v[2] = z; + glVertex3fv(v); +} + +/***************************************************************************** +* TessCombine +* +* Used by tessellator to handle self-pointsecting contours and degenerate +* geometry. +*****************************************************************************/ +VOID CALLBACK TessCombine(double coords[3], VOID* vertex_data[4], FLOAT weight[4], VOID** outData) +{ + if (!AppendToVertBuf((FLOAT) coords[0]) + || !AppendToVertBuf((FLOAT) coords[1]) + || !AppendToVertBuf((FLOAT) coords[2])) + TessErrorOccurred = GL_OUT_OF_MEMORY; + + *outData = VertBuf + (VertBufIndex - 3); +} + +/***************************************************************************** +* TessError +* +* Saves the last tessellator error code in the global TessErrorOccurred. +*****************************************************************************/ + +VOID CALLBACK TessError(GLenum error) +{ + TessErrorOccurred = error; +} + +/***************************************************************************** +* MakeLinesFromGlyph +* +* Converts the outline of a glyph from the TTPOLYGON format to a simple +* array of floating-point values containing one or more loops. +* +* The first element of the output array is a count of the number of loops. +* The loop data follows this count. Each loop consists of a count of the +* number of vertices it contains, followed by the vertices. Each vertex +* is an X and Y coordinate. For example, a single triangle might be +* described by this array: +* +* 1., 3., 0., 0., 1., 0., 0., 1. +* ^ ^ ^ ^ ^ ^ ^ ^ +* #loops #verts x1 y1 x2 y2 x3 y3 +* +* A two-loop glyph would look like this: +* +* 2., 3., 0.,0., 1.,0., 0.,1., 3., .2,.2, .4,.2, .2,.4 +* +* Line segments from the TTPOLYGON are transferred to the output array in +* the obvious way. Quadratic splines in the TTPOLYGON are converted to +* collections of line segments +*****************************************************************************/ + +INT MakeLinesFromGlyph(UCHAR* glyphBuf, DWORD glyphSize, FLOAT chordalDeviation) +{ + UCHAR* p; + INT status = 0; + + /* + * Pick up all the polygons (aka loops) that make up the glyph: + */ + if (!AppendToLineBuf(0.0F)) /* loop count at LineBuf[0] */ + goto exit; + + p = glyphBuf; + while (p < glyphBuf + glyphSize) + { + if (!MakeLinesFromTTPolygon(&p, chordalDeviation)) + goto exit; + LineBuf[0] += 1.0F; /* increment loop count */ + } + + status = 1; + +exit: + return status; +} + +/***************************************************************************** +* DrawGlyph +* +* Converts the outline of a glyph to OpenGL drawing primitives, tessellating +* as needed, and then draws the glyph. Tessellation of the quadratic splines +* in the outline is controlled by "chordalDeviation", and the drawing +* primitives (lines or polygons) are selected by "format". +* +* Return value is nonzero for success, zero for failure. +* +* Does not check for OpenGL errors, so if the caller needs to know about them, +* it should call glGetError(). +*****************************************************************************/ + +INT DrawGlyph(UCHAR* glyphBuf, DWORD glyphSize, FLOAT chordalDeviation, FLOAT extrusion, INT format) +{ + INT status = 0; + FLOAT* p; + DWORD loop; + DWORD point; + GLUtesselator* tess = NULL; + + /* + * Initialize the global buffer into which we place the outlines: + */ + if (!InitLineBuf()) + goto exit; + + /* + * Convert the glyph outlines to a set of polyline loops. + * (See MakeLinesFromGlyph() for the format of the loop data + * structure.) + */ + if (!MakeLinesFromGlyph(glyphBuf, glyphSize, chordalDeviation)) + goto exit; + p = LineBuf; + + + /* + * Now draw the loops in the appropriate format: + */ + if (format == WGL_FONT_LINES) + { + /* + * This is the easy case. Just draw the outlines. + */ + for (loop = (DWORD) *p++; loop; --loop) + { + glBegin(GL_LINE_LOOP); + for (point = (DWORD) *p++; point; --point) + { + glVertex2fv(p); + p += 2; + } + glEnd(); + } + status = 1; + } + + else if (format == WGL_FONT_POLYGONS) + { + double v[3]; + FLOAT *save_p = p; + GLfloat z_value; + + /* + * This is the hard case. We have to set up a tessellator + * to convert the outlines into a set of polygonal + * primitives, which the tessellator passes to some + * auxiliary routines for drawing. + */ + + if (!InitVertBuf()) + goto exit; + if (!(tess = gluNewTess())) + goto exit; + + gluTessCallback(tess, GLU_BEGIN, (VOID(CALLBACK *)()) glBegin); + gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (VOID(CALLBACK *)()) TessVertexOutData); + gluTessCallback(tess, GLU_END, (VOID(CALLBACK *)()) glEnd); + gluTessCallback(tess, GLU_ERROR, (VOID(CALLBACK *)()) TessError); + gluTessCallback(tess, GLU_TESS_COMBINE, (VOID(CALLBACK *)()) TessCombine); + gluTessNormal(tess, 0.0F, 0.0F, 1.0F); + + TessErrorOccurred = 0; + glNormal3f(0.0f, 0.0f, 1.0f); + v[2] = 0.0; + z_value = 0.0f; + + gluTessBeginPolygon(tess, (VOID *)*(INT *)&z_value); + + for (loop = (DWORD) *p++; loop; --loop) + { + gluTessBeginContour(tess); + + for (point = (DWORD) *p++; point; --point) + { + v[0] = p[0]; + v[1] = p[1]; + gluTessVertex(tess, v, p); + p += 2; + } + + gluTessEndContour(tess); + } + gluTessEndPolygon(tess); + + status = !TessErrorOccurred; + + /* Extrusion code */ + if (extrusion) + { + DWORD loops; + GLfloat thickness = (GLfloat) - extrusion; + FLOAT *vert, *vert2; + DWORD count; + + p = save_p; + loops = (DWORD) *p++; + + for (loop = 0; loop < loops; loop++) + { + GLfloat dx, dy, len; + DWORD last; + + count = (DWORD) *p++; + glBegin(GL_QUAD_STRIP); + + /* Check if the first and last vertex are identical + * so we don't draw the same quad twice. + */ + vert = p + (count-1)*2; + last = (p[0] == vert[0] && p[1] == vert[1]) ? count-1 : count; + + for (point = 0; point <= last; point++) + { + vert = p + 2 * (point % last); + vert2 = p + 2 * ((point+1) % last); + + dx = vert[0] - vert2[0]; + dy = vert[1] - vert2[1]; + len = (GLfloat)sqrt(dx * dx + dy * dy); + + glNormal3f(dy / len, -dx / len, 0.0f); + glVertex3f((GLfloat) vert[0], + (GLfloat) vert[1], thickness); + glVertex3f((GLfloat) vert[0], + (GLfloat) vert[1], 0.0f); + } + + glEnd(); + p += count*2; + } + + /* Draw the back face */ + p = save_p; + v[2] = thickness; + glNormal3f(0.0f, 0.0f, -1.0f); + gluTessNormal(tess, 0.0F, 0.0F, -1.0F); + + gluTessBeginPolygon(tess, (VOID *)*(INT *)&thickness); + + for (loop = (DWORD) *p++; loop; --loop) + { + count = (DWORD) *p++; + + gluTessBeginContour(tess); + + for (point = 0; point < count; point++) + { + vert = p + ((count-point-1)<<1); + v[0] = vert[0]; + v[1] = vert[1]; + gluTessVertex(tess, v, vert); + } + p += count*2; + + gluTessEndContour(tess); + } + gluTessEndPolygon(tess); + } + +#if !defined(NDEBUG) + if (TessErrorOccurred) + DBGPRINT("Tessellation error %s\n", gluErrorString(TessErrorOccurred)); +#endif + } + + +exit: + FreeLineBuf(); + if (tess) + gluDeleteTess(tess); + + FreeVertBuf(); + return status; +} + + +/***************************************************************************** +* MakeDisplayListFromGlyph +* +* Converts the outline of a glyph to an OpenGL display list. +* +* Return value is nonzero for success, zero for failure. +* +* Does not check for OpenGL errors, so if the caller needs to know about them, +* it should call glGetError(). +*****************************************************************************/ + +INT MakeDisplayListFromGlyph(DWORD listName, UCHAR* glyphBuf, DWORD glyphSize, LPGLYPHMETRICSFLOAT glyphMetricsFloat, + FLOAT chordalDeviation, FLOAT extrusion, INT format) +{ + INT status; + + glNewList(listName, GL_COMPILE); + status = DrawGlyph(glyphBuf, glyphSize, chordalDeviation, extrusion, format); + glTranslatef(glyphMetricsFloat->gmfCellIncX, glyphMetricsFloat->gmfCellIncY, 0.0F); + glEndList(); + + return status; +} + +// *********************************************************************** + +/***************************************************************************** +* IntUseFontBitmaps +* +* Converts a subrange of the glyphs in a GDI font to OpenGL display +* lists. +* +* Extended to support any GDI font, not just TrueType fonts. (DaveM) +* +*****************************************************************************/ + +BOOL APIENTRY IntUseFontBitmapsW(HDC hDC, DWORD first, DWORD count, DWORD listBase) +{ + INT i, ox, oy, ix, iy; + INT w = 0, h = 0; + INT iBufSize, iCurBufSize = 0; + DWORD *bitmapBuffer = NULL; + DWORD *invertedBitmapBuffer = NULL; + BOOL bSuccessOrFail = TRUE; + BOOL bTrueType = FALSE; + TEXTMETRIC tm; + GLYPHMETRICS gm; + RASTERIZER_STATUS rs; + MAT2 mat; + SIZE size; + RECT rect; + HDC hDCMem; + HBITMAP hBitmap; + BITMAPINFO bmi; + HFONT hFont; + + // Set up a unity matrix. + ZeroMemory(&mat, sizeof(mat)); + mat.eM11.value = 1; + mat.eM22.value = 1; + + // Test to see if selected font is TrueType or not + ZeroMemory(&tm, sizeof(tm)); + if (!GetTextMetrics(hDC, &tm)) + { + DBGPRINT("Font metrics error\n"); + return FALSE; + } + bTrueType = (tm.tmPitchAndFamily & TMPF_TRUETYPE) ? TRUE : FALSE; + + // Test to see if TRUE-TYPE capabilities are installed + // (only necessary if TrueType font selected) + ZeroMemory(&rs, sizeof(rs)); + + if (bTrueType) + { + if (!GetRasterizerCaps (&rs, sizeof (RASTERIZER_STATUS)) || !(rs.wFlags & TT_ENABLED)) + { + DBGPRINT("No TrueType caps\n"); + bTrueType = FALSE; + } + } + + // Trick to get the current font handle + hFont = SelectObject(hDC, GetStockObject(SYSTEM_FONT)); + SelectObject(hDC, hFont); + + // Have memory device context available for holding bitmaps of font glyphs + hDCMem = CreateCompatibleDC(hDC); + SelectObject(hDCMem, hFont); + SetTextColor(hDCMem, RGB(0xFF, 0xFF, 0xFF)); + SetBkColor(hDCMem, 0); + + for (i = first; (DWORD) i < (first + count); i++) + { + // Find out how much space is needed for the bitmap so we can + // Set the buffer size correctly. + if (bTrueType) + { + // Use TrueType support to get bitmap size of glyph + iBufSize = GetGlyphOutline(hDC, i, GGO_BITMAP, &gm, 0, NULL, &mat); + if (iBufSize == GDI_ERROR) + { + bSuccessOrFail = FALSE; + break; + } + } + else + { + // Use generic GDI support to compute bitmap size of glyph + w = tm.tmMaxCharWidth; + h = tm.tmHeight; + if (GetTextExtentPoint32(hDC, (LPCTSTR)&i, 1, &size)) + { + w = size.cx; + h = size.cy; + } + iBufSize = w * h; + // Use DWORD multiple for compatibility + iBufSize += 3; + iBufSize /= 4; + iBufSize *= 4; + } + + // If we need to allocate Larger Buffers, then do so - but allocate + // An extra 50 % so that we don't do too many mallocs ! + if (iBufSize > iCurBufSize) + { + if (bitmapBuffer) + { + HeapFree(GetProcessHeap(), 0, bitmapBuffer); + } + if (invertedBitmapBuffer) + { + HeapFree(GetProcessHeap(), 0, invertedBitmapBuffer); + } + + iCurBufSize = iBufSize * 2; + bitmapBuffer = (DWORD *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, iCurBufSize); + invertedBitmapBuffer = (DWORD *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, iCurBufSize); + + if (bitmapBuffer == NULL || invertedBitmapBuffer == NULL) + { + bSuccessOrFail = FALSE; + break; + } + } + + // If we fail to get the Glyph data, delete the display lists + // Created so far and return FALSE. + if (bTrueType) + { + // Use TrueType support to get bitmap of glyph + if (GetGlyphOutline(hDC, i, GGO_BITMAP, &gm, iBufSize, bitmapBuffer, &mat) == GDI_ERROR) + { + bSuccessOrFail = FALSE; + break; + } + + // Setup glBitmap parameters for current font glyph + w = gm.gmBlackBoxX; + h = gm.gmBlackBoxY; + ox = gm.gmptGlyphOrigin.x; + oy = gm.gmptGlyphOrigin.y; + ix = gm.gmCellIncX; + iy = gm.gmCellIncY; + } + else + { + // Use generic GDI support to create bitmap of glyph + ZeroMemory(bitmapBuffer, iBufSize); + + if (i >= tm.tmFirstChar && i <= tm.tmLastChar) + { + // Only create bitmaps for actual font glyphs + hBitmap = CreateBitmap(w, h, 1, 1, NULL); + SelectObject(hDCMem, hBitmap); + // Make bitmap of current font glyph + SetRect(&rect, 0, 0, w, h); + DrawText(hDCMem, (LPCTSTR)&i, 1, &rect, + DT_LEFT | DT_BOTTOM | DT_SINGLELINE | DT_NOCLIP); + // Make copy of bitmap in our local buffer + ZeroMemory(&bmi, sizeof(bmi)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = w; + bmi.bmiHeader.biHeight = -h; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 1; + bmi.bmiHeader.biCompression = BI_RGB; + GetDIBits(hDCMem, hBitmap, 0, h, bitmapBuffer, &bmi, 0); + DeleteObject(hBitmap); + } + else + { + // Otherwise use empty display list for non-existing glyph + iBufSize = 0; + } + + // Setup glBitmap parameters for current font glyph + ox = 0; + oy = tm.tmDescent; + ix = w; + iy = 0; + } + + // Create an OpenGL display list. + glNewList((listBase + i), GL_COMPILE); + + // Some fonts have no data for the space character, yet advertise + // a non-zero size. + if (0 == iBufSize) + { + glBitmap(0, 0, 0.0f, 0.0f, (GLfloat) ix, (GLfloat) iy, NULL); + } + else + { + // Invert the Glyph data. + InvertGlyphBitmap(w, h, bitmapBuffer, invertedBitmapBuffer); + + // Render an OpenGL bitmap and invert the origin. + glBitmap(w, h, + (GLfloat) ox, (GLfloat) (h-oy), + (GLfloat) ix, (GLfloat) iy, + (GLubyte *) invertedBitmapBuffer); + } + + // Close this display list. + glEndList(); + } + + if (bSuccessOrFail == FALSE) + { + DBGPRINT("DGL_UseFontBitmaps: Get glyph failed\n"); + glDeleteLists((i+listBase), (i-first)); + } + + // Release resources used + DeleteObject(hFont); + DeleteDC(hDCMem); + + if (bitmapBuffer) + HeapFree(GetProcessHeap(), 0, bitmapBuffer); + + if (invertedBitmapBuffer) + HeapFree(GetProcessHeap(), 0, invertedBitmapBuffer); + + return(bSuccessOrFail); +} + +BOOL APIENTRY IntUseFontBitmapsA(HDC hDC, DWORD first, DWORD count, DWORD listBase) +{ + /* Just call IntUseFontBitmapsW for now */ + return IntUseFontBitmapsW(hDC, first, count, listBase); +} + + + +/***************************************************************************** +* IntUseFontOutlines +* +* Converts a subrange of the glyphs in a TrueType font to OpenGL display +* lists. +*****************************************************************************/ + +BOOL APIENTRY IntUseFontOutlinesW(HDC hDC, DWORD first, DWORD count, DWORD listBase, FLOAT chordalDeviation, + FLOAT extrusion, INT format, GLYPHMETRICSFLOAT *glyphMetricsFloatArray) +{ + DWORD glyphIndex; + UCHAR* glyphBuf; + DWORD glyphBufSize; + + /* + * Flush any previous OpenGL errors. This allows us to check for + * new errors so they can be reported via the function return value. + */ + while (glGetError() != GL_NO_ERROR); + + /* + * Make sure that the current font can be sampled accurately. + */ + hNewFont = CreateHighResolutionFont(hDC); + + if (!hNewFont) + return FALSE; + + hOldFont = SelectObject(hDC, hNewFont); + if (!hOldFont) + return FALSE; + + /* + * Preallocate a buffer for the outline data, and track its size: + */ + glyphBuf = (UCHAR*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, glyphBufSize = 10240); + + if (!glyphBuf) + return FALSE; /*WGL_STATUS_NOT_ENOUGH_MEMORY*/ + + /* + * Process each glyph in the given range: + */ + for (glyphIndex = first; glyphIndex - first < count; ++glyphIndex) + { + GLYPHMETRICS glyphMetrics; + DWORD glyphSize; + static MAT2 matrix = + { + {0, 1}, {0, 0}, + {0, 0}, {0, 1} + }; + LPGLYPHMETRICSFLOAT glyphMetricsFloat = &glyphMetricsFloatArray[glyphIndex - first]; + + /* + * Determine how much space is needed to store the glyph's + * outlines. If our glyph buffer isn't large enough, + * resize it. + */ + + glyphSize = GetGlyphOutline(hDC, glyphIndex, GGO_NATIVE, &glyphMetrics, 0, NULL, &matrix); + + if (glyphSize < 0) + return FALSE; /*WGL_STATUS_FAILURE*/ + + if (glyphSize > glyphBufSize) + { + HeapFree(GetProcessHeap(), 0, glyphBuf); + glyphBuf = (UCHAR*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, glyphBufSize = glyphSize); + if (!glyphBuf) + return FALSE; /*WGL_STATUS_NOT_ENOUGH_MEMORY*/ + } + + + /* + * Get the glyph's outlines. + */ + if (GetGlyphOutline(hDC, glyphIndex, GGO_NATIVE, &glyphMetrics, glyphBufSize, glyphBuf, &matrix) < 0) + { + HeapFree(GetProcessHeap(), 0, glyphBuf); + return FALSE; /*WGL_STATUS_FAILURE*/ + } + + glyphMetricsFloat->gmfBlackBoxX = + (FLOAT) glyphMetrics.gmBlackBoxX * ScaleFactor; + glyphMetricsFloat->gmfBlackBoxY = + (FLOAT) glyphMetrics.gmBlackBoxY * ScaleFactor; + glyphMetricsFloat->gmfptGlyphOrigin.x = + (FLOAT) glyphMetrics.gmptGlyphOrigin.x * ScaleFactor; + glyphMetricsFloat->gmfptGlyphOrigin.y = + (FLOAT) glyphMetrics.gmptGlyphOrigin.y * ScaleFactor; + glyphMetricsFloat->gmfCellIncX = + (FLOAT) glyphMetrics.gmCellIncX * ScaleFactor; + glyphMetricsFloat->gmfCellIncY = + (FLOAT) glyphMetrics.gmCellIncY * ScaleFactor; + + /* + * Turn the glyph into a display list: + */ + if (!MakeDisplayListFromGlyph((glyphIndex - first) + listBase, glyphBuf, glyphSize, glyphMetricsFloat, + chordalDeviation + ScaleFactor, extrusion, format)) + { + HeapFree(GetProcessHeap(), 0, glyphBuf); + return FALSE; /*WGL_STATUS_FAILURE*/ + } + } + + /* + * Clean up temporary storage and return. If an error occurred, + * clear all OpenGL error flags and return FAILURE status; + * otherwise just return SUCCESS. + */ + HeapFree(GetProcessHeap(), 0, glyphBuf); + + DeleteObject(SelectObject(hDC, hOldFont)); + + if (glGetError() == GL_NO_ERROR) + { + return TRUE; /*WGL_STATUS_SUCCESS*/ + } + else + { + while (glGetError() != GL_NO_ERROR); + + return FALSE; /*WGL_STATUS_FAILURE*/ + } +} + +BOOL APIENTRY IntUseFontOutlinesA(HDC hDC, DWORD first, DWORD count, DWORD listBase, FLOAT chordalDeviation, + FLOAT extrusion, INT format, GLYPHMETRICSFLOAT *glyphMetricsFloatArray) +{ + /* Just call IntUseFontOutlinesW for now */ + return IntUseFontOutlinesW(hDC, first, count, listBase, chordalDeviation, extrusion, format, glyphMetricsFloatArray); +} diff --git a/reactos/dll/win32/opengl32/opengl32.h b/reactos/dll/win32/opengl32/opengl32.h index 32d761bb978..e22386c6cd1 100644 --- a/reactos/dll/win32/opengl32/opengl32.h +++ b/reactos/dll/win32/opengl32/opengl32.h @@ -40,6 +40,7 @@ extern "C" { #include #include +#include /* gl function list */ #include "glfuncs.h" @@ -190,6 +191,14 @@ extern GLPROCESSDATA OPENGL32_processdata; GLDRIVERDATA *OPENGL32_LoadICD( LPCWSTR driver ); BOOL OPENGL32_UnloadICD( GLDRIVERDATA *icd ); BOOL APIENTRY rosglMakeCurrent( HDC hdc, HGLRC hglrc ); +BOOL APIENTRY IntUseFontBitmapsA( HDC hDC, DWORD first, DWORD count, DWORD listBase ); +BOOL APIENTRY IntUseFontBitmapsW( HDC hDC, DWORD first, DWORD count, DWORD listBase ); +BOOL APIENTRY IntUseFontOutlinesA( HDC hDC, DWORD first, DWORD count, DWORD listBase, + FLOAT chordalDeviation, FLOAT extrusion, INT format, + GLYPHMETRICSFLOAT *glyphMetricsFloatArray ); +BOOL APIENTRY IntUseFontOutlinesW( HDC hDC, DWORD first, DWORD count, DWORD listBase, + FLOAT chordalDeviation, FLOAT extrusion, INT format, + GLYPHMETRICSFLOAT *glyphMetricsFloatArray ); /* empty gl functions from gl.c */ int WINAPI glEmptyFunc0( void ); diff --git a/reactos/dll/win32/opengl32/opengl32.rbuild b/reactos/dll/win32/opengl32/opengl32.rbuild index 96d645379d0..63992509a19 100644 --- a/reactos/dll/win32/opengl32/opengl32.rbuild +++ b/reactos/dll/win32/opengl32/opengl32.rbuild @@ -5,7 +5,9 @@ gdi32 user32 advapi32 + glu32 opengl32.h + font.c gl.c opengl32.c wgl.c diff --git a/reactos/dll/win32/opengl32/wgl.c b/reactos/dll/win32/opengl32/wgl.c index 3f714b9f41a..953305b894d 100644 --- a/reactos/dll/win32/opengl32/wgl.c +++ b/reactos/dll/win32/opengl32/wgl.c @@ -1209,33 +1209,26 @@ rosglSwapLayerBuffers( HDC hdc, UINT fuPlanes ) BOOL APIENTRY -rosglUseFontBitmapsA( HDC hdc, DWORD first, DWORD count, DWORD listBase ) +rosglUseFontBitmapsA( HDC hdc, DWORD first, DWORD count, DWORD listBase ) { - UNIMPLEMENTED; - SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); - return FALSE; + return IntUseFontBitmapsA(hdc, first, count, listBase); } BOOL APIENTRY -rosglUseFontBitmapsW( HDC hdc, DWORD first, DWORD count, DWORD listBase ) +rosglUseFontBitmapsW( HDC hdc, DWORD first, DWORD count, DWORD listBase ) { - UNIMPLEMENTED; - SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); - return FALSE; + return IntUseFontBitmapsW(hdc, first, count, listBase); } - BOOL APIENTRY rosglUseFontOutlinesA( HDC hdc, DWORD first, DWORD count, DWORD listBase, FLOAT deviation, FLOAT extrusion, int format, GLYPHMETRICSFLOAT *pgmf ) { - UNIMPLEMENTED; - SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); - return FALSE; + return IntUseFontOutlinesA(hdc, first, count, listBase, deviation, extrusion, format, pgmf); } @@ -1245,9 +1238,7 @@ rosglUseFontOutlinesW( HDC hdc, DWORD first, DWORD count, DWORD listBase, FLOAT deviation, FLOAT extrusion, int format, GLYPHMETRICSFLOAT *pgmf ) { - UNIMPLEMENTED; - SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); - return FALSE; + return IntUseFontOutlinesW(hdc, first, count, listBase, deviation, extrusion, format, pgmf); } #ifdef __cplusplus