int InitGlyph(wchar_t ch) { DWORD len,i,ii,j,k,t; BYTE *buffer,*bptr; LPVOID ptr; MAT2 mt={}; glyph_char=ch; mt.eM11.value=1; mt.eM22.value=-1; len=GetGlyphOutline(hDC,ch,GGO_GRAY8_BITMAP,&gm,0,0,&mt); if (len==-1) return -1; glyph_buffer=new BYTE[len]; len=GetGlyphOutline(hDC,ch,GGO_GRAY8_BITMAP,&gm,len,glyph_buffer,&mt); if (len==-1) return -1; BITMAPINFOHEADER info={sizeof(info),gm.gmBlackBoxX,gm.gmBlackBoxY,1,32,BI_RGB,0,0,0,0,0}; hBmp=CreateDIBSection(hMemDC,(BITMAPINFO*)&info,DIB_RGB_COLORS,&ptr,0,0); buffer=(BYTE*)ptr; bptr=glyph_buffer; k=(gm.gmBlackBoxX+3)&~3;t=0;ii=0; for (i=0;i<gm.gmBlackBoxY;i++) { for (j=0;j<gm.gmBlackBoxX;j++) { bptr[j]=64-bptr[j]; if (bptr[j]) buffer[0]=buffer[1]=buffer[2]=(bptr[j]<<2)-1; buffer+=4; } bptr+=k; } SelectObject(hMemDC,hBmp); return 0; }
test() { MAT2 glyph_mat = {{0,1},{0,0},{0,0},{0,1}}; int buff_len; GLYPHMETRICS glyph_metrics; unsigned char *buff; buff_len = GetGlyphOutline( NULL, L'a', GGO_BITMAP, &glyph_metrics, 0, NULL, &glyph_mat ); GetGlyphOutline( NULL, L'a', GGO_BITMAP, &glyph_metrics, buff_len, buff, &glyph_mat); }
GraphicsPath *XGraphicsPath::GetCharGlyph(HDC hdc,UINT chr,PointF ptfOffset) { GLYPHMETRICS gm; BYTE *buff; TTPOLYGONHEADER *phdr; TTPOLYCURVE *pcur; PointF ptf[4],ptfLast; int i,j,n,iTotalBytes,iTotalBytesCount,iPolygonBytes,iPolygonBytesCount; const MAT2 m={{0,1},{0,0},{0,0},{0,1}}; GraphicsPath *path=new GraphicsPath(); iTotalBytes=GetGlyphOutline(hdc, chr,GGO_BEZIER|GGO_UNHINTED,&gm,0,NULL,&m); if(iTotalBytes==0||iTotalBytes==GDI_ERROR) return path; buff=new BYTE[iTotalBytes]; iTotalBytesCount=0; GetGlyphOutline(hdc,chr,GGO_BEZIER|GGO_UNHINTED,&gm,iTotalBytes,buff,&m); phdr=(TTPOLYGONHEADER*)buff; while(iTotalBytesCount<iTotalBytes) { ptfLast=PointFx2PointF(phdr->pfxStart,ptfOffset); iPolygonBytes=phdr->cb; iPolygonBytesCount=sizeof(TTPOLYGONHEADER); pcur=(TTPOLYCURVE*)(phdr+1); while(iPolygonBytesCount<iPolygonBytes) { n=pcur->cpfx; switch(pcur->wType) { case TT_PRIM_QSPLINE: break; case TT_PRIM_CSPLINE: for(j=0;j<n;j+=3) { ptf[0]=ptfLast; for(i=0;i<3;++i) ptf[i+1]=PointFx2PointF(pcur->apfx[j+i],ptfOffset); path->AddBezier(ptf[0],ptf[1],ptf[2],ptf[3]); ptfLast=ptf[3]; } break; case TT_PRIM_LINE: for(j=0;j<n;++j) { ptf[0]=PointFx2PointF(pcur->apfx[j],ptfOffset); path->AddLine(ptfLast,ptf[0]); ptfLast=ptf[0]; } break; } iPolygonBytesCount+=sizeof(POINTFX)*n+4; pcur=(TTPOLYCURVE*)((char*)pcur+sizeof(POINTFX)*n+4); } path->CloseFigure(); iTotalBytesCount+=iPolygonBytes; phdr=(TTPOLYGONHEADER*)((char*)phdr+iPolygonBytes); } delete[] buff; return path; }
short CRenderEngine::CalcOffsety(HDC hDc) { MAT2 mat2 = {{0,1}, {0,0}, {0,0}, {0,1}}; GLYPHMETRICS gm={0}; if(GetGlyphOutline(hDc,_T('j'),GGO_GRAY8_BITMAP,&gm,0,NULL,&mat2)!=GDI_ERROR) { short y=gm.gmBlackBoxY; if(GetGlyphOutline(hDc,_T('b'),GGO_GRAY8_BITMAP,&gm,0,NULL,&mat2)!=GDI_ERROR) { return (y-gm.gmBlackBoxY); } } return 0; }
void Font::initGDIFont() { if (!m_platformData.size()) { m_fontMetrics.reset(); m_avgCharWidth = 0; m_maxCharWidth = 0; return; } HWndDC hdc(0); HGDIOBJ oldFont = SelectObject(hdc, m_platformData.hfont()); OUTLINETEXTMETRIC metrics; GetOutlineTextMetrics(hdc, sizeof(metrics), &metrics); TEXTMETRIC& textMetrics = metrics.otmTextMetrics; float ascent = textMetrics.tmAscent; float descent = textMetrics.tmDescent; float lineGap = textMetrics.tmExternalLeading; m_fontMetrics.setAscent(ascent); m_fontMetrics.setDescent(descent); m_fontMetrics.setLineGap(lineGap); m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap)); m_avgCharWidth = textMetrics.tmAveCharWidth; m_maxCharWidth = textMetrics.tmMaxCharWidth; float xHeight = ascent * 0.56f; // Best guess for xHeight if no x glyph is present. GLYPHMETRICS gm; static const MAT2 identity = { 0, 1, 0, 0, 0, 0, 0, 1 }; DWORD len = GetGlyphOutline(hdc, 'x', GGO_METRICS, &gm, 0, 0, &identity); if (len != GDI_ERROR && gm.gmptGlyphOrigin.y > 0) xHeight = gm.gmptGlyphOrigin.y; m_fontMetrics.setXHeight(xHeight); m_fontMetrics.setUnitsPerEm(metrics.otmEMSquare); SelectObject(hdc, oldFont); }
static int getGlyphWidth (HDC dc, int glyphNumber) { GLYPHMETRICS gm; gm.gmCellIncX = 0; GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, 0, &identityMatrix); return gm.gmCellIncX; }
BOOL TrueTypeImport::BuildCharacter(UINT index, float height, BezierShape &shape, float &width, int fontShapeVersion) { assert(hFont); if(!hFont) return 0; // Set up for the font and release it when this function returns FontReady fontRdy(hFont); // allocate space for the bitmap/outline GLYPHMETRICS gm; // init it to prevent UMR in GetGlyphOutline gm.gmBlackBoxX = gm.gmBlackBoxY = gm.gmptGlyphOrigin.x = gm.gmptGlyphOrigin.y = gm.gmCellIncX = gm.gmCellIncY = 0; // Give it an identity matrix MAT2 mat; IdentityMat(&mat); DWORD size = GetGlyphOutline(fontRdy.hdc, index, GGO_NATIVE, &gm, 0, NULL, &mat); if(size != GDI_ERROR && size > 0) { GenericAlloc mem(size); if(!mem.ptr) goto failure; if ((GetGlyphOutline(fontRdy.hdc, index, GGO_NATIVE, &gm, size, mem.ptr, &mat)) != size) goto failure; curSpline = NULL; // reset the current spline pointer if(!CreateCharacterShape((TTPOLYGONHEADER *)mem.ptr, size, shape, fontShapeVersion)) goto failure; // Make sure the height matches the request float scaleFactor = height / 1000.0f; Matrix3 tm = ScaleMatrix(Point3(scaleFactor, scaleFactor, 0.0f)); shape.Transform(tm); width = float(gm.gmCellIncX) * scaleFactor; return TRUE; } // Character wasn't found! failure: width = 0.0f; return FALSE; }
SIZE CRenderEngine::GetDrawSize(HDC hDc,LPTSTR lpszText,UINT uFormat,bool& bHaveOffsety,short& yOffset) { SIZE size={0,0}; if(lpszText==NULL) return size; long slen=_tcslen(lpszText); if(slen==0) return size; MAT2 mat2 = {{0,1}, {0,0}, {0,0}, {0,1}}; GLYPHMETRICS gm={0}; UINT nChar; for(int n=0; n<slen; n++) { nChar=*(lpszText+n); //·ÇÓ¢ÎÄ if(nChar >= 0xa0) { nChar = (((nChar<<8)&0xff00) | (*(lpszText+ ++n) & 0x00ff)); } if(GetGlyphOutline(hDc,nChar,GGO_GRAY8_BITMAP,&gm,0,NULL,&mat2)!=GDI_ERROR) { size.cx+=gm.gmBlackBoxX+m_cxSpacing; size.cy=max(size.cy,gm.gmBlackBoxY); } if(nChar == _T(' ')) { size.cx+=m_cwBlank; } else { switch(nChar) { case _T('j'): case _T('J'): case _T('y'): case _T('g'): bHaveOffsety=true; break; } } } if(bHaveOffsety) { yOffset=CalcOffsety(hDc); size.cy+=yOffset; } else yOffset=0; return size; }
void SimpleFontData::platformInit() { m_scriptCache = 0; m_scriptFontProperties = 0; m_isSystemFont = false; m_syntheticBoldOffset = m_platformData.syntheticBold() ? 1.0f : 0.f; if (m_platformData.useGDI()) return initGDIFont(); HDC hdc = GetDC(0); SaveDC(hdc); cairo_scaled_font_t* scaledFont = m_platformData.scaledFont(); const double metricsMultiplier = cairo_win32_scaled_font_get_metrics_factor(scaledFont) * m_platformData.size(); cairo_win32_scaled_font_select_font(scaledFont, hdc); TEXTMETRIC textMetrics; GetTextMetrics(hdc, &textMetrics); float ascent = textMetrics.tmAscent * metricsMultiplier; float descent = textMetrics.tmDescent * metricsMultiplier; float xHeight = ascent * 0.56f; // Best guess for xHeight for non-Truetype fonts. float lineGap = textMetrics.tmExternalLeading * metricsMultiplier; m_fontMetrics.setAscent(ascent); m_fontMetrics.setDescent(descent); m_fontMetrics.setLineGap(lineGap); m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap)); m_avgCharWidth = textMetrics.tmAveCharWidth * metricsMultiplier; m_maxCharWidth = textMetrics.tmMaxCharWidth * metricsMultiplier; OUTLINETEXTMETRIC metrics; if (GetOutlineTextMetrics(hdc, sizeof(metrics), &metrics) > 0) { // This is a TrueType font. We might be able to get an accurate xHeight GLYPHMETRICS gm; MAT2 mat = { 1, 0, 0, 1 }; DWORD len = GetGlyphOutline(hdc, 'x', GGO_METRICS, &gm, 0, 0, &mat); if (len != GDI_ERROR && gm.gmptGlyphOrigin.y > 0) xHeight = gm.gmptGlyphOrigin.y * metricsMultiplier; } m_fontMetrics.setXHeight(xHeight); cairo_win32_scaled_font_done_font(scaledFont); m_isSystemFont = false; m_scriptCache = 0; m_scriptFontProperties = 0; RestoreDC(hdc, -1); ReleaseDC(0, hdc); }
int CChildView::GetStringWidth(CString strong) { int width=0; CFont ThisFont; CFont* pOldFont; GLYPHMETRICS gm; DWORD res; MAT2 m2 = {{0, 1}, {0, 0}, {0, 0}, {0, 1}}; CPaintDC dc(this); // device context for painting HDC hDC = dc.m_hDC; ThisFont.CreateFontIndirect(&m_WndLogFont); pOldFont = dc.SelectObject(&ThisFont); res = GetGlyphOutline(hDC,32,GGO_BITMAP,&gm,32,NULL, &m2); for (int l=0; l<strong.GetLength(); l++){ res = dc.GetGlyphOutline(strong[l], 0, &gm, 0, NULL, &m2); res = GetGlyphOutline(hDC,strong[l],GGO_BITMAP,&gm,32,NULL, &m2); width += (int)(gm.gmBlackBoxX); } dc.SelectObject(pOldFont); return max(width,0); }
sFont2D::sLetterDimensions sFont2D::sGetLetterDimensions(const sChar letter) { sLetterDimensions result; SelectObject(sGDIDCOffscreen,prv->Font); ABC abc; if(GetCharABCWidths(sGDIDCOffscreen,letter,letter,&abc)) { result.Pre = abc.abcA; result.Cell = abc.abcB; result.Post = abc.abcC; result.Advance = abc.abcA + abc.abcB + abc.abcC; MAT2 mat; sClear(mat); mat.eM11.value = 1; mat.eM22.value = 1; GLYPHMETRICS metrics; if (GDI_ERROR != GetGlyphOutline(sGDIDCOffscreen, letter, GGO_METRICS, &metrics, 0, 0, &mat)) { result.Height = metrics.gmBlackBoxY; result.OriginY = metrics.gmptGlyphOrigin.y; } else { SIZE size; GetTextExtentPoint32(sGDIDCOffscreen, &letter, 1, &size); result.Height = size.cy; result.OriginY = size.cy; } } else { SIZE size; GetTextExtentPoint32(sGDIDCOffscreen,&letter,1,&size); result.Pre = 0; result.Cell = size.cx; result.Post = 0; result.Advance = size.cx; result.Height = size.cy; result.OriginY = size.cy; } return result; }
FloatRect SimpleFontData::platformBoundsForGlyph(Glyph glyph) const { HWndDC hdc(0); SetGraphicsMode(hdc, GM_ADVANCED); HGDIOBJ oldFont = SelectObject(hdc, m_platformData.hfont()); GLYPHMETRICS gdiMetrics; static const MAT2 identity = { 0, 1, 0, 0, 0, 0, 0, 1 }; if (GetGlyphOutline(hdc, glyph, GGO_METRICS | GGO_GLYPH_INDEX, &gdiMetrics, 0, 0, &identity) == -1) { if (FontPlatformData::ensureFontLoaded(m_platformData.hfont())) { // Retry GetTextMetrics. // FIXME: Handle gracefully the error if this call also fails. // See http://crbug.com/6401. if (GetGlyphOutline(hdc, glyph, GGO_METRICS | GGO_GLYPH_INDEX, &gdiMetrics, 0, 0, &identity) == -1) LOG_ERROR("Unable to get the glyph metrics after second attempt"); } } SelectObject(hdc, oldFont); return FloatRect(gdiMetrics.gmptGlyphOrigin.x, -gdiMetrics.gmptGlyphOrigin.y, gdiMetrics.gmBlackBoxX, gdiMetrics.gmBlackBoxY); }
/*------------------------------------------------------------------------------* | <<< アンチエイリアスフォントのピクセル情報を得る >>> | 入力 iID = 管理番号 | uiCode = 文字コード | 戻り値 false = 文字を得られなかった *------------------------------------------------------------------------------*/ static bool AFontPixelGet(int iID, uint uiCode, TEXTMETRIC *tm) { AFONT_WORK *a = &afont[iID]; //--- ビットマップ取得 ------------------------------------------ MAT2 m = {{0, 1}, {0, 0}, {0, 0}, {0, 1}}; uint fmt = GGO_GRAY8_BITMAP; // ほかに GGO_GRAY2_BITMAP、GGO_GRAY4_BITMAP がある //--- アンチフォントを取り出す ---------------------------------- HDC hDc = d3.font[a->cFontNum]->GetDC(); int iSize = GetGlyphOutline(hDc, uiCode, fmt, &a->gmAnt, 0, NULL, &m); if(iSize == 0){ return false;} GetGlyphOutline(hDc, uiCode, fmt, &a->gmAnt, iSize, a->pBuf, &m); GetTextMetrics( hDc, tm); //--- 文字の横幅を決める ---------------------------------------- int iW = d3.fontInfo[a->cFontNum].Width; int iH = d3.fontInfo[a->cFontNum].Height; if(uiCode >= 0x100){ iW *= 2;} // 全角ならば横幅は二倍になる return true; }
FloatRect Font::boundsForGDIGlyph(Glyph glyph) const { HWndDC hdc(0); SetGraphicsMode(hdc, GM_ADVANCED); HGDIOBJ oldFont = SelectObject(hdc, m_platformData.hfont()); GLYPHMETRICS gdiMetrics; static const MAT2 identity = { 0, 1, 0, 0, 0, 0, 0, 1 }; GetGlyphOutline(hdc, glyph, GGO_METRICS | GGO_GLYPH_INDEX, &gdiMetrics, 0, 0, &identity); SelectObject(hdc, oldFont); return FloatRect(gdiMetrics.gmptGlyphOrigin.x, -gdiMetrics.gmptGlyphOrigin.y, gdiMetrics.gmBlackBoxX + m_syntheticBoldOffset, gdiMetrics.gmBlackBoxY); }
float Font::widthForGDIGlyph(Glyph glyph) const { HWndDC hdc(0); SetGraphicsMode(hdc, GM_ADVANCED); HGDIOBJ oldFont = SelectObject(hdc, m_platformData.hfont()); GLYPHMETRICS gdiMetrics; static const MAT2 identity = { 0, 1, 0, 0, 0, 0, 0, 1 }; GetGlyphOutline(hdc, glyph, GGO_METRICS | GGO_GLYPH_INDEX, &gdiMetrics, 0, 0, &identity); float result = gdiMetrics.gmCellIncX + m_syntheticBoldOffset; SelectObject(hdc, oldFont); return result; }
void Font::initGDIFont() { if (!m_platformData.size()) { m_fontMetrics.reset(); m_avgCharWidth = 0; m_maxCharWidth = 0; return; } HWndDC hdc(0); HGDIOBJ oldFont = SelectObject(hdc, m_platformData.hfont()); OUTLINETEXTMETRIC metrics; GetOutlineTextMetrics(hdc, sizeof(metrics), &metrics); TEXTMETRIC& textMetrics = metrics.otmTextMetrics; float ascent, descent, lineGap; // The Open Font Format describes the OS/2 USE_TYPO_METRICS flag as follows: // "If set, it is strongly recommended to use OS/2.sTypoAscender - OS/2.sTypoDescender+ OS/2.sTypoLineGap as a value for default line spacing for this font." const UINT useTypoMetricsMask = 1 << 7; if (metrics.otmfsSelection & useTypoMetricsMask) { ascent = metrics.otmAscent; descent = metrics.otmDescent; lineGap = metrics.otmLineGap; } else { ascent = textMetrics.tmAscent; descent = textMetrics.tmDescent; lineGap = textMetrics.tmExternalLeading; } m_fontMetrics.setAscent(ascent); m_fontMetrics.setDescent(descent); m_fontMetrics.setLineGap(lineGap); m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap)); m_avgCharWidth = textMetrics.tmAveCharWidth; m_maxCharWidth = textMetrics.tmMaxCharWidth; float xHeight = ascent * 0.56f; // Best guess for xHeight if no x glyph is present. GLYPHMETRICS gm; static const MAT2 identity = { 0, 1, 0, 0, 0, 0, 0, 1 }; DWORD len = GetGlyphOutline(hdc, 'x', GGO_METRICS, &gm, 0, 0, &identity); if (len != GDI_ERROR && gm.gmptGlyphOrigin.y > 0) xHeight = gm.gmptGlyphOrigin.y; m_fontMetrics.setXHeight(xHeight); m_fontMetrics.setUnitsPerEm(metrics.otmEMSquare); SelectObject(hdc, oldFont); }
void Update() { if(m_text==m_ss.str()){ return; } m_text=m_ss.str(); m_image->Clear(); BYTE *data=m_image->GetSample(); int pitch=m_image->GetWidth()*4; // フォントの生成 LOGFONT lf = {m_fontsize, 0, 0, 0, 0, 0, 0, 0, SHIFTJIS_CHARSET, OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, FIXED_PITCH | FF_MODERN, _T("MS 明朝")}; HFONT hFont=CreateFontIndirect(&lf); if(!(hFont)){ return; } // デバイスコンテキスト取得 // デバイスにフォントを持たせないとGetGlyphOutline関数はエラーとなる HDC hdc = GetDC(NULL); HFONT oldFont = (HFONT)SelectObject(hdc, hFont); std::vector<BYTE> gryph; int x_pos=0; int y_pos=0; int col_size=m_fontsize/2; for(auto c=m_text.begin(); c!=m_text.end(); ++c){ if(y_pos>=m_row*m_fontsize){ break; } if(*c==L'\n'){ y_pos+=m_fontsize+5; x_pos=0; continue; } if(x_pos+col_size>=m_col*col_size){ y_pos+=m_fontsize+5; x_pos=0; } // フォントビットマップ取得 TEXTMETRIC TM; GetTextMetrics( hdc, &TM ); GLYPHMETRICS GM; CONST MAT2 Mat = {{0,1},{0,0},{0,0},{0,1}}; DWORD size = GetGlyphOutline(hdc, *c, GGO_GRAY4_BITMAP, &GM, 0, NULL, &Mat); if(size>0){ gryph.resize(size); GetGlyphOutline(hdc, *c, GGO_GRAY4_BITMAP, &GM, gryph.size(), &gryph[0], &Mat); // フォント情報の書き込み // iOfs_x, iOfs_y : 書き出し位置(左上) // iBmp_w, iBmp_h : フォントビットマップの幅高 // Level : α値の段階 (GGO_GRAY4_BITMAPなので17段階) int iOfs_x = x_pos+GM.gmptGlyphOrigin.x; int iOfs_y = y_pos+TM.tmAscent - GM.gmptGlyphOrigin.y; int iBmp_w = GM.gmBlackBoxX + (4-(GM.gmBlackBoxX%4))%4; int iBmp_h = GM.gmBlackBoxY; int Level = 17; DWORD Alpha, Color; for(int y=iOfs_y; y<iOfs_y+iBmp_h; y++){ for(size_t x=iOfs_x; x<iOfs_x+GM.gmBlackBoxX; x++){ Alpha = (255 * gryph[x-iOfs_x + iBmp_w*(y-iOfs_y)]) / (Level-1); Color = 0x00ffffff | (Alpha<<24); memcpy((BYTE*)data + pitch*y + 4*x, &Color, sizeof(DWORD)); } } x_pos+=iBmp_w; } else{ x_pos+=col_size; } } // デバイスコンテキストとフォントハンドルの開放 SelectObject(hdc, oldFont); DeleteObject(hFont); ReleaseDC(NULL, hdc); }
WINGDIAPI BOOL GLAPIENTRY wglUseFontBitmapsA(HDC hdc, DWORD first, DWORD count, DWORD listBase) { int i; GLuint font_list; DWORD size; GLYPHMETRICS gm; HANDLE hBits; LPSTR lpBits; MAT2 mat; int success = TRUE; if (count == 0) return FALSE; font_list = listBase; mat.eM11 = FixedFromDouble(1); mat.eM12 = FixedFromDouble(0); mat.eM21 = FixedFromDouble(0); mat.eM22 = FixedFromDouble(-1); memset(&gm,0,sizeof(gm)); /* ** If we can't get the glyph outline, it may be because this is a fixed ** font. Try processing it that way. */ if( GetGlyphOutline(hdc, first, GGO_BITMAP, &gm, 0, NULL, &mat) == GDI_ERROR ) { return wglUseFontBitmaps_FX( hdc, first, count, listBase ); } /* ** Otherwise process all desired characters. */ for (i = 0; i < (int)count; i++) { DWORD err; glNewList( font_list+i, GL_COMPILE ); /* allocate space for the bitmap/outline */ size = GetGlyphOutline(hdc, first + i, GGO_BITMAP, &gm, 0, NULL, &mat); if (size == GDI_ERROR) { glEndList( ); err = GetLastError(); success = FALSE; continue; } hBits = GlobalAlloc(GHND, size+1); lpBits = GlobalLock(hBits); err = GetGlyphOutline(hdc, /* handle to device context */ first + i, /* character to query */ GGO_BITMAP, /* format of data to return */ &gm, /* ptr to structure for metrics*/ size, /* size of buffer for data */ lpBits, /* pointer to buffer for data */ &mat /* pointer to transformation */ /* matrix structure */ ); if (err == GDI_ERROR) { GlobalUnlock(hBits); GlobalFree(hBits); glEndList( ); err = GetLastError(); success = FALSE; continue; } glBitmap(gm.gmBlackBoxX,gm.gmBlackBoxY, (GLfloat)-gm.gmptGlyphOrigin.x, (GLfloat)gm.gmptGlyphOrigin.y, (GLfloat)gm.gmCellIncX, (GLfloat)gm.gmCellIncY, (const GLubyte * )lpBits); GlobalUnlock(hBits); GlobalFree(hBits); glEndList( ); } return success; }
bool getOutlineForGlyph (int glyphNumber, Path& glyphPath) { if (glyphNumber < 0) glyphNumber = defaultGlyph; GLYPHMETRICS gm; // (although GetGlyphOutline returns a DWORD, it may be -1 on failure, so treat it as signed int..) const int bufSize = (int) GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, 0, &identityMatrix); if (bufSize > 0) { HeapBlock<char> data (bufSize); GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, bufSize, data, &identityMatrix); const TTPOLYGONHEADER* pheader = reinterpret_cast<TTPOLYGONHEADER*> (data.getData()); const float scaleX = 1.0f / tm.tmHeight; const float scaleY = -scaleX; while ((char*) pheader < data + bufSize) { glyphPath.startNewSubPath (scaleX * pheader->pfxStart.x.value, scaleY * pheader->pfxStart.y.value); const TTPOLYCURVE* curve = (const TTPOLYCURVE*) ((const char*) pheader + sizeof (TTPOLYGONHEADER)); const char* const curveEnd = ((const char*) pheader) + pheader->cb; while ((const char*) curve < curveEnd) { if (curve->wType == TT_PRIM_LINE) { for (int i = 0; i < curve->cpfx; ++i) glyphPath.lineTo (scaleX * curve->apfx[i].x.value, scaleY * curve->apfx[i].y.value); } else if (curve->wType == TT_PRIM_QSPLINE) { for (int i = 0; i < curve->cpfx - 1; ++i) { const float x2 = scaleX * curve->apfx[i].x.value; const float y2 = scaleY * curve->apfx[i].y.value; float x3 = scaleX * curve->apfx[i + 1].x.value; float y3 = scaleY * curve->apfx[i + 1].y.value; if (i < curve->cpfx - 2) { x3 = 0.5f * (x2 + x3); y3 = 0.5f * (y2 + y3); } glyphPath.quadraticTo (x2, y2, x3, y3); } } curve = (const TTPOLYCURVE*) &(curve->apfx [curve->cpfx]); } pheader = (const TTPOLYGONHEADER*) curve; glyphPath.closeSubPath(); } } return true; }
// **************************************************************************** // // Function Name: RTrueTypeFont::GetGlyphOutline( ) // // Description: // // Returns: Nothing // // Exceptions: Memory, Font // // **************************************************************************** // void RTrueTypeFont::GetGlyphOutline( Handle sfnt, long glyphIndex, GlyphOutline* pOutline, Matrix xform ) { short state = GetFontState( sfnt ); short upem, sideBearing, adjustToLsb; short* glyph; sfnt_FontHeader* head; sfnt_HorizontalHeader* hhea; sfnt_HorizontalMetrics* hori; long length; long longMetrics; try { ::HLock( sfnt ); head = (sfnt_FontHeader *)GetSfntTablePtr( sfnt, tag_FontHeader ); hhea = (sfnt_HorizontalHeader *)GetSfntTablePtr( sfnt, tag_HoriHeader ); hori = (sfnt_HorizontalMetrics *)GetSfntTablePtr( sfnt, tag_HorizontalMetrics ); if ( head == NULL || hhea == NULL || hori == NULL ) throw fontNotOutlineErr; upem = head->unitsPerEm; longMetrics = hhea->numberLongMetrics; if ( glyphIndex < longMetrics ) { pOutline->advance.x = ::FixRatio( hori[glyphIndex].advance, upem ); sideBearing = hori[glyphIndex].sideBearing; } else { short *lsb = (short *)&hori[longMetrics]; // first entry after[AW,LSB] array pOutline->advance.x = ::FixRatio( hori[longMetrics-1].advance, upem ); sideBearing = hori[glyphIndex - longMetrics].sideBearing; } pOutline->advance.y = 0; pOutline->origin.x = pOutline->origin.y = 0; if ( (glyph = (short *)GetSfntGlyphPtr(sfnt, glyphIndex, &length)) == 0 ) throw fontNotOutlineErr; if ( length == 0 ) { pOutline->contourCount = pOutline->pointCount = 0; SetFontState( sfnt, state ); return; } pOutline->contourCount = *glyph++; adjustToLsb = *glyph - sideBearing; // xmin - lsb glyph += 4; // skip bounds rect if ( pOutline->contourCount == 0 ) pOutline->pointCount = 0; else if ( pOutline->contourCount == -1 ) { short flags, index, newMatrix; pOutline->contourCount = pOutline->pointCount = 0; mySetHandleSize( (Handle)pOutline->endPoints, 0 ); mySetHandleSize( (Handle)pOutline->onCurve, 0 ); mySetHandleSize( (Handle)pOutline->x, 0 ); mySetHandleSize( (Handle)pOutline->y, 0 ); do { Matrix compXform; short arg1, arg2; flags = *glyph++; index = *glyph++; newMatrix = false; if ( flags & ARG_1_AND_2_ARE_WORDS ) { arg1 = *glyph++; arg2 = *glyph++; } else { char* byteP = (char*)glyph; if ( flags & ARGS_ARE_XY_VALUES ) { // offsets are signed arg1 = *byteP++; arg2 = *byteP; } else { // anchor points are unsigned arg1 = (unsigned char)*byteP++; arg2 = (unsigned char)*byteP; } ++glyph; } #if IMPLEMENT_SCALED_COMPONENTS if ( flags & (WE_HAVE_A_SCALE | WE_HAVE_AN_X_AND_Y_SCALE | WE_HAVE_A_TWO_BY_TWO) ) { Matrix subXform; MakeIdentityMatrix( subXform ); if ( flags & WE_HAVE_A_TWO_BY_TWO ) { compXform[0][0] = (Fixed)*glyph++ << 2; compXform[0][1] = (Fixed)*glyph++ << 2; compXform[1][0] = (Fixed)*glyph++ << 2; compXform[1][1] = (Fixed)*glyph++ << 2; } else if ( flags & WE_HAVE_AN_X_AND_Y_SCALE ) { compXform[0][0] = (Fixed)*glyph++ << 2; compXform[1][1] = (Fixed)*glyph++ << 2; } else compXform[0][0] = compXform[1][1] = (Fixed)*glyph++ << 2; PostMulMatrix (compXform, xform ); newMatrix = true; } #endif { GlyphOutline out; InitGlyphOutline( &out ); GetGlyphOutline( sfnt, index, &out, newMatrix ? compXform : xform ); { Fixed dx, dy; if ( flags & ARGS_ARE_XY_VALUES ) { dx = ::FixRatio(arg1, upem); dy = -::FixRatio(arg2, upem); } else { dx = (*pOutline->x)[arg1] - (*out.x)[arg2]; dy = (*pOutline->y)[arg1] - (*out.y)[arg2]; } MoveGlyphOutline( &out, dx, dy ); } AppendGlyphOutline( pOutline, &out ); KillGlyphOutline( &out ); } } while ( flags & MORE_COMPONENTS ); } else if ( pOutline->contourCount > 0 ) { // Load in the end points. { long size = pOutline->contourCount * sizeof(short); mySetHandleSize( (Handle)pOutline->endPoints, size ); BlockMove( (Ptr)glyph, (Ptr)*pOutline->endPoints, size ); glyph += pOutline->contourCount; } pOutline->pointCount = (*pOutline->endPoints)[pOutline->contourCount - 1] + 1; mySetHandleSize( (Handle)pOutline->onCurve, pOutline->pointCount * sizeof(char) ); mySetHandleSize( (Handle)pOutline->x, pOutline->pointCount * sizeof(Fixed) ); mySetHandleSize( (Handle)pOutline->y, pOutline->pointCount * sizeof(Fixed) ); // Skip the word for instruction count + the instructions. // Then load in the onCurve bytes. { Byte* p = (Byte*)glyph + sizeof(short) + *glyph; Byte* onCurve = *pOutline->onCurve; Byte* stop = onCurve + pOutline->pointCount; Byte flag; while ( onCurve < stop ) { *onCurve++ = flag = GetUnsignedByte( p ); if ( flag & REPEAT_FLAGS ) { short count = GetUnsignedByte( p ); for ( --count; count >= 0; --count ) *onCurve++ = flag; } } // Lets do X { short coord = adjustToLsb; Fixed* x = *pOutline->x; onCurve = *pOutline->onCurve; while ( onCurve < stop ) { if ( (flag = *onCurve++) & XSHORT ) { if ( flag & SHORT_X_IS_POS ) coord += GetUnsignedByte( p ); else coord -= GetUnsignedByte( p ); } else if ( !(flag & NEXT_X_IS_ZERO) ) { coord += (short)(*p++) << 8; coord += (Byte)*p++; } *x = ::FixRatio( coord, upem ); x++; } } // Lets do Y { short coord = 0; Fixed* y = *pOutline->y; onCurve = *pOutline->onCurve; while ( onCurve < stop ) { if ( (flag = *onCurve) & YSHORT ) { if ( flag & SHORT_Y_IS_POS ) coord += GetUnsignedByte( p ); else coord -= GetUnsignedByte( p ); } else if ( !(flag & NEXT_Y_IS_ZERO) ) { coord += (short)(*p++) << 8; coord += (Byte)*p++; } *y = -::FixRatio( coord, upem ); y++; // Filter off the extra bits *onCurve++ = flag & ONCURVE; } } } } else throw fontNotOutlineErr; } catch ( OSErr osErr ) { SetFontState( sfnt, state ); switch ( osErr ) { case memFullErr : // out of memeory SetFontState( sfnt, state ); throw kMemory; break; case fontNotOutlineErr : // bad font SetFontState( sfnt, state ); throw kResource; break; default: TpsAssertAlways( "Bad exception" ); throw; break; } } catch ( ... ) { SetFontState( sfnt, state ); throw; } SetFontState( sfnt, state ); }
// **************************************************************************** // // Function Name: RTrueTypeFont::ExtractCharacterOutline( ) // // Description: Retrieve a glyph outline and parse it into segment records // in the global buffer // // Returns: Boolean indicating successful completion // // Exceptions: Memory // // **************************************************************************** // BOOLEAN RTrueTypeFont::ExtractCharacterOutline( int character, uLONG cookie ) { YTTSegmentInfoRecord* pSegments = (YTTSegmentInfoRecord *)( cookie + sizeof(uLONG) ); const YFontInfo fontInfo = GetInfo(); GlyphOutline glyph; Matrix matrix; long glyphIndex = 0; // zero is the missing character // retrieve font resource if ( m_hSfnt == NULL ) { short rId; ResType rType; Str255 rName; m_hSfnt = GetSfntHandle( (const LPSZ)fontInfo.sbName, RFont::GetMacStyleBits( fontInfo.attributes ) ); GetResInfo( m_hSfnt, &rId, &rType, rName); m_sSfntId = rId; } else if ( *m_hSfnt == NULL ) { ::LoadResource( m_hSfnt ); // m_hSfnt = ::GetResource( 'sfnt', m_sSfntId ); } if ( m_hSfnt == NULL || *m_hSfnt == NULL ) return FALSE; ::HNoPurge( m_hSfnt ); // extract character outline InitMatrix( matrix ); InitGlyphOutline( &glyph ); MakeIdentityMatrix( matrix ); try { glyphIndex = GetCharGlyphIndex( m_hSfnt, character ); GetGlyphOutline( m_hSfnt, glyphIndex, &glyph, matrix ); ScaleGlyphOutline( &glyph, ::Long2Fix( fontInfo.height ), ::Long2Fix( fontInfo.height ) ); } catch ( YException except ) { ::HPurge( m_hSfnt ); KillGlyphOutline( &glyph ); switch ( except ) { case kResource: return FALSE; break; default: throw; break; } } catch ( ... ) { ::HPurge( m_hSfnt ); KillGlyphOutline( &glyph ); throw; } // loop thru the contours LockGlyphOutline( &glyph ); { long nrSegments = 0; long sp = 0; Fixed* x = *glyph.x; Fixed* y = *glyph.y; short* ep = *glyph.endPoints; Byte* onCurve = *glyph.onCurve; RIntPoint ptStart; for ( int i = 0; i < glyph.contourCount; i++ ) { long pts = *ep - sp + 1; // nr pts in contour // contour start point if ( *onCurve != 0 ) // 1st point on curve { ptStart.m_x = RoundFixed( *x ); ptStart.m_y = -RoundFixed( *y ); x++; y++; onCurve++; pts--; } else if ( *((*glyph.onCurve) + *ep) != 0 ) // use end point { ptStart.m_x = RoundFixed( *((*glyph.x) + *ep) ); ptStart.m_y = -RoundFixed( *((*glyph.y) + *ep) ); } else // compute midpoint between 1st and last curve points { Fixed x2 = ::FixDiv( (*x + *((*glyph.x) + *ep)), ::Long2Fix( 2 ) ); Fixed y2 = ::FixDiv( (*y + *((*glyph.y) + *ep)), ::Long2Fix( 2 ) ); ptStart.m_x = RoundFixed( x2 ); ptStart.m_y = -RoundFixed( y2 ); } // initial move to pSegments->opCode = MOVE_TO; pSegments->pt1 = ptStart; nrSegments++; pSegments++; // load segments of contour while ( pts-- > 0 ) { Fixed x0 = *x; Fixed y0 = *y; Byte onCurve0 = *onCurve; x++; y++; onCurve++; pSegments->pt1.m_x = RoundFixed( x0 ); pSegments->pt1.m_y = -RoundFixed( y0 ); if ( onCurve0 != 0 ) pSegments->opCode = LINE_TO; else if ( pts < 1 ) // quadratic w/ contour start point as end { pSegments->opCode = QUADRATIC_TO; pSegments->pt2 = ptStart; } else if ( *onCurve != 0 ) // quadratic w/ end point next line to { pSegments->opCode = QUADRATIC_TO; pSegments->pt2.m_x = RoundFixed( *x ); pSegments->pt2.m_y = -RoundFixed( *y ); x++; y++; onCurve++; pts--; } else // compute end point of quadratic as midpoint to next curve { Fixed x2 = ::FixDiv( (x0 + *x), ::Long2Fix( 2 ) ); Fixed y2 = ::FixDiv( (y0 + *y), ::Long2Fix( 2 ) ); pSegments->opCode = QUADRATIC_TO; pSegments->pt2.m_x = RoundFixed( x2 ); pSegments->pt2.m_y = -RoundFixed( y2 ); } nrSegments++; pSegments++; } pSegments->opCode = CLOSE_PATH; // end of contour pSegments->pt1.m_x = pSegments->pt2.m_x = 0; pSegments->pt1.m_y = pSegments->pt2.m_y = 0; nrSegments++; pSegments++; sp = *ep++ + 1; } pSegments->opCode = 0; // end of glyph pSegments->pt1.m_x = pSegments->pt2.m_x = 0; pSegments->pt1.m_y = pSegments->pt2.m_y = 0; *(uLONG *)cookie = nrSegments; } UnlockGlyphOutline( &glyph ); // cleanup ::HPurge( m_hSfnt ); KillGlyphOutline( &glyph ); return TRUE; }
bool loadGlyphIfPossible (juce_wchar character) { HDC dc = FontDCHolder::getInstance()->loadFont (name, isBold, isItalic, 0); GLYPHMETRICS gm; // if this is the fallback font, skip checking for the glyph's existence. This is because // with fonts like Tahoma, GetGlyphIndices can say that a glyph doesn't exist, but it still // gets correctly created later on. if (! isFallbackFont) { const WCHAR charToTest[] = { (WCHAR) character, 0 }; WORD index = 0; if (GetGlyphIndices (dc, charToTest, 1, &index, GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR && index == 0xffff) { return false; } } Path glyphPath; TEXTMETRIC tm; if (! GetTextMetrics (dc, &tm)) { addGlyph (character, glyphPath, 0); return true; } const float height = (float) tm.tmHeight; static const MAT2 identityMatrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; const int bufSize = GetGlyphOutline (dc, character, GGO_NATIVE, &gm, 0, 0, &identityMatrix); if (bufSize > 0) { HeapBlock<char> data (bufSize); GetGlyphOutline (dc, character, GGO_NATIVE, &gm, bufSize, data, &identityMatrix); const TTPOLYGONHEADER* pheader = reinterpret_cast<TTPOLYGONHEADER*> (data.getData()); const float scaleX = 1.0f / height; const float scaleY = -1.0f / height; while ((char*) pheader < data + bufSize) { float x = scaleX * pheader->pfxStart.x.value; float y = scaleY * pheader->pfxStart.y.value; glyphPath.startNewSubPath (x, y); const TTPOLYCURVE* curve = (const TTPOLYCURVE*) ((const char*) pheader + sizeof (TTPOLYGONHEADER)); const char* const curveEnd = ((const char*) pheader) + pheader->cb; while ((const char*) curve < curveEnd) { if (curve->wType == TT_PRIM_LINE) { for (int i = 0; i < curve->cpfx; ++i) { x = scaleX * curve->apfx[i].x.value; y = scaleY * curve->apfx[i].y.value; glyphPath.lineTo (x, y); } } else if (curve->wType == TT_PRIM_QSPLINE) { for (int i = 0; i < curve->cpfx - 1; ++i) { const float x2 = scaleX * curve->apfx[i].x.value; const float y2 = scaleY * curve->apfx[i].y.value; float x3, y3; if (i < curve->cpfx - 2) { x3 = 0.5f * (x2 + scaleX * curve->apfx[i + 1].x.value); y3 = 0.5f * (y2 + scaleY * curve->apfx[i + 1].y.value); } else { x3 = scaleX * curve->apfx[i + 1].x.value; y3 = scaleY * curve->apfx[i + 1].y.value; } glyphPath.quadraticTo (x2, y2, x3, y3); x = x3; y = y3; } } curve = (const TTPOLYCURVE*) &(curve->apfx [curve->cpfx]); } pheader = (const TTPOLYGONHEADER*) curve; glyphPath.closeSubPath(); } } addGlyph (character, glyphPath, gm.gmCellIncX / height); int numKPs; const KERNINGPAIR* const kps = FontDCHolder::getInstance()->getKerningPairs (numKPs); for (int i = 0; i < numKPs; ++i) { if (kps[i].wFirst == character) addKerningPair (kps[i].wFirst, kps[i].wSecond, kps[i].iKernAmount / height); } return true; }
JNIEXPORT jlong JNICALL Java_sun_font_FileFontStrike__1getGlyphImageFromWindows (JNIEnv *env, jobject unused, jstring fontFamily, jint style, jint size, jint glyphCode, jboolean fm) { GLYPHMETRICS glyphMetrics; LOGFONTW lf; BITMAPINFO bmi; TEXTMETRIC textMetric; RECT rect; int bytesWidth, dibBytesWidth, extra, imageSize, dibImageSize; unsigned char* dibImage = NULL, *rowPtr, *pixelPtr, *dibPixPtr, *dibRowPtr; unsigned char r,g,b; unsigned char* igTable; GlyphInfo* glyphInfo = NULL; int nameLen; LPWSTR name; HFONT oldFont, hFont; MAT2 mat2; unsigned short width; unsigned short height; short advanceX; short advanceY; int topLeftX; int topLeftY; int err; int bmWidth, bmHeight; int x, y; HBITMAP hBitmap = NULL, hOrigBM; int gamma, orient; HWND hWnd = NULL; HDC hDesktopDC = NULL; HDC hMemoryDC = NULL; hWnd = GetDesktopWindow(); hDesktopDC = GetWindowDC(hWnd); if (hDesktopDC == NULL) { return (jlong)0; } if (GetDeviceCaps(hDesktopDC, BITSPIXEL) < 15) { FREE_AND_RETURN; } hMemoryDC = CreateCompatibleDC(hDesktopDC); if (hMemoryDC == NULL || fontFamily == NULL) { FREE_AND_RETURN; } err = SetMapMode(hMemoryDC, MM_TEXT); if (err == 0) { FREE_AND_RETURN; } memset(&lf, 0, sizeof(LOGFONTW)); lf.lfHeight = -size; lf.lfWeight = (style & 1) ? FW_BOLD : FW_NORMAL; lf.lfItalic = (style & 2) ? 0xff : 0; lf.lfCharSet = DEFAULT_CHARSET; lf.lfQuality = CLEARTYPE_QUALITY; lf.lfOutPrecision = OUT_TT_PRECIS; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfPitchAndFamily = DEFAULT_PITCH; nameLen = (*env)->GetStringLength(env, fontFamily); name = (LPWSTR)alloca((nameLen+1)*2); if (name == NULL) { FREE_AND_RETURN; } (*env)->GetStringRegion(env, fontFamily, 0, nameLen, name); name[nameLen] = '\0'; if (nameLen < (sizeof(lf.lfFaceName) / sizeof(lf.lfFaceName[0]))) { wcscpy(lf.lfFaceName, name); } else { FREE_AND_RETURN; } hFont = CreateFontIndirectW(&lf); if (hFont == NULL) { FREE_AND_RETURN; } oldFont = SelectObject(hMemoryDC, hFont); memset(&textMetric, 0, sizeof(TEXTMETRIC)); err = GetTextMetrics(hMemoryDC, &textMetric); if (err == 0) { FREE_AND_RETURN; } memset(&glyphMetrics, 0, sizeof(GLYPHMETRICS)); memset(&mat2, 0, sizeof(MAT2)); mat2.eM11.value = 1; mat2.eM22.value = 1; err = GetGlyphOutline(hMemoryDC, glyphCode, GGO_METRICS|GGO_GLYPH_INDEX, &glyphMetrics, 0, NULL, &mat2); if (err == GDI_ERROR) { /* Probably no such glyph - ie the font wasn't the one we expected. */ FREE_AND_RETURN; } width = (unsigned short)glyphMetrics.gmBlackBoxX; height = (unsigned short)glyphMetrics.gmBlackBoxY; /* Don't handle "invisible" glyphs in this code */ if (width <= 0 || height == 0) { FREE_AND_RETURN; } advanceX = glyphMetrics.gmCellIncX; advanceY = glyphMetrics.gmCellIncY; topLeftX = glyphMetrics.gmptGlyphOrigin.x; topLeftY = glyphMetrics.gmptGlyphOrigin.y; /* GetGlyphOutline pre-dates cleartype and I'm not sure that it will * account for all pixels touched by the rendering. Need to widen, * and also adjust by one the x position at which it is rendered. * The extra pixels of width are used as follows : * One extra pixel at the left and the right will be needed to absorb * the pixels that will be touched by filtering by GDI to compensate * for colour fringing. * However there seem to be some cases where GDI renders two extra * pixels to the right, so we add one additional pixel to the right, * and in the code that copies this to the image cache we test for * the (rare) cases when this is touched, and if its not reduce the * stated image width for the blitting loops. * For fractional metrics : * One extra pixel at each end to account for sub-pixel positioning used * when fractional metrics is on in LCD mode. * The pixel at the left is needed so the blitting loop can index into * that a byte at a time to more accurately position the glyph. * The pixel at the right is needed so that when such indexing happens, * the blitting still can use the same width. * Consequently the width that is specified for the glyph is one less * than that of the actual image. * Note that in the FM case as a consequence we need to adjust the * position at which GDI renders, and the declared width of the glyph * See the if (fm) {} cases in the code. * For the non-FM case, we not only save 3 bytes per row, but this * prevents apparent glyph overlapping which affects the rendering * performance of accelerated pipelines since it adds additional * read-back requirements. */ width+=3; if (fm) { width+=1; } /* DIB scanline must end on a DWORD boundary. We specify 3 bytes per pixel, * so must round up as needed to a multiple of 4 bytes. */ dibBytesWidth = bytesWidth = width*3; extra = dibBytesWidth % 4; if (extra != 0) { dibBytesWidth += (4-extra); } /* The glyph cache image must be a multiple of 3 bytes wide. */ extra = bytesWidth % 3; if (extra != 0) { bytesWidth += (3-extra); } bmWidth = width; bmHeight = height; /* Must use desktop DC to create a bitmap of that depth */ hBitmap = CreateCompatibleBitmap(hDesktopDC, bmWidth, bmHeight); if (hBitmap == NULL) { FREE_AND_RETURN; } hOrigBM = (HBITMAP)SelectObject(hMemoryDC, hBitmap); /* Fill in black */ rect.left = 0; rect.top = 0; rect.right = bmWidth; rect.bottom = bmHeight; FillRect(hMemoryDC, (LPRECT)&rect, GetStockObject(BLACK_BRUSH)); /* Set text color to white, background to black. */ SetBkColor(hMemoryDC, RGB(0,0,0)); SetTextColor(hMemoryDC, RGB(255,255,255)); /* adjust rendering position */ x = -topLeftX+1; if (fm) { x += 1; } y = topLeftY - textMetric.tmAscent; err = ExtTextOutW(hMemoryDC, x, y, ETO_GLYPH_INDEX|ETO_OPAQUE, (LPRECT)&rect, (LPCWSTR)&glyphCode, 1, NULL); if (err == 0) { FREE_AND_RETURN; } /* Now get the image into a DIB. * MS docs for GetDIBits says the compatible bitmap must not be * selected into a DC, so restore the original first. */ SelectObject(hMemoryDC, hOrigBM); SelectObject(hMemoryDC, oldFont); DeleteObject(hFont); memset(&bmi, 0, sizeof(BITMAPINFO)); bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); bmi.bmiHeader.biWidth = width; bmi.bmiHeader.biHeight = -height; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 24; bmi.bmiHeader.biCompression = BI_RGB; dibImage = SAFE_SIZE_ARRAY_ALLOC(malloc, dibBytesWidth, height); if (dibImage == NULL) { FREE_AND_RETURN; } dibImageSize = dibBytesWidth*height; memset(dibImage, 0, dibImageSize); err = GetDIBits(hMemoryDC, hBitmap, 0, height, dibImage, &bmi, DIB_RGB_COLORS); if (err == 0) { /* GetDIBits failed. */ FREE_AND_RETURN; } err = SystemParametersInfo(SPI_GETFONTSMOOTHINGORIENTATION, 0, &orient, 0); if (err == 0) { FREE_AND_RETURN; } err = SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &gamma, 0); if (err == 0) { FREE_AND_RETURN; } igTable = getIGTable(gamma/10); if (igTable == NULL) { FREE_AND_RETURN; } /* Now copy glyph image into a GlyphInfo structure and return it. * NB the xadvance calculated here may be overwritten by the caller. * 1 is subtracted from the bitmap width to get the glyph width, since * that extra "1" was added as padding, so the sub-pixel positioning of * fractional metrics could index into it. */ glyphInfo = (GlyphInfo*)SAFE_SIZE_STRUCT_ALLOC(malloc, sizeof(GlyphInfo), bytesWidth, height); if (glyphInfo == NULL) { FREE_AND_RETURN; } imageSize = bytesWidth*height; glyphInfo->cellInfo = NULL; glyphInfo->rowBytes = bytesWidth; glyphInfo->width = width; if (fm) { glyphInfo->width -= 1; // must subtract 1 } glyphInfo->height = height; glyphInfo->advanceX = advanceX; glyphInfo->advanceY = advanceY; glyphInfo->topLeftX = (float)(topLeftX-1); if (fm) { glyphInfo->topLeftX -= 1; } glyphInfo->topLeftY = (float)-topLeftY; glyphInfo->image = (unsigned char*)glyphInfo+sizeof(GlyphInfo); memset(glyphInfo->image, 0, imageSize); /* DIB 24bpp data is always stored in BGR order, but we usually * need this in RGB, so we can't just memcpy and need to swap B and R. * Also need to apply inverse gamma adjustment here. * We re-use the variable "extra" to see if the last pixel is touched * at all. If its not we can reduce the glyph image width. This comes * into play in some cases where GDI touches more pixels than accounted * for by increasing width by two pixels over the B&W image. Whilst * the bytes are in the cache, it doesn't affect rendering performance * of the hardware pipelines. */ extra = 0; if (fm) { extra = 1; // always need it. } dibRowPtr = dibImage; rowPtr = glyphInfo->image; for (y=0;y<height;y++) { pixelPtr = rowPtr; dibPixPtr = dibRowPtr; for (x=0;x<width;x++) { if (orient == FE_FONTSMOOTHINGORIENTATIONRGB) { b = *dibPixPtr++; g = *dibPixPtr++; r = *dibPixPtr++; } else { r = *dibPixPtr++; g = *dibPixPtr++; b = *dibPixPtr++; } *pixelPtr++ = igTable[r]; *pixelPtr++ = igTable[g]; *pixelPtr++ = igTable[b]; if (!fm && (x==(width-1)) && (r|g|b)) { extra = 1; } } dibRowPtr += dibBytesWidth; rowPtr += bytesWidth; } if (!extra) { glyphInfo->width -= 1; } free(dibImage); ReleaseDC(hWnd, hDesktopDC); DeleteObject(hMemoryDC); DeleteObject(hBitmap); return ptr_to_jlong(glyphInfo); }
static CGPathRef createPathForGlyph(HDC hdc, Glyph glyph) { CGMutablePathRef path = CGPathCreateMutable(); static const MAT2 identity = { 0, 1, 0, 0, 0, 0, 0, 1 }; GLYPHMETRICS glyphMetrics; // GGO_NATIVE matches the outline perfectly when Windows font smoothing is off. // GGO_NATIVE | GGO_UNHINTED does not match perfectly either when Windows font smoothing is on or off. DWORD outlineLength = GetGlyphOutline(hdc, glyph, GGO_GLYPH_INDEX | GGO_NATIVE, &glyphMetrics, 0, 0, &identity); ASSERT(outlineLength >= 0); if (outlineLength < 0) return path; Vector<UInt8> outline(outlineLength); GetGlyphOutline(hdc, glyph, GGO_GLYPH_INDEX | GGO_NATIVE, &glyphMetrics, outlineLength, outline.data(), &identity); unsigned offset = 0; while (offset < outlineLength) { LPTTPOLYGONHEADER subpath = reinterpret_cast<LPTTPOLYGONHEADER>(outline.data() + offset); ASSERT(subpath->dwType == TT_POLYGON_TYPE); if (subpath->dwType != TT_POLYGON_TYPE) return path; CGPathMoveToPoint(path, 0, toCGFloat(subpath->pfxStart.x), toCGFloat(subpath->pfxStart.y)); unsigned subpathOffset = sizeof(*subpath); while (subpathOffset < subpath->cb) { LPTTPOLYCURVE segment = reinterpret_cast<LPTTPOLYCURVE>(reinterpret_cast<UInt8*>(subpath) + subpathOffset); switch (segment->wType) { case TT_PRIM_LINE: for (unsigned i = 0; i < segment->cpfx; i++) CGPathAddLineToPoint(path, 0, toCGFloat(segment->apfx[i].x), toCGFloat(segment->apfx[i].y)); break; case TT_PRIM_QSPLINE: for (unsigned i = 0; i < segment->cpfx; i++) { CGFloat x = toCGFloat(segment->apfx[i].x); CGFloat y = toCGFloat(segment->apfx[i].y); CGFloat cpx; CGFloat cpy; if (i == segment->cpfx - 2) { cpx = toCGFloat(segment->apfx[i + 1].x); cpy = toCGFloat(segment->apfx[i + 1].y); i++; } else { cpx = (toCGFloat(segment->apfx[i].x) + toCGFloat(segment->apfx[i + 1].x)) / 2; cpy = (toCGFloat(segment->apfx[i].y) + toCGFloat(segment->apfx[i + 1].y)) / 2; } CGPathAddQuadCurveToPoint(path, 0, x, y, cpx, cpy); } break; case TT_PRIM_CSPLINE: for (unsigned i = 0; i < segment->cpfx; i += 3) { CGFloat cp1x = toCGFloat(segment->apfx[i].x); CGFloat cp1y = toCGFloat(segment->apfx[i].y); CGFloat cp2x = toCGFloat(segment->apfx[i + 1].x); CGFloat cp2y = toCGFloat(segment->apfx[i + 1].y); CGFloat x = toCGFloat(segment->apfx[i + 2].x); CGFloat y = toCGFloat(segment->apfx[i + 2].y); CGPathAddCurveToPoint(path, 0, cp1x, cp1y, cp2x, cp2y, x, y); } break; default: ASSERT_NOT_REACHED(); return path; } subpathOffset += sizeof(*segment) + (segment->cpfx - 1) * sizeof(segment->apfx[0]); } CGPathCloseSubpath(path); offset += subpath->cb; } return path; }
// CreateFontManageData のWindows環境依存処理を行う関数 static int CreateFontManageData_Win( FONTMANAGE *ManageData, int DefaultCharSet ) { int CreateFontSize ; int SampleScale ; int EnableAddHeight = FALSE ; int AddHeight = 0 ; int OrigHeight = 0 ; switch( ManageData->ImageBitDepth ) { default: case DX_FONTIMAGE_BIT_1: SampleScale = 1 ; break ; case DX_FONTIMAGE_BIT_4: SampleScale = 4 ; break ; case DX_FONTIMAGE_BIT_8: SampleScale = 16 ; break ; } CreateFontSize = ManageData->BaseInfo.FontSize * SampleScale ; CREATEFONTLABEL: // 既にフォントが作成されていたら削除 if( ManageData->FontObj != NULL ) { DeleteObject( ManageData->FontObj ) ; } if( ManageData->FontName[0] != L'\0' ) { // 特に文字セットの指定が無い場合で、且つ指定のフォント名の指定の文字セットが無い場合は文字セットを DEFAULT_CHARSET にする if( DefaultCharSet == TRUE ) { wchar_t TempNameBuffer[ 16 ][ 64 ] ; wchar_t *TempNameBufferP ; int TempNameNum ; int TempNameBufferAlloc ; int i ; TempNameNum = EnumFontName( TempNameBuffer[ 0 ], 16, ManageData->BaseInfo.CharSet, ManageData->FontName ) ; if( TempNameNum >= 16 ) { TempNameNum = EnumFontName( NULL, 0, ManageData->BaseInfo.CharSet, ManageData->FontName ) ; TempNameBufferP = ( wchar_t * )malloc( TempNameNum * 64 * sizeof( wchar_t ) ) ; TempNameNum = EnumFontName( TempNameBufferP, TempNameNum, ManageData->BaseInfo.CharSet, ManageData->FontName ) ; TempNameBufferAlloc = TRUE ; } else { TempNameBufferAlloc = FALSE ; TempNameBufferP = TempNameBuffer[ 0 ] ; } for( i = 0 ; i < TempNameNum && CL_strcmp( CODEPAGE_UTF16LE, ( char * )( TempNameBufferP + i * 64 ), ( char * )ManageData->FontName ) != 0 ; i ++ ){} if( i == TempNameNum ) { ManageData->BaseInfo.CharSet = DX_CHARSET_DEFAULT ; } if( TempNameBufferAlloc ) { free( TempNameBufferP ) ; TempNameBufferP = NULL ; } } ManageData->FontObj = CreateFontW( CreateFontSize + AddHeight, 0, 0, 0, ManageData->BaseInfo.FontThickness * 100, ManageData->BaseInfo.Italic, FALSE, FALSE, CharSetTable[ ManageData->BaseInfo.CharSet ], OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, NONANTIALIASED_QUALITY, FIXED_PITCH, ManageData->FontName ) ; if( ManageData->FontObj == NULL ) { ManageData->FontObj = CreateFontW( CreateFontSize + AddHeight, 0, 0, 0, ManageData->BaseInfo.FontThickness * 100, ManageData->BaseInfo.Italic, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, NONANTIALIASED_QUALITY, FIXED_PITCH, ManageData->FontName ) ; if( ManageData->FontObj == NULL ) { ManageData->FontObj = CreateFontW( CreateFontSize + AddHeight, 0, 0, 0, ManageData->BaseInfo.FontThickness * 100, ManageData->BaseInfo.Italic, FALSE, FALSE, SHIFTJIS_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, NONANTIALIASED_QUALITY, FIXED_PITCH, ManageData->FontName ) ; if( ManageData->FontObj == NULL ) { wprintf( L"指定のフォントの作成に失敗しました\n" ) ; goto ERR ; } } } } else { ManageData->FontObj = CreateFontW( CreateFontSize + AddHeight, 0, 0, 0, ManageData->BaseInfo.FontThickness * 100, ManageData->BaseInfo.Italic, FALSE, FALSE, CharSetTable[ ManageData->BaseInfo.CharSet ], OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, NONANTIALIASED_QUALITY, FIXED_PITCH, L"MS ゴシック" ) ; ManageData->FontName[0] = L'\0' ; } if( ManageData->FontObj == NULL ) { ManageData->FontObj = CreateFontW( CreateFontSize + AddHeight, 0, 0, 0, ManageData->BaseInfo.FontThickness * 100, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, NONANTIALIASED_QUALITY, FIXED_PITCH, L"MS ゴシック" ) ; if( ManageData->FontObj == NULL ) { ManageData->FontObj = CreateFontW( CreateFontSize + AddHeight, 0, 0, 0, ManageData->BaseInfo.FontThickness * 100, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, NONANTIALIASED_QUALITY, FIXED_PITCH, NULL ) ; } ManageData->FontName[0] = L'\0' ; if( ManageData->FontObj == NULL ) { wprintf( L"フォントの作成に失敗しました\n" ) ; goto ERR ; } } // 文字のサイズを取得する { HDC DC ; HFONT OldFont ; TEXTMETRIC TextInfo ; // デバイスコンテキストを取得 DC = CreateCompatibleDC( NULL ) ; if( DC == NULL ) { wprintf( L"デバイスコンテキストの取得に失敗しました\n" ) ; goto ERR ; } // フォントのセット OldFont = ( HFONT )SelectObject( DC, ManageData->FontObj ) ; // フォントのステータスを取得 GetTextMetrics( DC, &TextInfo ) ; // もし TextInfo.tmInternalLeading + TextInfo.tmExternalLeading が 0 ではなかったらその高さを追加してフォントを作成しなおす if( EnableAddHeight == FALSE ) { if( TextInfo.tmInternalLeading + TextInfo.tmExternalLeading > 0 ) { OrigHeight = TextInfo.tmHeight ; AddHeight = ( int )( CreateFontSize / ( 1.0f - ( float )( TextInfo.tmInternalLeading + TextInfo.tmExternalLeading ) / TextInfo.tmHeight ) ) - CreateFontSize ; EnableAddHeight = TRUE ; SelectObject( DC, OldFont ) ; DeleteDC( DC ) ; DeleteObject( ManageData->FontObj ) ; ManageData->FontObj = NULL ; goto CREATEFONTLABEL ; } ManageData->BaseInfo.FontAddHeight = 0 ; } else { ManageData->BaseInfo.FontAddHeight = ( WORD )( ( TextInfo.tmHeight - OrigHeight ) / SampleScale ) ; } // フォントの最大サイズを取得 if( ManageData->BaseInfo.Italic ) { // イタリック体の場合は最大幅が 1.35倍になる ManageData->BaseInfo.MaxWidth = ( WORD )( ( TextInfo.tmMaxCharWidth * 135 / SampleScale + 4 * 135 ) / 100 ) ; } else { ManageData->BaseInfo.MaxWidth = ( WORD )( TextInfo.tmMaxCharWidth / SampleScale + 4 ) ; } // フォントの高さを保存 ManageData->BaseInfo.FontHeight = ( WORD )( TextInfo.tmHeight / SampleScale + 1 ) ; // GetGlyphOutline が使用できるかどうかを調べる { GLYPHMETRICS gm ; MAT2 mt = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } } ; unsigned int Code ; DWORD DataSize ; memset( &gm, 0, sizeof( GLYPHMETRICS ) ) ; Code = ' ' ; DataSize = GetGlyphOutline( DC, Code, GGO_BITMAP, &gm, 0, NULL, &mt ) ; // 失敗した場合は TextOut 方式を使用する if( DataSize == GDI_ERROR ) { ManageData->UseTextOut = TRUE ; } } // TextOut 方式を使用する場合は DIB を作成しておく ManageData->CacheBitmap = NULL ; ManageData->CacheBitmapMem = NULL ; ManageData->CacheBitmapMemPitch = 0 ; if( ManageData->UseTextOut ) { BITMAPINFO *BmpInfoPlus ; BITMAP BmpData ; // イメージビット深度も DX_FONTIMAGE_BIT_1 のみ ManageData->ImageBitDepth = DX_FONTIMAGE_BIT_1 ; // キャッシュ領域のステータスの初期化 BmpInfoPlus = ( BITMAPINFO * )malloc( sizeof( BITMAPINFO ) + sizeof( RGBQUAD ) * 256 ) ; if( BmpInfoPlus == NULL ) { wprintf( L"メモリの確保に失敗しました" ) ; return -1 ; } BmpInfoPlus->bmiHeader.biSize = sizeof( BITMAPINFOHEADER ) ; BmpInfoPlus->bmiHeader.biWidth = ManageData->BaseInfo.MaxWidth ; BmpInfoPlus->bmiHeader.biHeight = -ManageData->BaseInfo.MaxWidth ; BmpInfoPlus->bmiHeader.biPlanes = 1 ; BmpInfoPlus->bmiHeader.biBitCount = 8 ; BmpInfoPlus->bmiHeader.biCompression = BI_RGB ; BmpInfoPlus->bmiHeader.biSizeImage = ( DWORD )( ManageData->BaseInfo.MaxWidth * ManageData->BaseInfo.MaxWidth ) ; // カラーパレットのセット { RGBQUAD *Color ; int i ; Color = &BmpInfoPlus->bmiColors[0] ; for( i = 0 ; i < 256 ; i ++ ) { Color->rgbBlue = ( BYTE )i ; Color->rgbRed = ( BYTE )i ; Color->rgbBlue = ( BYTE )i ; Color->rgbReserved = 0 ; Color ++ ; } } // DIBデータを作成する ManageData->CacheBitmapMem = NULL ; ManageData->CacheBitmap = CreateDIBSection( DC, BmpInfoPlus, DIB_PAL_COLORS, ( void ** )&ManageData->CacheBitmapMem, NULL, 0 ) ; // ピッチを得る GetObject( ManageData->CacheBitmap, sizeof( BITMAP ), &BmpData ) ; ManageData->CacheBitmapMemPitch = BmpData.bmWidthBytes ; // メモリの解放 free( BmpInfoPlus ) ; } // フォントを元に戻す SelectObject( DC, OldFont ) ; // デバイスコンテキストを削除する DeleteDC( DC ) ; } // 正常終了 return 0 ; // エラー処理 ERR : return -1 ; }
//�������������������������������������������������������������������������Ŀ // capture_tt � // � // Captures a TrueType font into the output IFF (already open by main) � //��������������������������������������������������������������������������� void capture_tt(char *name, char *ttname, ushort width, ushort height, byte startc, ushort numc) { int j; byte *font, *fptr; ulong fontSize; GBergFileFontHDR header; byte work[4096]; assert(name != 0 && ttname != 0 && height > 0); memset(&header,0,sizeof(header)); strncpy(header.name,name,sizeof(header.name)); if (((int)startc+(int)numc-1) > 256) { cout << "��� Error: number of characters in set must fit in 128 or 256 chars"; exit(1); } int numchars = (((int)startc+(int)numc-1) > 128) ? 256 : 128; //��� Find and select font that meets input criteria HWND hWnd = GetDesktopWindow(); assert(hWnd != 0); HDC hDC = GetDC(hWnd); assert(hDC != 0); HFONT hFont, hOldFont; OUTLINETEXTMETRIC tm; for(;;) { LOGFONT fontinfo; memset(&fontinfo,0,sizeof(fontinfo)); fontinfo.lfHeight = height; if (Flags & FLAGS_TT_BOLD) fontinfo.lfWeight = FW_BOLD; if (Flags & FLAGS_TT_ITALIC) fontinfo.lfItalic = TRUE; if (Flags & FLAGS_TT_UNDERLINE) fontinfo.lfUnderline = TRUE; if (Flags & FLAGS_TT_STRIKEOUT) fontinfo.lfStrikeOut = TRUE; if (Flags & FLAGS_TT_OEMCHARSET) fontinfo.lfCharSet = OEM_CHARSET; fontinfo.lfOutPrecision = OUT_TT_ONLY_PRECIS; strncpy(fontinfo.lfFaceName,ttname,sizeof(fontinfo.lfFaceName)); hFont = CreateFontIndirect(&fontinfo); if (!hFont) { cout << "��� Error: failed to get TrueType font with specified characteristics"; ReleaseDC(hWnd,hDC); exit(1); } hOldFont = (HFONT__*)SelectObject(hDC,hFont); memset(&tm,0,sizeof(tm)); GetOutlineTextMetrics(hDC,sizeof(tm),&tm); if (!width || tm.otmTextMetrics.tmMaxCharWidth <= width) { break; } else if (height <= 1) { cout << "��� Error: failed to get TrueType font with specified characteristics\n"; SelectObject(hDC,hOldFont); DeleteObject(hFont); ReleaseDC(hWnd,hDC); exit(1); } else { height--; SelectObject(hDC,hOldFont); } } //��� See if we can download bitmap for font... { GLYPHMETRICS glyph; MAT2 matrix; memset(&matrix,0,sizeof(matrix)); matrix.eM11.value = 1; matrix.eM22.value = 1; if (GetGlyphOutline(hDC,0,GGO_BITMAP,&glyph,0,0,&matrix) == GDI_ERROR) { cout << "��� Error: Can't capture data for specified font characteristics\n"; SelectObject(hDC,hOldFont); DeleteObject(hFont); ReleaseDC(hWnd,hDC); exit(1); } } //��� Output information about selected font if (!(Flags & FLAGS_QUIET)) { cout << "Capturing TrueType font '" << ttname << "'...\n"; cout << " Size: " << tm.otmTextMetrics.tmMaxCharWidth << " by " << tm.otmTextMetrics.tmHeight << endl; cout << " Character #" << (int)startc << " to #" << ((int)startc+(int)numc-1) << endl; if (tm.otmfsType & 0x1) cout << " *Licensed font*"; cout << " Attributes: "; switch (tm.otmTextMetrics.tmWeight) { case FW_THIN: cout << "Thin "; break; case FW_EXTRALIGHT: cout << "ExtraLight "; break; case FW_LIGHT: cout << "Light "; break; case FW_NORMAL: cout << "Normal "; break; case FW_MEDIUM: cout << "Medium "; break; case FW_SEMIBOLD: cout << "SemiBold "; break; case FW_BOLD: cout << "Bold "; break; case FW_EXTRABOLD: cout << "ExtraBold "; break; case FW_HEAVY: cout << "Heavy "; break; default: cout << "Wght(" << tm.otmTextMetrics.tmWeight << ") "; break; } if (tm.otmTextMetrics.tmItalic) cout << "Italic "; if (tm.otmTextMetrics.tmUnderlined) cout << "Underlined "; if (tm.otmTextMetrics.tmStruckOut) cout << "StruckOut "; if (tm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH) cout << "Variable-pitch "; else cout << "Fixed-pitch "; cout << endl; cout << " Family: "; if (tm.otmTextMetrics.tmPitchAndFamily & FF_MODERN) cout << "Modern "; if (tm.otmTextMetrics.tmPitchAndFamily & FF_ROMAN) cout << "Roman "; if (tm.otmTextMetrics.tmPitchAndFamily & FF_SCRIPT) cout << "Script "; if (tm.otmTextMetrics.tmPitchAndFamily & FF_SWISS) cout << "Swiss "; if (tm.otmTextMetrics.tmPitchAndFamily & FF_DECORATIVE) cout << "Decorative "; cout << endl; cout << " CharSet: "; switch (tm.otmTextMetrics.tmCharSet) { case ANSI_CHARSET: cout << "ANSI "; break; case SYMBOL_CHARSET: cout << "Symbol "; break; case OEM_CHARSET: cout << "OEM "; break; default: cout << "#" << tm.otmTextMetrics.tmCharSet; break; } cout << endl; if ((numchars > 128) && (Flags & FLAGS_TT_CP850)) { cout << " Map special characters to Codepage 850\n"; } } if (tm.otmTextMetrics.tmMaxCharWidth > 32) { cout << "��� Error: Maximum width must be less than or equal to 32"; SelectObject(hDC,hOldFont); DeleteObject(hFont); ReleaseDC(hWnd,hDC); exit(1); } //�� Font Header header.width = (ushort)tm.otmTextMetrics.tmMaxCharWidth; header.height = (ushort)tm.otmTextMetrics.tmHeight; // Only supports MONO non-COMPRESSED fonts header.compression = 0; if (tm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH) { // Variable header.type = (numchars > 128) ? GBERG_FNT_MONOVARIABLEEX : GBERG_FNT_MONOVARIABLE; header.bpc = (ushort)((((header.width + 7) >> 3) * header.height) + 1); }
bool CreateFontTexture ( IDirect3DDevice9* pDev, DWORD usage, D3DPOOL pool, UINT ch, Font* pFont ) { HDC hdc = GetDC(NULL); HFONT hFont = ::CreateFont(0, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, _T("MS UI Gothic")); HFONT hOldFont = (HFONT)::SelectObject(hdc, hFont); HRESULT hr; int gm_w, gm_h; int fnt_x, fnt_y; BYTE *bmp_p = NULL; int bmp_w, bmp_h; //bmp_p { TEXTMETRIC tm; GLYPHMETRICS gm; MAT2 mat2 = { {0,1}, {0,0}, {0,0}, {0,1} }; GetTextMetrics(hdc, &tm); DWORD bufsize = GetGlyphOutline( hdc, ch, GGO_GRAY4_BITMAP, &gm, 0, NULL, &mat2); if (bufsize == GDI_ERROR) { bufsize = GetLastError(); goto fin; } bmp_p = (BYTE*)malloc(bufsize); DWORD r = GetGlyphOutline( hdc, ch, GGO_GRAY4_BITMAP, &gm, bufsize, bmp_p, &mat2); pFont->tm_max_w = tm.tmMaxCharWidth; pFont->tm_ave_w = tm.tmAveCharWidth; gm_w = gm.gmCellIncX; gm_h = tm.tmHeight; bmp_w = ((gm.gmBlackBoxX + 3) / 4) * 4; //4-align bmp_h = gm.gmBlackBoxY; fnt_x = gm.gmptGlyphOrigin.x; fnt_y = tm.tmAscent - gm.gmptGlyphOrigin.y; } IDirect3DTexture9* pTex; hr = pDev->CreateTexture(gm_w, gm_h, 1, usage, D3DFMT_A8R8G8B8, pool, &pTex, NULL); { D3DLOCKED_RECT rect; pTex->LockRect(0, &rect, NULL, D3DLOCK_DISCARD); FillMemory(rect.pBits, rect.Pitch * gm_h, 0); for (int y=0; y<bmp_h; ++y) { BYTE* p = ((BYTE*)rect.pBits) + rect.Pitch * (fnt_y + y) + fnt_x * 4; for (int x=0; x<bmp_w; ++x) { DWORD trans = ((255 * bmp_p[x+y*bmp_w]) /16)&0xFF; DWORD color = 0x00FFFFFF | (trans << 24); memcpy(p, &color, 4); p += 4; } } pTex->UnlockRect(0); } pFont->pTex = pTex; pFont->gm_w = gm_w; pFont->gm_h = gm_h; pFont->fnt_x = fnt_x; pFont->fnt_y = fnt_y; fin: if (bmp_p) { free(bmp_p); } SelectObject(hdc, hOldFont); return true; }
BOOL bTestGGO ( HDC hdc, LPMAT2 lpmat2 ) { ULONG row = 0; // screen row coordinate to print at ULONG rowIncr; HFONT hfont; HFONT hfontOriginal; LOGFONT lfnt; TEXTMETRIC tm; ULONG cjBuffer; PBYTE pjBuffer; GLYPHMETRICS gm; // Clear the screen to black. BitBlt(hdc, 0, 0, CX, CY, (HDC) 0, 0, 0, 0); // Get a font. memset(&lfnt, 0, sizeof(lfnt)); lstrcpy(lfnt.lfFaceName, "Lucida Fax"); lfnt.lfHeight = 24; lfnt.lfWeight = 400; if ((hfont = CreateFontIndirect(&lfnt)) == NULL) { DbgPrint("ft!bTestGGO(): Logical font creation failed.\n"); return FALSE; } hfontOriginal = SelectObject(hdc, hfont); // Get textmetrics. if ( !GetTextMetrics(hdc, &tm) ) { DbgPrint("ft!bTestGGO(): GetTextMetrics failed\n"); return FALSE; } rowIncr = tm.tmHeight + tm.tmExternalLeading; // Determine buffer size needed by GetGlyphOutline. if ( (cjBuffer = (ULONG) GetGlyphOutline(hdc, 0x0054, GGO_BITMAP, &gm, 0, (PVOID) NULL, lpmat2)) == (ULONG) -1 ) { DbgPrint("ft!bTestGGO(): could not get buffer size from API\n"); return FALSE; } // Allocate memory. DbgPrint("ft!bTestGGO(): allocating 0x%lx bytes for buffer\n", cjBuffer); if ( (pjBuffer = (PBYTE) LocalAlloc(LPTR, cjBuffer)) == (PBYTE) NULL ) { DbgPrint("ft!bTestGGO(): LocalAlloc(LPTR, 0x%lx) failed\n", cjBuffer); return FALSE; } // Get the bitmap. if ( GetGlyphOutline(hdc, 0x0054, GGO_BITMAP, &gm, cjBuffer, (PVOID) pjBuffer, lpmat2) == (DWORD) -1 ) { LocalFree(pjBuffer); DbgPrint("ft!bTestGGO(): call to GetFontData failed\n"); return FALSE; } // Print out GLYPHMETRIC data. sprintf(szOutText, "gmBlackBoxX: %ld", gm.gmBlackBoxX); TextOut(hdc, 0, row, szOutText, strlen(szOutText)); row += rowIncr; sprintf(szOutText, "gmBlackBoxY: %ld", gm.gmBlackBoxY); TextOut(hdc, 0, row, szOutText, strlen(szOutText)); row += rowIncr; sprintf(szOutText, "gmptGlyphOrigin: (%ld, %ld)", gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y); TextOut(hdc, 0, row, szOutText, strlen(szOutText)); row += rowIncr; sprintf(szOutText, "gmCellIncX: %ld", gm.gmCellIncX); TextOut(hdc, 0, row, szOutText, strlen(szOutText)); row += rowIncr; sprintf(szOutText, "gmCellIncY: %ld", gm.gmCellIncY); TextOut(hdc, 0, row, szOutText, strlen(szOutText)); row += rowIncr; // Flush out batched calls. GdiFlush(); // Allow an opportunity to examine the contents. DbgPrint("ft!bTestGGO(): call to GetGlyphOutline succeeded\n"); DbgPrint("\tbitmap is at 0x%lx, size is 0x%lx (%ld)\n", pjBuffer, cjBuffer, cjBuffer); DbgBreakPoint(); // Restore the font. SelectObject(hdc, hfontOriginal); DeleteObject(hfont); return TRUE; }
void font_instance::LoadGlyph(int glyph_id) { if ( pFont == NULL ) { return; } InitTheFace(); #ifndef USE_PANGO_WIN32 if ( !FT_IS_SCALABLE(theFace) ) { return; // bitmap font } #endif if ( id_to_no.find(glyph_id) == id_to_no.end() ) { Geom::PathBuilder path_builder; if ( nbGlyph >= maxGlyph ) { maxGlyph=2*nbGlyph+1; glyphs=(font_glyph*)realloc(glyphs,maxGlyph*sizeof(font_glyph)); } font_glyph n_g; n_g.pathvector=NULL; n_g.bbox[0]=n_g.bbox[1]=n_g.bbox[2]=n_g.bbox[3]=0; n_g.h_advance = 0; n_g.v_advance = 0; n_g.h_width = 0; n_g.v_width = 0; bool doAdd=false; #ifdef USE_PANGO_WIN32 #ifndef GGO_UNHINTED // For compatibility with old SDKs. #define GGO_UNHINTED 0x0100 #endif MAT2 identity = {{0,1},{0,0},{0,0},{0,1}}; OUTLINETEXTMETRIC otm; GetOutlineTextMetrics(daddy->hScreenDC, sizeof(otm), &otm); GLYPHMETRICS metrics; DWORD bufferSize=GetGlyphOutline (daddy->hScreenDC, glyph_id, GGO_GLYPH_INDEX | GGO_NATIVE | GGO_UNHINTED, &metrics, 0, NULL, &identity); double scale=1.0/daddy->fontSize; n_g.h_advance=metrics.gmCellIncX*scale; n_g.v_advance=otm.otmTextMetrics.tmHeight*scale; n_g.h_width=metrics.gmBlackBoxX*scale; n_g.v_width=metrics.gmBlackBoxY*scale; if ( bufferSize == GDI_ERROR) { // shit happened } else if ( bufferSize == 0) { // character has no visual representation, but is valid (eg whitespace) doAdd=true; } else { char *buffer = new char[bufferSize]; if ( GetGlyphOutline (daddy->hScreenDC, glyph_id, GGO_GLYPH_INDEX | GGO_NATIVE | GGO_UNHINTED, &metrics, bufferSize, buffer, &identity) <= 0 ) { // shit happened } else { // Platform SDK is rubbish, read KB87115 instead DWORD polyOffset=0; while ( polyOffset < bufferSize ) { TTPOLYGONHEADER const *polyHeader=(TTPOLYGONHEADER const *)(buffer+polyOffset); if (polyOffset+polyHeader->cb > bufferSize) break; if (polyHeader->dwType == TT_POLYGON_TYPE) { path_builder.moveTo(pointfx_to_nrpoint(polyHeader->pfxStart, scale)); DWORD curveOffset=polyOffset+sizeof(TTPOLYGONHEADER); while ( curveOffset < polyOffset+polyHeader->cb ) { TTPOLYCURVE const *polyCurve=(TTPOLYCURVE const *)(buffer+curveOffset); POINTFX const *p=polyCurve->apfx; POINTFX const *endp=p+polyCurve->cpfx; switch (polyCurve->wType) { case TT_PRIM_LINE: while ( p != endp ) path_builder.lineTo(pointfx_to_nrpoint(*p++, scale)); break; case TT_PRIM_QSPLINE: { g_assert(polyCurve->cpfx >= 2); // The list of points specifies one or more control points and ends with the end point. // The intermediate points (on the curve) are the points between the control points. Geom::Point this_control = pointfx_to_nrpoint(*p++, scale); while ( p+1 != endp ) { // Process all "midpoints" (all points except the last) Geom::Point new_control = pointfx_to_nrpoint(*p++, scale); path_builder.quadTo(this_control, (new_control+this_control)/2); this_control = new_control; } Geom::Point end = pointfx_to_nrpoint(*p++, scale); path_builder.quadTo(this_control, end); } break; case 3: // TT_PRIM_CSPLINE g_assert(polyCurve->cpfx % 3 == 0); while ( p != endp ) { path_builder.curveTo(pointfx_to_nrpoint(p[0], scale), pointfx_to_nrpoint(p[1], scale), pointfx_to_nrpoint(p[2], scale)); p += 3; } break; } curveOffset += sizeof(TTPOLYCURVE)+sizeof(POINTFX)*(polyCurve->cpfx-1); } } polyOffset += polyHeader->cb; } doAdd=true; } delete [] buffer; } #else if (FT_Load_Glyph (theFace, glyph_id, FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP)) { // shit happened } else { if ( FT_HAS_HORIZONTAL(theFace) ) { n_g.h_advance=((double)theFace->glyph->metrics.horiAdvance)/((double)theFace->units_per_EM); n_g.h_width=((double)theFace->glyph->metrics.width)/((double)theFace->units_per_EM); } else { n_g.h_width=n_g.h_advance=((double)(theFace->bbox.xMax-theFace->bbox.xMin))/((double)theFace->units_per_EM); } if ( FT_HAS_VERTICAL(theFace) ) { n_g.v_advance=((double)theFace->glyph->metrics.vertAdvance)/((double)theFace->units_per_EM); n_g.v_width=((double)theFace->glyph->metrics.height)/((double)theFace->units_per_EM); } else { n_g.v_width=n_g.v_advance=((double)theFace->height)/((double)theFace->units_per_EM); } if ( theFace->glyph->format == ft_glyph_format_outline ) { FT_Outline_Funcs ft2_outline_funcs = { ft2_move_to, ft2_line_to, ft2_conic_to, ft2_cubic_to, 0, 0 }; FT2GeomData user(path_builder, 1.0/((double)theFace->units_per_EM)); FT_Outline_Decompose (&theFace->glyph->outline, &ft2_outline_funcs, &user); } doAdd=true; } #endif path_builder.finish(); if ( doAdd ) { Geom::PathVector pv = path_builder.peek(); // close all paths for (Geom::PathVector::iterator i = pv.begin(); i != pv.end(); ++i) { i->close(); } if ( !pv.empty() ) { n_g.pathvector = new Geom::PathVector(pv); Geom::OptRect bounds = bounds_exact(*n_g.pathvector); if (bounds) { n_g.bbox[0] = bounds->left(); n_g.bbox[1] = bounds->top(); n_g.bbox[2] = bounds->right(); n_g.bbox[3] = bounds->bottom(); } } glyphs[nbGlyph]=n_g; id_to_no[glyph_id]=nbGlyph; nbGlyph++; } } else { } }