1// Copyright 2014 PDFium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7#include "xfa/src/fgas/src/fgas_base.h"
8#include "fx_gdifont.h"
9#include "fx_stdfontmgr.h"
10#ifdef _FXPLUS
11#if _FX_OS_ == _FX_WIN32_DESKTOP_ || _FX_OS_ == _FX_WIN32_MOBILE_ || \
12    _FX_OS_ == _FX_WIN64_
13CFX_GdiFontCache::CFX_GdiFontCache() : m_GlyphMap(128) {}
14CFX_GdiFontCache::~CFX_GdiFontCache() {
15  FX_POSITION pos = m_GlyphMap.GetStartPosition();
16  int32_t iGlyph;
17  FX_LPGDIGOCACHE pGlyph;
18  while (pos != NULL) {
19    pGlyph = NULL;
20    m_GlyphMap.GetNextAssoc(pos, (void*&)iGlyph, (void*&)pGlyph);
21    if (pGlyph != NULL) {
22      FX_Free(pGlyph->pOutline);
23      FX_Free(pGlyph);
24    }
25  }
26  m_GlyphMap.RemoveAll();
27}
28void CFX_GdiFontCache::SetCachedGlyphOutline(FX_DWORD dwGlyph,
29                                             const GLYPHMETRICS& gm,
30                                             uint8_t* pOutline) {
31  FXSYS_assert(pOutline != NULL);
32  FX_LPGDIGOCACHE pGlyph = FX_Alloc(FX_GDIGOCACHE, 1);
33  pGlyph->gm = gm;
34  pGlyph->pOutline = pOutline;
35  m_GlyphMap.SetAt((void*)dwGlyph, (void*)pGlyph);
36}
37FX_LPCGDIGOCACHE CFX_GdiFontCache::GetCachedGlyphOutline(
38    FX_DWORD dwGlyph) const {
39  FX_LPCGDIGOCACHE pGlyph = NULL;
40  if (!m_GlyphMap.Lookup((void*)dwGlyph, (void*&)pGlyph)) {
41    return FALSE;
42  }
43  return pGlyph;
44}
45IFX_Font* IFX_Font::LoadFont(const FX_WCHAR* pszFontFamily,
46                             FX_DWORD dwFontStyles,
47                             FX_WORD wCodePage,
48                             IFX_FontMgr* pFontMgr) {
49  CFX_GdiFont* pFont = new CFX_GdiFont(pFontMgr);
50  if (!pFont->LoadFont(pszFontFamily, dwFontStyles, wCodePage)) {
51    pFont->Release();
52    return NULL;
53  }
54  return pFont;
55}
56IFX_Font* IFX_Font::LoadFont(const uint8_t* pBuffer,
57                             int32_t iLength,
58                             IFX_FontMgr* pFontMgr) {
59  CFX_GdiFont* pFont = new CFX_GdiFont(pFontMgr);
60  if (!pFont->LoadFont(pBuffer, iLength)) {
61    pFont->Release();
62    return NULL;
63  }
64  return pFont;
65}
66IFX_Font* IFX_Font::LoadFont(const FX_WCHAR* pszFileName,
67                             IFX_FontMgr* pFontMgr) {
68  CFX_GdiFont* pFont = new CFX_GdiFont(pFontMgr);
69  if (!pFont->LoadFont(pszFileName)) {
70    pFont->Release();
71    return NULL;
72  }
73  return pFont;
74}
75IFX_Font* IFX_Font::LoadFont(IFX_Stream* pFontStream,
76                             IFX_FontMgr* pFontMgr,
77                             FX_BOOL bSaveStream) {
78  CFX_GdiFont* pFont = new CFX_GdiFont(pFontMgr);
79  if (!pFont->LoadFont(pFontStream)) {
80    pFont->Release();
81    return NULL;
82  }
83  return pFont;
84}
85IFX_Font* IFX_Font::LoadFont(CFX_Font* pExtFont, IFX_FontMgr* pFontMgr) {
86  FXSYS_assert(FALSE);
87  return NULL;
88}
89#define FX_GDIFONT_FONTCACHESIZE 8
90CFX_GdiFont::CFX_GdiFont(IFX_FontMgr* pFontMgr)
91    : m_pFontMgr(pFontMgr),
92      m_iRefCount(1),
93      m_WidthCache(1024),
94      m_hOldFont(NULL),
95      m_hFont(NULL),
96      m_hDC(NULL),
97      m_wsFontFileName(),
98      m_FontFamilies(),
99      m_hRes(NULL),
100      m_dwStyles(0),
101      m_SubstFonts(),
102      m_FontMapper(16),
103      m_FontCache(FX_GDIFONT_FONTCACHESIZE) {
104  m_hDC = ::CreateCompatibleDC(NULL);
105  FX_memset(&m_LogFont, 0, sizeof(m_LogFont));
106  FXSYS_assert(m_hDC != NULL);
107}
108CFX_GdiFont::~CFX_GdiFont() {
109  int32_t iCount = m_SubstFonts.GetSize();
110  for (int32_t i = 0; i < iCount; i++) {
111    IFX_Font* pFont = (IFX_Font*)m_SubstFonts[i];
112    pFont->Release();
113  }
114  m_SubstFonts.RemoveAll();
115  m_FontMapper.RemoveAll();
116  if (m_hFont != NULL) {
117    ::SelectObject(m_hDC, m_hOldFont);
118    ::DeleteObject(m_hFont);
119  }
120  ::DeleteDC(m_hDC);
121  if (m_hRes != NULL) {
122    if (m_wsFontFileName.GetLength() > 0) {
123      ::RemoveFontResourceW((const FX_WCHAR*)m_wsFontFileName);
124    } else {
125      ::RemoveFontMemResourceEx(m_hRes);
126    }
127  }
128  m_WidthCache.RemoveAll();
129  ClearCache();
130}
131void CFX_GdiFont::ClearCache() {
132  int32_t iCount = m_SubstFonts.GetSize();
133  for (int32_t i = 0; i < iCount; i++) {
134    IFX_Font* pFont = (IFX_Font*)m_SubstFonts[i];
135    ((CFX_GdiFont*)pFont)->ClearCache();
136  }
137  FX_POSITION pos = m_FontCache.GetStartPosition();
138  FX_DWORD dwMAT2;
139  CFX_GdiFontCache* pCache;
140  while (pos != NULL) {
141    pCache = NULL;
142    m_FontCache.GetNextAssoc(pos, (void*&)dwMAT2, (void*&)pCache);
143    if (pCache != NULL) {
144      delete pCache;
145    }
146  }
147  m_FontCache.RemoveAll();
148}
149void CFX_GdiFont::Release() {
150  if (--m_iRefCount < 1) {
151    if (m_pFontMgr != NULL) {
152      m_pFontMgr->RemoveFont(this);
153    }
154    delete this;
155  }
156}
157IFX_Font* CFX_GdiFont::Retain() {
158  ++m_iRefCount;
159  return this;
160}
161FX_BOOL CFX_GdiFont::LoadFont(const FX_WCHAR* pszFontFamily,
162                              FX_DWORD dwFontStyles,
163                              FX_WORD wCodePage) {
164  FXSYS_assert(m_hFont == NULL);
165  LOGFONTW lf;
166  FX_memset(&lf, 0, sizeof(lf));
167  lf.lfHeight = -1000;
168  lf.lfWeight = (dwFontStyles & FX_FONTSTYLE_Bold) ? FW_BOLD : FW_NORMAL;
169  lf.lfItalic = (dwFontStyles & FX_FONTSTYLE_Italic) != 0;
170  lf.lfPitchAndFamily =
171      (dwFontStyles & FX_FONTSTYLE_FixedPitch) ? FIXED_PITCH : VARIABLE_PITCH;
172  if (dwFontStyles & FX_FONTSTYLE_Serif) {
173    lf.lfPitchAndFamily |= FF_ROMAN;
174  }
175  if (dwFontStyles & FX_FONTSTYLE_Script) {
176    lf.lfPitchAndFamily |= FF_SCRIPT;
177  }
178  if (dwFontStyles & FX_FONTSTYLE_Symbolic) {
179    lf.lfCharSet = SYMBOL_CHARSET;
180  } else {
181    FX_WORD wCharSet = FX_GetCharsetFromCodePage(wCodePage);
182    lf.lfCharSet = wCharSet != 0xFFFF ? (uint8_t)wCharSet : DEFAULT_CHARSET;
183  }
184  if (pszFontFamily == NULL) {
185    lf.lfFaceName[0] = L'\0';
186  } else {
187    FXSYS_wcsncpy(lf.lfFaceName, pszFontFamily, 31);
188  }
189  return LoadFont(lf);
190}
191FX_BOOL CFX_GdiFont::LoadFont(const uint8_t* pBuffer, int32_t iLength) {
192  FXSYS_assert(m_hFont == NULL && pBuffer != NULL && iLength > 0);
193  Gdiplus::PrivateFontCollection pfc;
194  if (pfc.AddMemoryFont(pBuffer, iLength) != Gdiplus::Ok) {
195    return FALSE;
196  }
197  if (GetFontFamilies(pfc) < 1) {
198    return FALSE;
199  }
200  FX_DWORD dwCount = 0;
201  m_hRes = ::AddFontMemResourceEx((void*)pBuffer, iLength, 0, &dwCount);
202  if (m_hRes == NULL) {
203    return FALSE;
204  }
205  CFX_WideString wsFamily = m_FontFamilies[0];
206  m_hFont =
207      ::CreateFontW(-1000, 0, 0, 0, FW_NORMAL, FALSE, 0, 0, DEFAULT_CHARSET, 0,
208                    0, 0, 0, (const FX_WCHAR*)wsFamily);
209  if (m_hFont == NULL) {
210    ::RemoveFontMemResourceEx(m_hRes);
211    m_hRes = NULL;
212    return FALSE;
213  }
214  RetrieveFontStyles();
215  m_hOldFont = ::SelectObject(m_hDC, m_hFont);
216  ::GetOutlineTextMetricsW(m_hDC, sizeof(m_OutlineTM), &m_OutlineTM);
217  return TRUE;
218}
219FX_BOOL CFX_GdiFont::LoadFont(const FX_WCHAR* pszFileName) {
220  FXSYS_assert(m_hFont == NULL && pszFileName != NULL);
221  Gdiplus::PrivateFontCollection pfc;
222  if (pfc.AddFontFile(pszFileName) != Gdiplus::Ok) {
223    return FALSE;
224  }
225  if (GetFontFamilies(pfc) < 1) {
226    return FALSE;
227  }
228  m_wsFontFileName = pszFileName;
229  m_hRes = (HANDLE)::AddFontResourceW(pszFileName);
230  if (m_hRes == NULL) {
231    return FALSE;
232  }
233  CFX_WideString wsFamily = m_FontFamilies[0];
234  m_hFont =
235      ::CreateFontW(-1000, 0, 0, 0, FW_NORMAL, FALSE, 0, 0, DEFAULT_CHARSET, 0,
236                    0, 0, 0, (const FX_WCHAR*)wsFamily);
237  if (m_hFont == NULL) {
238    ::RemoveFontResourceW(pszFileName);
239    m_hRes = NULL;
240    return FALSE;
241  }
242  RetrieveFontStyles();
243  ::SelectObject(m_hDC, m_hFont);
244  ::GetOutlineTextMetricsW(m_hDC, sizeof(m_OutlineTM), &m_OutlineTM);
245  return TRUE;
246}
247FX_BOOL CFX_GdiFont::LoadFont(IFX_Stream* pFontStream) {
248  FXSYS_assert(m_hFont == NULL && pFontStream != NULL);
249  int32_t iLength = pFontStream->GetLength();
250  if (iLength < 1) {
251    return FALSE;
252  }
253  uint8_t* pBuf = FX_Alloc(uint8_t, iLength);
254  iLength = pFontStream->ReadData(pBuf, iLength);
255  FX_BOOL bRet = LoadFont(pBuf, iLength);
256  FX_Free(pBuf);
257  return bRet;
258}
259FX_BOOL CFX_GdiFont::LoadFont(const LOGFONTW& lf) {
260  FXSYS_assert(m_hFont == NULL);
261  m_hFont = ::CreateFontIndirectW((LPLOGFONTW)&lf);
262  if (m_hFont == NULL) {
263    return FALSE;
264  }
265  RetrieveFontStyles();
266  ::SelectObject(m_hDC, m_hFont);
267  ::GetOutlineTextMetricsW(m_hDC, sizeof(m_OutlineTM), &m_OutlineTM);
268  return TRUE;
269}
270int32_t CFX_GdiFont::GetFontFamilies(Gdiplus::FontCollection& fc) {
271  int32_t iCount = fc.GetFamilyCount();
272  if (iCount < 1) {
273    return iCount;
274  }
275  Gdiplus::FontFamily* pFontFamilies = FX_Alloc(Gdiplus::FontFamily, iCount);
276  int32_t iFind = 0;
277  fc.GetFamilies(iCount, pFontFamilies, &iFind);
278  for (int32_t i = 0; i < iCount; i++) {
279    CFX_WideString wsFamilyName;
280    FX_WCHAR* pName = wsFamilyName.GetBuffer(LF_FACESIZE);
281    pFontFamilies[i].GetFamilyName(pName);
282    wsFamilyName.ReleaseBuffer();
283    m_FontFamilies.Add(wsFamilyName);
284  }
285  FX_Free(pFontFamilies);
286  return iCount;
287}
288void CFX_GdiFont::RetrieveFontStyles() {
289  FXSYS_assert(m_hFont != NULL);
290  FX_memset(&m_LogFont, 0, sizeof(m_LogFont));
291  ::GetObjectW(m_hFont, sizeof(m_LogFont), &m_LogFont);
292  m_dwStyles = FX_GetGdiFontStyles(m_LogFont);
293}
294void CFX_GdiFont::GetFamilyName(CFX_WideString& wsFamily) const {
295  FXSYS_assert(m_hFont != NULL);
296  wsFamily = m_LogFont.lfFaceName;
297}
298FX_BOOL CFX_GdiFont::GetCharWidth(FX_WCHAR wUnicode,
299                                  int32_t& iWidth,
300                                  FX_BOOL bRecursive,
301                                  FX_BOOL bCharCode) {
302  iWidth = (int32_t)(int16_t)m_WidthCache.GetAt(wUnicode, 0);
303  if (iWidth == 0 || iWidth == -1) {
304    IFX_Font* pFont = NULL;
305    int32_t iGlyph = GetGlyphIndex(wUnicode, TRUE, &pFont, bCharCode);
306    if (iGlyph != 0xFFFF && pFont != NULL) {
307      if (pFont == (IFX_Font*)this) {
308        if (!::GetCharWidthI(m_hDC, iGlyph, 1, NULL, &iWidth)) {
309          iWidth = -1;
310        }
311      } else if (((CFX_GdiFont*)pFont)
312                     ->GetCharWidth(wUnicode, iWidth, FALSE, bCharCode)) {
313        return TRUE;
314      }
315    } else {
316      iWidth = -1;
317    }
318    Lock();
319    m_WidthCache.SetAtGrow(wUnicode, (int16_t)iWidth);
320    Unlock();
321  }
322  return iWidth > 0;
323}
324FX_BOOL CFX_GdiFont::GetCharWidth(FX_WCHAR wUnicode,
325                                  int32_t& iWidth,
326                                  FX_BOOL bCharCode) {
327  return GetCharWidth(wUnicode, iWidth, TRUE, bCharCode);
328}
329int32_t CFX_GdiFont::GetGlyphIndex(FX_WCHAR wUnicode,
330                                   FX_BOOL bRecursive,
331                                   IFX_Font** ppFont,
332                                   FX_BOOL bCharCode) {
333  int32_t iGlyph = 0XFFFF;
334  if (::GetGlyphIndicesW(m_hDC, &wUnicode, 1, (LPWORD)&iGlyph,
335                         GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR &&
336      iGlyph != 0xFFFF) {
337    if (ppFont != NULL) {
338      *ppFont = (IFX_Font*)this;
339    }
340    return iGlyph;
341  }
342  FX_LPCFONTUSB pFontUSB = FX_GetUnicodeBitField(wUnicode);
343  if (pFontUSB == NULL) {
344    return 0xFFFF;
345  }
346  FX_WORD wBitField = pFontUSB->wBitField;
347  if (wBitField >= 128) {
348    return 0xFFFF;
349  }
350  IFX_Font* pFont = NULL;
351  m_FontMapper.Lookup((void*)wBitField, (void*&)pFont);
352  if (pFont != NULL && pFont != (IFX_Font*)this) {
353    iGlyph =
354        ((CFX_GdiFont*)pFont)->GetGlyphIndex(wUnicode, FALSE, NULL, bCharCode);
355    if (iGlyph != 0xFFFF) {
356      int32_t i = m_SubstFonts.Find(pFont);
357      if (i > -1) {
358        iGlyph |= ((i + 1) << 24);
359        if (ppFont != NULL) {
360          *ppFont = pFont;
361        }
362        return iGlyph;
363      }
364    }
365  }
366  if (m_pFontMgr != NULL && bRecursive) {
367    IFX_Font* pFont = m_pFontMgr->GetDefFontByUnicode(wUnicode, m_dwStyles,
368                                                      m_LogFont.lfFaceName);
369    if (pFont != NULL) {
370      if (pFont == (IFX_Font*)this) {
371        pFont->Release();
372        return 0xFFFF;
373      }
374      m_FontMapper.SetAt((void*)wBitField, (void*)pFont);
375      int32_t i = m_SubstFonts.GetSize();
376      m_SubstFonts.Add(pFont);
377      iGlyph = ((CFX_GdiFont*)pFont)
378                   ->GetGlyphIndex(wUnicode, FALSE, NULL, bCharCode);
379      if (iGlyph != 0xFFFF) {
380        iGlyph |= ((i + 1) << 24);
381        if (ppFont != NULL) {
382          *ppFont = pFont;
383        }
384        return iGlyph;
385      }
386    }
387  }
388  return 0xFFFF;
389}
390int32_t CFX_GdiFont::GetGlyphIndex(FX_WCHAR wUnicode, FX_BOOL bCharCode) {
391  return GetGlyphIndex(wUnicode, TRUE, NULL, bCharCode);
392}
393int32_t CFX_GdiFont::GetAscent() const {
394  return m_OutlineTM.otmAscent;
395}
396int32_t CFX_GdiFont::GetDescent() const {
397  return m_OutlineTM.otmDescent;
398}
399FX_BOOL CFX_GdiFont::GetCharBBox(FX_WCHAR wUnicode,
400                                 CFX_Rect& bbox,
401                                 FX_BOOL bCharCode) {
402  int32_t iGlyphIndex = GetGlyphIndex(wUnicode, bCharCode);
403  if (iGlyphIndex == 0xFFFF) {
404    return FALSE;
405  }
406  IFX_Font* pFont = GetSubstFont(iGlyphIndex);
407  if (pFont == NULL) {
408    return FALSE;
409  }
410  GLYPHMETRICS gm;
411  iGlyphIndex &= 0x00FFFFFF;
412  static const MAT2 mat2 = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
413  if (::GetGlyphOutlineW(((CFX_GdiFont*)pFont)->m_hDC, iGlyphIndex,
414                         GGO_GLYPH_INDEX | GGO_METRICS, &gm, 0, NULL,
415                         &mat2) != GDI_ERROR) {
416    bbox.left = gm.gmptGlyphOrigin.x;
417    bbox.top = gm.gmptGlyphOrigin.y;
418    bbox.width = gm.gmBlackBoxX;
419    bbox.height = gm.gmBlackBoxY;
420    return TRUE;
421  }
422  return FALSE;
423}
424FX_BOOL CFX_GdiFont::GetBBox(CFX_Rect& bbox) {
425  bbox.left = m_OutlineTM.otmrcFontBox.left;
426  bbox.top = m_OutlineTM.otmrcFontBox.top;
427  bbox.width = m_OutlineTM.otmrcFontBox.right - m_OutlineTM.otmrcFontBox.left;
428  bbox.height = m_OutlineTM.otmrcFontBox.bottom - m_OutlineTM.otmrcFontBox.top;
429  return TRUE;
430}
431int32_t CFX_GdiFont::GetItalicAngle() const {
432  return m_OutlineTM.otmItalicAngle / 10;
433}
434void CFX_GdiFont::Reset() {
435  Lock();
436  m_WidthCache.RemoveAll();
437  ClearCache();
438  Unlock();
439}
440IFX_Font* CFX_GdiFont::GetSubstFont(int32_t iGlyphIndex) const {
441  int32_t iHigher = (iGlyphIndex & 0x7F000000) >> 24;
442  if (iHigher == 0) {
443    return (IFX_Font*)this;
444  }
445  if (iHigher > m_SubstFonts.GetSize()) {
446    return (IFX_Font*)this;
447  }
448  return (IFX_Font*)m_SubstFonts[iHigher - 1];
449}
450FX_DWORD CFX_GdiFont::GetGlyphDIBits(int32_t iGlyphIndex,
451                                     FX_ARGB argb,
452                                     const MAT2* pMatrix,
453                                     GLYPHMETRICS& gm,
454                                     void* pBuffer,
455                                     FX_DWORD bufSize) {
456  static const UINT uFormat = GGO_GLYPH_INDEX | GGO_GRAY8_BITMAP;
457  IFX_Font* pFont = GetSubstFont(iGlyphIndex);
458  if (pFont == NULL) {
459    return 0;
460  }
461  if (pFont != (IFX_Font*)this) {
462    return ((CFX_GdiFont*)pFont)
463        ->GetGlyphDIBits(iGlyphIndex & 0x00FFFFFF, argb, pMatrix, gm, pBuffer,
464                         bufSize);
465  }
466  uint8_t* pGlyphOutline = NULL;
467  FXSYS_assert(pMatrix != NULL);
468  FX_DWORD dwMAT2 = GetMAT2HashCode((const FIXED*)pMatrix);
469  CFX_GdiFontCache* pCache = NULL;
470  if (m_FontCache.Lookup((void*)dwMAT2, (void*&)pCache) && pCache != NULL) {
471    FX_LPCGDIGOCACHE pGO = pCache->GetCachedGlyphOutline(iGlyphIndex);
472    if (pGO != NULL) {
473      gm = pGO->gm;
474      pGlyphOutline = pGO->pOutline;
475    }
476  }
477  if (pGlyphOutline == NULL) {
478    FX_DWORD dwGlyphSize =
479        ::GetGlyphOutlineW(m_hDC, iGlyphIndex, uFormat, &gm, 0, NULL, pMatrix);
480    if (dwGlyphSize == 0 || dwGlyphSize == GDI_ERROR) {
481      return 0;
482    }
483    pGlyphOutline = FX_Alloc(uint8_t, dwGlyphSize);
484    ::GetGlyphOutlineW(m_hDC, iGlyphIndex, uFormat, &gm, dwGlyphSize,
485                       pGlyphOutline, pMatrix);
486    if (pCache == NULL) {
487      pCache = new CFX_GdiFontCache;
488      if (m_FontCache.GetCount() >= FX_GDIFONT_FONTCACHESIZE) {
489        ClearCache();
490      }
491      m_FontCache.SetAt((void*)dwMAT2, (void*)pCache);
492    }
493    pCache->SetCachedGlyphOutline(iGlyphIndex, gm, pGlyphOutline);
494  }
495  FX_DWORD dwDibSize = gm.gmBlackBoxX * 4 * gm.gmBlackBoxY;
496  if (pBuffer == NULL || bufSize < dwDibSize) {
497    return dwDibSize;
498  }
499  CreateGlyphBitmap(gm.gmBlackBoxX, gm.gmBlackBoxY, pGlyphOutline,
500                    (FX_DWORD*)pBuffer, argb);
501  return dwDibSize;
502}
503FX_DWORD CFX_GdiFont::GetHashCode() const {
504  return FX_GetFontHashCode(FX_GetCodePageFromCharset(m_LogFont.lfCharSet),
505                            FX_GetGdiFontStyles(m_LogFont));
506}
507FX_DWORD CFX_GdiFont::GetMAT2HashCode(const FIXED* pFixed) {
508  FXSYS_assert(pFixed != NULL);
509  FX_DWORD dwHash1 = 0, dwHash2 = 5381, dwRet;
510  for (int i = 0; i < 4; i++) {
511    dwRet = *((const FX_DWORD*)pFixed);
512    dwHash1 = 1313 * dwHash1 + dwRet;
513    dwHash2 += (dwHash2 << 5) + dwRet;
514    pFixed++;
515  }
516  return ((dwHash1 & 0x0000FFFF) << 16) | (dwHash2 & 0x0000FFFF);
517}
518void CFX_GdiFont::CreateGlyphBitmap(int32_t iWidth,
519                                    int32_t iHeight,
520                                    uint8_t* pOutline,
521                                    FX_DWORD* pDIB,
522                                    FX_ARGB argb) {
523  int32_t padding = ((iWidth + 3) / 4) * 4 - iWidth;
524  FX_DWORD alpha;
525  int32_t i, j;
526  for (j = iHeight - 1; j >= 0; --j) {
527    for (i = iWidth - 1; i >= 0; --i) {
528      if ((alpha = *pOutline++) == 0) {
529        *pDIB++ = 0;
530      } else {
531        alpha = (argb >> 24) * (alpha * 4 - 1) / 256;
532        *pDIB++ = (alpha << 24) | (argb & 0x00FFFFFF);
533      }
534    }
535    pOutline += padding;
536  }
537}
538#endif
539#endif
540