truetype.cpp
来自「Windows 图形编程 书籍」· C++ 代码 · 共 1,028 行 · 第 1/2 页
CPP
1,028 行
// scale points
for (int i=0; i<m_len; i++)
sc.Map(m_Point[i]);
// em square
if ( mark )
{
TCHAR temp[64];
wsprintf(temp, "Glyph: %d, Size %d Inst %d EM square %d",
m_glyphindex, m_glyphsize, m_instrsize, emsquare);
TextOut(hDC, x, (int)(y-emsquare*scale)-40, temp, _tcslen(temp));
if ( (mark==IDM_VIEW_LARGE) || (mark==IDM_VIEW_METRICS) )
{
// bounding box
RECT r = { (int) (x + m_bound.left * scale),
(int) (y - m_bound.bottom* scale),
(int) (x + m_bound.right * scale),
(int) (y - m_bound.top * scale) };
FrameRect(hDC, & r, GetSysColorBrush(COLOR_ACTIVECAPTION));
}
if ( mark!=IDM_VIEW_METRICS )
{
// em square
HPEN hPen = CreatePen(PS_DOT, 1, 0);
HGDIOBJ hOld = SelectObject(hDC, hPen);
for (int i=0; i<=emsquare; i+=(emsquare/16))
{
sc.Line(hDC, i, 0, i, emsquare);
sc.Line(hDC, 0, i, emsquare, i);
}
SelectObject(hDC, hOld);
DeleteObject(hOld);
}
if ( mark==IDM_VIEW_METRICS )
{
// descender
sc.Line(hDC, - emsquare/10, m_Descender, emsquare*11/10, m_Descender);
// base
sc.Line(hDC, - emsquare/10, 0, emsquare*11/10, 0);
// ascender
sc.Line(hDC, - emsquare/10, m_Ascender , emsquare*11/10, m_Ascender );
sc.Line(hDC, m_bound.left - m_lsb, m_Descender,
m_bound.left - m_lsb, m_Ascender);
sc.Line(hDC, m_bound.left - m_lsb + m_advancewidth, m_Descender,
m_bound.left - m_lsb + m_advancewidth, m_Ascender);
}
}
i = 0;
int x0, y0;
// curve
while ( i<m_len )
{
if ( m_Flag[i] & FLAG_OPEN )
{
x0 = m_Point[i].x; y0 = m_Point[i].y; MoveToEx(hDC, x0, y0, NULL);
}
else if ( m_Flag[i] & FLAG_ON )
{
x0 = m_Point[i].x; y0 = m_Point[i].y; LineTo(hDC, x0, y0);
}
else // off on, convert to cubic Bezier curve
{
// p0 p1 p2 -> p0 (p0+2p1)/3 (2p1+p2)/3, p2
Bezier(hDC, x0, y0, m_Point[i].x, m_Point[i].y, m_Point[i+1].x, m_Point[i+1].y);
i ++;
}
i ++;
}
// mark control points
if ( mark && (mark!=IDM_VIEW_METRICS) )
{
int seq = 0;
for (i=0; i<m_len; i++)
{
if ( (m_Flag[i] & FLAG_EXTRA)==0 )
{
if ( m_Flag[i] & FLAG_ON )
Mark(hDC, m_Point[i].x, m_Point[i].y, RGB(0xFF, 0, 0));
else
Mark(hDC, m_Point[i].x, m_Point[i].y, RGB(0, 0, 0xFF));
if ( mark==3 )
{
TCHAR temp[4];
wsprintf(temp, "%d", seq);
TextOut(hDC, m_Point[i].x+5, m_Point[i].y-5, temp, _tcslen(temp));
}
seq ++;
}
}
}
return TRUE;
}
};
int KTrueType::DecodeGlyph(int index, KCurve & curve, XFORM * xm) const
{
const GlyphHeader * pHeader = GetGlyph(index);
if ( pHeader==NULL )
{
// assert(false);
return 0;
}
int nContour = (short) reverse(pHeader->numberOfContours);
if ( nContour<0 )
{
return DecodeCompositeGlyph(pHeader+1, curve); // after the header
}
if ( nContour==0 )
return 0;
curve.SetBound(reverse((WORD)pHeader->xMin), reverse((WORD)pHeader->yMin),
reverse((WORD)pHeader->xMax), reverse((WORD)pHeader->yMax));
const USHORT * pEndPoint = (const USHORT *) (pHeader+1);
int nPoints = reverse(pEndPoint[nContour-1]) + 1; // endpoint of last contour + 1
int nInst = reverse(pEndPoint[nContour]); // instructon length
curve.m_glyphindex = index;
curve.m_glyphsize = (int) GetGlyph(index+1) - (int) GetGlyph(index);
curve.m_Ascender = m_Ascender;
curve.m_Descender = m_Descender;
curve.m_LineGap = m_LineGap;
GetMetrics(index, curve.m_advancewidth, curve.m_lsb);
if ( curve.m_glyphsize==0 )
return 0;
curve.m_instrsize = nInst;
const BYTE * pFlag = (const BYTE *) & pEndPoint[nContour] + 2 + nInst; // first byte in flag
const BYTE * pX = pFlag;
int xlen = 0;
for (int i=0; i<nPoints; i++, pX++)
{
int unit = 0;
switch ( pX[0] & G_XMASK )
{
case G_XADDBYTE:
case G_XSUBBYTE:
unit = 1;
break;
case G_XADDINT:
unit = 2;
}
if ( pX[0] & G_REPEAT )
{
xlen += unit * (pX[1]+1);
i += pX[1];
pX ++;
}
else
xlen += unit;
}
const BYTE * pY = pX + xlen;
int x = 0;
int y = 0;
i = 0;
BYTE flag = 0;
int rep = 0;
for (int j=0; j<nContour; j++)
{
int limit = reverse(pEndPoint[j]);
while ( i<=limit )
{
if ( rep==0 )
{
flag = * pFlag++;
rep = 1;
if ( flag & G_REPEAT )
rep += * pFlag ++;
}
int dx = 0, dy = 0;
switch ( flag & G_XMASK )
{
case G_XADDBYTE: dx = pX[0]; pX ++; break;
case G_XSUBBYTE: dx = - pX[0]; pX ++; break;
case G_XADDINT: dx = (signed short )( (pX[0] << 8) + pX[1]); pX+=2;
}
switch ( flag & G_YMASK )
{
case G_YADDBYTE: dy = pY[0]; pY ++; break;
case G_YSUBBYTE: dy = - pY[0]; pY ++; break;
case G_YADDINT: dy = (signed short )( (pY[0] << 8) + pY[1]); pY+=2;
}
x += dx;
y += dy;
assert(abs(x)<16384);
assert(abs(y)<16384);
if ( xm )
curve.Add((int) ( x * xm->eM11 + y * xm->eM21 + xm->eDx ),
(int) ( x * xm->eM12 + y * xm->eM22 + xm->eDy ),
(flag & G_ONCURVE) ? KCurve::FLAG_ON : 0);
else
curve.Add(x, y, (flag & G_ONCURVE) ? KCurve::FLAG_ON : 0);
rep --;
i ++;
}
curve.Close();
}
return curve.GetLength();
}
int KTrueType::GetOutline(int index, KCurve & curve) const
{
curve.Reset();
return DecodeGlyph(index, curve, NULL);
}
void KTrueType::DrawTTGlyph(HDC hDC, int x, int y, int index, double scale, int mark)
{
KCurve curve;
GetOutline(index, curve);
curve.Draw(hDC, x, y, scale, mark, m_unitsPerEm);
}
BOOL LogTableDirectory(KTrueType & tt, HINSTANCE hInstance, const char * FaceName)
{
KLogWindow * pLog = new KLogWindow;
if ( pLog==NULL )
return FALSE;
pLog->Create(hInstance, "Table Directory", LoadIcon(hInstance, MAKEINTRESOURCE(IDI_FONT)));
pLog->Log("%s \r\n", FaceName);
int no = tt.GetTableNo();
pLog->Log("%d tables\r\n\r\n", no);
pLog->Log("Tag CheckSum Offset Length\r\n");
pLog->Log("----------------------------------------\r\n");
for (int i=0; i<no; i++)
{
const TableEntry * pEntry = tt.GetTableEntry(i);
unsigned long offset = reverse(pEntry->offset);
unsigned long length = reverse(pEntry->length);
pLog->Log("%c%c%c%c %08X %08X %8x %8x\r\n",
pEntry->tag[0], pEntry->tag[1],
pEntry->tag[2], pEntry->tag[3],
* ((DWORD *) pEntry->tag),
pEntry->checkSum,
offset, length);
}
return TRUE;
}
/*
HBITMAP GetBackgroundBitmap(HWND hWnd)
{
ASSERT(::IsWindow( hWnd));
HWND hParentWnd = NULL;
HBITMAP hBitmap = NULL;
RECT rcWnd;
RECT rcParentWnd;
hParentWnd = ::GetParent( hWnd);
if (!hParentWnd)
return NULL;
::GetWindowRect( hWnd, &rcWnd);
rcParentWnd = rcWnd;
::ScreenToClient( hParentWnd, (LPPOINT)&rcParentWnd);
::ScreenToClient( hParentWnd, ((LPPOINT)&rcParentWnd)+1);
::GetClientRect( hWnd, &rcWnd);
HDC hParentDC = GetDC( hParentWnd);
if(!hParentDC)
return NULL;
HDC hBkgDC = ::CreateCompatibleDC( hParentDC);
if (!hBkgDC)
{
::ReleaseDC( hParentWnd, hParentDC);
::DeleteDC( hParentDC);
return NULL;
}
hBitmap = ::CreateCompatibleBitmap( hParentDC, rcWnd.right-rcWnd.left,
rcWnd.bottom-rcWnd.top);
HBITMAP hOldBitmap = (HBITMAP)::SelectObject( hBkgDC, hBitmap);
::SendMessage( hParentWnd, WM_ERASEBKGND, (WPARAM)hParentDC, 0L);
::BitBlt( hBkgDC, rcWnd.left, rcWnd.top, rcWnd.right-rcWnd.left,
rcWnd.bottom-rcWnd.top, hParentDC, rcParentWnd.left, rcParentWnd.top,
SRCCOPY);
::SelectObject( hBkgDC, hOldBitmap);
::DeleteObject( hOldBitmap);
::DeleteDC( hBkgDC);
::ReleaseDC( hParentWnd, hParentDC);
::DeleteDC( hParentDC);
return hBitmap;
}
*/
// Composite Glyph
typedef enum
{
ARG_1_AND_2_ARE_WORDS = 0x0001, // If this is set, the arguments are words; otherwise, they are bytes.
ARGS_ARE_XY_VALUES = 0x0002, // If this is set, the arguments are xy values; otherwise, they are points.
ROUND_XY_TO_GRID = 0x0004, // For the xy values if the preceding is true.
WE_HAVE_A_SCALE = 0x0008, // This indicates that there is a simple scale for the component. Otherwise, scale = 1.0.
RESERVED = 0x0010, // This bit is reserved. Set it to 0.
MORE_COMPONENTS = 0x0020, // Indicates at least one more glyph after this one.
WE_HAVE_AN_X_AND_Y_SCALE = 0x0040, // The x direction will use a different scale from the y direction.
WE_HAVE_A_TWO_BY_TWO = 0x0080, // There is a 2 by 2 transformation that will be used to scale the component.
WE_HAVE_INSTRUCTIONS = 0x0100, // Following the last component are instructions for the composite character.
USE_MY_METRICS = 0x0200, // If set, this forces the aw and lsb (and rsb) for the composite to be equal to those from this original glyph. This works for hinted and unhinted characters.
OVERLAP_COMPOUND = 0x0400, // Used by Apple in GX fonts.
SCALED_COMPONENT_OFFSET = 0x0800, // Composite designed to have the component offset scaled (designed for Apple rasterizer).
UNSCALED_COMPONENT_OFFSET = 0x1000, // Composite designed not to have the component offset scaled (designed for the Microsoft TrueType rasterizer).
};
class KDataStream
{
const unsigned char * pData;
public:
unsigned char GetByte(void)
{
return * pData ++;
}
unsigned short GetWord(void)
{
unsigned short rslt = GetByte();
return rslt * 256 + GetByte();
}
KDataStream(const void * pInput)
{
pData = (const unsigned char *) pInput;
}
};
int KTrueType::DecodeCompositeGlyph(const void * pGlyph, KCurve & curve) const
{
KDataStream str(pGlyph);
unsigned flags;
int len = 0;
do
{
flags = str.GetWord();
unsigned glyphIndex = str.GetWord();
// Argument1 and argument2 can be either x and y offsets to be added to the glyph or two point numbers.
// In the latter case, the first point number indicates the point that is to be matched to the new glyph.
// The second number indicates the new glyph's "matched" point. Once a glyph is added, its point numbers
// begin directly after the last glyphs (endpoint of first glyph + 1).
// When arguments 1 and 2 are an x and a y offset instead of points and the bit ROUND_XY_TO_GRID is set to 1,
// the values are rounded to those of the closest grid lines before they are added to the glyph.
// X and Y offsets are described in FUnits.
signed short argument1;
signed short argument2;
if ( flags & ARG_1_AND_2_ARE_WORDS )
{
argument1 = str.GetWord(); // (SHORT or FWord) argument1;
argument2 = str.GetWord(); // (SHORT or FWord) argument2;
}
else
{
argument1 = (signed char) str.GetByte();
argument2 = (signed char) str.GetByte();
}
signed short xscale, yscale, scale01, scale10;
xscale = 1;
yscale = 1;
scale01 = 0;
scale10 = 0;
if ( flags & WE_HAVE_A_SCALE )
{
xscale = str.GetWord();
yscale = xscale; // Format 2.14
}
else if ( flags & WE_HAVE_AN_X_AND_Y_SCALE )
{
xscale = str.GetWord();
yscale = str.GetWord();
}
else if ( flags & WE_HAVE_A_TWO_BY_TWO )
{
xscale = str.GetWord();
scale01 = str.GetWord();
scale10 = str.GetWord();
yscale = str.GetWord();
}
if ( flags & ARGS_ARE_XY_VALUES )
{
XFORM xm;
xm.eDx = (float) argument1;
xm.eDy = (float) argument2;
xm.eM11 = xscale / (float) 16384.0;
xm.eM12 = scale01 / (float) 16384.0;
xm.eM21 = scale10 / (float) 16384.0;
xm.eM22 = yscale / (float) 16384.0;
len += DecodeGlyph(glyphIndex, curve, & xm);
}
else
assert(false);
}
while ( flags & MORE_COMPONENTS );
if ( flags & WE_HAVE_INSTRUCTIONS )
{
unsigned numInstr = str.GetWord();
for (unsigned i=0; i<numInstr; i++)
str.GetByte();
}
// The purpose of USE_MY_METRICS is to force the lsb and rsb to take on a desired value.
// For example, an i-circumflex (Unicode 00ef) is often composed of the circumflex and a dotless-i.
// In order to force the composite to have the same metrics as the dotless-i,
// set USE_MY_METRICS for the dotless-i component of the composite. Without this bit,
// the rsb and lsb would be calculated from the HMTX entry for the composite (or would need to be
// explicitly set with TrueType instructions).
// Note that the behavior of the USE_MY_METRICS operation is undefined for rotated composite components.
return len;
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?