mirror of
https://github.com/reactos/reactos.git
synced 2024-08-19 18:02:55 +00:00
1156 lines
35 KiB
C
1156 lines
35 KiB
C
/****************************************************************************
|
|
* 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 <math.h>
|
|
|
|
#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;
|
|
|
|
/*****************************************************************************
|
|
* 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;
|
|
LineBufSize += LINE_BUF_QUANT;
|
|
|
|
f = (FLOAT*) HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, LineBuf, (LineBufSize) * 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;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* 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 *pz)
|
|
{
|
|
GLfloat v[3];
|
|
v[0] = (GLfloat) p[0];
|
|
v[1] = (GLfloat) p[1];
|
|
v[2] = *pz;
|
|
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:
|
|
*/
|
|
LineBuf = (FLOAT*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (LINE_BUF_QUANT) * sizeof(FLOAT));
|
|
|
|
if(!LineBuf)
|
|
goto exit;
|
|
|
|
LineBufSize = LINE_BUF_QUANT;
|
|
LineBufIndex = 0;
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
VertBuf = (FLOAT*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (VERT_BUF_QUANT) * sizeof(FLOAT));
|
|
|
|
if (!VertBuf)
|
|
goto exit;
|
|
|
|
VertBufSize = VERT_BUF_QUANT;
|
|
VertBufIndex = 0;
|
|
|
|
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, &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, &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:
|
|
|
|
if(LineBuf)
|
|
HeapFree(GetProcessHeap(), 0, LineBuf);
|
|
|
|
if(VertBuf)
|
|
HeapFree(GetProcessHeap(), 0, VertBuf);
|
|
|
|
if (tess)
|
|
gluDeleteTess(tess);
|
|
|
|
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);
|
|
}
|