?? fonttext.cpp
字號:
//-----------------------------------------------------------------------------------//
// Windows Graphics Programming: Win32 GDI and DirectDraw //
// ISBN 0-13-086985-6 //
// //
// Written by Yuan, Feng www.fengyuan.com //
// Copyright (c) 2000 by Hewlett-Packard Company www.hp.com //
// Published by Prentice Hall PTR, Prentice-Hall, Inc. www.phptr.com //
// //
// FileName : fonttext.cpp //
// Description: Generic font and text routines and classes //
// Version : 1.00.000, May 31, 2000 //
//-----------------------------------------------------------------------------------//
#define STRICT
#define WIN32_LEAN_AND_MEAN
#pragma pack(push, 4)
#include <windows.h>
#pragma pack(pop)
#include <tchar.h>
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include "fonttext.h"
// convert point size to logical coordinate space size
int PointSizetoLogical(HDC hDC, int points, int divisor)
{
POINT P[2] = // two POINTs in device space whose distance is the needed height
{
{ 0, 0 },
{ 0, ::GetDeviceCaps(hDC, LOGPIXELSY) * points }
};
DPtoLP(hDC, P, 2); // map device coordinate to logical size
return abs(P[1].y - P[0].y) / 72 / divisor;
}
// Create a font as large as EM square size for accurate metrics
HFONT CreateReferenceFont(HFONT hFont, int & emsquare)
{
LOGFONT lf;
OUTLINETEXTMETRIC otm[3]; // big enough for the strings
HDC hDC = GetDC(NULL);
HGDIOBJ hOld = SelectObject(hDC, hFont);
int size = GetOutlineTextMetrics(hDC, sizeof(otm), otm);
SelectObject(hDC, hOld);
ReleaseDC(NULL, hDC);
if ( size ) // TrueType
{
GetObject(hFont, sizeof(lf), & lf);
emsquare = otm[0].otmEMSquare; // get EM square size
lf.lfHeight = - emsquare; // font size for 1:1 mapping
lf.lfWidth = 0; // original proportion
return CreateFontIndirect(&lf);
}
else
return NULL;
}
// justify text string within a left..right margin
BOOL TextOutJust(HDC hDC, int left, int right, int y, LPCTSTR lpStr, int nCount, bool bAllowNegative, TCHAR cBreakChar)
{
SIZE size;
SetTextJustification(hDC, 0, 0);
GetTextExtentPoint32(hDC, lpStr, nCount, & size);
int nBreak = 0;
for (int i=0; i<nCount; i++)
if ( lpStr[i]==cBreakChar )
nBreak ++;
int breakextra = right - left - size.cx;
if ( (breakextra<0) && ! bAllowNegative )
breakextra =0;
SetTextJustification(hDC, breakextra, nBreak);
return TextOut(hDC, left, y, lpStr, nCount);
}
// ABC extent of a text string
// ( A0, B0, C0 ) + ( A1, B1, C1 ) = ( A0, B0+C0+A1+B1, C1 }
BOOL GetTextABCExtent(HDC hDC, LPCTSTR lpString, int cbString, long * pHeight, ABC * pABC)
{
SIZE size;
if ( ! GetTextExtentPoint32(hDC, lpString, cbString, & size) )
return FALSE;
* pHeight = size.cy;
pABC->abcB = size.cx;
ABC abc;
GetCharABCWidths(hDC, lpString[0], lpString[0], & abc); // first
pABC->abcB -= abc.abcA;
pABC->abcA = abc.abcA;
GetCharABCWidths(hDC, lpString[cbString-1], lpString[cbString-1], & abc); // last
pABC->abcB -= abc.abcC;
pABC->abcC = abc.abcC;
return TRUE;
}
BOOL GetOpaqueBox(HDC hDC, LPCTSTR lpString, int cbString, RECT * pRect, int x, int y)
{
long height;
ABC abc;
if ( ! GetTextABCExtent(hDC, lpString, cbString, & height, & abc) )
return FALSE;
switch ( GetTextAlign(hDC) & (TA_LEFT | TA_RIGHT | TA_CENTER) )
{
case TA_LEFT : break;
case TA_RIGHT : x -= abc.abcB; break;
case TA_CENTER : x -= abc.abcB/2; break;
default: assert(false);
}
switch ( GetTextAlign(hDC) & (TA_TOP | TA_BASELINE | TA_BOTTOM) )
{
case TA_TOP : break;
case TA_BOTTOM : y = - height; break;
case TA_BASELINE:
{
TEXTMETRIC tm;
GetTextMetrics(hDC, & tm);
y = - tm.tmAscent;
}
break;
default: assert(false);
}
pRect->left = x + min(abc.abcA, 0);
pRect->right = x + abc.abcA + abc.abcB + max(abc.abcC, 0);
pRect->top = y;
pRect->bottom = y + height;
return TRUE;
}
// Pixel-level precise text alignment
BOOL PreciseTextOut(HDC hDC, int x, int y, LPCTSTR lpString, int cbString)
{
long height;
ABC abc;
if ( GetTextABCExtent(hDC, lpString, cbString, & height, & abc) )
switch ( GetTextAlign(hDC) & (TA_LEFT | TA_RIGHT | TA_CENTER) )
{
case TA_LEFT : x -= abc.abcA; break;
case TA_RIGHT : x += abc.abcC; break;
case TA_CENTER: x -= (abc.abcA-abc.abcC)/2; break;
}
return TextOut(hDC, x, y, lpString, cbString);
}
KGlyph::~KGlyph(void)
{
if ( m_pPixels )
{
delete [] m_pPixels;
m_pPixels = NULL;
}
}
DWORD KGlyph::GetGlyph(HDC hDC, UINT uChar, UINT uFormat, const MAT2 * pMat2)
{
MAT2 mat2;
if ( pMat2==NULL )
{
memset(&mat2, 0, sizeof(mat2));
mat2.eM11.value = 1;
mat2.eM22.value = 1;
pMat2 = & mat2;
}
// query size
m_nDataSize = GetGlyphOutline(hDC, uChar, uFormat, & m_metrics, 0, NULL, pMat2);
if ( (m_nDataSize==0) || (m_nDataSize==GDI_ERROR) )
return m_nDataSize;
if ( m_pPixels && (m_nDataSize > m_nAllocSize) ) // deallocate if too small
{
delete m_pPixels;
m_pPixels = NULL;
}
if ( m_pPixels==NULL )
{
m_pPixels = new BYTE[m_nDataSize]; // new buffer allocation
assert(m_pPixels);
m_nAllocSize = m_nDataSize;
}
m_uFormat = uFormat;
// real data
m_nDataSize = GetGlyphOutline(hDC, uChar, uFormat, & m_metrics, m_nAllocSize, m_pPixels, pMat2);
return m_nDataSize;
}
typedef struct
{
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[256];
} BITMAPINFO8BPP;
BOOL KGlyph::DrawGlyphROP(HDC hDC, int x, int y, DWORD rop, COLORREF crBack, COLORREF crFore)
{
int levels;
switch (m_uFormat & 0x0F )
{
case GGO_BITMAP: levels = 2; break;
case GGO_GRAY2_BITMAP: levels = 5; break;
case GGO_GRAY4_BITMAP: levels = 17; break;
case GGO_GRAY8_BITMAP: levels = 65; break;
default:
return FALSE;
}
BITMAPINFO8BPP bmi;
memset(& bmi, 0, sizeof(bmi));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = m_metrics.gmBlackBoxX;
bmi.bmiHeader.biHeight = 0 - m_metrics.gmBlackBoxY; // top-down DIB
bmi.bmiHeader.biPlanes = 1;
if ( levels==2 )
{
bmi.bmiHeader.biBitCount = 1;
bmi.bmiColors[0].rgbRed = GetRValue(crBack);
bmi.bmiColors[0].rgbGreen = GetGValue(crBack);
bmi.bmiColors[0].rgbBlue = GetBValue(crBack);
bmi.bmiColors[1].rgbRed = GetRValue(crFore);
bmi.bmiColors[1].rgbGreen = GetGValue(crFore);
bmi.bmiColors[1].rgbBlue = GetBValue(crFore);
}
else
{
bmi.bmiHeader.biBitCount = 8;
// KColor back(crBack); back.ToHLS();
// KColor fore(crFore); fore.ToHLS();
for (int i=0; i<levels; i++)
{
// KColor m;
// m.hue = (back.hue * (levels-i-1) + fore.hue * i ) / (levels-1);
// m.lightness = (back.lightness * (levels-i-1) + fore.lightness * i ) / (levels-1);
// m.saturation = (back.saturation * (levels-i-1) + fore.saturation * i ) / (levels-1);
// m.ToRGB();
// bmi.bmiColors[i].rgbRed = m.red;
// bmi.bmiColors[i].rgbGreen = m.green;
// bmi.bmiColors[i].rgbBlue = m.blue;
bmi.bmiColors[i].rgbRed = (GetRValue(crBack) * (levels-i-1) + GetRValue(crFore) * i ) / (levels-1);
bmi.bmiColors[i].rgbGreen = (GetGValue(crBack) * (levels-i-1) + GetGValue(crFore) * i ) / (levels-1);
bmi.bmiColors[i].rgbBlue = (GetBValue(crBack) * (levels-i-1) + GetBValue(crFore) * i ) / (levels-1);
}
}
return StretchDIBits(hDC,
x, y, m_metrics.gmBlackBoxX, m_metrics.gmBlackBoxY,
0, 0, m_metrics.gmBlackBoxX, m_metrics.gmBlackBoxY,
m_pPixels,
(const BITMAPINFO *) & bmi,
DIB_RGB_COLORS, rop);
}
// assuming TA_LEFT | TA_BASELINE alignment
BOOL KGlyph::DrawGlyph(HDC hDC, int x, int y, int & dx, int & dy)
{
dx = m_metrics.gmCellIncX; // advancement
dy = m_metrics.gmCellIncY;
if ( GetBkMode(hDC)==OPAQUE ) // may overlap with next glyph
{
// black box is not big enough, need to calculate actual background box
RECT opaque;
TEXTMETRIC tm;
GetTextMetrics(hDC, & tm);
opaque.left = min(x, x + m_metrics.gmptGlyphOrigin.x);
opaque.right = max(x + m_metrics.gmCellIncX, x + m_metrics.gmptGlyphOrigin.x + (int) m_metrics.gmBlackBoxX);
opaque.top = y - tm.tmAscent;
opaque.bottom = y + tm.tmDescent;
HBRUSH hBackBrush = CreateSolidBrush(GetBkColor(hDC));
FillRect(hDC, & opaque, hBackBrush);
DeleteObject(hBackBrush);
}
// if foreground color, use pen color, otherwise donot change desitnation
HBRUSH hBrush = CreateSolidBrush(GetTextColor(hDC));
HBRUSH hOld = (HBRUSH) SelectObject(hDC, hBrush);
BOOL rslt = DrawGlyphROP(hDC,
x + m_metrics.gmptGlyphOrigin.x,
y - m_metrics.gmptGlyphOrigin.y,
0xE20746, // D^(S&(P^D)) -> if (S) P else D
RGB(0, 0, 0), // 0 -> 0
RGB(0xFF, 0xFF, 0xFF)); // 1 -> 1
SelectObject(hDC, hOld);
DeleteObject(hBrush);
return rslt;
}
FIXED MakeFixed(double value)
{
long val = (long) (value * 65536);
return * (FIXED *) & val;
}
BOOL OutlineTextOut(HDC hDC, int x, int y, const TCHAR * str, int count)
{
if ( count<0 )
count = _tcslen(str);
KGlyph glyph;
KGlyphOutline<512> outline;
// MAT2 mat2;
// mat2.eM11 = MakeFixed(cos(0.1));
// mat2.eM12 = MakeFixed(sin(0.1));
// mat2.eM21 = MakeFixed(-sin(0.1));
// mat2.eM22 = MakeFixed(cos(0.1));
while ( count>0 )
{
if ( glyph.GetGlyph(hDC, * str, GGO_NATIVE/*, & mat2*/)>0 )
if ( outline.DecodeOutline(glyph) )
outline.Draw(hDC, x, y);
x += glyph.m_metrics.gmCellIncX;
y += glyph.m_metrics.gmCellIncY;
str ++;
count --;
}
return TRUE;
}
BOOL BitmapTextOut(HDC hDC, int x, int y, const TCHAR * str, int count, int format)
{
if ( count<0 )
count = _tcslen(str);
KGlyph glyph;
while ( count>0 )
{
int dx=0, dy=0;
if ( glyph.GetGlyph(hDC, * str, format)>0 )
glyph.DrawGlyph(hDC, x, y, dx, dy);
x += dx;
y += dy;
str ++;
count --;
}
return TRUE;
}
BOOL BitmapTextOutROP(HDC hDC, int x, int y, const TCHAR * str, int count, DWORD rop)
{
if ( count<0 )
count = _tcslen(str);
KGlyph glyph;
COLORREF crBack = GetBkColor(hDC);
COLORREF crFore = GetTextColor(hDC);
while ( count>0 )
{
if ( glyph.GetGlyph(hDC, * str, GGO_BITMAP)>0 )
glyph.DrawGlyphROP(hDC,
x + glyph.m_metrics.gmptGlyphOrigin.x,
y - glyph.m_metrics.gmptGlyphOrigin.y,
rop, crBack, crFore);
x += glyph.m_metrics.gmCellIncX;
y += glyph.m_metrics.gmCellIncY;
str ++;
count --;
}
return TRUE;
}
/////////////////////////////////////////////////////////////
// pixelsize: in logical space
BOOL KTextFormator::SetupPixel(HDC hDC, HFONT hFont, float pixelsize)
{
OUTLINETEXTMETRIC otm[3]; // big enough for the strings
if ( GetOutlineTextMetrics(hDC, sizeof(otm), otm)==0 )
return FALSE;
LOGFONT lf;
GetObject(hFont, sizeof(lf), & lf);
int emsquare= otm[0].otmEMSquare; // get EM square size
lf.lfHeight = - emsquare; // font size for 1:1 mapping
lf.lfWidth = 0; // original proportion
HFONT hRefFont = CreateFontIndirect(&lf);
HDC hRefDC = CreateCompatibleDC(hDC);
HGDIOBJ hOld = SelectObject(hRefDC, hRefFont);
int nCharWidth[MaxCharNo];
GetCharWidth(hRefDC, 0, MaxCharNo-1, nCharWidth);
if ( GetOutlineTextMetrics(hRefDC, sizeof(otm), otm)==0 )
return FALSE;
SelectObject(hRefDC, hOld);
DeleteObject(hRefDC);
DeleteObject(hRefFont);
m_fHeight = otm[0].otmTextMetrics.tmHeight * pixelsize / emsquare;
m_fLinespace = ( otm[0].otmTextMetrics.tmHeight + otm[0].otmTextMetrics.tmExternalLeading) * pixelsize / emsquare;
for (int i=0; i<MaxCharNo; i++)
m_fCharWidth[i] = ((float) nCharWidth[i]) * pixelsize / emsquare;
return TRUE;
}
BOOL KTextFormator::Setup(HDC hDC, HFONT hFont, float pointsize)
{
return SetupPixel(hDC, hFont, pointsize * GetDeviceCaps(hDC, LOGPIXELSY) / 72);
}
BOOL KTextFormator::GetTextExtent(HDC hdc, LPCTSTR lpString, int cbString, float & width, float & height)
{
if ( cbString<=0 )
cbString = _tcslen(lpString);
width = 0;
for (int i=0; i<cbString; i++)
width = width + m_fCharWidth[lpString[i]];
height = m_fHeight;
return TRUE;
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -