blob: 5ea55bd7a43b5f5399e003c426359e5afbfcb927 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@google.comac6b9792011-03-11 15:42:51 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@google.comac6b9792011-03-11 15:42:51 +00007 */
8
reed@google.comac6b9792011-03-11 15:42:51 +00009#include "SkAdvancedTypefaceMetrics.h"
bungeman@google.coma5501992012-05-18 19:06:41 +000010#include "SkBase64.h"
bungeman@google.com1bfe01d2012-08-28 16:02:42 +000011#include "SkColorPriv.h"
bungeman@google.coma5501992012-05-18 19:06:41 +000012#include "SkData.h"
13#include "SkDescriptor.h"
bungeman@google.come70f7982012-06-01 19:38:19 +000014#include "SkFontDescriptor.h"
bungeman@google.coma5501992012-05-18 19:06:41 +000015#include "SkFontHost.h"
bungeman@google.combbe50132012-07-24 20:33:21 +000016#include "SkGlyph.h"
bungeman@google.com27f74aa2013-10-08 21:32:15 +000017#include "SkHRESULT.h"
bungeman@google.com97efada2012-07-30 20:40:50 +000018#include "SkMaskGamma.h"
bungeman@google.comd3fbd342014-04-15 15:52:07 +000019#include "SkMatrix22.h"
bungeman@google.com7bdd6142013-07-15 19:42:57 +000020#include "SkOTTable_maxp.h"
bungeman@google.coma9802692013-08-07 02:45:25 +000021#include "SkOTTable_name.h"
bungeman@google.coma5501992012-05-18 19:06:41 +000022#include "SkOTUtils.h"
reed@google.com27889872012-08-07 16:15:13 +000023#include "SkPath.h"
bungeman@google.comb10b51f2013-08-01 20:18:41 +000024#include "SkSFNTHeader.h"
reed@google.comac6b9792011-03-11 15:42:51 +000025#include "SkStream.h"
bungeman@google.coma5501992012-05-18 19:06:41 +000026#include "SkString.h"
bungeman@google.com05a729f2013-06-20 15:29:16 +000027#include "SkTemplates.h"
reed@google.comac6b9792011-03-11 15:42:51 +000028#include "SkThread.h"
29#include "SkTypeface_win.h"
reed@google.com59d2f632011-05-02 19:36:59 +000030#include "SkTypefaceCache.h"
reed@google.comac6b9792011-03-11 15:42:51 +000031#include "SkUtils.h"
32
bungeman@google.coma5501992012-05-18 19:06:41 +000033#include "SkTypes.h"
34#include <tchar.h>
35#include <usp10.h>
36#include <objbase.h>
reed@google.comac6b9792011-03-11 15:42:51 +000037
reed@google.comf210f502013-03-20 14:15:52 +000038static void (*gEnsureLOGFONTAccessibleProc)(const LOGFONT&);
39
40void SkTypeface_SetEnsureLOGFONTAccessibleProc(void (*proc)(const LOGFONT&)) {
41 gEnsureLOGFONTAccessibleProc = proc;
42}
43
reed@google.com055180c2013-03-21 18:46:35 +000044static void call_ensure_accessible(const LOGFONT& lf) {
45 if (gEnsureLOGFONTAccessibleProc) {
46 gEnsureLOGFONTAccessibleProc(lf);
47 }
48}
49
50///////////////////////////////////////////////////////////////////////////////
51
reed@google.com6f5df482011-09-28 20:33:24 +000052// always packed xxRRGGBB
53typedef uint32_t SkGdiRGB;
54
reed@google.coma767fa02011-08-05 21:40:26 +000055// define this in your Makefile or .gyp to enforce AA requests
56// which GDI ignores at small sizes. This flag guarantees AA
57// for rotated text, regardless of GDI's notions.
58//#define SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
59
reed@google.com82a34d82011-07-26 19:33:08 +000060static bool isLCD(const SkScalerContext::Rec& rec) {
reedd54d3fc2014-11-13 14:39:58 -080061 return SkMask::kLCD16_Format == rec.fMaskFormat;
reed@google.com82a34d82011-07-26 19:33:08 +000062}
63
reed@google.coma767fa02011-08-05 21:40:26 +000064static bool bothZero(SkScalar a, SkScalar b) {
65 return 0 == a && 0 == b;
66}
67
68// returns false if there is any non-90-rotation or skew
69static bool isAxisAligned(const SkScalerContext::Rec& rec) {
70 return 0 == rec.fPreSkewX &&
71 (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
72 bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
73}
74
75static bool needToRenderWithSkia(const SkScalerContext::Rec& rec) {
76#ifdef SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
77 // What we really want to catch is when GDI will ignore the AA request and give
78 // us BW instead. Smallish rotated text is one heuristic, so this code is just
79 // an approximation. We shouldn't need to do this for larger sizes, but at those
80 // sizes, the quality difference gets less and less between our general
81 // scanconverter and GDI's.
82 if (SkMask::kA8_Format == rec.fMaskFormat && !isAxisAligned(rec)) {
83 return true;
84 }
85#endif
bungeman@google.com0abbff92013-07-27 20:37:56 +000086 return rec.getHinting() == SkPaint::kNo_Hinting || rec.getHinting() == SkPaint::kSlight_Hinting;
reed@google.coma767fa02011-08-05 21:40:26 +000087}
88
reed@google.comac6b9792011-03-11 15:42:51 +000089using namespace skia_advanced_typeface_metrics_utils;
90
reed@google.coma65a6812013-05-02 19:47:24 +000091static void tchar_to_skstring(const TCHAR t[], SkString* s) {
reed@google.com484f5bc2013-04-24 19:14:56 +000092#ifdef UNICODE
93 size_t sSize = WideCharToMultiByte(CP_UTF8, 0, t, -1, NULL, 0, NULL, NULL);
94 s->resize(sSize);
95 WideCharToMultiByte(CP_UTF8, 0, t, -1, s->writable_str(), sSize, NULL, NULL);
96#else
97 s->set(t);
98#endif
99}
100
bungeman@google.coma9802692013-08-07 02:45:25 +0000101static void dcfontname_to_skstring(HDC deviceContext, const LOGFONT& lf, SkString* familyName) {
102 int fontNameLen; //length of fontName in TCHARS.
103 if (0 == (fontNameLen = GetTextFace(deviceContext, 0, NULL))) {
104 call_ensure_accessible(lf);
105 if (0 == (fontNameLen = GetTextFace(deviceContext, 0, NULL))) {
106 fontNameLen = 0;
107 }
108 }
109
110 SkAutoSTArray<LF_FULLFACESIZE, TCHAR> fontName(fontNameLen+1);
111 if (0 == GetTextFace(deviceContext, fontNameLen, fontName.get())) {
112 call_ensure_accessible(lf);
113 if (0 == GetTextFace(deviceContext, fontNameLen, fontName.get())) {
114 fontName[0] = 0;
115 }
116 }
117
118 tchar_to_skstring(fontName.get(), familyName);
119}
120
reed@google.comac6b9792011-03-11 15:42:51 +0000121static void make_canonical(LOGFONT* lf) {
bungeman@google.com53cbb0b2013-09-08 19:36:58 +0000122 lf->lfHeight = -64;
reed@google.com59d2f632011-05-02 19:36:59 +0000123 lf->lfQuality = CLEARTYPE_QUALITY;//PROOF_QUALITY;
124 lf->lfCharSet = DEFAULT_CHARSET;
reed@google.com82a34d82011-07-26 19:33:08 +0000125// lf->lfClipPrecision = 64;
reed@google.com59d2f632011-05-02 19:36:59 +0000126}
127
bungemana4c4a2d2014-10-20 13:33:19 -0700128static SkFontStyle get_style(const LOGFONT& lf) {
129 return SkFontStyle(lf.lfWeight,
130 lf.lfWidth,
131 lf.lfItalic ? SkFontStyle::kItalic_Slant : SkFontStyle::kUpright_Slant);
reed@google.comac6b9792011-03-11 15:42:51 +0000132}
133
134static inline FIXED SkFixedToFIXED(SkFixed x) {
135 return *(FIXED*)(&x);
136}
bungeman@google.coma0319f62012-04-18 15:40:50 +0000137static inline SkFixed SkFIXEDToFixed(FIXED x) {
138 return *(SkFixed*)(&x);
139}
reed@google.comac6b9792011-03-11 15:42:51 +0000140
141static inline FIXED SkScalarToFIXED(SkScalar x) {
142 return SkFixedToFIXED(SkScalarToFixed(x));
143}
144
bungeman@google.com4732df62014-01-23 15:22:42 +0000145static inline SkScalar SkFIXEDToScalar(FIXED x) {
146 return SkFixedToScalar(SkFIXEDToFixed(x));
147}
148
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000149static unsigned calculateGlyphCount(HDC hdc, const LOGFONT& lf) {
150 TEXTMETRIC textMetric;
151 if (0 == GetTextMetrics(hdc, &textMetric)) {
152 textMetric.tmPitchAndFamily = TMPF_VECTOR;
153 call_ensure_accessible(lf);
154 GetTextMetrics(hdc, &textMetric);
155 }
156
157 if (!(textMetric.tmPitchAndFamily & TMPF_VECTOR)) {
158 return textMetric.tmLastChar;
159 }
160
reed@google.comac6b9792011-03-11 15:42:51 +0000161 // The 'maxp' table stores the number of glyphs at offset 4, in 2 bytes.
reed@google.comac6b9792011-03-11 15:42:51 +0000162 uint16_t glyphs;
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000163 if (GDI_ERROR != GetFontData(hdc, SkOTTableMaximumProfile::TAG, 4, &glyphs, sizeof(glyphs))) {
reed@google.comac6b9792011-03-11 15:42:51 +0000164 return SkEndian_SwapBE16(glyphs);
165 }
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000166
reed@google.comac6b9792011-03-11 15:42:51 +0000167 // Binary search for glyph count.
168 static const MAT2 mat2 = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
169 int32_t max = SK_MaxU16 + 1;
170 int32_t min = 0;
171 GLYPHMETRICS gm;
172 while (min < max) {
173 int32_t mid = min + ((max - min) / 2);
174 if (GetGlyphOutlineW(hdc, mid, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0,
175 NULL, &mat2) == GDI_ERROR) {
176 max = mid;
177 } else {
178 min = mid + 1;
179 }
180 }
181 SkASSERT(min == max);
182 return min;
183}
184
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000185static unsigned calculateUPEM(HDC hdc, const LOGFONT& lf) {
186 TEXTMETRIC textMetric;
187 if (0 == GetTextMetrics(hdc, &textMetric)) {
188 textMetric.tmPitchAndFamily = TMPF_VECTOR;
189 call_ensure_accessible(lf);
190 GetTextMetrics(hdc, &textMetric);
191 }
192
193 if (!(textMetric.tmPitchAndFamily & TMPF_VECTOR)) {
194 return textMetric.tmMaxCharWidth;
195 }
196
197 OUTLINETEXTMETRIC otm;
198 unsigned int otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
199 if (0 == otmRet) {
200 call_ensure_accessible(lf);
201 otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
202 }
203
204 return (0 == otmRet) ? 0 : otm.otmEMSquare;
205}
206
reed@google.comac6b9792011-03-11 15:42:51 +0000207class LogFontTypeface : public SkTypeface {
reed@google.comac6b9792011-03-11 15:42:51 +0000208public:
bungemana4c4a2d2014-10-20 13:33:19 -0700209 LogFontTypeface(const SkFontStyle& style, const LOGFONT& lf, bool serializeAsStream)
210 : SkTypeface(style, SkTypefaceCache::NewFontID(), false)
211 , fLogFont(lf)
212 , fSerializeAsStream(serializeAsStream)
213 {
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000214
215 // If the font has cubic outlines, it will not be rendered with ClearType.
216 HFONT font = CreateFontIndirect(&lf);
217
218 HDC deviceContext = ::CreateCompatibleDC(NULL);
219 HFONT savefont = (HFONT)SelectObject(deviceContext, font);
220
221 TEXTMETRIC textMetric;
222 if (0 == GetTextMetrics(deviceContext, &textMetric)) {
reed@google.com055180c2013-03-21 18:46:35 +0000223 call_ensure_accessible(lf);
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000224 if (0 == GetTextMetrics(deviceContext, &textMetric)) {
225 textMetric.tmPitchAndFamily = TMPF_TRUETYPE;
226 }
227 }
228 if (deviceContext) {
229 ::SelectObject(deviceContext, savefont);
230 ::DeleteDC(deviceContext);
231 }
232 if (font) {
233 ::DeleteObject(font);
234 }
235
bungeman@google.comfe747652013-03-25 19:36:11 +0000236 // The fixed pitch bit is set if the font is *not* fixed pitch.
237 this->setIsFixedPitch((textMetric.tmPitchAndFamily & TMPF_FIXED_PITCH) == 0);
238
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000239 // Used a logfont on a memory context, should never get a device font.
240 // Therefore all TMPF_DEVICE will be PostScript (cubic) fonts.
241 fCanBeLCD = !((textMetric.tmPitchAndFamily & TMPF_VECTOR) &&
242 (textMetric.tmPitchAndFamily & TMPF_DEVICE));
243 }
reed@google.comac6b9792011-03-11 15:42:51 +0000244
reed@google.com59d2f632011-05-02 19:36:59 +0000245 LOGFONT fLogFont;
bungeman@google.come70f7982012-06-01 19:38:19 +0000246 bool fSerializeAsStream;
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000247 bool fCanBeLCD;
reed@google.comac6b9792011-03-11 15:42:51 +0000248
reed@google.com59d2f632011-05-02 19:36:59 +0000249 static LogFontTypeface* Create(const LOGFONT& lf) {
bungemana4c4a2d2014-10-20 13:33:19 -0700250 return new LogFontTypeface(get_style(lf), lf, false);
reed@google.comac6b9792011-03-11 15:42:51 +0000251 }
reed@google.com0da48612013-03-19 16:06:52 +0000252
reed@google.com055180c2013-03-21 18:46:35 +0000253 static void EnsureAccessible(const SkTypeface* face) {
254 call_ensure_accessible(static_cast<const LogFontTypeface*>(face)->fLogFont);
255 }
256
reed@google.com0da48612013-03-19 16:06:52 +0000257protected:
reed@google.com0042b9c2013-03-21 20:16:04 +0000258 virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE;
reed@google.com0da48612013-03-19 16:06:52 +0000259 virtual SkScalerContext* onCreateScalerContext(const SkDescriptor*) const SK_OVERRIDE;
260 virtual void onFilterRec(SkScalerContextRec*) const SK_OVERRIDE;
reed@google.com2689f612013-03-20 20:01:47 +0000261 virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
262 SkAdvancedTypefaceMetrics::PerGlyphInfo,
263 const uint32_t*, uint32_t) const SK_OVERRIDE;
reed@google.com5526ede2013-03-25 13:03:37 +0000264 virtual void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE;
bungeman@google.com3c996f82013-10-24 21:39:35 +0000265 virtual int onCharsToGlyphs(const void* chars, Encoding encoding,
266 uint16_t glyphs[], int glyphCount) const SK_OVERRIDE;
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000267 virtual int onCountGlyphs() const SK_OVERRIDE;
268 virtual int onGetUPEM() const SK_OVERRIDE;
bungemanb374d6a2014-09-17 07:48:59 -0700269 virtual void onGetFamilyName(SkString* familyName) const SK_OVERRIDE;
bungeman@google.com839702b2013-08-07 17:09:22 +0000270 virtual SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const SK_OVERRIDE;
bungeman@google.comb10b51f2013-08-01 20:18:41 +0000271 virtual int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
272 virtual size_t onGetTableData(SkFontTableTag, size_t offset,
273 size_t length, void* data) const SK_OVERRIDE;
reed@google.comac6b9792011-03-11 15:42:51 +0000274};
275
bungeman@google.coma5501992012-05-18 19:06:41 +0000276class FontMemResourceTypeface : public LogFontTypeface {
277public:
278 /**
bungeman@google.coma5501992012-05-18 19:06:41 +0000279 * The created FontMemResourceTypeface takes ownership of fontMemResource.
280 */
281 static FontMemResourceTypeface* Create(const LOGFONT& lf, HANDLE fontMemResource) {
bungemana4c4a2d2014-10-20 13:33:19 -0700282 return new FontMemResourceTypeface(get_style(lf), lf, fontMemResource);
bungeman@google.coma5501992012-05-18 19:06:41 +0000283 }
284
285protected:
286 virtual void weak_dispose() const SK_OVERRIDE {
287 RemoveFontMemResourceEx(fFontMemResource);
288 //SkTypefaceCache::Remove(this);
289 INHERITED::weak_dispose();
290 }
291
292private:
bungeman@google.com72cf4fc2014-03-21 22:48:32 +0000293 /**
294 * Takes ownership of fontMemResource.
295 */
bungemana4c4a2d2014-10-20 13:33:19 -0700296 FontMemResourceTypeface(const SkFontStyle& style, const LOGFONT& lf, HANDLE fontMemResource)
297 : LogFontTypeface(style, lf, true), fFontMemResource(fontMemResource)
298 { }
bungeman@google.com72cf4fc2014-03-21 22:48:32 +0000299
300 HANDLE fFontMemResource;
301
bungeman@google.coma5501992012-05-18 19:06:41 +0000302 typedef LogFontTypeface INHERITED;
303};
304
reed@google.comac6b9792011-03-11 15:42:51 +0000305static const LOGFONT& get_default_font() {
306 static LOGFONT gDefaultFont;
reed@google.comac6b9792011-03-11 15:42:51 +0000307 return gDefaultFont;
308}
309
bungemana4c4a2d2014-10-20 13:33:19 -0700310static bool FindByLogFont(SkTypeface* face, const SkFontStyle& requestedStyle, void* ctx) {
bungeman@google.coma5501992012-05-18 19:06:41 +0000311 LogFontTypeface* lface = static_cast<LogFontTypeface*>(face);
reed@google.com59d2f632011-05-02 19:36:59 +0000312 const LOGFONT* lf = reinterpret_cast<const LOGFONT*>(ctx);
reed@google.comac6b9792011-03-11 15:42:51 +0000313
bungeman@google.coma5501992012-05-18 19:06:41 +0000314 return lface &&
315 get_style(lface->fLogFont) == requestedStyle &&
reed@google.com59d2f632011-05-02 19:36:59 +0000316 !memcmp(&lface->fLogFont, lf, sizeof(LOGFONT));
317}
318
319/**
320 * This guy is public. It first searches the cache, and if a match is not found,
321 * it creates a new face.
322 */
323SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& origLF) {
324 LOGFONT lf = origLF;
325 make_canonical(&lf);
bungeman@google.comee51d1a2012-02-16 12:40:48 +0000326 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByLogFont, &lf);
327 if (NULL == face) {
reed@google.com59d2f632011-05-02 19:36:59 +0000328 face = LogFontTypeface::Create(lf);
bungeman@google.com90d812b2011-10-24 21:25:01 +0000329 SkTypefaceCache::Add(face, get_style(lf));
reed@google.com59d2f632011-05-02 19:36:59 +0000330 }
331 return face;
reed@google.comac6b9792011-03-11 15:42:51 +0000332}
333
reed@google.comdb77a6a2011-07-19 19:08:33 +0000334/**
bungeman@google.coma5501992012-05-18 19:06:41 +0000335 * The created SkTypeface takes ownership of fontMemResource.
336 */
337SkTypeface* SkCreateFontMemResourceTypefaceFromLOGFONT(const LOGFONT& origLF, HANDLE fontMemResource) {
338 LOGFONT lf = origLF;
339 make_canonical(&lf);
mtklein60b6e9d2014-10-24 10:43:15 -0700340 // We'll never get a cache hit, so no point in putting this in SkTypefaceCache.
341 return FontMemResourceTypeface::Create(lf, fontMemResource);
bungeman@google.coma5501992012-05-18 19:06:41 +0000342}
343
344/**
reed@google.comdb77a6a2011-07-19 19:08:33 +0000345 * This guy is public
346 */
347void SkLOGFONTFromTypeface(const SkTypeface* face, LOGFONT* lf) {
348 if (NULL == face) {
349 *lf = get_default_font();
350 } else {
bungeman@google.coma5501992012-05-18 19:06:41 +0000351 *lf = static_cast<const LogFontTypeface*>(face)->fLogFont;
reed@google.comdb77a6a2011-07-19 19:08:33 +0000352 }
353}
354
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000355// Construct Glyph to Unicode table.
356// Unicode code points that require conjugate pairs in utf16 are not
357// supported.
358// TODO(arthurhsu): Add support for conjugate pairs. It looks like that may
359// require parsing the TTF cmap table (platform 4, encoding 12) directly instead
360// of calling GetFontUnicodeRange().
361static void populate_glyph_to_unicode(HDC fontHdc, const unsigned glyphCount,
362 SkTDArray<SkUnichar>* glyphToUnicode) {
363 DWORD glyphSetBufferSize = GetFontUnicodeRanges(fontHdc, NULL);
364 if (!glyphSetBufferSize) {
365 return;
366 }
367
368 SkAutoTDeleteArray<BYTE> glyphSetBuffer(new BYTE[glyphSetBufferSize]);
369 GLYPHSET* glyphSet =
370 reinterpret_cast<LPGLYPHSET>(glyphSetBuffer.get());
371 if (GetFontUnicodeRanges(fontHdc, glyphSet) != glyphSetBufferSize) {
372 return;
373 }
374
375 glyphToUnicode->setCount(glyphCount);
376 memset(glyphToUnicode->begin(), 0, glyphCount * sizeof(SkUnichar));
377 for (DWORD i = 0; i < glyphSet->cRanges; ++i) {
378 // There is no guarantee that within a Unicode range, the corresponding
379 // glyph id in a font file are continuous. So, even if we have ranges,
380 // we can't just use the first and last entry of the range to compute
381 // result. We need to enumerate them one by one.
382 int count = glyphSet->ranges[i].cGlyphs;
383 SkAutoTArray<WCHAR> chars(count + 1);
384 chars[count] = 0; // termintate string
385 SkAutoTArray<WORD> glyph(count);
386 for (USHORT j = 0; j < count; ++j) {
387 chars[j] = glyphSet->ranges[i].wcLow + j;
388 }
389 GetGlyphIndicesW(fontHdc, chars.get(), count, glyph.get(),
390 GGI_MARK_NONEXISTING_GLYPHS);
391 // If the glyph ID is valid, and the glyph is not mapped, then we will
392 // fill in the char id into the vector. If the glyph is mapped already,
393 // skip it.
394 // TODO(arthurhsu): better improve this. e.g. Get all used char ids from
395 // font cache, then generate this mapping table from there. It's
396 // unlikely to have collisions since glyph reuse happens mostly for
397 // different Unicode pages.
398 for (USHORT j = 0; j < count; ++j) {
399 if (glyph[j] != 0xffff && glyph[j] < glyphCount &&
400 (*glyphToUnicode)[glyph[j]] == 0) {
401 (*glyphToUnicode)[glyph[j]] = chars[j];
402 }
403 }
404 }
405}
406
reed@google.com99edd432011-09-09 14:59:59 +0000407//////////////////////////////////////////////////////////////////////////////////////
408
409static int alignTo32(int n) {
410 return (n + 31) & ~31;
411}
412
413struct MyBitmapInfo : public BITMAPINFO {
414 RGBQUAD fMoreSpaceForColors[1];
415};
416
417class HDCOffscreen {
418public:
419 HDCOffscreen() {
420 fFont = 0;
421 fDC = 0;
422 fBM = 0;
423 fBits = NULL;
424 fWidth = fHeight = 0;
425 fIsBW = false;
426 }
427
428 ~HDCOffscreen() {
429 if (fDC) {
430 DeleteDC(fDC);
431 }
432 if (fBM) {
433 DeleteObject(fBM);
434 }
435 }
436
437 void init(HFONT font, const XFORM& xform) {
438 fFont = font;
439 fXform = xform;
440 }
441
bungeman@google.com97efada2012-07-30 20:40:50 +0000442 const void* draw(const SkGlyph&, bool isBW, size_t* srcRBPtr);
reed@google.com99edd432011-09-09 14:59:59 +0000443
444private:
445 HDC fDC;
446 HBITMAP fBM;
447 HFONT fFont;
448 XFORM fXform;
449 void* fBits; // points into fBM
450 int fWidth;
451 int fHeight;
452 bool fIsBW;
453};
454
reed@google.com754e4eb2011-09-26 13:21:39 +0000455const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW,
bungeman@google.com97efada2012-07-30 20:40:50 +0000456 size_t* srcRBPtr) {
reed@google.com84e22d82013-07-10 15:38:20 +0000457 // Can we share the scalercontext's fDDC, so we don't need to create
458 // a separate fDC here?
reed@google.com99edd432011-09-09 14:59:59 +0000459 if (0 == fDC) {
460 fDC = CreateCompatibleDC(0);
461 if (0 == fDC) {
462 return NULL;
463 }
464 SetGraphicsMode(fDC, GM_ADVANCED);
465 SetBkMode(fDC, TRANSPARENT);
466 SetTextAlign(fDC, TA_LEFT | TA_BASELINE);
467 SelectObject(fDC, fFont);
bungeman@google.com97efada2012-07-30 20:40:50 +0000468
469 COLORREF color = 0x00FFFFFF;
bsalomon@google.comb58a6392013-03-21 20:29:05 +0000470 SkDEBUGCODE(COLORREF prev =) SetTextColor(fDC, color);
bungeman@google.com97efada2012-07-30 20:40:50 +0000471 SkASSERT(prev != CLR_INVALID);
reed@google.com99edd432011-09-09 14:59:59 +0000472 }
473
474 if (fBM && (fIsBW != isBW || fWidth < glyph.fWidth || fHeight < glyph.fHeight)) {
475 DeleteObject(fBM);
476 fBM = 0;
477 }
reed@google.com754e4eb2011-09-26 13:21:39 +0000478 fIsBW = isBW;
reed@google.com99edd432011-09-09 14:59:59 +0000479
reed@google.com99edd432011-09-09 14:59:59 +0000480 fWidth = SkMax32(fWidth, glyph.fWidth);
481 fHeight = SkMax32(fHeight, glyph.fHeight);
482
483 int biWidth = isBW ? alignTo32(fWidth) : fWidth;
484
485 if (0 == fBM) {
486 MyBitmapInfo info;
487 sk_bzero(&info, sizeof(info));
488 if (isBW) {
489 RGBQUAD blackQuad = { 0, 0, 0, 0 };
490 RGBQUAD whiteQuad = { 0xFF, 0xFF, 0xFF, 0 };
491 info.bmiColors[0] = blackQuad;
492 info.bmiColors[1] = whiteQuad;
493 }
494 info.bmiHeader.biSize = sizeof(info.bmiHeader);
495 info.bmiHeader.biWidth = biWidth;
496 info.bmiHeader.biHeight = fHeight;
497 info.bmiHeader.biPlanes = 1;
498 info.bmiHeader.biBitCount = isBW ? 1 : 32;
499 info.bmiHeader.biCompression = BI_RGB;
500 if (isBW) {
501 info.bmiHeader.biClrUsed = 2;
502 }
503 fBM = CreateDIBSection(fDC, &info, DIB_RGB_COLORS, &fBits, 0, 0);
504 if (0 == fBM) {
505 return NULL;
506 }
507 SelectObject(fDC, fBM);
508 }
509
510 // erase
511 size_t srcRB = isBW ? (biWidth >> 3) : (fWidth << 2);
512 size_t size = fHeight * srcRB;
bungeman@google.com97efada2012-07-30 20:40:50 +0000513 memset(fBits, 0, size);
reed@google.com99edd432011-09-09 14:59:59 +0000514
515 XFORM xform = fXform;
516 xform.eDx = (float)-glyph.fLeft;
517 xform.eDy = (float)-glyph.fTop;
518 SetWorldTransform(fDC, &xform);
519
520 uint16_t glyphID = glyph.getGlyphID();
bungeman@google.com39698b12011-11-15 22:26:41 +0000521 BOOL ret = ExtTextOutW(fDC, 0, 0, ETO_GLYPH_INDEX, NULL, reinterpret_cast<LPCWSTR>(&glyphID), 1, NULL);
reed@google.com99edd432011-09-09 14:59:59 +0000522 GdiFlush();
bungeman@google.com39698b12011-11-15 22:26:41 +0000523 if (0 == ret) {
524 return NULL;
525 }
reed@google.com99edd432011-09-09 14:59:59 +0000526 *srcRBPtr = srcRB;
527 // offset to the start of the image
528 return (const char*)fBits + (fHeight - glyph.fHeight) * srcRB;
529}
530
reed@google.comb8a5c612012-06-13 20:01:44 +0000531//////////////////////////////////////////////////////////////////////////////
bungeman@google.com0abbff92013-07-27 20:37:56 +0000532#define BUFFERSIZE (1 << 13)
reed@google.com59d2f632011-05-02 19:36:59 +0000533
reed@google.com30ddd612013-07-30 17:47:39 +0000534class SkScalerContext_GDI : public SkScalerContext {
reed@google.comac6b9792011-03-11 15:42:51 +0000535public:
reed@google.com30ddd612013-07-30 17:47:39 +0000536 SkScalerContext_GDI(SkTypeface*, const SkDescriptor* desc);
537 virtual ~SkScalerContext_GDI();
reed@google.comac6b9792011-03-11 15:42:51 +0000538
reed@google.com84e22d82013-07-10 15:38:20 +0000539 // Returns true if the constructor was able to complete all of its
540 // initializations (which may include calling GDI).
541 bool isValid() const;
542
reed@google.comac6b9792011-03-11 15:42:51 +0000543protected:
bungeman@google.com97efada2012-07-30 20:40:50 +0000544 virtual unsigned generateGlyphCount() SK_OVERRIDE;
545 virtual uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
546 virtual void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
547 virtual void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
bungeman@google.coma76de722012-10-26 19:35:54 +0000548 virtual void generateImage(const SkGlyph& glyph) SK_OVERRIDE;
bungeman@google.com97efada2012-07-30 20:40:50 +0000549 virtual void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
bungeman41078062014-07-07 08:16:37 -0700550 virtual void generateFontMetrics(SkPaint::FontMetrics*) SK_OVERRIDE;
reed@google.com99edd432011-09-09 14:59:59 +0000551
reed@google.comac6b9792011-03-11 15:42:51 +0000552private:
bungeman@google.com0abbff92013-07-27 20:37:56 +0000553 DWORD getGDIGlyphPath(const SkGlyph& glyph, UINT flags,
554 SkAutoSTMalloc<BUFFERSIZE, uint8_t>* glyphbuf);
555
reed@google.com99edd432011-09-09 14:59:59 +0000556 HDCOffscreen fOffscreen;
bungeman@google.com0abbff92013-07-27 20:37:56 +0000557 /** fGsA is the non-rotational part of total matrix without the text height scale.
558 * Used to find the magnitude of advances.
559 */
560 MAT2 fGsA;
bungeman@google.com6a774a12013-07-30 01:07:48 +0000561 /** The total matrix without the textSize. */
reed@google.comac6b9792011-03-11 15:42:51 +0000562 MAT2 fMat22;
bungeman@google.com6a774a12013-07-30 01:07:48 +0000563 /** Scales font to EM size. */
564 MAT2 fHighResMat22;
reed@google.comac6b9792011-03-11 15:42:51 +0000565 HDC fDDC;
566 HFONT fSavefont;
567 HFONT fFont;
568 SCRIPT_CACHE fSC;
569 int fGlyphCount;
reed@google.com1dd17a12011-05-17 14:04:41 +0000570
bungeman@google.com6a774a12013-07-30 01:07:48 +0000571 /** The total matrix which also removes EM scale. */
reed@google.com1dd17a12011-05-17 14:04:41 +0000572 SkMatrix fHiResMatrix;
bungeman@google.com0abbff92013-07-27 20:37:56 +0000573 /** fG_inv is the inverse of the rotational part of the total matrix.
574 * Used to set the direction of advances.
575 */
576 SkMatrix fG_inv;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000577 enum Type {
bungeman@google.com4732df62014-01-23 15:22:42 +0000578 kTrueType_Type, kBitmap_Type, kLine_Type
bungeman@google.coma0319f62012-04-18 15:40:50 +0000579 } fType;
580 TEXTMETRIC fTM;
reed@google.comac6b9792011-03-11 15:42:51 +0000581};
582
reed@google.comac6b9792011-03-11 15:42:51 +0000583static FIXED float2FIXED(float x) {
584 return SkFixedToFIXED(SkFloatToFixed(x));
585}
586
reed@google.com82a34d82011-07-26 19:33:08 +0000587static BYTE compute_quality(const SkScalerContext::Rec& rec) {
588 switch (rec.fMaskFormat) {
589 case SkMask::kBW_Format:
590 return NONANTIALIASED_QUALITY;
591 case SkMask::kLCD16_Format:
reed@google.com82a34d82011-07-26 19:33:08 +0000592 return CLEARTYPE_QUALITY;
593 default:
reed@google.com8351aab2012-01-18 17:06:35 +0000594 if (rec.fFlags & SkScalerContext::kGenA8FromLCD_Flag) {
595 return CLEARTYPE_QUALITY;
596 } else {
597 return ANTIALIASED_QUALITY;
598 }
reed@google.com82a34d82011-07-26 19:33:08 +0000599 }
600}
601
reed@google.com30ddd612013-07-30 17:47:39 +0000602SkScalerContext_GDI::SkScalerContext_GDI(SkTypeface* rawTypeface,
reed@google.com0da48612013-03-19 16:06:52 +0000603 const SkDescriptor* desc)
reed@google.com055180c2013-03-21 18:46:35 +0000604 : SkScalerContext(rawTypeface, desc)
605 , fDDC(0)
reed@google.com055180c2013-03-21 18:46:35 +0000606 , fSavefont(0)
reed@google.com84e22d82013-07-10 15:38:20 +0000607 , fFont(0)
reed@google.com055180c2013-03-21 18:46:35 +0000608 , fSC(0)
bungeman@google.com05a729f2013-06-20 15:29:16 +0000609 , fGlyphCount(-1)
610{
reed@google.com055180c2013-03-21 18:46:35 +0000611 LogFontTypeface* typeface = reinterpret_cast<LogFontTypeface*>(rawTypeface);
612
reed@google.comac6b9792011-03-11 15:42:51 +0000613 fDDC = ::CreateCompatibleDC(NULL);
reed@google.com84e22d82013-07-10 15:38:20 +0000614 if (!fDDC) {
615 return;
616 }
reed@google.com1dd17a12011-05-17 14:04:41 +0000617 SetGraphicsMode(fDDC, GM_ADVANCED);
reed@google.comac6b9792011-03-11 15:42:51 +0000618 SetBkMode(fDDC, TRANSPARENT);
skia.committer@gmail.com27e21fe2013-07-28 07:01:03 +0000619
bungeman@google.com11ba3192013-10-03 20:17:51 +0000620 // When GDI hinting, remove the entire Y scale to prevent 'subpixel' metrics.
bungeman5f14c5e2014-12-05 12:26:44 -0800621 // When not hinting, remove only the gdiTextSize scale which will be applied by GDI.
622 SkScalerContextRec::PreMatrixScale scaleConstraints =
623 (fRec.getHinting() == SkPaint::kNo_Hinting || fRec.getHinting() == SkPaint::kSlight_Hinting)
624 ? SkScalerContextRec::kVerticalInteger_PreMatrixScale
625 : SkScalerContextRec::kVertical_PreMatrixScale;
626 SkVector scale;
627 SkMatrix sA;
628 SkMatrix GsA;
629 SkMatrix A;
630 fRec.computeMatrices(scaleConstraints, &scale, &sA, &GsA, &fG_inv, &A);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000631
632 fGsA.eM11 = SkScalarToFIXED(GsA.get(SkMatrix::kMScaleX));
633 fGsA.eM12 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewY)); // This should be ~0.
634 fGsA.eM21 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewX));
635 fGsA.eM22 = SkScalarToFIXED(GsA.get(SkMatrix::kMScaleY));
636
bungeman5f14c5e2014-12-05 12:26:44 -0800637 SkScalar gdiTextSize = scale.fY;
638 if (gdiTextSize == 0) {
639 gdiTextSize = SK_Scalar1;
640 }
bungeman@google.com0abbff92013-07-27 20:37:56 +0000641
reed@google.com055180c2013-03-21 18:46:35 +0000642 LOGFONT lf = typeface->fLogFont;
bungeman@google.com11ba3192013-10-03 20:17:51 +0000643 lf.lfHeight = -SkScalarTruncToInt(gdiTextSize);
reed@google.com82a34d82011-07-26 19:33:08 +0000644 lf.lfQuality = compute_quality(fRec);
reed@google.comac6b9792011-03-11 15:42:51 +0000645 fFont = CreateFontIndirect(&lf);
reed@google.com84e22d82013-07-10 15:38:20 +0000646 if (!fFont) {
647 return;
648 }
reed@google.com1dd17a12011-05-17 14:04:41 +0000649
reed@google.comac6b9792011-03-11 15:42:51 +0000650 fSavefont = (HFONT)SelectObject(fDDC, fFont);
reed@google.coma767fa02011-08-05 21:40:26 +0000651
bungeman@google.coma0319f62012-04-18 15:40:50 +0000652 if (0 == GetTextMetrics(fDDC, &fTM)) {
reed@google.com055180c2013-03-21 18:46:35 +0000653 call_ensure_accessible(lf);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000654 if (0 == GetTextMetrics(fDDC, &fTM)) {
655 fTM.tmPitchAndFamily = TMPF_TRUETYPE;
656 }
657 }
bungeman@google.com90b7e382012-04-20 15:26:28 +0000658
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000659 XFORM xform;
bungeman@google.com90b7e382012-04-20 15:26:28 +0000660 if (fTM.tmPitchAndFamily & TMPF_VECTOR) {
bungeman@google.com4732df62014-01-23 15:22:42 +0000661 // Used a logfont on a memory context, should never get a device font.
662 // Therefore all TMPF_DEVICE will be PostScript fonts.
663
664 // If TMPF_VECTOR is set, one of TMPF_TRUETYPE or TMPF_DEVICE means that
665 // we have an outline font. Otherwise we have a vector FON, which is
666 // scalable, but not an outline font.
667 // This was determined by testing with Type1 PFM/PFB and
668 // OpenTypeCFF OTF, as well as looking at Wine bugs and sources.
669 if (fTM.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_DEVICE)) {
670 // Truetype or PostScript.
671 fType = SkScalerContext_GDI::kTrueType_Type;
672 } else {
673 // Stroked FON.
674 fType = SkScalerContext_GDI::kLine_Type;
675 }
bungeman@google.coma0319f62012-04-18 15:40:50 +0000676
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000677 // fPost2x2 is column-major, left handed (y down).
678 // XFORM 2x2 is row-major, left handed (y down).
bungeman@google.com0abbff92013-07-27 20:37:56 +0000679 xform.eM11 = SkScalarToFloat(sA.get(SkMatrix::kMScaleX));
680 xform.eM12 = SkScalarToFloat(sA.get(SkMatrix::kMSkewY));
681 xform.eM21 = SkScalarToFloat(sA.get(SkMatrix::kMSkewX));
682 xform.eM22 = SkScalarToFloat(sA.get(SkMatrix::kMScaleY));
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000683 xform.eDx = 0;
684 xform.eDy = 0;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000685
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000686 // MAT2 is row major, right handed (y up).
687 fMat22.eM11 = float2FIXED(xform.eM11);
688 fMat22.eM12 = float2FIXED(-xform.eM12);
689 fMat22.eM21 = float2FIXED(-xform.eM21);
690 fMat22.eM22 = float2FIXED(xform.eM22);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000691
692 if (needToRenderWithSkia(fRec)) {
693 this->forceGenerateImageFromPath();
694 }
695
bungeman@google.com11ba3192013-10-03 20:17:51 +0000696 // Create a hires matrix if we need linear metrics.
bungeman@google.com0abbff92013-07-27 20:37:56 +0000697 if (this->isSubpixel()) {
698 OUTLINETEXTMETRIC otm;
699 UINT success = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
700 if (0 == success) {
701 call_ensure_accessible(lf);
702 success = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
703 }
704 if (0 != success) {
bungeman@google.com11ba3192013-10-03 20:17:51 +0000705 SkScalar upem = SkIntToScalar(otm.otmEMSquare);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000706
bungeman@google.com11ba3192013-10-03 20:17:51 +0000707 SkScalar gdiTextSizeToEMScale = upem / gdiTextSize;
708 fHighResMat22.eM11 = float2FIXED(gdiTextSizeToEMScale);
bungeman@google.com6a774a12013-07-30 01:07:48 +0000709 fHighResMat22.eM12 = float2FIXED(0);
710 fHighResMat22.eM21 = float2FIXED(0);
bungeman@google.com11ba3192013-10-03 20:17:51 +0000711 fHighResMat22.eM22 = float2FIXED(gdiTextSizeToEMScale);
bungeman@google.com6a774a12013-07-30 01:07:48 +0000712
bungeman@google.com11ba3192013-10-03 20:17:51 +0000713 SkScalar removeEMScale = SkScalarInvert(upem);
bungeman@google.com6a774a12013-07-30 01:07:48 +0000714 fHiResMatrix = A;
bungeman@google.com11ba3192013-10-03 20:17:51 +0000715 fHiResMatrix.preScale(removeEMScale, removeEMScale);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000716 }
717 }
718
bungeman@google.coma0319f62012-04-18 15:40:50 +0000719 } else {
720 // Assume bitmap
reed@google.com30ddd612013-07-30 17:47:39 +0000721 fType = SkScalerContext_GDI::kBitmap_Type;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000722
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000723 xform.eM11 = 1.0f;
724 xform.eM12 = 0.0f;
725 xform.eM21 = 0.0f;
726 xform.eM22 = 1.0f;
727 xform.eDx = 0.0f;
728 xform.eDy = 0.0f;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000729
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000730 // fPost2x2 is column-major, left handed (y down).
731 // MAT2 is row major, right handed (y up).
bungeman@google.coma0319f62012-04-18 15:40:50 +0000732 fMat22.eM11 = SkScalarToFIXED(fRec.fPost2x2[0][0]);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000733 fMat22.eM12 = SkScalarToFIXED(-fRec.fPost2x2[1][0]);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000734 fMat22.eM21 = SkScalarToFIXED(-fRec.fPost2x2[0][1]);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000735 fMat22.eM22 = SkScalarToFIXED(fRec.fPost2x2[1][1]);
reed@google.coma767fa02011-08-05 21:40:26 +0000736 }
reed@google.com99edd432011-09-09 14:59:59 +0000737
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000738 fOffscreen.init(fFont, xform);
reed@google.comac6b9792011-03-11 15:42:51 +0000739}
740
reed@google.com30ddd612013-07-30 17:47:39 +0000741SkScalerContext_GDI::~SkScalerContext_GDI() {
reed@google.comac6b9792011-03-11 15:42:51 +0000742 if (fDDC) {
743 ::SelectObject(fDDC, fSavefont);
744 ::DeleteDC(fDDC);
745 }
746 if (fFont) {
747 ::DeleteObject(fFont);
748 }
749 if (fSC) {
750 ::ScriptFreeCache(&fSC);
751 }
752}
753
reed@google.com30ddd612013-07-30 17:47:39 +0000754bool SkScalerContext_GDI::isValid() const {
reed@google.com84e22d82013-07-10 15:38:20 +0000755 return fDDC && fFont;
756}
757
reed@google.com30ddd612013-07-30 17:47:39 +0000758unsigned SkScalerContext_GDI::generateGlyphCount() {
reed@google.comac6b9792011-03-11 15:42:51 +0000759 if (fGlyphCount < 0) {
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000760 fGlyphCount = calculateGlyphCount(
761 fDDC, static_cast<const LogFontTypeface*>(this->getTypeface())->fLogFont);
reed@google.comac6b9792011-03-11 15:42:51 +0000762 }
763 return fGlyphCount;
764}
765
bungeman@google.com33346482013-08-27 19:05:32 +0000766uint16_t SkScalerContext_GDI::generateCharToGlyph(SkUnichar utf32) {
reed@google.comac6b9792011-03-11 15:42:51 +0000767 uint16_t index = 0;
bungeman@google.com33346482013-08-27 19:05:32 +0000768 WCHAR utf16[2];
reed@google.comac6b9792011-03-11 15:42:51 +0000769 // TODO(ctguil): Support characters that generate more than one glyph.
bungeman@google.com33346482013-08-27 19:05:32 +0000770 if (SkUTF16_FromUnichar(utf32, (uint16_t*)utf16) == 1) {
reed@google.comac6b9792011-03-11 15:42:51 +0000771 // Type1 fonts fail with uniscribe API. Use GetGlyphIndices for plane 0.
skia.committer@gmail.com7bd141d2013-08-28 07:01:18 +0000772
bungeman@google.com33346482013-08-27 19:05:32 +0000773 /** Real documentation for GetGlyphIndiciesW:
774 *
775 * When GGI_MARK_NONEXISTING_GLYPHS is not specified and a character does not map to a
776 * glyph, then the 'default character's glyph is returned instead. The 'default character'
bungeman@google.com4732df62014-01-23 15:22:42 +0000777 * is available in fTM.tmDefaultChar. FON fonts have a default character, and there exists
778 * a usDefaultChar in the 'OS/2' table, version 2 and later. If there is no
bungeman@google.com33346482013-08-27 19:05:32 +0000779 * 'default character' specified by the font, then often the first character found is used.
780 *
781 * When GGI_MARK_NONEXISTING_GLYPHS is specified and a character does not map to a glyph,
782 * then the glyph 0xFFFF is used. In Windows XP and earlier, Bitmap/Vector FON usually use
783 * glyph 0x1F instead ('Terminal' appears to be special, returning 0xFFFF).
784 * Type1 PFM/PFB, TT, OT TT, OT CFF all appear to use 0xFFFF, even on XP.
785 */
786 DWORD result = GetGlyphIndicesW(fDDC, utf16, 1, &index, GGI_MARK_NONEXISTING_GLYPHS);
787 if (result == GDI_ERROR
788 || 0xFFFF == index
bungeman@google.com4732df62014-01-23 15:22:42 +0000789 || (0x1F == index &&
790 (fType == SkScalerContext_GDI::kBitmap_Type ||
791 fType == SkScalerContext_GDI::kLine_Type)
792 /*&& winVer < Vista */)
793 )
bungeman@google.com33346482013-08-27 19:05:32 +0000794 {
795 index = 0;
796 }
reed@google.comac6b9792011-03-11 15:42:51 +0000797 } else {
798 // Use uniscribe to detemine glyph index for non-BMP characters.
bungeman@google.com27f74aa2013-10-08 21:32:15 +0000799 static const int numWCHAR = 2;
800 static const int maxItems = 2;
801 // MSDN states that this can be NULL, but some things don't work then.
802 SCRIPT_CONTROL sc = { 0 };
803 // Add extra item to SCRIPT_ITEM to work around a bug (now documented).
804 // https://bugzilla.mozilla.org/show_bug.cgi?id=366643
805 SCRIPT_ITEM si[maxItems + 1];
806 int numItems;
807 HRZM(ScriptItemize(utf16, numWCHAR, maxItems, &sc, NULL, si, &numItems),
808 "Could not itemize character.");
reed@google.comac6b9792011-03-11 15:42:51 +0000809
bungeman@google.com27f74aa2013-10-08 21:32:15 +0000810 // Sometimes ScriptShape cannot find a glyph for a non-BMP and returns 2 space glyphs.
811 static const int maxGlyphs = 2;
812 SCRIPT_VISATTR vsa[maxGlyphs];
813 WORD outGlyphs[maxGlyphs];
814 WORD logClust[numWCHAR];
815 int numGlyphs;
816 HRZM(ScriptShape(fDDC, &fSC, utf16, numWCHAR, maxGlyphs, &si[0].a,
817 outGlyphs, logClust, vsa, &numGlyphs),
818 "Could not shape character.");
819 if (1 == numGlyphs) {
820 index = outGlyphs[0];
821 }
reed@google.comac6b9792011-03-11 15:42:51 +0000822 }
823 return index;
824}
825
reed@google.com30ddd612013-07-30 17:47:39 +0000826void SkScalerContext_GDI::generateAdvance(SkGlyph* glyph) {
reed@google.comac6b9792011-03-11 15:42:51 +0000827 this->generateMetrics(glyph);
828}
829
reed@google.com30ddd612013-07-30 17:47:39 +0000830void SkScalerContext_GDI::generateMetrics(SkGlyph* glyph) {
reed@google.comac6b9792011-03-11 15:42:51 +0000831 SkASSERT(fDDC);
832
bungeman@google.com4732df62014-01-23 15:22:42 +0000833 if (fType == SkScalerContext_GDI::kBitmap_Type || fType == SkScalerContext_GDI::kLine_Type) {
bungeman@google.coma0319f62012-04-18 15:40:50 +0000834 SIZE size;
djsollen1b277042014-08-06 06:58:06 -0700835 WORD glyphs = glyph->getGlyphID();
bungeman@google.coma0319f62012-04-18 15:40:50 +0000836 if (0 == GetTextExtentPointI(fDDC, &glyphs, 1, &size)) {
837 glyph->fWidth = SkToS16(fTM.tmMaxCharWidth);
838 } else {
839 glyph->fWidth = SkToS16(size.cx);
840 }
841 glyph->fHeight = SkToS16(size.cy);
842
843 glyph->fTop = SkToS16(-fTM.tmAscent);
bungeman@google.com4732df62014-01-23 15:22:42 +0000844 // Bitmap FON cannot underhang, but vector FON may.
845 // There appears no means of determining underhang of vector FON.
bungeman@google.coma0319f62012-04-18 15:40:50 +0000846 glyph->fLeft = SkToS16(0);
847 glyph->fAdvanceX = SkIntToFixed(glyph->fWidth);
848 glyph->fAdvanceY = 0;
849
bungeman@google.com4732df62014-01-23 15:22:42 +0000850 // Vector FON will transform nicely, but bitmap FON do not.
851 if (fType == SkScalerContext_GDI::kLine_Type) {
852 SkRect bounds = SkRect::MakeXYWH(glyph->fLeft, glyph->fTop,
853 glyph->fWidth, glyph->fHeight);
854 SkMatrix m;
855 m.setAll(SkFIXEDToScalar(fMat22.eM11), -SkFIXEDToScalar(fMat22.eM21), 0,
856 -SkFIXEDToScalar(fMat22.eM12), SkFIXEDToScalar(fMat22.eM22), 0,
857 0, 0, SkScalarToPersp(SK_Scalar1));
858 m.mapRect(&bounds);
reedd02cf262014-11-18 18:06:45 -0800859 bounds.roundOut(&bounds);
bungeman@google.com4732df62014-01-23 15:22:42 +0000860 glyph->fLeft = SkScalarTruncToInt(bounds.fLeft);
861 glyph->fTop = SkScalarTruncToInt(bounds.fTop);
862 glyph->fWidth = SkScalarTruncToInt(bounds.width());
863 glyph->fHeight = SkScalarTruncToInt(bounds.height());
864 }
865
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000866 // Apply matrix to advance.
bungeman@google.com4732df62014-01-23 15:22:42 +0000867 glyph->fAdvanceY = SkFixedMul(-SkFIXEDToFixed(fMat22.eM12), glyph->fAdvanceX);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000868 glyph->fAdvanceX = SkFixedMul(SkFIXEDToFixed(fMat22.eM11), glyph->fAdvanceX);
869
870 return;
871 }
872
djsollen1b277042014-08-06 06:58:06 -0700873 UINT glyphId = glyph->getGlyphID();
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000874
reed@google.comac6b9792011-03-11 15:42:51 +0000875 GLYPHMETRICS gm;
reed@google.com1dd17a12011-05-17 14:04:41 +0000876 sk_bzero(&gm, sizeof(gm));
reed@google.comac6b9792011-03-11 15:42:51 +0000877
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000878 DWORD status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
879 if (GDI_ERROR == status) {
reed@google.com055180c2013-03-21 18:46:35 +0000880 LogFontTypeface::EnsureAccessible(this->getTypeface());
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000881 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
882 if (GDI_ERROR == status) {
883 glyph->zeroMetrics();
884 return;
885 }
886 }
887
888 bool empty = false;
889 // The black box is either the embedded bitmap size or the outline extent.
890 // It is 1x1 if nothing is to be drawn, but will also be 1x1 if something very small
891 // is to be drawn, like a '.'. We need to outset '.' but do not wish to outset ' '.
892 if (1 == gm.gmBlackBoxX && 1 == gm.gmBlackBoxY) {
893 // If GetGlyphOutline with GGO_NATIVE returns 0, we know there was no outline.
894 DWORD bufferSize = GetGlyphOutlineW(fDDC, glyphId, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
895 empty = (0 == bufferSize);
896 }
897
898 glyph->fTop = SkToS16(-gm.gmptGlyphOrigin.y);
899 glyph->fLeft = SkToS16(gm.gmptGlyphOrigin.x);
900 if (empty) {
901 glyph->fWidth = 0;
902 glyph->fHeight = 0;
903 } else {
904 // Outset, since the image may bleed out of the black box.
905 // For embedded bitmaps the black box should be exact.
906 // For outlines we need to outset by 1 in all directions for bleed.
907 // For ClearType we need to outset by 2 for bleed.
908 glyph->fWidth = gm.gmBlackBoxX + 4;
909 glyph->fHeight = gm.gmBlackBoxY + 4;
910 glyph->fTop -= 2;
911 glyph->fLeft -= 2;
912 }
913 glyph->fAdvanceX = SkIntToFixed(gm.gmCellIncX);
914 glyph->fAdvanceY = SkIntToFixed(gm.gmCellIncY);
reed@google.comac6b9792011-03-11 15:42:51 +0000915 glyph->fRsbDelta = 0;
916 glyph->fLsbDelta = 0;
917
bungeman@google.com6a774a12013-07-30 01:07:48 +0000918 if (this->isSubpixel()) {
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000919 sk_bzero(&gm, sizeof(gm));
bungeman@google.com6a774a12013-07-30 01:07:48 +0000920 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fHighResMat22);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000921 if (GDI_ERROR != status) {
922 SkPoint advance;
923 fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
924 glyph->fAdvanceX = SkScalarToFixed(advance.fX);
925 glyph->fAdvanceY = SkScalarToFixed(advance.fY);
reed@google.comac6b9792011-03-11 15:42:51 +0000926 }
bungeman@google.com0abbff92013-07-27 20:37:56 +0000927 } else if (!isAxisAligned(this->fRec)) {
928 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fGsA);
929 if (GDI_ERROR != status) {
930 SkPoint advance;
931 fG_inv.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
932 glyph->fAdvanceX = SkScalarToFixed(advance.fX);
933 glyph->fAdvanceY = SkScalarToFixed(advance.fY);
934 }
reed@google.comac6b9792011-03-11 15:42:51 +0000935 }
936}
937
bungeman@google.com6a774a12013-07-30 01:07:48 +0000938static const MAT2 gMat2Identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
bungeman41078062014-07-07 08:16:37 -0700939void SkScalerContext_GDI::generateFontMetrics(SkPaint::FontMetrics* metrics) {
940 if (NULL == metrics) {
941 return;
reed@google.com60af92c2013-05-08 14:11:28 +0000942 }
bungeman41078062014-07-07 08:16:37 -0700943 sk_bzero(metrics, sizeof(*metrics));
reed@google.comac6b9792011-03-11 15:42:51 +0000944
945 SkASSERT(fDDC);
946
bungeman@google.come9d83192013-06-21 05:31:38 +0000947#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
bungeman@google.com4732df62014-01-23 15:22:42 +0000948 if (fType == SkScalerContext_GDI::kBitmap_Type || fType == SkScalerContext_GDI::kLine_Type) {
bungeman@google.come9d83192013-06-21 05:31:38 +0000949#endif
bungeman41078062014-07-07 08:16:37 -0700950 metrics->fTop = SkIntToScalar(-fTM.tmAscent);
951 metrics->fAscent = SkIntToScalar(-fTM.tmAscent);
952 metrics->fDescent = SkIntToScalar(fTM.tmDescent);
953 metrics->fBottom = SkIntToScalar(fTM.tmDescent);
954 metrics->fLeading = SkIntToScalar(fTM.tmExternalLeading);
955 metrics->fAvgCharWidth = SkIntToScalar(fTM.tmAveCharWidth);
956 metrics->fMaxCharWidth = SkIntToScalar(fTM.tmMaxCharWidth);
957 metrics->fXMin = 0;
958 metrics->fXMax = metrics->fMaxCharWidth;
959 //metrics->fXHeight = 0;
bungeman@google.come9d83192013-06-21 05:31:38 +0000960#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
bungeman@google.coma0319f62012-04-18 15:40:50 +0000961 return;
962 }
bungeman@google.come9d83192013-06-21 05:31:38 +0000963#endif
bungeman@google.coma0319f62012-04-18 15:40:50 +0000964
reed@google.comac6b9792011-03-11 15:42:51 +0000965 OUTLINETEXTMETRIC otm;
966
967 uint32_t ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000968 if (0 == ret) {
reed@google.com055180c2013-03-21 18:46:35 +0000969 LogFontTypeface::EnsureAccessible(this->getTypeface());
bungeman@google.com39698b12011-11-15 22:26:41 +0000970 ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
971 }
bungeman@google.com0abbff92013-07-27 20:37:56 +0000972 if (0 == ret) {
bungeman@google.coma0319f62012-04-18 15:40:50 +0000973 return;
reed@google.comac6b9792011-03-11 15:42:51 +0000974 }
975
bungeman@google.come9d83192013-06-21 05:31:38 +0000976#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
bungeman41078062014-07-07 08:16:37 -0700977 metrics->fTop = SkIntToScalar(-otm.otmrcFontBox.top);
978 metrics->fAscent = SkIntToScalar(-otm.otmAscent);
979 metrics->fDescent = SkIntToScalar(-otm.otmDescent);
980 metrics->fBottom = SkIntToScalar(-otm.otmrcFontBox.bottom);
981 metrics->fLeading = SkIntToScalar(otm.otmLineGap);
982 metrics->fAvgCharWidth = SkIntToScalar(otm.otmTextMetrics.tmAveCharWidth);
983 metrics->fMaxCharWidth = SkIntToScalar(otm.otmTextMetrics.tmMaxCharWidth);
984 metrics->fXMin = SkIntToScalar(otm.otmrcFontBox.left);
985 metrics->fXMax = SkIntToScalar(otm.otmrcFontBox.right);
commit-bot@chromium.orgd3031aa2014-05-14 14:54:51 +0000986#endif
bungeman41078062014-07-07 08:16:37 -0700987 metrics->fUnderlineThickness = SkIntToScalar(otm.otmsUnderscoreSize);
988 metrics->fUnderlinePosition = -SkIntToScalar(otm.otmsUnderscorePosition);
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +0000989
bungeman41078062014-07-07 08:16:37 -0700990 metrics->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag;
991 metrics->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
bungeman@google.com4a867a62014-05-22 17:59:21 +0000992
bungeman41078062014-07-07 08:16:37 -0700993 metrics->fXHeight = SkIntToScalar(otm.otmsXHeight);
994 GLYPHMETRICS gm;
995 sk_bzero(&gm, sizeof(gm));
996 DWORD len = GetGlyphOutlineW(fDDC, 'x', GGO_METRICS, &gm, 0, 0, &gMat2Identity);
997 if (len != GDI_ERROR && gm.gmBlackBoxY > 0) {
998 metrics->fXHeight = SkIntToScalar(gm.gmBlackBoxY);
reed@google.comac6b9792011-03-11 15:42:51 +0000999 }
1000}
1001
reed@google.com7430a332011-10-03 14:37:38 +00001002////////////////////////////////////////////////////////////////////////////////////////
1003
bungeman@google.com0abbff92013-07-27 20:37:56 +00001004#define SK_SHOW_TEXT_BLIT_COVERAGE 0
1005
reed@google.com7430a332011-10-03 14:37:38 +00001006static void build_power_table(uint8_t table[], float ee) {
1007 for (int i = 0; i < 256; i++) {
1008 float x = i / 255.f;
bungeman@google.com97efada2012-07-30 20:40:50 +00001009 x = sk_float_pow(x, ee);
reed@google.come1ca7052013-12-17 19:22:07 +00001010 int xx = SkScalarRoundToInt(x * 255);
reed@google.com7430a332011-10-03 14:37:38 +00001011 table[i] = SkToU8(xx);
1012 }
1013}
1014
bungeman@google.com97efada2012-07-30 20:40:50 +00001015/**
1016 * This will invert the gamma applied by GDI (gray-scale antialiased), so we
1017 * can get linear values.
1018 *
1019 * GDI grayscale appears to use a hard-coded gamma of 2.3.
1020 *
1021 * GDI grayscale appears to draw using the black and white rasterizer at four
1022 * times the size and then downsamples to compute the coverage mask. As a
1023 * result there are only seventeen total grays. This lack of fidelity means
1024 * that shifting into other color spaces is imprecise.
1025 */
1026static const uint8_t* getInverseGammaTableGDI() {
bungeman@google.com05a729f2013-06-20 15:29:16 +00001027 // Since build_power_table is idempotent, many threads can build gTableGdi
1028 // simultaneously.
1029
1030 // Microsoft Specific:
1031 // Making gInited volatile provides read-aquire and write-release in vc++.
1032 // In VS2012, see compiler option /volatile:(ms|iso).
1033 // Replace with C++11 atomics when possible.
1034 static volatile bool gInited;
bungeman@google.com97efada2012-07-30 20:40:50 +00001035 static uint8_t gTableGdi[256];
bungeman@google.com05a729f2013-06-20 15:29:16 +00001036 if (gInited) {
1037 // Need a L/L (read) barrier (full acquire not needed). If gInited is observed
1038 // true then gTableGdi is observable, but it must be requested.
1039 } else {
bungeman@google.com97efada2012-07-30 20:40:50 +00001040 build_power_table(gTableGdi, 2.3f);
bungeman@google.com05a729f2013-06-20 15:29:16 +00001041 // Need a S/S (write) barrier (full release not needed) here so that this
1042 // write to gInited becomes observable after gTableGdi.
bungeman@google.com97efada2012-07-30 20:40:50 +00001043 gInited = true;
1044 }
1045 return gTableGdi;
1046}
1047
1048/**
1049 * This will invert the gamma applied by GDI ClearType, so we can get linear
1050 * values.
1051 *
1052 * GDI ClearType uses SPI_GETFONTSMOOTHINGCONTRAST / 1000 as the gamma value.
1053 * If this value is not specified, the default is a gamma of 1.4.
1054 */
1055static const uint8_t* getInverseGammaTableClearType() {
bungeman@google.com05a729f2013-06-20 15:29:16 +00001056 // We don't expect SPI_GETFONTSMOOTHINGCONTRAST to ever change, so building
1057 // gTableClearType with build_power_table is effectively idempotent.
1058
1059 // Microsoft Specific:
1060 // Making gInited volatile provides read-aquire and write-release in vc++.
1061 // In VS2012, see compiler option /volatile:(ms|iso).
1062 // Replace with C++11 atomics when possible.
1063 static volatile bool gInited;
bungeman@google.com97efada2012-07-30 20:40:50 +00001064 static uint8_t gTableClearType[256];
bungeman@google.com05a729f2013-06-20 15:29:16 +00001065 if (gInited) {
1066 // Need a L/L (read) barrier (acquire not needed). If gInited is observed
1067 // true then gTableClearType is observable, but it must be requested.
1068 } else {
reed@google.com7430a332011-10-03 14:37:38 +00001069 UINT level = 0;
1070 if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &level, 0) || !level) {
1071 // can't get the data, so use a default
1072 level = 1400;
1073 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001074 build_power_table(gTableClearType, level / 1000.0f);
bungeman@google.com05a729f2013-06-20 15:29:16 +00001075 // Need a S/S (write) barrier (release not needed) here so that this
1076 // write to gInited becomes observable after gTableClearType.
reed@google.com7430a332011-10-03 14:37:38 +00001077 gInited = true;
1078 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001079 return gTableClearType;
reed@google.com7430a332011-10-03 14:37:38 +00001080}
1081
reed@google.comac6b9792011-03-11 15:42:51 +00001082#include "SkColorPriv.h"
1083
bungeman@google.com63853142012-08-01 15:36:46 +00001084//Cannot assume that the input rgb is gray due to possible setting of kGenA8FromLCD_Flag.
bungeman@google.com97efada2012-07-30 20:40:50 +00001085template<bool APPLY_PREBLEND>
1086static inline uint8_t rgb_to_a8(SkGdiRGB rgb, const uint8_t* table8) {
bungeman@google.com63853142012-08-01 15:36:46 +00001087 U8CPU r = (rgb >> 16) & 0xFF;
1088 U8CPU g = (rgb >> 8) & 0xFF;
1089 U8CPU b = (rgb >> 0) & 0xFF;
bungeman@google.com1bfe01d2012-08-28 16:02:42 +00001090 return sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
reed@google.com8351aab2012-01-18 17:06:35 +00001091}
1092
bungeman@google.com97efada2012-07-30 20:40:50 +00001093template<bool APPLY_PREBLEND>
1094static inline uint16_t rgb_to_lcd16(SkGdiRGB rgb, const uint8_t* tableR,
1095 const uint8_t* tableG,
1096 const uint8_t* tableB) {
1097 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1098 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1099 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001100#if SK_SHOW_TEXT_BLIT_COVERAGE
1101 r = SkMax32(r, 10); g = SkMax32(g, 10); b = SkMax32(b, 10);
1102#endif
bungeman@google.com97efada2012-07-30 20:40:50 +00001103 return SkPack888ToRGB16(r, g, b);
reed@google.com82a34d82011-07-26 19:33:08 +00001104}
1105
reed@google.com82cff022011-09-22 14:33:40 +00001106// Is this GDI color neither black nor white? If so, we have to keep this
1107// image as is, rather than smashing it down to a BW mask.
1108//
1109// returns int instead of bool, since we don't want/have to pay to convert
1110// the zero/non-zero value into a bool
1111static int is_not_black_or_white(SkGdiRGB c) {
1112 // same as (but faster than)
1113 // c &= 0x00FFFFFF;
1114 // return 0 == c || 0x00FFFFFF == c;
1115 return (c + (c & 1)) & 0x00FFFFFF;
reed@google.com5e2df642011-09-21 18:42:09 +00001116}
1117
bungeman@google.com4b18f572013-07-22 15:21:23 +00001118static bool is_rgb_really_bw(const SkGdiRGB* src, int width, int height, size_t srcRB) {
reed@google.com5e2df642011-09-21 18:42:09 +00001119 for (int y = 0; y < height; ++y) {
1120 for (int x = 0; x < width; ++x) {
reed@google.com82cff022011-09-22 14:33:40 +00001121 if (is_not_black_or_white(src[x])) {
reed@google.com5e2df642011-09-21 18:42:09 +00001122 return false;
1123 }
1124 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001125 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001126 }
1127 return true;
1128}
1129
bungeman@google.com97efada2012-07-30 20:40:50 +00001130// gdi's bitmap is upside-down, so we reverse dst walking in Y
1131// whenever we copy it into skia's buffer
reed@google.com5e2df642011-09-21 18:42:09 +00001132static void rgb_to_bw(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
bungeman@google.com97efada2012-07-30 20:40:50 +00001133 const SkGlyph& glyph) {
reed@google.com5e2df642011-09-21 18:42:09 +00001134 const int width = glyph.fWidth;
1135 const size_t dstRB = (width + 7) >> 3;
1136 uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1137
1138 int byteCount = width >> 3;
1139 int bitCount = width & 7;
1140
1141 // adjust srcRB to skip the values in our byteCount loop,
1142 // since we increment src locally there
1143 srcRB -= byteCount * 8 * sizeof(SkGdiRGB);
1144
1145 for (int y = 0; y < glyph.fHeight; ++y) {
1146 if (byteCount > 0) {
reed@google.com5e2df642011-09-21 18:42:09 +00001147 for (int i = 0; i < byteCount; ++i) {
reed@google.com7a230142011-09-27 14:07:21 +00001148 unsigned byte = 0;
bungeman@google.com97efada2012-07-30 20:40:50 +00001149 byte |= src[0] & (1 << 7);
1150 byte |= src[1] & (1 << 6);
1151 byte |= src[2] & (1 << 5);
1152 byte |= src[3] & (1 << 4);
1153 byte |= src[4] & (1 << 3);
1154 byte |= src[5] & (1 << 2);
1155 byte |= src[6] & (1 << 1);
1156 byte |= src[7] & (1 << 0);
reed@google.com6a8f14d2011-09-27 12:54:24 +00001157 dst[i] = byte;
reed@google.com5e2df642011-09-21 18:42:09 +00001158 src += 8;
1159 }
1160 }
1161 if (bitCount > 0) {
1162 unsigned byte = 0;
1163 unsigned mask = 0x80;
1164 for (int i = 0; i < bitCount; i++) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001165 byte |= src[i] & mask;
reed@google.com5e2df642011-09-21 18:42:09 +00001166 mask >>= 1;
1167 }
1168 dst[byteCount] = byte;
1169 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001170 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001171 dst -= dstRB;
1172 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001173#if SK_SHOW_TEXT_BLIT_COVERAGE
1174 if (glyph.fWidth > 0 && glyph.fHeight > 0) {
1175 uint8_t* first = (uint8_t*)glyph.fImage;
1176 uint8_t* last = (uint8_t*)((char*)glyph.fImage + glyph.fHeight * dstRB - 1);
1177 *first |= 1 << 7;
1178 *last |= bitCount == 0 ? 1 : 1 << (8 - bitCount);
1179 }
1180#endif
reed@google.com5e2df642011-09-21 18:42:09 +00001181}
1182
bungeman@google.com97efada2012-07-30 20:40:50 +00001183template<bool APPLY_PREBLEND>
reed@google.com5e2df642011-09-21 18:42:09 +00001184static void rgb_to_a8(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
bungeman@google.com97efada2012-07-30 20:40:50 +00001185 const SkGlyph& glyph, const uint8_t* table8) {
reed@google.com5e2df642011-09-21 18:42:09 +00001186 const size_t dstRB = glyph.rowBytes();
1187 const int width = glyph.fWidth;
1188 uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1189
1190 for (int y = 0; y < glyph.fHeight; y++) {
1191 for (int i = 0; i < width; i++) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001192 dst[i] = rgb_to_a8<APPLY_PREBLEND>(src[i], table8);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001193#if SK_SHOW_TEXT_BLIT_COVERAGE
1194 dst[i] = SkMax32(dst[i], 10);
1195#endif
reed@google.com5e2df642011-09-21 18:42:09 +00001196 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001197 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001198 dst -= dstRB;
1199 }
1200}
1201
bungeman@google.com97efada2012-07-30 20:40:50 +00001202template<bool APPLY_PREBLEND>
1203static void rgb_to_lcd16(const SkGdiRGB* SK_RESTRICT src, size_t srcRB, const SkGlyph& glyph,
1204 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
reed@google.com5e2df642011-09-21 18:42:09 +00001205 const size_t dstRB = glyph.rowBytes();
1206 const int width = glyph.fWidth;
1207 uint16_t* SK_RESTRICT dst = (uint16_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1208
1209 for (int y = 0; y < glyph.fHeight; y++) {
1210 for (int i = 0; i < width; i++) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001211 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(src[i], tableR, tableG, tableB);
reed@google.com5e2df642011-09-21 18:42:09 +00001212 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001213 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001214 dst = (uint16_t*)((char*)dst - dstRB);
1215 }
1216}
1217
reed@google.com6f5df482011-09-28 20:33:24 +00001218static inline unsigned clamp255(unsigned x) {
1219 SkASSERT(x <= 256);
1220 return x - (x >> 8);
1221}
1222
reed@google.com30ddd612013-07-30 17:47:39 +00001223void SkScalerContext_GDI::generateImage(const SkGlyph& glyph) {
reed@google.comac6b9792011-03-11 15:42:51 +00001224 SkASSERT(fDDC);
1225
reed@google.com62711172011-05-18 15:08:10 +00001226 const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
reed@google.com82a34d82011-07-26 19:33:08 +00001227 const bool isAA = !isLCD(fRec);
reed@google.comac6b9792011-03-11 15:42:51 +00001228
reed@google.com99edd432011-09-09 14:59:59 +00001229 size_t srcRB;
bungeman@google.com97efada2012-07-30 20:40:50 +00001230 const void* bits = fOffscreen.draw(glyph, isBW, &srcRB);
bungeman@google.com39698b12011-11-15 22:26:41 +00001231 if (NULL == bits) {
reed@google.com055180c2013-03-21 18:46:35 +00001232 LogFontTypeface::EnsureAccessible(this->getTypeface());
bungeman@google.com97efada2012-07-30 20:40:50 +00001233 bits = fOffscreen.draw(glyph, isBW, &srcRB);
bungeman@google.com39698b12011-11-15 22:26:41 +00001234 if (NULL == bits) {
1235 sk_bzero(glyph.fImage, glyph.computeImageSize());
1236 return;
1237 }
reed@google.com82a34d82011-07-26 19:33:08 +00001238 }
reed@google.comac6b9792011-03-11 15:42:51 +00001239
bungeman@google.com97efada2012-07-30 20:40:50 +00001240 if (!isBW) {
bungeman@google.com1bd2d672012-08-13 20:01:51 +00001241 const uint8_t* table;
1242 //The offscreen contains a GDI blit if isAA and kGenA8FromLCD_Flag is not set.
1243 //Otherwise the offscreen contains a ClearType blit.
1244 if (isAA && !(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag)) {
1245 table = getInverseGammaTableGDI();
1246 } else {
1247 table = getInverseGammaTableClearType();
bungeman@google.com97efada2012-07-30 20:40:50 +00001248 }
1249 //Note that the following cannot really be integrated into the
1250 //pre-blend, since we may not be applying the pre-blend; when we aren't
1251 //applying the pre-blend it means that a filter wants linear anyway.
1252 //Other code may also be applying the pre-blend, so we'd need another
1253 //one with this and one without.
reed@google.com6f5df482011-09-28 20:33:24 +00001254 SkGdiRGB* addr = (SkGdiRGB*)bits;
1255 for (int y = 0; y < glyph.fHeight; ++y) {
1256 for (int x = 0; x < glyph.fWidth; ++x) {
1257 int r = (addr[x] >> 16) & 0xFF;
1258 int g = (addr[x] >> 8) & 0xFF;
1259 int b = (addr[x] >> 0) & 0xFF;
reed@google.com7430a332011-10-03 14:37:38 +00001260 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
reed@google.com6f5df482011-09-28 20:33:24 +00001261 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001262 addr = SkTAddOffset<SkGdiRGB>(addr, srcRB);
reed@google.com6f5df482011-09-28 20:33:24 +00001263 }
1264 }
1265
reed@google.com82a34d82011-07-26 19:33:08 +00001266 int width = glyph.fWidth;
1267 size_t dstRB = glyph.rowBytes();
1268 if (isBW) {
1269 const uint8_t* src = (const uint8_t*)bits;
reed@google.com82a34d82011-07-26 19:33:08 +00001270 uint8_t* dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1271 for (int y = 0; y < glyph.fHeight; y++) {
1272 memcpy(dst, src, dstRB);
1273 src += srcRB;
1274 dst -= dstRB;
reed@google.comac6b9792011-03-11 15:42:51 +00001275 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001276#if SK_SHOW_TEXT_BLIT_COVERAGE
1277 if (glyph.fWidth > 0 && glyph.fHeight > 0) {
1278 int bitCount = width & 7;
1279 uint8_t* first = (uint8_t*)glyph.fImage;
1280 uint8_t* last = (uint8_t*)((char*)glyph.fImage + glyph.fHeight * dstRB - 1);
1281 *first |= 1 << 7;
1282 *last |= bitCount == 0 ? 1 : 1 << (8 - bitCount);
1283 }
1284#endif
reed@google.com82a34d82011-07-26 19:33:08 +00001285 } else if (isAA) {
reed@google.com6f5df482011-09-28 20:33:24 +00001286 // since the caller may require A8 for maskfilters, we can't check for BW
1287 // ... until we have the caller tell us that explicitly
reed@google.com5e2df642011-09-21 18:42:09 +00001288 const SkGdiRGB* src = (const SkGdiRGB*)bits;
bungeman@google.coma76de722012-10-26 19:35:54 +00001289 if (fPreBlend.isApplicable()) {
1290 rgb_to_a8<true>(src, srcRB, glyph, fPreBlend.fG);
bungeman@google.com97efada2012-07-30 20:40:50 +00001291 } else {
bungeman@google.coma76de722012-10-26 19:35:54 +00001292 rgb_to_a8<false>(src, srcRB, glyph, fPreBlend.fG);
bungeman@google.com97efada2012-07-30 20:40:50 +00001293 }
reed@google.com82a34d82011-07-26 19:33:08 +00001294 } else { // LCD16
reed@google.com5e2df642011-09-21 18:42:09 +00001295 const SkGdiRGB* src = (const SkGdiRGB*)bits;
1296 if (is_rgb_really_bw(src, width, glyph.fHeight, srcRB)) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001297 rgb_to_bw(src, srcRB, glyph);
reed@google.com5e2df642011-09-21 18:42:09 +00001298 ((SkGlyph*)&glyph)->fMaskFormat = SkMask::kBW_Format;
1299 } else {
reedd54d3fc2014-11-13 14:39:58 -08001300 SkASSERT(SkMask::kLCD16_Format == glyph.fMaskFormat);
1301 if (fPreBlend.isApplicable()) {
1302 rgb_to_lcd16<true>(src, srcRB, glyph,
1303 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
reed@google.com754e4eb2011-09-26 13:21:39 +00001304 } else {
reedd54d3fc2014-11-13 14:39:58 -08001305 rgb_to_lcd16<false>(src, srcRB, glyph,
1306 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
reed@google.com754e4eb2011-09-26 13:21:39 +00001307 }
reed@google.comac6b9792011-03-11 15:42:51 +00001308 }
1309 }
reed@google.comac6b9792011-03-11 15:42:51 +00001310}
1311
bungeman@google.com0abbff92013-07-27 20:37:56 +00001312class GDIGlyphbufferPointIter {
1313public:
skia.committer@gmail.com27e21fe2013-07-28 07:01:03 +00001314 GDIGlyphbufferPointIter(const uint8_t* glyphbuf, DWORD total_size)
bungeman@google.com0abbff92013-07-27 20:37:56 +00001315 : fHeaderIter(glyphbuf, total_size), fCurveIter(), fPointIter()
1316 { }
reed@google.comac6b9792011-03-11 15:42:51 +00001317
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001318 POINTFX const * next() {
bungeman@google.com0abbff92013-07-27 20:37:56 +00001319nextHeader:
1320 if (!fCurveIter.isSet()) {
1321 const TTPOLYGONHEADER* header = fHeaderIter.next();
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001322 if (NULL == header) {
1323 return NULL;
1324 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001325 fCurveIter.set(header);
1326 const TTPOLYCURVE* curve = fCurveIter.next();
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001327 if (NULL == curve) {
1328 return NULL;
1329 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001330 fPointIter.set(curve);
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001331 return &header->pfxStart;
bungeman@google.com05a729f2013-06-20 15:29:16 +00001332 }
1333
bungeman@google.com0abbff92013-07-27 20:37:56 +00001334 const POINTFX* nextPoint = fPointIter.next();
1335 if (NULL == nextPoint) {
1336 const TTPOLYCURVE* curve = fCurveIter.next();
1337 if (NULL == curve) {
1338 fCurveIter.set();
1339 goto nextHeader;
1340 } else {
1341 fPointIter.set(curve);
bungeman@google.com05a729f2013-06-20 15:29:16 +00001342 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001343 nextPoint = fPointIter.next();
reed@google.comac6b9792011-03-11 15:42:51 +00001344 }
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001345 return nextPoint;
reed@google.comac6b9792011-03-11 15:42:51 +00001346 }
bungeman@google.comd1c7f712013-03-11 18:51:19 +00001347
bungeman@google.com0abbff92013-07-27 20:37:56 +00001348 WORD currentCurveType() {
1349 return fPointIter.fCurveType;
1350 }
1351
1352private:
1353 /** Iterates over all of the polygon headers in a glyphbuf. */
1354 class GDIPolygonHeaderIter {
1355 public:
skia.committer@gmail.com27e21fe2013-07-28 07:01:03 +00001356 GDIPolygonHeaderIter(const uint8_t* glyphbuf, DWORD total_size)
bungeman@google.com0abbff92013-07-27 20:37:56 +00001357 : fCurPolygon(reinterpret_cast<const TTPOLYGONHEADER*>(glyphbuf))
1358 , fEndPolygon(SkTAddOffset<const TTPOLYGONHEADER>(glyphbuf, total_size))
1359 { }
1360
1361 const TTPOLYGONHEADER* next() {
1362 if (fCurPolygon >= fEndPolygon) {
1363 return NULL;
1364 }
1365 const TTPOLYGONHEADER* thisPolygon = fCurPolygon;
1366 fCurPolygon = SkTAddOffset<const TTPOLYGONHEADER>(fCurPolygon, fCurPolygon->cb);
1367 return thisPolygon;
1368 }
1369 private:
1370 const TTPOLYGONHEADER* fCurPolygon;
1371 const TTPOLYGONHEADER* fEndPolygon;
1372 };
1373
1374 /** Iterates over all of the polygon curves in a polygon header. */
1375 class GDIPolygonCurveIter {
1376 public:
1377 GDIPolygonCurveIter() : fCurCurve(NULL), fEndCurve(NULL) { }
1378
1379 GDIPolygonCurveIter(const TTPOLYGONHEADER* curPolygon)
1380 : fCurCurve(SkTAddOffset<const TTPOLYCURVE>(curPolygon, sizeof(TTPOLYGONHEADER)))
1381 , fEndCurve(SkTAddOffset<const TTPOLYCURVE>(curPolygon, curPolygon->cb))
1382 { }
1383
1384 bool isSet() { return fCurCurve != NULL; }
1385
1386 void set(const TTPOLYGONHEADER* curPolygon) {
1387 fCurCurve = SkTAddOffset<const TTPOLYCURVE>(curPolygon, sizeof(TTPOLYGONHEADER));
1388 fEndCurve = SkTAddOffset<const TTPOLYCURVE>(curPolygon, curPolygon->cb);
1389 }
1390 void set() {
1391 fCurCurve = NULL;
1392 fEndCurve = NULL;
1393 }
1394
1395 const TTPOLYCURVE* next() {
1396 if (fCurCurve >= fEndCurve) {
1397 return NULL;
1398 }
1399 const TTPOLYCURVE* thisCurve = fCurCurve;
1400 fCurCurve = SkTAddOffset<const TTPOLYCURVE>(fCurCurve, size_of_TTPOLYCURVE(*fCurCurve));
1401 return thisCurve;
1402 }
1403 private:
1404 size_t size_of_TTPOLYCURVE(const TTPOLYCURVE& curve) {
1405 return 2*sizeof(WORD) + curve.cpfx*sizeof(POINTFX);
1406 }
1407 const TTPOLYCURVE* fCurCurve;
1408 const TTPOLYCURVE* fEndCurve;
1409 };
1410
1411 /** Iterates over all of the polygon points in a polygon curve. */
1412 class GDIPolygonCurvePointIter {
1413 public:
1414 GDIPolygonCurvePointIter() : fCurveType(0), fCurPoint(NULL), fEndPoint(NULL) { }
1415
1416 GDIPolygonCurvePointIter(const TTPOLYCURVE* curPolygon)
1417 : fCurveType(curPolygon->wType)
1418 , fCurPoint(&curPolygon->apfx[0])
1419 , fEndPoint(&curPolygon->apfx[curPolygon->cpfx])
1420 { }
1421
1422 bool isSet() { return fCurPoint != NULL; }
1423
1424 void set(const TTPOLYCURVE* curPolygon) {
1425 fCurveType = curPolygon->wType;
1426 fCurPoint = &curPolygon->apfx[0];
1427 fEndPoint = &curPolygon->apfx[curPolygon->cpfx];
1428 }
1429 void set() {
1430 fCurPoint = NULL;
1431 fEndPoint = NULL;
1432 }
1433
1434 const POINTFX* next() {
1435 if (fCurPoint >= fEndPoint) {
1436 return NULL;
1437 }
1438 const POINTFX* thisPoint = fCurPoint;
1439 ++fCurPoint;
1440 return thisPoint;
1441 }
1442
1443 WORD fCurveType;
1444 private:
1445 const POINTFX* fCurPoint;
1446 const POINTFX* fEndPoint;
1447 };
1448
1449 GDIPolygonHeaderIter fHeaderIter;
1450 GDIPolygonCurveIter fCurveIter;
1451 GDIPolygonCurvePointIter fPointIter;
1452};
1453
1454static void sk_path_from_gdi_path(SkPath* path, const uint8_t* glyphbuf, DWORD total_size) {
bungeman@google.comd1c7f712013-03-11 18:51:19 +00001455 const uint8_t* cur_glyph = glyphbuf;
1456 const uint8_t* end_glyph = glyphbuf + total_size;
1457
1458 while (cur_glyph < end_glyph) {
1459 const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
1460
1461 const uint8_t* end_poly = cur_glyph + th->cb;
1462 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
1463
1464 path->moveTo(SkFixedToScalar( SkFIXEDToFixed(th->pfxStart.x)),
1465 SkFixedToScalar(-SkFIXEDToFixed(th->pfxStart.y)));
1466
1467 while (cur_poly < end_poly) {
1468 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
1469
1470 if (pc->wType == TT_PRIM_LINE) {
1471 for (uint16_t i = 0; i < pc->cpfx; i++) {
1472 path->lineTo(SkFixedToScalar( SkFIXEDToFixed(pc->apfx[i].x)),
1473 SkFixedToScalar(-SkFIXEDToFixed(pc->apfx[i].y)));
1474 }
1475 }
1476
1477 if (pc->wType == TT_PRIM_QSPLINE) {
1478 for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
1479 POINTFX pnt_b = pc->apfx[u]; // B is always the current point
1480 POINTFX pnt_c = pc->apfx[u+1];
1481
1482 if (u < pc->cpfx - 2) { // If not on last spline, compute C
1483 pnt_c.x = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.x),
1484 SkFIXEDToFixed(pnt_c.x)));
1485 pnt_c.y = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.y),
1486 SkFIXEDToFixed(pnt_c.y)));
1487 }
1488
1489 path->quadTo(SkFixedToScalar( SkFIXEDToFixed(pnt_b.x)),
1490 SkFixedToScalar(-SkFIXEDToFixed(pnt_b.y)),
1491 SkFixedToScalar( SkFIXEDToFixed(pnt_c.x)),
1492 SkFixedToScalar(-SkFIXEDToFixed(pnt_c.y)));
1493 }
1494 }
1495 // Advance past this TTPOLYCURVE.
1496 cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx;
1497 }
1498 cur_glyph += th->cb;
1499 path->close();
reed@google.comac6b9792011-03-11 15:42:51 +00001500 }
reed@google.comac6b9792011-03-11 15:42:51 +00001501}
1502
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001503#define move_next_expected_hinted_point(iter, pElem) do {\
1504 pElem = iter.next(); \
1505 if (NULL == pElem) return false; \
1506} while(0)
1507
1508// It is possible for the hinted and unhinted versions of the same path to have
1509// a different number of points due to GDI's handling of flipped points.
1510// If this is detected, this will return false.
1511static bool sk_path_from_gdi_paths(SkPath* path, const uint8_t* glyphbuf, DWORD total_size,
bungeman@google.com0abbff92013-07-27 20:37:56 +00001512 GDIGlyphbufferPointIter hintedYs) {
1513 const uint8_t* cur_glyph = glyphbuf;
1514 const uint8_t* end_glyph = glyphbuf + total_size;
1515
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001516 POINTFX const * hintedPoint;
1517
bungeman@google.com0abbff92013-07-27 20:37:56 +00001518 while (cur_glyph < end_glyph) {
1519 const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
1520
1521 const uint8_t* end_poly = cur_glyph + th->cb;
1522 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
1523
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001524 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001525 path->moveTo(SkFixedToScalar( SkFIXEDToFixed(th->pfxStart.x)),
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001526 SkFixedToScalar(-SkFIXEDToFixed(hintedPoint->y)));
bungeman@google.com0abbff92013-07-27 20:37:56 +00001527
1528 while (cur_poly < end_poly) {
1529 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
1530
1531 if (pc->wType == TT_PRIM_LINE) {
1532 for (uint16_t i = 0; i < pc->cpfx; i++) {
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001533 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001534 path->lineTo(SkFixedToScalar( SkFIXEDToFixed(pc->apfx[i].x)),
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001535 SkFixedToScalar(-SkFIXEDToFixed(hintedPoint->y)));
bungeman@google.com0abbff92013-07-27 20:37:56 +00001536 }
1537 }
1538
1539 if (pc->wType == TT_PRIM_QSPLINE) {
1540 POINTFX currentPoint = pc->apfx[0];
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001541 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001542 // only take the hinted y if it wasn't flipped
1543 if (hintedYs.currentCurveType() == TT_PRIM_QSPLINE) {
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001544 currentPoint.y = hintedPoint->y;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001545 }
1546 for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
1547 POINTFX pnt_b = currentPoint;//pc->apfx[u]; // B is always the current point
1548 POINTFX pnt_c = pc->apfx[u+1];
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001549 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001550 // only take the hinted y if it wasn't flipped
1551 if (hintedYs.currentCurveType() == TT_PRIM_QSPLINE) {
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001552 pnt_c.y = hintedPoint->y;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001553 }
1554 currentPoint.x = pnt_c.x;
1555 currentPoint.y = pnt_c.y;
1556
1557 if (u < pc->cpfx - 2) { // If not on last spline, compute C
1558 pnt_c.x = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.x),
1559 SkFIXEDToFixed(pnt_c.x)));
1560 pnt_c.y = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.y),
1561 SkFIXEDToFixed(pnt_c.y)));
1562 }
1563
1564 path->quadTo(SkFixedToScalar( SkFIXEDToFixed(pnt_b.x)),
1565 SkFixedToScalar(-SkFIXEDToFixed(pnt_b.y)),
1566 SkFixedToScalar( SkFIXEDToFixed(pnt_c.x)),
1567 SkFixedToScalar(-SkFIXEDToFixed(pnt_c.y)));
1568 }
1569 }
1570 // Advance past this TTPOLYCURVE.
1571 cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx;
1572 }
1573 cur_glyph += th->cb;
1574 path->close();
1575 }
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001576 return true;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001577}
1578
reed@google.com30ddd612013-07-30 17:47:39 +00001579DWORD SkScalerContext_GDI::getGDIGlyphPath(const SkGlyph& glyph, UINT flags,
bungeman@google.com0abbff92013-07-27 20:37:56 +00001580 SkAutoSTMalloc<BUFFERSIZE, uint8_t>* glyphbuf)
1581{
1582 GLYPHMETRICS gm;
1583
1584 DWORD total_size = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, BUFFERSIZE, glyphbuf->get(), &fMat22);
1585 // Sometimes GetGlyphOutlineW returns a number larger than BUFFERSIZE even if BUFFERSIZE > 0.
1586 // It has been verified that this does not involve a buffer overrun.
1587 if (GDI_ERROR == total_size || total_size > BUFFERSIZE) {
1588 // GDI_ERROR because the BUFFERSIZE was too small, or because the data was not accessible.
1589 // When the data is not accessable GetGlyphOutlineW fails rather quickly,
1590 // so just try to get the size. If that fails then ensure the data is accessible.
1591 total_size = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, 0, NULL, &fMat22);
1592 if (GDI_ERROR == total_size) {
1593 LogFontTypeface::EnsureAccessible(this->getTypeface());
1594 total_size = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, 0, NULL, &fMat22);
1595 if (GDI_ERROR == total_size) {
kkinnunenc6cb56f2014-06-24 00:12:27 -07001596 // GetGlyphOutlineW is known to fail for some characters, such as spaces.
1597 // In these cases, just return that the glyph does not have a shape.
bungeman@google.com0abbff92013-07-27 20:37:56 +00001598 return 0;
1599 }
1600 }
1601
1602 glyphbuf->reset(total_size);
1603
1604 DWORD ret = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, total_size, glyphbuf->get(), &fMat22);
1605 if (GDI_ERROR == ret) {
1606 LogFontTypeface::EnsureAccessible(this->getTypeface());
1607 ret = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, total_size, glyphbuf->get(), &fMat22);
1608 if (GDI_ERROR == ret) {
1609 SkASSERT(false);
1610 return 0;
1611 }
1612 }
1613 }
1614 return total_size;
1615}
1616
reed@google.com30ddd612013-07-30 17:47:39 +00001617void SkScalerContext_GDI::generatePath(const SkGlyph& glyph, SkPath* path) {
bungeman@google.com0abbff92013-07-27 20:37:56 +00001618 SkASSERT(&glyph && path);
1619 SkASSERT(fDDC);
1620
1621 path->reset();
1622
1623 // Out of all the fonts on a typical Windows box,
1624 // 25% of glyphs require more than 2KB.
1625 // 1% of glyphs require more than 4KB.
1626 // 0.01% of glyphs require more than 8KB.
1627 // 8KB is less than 1% of the normal 1MB stack on Windows.
1628 // Note that some web fonts glyphs require more than 20KB.
1629 //static const DWORD BUFFERSIZE = (1 << 13);
1630
1631 //GDI only uses hinted outlines when axis aligned.
1632 UINT format = GGO_NATIVE | GGO_GLYPH_INDEX;
1633 if (fRec.getHinting() == SkPaint::kNo_Hinting || fRec.getHinting() == SkPaint::kSlight_Hinting){
1634 format |= GGO_UNHINTED;
1635 }
1636 SkAutoSTMalloc<BUFFERSIZE, uint8_t> glyphbuf(BUFFERSIZE);
1637 DWORD total_size = getGDIGlyphPath(glyph, format, &glyphbuf);
1638 if (0 == total_size) {
1639 return;
1640 }
1641
1642 if (fRec.getHinting() != SkPaint::kSlight_Hinting) {
1643 sk_path_from_gdi_path(path, glyphbuf, total_size);
1644 } else {
1645 //GDI only uses hinted outlines when axis aligned.
1646 UINT format = GGO_NATIVE | GGO_GLYPH_INDEX;
1647
1648 SkAutoSTMalloc<BUFFERSIZE, uint8_t> hintedGlyphbuf(BUFFERSIZE);
1649 DWORD hinted_total_size = getGDIGlyphPath(glyph, format, &hintedGlyphbuf);
1650 if (0 == hinted_total_size) {
1651 return;
1652 }
1653
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001654 if (!sk_path_from_gdi_paths(path, glyphbuf, total_size,
1655 GDIGlyphbufferPointIter(hintedGlyphbuf, hinted_total_size)))
1656 {
1657 path->reset();
1658 sk_path_from_gdi_path(path, glyphbuf, total_size);
1659 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001660 }
1661}
1662
reed@google.com484f5bc2013-04-24 19:14:56 +00001663static void logfont_for_name(const char* familyName, LOGFONT* lf) {
1664 sk_bzero(lf, sizeof(LOGFONT));
bungeman@google.come70f7982012-06-01 19:38:19 +00001665#ifdef UNICODE
reed@google.com484f5bc2013-04-24 19:14:56 +00001666 // Get the buffer size needed first.
1667 size_t str_len = ::MultiByteToWideChar(CP_UTF8, 0, familyName,
1668 -1, NULL, 0);
1669 // Allocate a buffer (str_len already has terminating null
1670 // accounted for).
1671 wchar_t *wideFamilyName = new wchar_t[str_len];
1672 // Now actually convert the string.
1673 ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1,
1674 wideFamilyName, str_len);
1675 ::wcsncpy(lf->lfFaceName, wideFamilyName, LF_FACESIZE - 1);
1676 delete [] wideFamilyName;
1677 lf->lfFaceName[LF_FACESIZE-1] = L'\0';
bungeman@google.come70f7982012-06-01 19:38:19 +00001678#else
reed@google.com484f5bc2013-04-24 19:14:56 +00001679 ::strncpy(lf->lfFaceName, familyName, LF_FACESIZE - 1);
1680 lf->lfFaceName[LF_FACESIZE - 1] = '\0';
bungeman@google.come70f7982012-06-01 19:38:19 +00001681#endif
1682}
1683
bungemanb374d6a2014-09-17 07:48:59 -07001684void LogFontTypeface::onGetFamilyName(SkString* familyName) const {
bungeman@google.com7103f182012-10-31 20:53:49 +00001685 // Get the actual name of the typeface. The logfont may not know this.
reed@google.com5526ede2013-03-25 13:03:37 +00001686 HFONT font = CreateFontIndirect(&fLogFont);
bungeman@google.com7103f182012-10-31 20:53:49 +00001687
1688 HDC deviceContext = ::CreateCompatibleDC(NULL);
1689 HFONT savefont = (HFONT)SelectObject(deviceContext, font);
1690
bungemanb374d6a2014-09-17 07:48:59 -07001691 dcfontname_to_skstring(deviceContext, fLogFont, familyName);
bungeman@google.com7103f182012-10-31 20:53:49 +00001692
1693 if (deviceContext) {
1694 ::SelectObject(deviceContext, savefont);
1695 ::DeleteDC(deviceContext);
1696 }
1697 if (font) {
1698 ::DeleteObject(font);
1699 }
bungemanb374d6a2014-09-17 07:48:59 -07001700}
bungeman@google.com7103f182012-10-31 20:53:49 +00001701
bungemanb374d6a2014-09-17 07:48:59 -07001702void LogFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc,
1703 bool* isLocalStream) const {
1704 SkString familyName;
1705 this->onGetFamilyName(&familyName);
reed@google.com5526ede2013-03-25 13:03:37 +00001706 desc->setFamilyName(familyName.c_str());
1707 *isLocalStream = this->fSerializeAsStream;
reed@google.comac6b9792011-03-11 15:42:51 +00001708}
1709
1710static bool getWidthAdvance(HDC hdc, int gId, int16_t* advance) {
1711 // Initialize the MAT2 structure to the identify transformation matrix.
1712 static const MAT2 mat2 = {SkScalarToFIXED(1), SkScalarToFIXED(0),
1713 SkScalarToFIXED(0), SkScalarToFIXED(1)};
1714 int flags = GGO_METRICS | GGO_GLYPH_INDEX;
1715 GLYPHMETRICS gm;
1716 if (GDI_ERROR == GetGlyphOutline(hdc, gId, flags, &gm, 0, NULL, &mat2)) {
1717 return false;
1718 }
1719 SkASSERT(advance);
1720 *advance = gm.gmCellIncX;
1721 return true;
1722}
1723
reed@google.com2689f612013-03-20 20:01:47 +00001724SkAdvancedTypefaceMetrics* LogFontTypeface::onGetAdvancedTypefaceMetrics(
vandebo@chromium.org37ad8fb2011-08-18 02:38:50 +00001725 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
1726 const uint32_t* glyphIDs,
reed@google.com2689f612013-03-20 20:01:47 +00001727 uint32_t glyphIDsCount) const {
1728 LOGFONT lf = fLogFont;
reed@google.comac6b9792011-03-11 15:42:51 +00001729 SkAdvancedTypefaceMetrics* info = NULL;
1730
1731 HDC hdc = CreateCompatibleDC(NULL);
1732 HFONT font = CreateFontIndirect(&lf);
1733 HFONT savefont = (HFONT)SelectObject(hdc, font);
1734 HFONT designFont = NULL;
1735
reed@google.com05b6f3a2011-11-28 15:30:28 +00001736 const char stem_chars[] = {'i', 'I', '!', '1'};
1737 int16_t min_width;
1738 unsigned glyphCount;
1739
reed@google.comac6b9792011-03-11 15:42:51 +00001740 // To request design units, create a logical font whose height is specified
1741 // as unitsPerEm.
1742 OUTLINETEXTMETRIC otm;
bungeman@google.com39698b12011-11-15 22:26:41 +00001743 unsigned int otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
1744 if (0 == otmRet) {
reed@google.com055180c2013-03-21 18:46:35 +00001745 call_ensure_accessible(lf);
bungeman@google.com39698b12011-11-15 22:26:41 +00001746 otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
1747 }
1748 if (!otmRet || !GetTextFace(hdc, LF_FACESIZE, lf.lfFaceName)) {
reed@google.comac6b9792011-03-11 15:42:51 +00001749 goto Error;
1750 }
1751 lf.lfHeight = -SkToS32(otm.otmEMSquare);
1752 designFont = CreateFontIndirect(&lf);
1753 SelectObject(hdc, designFont);
1754 if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) {
1755 goto Error;
1756 }
bungeman@google.com7bdd6142013-07-15 19:42:57 +00001757 glyphCount = calculateGlyphCount(hdc, fLogFont);
reed@google.comac6b9792011-03-11 15:42:51 +00001758
1759 info = new SkAdvancedTypefaceMetrics;
1760 info->fEmSize = otm.otmEMSquare;
reed@google.comac6b9792011-03-11 15:42:51 +00001761 info->fLastGlyphID = SkToU16(glyphCount - 1);
1762 info->fStyle = 0;
bungeman@google.com7103f182012-10-31 20:53:49 +00001763 tchar_to_skstring(lf.lfFaceName, &info->fFontName);
vandebo0f9bad02014-06-19 11:05:39 -07001764 info->fFlags = SkAdvancedTypefaceMetrics::kEmpty_FontFlag;
1765 // If bit 1 is set, the font may not be embedded in a document.
1766 // If bit 1 is clear, the font can be embedded.
1767 // If bit 2 is set, the embedding is read-only.
1768 if (otm.otmfsType & 0x1) {
1769 info->fFlags = SkTBitOr<SkAdvancedTypefaceMetrics::FontFlags>(
1770 info->fFlags,
1771 SkAdvancedTypefaceMetrics::kNotEmbeddable_FontFlag);
1772 }
reed@google.comac6b9792011-03-11 15:42:51 +00001773
vandebo@chromium.org6744d492011-05-09 18:13:47 +00001774 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
1775 populate_glyph_to_unicode(hdc, glyphCount, &(info->fGlyphToUnicode));
1776 }
1777
vandebo@chromium.orgd41e70d2012-03-08 19:41:01 +00001778 if (glyphCount > 0 &&
1779 (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE)) {
reed@google.comac6b9792011-03-11 15:42:51 +00001780 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
1781 } else {
1782 info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
1783 info->fItalicAngle = 0;
1784 info->fAscent = 0;
1785 info->fDescent = 0;
1786 info->fStemV = 0;
1787 info->fCapHeight = 0;
1788 info->fBBox = SkIRect::MakeEmpty();
edisonn@google.com390c6d72013-04-06 20:26:15 +00001789 goto ReturnInfo;
reed@google.comac6b9792011-03-11 15:42:51 +00001790 }
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +00001791
reed@google.comac6b9792011-03-11 15:42:51 +00001792 // If this bit is clear the font is a fixed pitch font.
1793 if (!(otm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
1794 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1795 }
1796 if (otm.otmTextMetrics.tmItalic) {
1797 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1798 }
reed@google.comac6b9792011-03-11 15:42:51 +00001799 if (otm.otmTextMetrics.tmPitchAndFamily & FF_ROMAN) {
1800 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1801 } else if (otm.otmTextMetrics.tmPitchAndFamily & FF_SCRIPT) {
1802 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1803 }
1804
1805 // The main italic angle of the font, in tenths of a degree counterclockwise
1806 // from vertical.
1807 info->fItalicAngle = otm.otmItalicAngle / 10;
1808 info->fAscent = SkToS16(otm.otmTextMetrics.tmAscent);
1809 info->fDescent = SkToS16(-otm.otmTextMetrics.tmDescent);
1810 // TODO(ctguil): Use alternate cap height calculation.
1811 // MSDN says otmsCapEmHeight is not support but it is returning a value on
1812 // my Win7 box.
1813 info->fCapHeight = otm.otmsCapEmHeight;
1814 info->fBBox =
1815 SkIRect::MakeLTRB(otm.otmrcFontBox.left, otm.otmrcFontBox.top,
1816 otm.otmrcFontBox.right, otm.otmrcFontBox.bottom);
1817
1818 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1819 // This probably isn't very good with an italic font.
reed@google.com05b6f3a2011-11-28 15:30:28 +00001820 min_width = SHRT_MAX;
reed@google.comac6b9792011-03-11 15:42:51 +00001821 info->fStemV = 0;
reed@google.comac6b9792011-03-11 15:42:51 +00001822 for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {
1823 ABC abcWidths;
1824 if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) {
1825 int16_t width = abcWidths.abcB;
1826 if (width > 0 && width < min_width) {
1827 min_width = width;
1828 info->fStemV = min_width;
1829 }
1830 }
1831 }
1832
vandebo0f9bad02014-06-19 11:05:39 -07001833 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +00001834 if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
1835 appendRange(&info->fGlyphWidths, 0);
1836 info->fGlyphWidths->fAdvance.append(1, &min_width);
1837 finishRange(info->fGlyphWidths.get(), 0,
1838 SkAdvancedTypefaceMetrics::WidthRange::kDefault);
1839 } else {
1840 info->fGlyphWidths.reset(
vandebo@chromium.org37ad8fb2011-08-18 02:38:50 +00001841 getAdvanceData(hdc,
1842 glyphCount,
1843 glyphIDs,
1844 glyphIDsCount,
1845 &getWidthAdvance));
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +00001846 }
reed@google.comac6b9792011-03-11 15:42:51 +00001847 }
1848
1849Error:
edisonn@google.com390c6d72013-04-06 20:26:15 +00001850ReturnInfo:
reed@google.comac6b9792011-03-11 15:42:51 +00001851 SelectObject(hdc, savefont);
1852 DeleteObject(designFont);
1853 DeleteObject(font);
1854 DeleteDC(hdc);
1855
1856 return info;
1857}
1858
bungeman@google.coma5501992012-05-18 19:06:41 +00001859//Dummy representation of a Base64 encoded GUID from create_unique_font_name.
1860#define BASE64_GUID_ID "XXXXXXXXXXXXXXXXXXXXXXXX"
1861//Length of GUID representation from create_id, including NULL terminator.
1862#define BASE64_GUID_ID_LEN SK_ARRAY_COUNT(BASE64_GUID_ID)
reed@google.comac6b9792011-03-11 15:42:51 +00001863
bungeman@google.coma5501992012-05-18 19:06:41 +00001864SK_COMPILE_ASSERT(BASE64_GUID_ID_LEN < LF_FACESIZE, GUID_longer_than_facesize);
1865
1866/**
1867 NameID 6 Postscript names cannot have the character '/'.
1868 It would be easier to hex encode the GUID, but that is 32 bytes,
1869 and many systems have issues with names longer than 28 bytes.
1870 The following need not be any standard base64 encoding.
1871 The encoded value is never decoded.
1872*/
rmistry@google.comd6176b02012-08-23 18:14:13 +00001873static const char postscript_safe_base64_encode[] =
bungeman@google.coma5501992012-05-18 19:06:41 +00001874 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1875 "abcdefghijklmnopqrstuvwxyz"
1876 "0123456789-_=";
1877
1878/**
1879 Formats a GUID into Base64 and places it into buffer.
1880 buffer should have space for at least BASE64_GUID_ID_LEN characters.
1881 The string will always be null terminated.
1882 XXXXXXXXXXXXXXXXXXXXXXXX0
1883 */
1884static void format_guid_b64(const GUID& guid, char* buffer, size_t bufferSize) {
1885 SkASSERT(bufferSize >= BASE64_GUID_ID_LEN);
1886 size_t written = SkBase64::Encode(&guid, sizeof(guid), buffer, postscript_safe_base64_encode);
1887 SkASSERT(written < LF_FACESIZE);
1888 buffer[written] = '\0';
1889}
1890
1891/**
1892 Creates a Base64 encoded GUID and places it into buffer.
1893 buffer should have space for at least BASE64_GUID_ID_LEN characters.
1894 The string will always be null terminated.
1895 XXXXXXXXXXXXXXXXXXXXXXXX0
1896 */
1897static HRESULT create_unique_font_name(char* buffer, size_t bufferSize) {
1898 GUID guid = {};
1899 if (FAILED(CoCreateGuid(&guid))) {
1900 return E_UNEXPECTED;
1901 }
1902 format_guid_b64(guid, buffer, bufferSize);
1903
1904 return S_OK;
1905}
1906
1907/**
1908 Introduces a font to GDI. On failure will return NULL. The returned handle
1909 should eventually be passed to RemoveFontMemResourceEx.
1910*/
1911static HANDLE activate_font(SkData* fontData) {
1912 DWORD numFonts = 0;
1913 //AddFontMemResourceEx just copies the data, but does not specify const.
1914 HANDLE fontHandle = AddFontMemResourceEx(const_cast<void*>(fontData->data()),
bungeman@google.com4b18f572013-07-22 15:21:23 +00001915 static_cast<DWORD>(fontData->size()),
bungeman@google.coma5501992012-05-18 19:06:41 +00001916 0,
1917 &numFonts);
1918
1919 if (fontHandle != NULL && numFonts < 1) {
1920 RemoveFontMemResourceEx(fontHandle);
1921 return NULL;
1922 }
1923
1924 return fontHandle;
1925}
1926
reed@google.com437eea12013-04-25 20:40:02 +00001927static SkTypeface* create_from_stream(SkStream* stream) {
bungeman@google.coma5501992012-05-18 19:06:41 +00001928 // Create a unique and unpredictable font name.
1929 // Avoids collisions and access from CSS.
1930 char familyName[BASE64_GUID_ID_LEN];
1931 const int familyNameSize = SK_ARRAY_COUNT(familyName);
1932 if (FAILED(create_unique_font_name(familyName, familyNameSize))) {
1933 return NULL;
1934 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001935
bungeman@google.coma5501992012-05-18 19:06:41 +00001936 // Change the name of the font.
bungeman@google.come9bbee32012-05-21 13:46:13 +00001937 SkAutoTUnref<SkData> rewrittenFontData(SkOTUtils::RenameFont(stream, familyName, familyNameSize-1));
1938 if (NULL == rewrittenFontData.get()) {
bungeman@google.coma5501992012-05-18 19:06:41 +00001939 return NULL;
1940 }
bungeman@google.coma5501992012-05-18 19:06:41 +00001941
1942 // Register the font with GDI.
bungeman@google.come9bbee32012-05-21 13:46:13 +00001943 HANDLE fontReference = activate_font(rewrittenFontData.get());
bungeman@google.coma5501992012-05-18 19:06:41 +00001944 if (NULL == fontReference) {
1945 return NULL;
1946 }
1947
1948 // Create the typeface.
1949 LOGFONT lf;
reed@google.com484f5bc2013-04-24 19:14:56 +00001950 logfont_for_name(familyName, &lf);
bungeman@google.coma5501992012-05-18 19:06:41 +00001951
1952 return SkCreateFontMemResourceTypefaceFromLOGFONT(lf, fontReference);
reed@google.comac6b9792011-03-11 15:42:51 +00001953}
1954
reed@google.com0042b9c2013-03-21 20:16:04 +00001955SkStream* LogFontTypeface::onOpenStream(int* ttcIndex) const {
1956 *ttcIndex = 0;
1957
ctguil@chromium.orgf4c26222011-05-16 22:00:05 +00001958 const DWORD kTTCTag =
1959 SkEndian_SwapBE32(SkSetFourByteTag('t', 't', 'c', 'f'));
reed@google.com0042b9c2013-03-21 20:16:04 +00001960 LOGFONT lf = fLogFont;
reed@google.comac6b9792011-03-11 15:42:51 +00001961
1962 HDC hdc = ::CreateCompatibleDC(NULL);
reed@google.com59d2f632011-05-02 19:36:59 +00001963 HFONT font = CreateFontIndirect(&lf);
reed@google.comac6b9792011-03-11 15:42:51 +00001964 HFONT savefont = (HFONT)SelectObject(hdc, font);
1965
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001966 SkMemoryStream* stream = NULL;
1967 DWORD tables[2] = {kTTCTag, 0};
1968 for (int i = 0; i < SK_ARRAY_COUNT(tables); i++) {
bungeman@google.com4b18f572013-07-22 15:21:23 +00001969 DWORD bufferSize = GetFontData(hdc, tables[i], 0, NULL, 0);
bungeman@google.com39698b12011-11-15 22:26:41 +00001970 if (bufferSize == GDI_ERROR) {
reed@google.com055180c2013-03-21 18:46:35 +00001971 call_ensure_accessible(lf);
bungeman@google.com39698b12011-11-15 22:26:41 +00001972 bufferSize = GetFontData(hdc, tables[i], 0, NULL, 0);
1973 }
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001974 if (bufferSize != GDI_ERROR) {
1975 stream = new SkMemoryStream(bufferSize);
bungeman@google.com4b18f572013-07-22 15:21:23 +00001976 if (GetFontData(hdc, tables[i], 0, (void*)stream->getMemoryBase(), bufferSize)) {
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001977 break;
1978 } else {
1979 delete stream;
1980 stream = NULL;
1981 }
1982 }
reed@google.comac6b9792011-03-11 15:42:51 +00001983 }
1984
1985 SelectObject(hdc, savefont);
1986 DeleteObject(font);
1987 DeleteDC(hdc);
1988
1989 return stream;
1990}
1991
bungeman@google.com3c996f82013-10-24 21:39:35 +00001992static void bmpCharsToGlyphs(HDC hdc, const WCHAR* bmpChars, int count, uint16_t* glyphs,
1993 bool Ox1FHack)
1994{
1995 DWORD result = GetGlyphIndicesW(hdc, bmpChars, count, glyphs, GGI_MARK_NONEXISTING_GLYPHS);
1996 if (GDI_ERROR == result) {
1997 for (int i = 0; i < count; ++i) {
1998 glyphs[i] = 0;
1999 }
2000 return;
2001 }
2002
2003 if (Ox1FHack) {
2004 for (int i = 0; i < count; ++i) {
2005 if (0xFFFF == glyphs[i] || 0x1F == glyphs[i]) {
2006 glyphs[i] = 0;
2007 }
2008 }
2009 } else {
2010 for (int i = 0; i < count; ++i) {
2011 if (0xFFFF == glyphs[i]){
2012 glyphs[i] = 0;
2013 }
2014 }
2015 }
2016}
2017
2018static uint16_t nonBmpCharToGlyph(HDC hdc, SCRIPT_CACHE* scriptCache, const WCHAR utf16[2]) {
2019 uint16_t index = 0;
2020 // Use uniscribe to detemine glyph index for non-BMP characters.
2021 static const int numWCHAR = 2;
2022 static const int maxItems = 2;
2023 // MSDN states that this can be NULL, but some things don't work then.
2024 SCRIPT_CONTROL scriptControl = { 0 };
2025 // Add extra item to SCRIPT_ITEM to work around a bug (now documented).
2026 // https://bugzilla.mozilla.org/show_bug.cgi?id=366643
2027 SCRIPT_ITEM si[maxItems + 1];
2028 int numItems;
2029 HRZM(ScriptItemize(utf16, numWCHAR, maxItems, &scriptControl, NULL, si, &numItems),
2030 "Could not itemize character.");
2031
2032 // Sometimes ScriptShape cannot find a glyph for a non-BMP and returns 2 space glyphs.
2033 static const int maxGlyphs = 2;
2034 SCRIPT_VISATTR vsa[maxGlyphs];
2035 WORD outGlyphs[maxGlyphs];
2036 WORD logClust[numWCHAR];
2037 int numGlyphs;
2038 HRZM(ScriptShape(hdc, scriptCache, utf16, numWCHAR, maxGlyphs, &si[0].a,
2039 outGlyphs, logClust, vsa, &numGlyphs),
2040 "Could not shape character.");
2041 if (1 == numGlyphs) {
2042 index = outGlyphs[0];
2043 }
2044 return index;
2045}
2046
2047class SkAutoHDC {
2048public:
2049 SkAutoHDC(const LOGFONT& lf)
2050 : fHdc(::CreateCompatibleDC(NULL))
2051 , fFont(::CreateFontIndirect(&lf))
2052 , fSavefont((HFONT)SelectObject(fHdc, fFont))
2053 { }
2054 ~SkAutoHDC() {
2055 SelectObject(fHdc, fSavefont);
2056 DeleteObject(fFont);
2057 DeleteDC(fHdc);
2058 }
2059 operator HDC() { return fHdc; }
2060private:
2061 HDC fHdc;
2062 HFONT fFont;
2063 HFONT fSavefont;
2064};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +00002065#define SkAutoHDC(...) SK_REQUIRE_LOCAL_VAR(SkAutoHDC)
bungeman@google.com3c996f82013-10-24 21:39:35 +00002066
2067int LogFontTypeface::onCharsToGlyphs(const void* chars, Encoding encoding,
2068 uint16_t userGlyphs[], int glyphCount) const
2069{
2070 SkAutoHDC hdc(fLogFont);
2071
2072 TEXTMETRIC tm;
2073 if (0 == GetTextMetrics(hdc, &tm)) {
2074 call_ensure_accessible(fLogFont);
2075 if (0 == GetTextMetrics(hdc, &tm)) {
2076 tm.tmPitchAndFamily = TMPF_TRUETYPE;
2077 }
2078 }
2079 bool Ox1FHack = !(tm.tmPitchAndFamily & TMPF_VECTOR) /*&& winVer < Vista */;
2080
2081 SkAutoSTMalloc<256, uint16_t> scratchGlyphs;
2082 uint16_t* glyphs;
2083 if (userGlyphs != NULL) {
2084 glyphs = userGlyphs;
2085 } else {
2086 glyphs = scratchGlyphs.reset(glyphCount);
2087 }
2088
2089 SCRIPT_CACHE sc = 0;
2090 switch (encoding) {
2091 case SkTypeface::kUTF8_Encoding: {
2092 static const int scratchCount = 256;
2093 WCHAR scratch[scratchCount];
2094 int glyphIndex = 0;
2095 const char* currentUtf8 = reinterpret_cast<const char*>(chars);
2096 SkUnichar currentChar;
2097 if (glyphCount) {
2098 currentChar = SkUTF8_NextUnichar(&currentUtf8);
2099 }
2100 while (glyphIndex < glyphCount) {
2101 // Try a run of bmp.
2102 int glyphsLeft = SkTMin(glyphCount - glyphIndex, scratchCount);
2103 int runLength = 0;
2104 while (runLength < glyphsLeft && currentChar <= 0xFFFF) {
2105 scratch[runLength] = static_cast<WCHAR>(currentChar);
2106 ++runLength;
2107 if (runLength < glyphsLeft) {
2108 currentChar = SkUTF8_NextUnichar(&currentUtf8);
2109 }
2110 }
2111 if (runLength) {
2112 bmpCharsToGlyphs(hdc, scratch, runLength, &glyphs[glyphIndex], Ox1FHack);
2113 glyphIndex += runLength;
2114 }
2115
2116 // Try a run of non-bmp.
2117 while (glyphIndex < glyphCount && currentChar > 0xFFFF) {
2118 SkUTF16_FromUnichar(currentChar, reinterpret_cast<uint16_t*>(scratch));
2119 glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, scratch);
2120 ++glyphIndex;
2121 if (glyphIndex < glyphCount) {
2122 currentChar = SkUTF8_NextUnichar(&currentUtf8);
2123 }
2124 }
2125 }
2126 break;
2127 }
2128 case SkTypeface::kUTF16_Encoding: {
2129 int glyphIndex = 0;
2130 const WCHAR* currentUtf16 = reinterpret_cast<const WCHAR*>(chars);
2131 while (glyphIndex < glyphCount) {
2132 // Try a run of bmp.
2133 int glyphsLeft = glyphCount - glyphIndex;
2134 int runLength = 0;
2135 while (runLength < glyphsLeft && !SkUTF16_IsHighSurrogate(currentUtf16[runLength])) {
2136 ++runLength;
2137 }
2138 if (runLength) {
2139 bmpCharsToGlyphs(hdc, currentUtf16, runLength, &glyphs[glyphIndex], Ox1FHack);
2140 glyphIndex += runLength;
2141 currentUtf16 += runLength;
2142 }
2143
2144 // Try a run of non-bmp.
2145 while (glyphIndex < glyphCount && SkUTF16_IsHighSurrogate(*currentUtf16)) {
2146 glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, currentUtf16);
2147 ++glyphIndex;
2148 currentUtf16 += 2;
2149 }
2150 }
2151 break;
2152 }
2153 case SkTypeface::kUTF32_Encoding: {
2154 static const int scratchCount = 256;
2155 WCHAR scratch[scratchCount];
2156 int glyphIndex = 0;
2157 const uint32_t* utf32 = reinterpret_cast<const uint32_t*>(chars);
2158 while (glyphIndex < glyphCount) {
2159 // Try a run of bmp.
2160 int glyphsLeft = SkTMin(glyphCount - glyphIndex, scratchCount);
2161 int runLength = 0;
2162 while (runLength < glyphsLeft && utf32[glyphIndex + runLength] <= 0xFFFF) {
2163 scratch[runLength] = static_cast<WCHAR>(utf32[glyphIndex + runLength]);
2164 ++runLength;
2165 }
2166 if (runLength) {
2167 bmpCharsToGlyphs(hdc, scratch, runLength, &glyphs[glyphIndex], Ox1FHack);
2168 glyphIndex += runLength;
2169 }
2170
2171 // Try a run of non-bmp.
2172 while (glyphIndex < glyphCount && utf32[glyphIndex] > 0xFFFF) {
2173 SkUTF16_FromUnichar(utf32[glyphIndex], reinterpret_cast<uint16_t*>(scratch));
2174 glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, scratch);
2175 ++glyphIndex;
2176 }
2177 }
2178 break;
2179 }
2180 default:
2181 SK_CRASH();
2182 }
2183
2184 if (sc) {
2185 ::ScriptFreeCache(&sc);
2186 }
2187
2188 for (int i = 0; i < glyphCount; ++i) {
2189 if (0 == glyphs[i]) {
2190 return i;
2191 }
2192 }
2193 return glyphCount;
2194}
2195
bungeman@google.com7bdd6142013-07-15 19:42:57 +00002196int LogFontTypeface::onCountGlyphs() const {
2197 HDC hdc = ::CreateCompatibleDC(NULL);
2198 HFONT font = CreateFontIndirect(&fLogFont);
2199 HFONT savefont = (HFONT)SelectObject(hdc, font);
2200
2201 unsigned int glyphCount = calculateGlyphCount(hdc, fLogFont);
2202
2203 SelectObject(hdc, savefont);
2204 DeleteObject(font);
2205 DeleteDC(hdc);
2206
2207 return glyphCount;
2208}
2209
2210int LogFontTypeface::onGetUPEM() const {
2211 HDC hdc = ::CreateCompatibleDC(NULL);
2212 HFONT font = CreateFontIndirect(&fLogFont);
2213 HFONT savefont = (HFONT)SelectObject(hdc, font);
2214
2215 unsigned int upem = calculateUPEM(hdc, fLogFont);
2216
2217 SelectObject(hdc, savefont);
2218 DeleteObject(font);
2219 DeleteDC(hdc);
2220
2221 return upem;
2222}
2223
bungeman@google.com839702b2013-08-07 17:09:22 +00002224SkTypeface::LocalizedStrings* LogFontTypeface::onCreateFamilyNameIterator() const {
bungeman@google.coma9802692013-08-07 02:45:25 +00002225 SkTypeface::LocalizedStrings* nameIter =
2226 SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*this);
2227 if (NULL == nameIter) {
2228 SkString familyName;
2229 this->getFamilyName(&familyName);
2230 SkString language("und"); //undetermined
2231 nameIter = new SkOTUtils::LocalizedStrings_SingleName(familyName, language);
2232 }
2233 return nameIter;
2234}
2235
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002236int LogFontTypeface::onGetTableTags(SkFontTableTag tags[]) const {
2237 SkSFNTHeader header;
2238 if (sizeof(header) != this->onGetTableData(0, 0, sizeof(header), &header)) {
2239 return 0;
2240 }
2241
2242 int numTables = SkEndian_SwapBE16(header.numTables);
2243
2244 if (tags) {
2245 size_t size = numTables * sizeof(SkSFNTHeader::TableDirectoryEntry);
2246 SkAutoSTMalloc<0x20, SkSFNTHeader::TableDirectoryEntry> dir(numTables);
2247 if (size != this->onGetTableData(0, sizeof(header), size, dir.get())) {
2248 return 0;
2249 }
2250
2251 for (int i = 0; i < numTables; ++i) {
2252 tags[i] = SkEndian_SwapBE32(dir[i].tag);
2253 }
2254 }
2255 return numTables;
2256}
2257
2258size_t LogFontTypeface::onGetTableData(SkFontTableTag tag, size_t offset,
2259 size_t length, void* data) const
2260{
2261 LOGFONT lf = fLogFont;
2262
2263 HDC hdc = ::CreateCompatibleDC(NULL);
2264 HFONT font = CreateFontIndirect(&lf);
2265 HFONT savefont = (HFONT)SelectObject(hdc, font);
2266
2267 tag = SkEndian_SwapBE32(tag);
2268 if (NULL == data) {
2269 length = 0;
2270 }
robertphillips@google.com8b169312013-10-15 17:47:36 +00002271 DWORD bufferSize = GetFontData(hdc, tag, (DWORD) offset, data, (DWORD) length);
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002272 if (bufferSize == GDI_ERROR) {
2273 call_ensure_accessible(lf);
robertphillips@google.com8b169312013-10-15 17:47:36 +00002274 bufferSize = GetFontData(hdc, tag, (DWORD) offset, data, (DWORD) length);
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002275 }
2276
2277 SelectObject(hdc, savefont);
2278 DeleteObject(font);
2279 DeleteDC(hdc);
2280
2281 return bufferSize == GDI_ERROR ? 0 : bufferSize;
2282}
2283
reed@google.com0da48612013-03-19 16:06:52 +00002284SkScalerContext* LogFontTypeface::onCreateScalerContext(const SkDescriptor* desc) const {
reed@google.com30ddd612013-07-30 17:47:39 +00002285 SkScalerContext_GDI* ctx = SkNEW_ARGS(SkScalerContext_GDI,
reed@google.com84e22d82013-07-10 15:38:20 +00002286 (const_cast<LogFontTypeface*>(this), desc));
2287 if (!ctx->isValid()) {
2288 SkDELETE(ctx);
2289 ctx = NULL;
2290 }
2291 return ctx;
reed@google.comac6b9792011-03-11 15:42:51 +00002292}
2293
reed@google.com0da48612013-03-19 16:06:52 +00002294void LogFontTypeface::onFilterRec(SkScalerContextRec* rec) const {
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00002295 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
2296 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
2297 {
2298 rec->fMaskFormat = SkMask::kA8_Format;
2299 rec->fFlags |= SkScalerContext::kGenA8FromLCD_Flag;
2300 }
2301
bungeman41078062014-07-07 08:16:37 -07002302 unsigned flagsWeDontSupport = SkScalerContext::kVertical_Flag |
2303 SkScalerContext::kDevKernText_Flag |
bungeman@google.comf6f56872014-01-23 19:01:36 +00002304 SkScalerContext::kForceAutohinting_Flag |
reed@google.come8fab012011-07-13 15:25:33 +00002305 SkScalerContext::kEmbeddedBitmapText_Flag |
2306 SkScalerContext::kEmbolden_Flag |
2307 SkScalerContext::kLCD_BGROrder_Flag |
2308 SkScalerContext::kLCD_Vertical_Flag;
2309 rec->fFlags &= ~flagsWeDontSupport;
2310
reed@google.come8fab012011-07-13 15:25:33 +00002311 SkPaint::Hinting h = rec->getHinting();
2312 switch (h) {
2313 case SkPaint::kNo_Hinting:
bungeman@google.com0abbff92013-07-27 20:37:56 +00002314 break;
reed@google.come8fab012011-07-13 15:25:33 +00002315 case SkPaint::kSlight_Hinting:
bungeman@google.com0abbff92013-07-27 20:37:56 +00002316 // Only do slight hinting when axis aligned.
bungeman@google.com7c86d8e2013-07-27 21:42:21 +00002317 // TODO: re-enable slight hinting when FontHostTest can pass.
2318 //if (!isAxisAligned(*rec)) {
bungeman@google.com0abbff92013-07-27 20:37:56 +00002319 h = SkPaint::kNo_Hinting;
bungeman@google.com7c86d8e2013-07-27 21:42:21 +00002320 //}
reed@google.come8fab012011-07-13 15:25:33 +00002321 break;
2322 case SkPaint::kNormal_Hinting:
2323 case SkPaint::kFull_Hinting:
bungeman@google.com0abbff92013-07-27 20:37:56 +00002324 // TODO: need to be able to distinguish subpixel positioned glyphs
2325 // and linear metrics.
2326 //rec->fFlags &= ~SkScalerContext::kSubpixelPositioning_Flag;
reed@google.come8fab012011-07-13 15:25:33 +00002327 h = SkPaint::kNormal_Hinting;
2328 break;
2329 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002330 SkDEBUGFAIL("unknown hinting");
reed@google.come8fab012011-07-13 15:25:33 +00002331 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00002332 //TODO: if this is a bitmap font, squash hinting and subpixel.
reed@google.come8fab012011-07-13 15:25:33 +00002333 rec->setHinting(h);
reed@google.comda440672011-07-13 18:02:28 +00002334
reed@google.com9181aa82011-08-05 14:28:31 +00002335// turn this off since GDI might turn A8 into BW! Need a bigger fix.
2336#if 0
reed@google.comda440672011-07-13 18:02:28 +00002337 // Disable LCD when rotated, since GDI's output is ugly
2338 if (isLCD(*rec) && !isAxisAligned(*rec)) {
2339 rec->fMaskFormat = SkMask::kA8_Format;
2340 }
reed@google.com9181aa82011-08-05 14:28:31 +00002341#endif
reed@google.com754e4eb2011-09-26 13:21:39 +00002342
reed@google.com0da48612013-03-19 16:06:52 +00002343 if (!fCanBeLCD && isLCD(*rec)) {
bungeman@google.comcb1bbb32012-10-12 18:48:35 +00002344 rec->fMaskFormat = SkMask::kA8_Format;
2345 rec->fFlags &= ~SkScalerContext::kGenA8FromLCD_Flag;
reed@google.com754e4eb2011-09-26 13:21:39 +00002346 }
reed@google.com754e4eb2011-09-26 13:21:39 +00002347}
reed@google.com070da5e2013-03-27 20:01:49 +00002348
2349///////////////////////////////////////////////////////////////////////////////
2350
2351#include "SkFontMgr.h"
reed@google.com484f5bc2013-04-24 19:14:56 +00002352#include "SkDataTable.h"
2353
bungeman@google.coma9802692013-08-07 02:45:25 +00002354static bool valid_logfont_for_enum(const LOGFONT& lf) {
2355 // TODO: Vector FON is unsupported and should not be listed.
2356 return
2357 // Ignore implicit vertical variants.
2358 lf.lfFaceName[0] && lf.lfFaceName[0] != '@'
2359
2360 // DEFAULT_CHARSET is used to get all fonts, but also implies all
2361 // character sets. Filter assuming all fonts support ANSI_CHARSET.
2362 && ANSI_CHARSET == lf.lfCharSet
2363 ;
reed@google.com484f5bc2013-04-24 19:14:56 +00002364}
2365
bungeman@google.coma9802692013-08-07 02:45:25 +00002366/** An EnumFontFamExProc implementation which interprets builderParam as
2367 * an SkTDArray<ENUMLOGFONTEX>* and appends logfonts which
2368 * pass the valid_logfont_for_enum predicate.
2369 */
2370static int CALLBACK enum_family_proc(const LOGFONT* lf, const TEXTMETRIC*,
2371 DWORD fontType, LPARAM builderParam) {
2372 if (valid_logfont_for_enum(*lf)) {
reed@google.coma65a6812013-05-02 19:47:24 +00002373 SkTDArray<ENUMLOGFONTEX>* array = (SkTDArray<ENUMLOGFONTEX>*)builderParam;
2374 *array->append() = *(ENUMLOGFONTEX*)lf;
reed@google.com484f5bc2013-04-24 19:14:56 +00002375 }
2376 return 1; // non-zero means continue
2377}
2378
reed@google.com484f5bc2013-04-24 19:14:56 +00002379class SkFontStyleSetGDI : public SkFontStyleSet {
2380public:
reed@google.coma65a6812013-05-02 19:47:24 +00002381 SkFontStyleSetGDI(const TCHAR familyName[]) {
bungeman@google.coma9802692013-08-07 02:45:25 +00002382 LOGFONT lf;
2383 sk_bzero(&lf, sizeof(lf));
2384 lf.lfCharSet = DEFAULT_CHARSET;
2385 _tcscpy_s(lf.lfFaceName, familyName);
2386
reed@google.com484f5bc2013-04-24 19:14:56 +00002387 HDC hdc = ::CreateCompatibleDC(NULL);
bungeman@google.coma9802692013-08-07 02:45:25 +00002388 ::EnumFontFamiliesEx(hdc, &lf, enum_family_proc, (LPARAM)&fArray, 0);
reed@google.com484f5bc2013-04-24 19:14:56 +00002389 ::DeleteDC(hdc);
2390 }
2391
2392 virtual int count() SK_OVERRIDE {
2393 return fArray.count();
2394 }
2395
2396 virtual void getStyle(int index, SkFontStyle* fs, SkString* styleName) SK_OVERRIDE {
2397 if (fs) {
bungemana4c4a2d2014-10-20 13:33:19 -07002398 *fs = get_style(fArray[index].elfLogFont);
reed@google.com484f5bc2013-04-24 19:14:56 +00002399 }
reed@google.coma65a6812013-05-02 19:47:24 +00002400 if (styleName) {
2401 const ENUMLOGFONTEX& ref = fArray[index];
2402 // For some reason, ENUMLOGFONTEX and LOGFONT disagree on their type in the
2403 // non-unicode version.
2404 // ENUMLOGFONTEX uses BYTE
2405 // LOGFONT uses CHAR
2406 // Here we assert they that the style name is logically the same (size) as
2407 // a TCHAR, so we can use the same converter function.
2408 SkASSERT(sizeof(TCHAR) == sizeof(ref.elfStyle[0]));
2409 tchar_to_skstring((const TCHAR*)ref.elfStyle, styleName);
2410 }
reed@google.com484f5bc2013-04-24 19:14:56 +00002411 }
2412
2413 virtual SkTypeface* createTypeface(int index) SK_OVERRIDE {
reed@google.coma65a6812013-05-02 19:47:24 +00002414 return SkCreateTypefaceFromLOGFONT(fArray[index].elfLogFont);
reed@google.com484f5bc2013-04-24 19:14:56 +00002415 }
2416
2417 virtual SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE {
2418 // todo:
reed@google.coma65a6812013-05-02 19:47:24 +00002419 return SkCreateTypefaceFromLOGFONT(fArray[0].elfLogFont);
reed@google.com484f5bc2013-04-24 19:14:56 +00002420 }
2421
2422private:
reed@google.coma65a6812013-05-02 19:47:24 +00002423 SkTDArray<ENUMLOGFONTEX> fArray;
reed@google.com484f5bc2013-04-24 19:14:56 +00002424};
2425
reed@google.com484f5bc2013-04-24 19:14:56 +00002426class SkFontMgrGDI : public SkFontMgr {
bungeman@google.coma9802692013-08-07 02:45:25 +00002427public:
2428 SkFontMgrGDI() {
reed@google.com484f5bc2013-04-24 19:14:56 +00002429 LOGFONT lf;
2430 sk_bzero(&lf, sizeof(lf));
2431 lf.lfCharSet = DEFAULT_CHARSET;
2432
2433 HDC hdc = ::CreateCompatibleDC(NULL);
2434 ::EnumFontFamiliesEx(hdc, &lf, enum_family_proc, (LPARAM)&fLogFontArray, 0);
2435 ::DeleteDC(hdc);
2436 }
2437
reed@google.com484f5bc2013-04-24 19:14:56 +00002438protected:
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002439 virtual int onCountFamilies() const SK_OVERRIDE {
reed@google.com484f5bc2013-04-24 19:14:56 +00002440 return fLogFontArray.count();
2441 }
2442
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002443 virtual void onGetFamilyName(int index, SkString* familyName) const SK_OVERRIDE {
reed@google.com484f5bc2013-04-24 19:14:56 +00002444 SkASSERT((unsigned)index < (unsigned)fLogFontArray.count());
reed@google.coma65a6812013-05-02 19:47:24 +00002445 tchar_to_skstring(fLogFontArray[index].elfLogFont.lfFaceName, familyName);
reed@google.com484f5bc2013-04-24 19:14:56 +00002446 }
2447
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002448 virtual SkFontStyleSet* onCreateStyleSet(int index) const SK_OVERRIDE {
reed@google.com484f5bc2013-04-24 19:14:56 +00002449 SkASSERT((unsigned)index < (unsigned)fLogFontArray.count());
reed@google.coma65a6812013-05-02 19:47:24 +00002450 return SkNEW_ARGS(SkFontStyleSetGDI, (fLogFontArray[index].elfLogFont.lfFaceName));
reed@google.com484f5bc2013-04-24 19:14:56 +00002451 }
2452
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002453 virtual SkFontStyleSet* onMatchFamily(const char familyName[]) const SK_OVERRIDE {
reed@google.com484f5bc2013-04-24 19:14:56 +00002454 if (NULL == familyName) {
2455 familyName = ""; // do we need this check???
2456 }
2457 LOGFONT lf;
2458 logfont_for_name(familyName, &lf);
reed@google.coma65a6812013-05-02 19:47:24 +00002459 return SkNEW_ARGS(SkFontStyleSetGDI, (lf.lfFaceName));
reed@google.com484f5bc2013-04-24 19:14:56 +00002460 }
2461
reed@google.com484f5bc2013-04-24 19:14:56 +00002462 virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002463 const SkFontStyle& fontstyle) const SK_OVERRIDE {
reed@google.com437eea12013-04-25 20:40:02 +00002464 // could be in base impl
reed@google.com484f5bc2013-04-24 19:14:56 +00002465 SkAutoTUnref<SkFontStyleSet> sset(this->matchFamily(familyName));
2466 return sset->matchStyle(fontstyle);
2467 }
2468
djsollen33068c12014-11-14 10:52:53 -08002469 virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
2470 const char* bcp47[], int bcp47Count,
2471 SkUnichar character) const SK_OVERRIDE {
2472 return NULL;
2473 }
2474
reed@google.com484f5bc2013-04-24 19:14:56 +00002475 virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002476 const SkFontStyle& fontstyle) const SK_OVERRIDE {
reed@google.com437eea12013-04-25 20:40:02 +00002477 // could be in base impl
reed@google.com484f5bc2013-04-24 19:14:56 +00002478 SkString familyName;
2479 ((LogFontTypeface*)familyMember)->getFamilyName(&familyName);
2480 return this->matchFamilyStyle(familyName.c_str(), fontstyle);
2481 }
2482
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002483 virtual SkTypeface* onCreateFromStream(SkStream* stream, int ttcIndex) const SK_OVERRIDE {
reed@google.com437eea12013-04-25 20:40:02 +00002484 return create_from_stream(stream);
reed@google.com484f5bc2013-04-24 19:14:56 +00002485 }
reed@google.com437eea12013-04-25 20:40:02 +00002486
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002487 virtual SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const SK_OVERRIDE {
reed@google.com437eea12013-04-25 20:40:02 +00002488 // could be in base impl
2489 SkAutoTUnref<SkStream> stream(SkNEW_ARGS(SkMemoryStream, (data)));
2490 return this->createFromStream(stream);
reed@google.com484f5bc2013-04-24 19:14:56 +00002491 }
reed@google.com437eea12013-04-25 20:40:02 +00002492
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002493 virtual SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const SK_OVERRIDE {
reed@google.com437eea12013-04-25 20:40:02 +00002494 // could be in base impl
2495 SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(path));
2496 return this->createFromStream(stream);
reed@google.com484f5bc2013-04-24 19:14:56 +00002497 }
2498
reed@google.com30ddd612013-07-30 17:47:39 +00002499 virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002500 unsigned styleBits) const SK_OVERRIDE {
bungeman@google.comf7159bb2013-11-20 15:11:05 +00002501 LOGFONT lf;
2502 if (NULL == familyName) {
2503 lf = get_default_font();
2504 } else {
2505 logfont_for_name(familyName, &lf);
2506 }
bungemana4c4a2d2014-10-20 13:33:19 -07002507
2508 SkTypeface::Style style = (SkTypeface::Style)styleBits;
2509 lf.lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL;
2510 lf.lfItalic = ((style & SkTypeface::kItalic) != 0);
bungeman@google.comf7159bb2013-11-20 15:11:05 +00002511 return SkCreateTypefaceFromLOGFONT(lf);
reed@google.com30ddd612013-07-30 17:47:39 +00002512 }
2513
reed@google.com484f5bc2013-04-24 19:14:56 +00002514private:
reed@google.coma65a6812013-05-02 19:47:24 +00002515 SkTDArray<ENUMLOGFONTEX> fLogFontArray;
reed@google.com484f5bc2013-04-24 19:14:56 +00002516};
reed@google.com070da5e2013-03-27 20:01:49 +00002517
reed@google.com30ddd612013-07-30 17:47:39 +00002518///////////////////////////////////////////////////////////////////////////////
2519
bungeman@google.combfc6cc42013-08-21 15:20:43 +00002520SkFontMgr* SkFontMgr_New_GDI() {
reed@google.com484f5bc2013-04-24 19:14:56 +00002521 return SkNEW(SkFontMgrGDI);
reed@google.com070da5e2013-03-27 20:01:49 +00002522}