489 lines
14 KiB
C
489 lines
14 KiB
C
|
/******************************Module*Header*******************************\
|
||
|
* Module Name: font.c
|
||
|
*
|
||
|
* Font and text handling for the OpenGL-based 3D Text screen saver.
|
||
|
*
|
||
|
* Created: 12-24-94 -by- Marc Fortier [marcfo]
|
||
|
*
|
||
|
* Copyright (c) 1994 Microsoft Corporation
|
||
|
\**************************************************************************/
|
||
|
|
||
|
#include <nt.h>
|
||
|
#include <ntrtl.h>
|
||
|
#include <nturtl.h>
|
||
|
#include <windows.h>
|
||
|
#include <commdlg.h>
|
||
|
#include <ptypes32.h>
|
||
|
#include <pwin32.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <memory.h>
|
||
|
#include <float.h> // for FLT_MAX
|
||
|
#include <GL/gl.h>
|
||
|
#include "sscommon.h"
|
||
|
#include "sstext3d.h"
|
||
|
|
||
|
#ifndef UNICODE
|
||
|
// Support dbcs characters (this is a patch)
|
||
|
#define SS_DBCS_SUPPORT 1
|
||
|
#endif
|
||
|
|
||
|
static LISTENTRY* FindExtendedLUTEntry( TCHAR c, WglFontContext *pwfc );
|
||
|
static BOOL UpdateReducedFontList( TCHAR c, LISTENTRY *ple,
|
||
|
WglFontContext *pwfc );
|
||
|
#ifdef SS_DBCS_SUPPORT
|
||
|
static BOOL UpdateReducedFontListDBCS( DWORD c, LISTENTRY *ple,
|
||
|
WglFontContext *pwfc );
|
||
|
#endif
|
||
|
static void DeleteExtendedLUTTableEntries( WglFontContext *pwfc );
|
||
|
static void DeleteDirectLUTTableEntries( WglFontContext *pwfc );
|
||
|
static void DeleteLUTEntry( LISTENTRY *ple );
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* CreateWglFontContext
|
||
|
*
|
||
|
* Create a WglFontContext.
|
||
|
*
|
||
|
* Initializes the look-up-table memory for characters.
|
||
|
* The first half of this table is a direct look-up, using the char value.
|
||
|
* The second half is used for unicode chars that are >256, and must be
|
||
|
* searched sequentially.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
WglFontContext *
|
||
|
CreateWglFontContext( HDC hdc, int type, float fExtrusion,
|
||
|
float fChordalDeviation )
|
||
|
{
|
||
|
WglFontContext *pwfc;
|
||
|
|
||
|
pwfc = (WglFontContext *) LocalAlloc( LMEM_FIXED, sizeof(WglFontContext) );
|
||
|
if( !pwfc ) {
|
||
|
SS_ALLOC_FAILURE( "CreateWglFontContext" );
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
pwfc->hdc = hdc;
|
||
|
pwfc->type = type;
|
||
|
pwfc->extrusion = fExtrusion;
|
||
|
pwfc->chordalDeviation = fChordalDeviation;
|
||
|
|
||
|
pwfc->listLUT = (LISTENTRY*) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,
|
||
|
SIZE_LIST_LUT * sizeof(LISTENTRY) );
|
||
|
|
||
|
if( !pwfc->listLUT ) {
|
||
|
LocalFree( pwfc );
|
||
|
SS_ALLOC_FAILURE( "CreateWglFontContext" );
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
pwfc->LUTIndex = 0;
|
||
|
|
||
|
return pwfc;
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DeleteWglFontContext
|
||
|
*
|
||
|
* Delete a WglFontContext.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
void
|
||
|
DeleteWglFontContext( WglFontContext *pwfc )
|
||
|
{
|
||
|
if( !pwfc )
|
||
|
return;
|
||
|
|
||
|
// Delete the extended lut table entries
|
||
|
DeleteExtendedLUTTableEntries( pwfc );
|
||
|
|
||
|
// Delete the direct lut table entries
|
||
|
DeleteDirectLUTTableEntries( pwfc );
|
||
|
|
||
|
if( pwfc->listLUT )
|
||
|
LocalFree( pwfc->listLUT );
|
||
|
|
||
|
LocalFree( pwfc );
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DeleteExtendedLUTTablEntries
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
static void
|
||
|
DeleteExtendedLUTTableEntries( WglFontContext *pwfc )
|
||
|
{
|
||
|
int i;
|
||
|
LISTENTRY *ple = pwfc->listLUT + MAX_DIRECT_LUT;
|
||
|
|
||
|
for( i = 0; i < pwfc->LUTIndex; i ++, ple++ )
|
||
|
DeleteLUTEntry( ple );
|
||
|
|
||
|
pwfc->LUTIndex = 0;
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DeleteDirectLUTTablEntries
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
static void
|
||
|
DeleteDirectLUTTableEntries( WglFontContext *pwfc )
|
||
|
{
|
||
|
int i;
|
||
|
LISTENTRY *ple = pwfc->listLUT;
|
||
|
|
||
|
for( i = 0; i < MAX_DIRECT_LUT; i ++, ple++ )
|
||
|
DeleteLUTEntry( ple );
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DeleteLUTEntry
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
static void
|
||
|
DeleteLUTEntry( LISTENTRY *ple )
|
||
|
{
|
||
|
if( ple->listNum ) {
|
||
|
glDeleteLists( ple->listNum, 1 );
|
||
|
ple->listNum = 0;
|
||
|
}
|
||
|
if( ple->lpgmf ) {
|
||
|
LocalFree( ple->lpgmf );
|
||
|
ple->lpgmf = NULL;
|
||
|
}
|
||
|
ple->glyph = 0;
|
||
|
}
|
||
|
|
||
|
/**************************************************************************\
|
||
|
* ConvertStringToList
|
||
|
*
|
||
|
* - Translate the string into display list numbers
|
||
|
* - If no entry in the list table, update the table
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
void
|
||
|
ConvertStringToList( LPTSTR pszSrc, USHORT *usDst, WglFontContext *pwfc )
|
||
|
{
|
||
|
int strLen;
|
||
|
LISTENTRY *ple;
|
||
|
TCHAR c;
|
||
|
TUCHAR cindex; // use unsigned TCHAR for table indexing
|
||
|
#ifdef SS_DBCS_SUPPORT
|
||
|
BOOL bIsLeadByte;
|
||
|
#endif
|
||
|
|
||
|
if( !pwfc )
|
||
|
return;
|
||
|
|
||
|
/* Go thru the string, making sure every char has a display list
|
||
|
* Copy the display list # into usDst
|
||
|
*/
|
||
|
|
||
|
strLen = lstrlen( pszSrc );
|
||
|
if( strLen > TEXT_BUF_SIZE )
|
||
|
strLen = TEXT_BUF_SIZE;
|
||
|
|
||
|
/* Check for possible overflow with the extended LUT section, and
|
||
|
* invalidate the extended LUT if so
|
||
|
*/
|
||
|
if( pwfc->LUTIndex ) {
|
||
|
int roomLeft; // spaces left in extended lut table
|
||
|
|
||
|
roomLeft = SIZE_LIST_LUT - (pwfc->LUTIndex + MAX_DIRECT_LUT +1);
|
||
|
if( roomLeft < strLen ) {
|
||
|
/* If none of the characters in this string have entries in the
|
||
|
* table, we'll run out of room. Assume the worst, invalidate
|
||
|
* the whole table, and start over again.
|
||
|
*/
|
||
|
DeleteExtendedLUTTableEntries( pwfc );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for( ; strLen; strLen--, pszSrc++ ) {
|
||
|
c = *pszSrc;
|
||
|
cindex = (TUCHAR) c;
|
||
|
|
||
|
// see if char already has cmd list entry - if not, create it
|
||
|
#ifdef SS_DBCS_SUPPORT
|
||
|
bIsLeadByte = IsDBCSLeadByte(cindex);
|
||
|
if( !bIsLeadByte && (cindex < MAX_DIRECT_LUT) ) { // direct LUT
|
||
|
#else
|
||
|
if( cindex < MAX_DIRECT_LUT ) { // direct LUT entry
|
||
|
#endif
|
||
|
ple = &pwfc->listLUT[cindex];
|
||
|
if( !ple->listNum ) {
|
||
|
// have to create new cmd list for this glyph
|
||
|
if( ! UpdateReducedFontList( c, ple, pwfc ) ) {
|
||
|
DeleteLUTEntry( ple );
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
} else { // extended LUT entry
|
||
|
#ifdef SS_DBCS_SUPPORT
|
||
|
DWORD dwChar;
|
||
|
if( bIsLeadByte ) {
|
||
|
// Iterate over the lead byte
|
||
|
pszSrc++;
|
||
|
strLen--;
|
||
|
c = *pszSrc;
|
||
|
// Setup the double byte word so that :
|
||
|
// High byte is the lead byte
|
||
|
// Low byte is the next byte in the byte stream (this byte
|
||
|
// will be used to access the extended lut table)
|
||
|
dwChar = (DWORD) ( (cindex << 8) | ((TUCHAR) c) );
|
||
|
} else
|
||
|
dwChar = (DWORD) (TUCHAR) c;
|
||
|
|
||
|
if( !(ple = FindExtendedLUTEntry( c, pwfc )) ) {
|
||
|
ple = &pwfc->listLUT[pwfc->LUTIndex+MAX_DIRECT_LUT];
|
||
|
if( ! UpdateReducedFontListDBCS( dwChar, ple, pwfc ) ) {
|
||
|
DeleteLUTEntry( ple );
|
||
|
continue;
|
||
|
}
|
||
|
ple->glyph = c;
|
||
|
pwfc->LUTIndex++;
|
||
|
}
|
||
|
#else
|
||
|
if( !(ple = FindExtendedLUTEntry( c, pwfc )) ) {
|
||
|
ple = &pwfc->listLUT[pwfc->LUTIndex+MAX_DIRECT_LUT];
|
||
|
if( ! UpdateReducedFontList( c, ple, pwfc ) ) {
|
||
|
DeleteLUTEntry( ple );
|
||
|
continue;
|
||
|
}
|
||
|
ple->glyph = c;
|
||
|
pwfc->LUTIndex++;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
*usDst++ = ple->listNum;
|
||
|
}
|
||
|
// zero terminate
|
||
|
*usDst = 0;
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* UpdateReducedFontList
|
||
|
*
|
||
|
* Calls wglUseFontOutlines() to create a command list for the supplied
|
||
|
* character.
|
||
|
*
|
||
|
* Allocates memory for the characters glyphmetrics as well
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
static BOOL
|
||
|
UpdateReducedFontList( TCHAR c, LISTENTRY *ple, WglFontContext *pwfc )
|
||
|
{
|
||
|
ple->lpgmf = (LPGLYPHMETRICSFLOAT) LocalAlloc( LMEM_FIXED,
|
||
|
sizeof( GLYPHMETRICSFLOAT ) );
|
||
|
|
||
|
if( !ple->lpgmf ) {
|
||
|
SS_ALLOC_FAILURE( "UpdateReducedFontList" );
|
||
|
return FAILURE;
|
||
|
}
|
||
|
|
||
|
// get next list # for this glyph.
|
||
|
ple->listNum = (USHORT) glGenLists( 1 );
|
||
|
if( !ple->listNum ) {
|
||
|
SS_WARNING( "glGenLists failed\n" );
|
||
|
return FAILURE;
|
||
|
}
|
||
|
|
||
|
if( !wglUseFontOutlines(pwfc->hdc, (TUCHAR) c, 1, ple->listNum,
|
||
|
pwfc->chordalDeviation,
|
||
|
pwfc->extrusion,
|
||
|
pwfc->type, ple->lpgmf) ) {
|
||
|
SS_WARNING( "wglUseFontOutlines failed\n" );
|
||
|
return FAILURE;
|
||
|
}
|
||
|
return SUCCESS;
|
||
|
}
|
||
|
|
||
|
#ifdef SS_DBCS_SUPPORT
|
||
|
|
||
|
// Modified version of UpdateReducedFontList that handles dbcs characters.
|
||
|
// This is a *patch* (as is all the SS_DBCS_SUPPORT stuff) to handle dbcs
|
||
|
// characters without jeopardizing SUR release stability. Later, the functions
|
||
|
// can be consolidated.
|
||
|
|
||
|
static BOOL
|
||
|
UpdateReducedFontListDBCS( DWORD c, LISTENTRY *ple, WglFontContext *pwfc )
|
||
|
{
|
||
|
ple->lpgmf = (LPGLYPHMETRICSFLOAT) LocalAlloc( LMEM_FIXED,
|
||
|
sizeof( GLYPHMETRICSFLOAT ) );
|
||
|
|
||
|
if( !ple->lpgmf ) {
|
||
|
SS_ALLOC_FAILURE( "UpdateReducedFontList" );
|
||
|
return FAILURE;
|
||
|
}
|
||
|
|
||
|
// get next list # for this glyph.
|
||
|
ple->listNum = (USHORT) glGenLists( 1 );
|
||
|
if( !ple->listNum ) {
|
||
|
SS_WARNING( "glGenLists failed\n" );
|
||
|
return FAILURE;
|
||
|
}
|
||
|
|
||
|
if( !wglUseFontOutlines(pwfc->hdc, c, 1, ple->listNum,
|
||
|
pwfc->chordalDeviation,
|
||
|
pwfc->extrusion,
|
||
|
pwfc->type, ple->lpgmf) ) {
|
||
|
SS_WARNING( "wglUseFontOutlines failed\n" );
|
||
|
return FAILURE;
|
||
|
}
|
||
|
return SUCCESS;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* FindExtendedLUTEntry
|
||
|
*
|
||
|
* Searches through the extended character LUT, and returns ptr to the
|
||
|
* char's info if found, otherwise NULL.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
static LISTENTRY*
|
||
|
FindExtendedLUTEntry( TCHAR c, WglFontContext *pwfc )
|
||
|
{
|
||
|
int i;
|
||
|
LISTENTRY *ple = pwfc->listLUT + MAX_DIRECT_LUT;
|
||
|
|
||
|
for( i = 0; i < pwfc->LUTIndex; i ++, ple++ ) {
|
||
|
if( ple->glyph == c )
|
||
|
return ple;
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DrawString
|
||
|
*
|
||
|
* Draws string by calling cmd list for each char.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
void
|
||
|
DrawString( USHORT *string,
|
||
|
int strLen,
|
||
|
WglFontContext *pwfc )
|
||
|
{
|
||
|
if( !pwfc )
|
||
|
return;
|
||
|
|
||
|
glCallLists(strLen, GL_UNSIGNED_SHORT, (GLushort *) string);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* GetStringExtent
|
||
|
*
|
||
|
* Calculate a string's origin and extent in world coordinates.
|
||
|
*
|
||
|
* The origin is determined by the first character's location, and thereafter
|
||
|
* each char's location is determined by adding the previous char's cellInc
|
||
|
* values.
|
||
|
*
|
||
|
* This will work for all string orientations.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
int
|
||
|
GetStringExtent( LPTSTR szString,
|
||
|
POINTFLOAT *extent,
|
||
|
POINTFLOAT *origin,
|
||
|
WglFontContext *pwfc )
|
||
|
{
|
||
|
|
||
|
int len, strLen;
|
||
|
TCHAR *c;
|
||
|
TUCHAR cindex;
|
||
|
LPGLYPHMETRICSFLOAT lpgmf;
|
||
|
POINTFLOAT cellOrigin = {0.0f, 0.0f};
|
||
|
POINTFLOAT extentOrigin = {FLT_MAX, -FLT_MAX};
|
||
|
POINTFLOAT extentLowerRight = {-FLT_MAX, FLT_MAX};
|
||
|
POINTFLOAT boxOrigin, boxLowerRight;
|
||
|
LISTENTRY *listLUT, *ple;
|
||
|
#ifdef SS_DBCS_SUPPORT
|
||
|
BOOL bIsLeadByte;
|
||
|
#endif
|
||
|
|
||
|
if( !pwfc )
|
||
|
return 0;
|
||
|
|
||
|
listLUT = pwfc->listLUT;
|
||
|
|
||
|
extent->x = extent->y = 0.0f;
|
||
|
origin->x = origin->y = 0.0f;
|
||
|
len = strLen = lstrlen( szString );
|
||
|
if( !len )
|
||
|
return 0; // otherwise extents will be calc'd erroneously
|
||
|
|
||
|
c = szString;
|
||
|
|
||
|
for( ; strLen; strLen--, c++ ) {
|
||
|
cindex = (TUCHAR) *c;
|
||
|
#ifdef SS_DBCS_SUPPORT
|
||
|
if( bIsLeadByte = IsDBCSLeadByte(cindex) ) {
|
||
|
// iterate over lead byte
|
||
|
c++;
|
||
|
strLen--;
|
||
|
cindex = (TUCHAR) *c;
|
||
|
}
|
||
|
if( !bIsLeadByte && (cindex < MAX_DIRECT_LUT) )
|
||
|
#else
|
||
|
if( cindex < MAX_DIRECT_LUT )
|
||
|
#endif
|
||
|
ple = &listLUT[cindex];
|
||
|
else
|
||
|
ple = FindExtendedLUTEntry( *c, pwfc );
|
||
|
|
||
|
lpgmf = ple->lpgmf;
|
||
|
|
||
|
if( !lpgmf )
|
||
|
// Memory must be running low, but keep going
|
||
|
continue;
|
||
|
|
||
|
// calc global position of this char's BlackBox (this is
|
||
|
// 'upper left')
|
||
|
boxOrigin.x = cellOrigin.x + lpgmf->gmfptGlyphOrigin.x;
|
||
|
boxOrigin.y = cellOrigin.y + lpgmf->gmfptGlyphOrigin.y;
|
||
|
|
||
|
// calc lower right position
|
||
|
boxLowerRight.x = boxOrigin.x + lpgmf->gmfBlackBoxX;
|
||
|
boxLowerRight.y = boxOrigin.y - lpgmf->gmfBlackBoxY;
|
||
|
|
||
|
// compare against the current bounding box
|
||
|
|
||
|
if( boxOrigin.x < extentOrigin.x )
|
||
|
extentOrigin.x = boxOrigin.x;
|
||
|
if( boxOrigin.y > extentOrigin.y )
|
||
|
extentOrigin.y = boxOrigin.y;
|
||
|
if( boxLowerRight.x > extentLowerRight.x )
|
||
|
extentLowerRight.x = boxLowerRight.x;
|
||
|
if( boxLowerRight.y < extentLowerRight.y )
|
||
|
extentLowerRight.y = boxLowerRight.y;
|
||
|
|
||
|
// set global position of next cell
|
||
|
cellOrigin.x = cellOrigin.x + lpgmf->gmfCellIncX;
|
||
|
cellOrigin.y = cellOrigin.y + lpgmf->gmfCellIncY;
|
||
|
}
|
||
|
|
||
|
// Check for possible total lack of glyphmetric info
|
||
|
if( extentOrigin.x == FLT_MAX ) {
|
||
|
// Can assume if this value is still maxed out that all glyphmetric
|
||
|
// info was NULL
|
||
|
origin->x = origin->y = 0.0f;
|
||
|
extent->x = extent->y = 0.0f;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
*origin = extentOrigin;
|
||
|
extent->x = extentLowerRight.x - extentOrigin.x;
|
||
|
extent->y = extentOrigin.y - extentLowerRight.y;
|
||
|
return len;
|
||
|
}
|