blob: 67927fd81cc6d8449630ec4047fd498ac941168d [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.combbe50132012-07-24 20:33:21 +000015#include "SkGlyph.h"
bungeman@google.com27f74aa2013-10-08 21:32:15 +000016#include "SkHRESULT.h"
bungeman@google.com97efada2012-07-30 20:40:50 +000017#include "SkMaskGamma.h"
bungeman@google.comd3fbd342014-04-15 15:52:07 +000018#include "SkMatrix22.h"
bungeman@google.com7bdd6142013-07-15 19:42:57 +000019#include "SkOTTable_maxp.h"
bungeman@google.coma9802692013-08-07 02:45:25 +000020#include "SkOTTable_name.h"
bungeman@google.coma5501992012-05-18 19:06:41 +000021#include "SkOTUtils.h"
reed@google.com27889872012-08-07 16:15:13 +000022#include "SkPath.h"
bungeman@google.comb10b51f2013-08-01 20:18:41 +000023#include "SkSFNTHeader.h"
reed@google.comac6b9792011-03-11 15:42:51 +000024#include "SkStream.h"
bungeman@google.coma5501992012-05-18 19:06:41 +000025#include "SkString.h"
bungeman@google.com05a729f2013-06-20 15:29:16 +000026#include "SkTemplates.h"
reed@google.comac6b9792011-03-11 15:42:51 +000027#include "SkThread.h"
28#include "SkTypeface_win.h"
reed@google.com59d2f632011-05-02 19:36:59 +000029#include "SkTypefaceCache.h"
reed@google.comac6b9792011-03-11 15:42:51 +000030#include "SkUtils.h"
31
bungeman@google.coma5501992012-05-18 19:06:41 +000032#include "SkTypes.h"
33#include <tchar.h>
34#include <usp10.h>
35#include <objbase.h>
reed@google.comac6b9792011-03-11 15:42:51 +000036
reed@google.comf210f502013-03-20 14:15:52 +000037static void (*gEnsureLOGFONTAccessibleProc)(const LOGFONT&);
38
39void SkTypeface_SetEnsureLOGFONTAccessibleProc(void (*proc)(const LOGFONT&)) {
40 gEnsureLOGFONTAccessibleProc = proc;
41}
42
reed@google.com055180c2013-03-21 18:46:35 +000043static void call_ensure_accessible(const LOGFONT& lf) {
44 if (gEnsureLOGFONTAccessibleProc) {
45 gEnsureLOGFONTAccessibleProc(lf);
46 }
47}
48
49///////////////////////////////////////////////////////////////////////////////
50
reed@google.com6f5df482011-09-28 20:33:24 +000051// always packed xxRRGGBB
52typedef uint32_t SkGdiRGB;
53
reed@google.coma767fa02011-08-05 21:40:26 +000054// define this in your Makefile or .gyp to enforce AA requests
55// which GDI ignores at small sizes. This flag guarantees AA
56// for rotated text, regardless of GDI's notions.
57//#define SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
58
reed@google.com82a34d82011-07-26 19:33:08 +000059static bool isLCD(const SkScalerContext::Rec& rec) {
reedd54d3fc2014-11-13 14:39:58 -080060 return SkMask::kLCD16_Format == rec.fMaskFormat;
reed@google.com82a34d82011-07-26 19:33:08 +000061}
62
reed@google.coma767fa02011-08-05 21:40:26 +000063static bool bothZero(SkScalar a, SkScalar b) {
64 return 0 == a && 0 == b;
65}
66
67// returns false if there is any non-90-rotation or skew
68static bool isAxisAligned(const SkScalerContext::Rec& rec) {
69 return 0 == rec.fPreSkewX &&
70 (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
71 bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
72}
73
74static bool needToRenderWithSkia(const SkScalerContext::Rec& rec) {
75#ifdef SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
76 // What we really want to catch is when GDI will ignore the AA request and give
77 // us BW instead. Smallish rotated text is one heuristic, so this code is just
78 // an approximation. We shouldn't need to do this for larger sizes, but at those
79 // sizes, the quality difference gets less and less between our general
80 // scanconverter and GDI's.
81 if (SkMask::kA8_Format == rec.fMaskFormat && !isAxisAligned(rec)) {
82 return true;
83 }
84#endif
bungeman@google.com0abbff92013-07-27 20:37:56 +000085 return rec.getHinting() == SkPaint::kNo_Hinting || rec.getHinting() == SkPaint::kSlight_Hinting;
reed@google.coma767fa02011-08-05 21:40:26 +000086}
87
reed@google.comac6b9792011-03-11 15:42:51 +000088using namespace skia_advanced_typeface_metrics_utils;
89
reed@google.coma65a6812013-05-02 19:47:24 +000090static void tchar_to_skstring(const TCHAR t[], SkString* s) {
reed@google.com484f5bc2013-04-24 19:14:56 +000091#ifdef UNICODE
92 size_t sSize = WideCharToMultiByte(CP_UTF8, 0, t, -1, NULL, 0, NULL, NULL);
93 s->resize(sSize);
94 WideCharToMultiByte(CP_UTF8, 0, t, -1, s->writable_str(), sSize, NULL, NULL);
95#else
96 s->set(t);
97#endif
98}
99
bungeman@google.coma9802692013-08-07 02:45:25 +0000100static void dcfontname_to_skstring(HDC deviceContext, const LOGFONT& lf, SkString* familyName) {
101 int fontNameLen; //length of fontName in TCHARS.
102 if (0 == (fontNameLen = GetTextFace(deviceContext, 0, NULL))) {
103 call_ensure_accessible(lf);
104 if (0 == (fontNameLen = GetTextFace(deviceContext, 0, NULL))) {
105 fontNameLen = 0;
106 }
107 }
108
109 SkAutoSTArray<LF_FULLFACESIZE, TCHAR> fontName(fontNameLen+1);
110 if (0 == GetTextFace(deviceContext, fontNameLen, fontName.get())) {
111 call_ensure_accessible(lf);
112 if (0 == GetTextFace(deviceContext, fontNameLen, fontName.get())) {
113 fontName[0] = 0;
114 }
115 }
116
117 tchar_to_skstring(fontName.get(), familyName);
118}
119
reed@google.comac6b9792011-03-11 15:42:51 +0000120static void make_canonical(LOGFONT* lf) {
bungeman@google.com53cbb0b2013-09-08 19:36:58 +0000121 lf->lfHeight = -64;
reed@google.com59d2f632011-05-02 19:36:59 +0000122 lf->lfQuality = CLEARTYPE_QUALITY;//PROOF_QUALITY;
123 lf->lfCharSet = DEFAULT_CHARSET;
reed@google.com82a34d82011-07-26 19:33:08 +0000124// lf->lfClipPrecision = 64;
reed@google.com59d2f632011-05-02 19:36:59 +0000125}
126
bungemana4c4a2d2014-10-20 13:33:19 -0700127static SkFontStyle get_style(const LOGFONT& lf) {
128 return SkFontStyle(lf.lfWeight,
129 lf.lfWidth,
130 lf.lfItalic ? SkFontStyle::kItalic_Slant : SkFontStyle::kUpright_Slant);
reed@google.comac6b9792011-03-11 15:42:51 +0000131}
132
133static inline FIXED SkFixedToFIXED(SkFixed x) {
134 return *(FIXED*)(&x);
135}
bungeman@google.coma0319f62012-04-18 15:40:50 +0000136static inline SkFixed SkFIXEDToFixed(FIXED x) {
137 return *(SkFixed*)(&x);
138}
reed@google.comac6b9792011-03-11 15:42:51 +0000139
140static inline FIXED SkScalarToFIXED(SkScalar x) {
141 return SkFixedToFIXED(SkScalarToFixed(x));
142}
143
bungeman@google.com4732df62014-01-23 15:22:42 +0000144static inline SkScalar SkFIXEDToScalar(FIXED x) {
145 return SkFixedToScalar(SkFIXEDToFixed(x));
146}
147
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000148static unsigned calculateGlyphCount(HDC hdc, const LOGFONT& lf) {
149 TEXTMETRIC textMetric;
150 if (0 == GetTextMetrics(hdc, &textMetric)) {
151 textMetric.tmPitchAndFamily = TMPF_VECTOR;
152 call_ensure_accessible(lf);
153 GetTextMetrics(hdc, &textMetric);
154 }
155
156 if (!(textMetric.tmPitchAndFamily & TMPF_VECTOR)) {
157 return textMetric.tmLastChar;
158 }
159
reed@google.comac6b9792011-03-11 15:42:51 +0000160 // The 'maxp' table stores the number of glyphs at offset 4, in 2 bytes.
reed@google.comac6b9792011-03-11 15:42:51 +0000161 uint16_t glyphs;
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000162 if (GDI_ERROR != GetFontData(hdc, SkOTTableMaximumProfile::TAG, 4, &glyphs, sizeof(glyphs))) {
reed@google.comac6b9792011-03-11 15:42:51 +0000163 return SkEndian_SwapBE16(glyphs);
164 }
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000165
reed@google.comac6b9792011-03-11 15:42:51 +0000166 // Binary search for glyph count.
167 static const MAT2 mat2 = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
168 int32_t max = SK_MaxU16 + 1;
169 int32_t min = 0;
170 GLYPHMETRICS gm;
171 while (min < max) {
172 int32_t mid = min + ((max - min) / 2);
173 if (GetGlyphOutlineW(hdc, mid, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0,
174 NULL, &mat2) == GDI_ERROR) {
175 max = mid;
176 } else {
177 min = mid + 1;
178 }
179 }
180 SkASSERT(min == max);
181 return min;
182}
183
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000184static unsigned calculateUPEM(HDC hdc, const LOGFONT& lf) {
185 TEXTMETRIC textMetric;
186 if (0 == GetTextMetrics(hdc, &textMetric)) {
187 textMetric.tmPitchAndFamily = TMPF_VECTOR;
188 call_ensure_accessible(lf);
189 GetTextMetrics(hdc, &textMetric);
190 }
191
192 if (!(textMetric.tmPitchAndFamily & TMPF_VECTOR)) {
193 return textMetric.tmMaxCharWidth;
194 }
195
196 OUTLINETEXTMETRIC otm;
197 unsigned int otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
198 if (0 == otmRet) {
199 call_ensure_accessible(lf);
200 otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
201 }
202
203 return (0 == otmRet) ? 0 : otm.otmEMSquare;
204}
205
reed@google.comac6b9792011-03-11 15:42:51 +0000206class LogFontTypeface : public SkTypeface {
reed@google.comac6b9792011-03-11 15:42:51 +0000207public:
bungemana4c4a2d2014-10-20 13:33:19 -0700208 LogFontTypeface(const SkFontStyle& style, const LOGFONT& lf, bool serializeAsStream)
209 : SkTypeface(style, SkTypefaceCache::NewFontID(), false)
210 , fLogFont(lf)
211 , fSerializeAsStream(serializeAsStream)
212 {
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000213
214 // If the font has cubic outlines, it will not be rendered with ClearType.
215 HFONT font = CreateFontIndirect(&lf);
216
217 HDC deviceContext = ::CreateCompatibleDC(NULL);
218 HFONT savefont = (HFONT)SelectObject(deviceContext, font);
219
220 TEXTMETRIC textMetric;
221 if (0 == GetTextMetrics(deviceContext, &textMetric)) {
reed@google.com055180c2013-03-21 18:46:35 +0000222 call_ensure_accessible(lf);
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000223 if (0 == GetTextMetrics(deviceContext, &textMetric)) {
224 textMetric.tmPitchAndFamily = TMPF_TRUETYPE;
225 }
226 }
227 if (deviceContext) {
228 ::SelectObject(deviceContext, savefont);
229 ::DeleteDC(deviceContext);
230 }
231 if (font) {
232 ::DeleteObject(font);
233 }
234
bungeman@google.comfe747652013-03-25 19:36:11 +0000235 // The fixed pitch bit is set if the font is *not* fixed pitch.
236 this->setIsFixedPitch((textMetric.tmPitchAndFamily & TMPF_FIXED_PITCH) == 0);
237
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000238 // Used a logfont on a memory context, should never get a device font.
239 // Therefore all TMPF_DEVICE will be PostScript (cubic) fonts.
240 fCanBeLCD = !((textMetric.tmPitchAndFamily & TMPF_VECTOR) &&
241 (textMetric.tmPitchAndFamily & TMPF_DEVICE));
242 }
reed@google.comac6b9792011-03-11 15:42:51 +0000243
reed@google.com59d2f632011-05-02 19:36:59 +0000244 LOGFONT fLogFont;
bungeman@google.come70f7982012-06-01 19:38:19 +0000245 bool fSerializeAsStream;
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000246 bool fCanBeLCD;
reed@google.comac6b9792011-03-11 15:42:51 +0000247
reed@google.com59d2f632011-05-02 19:36:59 +0000248 static LogFontTypeface* Create(const LOGFONT& lf) {
bungemana4c4a2d2014-10-20 13:33:19 -0700249 return new LogFontTypeface(get_style(lf), lf, false);
reed@google.comac6b9792011-03-11 15:42:51 +0000250 }
reed@google.com0da48612013-03-19 16:06:52 +0000251
reed@google.com055180c2013-03-21 18:46:35 +0000252 static void EnsureAccessible(const SkTypeface* face) {
253 call_ensure_accessible(static_cast<const LogFontTypeface*>(face)->fLogFont);
254 }
255
reed@google.com0da48612013-03-19 16:06:52 +0000256protected:
bungeman5f213d92015-01-27 05:39:10 -0800257 SkStreamAsset* onOpenStream(int* ttcIndex) const SK_OVERRIDE;
mtklein72c9faa2015-01-09 10:06:39 -0800258 SkScalerContext* onCreateScalerContext(const SkDescriptor*) const SK_OVERRIDE;
259 void onFilterRec(SkScalerContextRec*) const SK_OVERRIDE;
reed@google.com2689f612013-03-20 20:01:47 +0000260 virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
261 SkAdvancedTypefaceMetrics::PerGlyphInfo,
262 const uint32_t*, uint32_t) const SK_OVERRIDE;
mtklein72c9faa2015-01-09 10:06:39 -0800263 void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE;
bungeman@google.com3c996f82013-10-24 21:39:35 +0000264 virtual int onCharsToGlyphs(const void* chars, Encoding encoding,
265 uint16_t glyphs[], int glyphCount) const SK_OVERRIDE;
mtklein72c9faa2015-01-09 10:06:39 -0800266 int onCountGlyphs() const SK_OVERRIDE;
267 int onGetUPEM() const SK_OVERRIDE;
268 void onGetFamilyName(SkString* familyName) const SK_OVERRIDE;
269 SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const SK_OVERRIDE;
270 int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
bungeman@google.comb10b51f2013-08-01 20:18:41 +0000271 virtual size_t onGetTableData(SkFontTableTag, size_t offset,
272 size_t length, void* data) const SK_OVERRIDE;
reed@google.comac6b9792011-03-11 15:42:51 +0000273};
274
bungeman@google.coma5501992012-05-18 19:06:41 +0000275class FontMemResourceTypeface : public LogFontTypeface {
276public:
277 /**
bungeman@google.coma5501992012-05-18 19:06:41 +0000278 * The created FontMemResourceTypeface takes ownership of fontMemResource.
279 */
280 static FontMemResourceTypeface* Create(const LOGFONT& lf, HANDLE fontMemResource) {
bungemana4c4a2d2014-10-20 13:33:19 -0700281 return new FontMemResourceTypeface(get_style(lf), lf, fontMemResource);
bungeman@google.coma5501992012-05-18 19:06:41 +0000282 }
283
284protected:
mtklein72c9faa2015-01-09 10:06:39 -0800285 void weak_dispose() const SK_OVERRIDE {
bungeman@google.coma5501992012-05-18 19:06:41 +0000286 RemoveFontMemResourceEx(fFontMemResource);
287 //SkTypefaceCache::Remove(this);
288 INHERITED::weak_dispose();
289 }
290
291private:
bungeman@google.com72cf4fc2014-03-21 22:48:32 +0000292 /**
293 * Takes ownership of fontMemResource.
294 */
bungemana4c4a2d2014-10-20 13:33:19 -0700295 FontMemResourceTypeface(const SkFontStyle& style, const LOGFONT& lf, HANDLE fontMemResource)
296 : LogFontTypeface(style, lf, true), fFontMemResource(fontMemResource)
297 { }
bungeman@google.com72cf4fc2014-03-21 22:48:32 +0000298
299 HANDLE fFontMemResource;
300
bungeman@google.coma5501992012-05-18 19:06:41 +0000301 typedef LogFontTypeface INHERITED;
302};
303
reed@google.comac6b9792011-03-11 15:42:51 +0000304static const LOGFONT& get_default_font() {
305 static LOGFONT gDefaultFont;
reed@google.comac6b9792011-03-11 15:42:51 +0000306 return gDefaultFont;
307}
308
bungemana4c4a2d2014-10-20 13:33:19 -0700309static bool FindByLogFont(SkTypeface* face, const SkFontStyle& requestedStyle, void* ctx) {
bungeman@google.coma5501992012-05-18 19:06:41 +0000310 LogFontTypeface* lface = static_cast<LogFontTypeface*>(face);
reed@google.com59d2f632011-05-02 19:36:59 +0000311 const LOGFONT* lf = reinterpret_cast<const LOGFONT*>(ctx);
reed@google.comac6b9792011-03-11 15:42:51 +0000312
bungeman@google.coma5501992012-05-18 19:06:41 +0000313 return lface &&
314 get_style(lface->fLogFont) == requestedStyle &&
reed@google.com59d2f632011-05-02 19:36:59 +0000315 !memcmp(&lface->fLogFont, lf, sizeof(LOGFONT));
316}
317
318/**
319 * This guy is public. It first searches the cache, and if a match is not found,
320 * it creates a new face.
321 */
322SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& origLF) {
323 LOGFONT lf = origLF;
324 make_canonical(&lf);
bungeman@google.comee51d1a2012-02-16 12:40:48 +0000325 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByLogFont, &lf);
326 if (NULL == face) {
reed@google.com59d2f632011-05-02 19:36:59 +0000327 face = LogFontTypeface::Create(lf);
bungeman@google.com90d812b2011-10-24 21:25:01 +0000328 SkTypefaceCache::Add(face, get_style(lf));
reed@google.com59d2f632011-05-02 19:36:59 +0000329 }
330 return face;
reed@google.comac6b9792011-03-11 15:42:51 +0000331}
332
reed@google.comdb77a6a2011-07-19 19:08:33 +0000333/**
bungeman@google.coma5501992012-05-18 19:06:41 +0000334 * The created SkTypeface takes ownership of fontMemResource.
335 */
336SkTypeface* SkCreateFontMemResourceTypefaceFromLOGFONT(const LOGFONT& origLF, HANDLE fontMemResource) {
337 LOGFONT lf = origLF;
338 make_canonical(&lf);
mtklein60b6e9d2014-10-24 10:43:15 -0700339 // We'll never get a cache hit, so no point in putting this in SkTypefaceCache.
340 return FontMemResourceTypeface::Create(lf, fontMemResource);
bungeman@google.coma5501992012-05-18 19:06:41 +0000341}
342
343/**
reed@google.comdb77a6a2011-07-19 19:08:33 +0000344 * This guy is public
345 */
346void SkLOGFONTFromTypeface(const SkTypeface* face, LOGFONT* lf) {
347 if (NULL == face) {
348 *lf = get_default_font();
349 } else {
bungeman@google.coma5501992012-05-18 19:06:41 +0000350 *lf = static_cast<const LogFontTypeface*>(face)->fLogFont;
reed@google.comdb77a6a2011-07-19 19:08:33 +0000351 }
352}
353
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000354// Construct Glyph to Unicode table.
355// Unicode code points that require conjugate pairs in utf16 are not
356// supported.
357// TODO(arthurhsu): Add support for conjugate pairs. It looks like that may
358// require parsing the TTF cmap table (platform 4, encoding 12) directly instead
359// of calling GetFontUnicodeRange().
360static void populate_glyph_to_unicode(HDC fontHdc, const unsigned glyphCount,
361 SkTDArray<SkUnichar>* glyphToUnicode) {
362 DWORD glyphSetBufferSize = GetFontUnicodeRanges(fontHdc, NULL);
363 if (!glyphSetBufferSize) {
364 return;
365 }
366
367 SkAutoTDeleteArray<BYTE> glyphSetBuffer(new BYTE[glyphSetBufferSize]);
368 GLYPHSET* glyphSet =
369 reinterpret_cast<LPGLYPHSET>(glyphSetBuffer.get());
370 if (GetFontUnicodeRanges(fontHdc, glyphSet) != glyphSetBufferSize) {
371 return;
372 }
373
374 glyphToUnicode->setCount(glyphCount);
375 memset(glyphToUnicode->begin(), 0, glyphCount * sizeof(SkUnichar));
376 for (DWORD i = 0; i < glyphSet->cRanges; ++i) {
377 // There is no guarantee that within a Unicode range, the corresponding
378 // glyph id in a font file are continuous. So, even if we have ranges,
379 // we can't just use the first and last entry of the range to compute
380 // result. We need to enumerate them one by one.
381 int count = glyphSet->ranges[i].cGlyphs;
382 SkAutoTArray<WCHAR> chars(count + 1);
383 chars[count] = 0; // termintate string
384 SkAutoTArray<WORD> glyph(count);
385 for (USHORT j = 0; j < count; ++j) {
386 chars[j] = glyphSet->ranges[i].wcLow + j;
387 }
388 GetGlyphIndicesW(fontHdc, chars.get(), count, glyph.get(),
389 GGI_MARK_NONEXISTING_GLYPHS);
390 // If the glyph ID is valid, and the glyph is not mapped, then we will
391 // fill in the char id into the vector. If the glyph is mapped already,
392 // skip it.
393 // TODO(arthurhsu): better improve this. e.g. Get all used char ids from
394 // font cache, then generate this mapping table from there. It's
395 // unlikely to have collisions since glyph reuse happens mostly for
396 // different Unicode pages.
397 for (USHORT j = 0; j < count; ++j) {
398 if (glyph[j] != 0xffff && glyph[j] < glyphCount &&
399 (*glyphToUnicode)[glyph[j]] == 0) {
400 (*glyphToUnicode)[glyph[j]] = chars[j];
401 }
402 }
403 }
404}
405
reed@google.com99edd432011-09-09 14:59:59 +0000406//////////////////////////////////////////////////////////////////////////////////////
407
408static int alignTo32(int n) {
409 return (n + 31) & ~31;
410}
411
412struct MyBitmapInfo : public BITMAPINFO {
413 RGBQUAD fMoreSpaceForColors[1];
414};
415
416class HDCOffscreen {
417public:
418 HDCOffscreen() {
419 fFont = 0;
420 fDC = 0;
421 fBM = 0;
422 fBits = NULL;
423 fWidth = fHeight = 0;
424 fIsBW = false;
425 }
426
427 ~HDCOffscreen() {
428 if (fDC) {
429 DeleteDC(fDC);
430 }
431 if (fBM) {
432 DeleteObject(fBM);
433 }
434 }
435
436 void init(HFONT font, const XFORM& xform) {
437 fFont = font;
438 fXform = xform;
439 }
440
bungeman@google.com97efada2012-07-30 20:40:50 +0000441 const void* draw(const SkGlyph&, bool isBW, size_t* srcRBPtr);
reed@google.com99edd432011-09-09 14:59:59 +0000442
443private:
444 HDC fDC;
445 HBITMAP fBM;
446 HFONT fFont;
447 XFORM fXform;
448 void* fBits; // points into fBM
449 int fWidth;
450 int fHeight;
451 bool fIsBW;
452};
453
reed@google.com754e4eb2011-09-26 13:21:39 +0000454const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW,
bungeman@google.com97efada2012-07-30 20:40:50 +0000455 size_t* srcRBPtr) {
reed@google.com84e22d82013-07-10 15:38:20 +0000456 // Can we share the scalercontext's fDDC, so we don't need to create
457 // a separate fDC here?
reed@google.com99edd432011-09-09 14:59:59 +0000458 if (0 == fDC) {
459 fDC = CreateCompatibleDC(0);
460 if (0 == fDC) {
461 return NULL;
462 }
463 SetGraphicsMode(fDC, GM_ADVANCED);
464 SetBkMode(fDC, TRANSPARENT);
465 SetTextAlign(fDC, TA_LEFT | TA_BASELINE);
466 SelectObject(fDC, fFont);
bungeman@google.com97efada2012-07-30 20:40:50 +0000467
468 COLORREF color = 0x00FFFFFF;
bsalomon@google.comb58a6392013-03-21 20:29:05 +0000469 SkDEBUGCODE(COLORREF prev =) SetTextColor(fDC, color);
bungeman@google.com97efada2012-07-30 20:40:50 +0000470 SkASSERT(prev != CLR_INVALID);
reed@google.com99edd432011-09-09 14:59:59 +0000471 }
472
473 if (fBM && (fIsBW != isBW || fWidth < glyph.fWidth || fHeight < glyph.fHeight)) {
474 DeleteObject(fBM);
475 fBM = 0;
476 }
reed@google.com754e4eb2011-09-26 13:21:39 +0000477 fIsBW = isBW;
reed@google.com99edd432011-09-09 14:59:59 +0000478
reed@google.com99edd432011-09-09 14:59:59 +0000479 fWidth = SkMax32(fWidth, glyph.fWidth);
480 fHeight = SkMax32(fHeight, glyph.fHeight);
481
482 int biWidth = isBW ? alignTo32(fWidth) : fWidth;
483
484 if (0 == fBM) {
485 MyBitmapInfo info;
486 sk_bzero(&info, sizeof(info));
487 if (isBW) {
488 RGBQUAD blackQuad = { 0, 0, 0, 0 };
489 RGBQUAD whiteQuad = { 0xFF, 0xFF, 0xFF, 0 };
490 info.bmiColors[0] = blackQuad;
491 info.bmiColors[1] = whiteQuad;
492 }
493 info.bmiHeader.biSize = sizeof(info.bmiHeader);
494 info.bmiHeader.biWidth = biWidth;
495 info.bmiHeader.biHeight = fHeight;
496 info.bmiHeader.biPlanes = 1;
497 info.bmiHeader.biBitCount = isBW ? 1 : 32;
498 info.bmiHeader.biCompression = BI_RGB;
499 if (isBW) {
500 info.bmiHeader.biClrUsed = 2;
501 }
502 fBM = CreateDIBSection(fDC, &info, DIB_RGB_COLORS, &fBits, 0, 0);
503 if (0 == fBM) {
504 return NULL;
505 }
506 SelectObject(fDC, fBM);
507 }
508
509 // erase
510 size_t srcRB = isBW ? (biWidth >> 3) : (fWidth << 2);
511 size_t size = fHeight * srcRB;
bungeman@google.com97efada2012-07-30 20:40:50 +0000512 memset(fBits, 0, size);
reed@google.com99edd432011-09-09 14:59:59 +0000513
514 XFORM xform = fXform;
515 xform.eDx = (float)-glyph.fLeft;
516 xform.eDy = (float)-glyph.fTop;
517 SetWorldTransform(fDC, &xform);
518
519 uint16_t glyphID = glyph.getGlyphID();
bungeman@google.com39698b12011-11-15 22:26:41 +0000520 BOOL ret = ExtTextOutW(fDC, 0, 0, ETO_GLYPH_INDEX, NULL, reinterpret_cast<LPCWSTR>(&glyphID), 1, NULL);
reed@google.com99edd432011-09-09 14:59:59 +0000521 GdiFlush();
bungeman@google.com39698b12011-11-15 22:26:41 +0000522 if (0 == ret) {
523 return NULL;
524 }
reed@google.com99edd432011-09-09 14:59:59 +0000525 *srcRBPtr = srcRB;
526 // offset to the start of the image
527 return (const char*)fBits + (fHeight - glyph.fHeight) * srcRB;
528}
529
reed@google.comb8a5c612012-06-13 20:01:44 +0000530//////////////////////////////////////////////////////////////////////////////
bungeman@google.com0abbff92013-07-27 20:37:56 +0000531#define BUFFERSIZE (1 << 13)
reed@google.com59d2f632011-05-02 19:36:59 +0000532
reed@google.com30ddd612013-07-30 17:47:39 +0000533class SkScalerContext_GDI : public SkScalerContext {
reed@google.comac6b9792011-03-11 15:42:51 +0000534public:
reed@google.com30ddd612013-07-30 17:47:39 +0000535 SkScalerContext_GDI(SkTypeface*, const SkDescriptor* desc);
536 virtual ~SkScalerContext_GDI();
reed@google.comac6b9792011-03-11 15:42:51 +0000537
reed@google.com84e22d82013-07-10 15:38:20 +0000538 // Returns true if the constructor was able to complete all of its
539 // initializations (which may include calling GDI).
540 bool isValid() const;
541
reed@google.comac6b9792011-03-11 15:42:51 +0000542protected:
mtklein72c9faa2015-01-09 10:06:39 -0800543 unsigned generateGlyphCount() SK_OVERRIDE;
544 uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
545 void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
546 void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
547 void generateImage(const SkGlyph& glyph) SK_OVERRIDE;
548 void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
549 void generateFontMetrics(SkPaint::FontMetrics*) SK_OVERRIDE;
reed@google.com99edd432011-09-09 14:59:59 +0000550
reed@google.comac6b9792011-03-11 15:42:51 +0000551private:
bungeman@google.com0abbff92013-07-27 20:37:56 +0000552 DWORD getGDIGlyphPath(const SkGlyph& glyph, UINT flags,
553 SkAutoSTMalloc<BUFFERSIZE, uint8_t>* glyphbuf);
554
reed@google.com99edd432011-09-09 14:59:59 +0000555 HDCOffscreen fOffscreen;
bungeman@google.com0abbff92013-07-27 20:37:56 +0000556 /** fGsA is the non-rotational part of total matrix without the text height scale.
557 * Used to find the magnitude of advances.
558 */
559 MAT2 fGsA;
bungeman@google.com6a774a12013-07-30 01:07:48 +0000560 /** The total matrix without the textSize. */
reed@google.comac6b9792011-03-11 15:42:51 +0000561 MAT2 fMat22;
bungeman@google.com6a774a12013-07-30 01:07:48 +0000562 /** Scales font to EM size. */
563 MAT2 fHighResMat22;
reed@google.comac6b9792011-03-11 15:42:51 +0000564 HDC fDDC;
565 HFONT fSavefont;
566 HFONT fFont;
567 SCRIPT_CACHE fSC;
568 int fGlyphCount;
reed@google.com1dd17a12011-05-17 14:04:41 +0000569
bungeman@google.com6a774a12013-07-30 01:07:48 +0000570 /** The total matrix which also removes EM scale. */
reed@google.com1dd17a12011-05-17 14:04:41 +0000571 SkMatrix fHiResMatrix;
bungeman@google.com0abbff92013-07-27 20:37:56 +0000572 /** fG_inv is the inverse of the rotational part of the total matrix.
573 * Used to set the direction of advances.
574 */
575 SkMatrix fG_inv;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000576 enum Type {
bungeman@google.com4732df62014-01-23 15:22:42 +0000577 kTrueType_Type, kBitmap_Type, kLine_Type
bungeman@google.coma0319f62012-04-18 15:40:50 +0000578 } fType;
579 TEXTMETRIC fTM;
reed@google.comac6b9792011-03-11 15:42:51 +0000580};
581
reed@google.comac6b9792011-03-11 15:42:51 +0000582static FIXED float2FIXED(float x) {
583 return SkFixedToFIXED(SkFloatToFixed(x));
584}
585
reed@google.com82a34d82011-07-26 19:33:08 +0000586static BYTE compute_quality(const SkScalerContext::Rec& rec) {
587 switch (rec.fMaskFormat) {
588 case SkMask::kBW_Format:
589 return NONANTIALIASED_QUALITY;
590 case SkMask::kLCD16_Format:
reed@google.com82a34d82011-07-26 19:33:08 +0000591 return CLEARTYPE_QUALITY;
592 default:
reed@google.com8351aab2012-01-18 17:06:35 +0000593 if (rec.fFlags & SkScalerContext::kGenA8FromLCD_Flag) {
594 return CLEARTYPE_QUALITY;
595 } else {
596 return ANTIALIASED_QUALITY;
597 }
reed@google.com82a34d82011-07-26 19:33:08 +0000598 }
599}
600
reed@google.com30ddd612013-07-30 17:47:39 +0000601SkScalerContext_GDI::SkScalerContext_GDI(SkTypeface* rawTypeface,
reed@google.com0da48612013-03-19 16:06:52 +0000602 const SkDescriptor* desc)
reed@google.com055180c2013-03-21 18:46:35 +0000603 : SkScalerContext(rawTypeface, desc)
604 , fDDC(0)
reed@google.com055180c2013-03-21 18:46:35 +0000605 , fSavefont(0)
reed@google.com84e22d82013-07-10 15:38:20 +0000606 , fFont(0)
reed@google.com055180c2013-03-21 18:46:35 +0000607 , fSC(0)
bungeman@google.com05a729f2013-06-20 15:29:16 +0000608 , fGlyphCount(-1)
609{
reed@google.com055180c2013-03-21 18:46:35 +0000610 LogFontTypeface* typeface = reinterpret_cast<LogFontTypeface*>(rawTypeface);
611
reed@google.comac6b9792011-03-11 15:42:51 +0000612 fDDC = ::CreateCompatibleDC(NULL);
reed@google.com84e22d82013-07-10 15:38:20 +0000613 if (!fDDC) {
614 return;
615 }
reed@google.com1dd17a12011-05-17 14:04:41 +0000616 SetGraphicsMode(fDDC, GM_ADVANCED);
reed@google.comac6b9792011-03-11 15:42:51 +0000617 SetBkMode(fDDC, TRANSPARENT);
skia.committer@gmail.com27e21fe2013-07-28 07:01:03 +0000618
bungeman@google.com11ba3192013-10-03 20:17:51 +0000619 // When GDI hinting, remove the entire Y scale to prevent 'subpixel' metrics.
bungeman5f14c5e2014-12-05 12:26:44 -0800620 // When not hinting, remove only the gdiTextSize scale which will be applied by GDI.
621 SkScalerContextRec::PreMatrixScale scaleConstraints =
622 (fRec.getHinting() == SkPaint::kNo_Hinting || fRec.getHinting() == SkPaint::kSlight_Hinting)
623 ? SkScalerContextRec::kVerticalInteger_PreMatrixScale
624 : SkScalerContextRec::kVertical_PreMatrixScale;
625 SkVector scale;
626 SkMatrix sA;
627 SkMatrix GsA;
628 SkMatrix A;
629 fRec.computeMatrices(scaleConstraints, &scale, &sA, &GsA, &fG_inv, &A);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000630
631 fGsA.eM11 = SkScalarToFIXED(GsA.get(SkMatrix::kMScaleX));
632 fGsA.eM12 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewY)); // This should be ~0.
633 fGsA.eM21 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewX));
634 fGsA.eM22 = SkScalarToFIXED(GsA.get(SkMatrix::kMScaleY));
635
bungeman5f14c5e2014-12-05 12:26:44 -0800636 SkScalar gdiTextSize = scale.fY;
637 if (gdiTextSize == 0) {
638 gdiTextSize = SK_Scalar1;
639 }
bungeman@google.com0abbff92013-07-27 20:37:56 +0000640
reed@google.com055180c2013-03-21 18:46:35 +0000641 LOGFONT lf = typeface->fLogFont;
bungeman@google.com11ba3192013-10-03 20:17:51 +0000642 lf.lfHeight = -SkScalarTruncToInt(gdiTextSize);
reed@google.com82a34d82011-07-26 19:33:08 +0000643 lf.lfQuality = compute_quality(fRec);
reed@google.comac6b9792011-03-11 15:42:51 +0000644 fFont = CreateFontIndirect(&lf);
reed@google.com84e22d82013-07-10 15:38:20 +0000645 if (!fFont) {
646 return;
647 }
reed@google.com1dd17a12011-05-17 14:04:41 +0000648
reed@google.comac6b9792011-03-11 15:42:51 +0000649 fSavefont = (HFONT)SelectObject(fDDC, fFont);
reed@google.coma767fa02011-08-05 21:40:26 +0000650
bungeman@google.coma0319f62012-04-18 15:40:50 +0000651 if (0 == GetTextMetrics(fDDC, &fTM)) {
reed@google.com055180c2013-03-21 18:46:35 +0000652 call_ensure_accessible(lf);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000653 if (0 == GetTextMetrics(fDDC, &fTM)) {
654 fTM.tmPitchAndFamily = TMPF_TRUETYPE;
655 }
656 }
bungeman@google.com90b7e382012-04-20 15:26:28 +0000657
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000658 XFORM xform;
bungeman@google.com90b7e382012-04-20 15:26:28 +0000659 if (fTM.tmPitchAndFamily & TMPF_VECTOR) {
bungeman@google.com4732df62014-01-23 15:22:42 +0000660 // Used a logfont on a memory context, should never get a device font.
661 // Therefore all TMPF_DEVICE will be PostScript fonts.
662
663 // If TMPF_VECTOR is set, one of TMPF_TRUETYPE or TMPF_DEVICE means that
664 // we have an outline font. Otherwise we have a vector FON, which is
665 // scalable, but not an outline font.
666 // This was determined by testing with Type1 PFM/PFB and
667 // OpenTypeCFF OTF, as well as looking at Wine bugs and sources.
668 if (fTM.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_DEVICE)) {
669 // Truetype or PostScript.
670 fType = SkScalerContext_GDI::kTrueType_Type;
671 } else {
672 // Stroked FON.
673 fType = SkScalerContext_GDI::kLine_Type;
674 }
bungeman@google.coma0319f62012-04-18 15:40:50 +0000675
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000676 // fPost2x2 is column-major, left handed (y down).
677 // XFORM 2x2 is row-major, left handed (y down).
bungeman@google.com0abbff92013-07-27 20:37:56 +0000678 xform.eM11 = SkScalarToFloat(sA.get(SkMatrix::kMScaleX));
679 xform.eM12 = SkScalarToFloat(sA.get(SkMatrix::kMSkewY));
680 xform.eM21 = SkScalarToFloat(sA.get(SkMatrix::kMSkewX));
681 xform.eM22 = SkScalarToFloat(sA.get(SkMatrix::kMScaleY));
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000682 xform.eDx = 0;
683 xform.eDy = 0;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000684
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000685 // MAT2 is row major, right handed (y up).
686 fMat22.eM11 = float2FIXED(xform.eM11);
687 fMat22.eM12 = float2FIXED(-xform.eM12);
688 fMat22.eM21 = float2FIXED(-xform.eM21);
689 fMat22.eM22 = float2FIXED(xform.eM22);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000690
691 if (needToRenderWithSkia(fRec)) {
692 this->forceGenerateImageFromPath();
693 }
694
bungeman@google.com11ba3192013-10-03 20:17:51 +0000695 // Create a hires matrix if we need linear metrics.
bungeman@google.com0abbff92013-07-27 20:37:56 +0000696 if (this->isSubpixel()) {
697 OUTLINETEXTMETRIC otm;
698 UINT success = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
699 if (0 == success) {
700 call_ensure_accessible(lf);
701 success = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
702 }
703 if (0 != success) {
bungeman@google.com11ba3192013-10-03 20:17:51 +0000704 SkScalar upem = SkIntToScalar(otm.otmEMSquare);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000705
bungeman@google.com11ba3192013-10-03 20:17:51 +0000706 SkScalar gdiTextSizeToEMScale = upem / gdiTextSize;
707 fHighResMat22.eM11 = float2FIXED(gdiTextSizeToEMScale);
bungeman@google.com6a774a12013-07-30 01:07:48 +0000708 fHighResMat22.eM12 = float2FIXED(0);
709 fHighResMat22.eM21 = float2FIXED(0);
bungeman@google.com11ba3192013-10-03 20:17:51 +0000710 fHighResMat22.eM22 = float2FIXED(gdiTextSizeToEMScale);
bungeman@google.com6a774a12013-07-30 01:07:48 +0000711
bungeman@google.com11ba3192013-10-03 20:17:51 +0000712 SkScalar removeEMScale = SkScalarInvert(upem);
bungeman@google.com6a774a12013-07-30 01:07:48 +0000713 fHiResMatrix = A;
bungeman@google.com11ba3192013-10-03 20:17:51 +0000714 fHiResMatrix.preScale(removeEMScale, removeEMScale);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000715 }
716 }
717
bungeman@google.coma0319f62012-04-18 15:40:50 +0000718 } else {
719 // Assume bitmap
reed@google.com30ddd612013-07-30 17:47:39 +0000720 fType = SkScalerContext_GDI::kBitmap_Type;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000721
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000722 xform.eM11 = 1.0f;
723 xform.eM12 = 0.0f;
724 xform.eM21 = 0.0f;
725 xform.eM22 = 1.0f;
726 xform.eDx = 0.0f;
727 xform.eDy = 0.0f;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000728
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000729 // fPost2x2 is column-major, left handed (y down).
730 // MAT2 is row major, right handed (y up).
bungeman@google.coma0319f62012-04-18 15:40:50 +0000731 fMat22.eM11 = SkScalarToFIXED(fRec.fPost2x2[0][0]);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000732 fMat22.eM12 = SkScalarToFIXED(-fRec.fPost2x2[1][0]);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000733 fMat22.eM21 = SkScalarToFIXED(-fRec.fPost2x2[0][1]);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000734 fMat22.eM22 = SkScalarToFIXED(fRec.fPost2x2[1][1]);
reed@google.coma767fa02011-08-05 21:40:26 +0000735 }
reed@google.com99edd432011-09-09 14:59:59 +0000736
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000737 fOffscreen.init(fFont, xform);
reed@google.comac6b9792011-03-11 15:42:51 +0000738}
739
reed@google.com30ddd612013-07-30 17:47:39 +0000740SkScalerContext_GDI::~SkScalerContext_GDI() {
reed@google.comac6b9792011-03-11 15:42:51 +0000741 if (fDDC) {
742 ::SelectObject(fDDC, fSavefont);
743 ::DeleteDC(fDDC);
744 }
745 if (fFont) {
746 ::DeleteObject(fFont);
747 }
748 if (fSC) {
749 ::ScriptFreeCache(&fSC);
750 }
751}
752
reed@google.com30ddd612013-07-30 17:47:39 +0000753bool SkScalerContext_GDI::isValid() const {
reed@google.com84e22d82013-07-10 15:38:20 +0000754 return fDDC && fFont;
755}
756
reed@google.com30ddd612013-07-30 17:47:39 +0000757unsigned SkScalerContext_GDI::generateGlyphCount() {
reed@google.comac6b9792011-03-11 15:42:51 +0000758 if (fGlyphCount < 0) {
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000759 fGlyphCount = calculateGlyphCount(
760 fDDC, static_cast<const LogFontTypeface*>(this->getTypeface())->fLogFont);
reed@google.comac6b9792011-03-11 15:42:51 +0000761 }
762 return fGlyphCount;
763}
764
bungeman@google.com33346482013-08-27 19:05:32 +0000765uint16_t SkScalerContext_GDI::generateCharToGlyph(SkUnichar utf32) {
reed@google.comac6b9792011-03-11 15:42:51 +0000766 uint16_t index = 0;
bungeman@google.com33346482013-08-27 19:05:32 +0000767 WCHAR utf16[2];
reed@google.comac6b9792011-03-11 15:42:51 +0000768 // TODO(ctguil): Support characters that generate more than one glyph.
bungeman@google.com33346482013-08-27 19:05:32 +0000769 if (SkUTF16_FromUnichar(utf32, (uint16_t*)utf16) == 1) {
reed@google.comac6b9792011-03-11 15:42:51 +0000770 // Type1 fonts fail with uniscribe API. Use GetGlyphIndices for plane 0.
skia.committer@gmail.com7bd141d2013-08-28 07:01:18 +0000771
bungeman@google.com33346482013-08-27 19:05:32 +0000772 /** Real documentation for GetGlyphIndiciesW:
773 *
774 * When GGI_MARK_NONEXISTING_GLYPHS is not specified and a character does not map to a
775 * glyph, then the 'default character's glyph is returned instead. The 'default character'
bungeman@google.com4732df62014-01-23 15:22:42 +0000776 * is available in fTM.tmDefaultChar. FON fonts have a default character, and there exists
777 * a usDefaultChar in the 'OS/2' table, version 2 and later. If there is no
bungeman@google.com33346482013-08-27 19:05:32 +0000778 * 'default character' specified by the font, then often the first character found is used.
779 *
780 * When GGI_MARK_NONEXISTING_GLYPHS is specified and a character does not map to a glyph,
781 * then the glyph 0xFFFF is used. In Windows XP and earlier, Bitmap/Vector FON usually use
782 * glyph 0x1F instead ('Terminal' appears to be special, returning 0xFFFF).
783 * Type1 PFM/PFB, TT, OT TT, OT CFF all appear to use 0xFFFF, even on XP.
784 */
785 DWORD result = GetGlyphIndicesW(fDDC, utf16, 1, &index, GGI_MARK_NONEXISTING_GLYPHS);
786 if (result == GDI_ERROR
787 || 0xFFFF == index
bungeman@google.com4732df62014-01-23 15:22:42 +0000788 || (0x1F == index &&
789 (fType == SkScalerContext_GDI::kBitmap_Type ||
790 fType == SkScalerContext_GDI::kLine_Type)
791 /*&& winVer < Vista */)
792 )
bungeman@google.com33346482013-08-27 19:05:32 +0000793 {
794 index = 0;
795 }
reed@google.comac6b9792011-03-11 15:42:51 +0000796 } else {
797 // Use uniscribe to detemine glyph index for non-BMP characters.
bungeman@google.com27f74aa2013-10-08 21:32:15 +0000798 static const int numWCHAR = 2;
799 static const int maxItems = 2;
800 // MSDN states that this can be NULL, but some things don't work then.
801 SCRIPT_CONTROL sc = { 0 };
802 // Add extra item to SCRIPT_ITEM to work around a bug (now documented).
803 // https://bugzilla.mozilla.org/show_bug.cgi?id=366643
804 SCRIPT_ITEM si[maxItems + 1];
805 int numItems;
806 HRZM(ScriptItemize(utf16, numWCHAR, maxItems, &sc, NULL, si, &numItems),
807 "Could not itemize character.");
reed@google.comac6b9792011-03-11 15:42:51 +0000808
bungeman@google.com27f74aa2013-10-08 21:32:15 +0000809 // Sometimes ScriptShape cannot find a glyph for a non-BMP and returns 2 space glyphs.
810 static const int maxGlyphs = 2;
811 SCRIPT_VISATTR vsa[maxGlyphs];
812 WORD outGlyphs[maxGlyphs];
813 WORD logClust[numWCHAR];
814 int numGlyphs;
815 HRZM(ScriptShape(fDDC, &fSC, utf16, numWCHAR, maxGlyphs, &si[0].a,
816 outGlyphs, logClust, vsa, &numGlyphs),
817 "Could not shape character.");
818 if (1 == numGlyphs) {
819 index = outGlyphs[0];
820 }
reed@google.comac6b9792011-03-11 15:42:51 +0000821 }
822 return index;
823}
824
reed@google.com30ddd612013-07-30 17:47:39 +0000825void SkScalerContext_GDI::generateAdvance(SkGlyph* glyph) {
reed@google.comac6b9792011-03-11 15:42:51 +0000826 this->generateMetrics(glyph);
827}
828
reed@google.com30ddd612013-07-30 17:47:39 +0000829void SkScalerContext_GDI::generateMetrics(SkGlyph* glyph) {
reed@google.comac6b9792011-03-11 15:42:51 +0000830 SkASSERT(fDDC);
831
bungeman@google.com4732df62014-01-23 15:22:42 +0000832 if (fType == SkScalerContext_GDI::kBitmap_Type || fType == SkScalerContext_GDI::kLine_Type) {
bungeman@google.coma0319f62012-04-18 15:40:50 +0000833 SIZE size;
djsollen1b277042014-08-06 06:58:06 -0700834 WORD glyphs = glyph->getGlyphID();
bungeman@google.coma0319f62012-04-18 15:40:50 +0000835 if (0 == GetTextExtentPointI(fDDC, &glyphs, 1, &size)) {
836 glyph->fWidth = SkToS16(fTM.tmMaxCharWidth);
837 } else {
838 glyph->fWidth = SkToS16(size.cx);
839 }
840 glyph->fHeight = SkToS16(size.cy);
841
842 glyph->fTop = SkToS16(-fTM.tmAscent);
bungeman@google.com4732df62014-01-23 15:22:42 +0000843 // Bitmap FON cannot underhang, but vector FON may.
844 // There appears no means of determining underhang of vector FON.
bungeman@google.coma0319f62012-04-18 15:40:50 +0000845 glyph->fLeft = SkToS16(0);
846 glyph->fAdvanceX = SkIntToFixed(glyph->fWidth);
847 glyph->fAdvanceY = 0;
848
bungeman@google.com4732df62014-01-23 15:22:42 +0000849 // Vector FON will transform nicely, but bitmap FON do not.
850 if (fType == SkScalerContext_GDI::kLine_Type) {
851 SkRect bounds = SkRect::MakeXYWH(glyph->fLeft, glyph->fTop,
852 glyph->fWidth, glyph->fHeight);
853 SkMatrix m;
854 m.setAll(SkFIXEDToScalar(fMat22.eM11), -SkFIXEDToScalar(fMat22.eM21), 0,
855 -SkFIXEDToScalar(fMat22.eM12), SkFIXEDToScalar(fMat22.eM22), 0,
reed3f43f8a2015-01-20 19:58:36 -0800856 0, 0, 1);
bungeman@google.com4732df62014-01-23 15:22:42 +0000857 m.mapRect(&bounds);
reedd02cf262014-11-18 18:06:45 -0800858 bounds.roundOut(&bounds);
bungeman@google.com4732df62014-01-23 15:22:42 +0000859 glyph->fLeft = SkScalarTruncToInt(bounds.fLeft);
860 glyph->fTop = SkScalarTruncToInt(bounds.fTop);
861 glyph->fWidth = SkScalarTruncToInt(bounds.width());
862 glyph->fHeight = SkScalarTruncToInt(bounds.height());
863 }
864
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000865 // Apply matrix to advance.
bungeman@google.com4732df62014-01-23 15:22:42 +0000866 glyph->fAdvanceY = SkFixedMul(-SkFIXEDToFixed(fMat22.eM12), glyph->fAdvanceX);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000867 glyph->fAdvanceX = SkFixedMul(SkFIXEDToFixed(fMat22.eM11), glyph->fAdvanceX);
868
869 return;
870 }
871
djsollen1b277042014-08-06 06:58:06 -0700872 UINT glyphId = glyph->getGlyphID();
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000873
reed@google.comac6b9792011-03-11 15:42:51 +0000874 GLYPHMETRICS gm;
reed@google.com1dd17a12011-05-17 14:04:41 +0000875 sk_bzero(&gm, sizeof(gm));
reed@google.comac6b9792011-03-11 15:42:51 +0000876
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000877 DWORD status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
878 if (GDI_ERROR == status) {
reed@google.com055180c2013-03-21 18:46:35 +0000879 LogFontTypeface::EnsureAccessible(this->getTypeface());
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000880 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
881 if (GDI_ERROR == status) {
882 glyph->zeroMetrics();
883 return;
884 }
885 }
886
887 bool empty = false;
888 // The black box is either the embedded bitmap size or the outline extent.
889 // It is 1x1 if nothing is to be drawn, but will also be 1x1 if something very small
890 // is to be drawn, like a '.'. We need to outset '.' but do not wish to outset ' '.
891 if (1 == gm.gmBlackBoxX && 1 == gm.gmBlackBoxY) {
892 // If GetGlyphOutline with GGO_NATIVE returns 0, we know there was no outline.
893 DWORD bufferSize = GetGlyphOutlineW(fDDC, glyphId, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
894 empty = (0 == bufferSize);
895 }
896
897 glyph->fTop = SkToS16(-gm.gmptGlyphOrigin.y);
898 glyph->fLeft = SkToS16(gm.gmptGlyphOrigin.x);
899 if (empty) {
900 glyph->fWidth = 0;
901 glyph->fHeight = 0;
902 } else {
903 // Outset, since the image may bleed out of the black box.
904 // For embedded bitmaps the black box should be exact.
905 // For outlines we need to outset by 1 in all directions for bleed.
906 // For ClearType we need to outset by 2 for bleed.
907 glyph->fWidth = gm.gmBlackBoxX + 4;
908 glyph->fHeight = gm.gmBlackBoxY + 4;
909 glyph->fTop -= 2;
910 glyph->fLeft -= 2;
911 }
912 glyph->fAdvanceX = SkIntToFixed(gm.gmCellIncX);
913 glyph->fAdvanceY = SkIntToFixed(gm.gmCellIncY);
reed@google.comac6b9792011-03-11 15:42:51 +0000914 glyph->fRsbDelta = 0;
915 glyph->fLsbDelta = 0;
916
bungeman@google.com6a774a12013-07-30 01:07:48 +0000917 if (this->isSubpixel()) {
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000918 sk_bzero(&gm, sizeof(gm));
bungeman@google.com6a774a12013-07-30 01:07:48 +0000919 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fHighResMat22);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000920 if (GDI_ERROR != status) {
921 SkPoint advance;
922 fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
923 glyph->fAdvanceX = SkScalarToFixed(advance.fX);
924 glyph->fAdvanceY = SkScalarToFixed(advance.fY);
reed@google.comac6b9792011-03-11 15:42:51 +0000925 }
bungeman@google.com0abbff92013-07-27 20:37:56 +0000926 } else if (!isAxisAligned(this->fRec)) {
927 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fGsA);
928 if (GDI_ERROR != status) {
929 SkPoint advance;
930 fG_inv.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
931 glyph->fAdvanceX = SkScalarToFixed(advance.fX);
932 glyph->fAdvanceY = SkScalarToFixed(advance.fY);
933 }
reed@google.comac6b9792011-03-11 15:42:51 +0000934 }
935}
936
bungeman@google.com6a774a12013-07-30 01:07:48 +0000937static const MAT2 gMat2Identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
bungeman41078062014-07-07 08:16:37 -0700938void SkScalerContext_GDI::generateFontMetrics(SkPaint::FontMetrics* metrics) {
939 if (NULL == metrics) {
940 return;
reed@google.com60af92c2013-05-08 14:11:28 +0000941 }
bungeman41078062014-07-07 08:16:37 -0700942 sk_bzero(metrics, sizeof(*metrics));
reed@google.comac6b9792011-03-11 15:42:51 +0000943
944 SkASSERT(fDDC);
945
bungeman@google.come9d83192013-06-21 05:31:38 +0000946#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
bungeman@google.com4732df62014-01-23 15:22:42 +0000947 if (fType == SkScalerContext_GDI::kBitmap_Type || fType == SkScalerContext_GDI::kLine_Type) {
bungeman@google.come9d83192013-06-21 05:31:38 +0000948#endif
bungeman41078062014-07-07 08:16:37 -0700949 metrics->fTop = SkIntToScalar(-fTM.tmAscent);
950 metrics->fAscent = SkIntToScalar(-fTM.tmAscent);
951 metrics->fDescent = SkIntToScalar(fTM.tmDescent);
952 metrics->fBottom = SkIntToScalar(fTM.tmDescent);
953 metrics->fLeading = SkIntToScalar(fTM.tmExternalLeading);
954 metrics->fAvgCharWidth = SkIntToScalar(fTM.tmAveCharWidth);
955 metrics->fMaxCharWidth = SkIntToScalar(fTM.tmMaxCharWidth);
956 metrics->fXMin = 0;
957 metrics->fXMax = metrics->fMaxCharWidth;
958 //metrics->fXHeight = 0;
bungeman@google.come9d83192013-06-21 05:31:38 +0000959#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
bungeman@google.coma0319f62012-04-18 15:40:50 +0000960 return;
961 }
bungeman@google.come9d83192013-06-21 05:31:38 +0000962#endif
bungeman@google.coma0319f62012-04-18 15:40:50 +0000963
reed@google.comac6b9792011-03-11 15:42:51 +0000964 OUTLINETEXTMETRIC otm;
965
966 uint32_t ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000967 if (0 == ret) {
reed@google.com055180c2013-03-21 18:46:35 +0000968 LogFontTypeface::EnsureAccessible(this->getTypeface());
bungeman@google.com39698b12011-11-15 22:26:41 +0000969 ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
970 }
bungeman@google.com0abbff92013-07-27 20:37:56 +0000971 if (0 == ret) {
bungeman@google.coma0319f62012-04-18 15:40:50 +0000972 return;
reed@google.comac6b9792011-03-11 15:42:51 +0000973 }
974
bungeman@google.come9d83192013-06-21 05:31:38 +0000975#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
bungeman41078062014-07-07 08:16:37 -0700976 metrics->fTop = SkIntToScalar(-otm.otmrcFontBox.top);
977 metrics->fAscent = SkIntToScalar(-otm.otmAscent);
978 metrics->fDescent = SkIntToScalar(-otm.otmDescent);
979 metrics->fBottom = SkIntToScalar(-otm.otmrcFontBox.bottom);
980 metrics->fLeading = SkIntToScalar(otm.otmLineGap);
981 metrics->fAvgCharWidth = SkIntToScalar(otm.otmTextMetrics.tmAveCharWidth);
982 metrics->fMaxCharWidth = SkIntToScalar(otm.otmTextMetrics.tmMaxCharWidth);
983 metrics->fXMin = SkIntToScalar(otm.otmrcFontBox.left);
984 metrics->fXMax = SkIntToScalar(otm.otmrcFontBox.right);
commit-bot@chromium.orgd3031aa2014-05-14 14:54:51 +0000985#endif
bungeman41078062014-07-07 08:16:37 -0700986 metrics->fUnderlineThickness = SkIntToScalar(otm.otmsUnderscoreSize);
987 metrics->fUnderlinePosition = -SkIntToScalar(otm.otmsUnderscorePosition);
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +0000988
bungeman41078062014-07-07 08:16:37 -0700989 metrics->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag;
990 metrics->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
bungeman@google.com4a867a62014-05-22 17:59:21 +0000991
bungeman41078062014-07-07 08:16:37 -0700992 metrics->fXHeight = SkIntToScalar(otm.otmsXHeight);
993 GLYPHMETRICS gm;
994 sk_bzero(&gm, sizeof(gm));
995 DWORD len = GetGlyphOutlineW(fDDC, 'x', GGO_METRICS, &gm, 0, 0, &gMat2Identity);
996 if (len != GDI_ERROR && gm.gmBlackBoxY > 0) {
997 metrics->fXHeight = SkIntToScalar(gm.gmBlackBoxY);
reed@google.comac6b9792011-03-11 15:42:51 +0000998 }
999}
1000
reed@google.com7430a332011-10-03 14:37:38 +00001001////////////////////////////////////////////////////////////////////////////////////////
1002
bungeman@google.com0abbff92013-07-27 20:37:56 +00001003#define SK_SHOW_TEXT_BLIT_COVERAGE 0
1004
reed@google.com7430a332011-10-03 14:37:38 +00001005static void build_power_table(uint8_t table[], float ee) {
1006 for (int i = 0; i < 256; i++) {
1007 float x = i / 255.f;
bungeman@google.com97efada2012-07-30 20:40:50 +00001008 x = sk_float_pow(x, ee);
reed@google.come1ca7052013-12-17 19:22:07 +00001009 int xx = SkScalarRoundToInt(x * 255);
reed@google.com7430a332011-10-03 14:37:38 +00001010 table[i] = SkToU8(xx);
1011 }
1012}
1013
bungeman@google.com97efada2012-07-30 20:40:50 +00001014/**
1015 * This will invert the gamma applied by GDI (gray-scale antialiased), so we
1016 * can get linear values.
1017 *
1018 * GDI grayscale appears to use a hard-coded gamma of 2.3.
1019 *
1020 * GDI grayscale appears to draw using the black and white rasterizer at four
1021 * times the size and then downsamples to compute the coverage mask. As a
1022 * result there are only seventeen total grays. This lack of fidelity means
1023 * that shifting into other color spaces is imprecise.
1024 */
1025static const uint8_t* getInverseGammaTableGDI() {
bungeman@google.com05a729f2013-06-20 15:29:16 +00001026 // Since build_power_table is idempotent, many threads can build gTableGdi
1027 // simultaneously.
1028
1029 // Microsoft Specific:
1030 // Making gInited volatile provides read-aquire and write-release in vc++.
1031 // In VS2012, see compiler option /volatile:(ms|iso).
1032 // Replace with C++11 atomics when possible.
1033 static volatile bool gInited;
bungeman@google.com97efada2012-07-30 20:40:50 +00001034 static uint8_t gTableGdi[256];
bungeman@google.com05a729f2013-06-20 15:29:16 +00001035 if (gInited) {
1036 // Need a L/L (read) barrier (full acquire not needed). If gInited is observed
1037 // true then gTableGdi is observable, but it must be requested.
1038 } else {
bungeman@google.com97efada2012-07-30 20:40:50 +00001039 build_power_table(gTableGdi, 2.3f);
bungeman@google.com05a729f2013-06-20 15:29:16 +00001040 // Need a S/S (write) barrier (full release not needed) here so that this
1041 // write to gInited becomes observable after gTableGdi.
bungeman@google.com97efada2012-07-30 20:40:50 +00001042 gInited = true;
1043 }
1044 return gTableGdi;
1045}
1046
1047/**
1048 * This will invert the gamma applied by GDI ClearType, so we can get linear
1049 * values.
1050 *
1051 * GDI ClearType uses SPI_GETFONTSMOOTHINGCONTRAST / 1000 as the gamma value.
1052 * If this value is not specified, the default is a gamma of 1.4.
1053 */
1054static const uint8_t* getInverseGammaTableClearType() {
bungeman@google.com05a729f2013-06-20 15:29:16 +00001055 // We don't expect SPI_GETFONTSMOOTHINGCONTRAST to ever change, so building
1056 // gTableClearType with build_power_table is effectively idempotent.
1057
1058 // Microsoft Specific:
1059 // Making gInited volatile provides read-aquire and write-release in vc++.
1060 // In VS2012, see compiler option /volatile:(ms|iso).
1061 // Replace with C++11 atomics when possible.
1062 static volatile bool gInited;
bungeman@google.com97efada2012-07-30 20:40:50 +00001063 static uint8_t gTableClearType[256];
bungeman@google.com05a729f2013-06-20 15:29:16 +00001064 if (gInited) {
1065 // Need a L/L (read) barrier (acquire not needed). If gInited is observed
1066 // true then gTableClearType is observable, but it must be requested.
1067 } else {
reed@google.com7430a332011-10-03 14:37:38 +00001068 UINT level = 0;
1069 if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &level, 0) || !level) {
1070 // can't get the data, so use a default
1071 level = 1400;
1072 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001073 build_power_table(gTableClearType, level / 1000.0f);
bungeman@google.com05a729f2013-06-20 15:29:16 +00001074 // Need a S/S (write) barrier (release not needed) here so that this
1075 // write to gInited becomes observable after gTableClearType.
reed@google.com7430a332011-10-03 14:37:38 +00001076 gInited = true;
1077 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001078 return gTableClearType;
reed@google.com7430a332011-10-03 14:37:38 +00001079}
1080
reed@google.comac6b9792011-03-11 15:42:51 +00001081#include "SkColorPriv.h"
1082
bungeman@google.com63853142012-08-01 15:36:46 +00001083//Cannot assume that the input rgb is gray due to possible setting of kGenA8FromLCD_Flag.
bungeman@google.com97efada2012-07-30 20:40:50 +00001084template<bool APPLY_PREBLEND>
1085static inline uint8_t rgb_to_a8(SkGdiRGB rgb, const uint8_t* table8) {
bungeman@google.com63853142012-08-01 15:36:46 +00001086 U8CPU r = (rgb >> 16) & 0xFF;
1087 U8CPU g = (rgb >> 8) & 0xFF;
1088 U8CPU b = (rgb >> 0) & 0xFF;
bungeman@google.com1bfe01d2012-08-28 16:02:42 +00001089 return sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
reed@google.com8351aab2012-01-18 17:06:35 +00001090}
1091
bungeman@google.com97efada2012-07-30 20:40:50 +00001092template<bool APPLY_PREBLEND>
1093static inline uint16_t rgb_to_lcd16(SkGdiRGB rgb, const uint8_t* tableR,
1094 const uint8_t* tableG,
1095 const uint8_t* tableB) {
1096 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1097 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1098 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001099#if SK_SHOW_TEXT_BLIT_COVERAGE
1100 r = SkMax32(r, 10); g = SkMax32(g, 10); b = SkMax32(b, 10);
1101#endif
bungeman@google.com97efada2012-07-30 20:40:50 +00001102 return SkPack888ToRGB16(r, g, b);
reed@google.com82a34d82011-07-26 19:33:08 +00001103}
1104
reed@google.com82cff022011-09-22 14:33:40 +00001105// Is this GDI color neither black nor white? If so, we have to keep this
1106// image as is, rather than smashing it down to a BW mask.
1107//
1108// returns int instead of bool, since we don't want/have to pay to convert
1109// the zero/non-zero value into a bool
1110static int is_not_black_or_white(SkGdiRGB c) {
1111 // same as (but faster than)
1112 // c &= 0x00FFFFFF;
1113 // return 0 == c || 0x00FFFFFF == c;
1114 return (c + (c & 1)) & 0x00FFFFFF;
reed@google.com5e2df642011-09-21 18:42:09 +00001115}
1116
bungeman@google.com4b18f572013-07-22 15:21:23 +00001117static bool is_rgb_really_bw(const SkGdiRGB* src, int width, int height, size_t srcRB) {
reed@google.com5e2df642011-09-21 18:42:09 +00001118 for (int y = 0; y < height; ++y) {
1119 for (int x = 0; x < width; ++x) {
reed@google.com82cff022011-09-22 14:33:40 +00001120 if (is_not_black_or_white(src[x])) {
reed@google.com5e2df642011-09-21 18:42:09 +00001121 return false;
1122 }
1123 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001124 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001125 }
1126 return true;
1127}
1128
bungeman@google.com97efada2012-07-30 20:40:50 +00001129// gdi's bitmap is upside-down, so we reverse dst walking in Y
1130// whenever we copy it into skia's buffer
reed@google.com5e2df642011-09-21 18:42:09 +00001131static void rgb_to_bw(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
bungeman@google.com97efada2012-07-30 20:40:50 +00001132 const SkGlyph& glyph) {
reed@google.com5e2df642011-09-21 18:42:09 +00001133 const int width = glyph.fWidth;
1134 const size_t dstRB = (width + 7) >> 3;
1135 uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1136
1137 int byteCount = width >> 3;
1138 int bitCount = width & 7;
1139
1140 // adjust srcRB to skip the values in our byteCount loop,
1141 // since we increment src locally there
1142 srcRB -= byteCount * 8 * sizeof(SkGdiRGB);
1143
1144 for (int y = 0; y < glyph.fHeight; ++y) {
1145 if (byteCount > 0) {
reed@google.com5e2df642011-09-21 18:42:09 +00001146 for (int i = 0; i < byteCount; ++i) {
reed@google.com7a230142011-09-27 14:07:21 +00001147 unsigned byte = 0;
bungeman@google.com97efada2012-07-30 20:40:50 +00001148 byte |= src[0] & (1 << 7);
1149 byte |= src[1] & (1 << 6);
1150 byte |= src[2] & (1 << 5);
1151 byte |= src[3] & (1 << 4);
1152 byte |= src[4] & (1 << 3);
1153 byte |= src[5] & (1 << 2);
1154 byte |= src[6] & (1 << 1);
1155 byte |= src[7] & (1 << 0);
reed@google.com6a8f14d2011-09-27 12:54:24 +00001156 dst[i] = byte;
reed@google.com5e2df642011-09-21 18:42:09 +00001157 src += 8;
1158 }
1159 }
1160 if (bitCount > 0) {
1161 unsigned byte = 0;
1162 unsigned mask = 0x80;
1163 for (int i = 0; i < bitCount; i++) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001164 byte |= src[i] & mask;
reed@google.com5e2df642011-09-21 18:42:09 +00001165 mask >>= 1;
1166 }
1167 dst[byteCount] = byte;
1168 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001169 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001170 dst -= dstRB;
1171 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001172#if SK_SHOW_TEXT_BLIT_COVERAGE
1173 if (glyph.fWidth > 0 && glyph.fHeight > 0) {
1174 uint8_t* first = (uint8_t*)glyph.fImage;
1175 uint8_t* last = (uint8_t*)((char*)glyph.fImage + glyph.fHeight * dstRB - 1);
1176 *first |= 1 << 7;
1177 *last |= bitCount == 0 ? 1 : 1 << (8 - bitCount);
1178 }
1179#endif
reed@google.com5e2df642011-09-21 18:42:09 +00001180}
1181
bungeman@google.com97efada2012-07-30 20:40:50 +00001182template<bool APPLY_PREBLEND>
reed@google.com5e2df642011-09-21 18:42:09 +00001183static void rgb_to_a8(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
bungeman@google.com97efada2012-07-30 20:40:50 +00001184 const SkGlyph& glyph, const uint8_t* table8) {
reed@google.com5e2df642011-09-21 18:42:09 +00001185 const size_t dstRB = glyph.rowBytes();
1186 const int width = glyph.fWidth;
1187 uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1188
1189 for (int y = 0; y < glyph.fHeight; y++) {
1190 for (int i = 0; i < width; i++) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001191 dst[i] = rgb_to_a8<APPLY_PREBLEND>(src[i], table8);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001192#if SK_SHOW_TEXT_BLIT_COVERAGE
1193 dst[i] = SkMax32(dst[i], 10);
1194#endif
reed@google.com5e2df642011-09-21 18:42:09 +00001195 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001196 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001197 dst -= dstRB;
1198 }
1199}
1200
bungeman@google.com97efada2012-07-30 20:40:50 +00001201template<bool APPLY_PREBLEND>
1202static void rgb_to_lcd16(const SkGdiRGB* SK_RESTRICT src, size_t srcRB, const SkGlyph& glyph,
1203 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
reed@google.com5e2df642011-09-21 18:42:09 +00001204 const size_t dstRB = glyph.rowBytes();
1205 const int width = glyph.fWidth;
1206 uint16_t* SK_RESTRICT dst = (uint16_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1207
1208 for (int y = 0; y < glyph.fHeight; y++) {
1209 for (int i = 0; i < width; i++) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001210 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(src[i], tableR, tableG, tableB);
reed@google.com5e2df642011-09-21 18:42:09 +00001211 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001212 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001213 dst = (uint16_t*)((char*)dst - dstRB);
1214 }
1215}
1216
reed@google.com6f5df482011-09-28 20:33:24 +00001217static inline unsigned clamp255(unsigned x) {
1218 SkASSERT(x <= 256);
1219 return x - (x >> 8);
1220}
1221
reed@google.com30ddd612013-07-30 17:47:39 +00001222void SkScalerContext_GDI::generateImage(const SkGlyph& glyph) {
reed@google.comac6b9792011-03-11 15:42:51 +00001223 SkASSERT(fDDC);
1224
reed@google.com62711172011-05-18 15:08:10 +00001225 const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
reed@google.com82a34d82011-07-26 19:33:08 +00001226 const bool isAA = !isLCD(fRec);
reed@google.comac6b9792011-03-11 15:42:51 +00001227
reed@google.com99edd432011-09-09 14:59:59 +00001228 size_t srcRB;
bungeman@google.com97efada2012-07-30 20:40:50 +00001229 const void* bits = fOffscreen.draw(glyph, isBW, &srcRB);
bungeman@google.com39698b12011-11-15 22:26:41 +00001230 if (NULL == bits) {
reed@google.com055180c2013-03-21 18:46:35 +00001231 LogFontTypeface::EnsureAccessible(this->getTypeface());
bungeman@google.com97efada2012-07-30 20:40:50 +00001232 bits = fOffscreen.draw(glyph, isBW, &srcRB);
bungeman@google.com39698b12011-11-15 22:26:41 +00001233 if (NULL == bits) {
1234 sk_bzero(glyph.fImage, glyph.computeImageSize());
1235 return;
1236 }
reed@google.com82a34d82011-07-26 19:33:08 +00001237 }
reed@google.comac6b9792011-03-11 15:42:51 +00001238
bungeman@google.com97efada2012-07-30 20:40:50 +00001239 if (!isBW) {
bungeman@google.com1bd2d672012-08-13 20:01:51 +00001240 const uint8_t* table;
1241 //The offscreen contains a GDI blit if isAA and kGenA8FromLCD_Flag is not set.
1242 //Otherwise the offscreen contains a ClearType blit.
1243 if (isAA && !(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag)) {
1244 table = getInverseGammaTableGDI();
1245 } else {
1246 table = getInverseGammaTableClearType();
bungeman@google.com97efada2012-07-30 20:40:50 +00001247 }
1248 //Note that the following cannot really be integrated into the
1249 //pre-blend, since we may not be applying the pre-blend; when we aren't
1250 //applying the pre-blend it means that a filter wants linear anyway.
1251 //Other code may also be applying the pre-blend, so we'd need another
1252 //one with this and one without.
reed@google.com6f5df482011-09-28 20:33:24 +00001253 SkGdiRGB* addr = (SkGdiRGB*)bits;
1254 for (int y = 0; y < glyph.fHeight; ++y) {
1255 for (int x = 0; x < glyph.fWidth; ++x) {
1256 int r = (addr[x] >> 16) & 0xFF;
1257 int g = (addr[x] >> 8) & 0xFF;
1258 int b = (addr[x] >> 0) & 0xFF;
reed@google.com7430a332011-10-03 14:37:38 +00001259 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
reed@google.com6f5df482011-09-28 20:33:24 +00001260 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001261 addr = SkTAddOffset<SkGdiRGB>(addr, srcRB);
reed@google.com6f5df482011-09-28 20:33:24 +00001262 }
1263 }
1264
reed@google.com82a34d82011-07-26 19:33:08 +00001265 int width = glyph.fWidth;
1266 size_t dstRB = glyph.rowBytes();
1267 if (isBW) {
1268 const uint8_t* src = (const uint8_t*)bits;
reed@google.com82a34d82011-07-26 19:33:08 +00001269 uint8_t* dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1270 for (int y = 0; y < glyph.fHeight; y++) {
1271 memcpy(dst, src, dstRB);
1272 src += srcRB;
1273 dst -= dstRB;
reed@google.comac6b9792011-03-11 15:42:51 +00001274 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001275#if SK_SHOW_TEXT_BLIT_COVERAGE
1276 if (glyph.fWidth > 0 && glyph.fHeight > 0) {
1277 int bitCount = width & 7;
1278 uint8_t* first = (uint8_t*)glyph.fImage;
1279 uint8_t* last = (uint8_t*)((char*)glyph.fImage + glyph.fHeight * dstRB - 1);
1280 *first |= 1 << 7;
1281 *last |= bitCount == 0 ? 1 : 1 << (8 - bitCount);
1282 }
1283#endif
reed@google.com82a34d82011-07-26 19:33:08 +00001284 } else if (isAA) {
reed@google.com6f5df482011-09-28 20:33:24 +00001285 // since the caller may require A8 for maskfilters, we can't check for BW
1286 // ... until we have the caller tell us that explicitly
reed@google.com5e2df642011-09-21 18:42:09 +00001287 const SkGdiRGB* src = (const SkGdiRGB*)bits;
bungeman@google.coma76de722012-10-26 19:35:54 +00001288 if (fPreBlend.isApplicable()) {
1289 rgb_to_a8<true>(src, srcRB, glyph, fPreBlend.fG);
bungeman@google.com97efada2012-07-30 20:40:50 +00001290 } else {
bungeman@google.coma76de722012-10-26 19:35:54 +00001291 rgb_to_a8<false>(src, srcRB, glyph, fPreBlend.fG);
bungeman@google.com97efada2012-07-30 20:40:50 +00001292 }
reed@google.com82a34d82011-07-26 19:33:08 +00001293 } else { // LCD16
reed@google.com5e2df642011-09-21 18:42:09 +00001294 const SkGdiRGB* src = (const SkGdiRGB*)bits;
1295 if (is_rgb_really_bw(src, width, glyph.fHeight, srcRB)) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001296 rgb_to_bw(src, srcRB, glyph);
reed@google.com5e2df642011-09-21 18:42:09 +00001297 ((SkGlyph*)&glyph)->fMaskFormat = SkMask::kBW_Format;
1298 } else {
reedd54d3fc2014-11-13 14:39:58 -08001299 SkASSERT(SkMask::kLCD16_Format == glyph.fMaskFormat);
1300 if (fPreBlend.isApplicable()) {
1301 rgb_to_lcd16<true>(src, srcRB, glyph,
1302 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
reed@google.com754e4eb2011-09-26 13:21:39 +00001303 } else {
reedd54d3fc2014-11-13 14:39:58 -08001304 rgb_to_lcd16<false>(src, srcRB, glyph,
1305 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
reed@google.com754e4eb2011-09-26 13:21:39 +00001306 }
reed@google.comac6b9792011-03-11 15:42:51 +00001307 }
1308 }
reed@google.comac6b9792011-03-11 15:42:51 +00001309}
1310
bungeman@google.com0abbff92013-07-27 20:37:56 +00001311class GDIGlyphbufferPointIter {
1312public:
skia.committer@gmail.com27e21fe2013-07-28 07:01:03 +00001313 GDIGlyphbufferPointIter(const uint8_t* glyphbuf, DWORD total_size)
bungeman@google.com0abbff92013-07-27 20:37:56 +00001314 : fHeaderIter(glyphbuf, total_size), fCurveIter(), fPointIter()
1315 { }
reed@google.comac6b9792011-03-11 15:42:51 +00001316
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001317 POINTFX const * next() {
bungeman@google.com0abbff92013-07-27 20:37:56 +00001318nextHeader:
1319 if (!fCurveIter.isSet()) {
1320 const TTPOLYGONHEADER* header = fHeaderIter.next();
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001321 if (NULL == header) {
1322 return NULL;
1323 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001324 fCurveIter.set(header);
1325 const TTPOLYCURVE* curve = fCurveIter.next();
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001326 if (NULL == curve) {
1327 return NULL;
1328 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001329 fPointIter.set(curve);
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001330 return &header->pfxStart;
bungeman@google.com05a729f2013-06-20 15:29:16 +00001331 }
1332
bungeman@google.com0abbff92013-07-27 20:37:56 +00001333 const POINTFX* nextPoint = fPointIter.next();
1334 if (NULL == nextPoint) {
1335 const TTPOLYCURVE* curve = fCurveIter.next();
1336 if (NULL == curve) {
1337 fCurveIter.set();
1338 goto nextHeader;
1339 } else {
1340 fPointIter.set(curve);
bungeman@google.com05a729f2013-06-20 15:29:16 +00001341 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001342 nextPoint = fPointIter.next();
reed@google.comac6b9792011-03-11 15:42:51 +00001343 }
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001344 return nextPoint;
reed@google.comac6b9792011-03-11 15:42:51 +00001345 }
bungeman@google.comd1c7f712013-03-11 18:51:19 +00001346
bungeman@google.com0abbff92013-07-27 20:37:56 +00001347 WORD currentCurveType() {
1348 return fPointIter.fCurveType;
1349 }
1350
1351private:
1352 /** Iterates over all of the polygon headers in a glyphbuf. */
1353 class GDIPolygonHeaderIter {
1354 public:
skia.committer@gmail.com27e21fe2013-07-28 07:01:03 +00001355 GDIPolygonHeaderIter(const uint8_t* glyphbuf, DWORD total_size)
bungeman@google.com0abbff92013-07-27 20:37:56 +00001356 : fCurPolygon(reinterpret_cast<const TTPOLYGONHEADER*>(glyphbuf))
1357 , fEndPolygon(SkTAddOffset<const TTPOLYGONHEADER>(glyphbuf, total_size))
1358 { }
1359
1360 const TTPOLYGONHEADER* next() {
1361 if (fCurPolygon >= fEndPolygon) {
1362 return NULL;
1363 }
1364 const TTPOLYGONHEADER* thisPolygon = fCurPolygon;
1365 fCurPolygon = SkTAddOffset<const TTPOLYGONHEADER>(fCurPolygon, fCurPolygon->cb);
1366 return thisPolygon;
1367 }
1368 private:
1369 const TTPOLYGONHEADER* fCurPolygon;
1370 const TTPOLYGONHEADER* fEndPolygon;
1371 };
1372
1373 /** Iterates over all of the polygon curves in a polygon header. */
1374 class GDIPolygonCurveIter {
1375 public:
1376 GDIPolygonCurveIter() : fCurCurve(NULL), fEndCurve(NULL) { }
1377
1378 GDIPolygonCurveIter(const TTPOLYGONHEADER* curPolygon)
1379 : fCurCurve(SkTAddOffset<const TTPOLYCURVE>(curPolygon, sizeof(TTPOLYGONHEADER)))
1380 , fEndCurve(SkTAddOffset<const TTPOLYCURVE>(curPolygon, curPolygon->cb))
1381 { }
1382
1383 bool isSet() { return fCurCurve != NULL; }
1384
1385 void set(const TTPOLYGONHEADER* curPolygon) {
1386 fCurCurve = SkTAddOffset<const TTPOLYCURVE>(curPolygon, sizeof(TTPOLYGONHEADER));
1387 fEndCurve = SkTAddOffset<const TTPOLYCURVE>(curPolygon, curPolygon->cb);
1388 }
1389 void set() {
1390 fCurCurve = NULL;
1391 fEndCurve = NULL;
1392 }
1393
1394 const TTPOLYCURVE* next() {
1395 if (fCurCurve >= fEndCurve) {
1396 return NULL;
1397 }
1398 const TTPOLYCURVE* thisCurve = fCurCurve;
1399 fCurCurve = SkTAddOffset<const TTPOLYCURVE>(fCurCurve, size_of_TTPOLYCURVE(*fCurCurve));
1400 return thisCurve;
1401 }
1402 private:
1403 size_t size_of_TTPOLYCURVE(const TTPOLYCURVE& curve) {
1404 return 2*sizeof(WORD) + curve.cpfx*sizeof(POINTFX);
1405 }
1406 const TTPOLYCURVE* fCurCurve;
1407 const TTPOLYCURVE* fEndCurve;
1408 };
1409
1410 /** Iterates over all of the polygon points in a polygon curve. */
1411 class GDIPolygonCurvePointIter {
1412 public:
1413 GDIPolygonCurvePointIter() : fCurveType(0), fCurPoint(NULL), fEndPoint(NULL) { }
1414
1415 GDIPolygonCurvePointIter(const TTPOLYCURVE* curPolygon)
1416 : fCurveType(curPolygon->wType)
1417 , fCurPoint(&curPolygon->apfx[0])
1418 , fEndPoint(&curPolygon->apfx[curPolygon->cpfx])
1419 { }
1420
1421 bool isSet() { return fCurPoint != NULL; }
1422
1423 void set(const TTPOLYCURVE* curPolygon) {
1424 fCurveType = curPolygon->wType;
1425 fCurPoint = &curPolygon->apfx[0];
1426 fEndPoint = &curPolygon->apfx[curPolygon->cpfx];
1427 }
1428 void set() {
1429 fCurPoint = NULL;
1430 fEndPoint = NULL;
1431 }
1432
1433 const POINTFX* next() {
1434 if (fCurPoint >= fEndPoint) {
1435 return NULL;
1436 }
1437 const POINTFX* thisPoint = fCurPoint;
1438 ++fCurPoint;
1439 return thisPoint;
1440 }
1441
1442 WORD fCurveType;
1443 private:
1444 const POINTFX* fCurPoint;
1445 const POINTFX* fEndPoint;
1446 };
1447
1448 GDIPolygonHeaderIter fHeaderIter;
1449 GDIPolygonCurveIter fCurveIter;
1450 GDIPolygonCurvePointIter fPointIter;
1451};
1452
1453static void sk_path_from_gdi_path(SkPath* path, const uint8_t* glyphbuf, DWORD total_size) {
bungeman@google.comd1c7f712013-03-11 18:51:19 +00001454 const uint8_t* cur_glyph = glyphbuf;
1455 const uint8_t* end_glyph = glyphbuf + total_size;
1456
1457 while (cur_glyph < end_glyph) {
1458 const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
1459
1460 const uint8_t* end_poly = cur_glyph + th->cb;
1461 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
1462
1463 path->moveTo(SkFixedToScalar( SkFIXEDToFixed(th->pfxStart.x)),
1464 SkFixedToScalar(-SkFIXEDToFixed(th->pfxStart.y)));
1465
1466 while (cur_poly < end_poly) {
1467 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
1468
1469 if (pc->wType == TT_PRIM_LINE) {
1470 for (uint16_t i = 0; i < pc->cpfx; i++) {
1471 path->lineTo(SkFixedToScalar( SkFIXEDToFixed(pc->apfx[i].x)),
1472 SkFixedToScalar(-SkFIXEDToFixed(pc->apfx[i].y)));
1473 }
1474 }
1475
1476 if (pc->wType == TT_PRIM_QSPLINE) {
1477 for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
1478 POINTFX pnt_b = pc->apfx[u]; // B is always the current point
1479 POINTFX pnt_c = pc->apfx[u+1];
1480
1481 if (u < pc->cpfx - 2) { // If not on last spline, compute C
1482 pnt_c.x = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.x),
1483 SkFIXEDToFixed(pnt_c.x)));
1484 pnt_c.y = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.y),
1485 SkFIXEDToFixed(pnt_c.y)));
1486 }
1487
1488 path->quadTo(SkFixedToScalar( SkFIXEDToFixed(pnt_b.x)),
1489 SkFixedToScalar(-SkFIXEDToFixed(pnt_b.y)),
1490 SkFixedToScalar( SkFIXEDToFixed(pnt_c.x)),
1491 SkFixedToScalar(-SkFIXEDToFixed(pnt_c.y)));
1492 }
1493 }
1494 // Advance past this TTPOLYCURVE.
1495 cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx;
1496 }
1497 cur_glyph += th->cb;
1498 path->close();
reed@google.comac6b9792011-03-11 15:42:51 +00001499 }
reed@google.comac6b9792011-03-11 15:42:51 +00001500}
1501
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001502#define move_next_expected_hinted_point(iter, pElem) do {\
1503 pElem = iter.next(); \
1504 if (NULL == pElem) return false; \
1505} while(0)
1506
1507// It is possible for the hinted and unhinted versions of the same path to have
1508// a different number of points due to GDI's handling of flipped points.
1509// If this is detected, this will return false.
1510static bool sk_path_from_gdi_paths(SkPath* path, const uint8_t* glyphbuf, DWORD total_size,
bungeman@google.com0abbff92013-07-27 20:37:56 +00001511 GDIGlyphbufferPointIter hintedYs) {
1512 const uint8_t* cur_glyph = glyphbuf;
1513 const uint8_t* end_glyph = glyphbuf + total_size;
1514
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001515 POINTFX const * hintedPoint;
1516
bungeman@google.com0abbff92013-07-27 20:37:56 +00001517 while (cur_glyph < end_glyph) {
1518 const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
1519
1520 const uint8_t* end_poly = cur_glyph + th->cb;
1521 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
1522
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001523 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001524 path->moveTo(SkFixedToScalar( SkFIXEDToFixed(th->pfxStart.x)),
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001525 SkFixedToScalar(-SkFIXEDToFixed(hintedPoint->y)));
bungeman@google.com0abbff92013-07-27 20:37:56 +00001526
1527 while (cur_poly < end_poly) {
1528 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
1529
1530 if (pc->wType == TT_PRIM_LINE) {
1531 for (uint16_t i = 0; i < pc->cpfx; i++) {
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001532 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001533 path->lineTo(SkFixedToScalar( SkFIXEDToFixed(pc->apfx[i].x)),
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001534 SkFixedToScalar(-SkFIXEDToFixed(hintedPoint->y)));
bungeman@google.com0abbff92013-07-27 20:37:56 +00001535 }
1536 }
1537
1538 if (pc->wType == TT_PRIM_QSPLINE) {
1539 POINTFX currentPoint = pc->apfx[0];
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001540 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001541 // only take the hinted y if it wasn't flipped
1542 if (hintedYs.currentCurveType() == TT_PRIM_QSPLINE) {
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001543 currentPoint.y = hintedPoint->y;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001544 }
1545 for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
1546 POINTFX pnt_b = currentPoint;//pc->apfx[u]; // B is always the current point
1547 POINTFX pnt_c = pc->apfx[u+1];
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001548 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001549 // only take the hinted y if it wasn't flipped
1550 if (hintedYs.currentCurveType() == TT_PRIM_QSPLINE) {
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001551 pnt_c.y = hintedPoint->y;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001552 }
1553 currentPoint.x = pnt_c.x;
1554 currentPoint.y = pnt_c.y;
1555
1556 if (u < pc->cpfx - 2) { // If not on last spline, compute C
1557 pnt_c.x = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.x),
1558 SkFIXEDToFixed(pnt_c.x)));
1559 pnt_c.y = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.y),
1560 SkFIXEDToFixed(pnt_c.y)));
1561 }
1562
1563 path->quadTo(SkFixedToScalar( SkFIXEDToFixed(pnt_b.x)),
1564 SkFixedToScalar(-SkFIXEDToFixed(pnt_b.y)),
1565 SkFixedToScalar( SkFIXEDToFixed(pnt_c.x)),
1566 SkFixedToScalar(-SkFIXEDToFixed(pnt_c.y)));
1567 }
1568 }
1569 // Advance past this TTPOLYCURVE.
1570 cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx;
1571 }
1572 cur_glyph += th->cb;
1573 path->close();
1574 }
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001575 return true;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001576}
1577
reed@google.com30ddd612013-07-30 17:47:39 +00001578DWORD SkScalerContext_GDI::getGDIGlyphPath(const SkGlyph& glyph, UINT flags,
bungeman@google.com0abbff92013-07-27 20:37:56 +00001579 SkAutoSTMalloc<BUFFERSIZE, uint8_t>* glyphbuf)
1580{
1581 GLYPHMETRICS gm;
1582
herbb69d0e02015-02-25 06:47:06 -08001583 DWORD total_size = GetGlyphOutlineW(fDDC, glyph.getGlyphID(), flags, &gm, BUFFERSIZE, glyphbuf->get(), &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001584 // Sometimes GetGlyphOutlineW returns a number larger than BUFFERSIZE even if BUFFERSIZE > 0.
1585 // It has been verified that this does not involve a buffer overrun.
1586 if (GDI_ERROR == total_size || total_size > BUFFERSIZE) {
1587 // GDI_ERROR because the BUFFERSIZE was too small, or because the data was not accessible.
1588 // When the data is not accessable GetGlyphOutlineW fails rather quickly,
1589 // so just try to get the size. If that fails then ensure the data is accessible.
herbb69d0e02015-02-25 06:47:06 -08001590 total_size = GetGlyphOutlineW(fDDC, glyph.getGlyphID(), flags, &gm, 0, NULL, &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001591 if (GDI_ERROR == total_size) {
1592 LogFontTypeface::EnsureAccessible(this->getTypeface());
herbb69d0e02015-02-25 06:47:06 -08001593 total_size = GetGlyphOutlineW(fDDC, glyph.getGlyphID(), flags, &gm, 0, NULL, &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001594 if (GDI_ERROR == total_size) {
kkinnunenc6cb56f2014-06-24 00:12:27 -07001595 // GetGlyphOutlineW is known to fail for some characters, such as spaces.
1596 // In these cases, just return that the glyph does not have a shape.
bungeman@google.com0abbff92013-07-27 20:37:56 +00001597 return 0;
1598 }
1599 }
1600
1601 glyphbuf->reset(total_size);
1602
herbb69d0e02015-02-25 06:47:06 -08001603 DWORD ret = GetGlyphOutlineW(fDDC, glyph.getGlyphID(), flags, &gm, total_size, glyphbuf->get(), &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001604 if (GDI_ERROR == ret) {
1605 LogFontTypeface::EnsureAccessible(this->getTypeface());
herbb69d0e02015-02-25 06:47:06 -08001606 ret = GetGlyphOutlineW(fDDC, glyph.getGlyphID(), flags, &gm, total_size, glyphbuf->get(), &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001607 if (GDI_ERROR == ret) {
1608 SkASSERT(false);
1609 return 0;
1610 }
1611 }
1612 }
1613 return total_size;
1614}
1615
reed@google.com30ddd612013-07-30 17:47:39 +00001616void SkScalerContext_GDI::generatePath(const SkGlyph& glyph, SkPath* path) {
bungeman@google.com0abbff92013-07-27 20:37:56 +00001617 SkASSERT(&glyph && path);
1618 SkASSERT(fDDC);
1619
1620 path->reset();
1621
1622 // Out of all the fonts on a typical Windows box,
1623 // 25% of glyphs require more than 2KB.
1624 // 1% of glyphs require more than 4KB.
1625 // 0.01% of glyphs require more than 8KB.
1626 // 8KB is less than 1% of the normal 1MB stack on Windows.
1627 // Note that some web fonts glyphs require more than 20KB.
1628 //static const DWORD BUFFERSIZE = (1 << 13);
1629
1630 //GDI only uses hinted outlines when axis aligned.
1631 UINT format = GGO_NATIVE | GGO_GLYPH_INDEX;
1632 if (fRec.getHinting() == SkPaint::kNo_Hinting || fRec.getHinting() == SkPaint::kSlight_Hinting){
1633 format |= GGO_UNHINTED;
1634 }
1635 SkAutoSTMalloc<BUFFERSIZE, uint8_t> glyphbuf(BUFFERSIZE);
1636 DWORD total_size = getGDIGlyphPath(glyph, format, &glyphbuf);
1637 if (0 == total_size) {
1638 return;
1639 }
1640
1641 if (fRec.getHinting() != SkPaint::kSlight_Hinting) {
1642 sk_path_from_gdi_path(path, glyphbuf, total_size);
1643 } else {
1644 //GDI only uses hinted outlines when axis aligned.
1645 UINT format = GGO_NATIVE | GGO_GLYPH_INDEX;
1646
1647 SkAutoSTMalloc<BUFFERSIZE, uint8_t> hintedGlyphbuf(BUFFERSIZE);
1648 DWORD hinted_total_size = getGDIGlyphPath(glyph, format, &hintedGlyphbuf);
1649 if (0 == hinted_total_size) {
1650 return;
1651 }
1652
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001653 if (!sk_path_from_gdi_paths(path, glyphbuf, total_size,
1654 GDIGlyphbufferPointIter(hintedGlyphbuf, hinted_total_size)))
1655 {
1656 path->reset();
1657 sk_path_from_gdi_path(path, glyphbuf, total_size);
1658 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001659 }
1660}
1661
reed@google.com484f5bc2013-04-24 19:14:56 +00001662static void logfont_for_name(const char* familyName, LOGFONT* lf) {
1663 sk_bzero(lf, sizeof(LOGFONT));
bungeman@google.come70f7982012-06-01 19:38:19 +00001664#ifdef UNICODE
reed@google.com484f5bc2013-04-24 19:14:56 +00001665 // Get the buffer size needed first.
1666 size_t str_len = ::MultiByteToWideChar(CP_UTF8, 0, familyName,
1667 -1, NULL, 0);
1668 // Allocate a buffer (str_len already has terminating null
1669 // accounted for).
1670 wchar_t *wideFamilyName = new wchar_t[str_len];
1671 // Now actually convert the string.
1672 ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1,
1673 wideFamilyName, str_len);
1674 ::wcsncpy(lf->lfFaceName, wideFamilyName, LF_FACESIZE - 1);
1675 delete [] wideFamilyName;
1676 lf->lfFaceName[LF_FACESIZE-1] = L'\0';
bungeman@google.come70f7982012-06-01 19:38:19 +00001677#else
reed@google.com484f5bc2013-04-24 19:14:56 +00001678 ::strncpy(lf->lfFaceName, familyName, LF_FACESIZE - 1);
1679 lf->lfFaceName[LF_FACESIZE - 1] = '\0';
bungeman@google.come70f7982012-06-01 19:38:19 +00001680#endif
1681}
1682
bungemanb374d6a2014-09-17 07:48:59 -07001683void LogFontTypeface::onGetFamilyName(SkString* familyName) const {
bungeman@google.com7103f182012-10-31 20:53:49 +00001684 // Get the actual name of the typeface. The logfont may not know this.
reed@google.com5526ede2013-03-25 13:03:37 +00001685 HFONT font = CreateFontIndirect(&fLogFont);
bungeman@google.com7103f182012-10-31 20:53:49 +00001686
1687 HDC deviceContext = ::CreateCompatibleDC(NULL);
1688 HFONT savefont = (HFONT)SelectObject(deviceContext, font);
1689
bungemanb374d6a2014-09-17 07:48:59 -07001690 dcfontname_to_skstring(deviceContext, fLogFont, familyName);
bungeman@google.com7103f182012-10-31 20:53:49 +00001691
1692 if (deviceContext) {
1693 ::SelectObject(deviceContext, savefont);
1694 ::DeleteDC(deviceContext);
1695 }
1696 if (font) {
1697 ::DeleteObject(font);
1698 }
bungemanb374d6a2014-09-17 07:48:59 -07001699}
bungeman@google.com7103f182012-10-31 20:53:49 +00001700
bungemanb374d6a2014-09-17 07:48:59 -07001701void LogFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc,
1702 bool* isLocalStream) const {
1703 SkString familyName;
1704 this->onGetFamilyName(&familyName);
reed@google.com5526ede2013-03-25 13:03:37 +00001705 desc->setFamilyName(familyName.c_str());
1706 *isLocalStream = this->fSerializeAsStream;
reed@google.comac6b9792011-03-11 15:42:51 +00001707}
1708
1709static bool getWidthAdvance(HDC hdc, int gId, int16_t* advance) {
1710 // Initialize the MAT2 structure to the identify transformation matrix.
1711 static const MAT2 mat2 = {SkScalarToFIXED(1), SkScalarToFIXED(0),
1712 SkScalarToFIXED(0), SkScalarToFIXED(1)};
1713 int flags = GGO_METRICS | GGO_GLYPH_INDEX;
1714 GLYPHMETRICS gm;
1715 if (GDI_ERROR == GetGlyphOutline(hdc, gId, flags, &gm, 0, NULL, &mat2)) {
1716 return false;
1717 }
1718 SkASSERT(advance);
1719 *advance = gm.gmCellIncX;
1720 return true;
1721}
1722
reed@google.com2689f612013-03-20 20:01:47 +00001723SkAdvancedTypefaceMetrics* LogFontTypeface::onGetAdvancedTypefaceMetrics(
vandebo@chromium.org37ad8fb2011-08-18 02:38:50 +00001724 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
1725 const uint32_t* glyphIDs,
reed@google.com2689f612013-03-20 20:01:47 +00001726 uint32_t glyphIDsCount) const {
1727 LOGFONT lf = fLogFont;
reed@google.comac6b9792011-03-11 15:42:51 +00001728 SkAdvancedTypefaceMetrics* info = NULL;
1729
1730 HDC hdc = CreateCompatibleDC(NULL);
1731 HFONT font = CreateFontIndirect(&lf);
1732 HFONT savefont = (HFONT)SelectObject(hdc, font);
1733 HFONT designFont = NULL;
1734
reed@google.com05b6f3a2011-11-28 15:30:28 +00001735 const char stem_chars[] = {'i', 'I', '!', '1'};
1736 int16_t min_width;
1737 unsigned glyphCount;
1738
reed@google.comac6b9792011-03-11 15:42:51 +00001739 // To request design units, create a logical font whose height is specified
1740 // as unitsPerEm.
1741 OUTLINETEXTMETRIC otm;
bungeman@google.com39698b12011-11-15 22:26:41 +00001742 unsigned int otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
1743 if (0 == otmRet) {
reed@google.com055180c2013-03-21 18:46:35 +00001744 call_ensure_accessible(lf);
bungeman@google.com39698b12011-11-15 22:26:41 +00001745 otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
1746 }
1747 if (!otmRet || !GetTextFace(hdc, LF_FACESIZE, lf.lfFaceName)) {
reed@google.comac6b9792011-03-11 15:42:51 +00001748 goto Error;
1749 }
1750 lf.lfHeight = -SkToS32(otm.otmEMSquare);
1751 designFont = CreateFontIndirect(&lf);
1752 SelectObject(hdc, designFont);
1753 if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) {
1754 goto Error;
1755 }
bungeman@google.com7bdd6142013-07-15 19:42:57 +00001756 glyphCount = calculateGlyphCount(hdc, fLogFont);
reed@google.comac6b9792011-03-11 15:42:51 +00001757
1758 info = new SkAdvancedTypefaceMetrics;
1759 info->fEmSize = otm.otmEMSquare;
reed@google.comac6b9792011-03-11 15:42:51 +00001760 info->fLastGlyphID = SkToU16(glyphCount - 1);
1761 info->fStyle = 0;
bungeman@google.com7103f182012-10-31 20:53:49 +00001762 tchar_to_skstring(lf.lfFaceName, &info->fFontName);
vandebo0f9bad02014-06-19 11:05:39 -07001763 info->fFlags = SkAdvancedTypefaceMetrics::kEmpty_FontFlag;
1764 // If bit 1 is set, the font may not be embedded in a document.
1765 // If bit 1 is clear, the font can be embedded.
1766 // If bit 2 is set, the embedding is read-only.
1767 if (otm.otmfsType & 0x1) {
1768 info->fFlags = SkTBitOr<SkAdvancedTypefaceMetrics::FontFlags>(
1769 info->fFlags,
1770 SkAdvancedTypefaceMetrics::kNotEmbeddable_FontFlag);
1771 }
reed@google.comac6b9792011-03-11 15:42:51 +00001772
vandebo@chromium.org6744d492011-05-09 18:13:47 +00001773 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
1774 populate_glyph_to_unicode(hdc, glyphCount, &(info->fGlyphToUnicode));
1775 }
1776
vandebo@chromium.orgd41e70d2012-03-08 19:41:01 +00001777 if (glyphCount > 0 &&
1778 (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE)) {
reed@google.comac6b9792011-03-11 15:42:51 +00001779 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
1780 } else {
1781 info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
1782 info->fItalicAngle = 0;
1783 info->fAscent = 0;
1784 info->fDescent = 0;
1785 info->fStemV = 0;
1786 info->fCapHeight = 0;
1787 info->fBBox = SkIRect::MakeEmpty();
edisonn@google.com390c6d72013-04-06 20:26:15 +00001788 goto ReturnInfo;
reed@google.comac6b9792011-03-11 15:42:51 +00001789 }
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +00001790
reed@google.comac6b9792011-03-11 15:42:51 +00001791 // If this bit is clear the font is a fixed pitch font.
1792 if (!(otm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
1793 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1794 }
1795 if (otm.otmTextMetrics.tmItalic) {
1796 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1797 }
reed@google.comac6b9792011-03-11 15:42:51 +00001798 if (otm.otmTextMetrics.tmPitchAndFamily & FF_ROMAN) {
1799 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1800 } else if (otm.otmTextMetrics.tmPitchAndFamily & FF_SCRIPT) {
1801 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1802 }
1803
1804 // The main italic angle of the font, in tenths of a degree counterclockwise
1805 // from vertical.
1806 info->fItalicAngle = otm.otmItalicAngle / 10;
1807 info->fAscent = SkToS16(otm.otmTextMetrics.tmAscent);
1808 info->fDescent = SkToS16(-otm.otmTextMetrics.tmDescent);
1809 // TODO(ctguil): Use alternate cap height calculation.
1810 // MSDN says otmsCapEmHeight is not support but it is returning a value on
1811 // my Win7 box.
1812 info->fCapHeight = otm.otmsCapEmHeight;
1813 info->fBBox =
1814 SkIRect::MakeLTRB(otm.otmrcFontBox.left, otm.otmrcFontBox.top,
1815 otm.otmrcFontBox.right, otm.otmrcFontBox.bottom);
1816
1817 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1818 // This probably isn't very good with an italic font.
reed@google.com05b6f3a2011-11-28 15:30:28 +00001819 min_width = SHRT_MAX;
reed@google.comac6b9792011-03-11 15:42:51 +00001820 info->fStemV = 0;
reed@google.comac6b9792011-03-11 15:42:51 +00001821 for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {
1822 ABC abcWidths;
1823 if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) {
1824 int16_t width = abcWidths.abcB;
1825 if (width > 0 && width < min_width) {
1826 min_width = width;
1827 info->fStemV = min_width;
1828 }
1829 }
1830 }
1831
vandebo0f9bad02014-06-19 11:05:39 -07001832 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +00001833 if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
1834 appendRange(&info->fGlyphWidths, 0);
1835 info->fGlyphWidths->fAdvance.append(1, &min_width);
1836 finishRange(info->fGlyphWidths.get(), 0,
1837 SkAdvancedTypefaceMetrics::WidthRange::kDefault);
1838 } else {
1839 info->fGlyphWidths.reset(
vandebo@chromium.org37ad8fb2011-08-18 02:38:50 +00001840 getAdvanceData(hdc,
1841 glyphCount,
1842 glyphIDs,
1843 glyphIDsCount,
1844 &getWidthAdvance));
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +00001845 }
reed@google.comac6b9792011-03-11 15:42:51 +00001846 }
1847
1848Error:
edisonn@google.com390c6d72013-04-06 20:26:15 +00001849ReturnInfo:
reed@google.comac6b9792011-03-11 15:42:51 +00001850 SelectObject(hdc, savefont);
1851 DeleteObject(designFont);
1852 DeleteObject(font);
1853 DeleteDC(hdc);
1854
1855 return info;
1856}
1857
bungeman@google.coma5501992012-05-18 19:06:41 +00001858//Dummy representation of a Base64 encoded GUID from create_unique_font_name.
1859#define BASE64_GUID_ID "XXXXXXXXXXXXXXXXXXXXXXXX"
1860//Length of GUID representation from create_id, including NULL terminator.
1861#define BASE64_GUID_ID_LEN SK_ARRAY_COUNT(BASE64_GUID_ID)
reed@google.comac6b9792011-03-11 15:42:51 +00001862
bungeman@google.coma5501992012-05-18 19:06:41 +00001863SK_COMPILE_ASSERT(BASE64_GUID_ID_LEN < LF_FACESIZE, GUID_longer_than_facesize);
1864
1865/**
1866 NameID 6 Postscript names cannot have the character '/'.
1867 It would be easier to hex encode the GUID, but that is 32 bytes,
1868 and many systems have issues with names longer than 28 bytes.
1869 The following need not be any standard base64 encoding.
1870 The encoded value is never decoded.
1871*/
rmistry@google.comd6176b02012-08-23 18:14:13 +00001872static const char postscript_safe_base64_encode[] =
bungeman@google.coma5501992012-05-18 19:06:41 +00001873 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1874 "abcdefghijklmnopqrstuvwxyz"
1875 "0123456789-_=";
1876
1877/**
1878 Formats a GUID into Base64 and places it into buffer.
1879 buffer should have space for at least BASE64_GUID_ID_LEN characters.
1880 The string will always be null terminated.
1881 XXXXXXXXXXXXXXXXXXXXXXXX0
1882 */
1883static void format_guid_b64(const GUID& guid, char* buffer, size_t bufferSize) {
1884 SkASSERT(bufferSize >= BASE64_GUID_ID_LEN);
1885 size_t written = SkBase64::Encode(&guid, sizeof(guid), buffer, postscript_safe_base64_encode);
1886 SkASSERT(written < LF_FACESIZE);
1887 buffer[written] = '\0';
1888}
1889
1890/**
1891 Creates a Base64 encoded GUID and places it into buffer.
1892 buffer should have space for at least BASE64_GUID_ID_LEN characters.
1893 The string will always be null terminated.
1894 XXXXXXXXXXXXXXXXXXXXXXXX0
1895 */
1896static HRESULT create_unique_font_name(char* buffer, size_t bufferSize) {
1897 GUID guid = {};
1898 if (FAILED(CoCreateGuid(&guid))) {
1899 return E_UNEXPECTED;
1900 }
1901 format_guid_b64(guid, buffer, bufferSize);
1902
1903 return S_OK;
1904}
1905
1906/**
1907 Introduces a font to GDI. On failure will return NULL. The returned handle
1908 should eventually be passed to RemoveFontMemResourceEx.
1909*/
1910static HANDLE activate_font(SkData* fontData) {
1911 DWORD numFonts = 0;
1912 //AddFontMemResourceEx just copies the data, but does not specify const.
1913 HANDLE fontHandle = AddFontMemResourceEx(const_cast<void*>(fontData->data()),
bungeman@google.com4b18f572013-07-22 15:21:23 +00001914 static_cast<DWORD>(fontData->size()),
bungeman@google.coma5501992012-05-18 19:06:41 +00001915 0,
1916 &numFonts);
1917
1918 if (fontHandle != NULL && numFonts < 1) {
1919 RemoveFontMemResourceEx(fontHandle);
1920 return NULL;
1921 }
1922
1923 return fontHandle;
1924}
1925
scroggoa1193e42015-01-21 12:09:53 -08001926// Does not affect ownership of stream.
bungeman5f213d92015-01-27 05:39:10 -08001927static SkTypeface* create_from_stream(SkStreamAsset* 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
bungeman5f213d92015-01-27 05:39:10 -08001955SkStreamAsset* LogFontTypeface::onOpenStream(int* ttcIndex) const {
reed@google.com0042b9c2013-03-21 20:16:04 +00001956 *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
mtklein72c9faa2015-01-09 10:06:39 -08002392 int count() SK_OVERRIDE {
reed@google.com484f5bc2013-04-24 19:14:56 +00002393 return fArray.count();
2394 }
2395
mtklein72c9faa2015-01-09 10:06:39 -08002396 void getStyle(int index, SkFontStyle* fs, SkString* styleName) SK_OVERRIDE {
reed@google.com484f5bc2013-04-24 19:14:56 +00002397 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
mtklein72c9faa2015-01-09 10:06:39 -08002413 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
mtklein72c9faa2015-01-09 10:06:39 -08002417 SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE {
reed@google.com484f5bc2013-04-24 19:14:56 +00002418 // 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:
mtklein72c9faa2015-01-09 10:06:39 -08002439 int onCountFamilies() const SK_OVERRIDE {
reed@google.com484f5bc2013-04-24 19:14:56 +00002440 return fLogFontArray.count();
2441 }
2442
mtklein72c9faa2015-01-09 10:06:39 -08002443 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
mtklein72c9faa2015-01-09 10:06:39 -08002448 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
mtklein72c9faa2015-01-09 10:06:39 -08002453 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
bungeman5f213d92015-01-27 05:39:10 -08002483 SkTypeface* onCreateFromStream(SkStreamAsset* bareStream, int ttcIndex) const SK_OVERRIDE {
2484 SkAutoTDelete<SkStreamAsset> stream(bareStream);
reed@google.com437eea12013-04-25 20:40:02 +00002485 return create_from_stream(stream);
reed@google.com484f5bc2013-04-24 19:14:56 +00002486 }
reed@google.com437eea12013-04-25 20:40:02 +00002487
mtklein72c9faa2015-01-09 10:06:39 -08002488 SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const SK_OVERRIDE {
reed@google.com437eea12013-04-25 20:40:02 +00002489 // could be in base impl
scroggoa1193e42015-01-21 12:09:53 -08002490 return this->createFromStream(SkNEW_ARGS(SkMemoryStream, (data)));
reed@google.com484f5bc2013-04-24 19:14:56 +00002491 }
reed@google.com437eea12013-04-25 20:40:02 +00002492
mtklein72c9faa2015-01-09 10:06:39 -08002493 SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const SK_OVERRIDE {
reed@google.com437eea12013-04-25 20:40:02 +00002494 // could be in base impl
scroggoa1193e42015-01-21 12:09:53 -08002495 return this->createFromStream(SkStream::NewFromFile(path));
reed@google.com484f5bc2013-04-24 19:14:56 +00002496 }
2497
reed@google.com30ddd612013-07-30 17:47:39 +00002498 virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002499 unsigned styleBits) const SK_OVERRIDE {
bungeman@google.comf7159bb2013-11-20 15:11:05 +00002500 LOGFONT lf;
2501 if (NULL == familyName) {
2502 lf = get_default_font();
2503 } else {
2504 logfont_for_name(familyName, &lf);
2505 }
bungemana4c4a2d2014-10-20 13:33:19 -07002506
2507 SkTypeface::Style style = (SkTypeface::Style)styleBits;
2508 lf.lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL;
2509 lf.lfItalic = ((style & SkTypeface::kItalic) != 0);
bungeman@google.comf7159bb2013-11-20 15:11:05 +00002510 return SkCreateTypefaceFromLOGFONT(lf);
reed@google.com30ddd612013-07-30 17:47:39 +00002511 }
2512
reed@google.com484f5bc2013-04-24 19:14:56 +00002513private:
reed@google.coma65a6812013-05-02 19:47:24 +00002514 SkTDArray<ENUMLOGFONTEX> fLogFontArray;
reed@google.com484f5bc2013-04-24 19:14:56 +00002515};
reed@google.com070da5e2013-03-27 20:01:49 +00002516
reed@google.com30ddd612013-07-30 17:47:39 +00002517///////////////////////////////////////////////////////////////////////////////
2518
bungeman@google.combfc6cc42013-08-21 15:20:43 +00002519SkFontMgr* SkFontMgr_New_GDI() {
reed@google.com484f5bc2013-04-24 19:14:56 +00002520 return SkNEW(SkFontMgrGDI);
reed@google.com070da5e2013-03-27 20:01:49 +00002521}