fonttext.cpp

来自「Windows 图形编程 书籍」· C++ 代码 · 共 1,012 行 · 第 1/2 页

CPP
1,012
字号
//-----------------------------------------------------------------------------------//
//              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 + =
减小字号Ctrl + -
显示快捷键?