blob: 7ebfd1ea3a7ec7da767a95fca0187ce91d3c1a04 [file] [log] [blame]
reed@google.comac6b9792011-03-11 15:42:51 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2006 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@google.comac6b9792011-03-11 15:42:51 +00006 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkTypes.h"
Mike Klein8f11d4d2018-01-24 12:42:55 -05009#if defined(SK_BUILD_FOR_WIN)
mtklein1ee76512015-11-02 10:20:27 -080010
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkData.h"
12#include "include/core/SkFontMetrics.h"
13#include "include/core/SkPath.h"
14#include "include/core/SkStream.h"
15#include "include/core/SkString.h"
16#include "include/ports/SkTypeface_win.h"
17#include "include/private/SkColorData.h"
18#include "include/private/SkMacros.h"
19#include "include/private/SkOnce.h"
20#include "include/private/SkTemplates.h"
21#include "include/private/SkTo.h"
22#include "include/utils/SkBase64.h"
23#include "src/core/SkAdvancedTypefaceMetrics.h"
24#include "src/core/SkDescriptor.h"
25#include "src/core/SkFontDescriptor.h"
26#include "src/core/SkGlyph.h"
Ben Wagnerab6eefe2019-05-20 11:02:49 -040027#include "src/core/SkLeanWindows.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050028#include "src/core/SkMakeUnique.h"
29#include "src/core/SkMaskGamma.h"
30#include "src/core/SkTypefaceCache.h"
31#include "src/core/SkUtils.h"
32#include "src/sfnt/SkOTTable_OS_2.h"
33#include "src/sfnt/SkOTTable_maxp.h"
34#include "src/sfnt/SkOTTable_name.h"
35#include "src/sfnt/SkOTUtils.h"
36#include "src/sfnt/SkSFNTHeader.h"
37#include "src/utils/SkMatrix22.h"
38#include "src/utils/win/SkHRESULT.h"
reed@google.comac6b9792011-03-11 15:42:51 +000039
bungeman@google.coma5501992012-05-18 19:06:41 +000040#include <tchar.h>
41#include <usp10.h>
42#include <objbase.h>
reed@google.comac6b9792011-03-11 15:42:51 +000043
reed@google.comf210f502013-03-20 14:15:52 +000044static void (*gEnsureLOGFONTAccessibleProc)(const LOGFONT&);
45
46void SkTypeface_SetEnsureLOGFONTAccessibleProc(void (*proc)(const LOGFONT&)) {
47 gEnsureLOGFONTAccessibleProc = proc;
48}
49
reed@google.com055180c2013-03-21 18:46:35 +000050static void call_ensure_accessible(const LOGFONT& lf) {
51 if (gEnsureLOGFONTAccessibleProc) {
52 gEnsureLOGFONTAccessibleProc(lf);
53 }
54}
55
56///////////////////////////////////////////////////////////////////////////////
57
reed@google.com6f5df482011-09-28 20:33:24 +000058// always packed xxRRGGBB
59typedef uint32_t SkGdiRGB;
60
reed@google.coma767fa02011-08-05 21:40:26 +000061// define this in your Makefile or .gyp to enforce AA requests
62// which GDI ignores at small sizes. This flag guarantees AA
63// for rotated text, regardless of GDI's notions.
64//#define SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
65
Ben Wagner3e45a2f2017-11-01 11:47:39 -040066static bool isLCD(const SkScalerContextRec& rec) {
reedd54d3fc2014-11-13 14:39:58 -080067 return SkMask::kLCD16_Format == rec.fMaskFormat;
reed@google.com82a34d82011-07-26 19:33:08 +000068}
69
reed@google.coma767fa02011-08-05 21:40:26 +000070static bool bothZero(SkScalar a, SkScalar b) {
71 return 0 == a && 0 == b;
72}
73
74// returns false if there is any non-90-rotation or skew
Ben Wagner3e45a2f2017-11-01 11:47:39 -040075static bool isAxisAligned(const SkScalerContextRec& rec) {
reed@google.coma767fa02011-08-05 21:40:26 +000076 return 0 == rec.fPreSkewX &&
77 (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
78 bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
79}
80
Ben Wagner3e45a2f2017-11-01 11:47:39 -040081static bool needToRenderWithSkia(const SkScalerContextRec& rec) {
reed@google.coma767fa02011-08-05 21:40:26 +000082#ifdef SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
83 // What we really want to catch is when GDI will ignore the AA request and give
84 // us BW instead. Smallish rotated text is one heuristic, so this code is just
85 // an approximation. We shouldn't need to do this for larger sizes, but at those
86 // sizes, the quality difference gets less and less between our general
87 // scanconverter and GDI's.
88 if (SkMask::kA8_Format == rec.fMaskFormat && !isAxisAligned(rec)) {
89 return true;
90 }
91#endif
Ben Wagner5785e4a2019-05-07 16:50:29 -040092 return rec.getHinting() == SkFontHinting::kNone || rec.getHinting() == SkFontHinting::kSlight;
reed@google.coma767fa02011-08-05 21:40:26 +000093}
94
reed@google.coma65a6812013-05-02 19:47:24 +000095static void tchar_to_skstring(const TCHAR t[], SkString* s) {
reed@google.com484f5bc2013-04-24 19:14:56 +000096#ifdef UNICODE
halcanary96fcdcc2015-08-27 07:41:13 -070097 size_t sSize = WideCharToMultiByte(CP_UTF8, 0, t, -1, nullptr, 0, nullptr, nullptr);
reed@google.com484f5bc2013-04-24 19:14:56 +000098 s->resize(sSize);
halcanary96fcdcc2015-08-27 07:41:13 -070099 WideCharToMultiByte(CP_UTF8, 0, t, -1, s->writable_str(), sSize, nullptr, nullptr);
reed@google.com484f5bc2013-04-24 19:14:56 +0000100#else
101 s->set(t);
102#endif
103}
104
bungeman@google.coma9802692013-08-07 02:45:25 +0000105static void dcfontname_to_skstring(HDC deviceContext, const LOGFONT& lf, SkString* familyName) {
106 int fontNameLen; //length of fontName in TCHARS.
halcanary96fcdcc2015-08-27 07:41:13 -0700107 if (0 == (fontNameLen = GetTextFace(deviceContext, 0, nullptr))) {
bungeman@google.coma9802692013-08-07 02:45:25 +0000108 call_ensure_accessible(lf);
halcanary96fcdcc2015-08-27 07:41:13 -0700109 if (0 == (fontNameLen = GetTextFace(deviceContext, 0, nullptr))) {
bungeman@google.coma9802692013-08-07 02:45:25 +0000110 fontNameLen = 0;
111 }
112 }
113
114 SkAutoSTArray<LF_FULLFACESIZE, TCHAR> fontName(fontNameLen+1);
115 if (0 == GetTextFace(deviceContext, fontNameLen, fontName.get())) {
116 call_ensure_accessible(lf);
117 if (0 == GetTextFace(deviceContext, fontNameLen, fontName.get())) {
118 fontName[0] = 0;
119 }
120 }
121
122 tchar_to_skstring(fontName.get(), familyName);
123}
124
reed@google.comac6b9792011-03-11 15:42:51 +0000125static void make_canonical(LOGFONT* lf) {
bungeman@google.com53cbb0b2013-09-08 19:36:58 +0000126 lf->lfHeight = -64;
bungeman59f093d2016-03-22 10:59:09 -0700127 lf->lfWidth = 0; // lfWidth is related to lfHeight, not to the OS/2::usWidthClass.
reed@google.com59d2f632011-05-02 19:36:59 +0000128 lf->lfQuality = CLEARTYPE_QUALITY;//PROOF_QUALITY;
129 lf->lfCharSet = DEFAULT_CHARSET;
reed@google.com82a34d82011-07-26 19:33:08 +0000130// lf->lfClipPrecision = 64;
reed@google.com59d2f632011-05-02 19:36:59 +0000131}
132
bungemana4c4a2d2014-10-20 13:33:19 -0700133static SkFontStyle get_style(const LOGFONT& lf) {
134 return SkFontStyle(lf.lfWeight,
bungeman59f093d2016-03-22 10:59:09 -0700135 SkFontStyle::kNormal_Width,
bungemana4c4a2d2014-10-20 13:33:19 -0700136 lf.lfItalic ? SkFontStyle::kItalic_Slant : SkFontStyle::kUpright_Slant);
reed@google.comac6b9792011-03-11 15:42:51 +0000137}
138
139static inline FIXED SkFixedToFIXED(SkFixed x) {
140 return *(FIXED*)(&x);
141}
bungeman@google.coma0319f62012-04-18 15:40:50 +0000142static inline SkFixed SkFIXEDToFixed(FIXED x) {
143 return *(SkFixed*)(&x);
144}
reed@google.comac6b9792011-03-11 15:42:51 +0000145
146static inline FIXED SkScalarToFIXED(SkScalar x) {
147 return SkFixedToFIXED(SkScalarToFixed(x));
148}
149
bungeman@google.com4732df62014-01-23 15:22:42 +0000150static inline SkScalar SkFIXEDToScalar(FIXED x) {
151 return SkFixedToScalar(SkFIXEDToFixed(x));
152}
153
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000154static unsigned calculateGlyphCount(HDC hdc, const LOGFONT& lf) {
155 TEXTMETRIC textMetric;
156 if (0 == GetTextMetrics(hdc, &textMetric)) {
157 textMetric.tmPitchAndFamily = TMPF_VECTOR;
158 call_ensure_accessible(lf);
159 GetTextMetrics(hdc, &textMetric);
160 }
161
162 if (!(textMetric.tmPitchAndFamily & TMPF_VECTOR)) {
163 return textMetric.tmLastChar;
164 }
165
reed@google.comac6b9792011-03-11 15:42:51 +0000166 // The 'maxp' table stores the number of glyphs at offset 4, in 2 bytes.
reed@google.comac6b9792011-03-11 15:42:51 +0000167 uint16_t glyphs;
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000168 if (GDI_ERROR != GetFontData(hdc, SkOTTableMaximumProfile::TAG, 4, &glyphs, sizeof(glyphs))) {
reed@google.comac6b9792011-03-11 15:42:51 +0000169 return SkEndian_SwapBE16(glyphs);
170 }
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000171
reed@google.comac6b9792011-03-11 15:42:51 +0000172 // Binary search for glyph count.
173 static const MAT2 mat2 = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
Ben Wagnerb0897652018-06-15 15:37:57 +0000174 int32_t max = UINT16_MAX + 1;
reed@google.comac6b9792011-03-11 15:42:51 +0000175 int32_t min = 0;
176 GLYPHMETRICS gm;
177 while (min < max) {
178 int32_t mid = min + ((max - min) / 2);
179 if (GetGlyphOutlineW(hdc, mid, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0,
halcanary96fcdcc2015-08-27 07:41:13 -0700180 nullptr, &mat2) == GDI_ERROR) {
reed@google.comac6b9792011-03-11 15:42:51 +0000181 max = mid;
182 } else {
183 min = mid + 1;
184 }
185 }
186 SkASSERT(min == max);
187 return min;
188}
189
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000190static unsigned calculateUPEM(HDC hdc, const LOGFONT& lf) {
191 TEXTMETRIC textMetric;
192 if (0 == GetTextMetrics(hdc, &textMetric)) {
193 textMetric.tmPitchAndFamily = TMPF_VECTOR;
194 call_ensure_accessible(lf);
195 GetTextMetrics(hdc, &textMetric);
196 }
197
198 if (!(textMetric.tmPitchAndFamily & TMPF_VECTOR)) {
199 return textMetric.tmMaxCharWidth;
200 }
201
202 OUTLINETEXTMETRIC otm;
203 unsigned int otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
204 if (0 == otmRet) {
205 call_ensure_accessible(lf);
206 otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
207 }
208
209 return (0 == otmRet) ? 0 : otm.otmEMSquare;
210}
211
reed@google.comac6b9792011-03-11 15:42:51 +0000212class LogFontTypeface : public SkTypeface {
reed@google.comac6b9792011-03-11 15:42:51 +0000213public:
bungemana4c4a2d2014-10-20 13:33:19 -0700214 LogFontTypeface(const SkFontStyle& style, const LOGFONT& lf, bool serializeAsStream)
bungemane3aea102016-07-13 05:16:58 -0700215 : SkTypeface(style, false)
bungemana4c4a2d2014-10-20 13:33:19 -0700216 , fLogFont(lf)
217 , fSerializeAsStream(serializeAsStream)
218 {
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000219 HFONT font = CreateFontIndirect(&lf);
220
halcanary96fcdcc2015-08-27 07:41:13 -0700221 HDC deviceContext = ::CreateCompatibleDC(nullptr);
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000222 HFONT savefont = (HFONT)SelectObject(deviceContext, font);
223
224 TEXTMETRIC textMetric;
225 if (0 == GetTextMetrics(deviceContext, &textMetric)) {
reed@google.com055180c2013-03-21 18:46:35 +0000226 call_ensure_accessible(lf);
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000227 if (0 == GetTextMetrics(deviceContext, &textMetric)) {
228 textMetric.tmPitchAndFamily = TMPF_TRUETYPE;
229 }
230 }
231 if (deviceContext) {
232 ::SelectObject(deviceContext, savefont);
233 ::DeleteDC(deviceContext);
234 }
235 if (font) {
236 ::DeleteObject(font);
237 }
238
bungeman@google.comfe747652013-03-25 19:36:11 +0000239 // The fixed pitch bit is set if the font is *not* fixed pitch.
240 this->setIsFixedPitch((textMetric.tmPitchAndFamily & TMPF_FIXED_PITCH) == 0);
bungeman6e45bda2016-07-25 15:11:49 -0700241 this->setFontStyle(SkFontStyle(textMetric.tmWeight, style.width(), style.slant()));
bungeman@google.comfe747652013-03-25 19:36:11 +0000242
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000243 // Used a logfont on a memory context, should never get a device font.
244 // Therefore all TMPF_DEVICE will be PostScript (cubic) fonts.
bungeman6e45bda2016-07-25 15:11:49 -0700245 // If the font has cubic outlines, it will not be rendered with ClearType.
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000246 fCanBeLCD = !((textMetric.tmPitchAndFamily & TMPF_VECTOR) &&
247 (textMetric.tmPitchAndFamily & TMPF_DEVICE));
248 }
reed@google.comac6b9792011-03-11 15:42:51 +0000249
reed@google.com59d2f632011-05-02 19:36:59 +0000250 LOGFONT fLogFont;
bungeman@google.come70f7982012-06-01 19:38:19 +0000251 bool fSerializeAsStream;
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000252 bool fCanBeLCD;
reed@google.comac6b9792011-03-11 15:42:51 +0000253
Ben Wagner68efb212019-03-04 16:15:40 -0500254 static sk_sp<LogFontTypeface> Make(const LOGFONT& lf) {
255 return sk_sp<LogFontTypeface>(new LogFontTypeface(get_style(lf), lf, false));
reed@google.comac6b9792011-03-11 15:42:51 +0000256 }
reed@google.com0da48612013-03-19 16:06:52 +0000257
reed@google.com055180c2013-03-21 18:46:35 +0000258 static void EnsureAccessible(const SkTypeface* face) {
259 call_ensure_accessible(static_cast<const LogFontTypeface*>(face)->fLogFont);
260 }
261
reed@google.com0da48612013-03-19 16:06:52 +0000262protected:
Ben Wagner4212a7d2019-02-25 14:27:46 -0500263 std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const override;
Bruce Wang536ad2c2018-06-25 11:37:25 -0400264 sk_sp<SkTypeface> onMakeClone(const SkFontArguments& args) const override;
reeda9322c22016-04-12 06:47:05 -0700265 SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
266 const SkDescriptor*) const override;
mtklein36352bf2015-03-25 18:17:31 -0700267 void onFilterRec(SkScalerContextRec*) const override;
Hal Canary46cc3da2018-05-09 11:50:34 -0400268 void getGlyphToUnicodeMap(SkUnichar*) const override;
Hal Canary209e4b12017-05-04 14:23:55 -0400269 std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
mtklein36352bf2015-03-25 18:17:31 -0700270 void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
Mike Reed64670cb2019-04-16 11:37:38 -0700271 void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override;
mtklein36352bf2015-03-25 18:17:31 -0700272 int onCountGlyphs() const override;
Ben Wagner2c2240f2019-04-17 16:04:30 -0400273 void getPostScriptGlyphNames(SkString*) const override;
mtklein36352bf2015-03-25 18:17:31 -0700274 int onGetUPEM() const override;
275 void onGetFamilyName(SkString* familyName) const override;
276 SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
Ben Wagnerfc497342017-02-24 11:15:26 -0500277 int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],
278 int coordinateCount) const override
279 {
280 return -1;
281 }
Ben Wagnere346b1e2018-06-26 11:22:37 -0400282 int onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[],
283 int parameterCount) const override
284 {
285 return -1;
286 }
mtklein36352bf2015-03-25 18:17:31 -0700287 int onGetTableTags(SkFontTableTag tags[]) const override;
Ben Wagnerfc497342017-02-24 11:15:26 -0500288 size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override;
reed@google.comac6b9792011-03-11 15:42:51 +0000289};
290
bungeman@google.coma5501992012-05-18 19:06:41 +0000291class FontMemResourceTypeface : public LogFontTypeface {
292public:
293 /**
bungeman@google.coma5501992012-05-18 19:06:41 +0000294 * The created FontMemResourceTypeface takes ownership of fontMemResource.
295 */
Ben Wagner68efb212019-03-04 16:15:40 -0500296 static sk_sp<FontMemResourceTypeface> Make(const LOGFONT& lf, HANDLE fontMemResource) {
297 return sk_sp<FontMemResourceTypeface>(
298 new FontMemResourceTypeface(get_style(lf), lf, fontMemResource));
bungeman@google.coma5501992012-05-18 19:06:41 +0000299 }
300
301protected:
mtklein36352bf2015-03-25 18:17:31 -0700302 void weak_dispose() const override {
bungeman@google.coma5501992012-05-18 19:06:41 +0000303 RemoveFontMemResourceEx(fFontMemResource);
304 //SkTypefaceCache::Remove(this);
305 INHERITED::weak_dispose();
306 }
307
308private:
bungeman@google.com72cf4fc2014-03-21 22:48:32 +0000309 /**
310 * Takes ownership of fontMemResource.
311 */
bungemana4c4a2d2014-10-20 13:33:19 -0700312 FontMemResourceTypeface(const SkFontStyle& style, const LOGFONT& lf, HANDLE fontMemResource)
313 : LogFontTypeface(style, lf, true), fFontMemResource(fontMemResource)
314 { }
bungeman@google.com72cf4fc2014-03-21 22:48:32 +0000315
316 HANDLE fFontMemResource;
317
bungeman@google.coma5501992012-05-18 19:06:41 +0000318 typedef LogFontTypeface INHERITED;
319};
320
reed@google.comac6b9792011-03-11 15:42:51 +0000321static const LOGFONT& get_default_font() {
322 static LOGFONT gDefaultFont;
reed@google.comac6b9792011-03-11 15:42:51 +0000323 return gDefaultFont;
324}
325
bungeman82a455f2016-04-14 08:04:45 -0700326static bool FindByLogFont(SkTypeface* face, void* ctx) {
bungeman@google.coma5501992012-05-18 19:06:41 +0000327 LogFontTypeface* lface = static_cast<LogFontTypeface*>(face);
reed@google.com59d2f632011-05-02 19:36:59 +0000328 const LOGFONT* lf = reinterpret_cast<const LOGFONT*>(ctx);
reed@google.comac6b9792011-03-11 15:42:51 +0000329
bungeman82a455f2016-04-14 08:04:45 -0700330 return !memcmp(&lface->fLogFont, lf, sizeof(LOGFONT));
reed@google.com59d2f632011-05-02 19:36:59 +0000331}
332
333/**
334 * This guy is public. It first searches the cache, and if a match is not found,
335 * it creates a new face.
336 */
337SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& origLF) {
338 LOGFONT lf = origLF;
339 make_canonical(&lf);
Ben Wagner68efb212019-03-04 16:15:40 -0500340 sk_sp<SkTypeface> face = SkTypefaceCache::FindByProcAndRef(FindByLogFont, &lf);
341 if (!face) {
342 face = LogFontTypeface::Make(lf);
bungeman82a455f2016-04-14 08:04:45 -0700343 SkTypefaceCache::Add(face);
reed@google.com59d2f632011-05-02 19:36:59 +0000344 }
Ben Wagner68efb212019-03-04 16:15:40 -0500345 return face.release();
reed@google.comac6b9792011-03-11 15:42:51 +0000346}
347
reed@google.comdb77a6a2011-07-19 19:08:33 +0000348/**
bungeman@google.coma5501992012-05-18 19:06:41 +0000349 * The created SkTypeface takes ownership of fontMemResource.
350 */
Ben Wagner68efb212019-03-04 16:15:40 -0500351sk_sp<SkTypeface> SkCreateFontMemResourceTypefaceFromLOGFONT(const LOGFONT& origLF, HANDLE fontMemResource) {
bungeman@google.coma5501992012-05-18 19:06:41 +0000352 LOGFONT lf = origLF;
353 make_canonical(&lf);
mtklein60b6e9d2014-10-24 10:43:15 -0700354 // We'll never get a cache hit, so no point in putting this in SkTypefaceCache.
Ben Wagner68efb212019-03-04 16:15:40 -0500355 return FontMemResourceTypeface::Make(lf, fontMemResource);
bungeman@google.coma5501992012-05-18 19:06:41 +0000356}
357
358/**
reed@google.comdb77a6a2011-07-19 19:08:33 +0000359 * This guy is public
360 */
361void SkLOGFONTFromTypeface(const SkTypeface* face, LOGFONT* lf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700362 if (nullptr == face) {
reed@google.comdb77a6a2011-07-19 19:08:33 +0000363 *lf = get_default_font();
364 } else {
bungeman@google.coma5501992012-05-18 19:06:41 +0000365 *lf = static_cast<const LogFontTypeface*>(face)->fLogFont;
reed@google.comdb77a6a2011-07-19 19:08:33 +0000366 }
367}
368
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000369// Construct Glyph to Unicode table.
370// Unicode code points that require conjugate pairs in utf16 are not
371// supported.
372// TODO(arthurhsu): Add support for conjugate pairs. It looks like that may
373// require parsing the TTF cmap table (platform 4, encoding 12) directly instead
374// of calling GetFontUnicodeRange().
375static void populate_glyph_to_unicode(HDC fontHdc, const unsigned glyphCount,
Hal Canary46cc3da2018-05-09 11:50:34 -0400376 SkUnichar* glyphToUnicode) {
377 sk_bzero(glyphToUnicode, sizeof(SkUnichar) * glyphCount);
halcanary96fcdcc2015-08-27 07:41:13 -0700378 DWORD glyphSetBufferSize = GetFontUnicodeRanges(fontHdc, nullptr);
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000379 if (!glyphSetBufferSize) {
380 return;
381 }
382
Ben Wagner7ecc5962016-11-02 17:07:33 -0400383 std::unique_ptr<BYTE[]> glyphSetBuffer(new BYTE[glyphSetBufferSize]);
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000384 GLYPHSET* glyphSet =
385 reinterpret_cast<LPGLYPHSET>(glyphSetBuffer.get());
386 if (GetFontUnicodeRanges(fontHdc, glyphSet) != glyphSetBufferSize) {
387 return;
388 }
389
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000390 for (DWORD i = 0; i < glyphSet->cRanges; ++i) {
391 // There is no guarantee that within a Unicode range, the corresponding
392 // glyph id in a font file are continuous. So, even if we have ranges,
393 // we can't just use the first and last entry of the range to compute
394 // result. We need to enumerate them one by one.
395 int count = glyphSet->ranges[i].cGlyphs;
396 SkAutoTArray<WCHAR> chars(count + 1);
397 chars[count] = 0; // termintate string
398 SkAutoTArray<WORD> glyph(count);
399 for (USHORT j = 0; j < count; ++j) {
400 chars[j] = glyphSet->ranges[i].wcLow + j;
401 }
402 GetGlyphIndicesW(fontHdc, chars.get(), count, glyph.get(),
403 GGI_MARK_NONEXISTING_GLYPHS);
404 // If the glyph ID is valid, and the glyph is not mapped, then we will
405 // fill in the char id into the vector. If the glyph is mapped already,
406 // skip it.
407 // TODO(arthurhsu): better improve this. e.g. Get all used char ids from
408 // font cache, then generate this mapping table from there. It's
409 // unlikely to have collisions since glyph reuse happens mostly for
410 // different Unicode pages.
411 for (USHORT j = 0; j < count; ++j) {
Hal Canary46cc3da2018-05-09 11:50:34 -0400412 if (glyph[j] != 0xFFFF && glyph[j] < glyphCount && glyphToUnicode[glyph[j]] == 0) {
413 glyphToUnicode[glyph[j]] = chars[j];
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000414 }
415 }
416 }
417}
418
reed@google.com99edd432011-09-09 14:59:59 +0000419//////////////////////////////////////////////////////////////////////////////////////
420
421static int alignTo32(int n) {
422 return (n + 31) & ~31;
423}
424
425struct MyBitmapInfo : public BITMAPINFO {
426 RGBQUAD fMoreSpaceForColors[1];
427};
428
429class HDCOffscreen {
430public:
431 HDCOffscreen() {
432 fFont = 0;
433 fDC = 0;
434 fBM = 0;
halcanary96fcdcc2015-08-27 07:41:13 -0700435 fBits = nullptr;
reed@google.com99edd432011-09-09 14:59:59 +0000436 fWidth = fHeight = 0;
437 fIsBW = false;
438 }
439
440 ~HDCOffscreen() {
441 if (fDC) {
442 DeleteDC(fDC);
443 }
444 if (fBM) {
445 DeleteObject(fBM);
446 }
447 }
448
449 void init(HFONT font, const XFORM& xform) {
450 fFont = font;
451 fXform = xform;
452 }
453
bungeman@google.com97efada2012-07-30 20:40:50 +0000454 const void* draw(const SkGlyph&, bool isBW, size_t* srcRBPtr);
reed@google.com99edd432011-09-09 14:59:59 +0000455
456private:
457 HDC fDC;
458 HBITMAP fBM;
459 HFONT fFont;
460 XFORM fXform;
461 void* fBits; // points into fBM
462 int fWidth;
463 int fHeight;
464 bool fIsBW;
465};
466
reed@google.com754e4eb2011-09-26 13:21:39 +0000467const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW,
bungeman@google.com97efada2012-07-30 20:40:50 +0000468 size_t* srcRBPtr) {
reed@google.com84e22d82013-07-10 15:38:20 +0000469 // Can we share the scalercontext's fDDC, so we don't need to create
470 // a separate fDC here?
reed@google.com99edd432011-09-09 14:59:59 +0000471 if (0 == fDC) {
472 fDC = CreateCompatibleDC(0);
473 if (0 == fDC) {
halcanary96fcdcc2015-08-27 07:41:13 -0700474 return nullptr;
reed@google.com99edd432011-09-09 14:59:59 +0000475 }
476 SetGraphicsMode(fDC, GM_ADVANCED);
477 SetBkMode(fDC, TRANSPARENT);
478 SetTextAlign(fDC, TA_LEFT | TA_BASELINE);
479 SelectObject(fDC, fFont);
bungeman@google.com97efada2012-07-30 20:40:50 +0000480
481 COLORREF color = 0x00FFFFFF;
bsalomon@google.comb58a6392013-03-21 20:29:05 +0000482 SkDEBUGCODE(COLORREF prev =) SetTextColor(fDC, color);
bungeman@google.com97efada2012-07-30 20:40:50 +0000483 SkASSERT(prev != CLR_INVALID);
reed@google.com99edd432011-09-09 14:59:59 +0000484 }
485
Herb Derby793818b2019-06-25 17:10:11 -0400486 if (fBM && (fIsBW != isBW || fWidth < glyph.width() || fHeight < glyph.height())) {
reed@google.com99edd432011-09-09 14:59:59 +0000487 DeleteObject(fBM);
488 fBM = 0;
489 }
reed@google.com754e4eb2011-09-26 13:21:39 +0000490 fIsBW = isBW;
reed@google.com99edd432011-09-09 14:59:59 +0000491
Herb Derby793818b2019-06-25 17:10:11 -0400492 fWidth = SkMax32(fWidth, glyph.width());
493 fHeight = SkMax32(fHeight, glyph.height());
reed@google.com99edd432011-09-09 14:59:59 +0000494
495 int biWidth = isBW ? alignTo32(fWidth) : fWidth;
496
497 if (0 == fBM) {
498 MyBitmapInfo info;
499 sk_bzero(&info, sizeof(info));
500 if (isBW) {
501 RGBQUAD blackQuad = { 0, 0, 0, 0 };
502 RGBQUAD whiteQuad = { 0xFF, 0xFF, 0xFF, 0 };
503 info.bmiColors[0] = blackQuad;
504 info.bmiColors[1] = whiteQuad;
505 }
506 info.bmiHeader.biSize = sizeof(info.bmiHeader);
507 info.bmiHeader.biWidth = biWidth;
508 info.bmiHeader.biHeight = fHeight;
509 info.bmiHeader.biPlanes = 1;
510 info.bmiHeader.biBitCount = isBW ? 1 : 32;
511 info.bmiHeader.biCompression = BI_RGB;
512 if (isBW) {
513 info.bmiHeader.biClrUsed = 2;
514 }
515 fBM = CreateDIBSection(fDC, &info, DIB_RGB_COLORS, &fBits, 0, 0);
516 if (0 == fBM) {
halcanary96fcdcc2015-08-27 07:41:13 -0700517 return nullptr;
reed@google.com99edd432011-09-09 14:59:59 +0000518 }
519 SelectObject(fDC, fBM);
520 }
521
522 // erase
523 size_t srcRB = isBW ? (biWidth >> 3) : (fWidth << 2);
524 size_t size = fHeight * srcRB;
bungeman@google.com97efada2012-07-30 20:40:50 +0000525 memset(fBits, 0, size);
reed@google.com99edd432011-09-09 14:59:59 +0000526
527 XFORM xform = fXform;
Herb Derby793818b2019-06-25 17:10:11 -0400528 xform.eDx = (float)-glyph.left();
529 xform.eDy = (float)-glyph.top();
reed@google.com99edd432011-09-09 14:59:59 +0000530 SetWorldTransform(fDC, &xform);
531
532 uint16_t glyphID = glyph.getGlyphID();
halcanary96fcdcc2015-08-27 07:41:13 -0700533 BOOL ret = ExtTextOutW(fDC, 0, 0, ETO_GLYPH_INDEX, nullptr, reinterpret_cast<LPCWSTR>(&glyphID), 1, nullptr);
reed@google.com99edd432011-09-09 14:59:59 +0000534 GdiFlush();
bungeman@google.com39698b12011-11-15 22:26:41 +0000535 if (0 == ret) {
halcanary96fcdcc2015-08-27 07:41:13 -0700536 return nullptr;
bungeman@google.com39698b12011-11-15 22:26:41 +0000537 }
reed@google.com99edd432011-09-09 14:59:59 +0000538 *srcRBPtr = srcRB;
539 // offset to the start of the image
Herb Derby793818b2019-06-25 17:10:11 -0400540 return (const char*)fBits + (fHeight - glyph.height()) * srcRB;
reed@google.com99edd432011-09-09 14:59:59 +0000541}
542
reed@google.comb8a5c612012-06-13 20:01:44 +0000543//////////////////////////////////////////////////////////////////////////////
bungeman@google.com0abbff92013-07-27 20:37:56 +0000544#define BUFFERSIZE (1 << 13)
reed@google.com59d2f632011-05-02 19:36:59 +0000545
reed@google.com30ddd612013-07-30 17:47:39 +0000546class SkScalerContext_GDI : public SkScalerContext {
reed@google.comac6b9792011-03-11 15:42:51 +0000547public:
bungeman7cfd46a2016-10-20 16:06:52 -0400548 SkScalerContext_GDI(sk_sp<LogFontTypeface>,
549 const SkScalerContextEffects&,
550 const SkDescriptor* desc);
Chris Dalton1ef80942017-12-04 12:01:30 -0700551 ~SkScalerContext_GDI() override;
reed@google.comac6b9792011-03-11 15:42:51 +0000552
reed@google.com84e22d82013-07-10 15:38:20 +0000553 // Returns true if the constructor was able to complete all of its
554 // initializations (which may include calling GDI).
555 bool isValid() const;
556
reed@google.comac6b9792011-03-11 15:42:51 +0000557protected:
mtklein36352bf2015-03-25 18:17:31 -0700558 unsigned generateGlyphCount() override;
Ben Wagnere5416452018-08-09 14:03:42 -0400559 bool generateAdvance(SkGlyph* glyph) override;
mtklein36352bf2015-03-25 18:17:31 -0700560 void generateMetrics(SkGlyph* glyph) override;
561 void generateImage(const SkGlyph& glyph) override;
Ben Wagner5ddb3082018-03-29 11:18:06 -0400562 bool generatePath(SkGlyphID glyph, SkPath* path) override;
Mike Reedb5784ac2018-11-12 09:35:15 -0500563 void generateFontMetrics(SkFontMetrics*) override;
reed@google.com99edd432011-09-09 14:59:59 +0000564
reed@google.comac6b9792011-03-11 15:42:51 +0000565private:
Ben Wagner6e9ac122016-11-11 14:31:06 -0500566 DWORD getGDIGlyphPath(SkGlyphID glyph, UINT flags,
bungeman@google.com0abbff92013-07-27 20:37:56 +0000567 SkAutoSTMalloc<BUFFERSIZE, uint8_t>* glyphbuf);
Herb Derby793818b2019-06-25 17:10:11 -0400568 template<bool APPLY_PREBLEND>
569 static void RGBToA8(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
570 const SkGlyph& glyph, const uint8_t* table8);
571
572 template<bool APPLY_PREBLEND>
573 static void RGBToLcd16(const SkGdiRGB* SK_RESTRICT src, size_t srcRB, const SkGlyph& glyph,
574 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000575
reed@google.com99edd432011-09-09 14:59:59 +0000576 HDCOffscreen fOffscreen;
bungeman@google.com0abbff92013-07-27 20:37:56 +0000577 /** fGsA is the non-rotational part of total matrix without the text height scale.
578 * Used to find the magnitude of advances.
579 */
580 MAT2 fGsA;
bungeman@google.com6a774a12013-07-30 01:07:48 +0000581 /** The total matrix without the textSize. */
reed@google.comac6b9792011-03-11 15:42:51 +0000582 MAT2 fMat22;
bungeman@google.com6a774a12013-07-30 01:07:48 +0000583 /** Scales font to EM size. */
584 MAT2 fHighResMat22;
reed@google.comac6b9792011-03-11 15:42:51 +0000585 HDC fDDC;
586 HFONT fSavefont;
587 HFONT fFont;
588 SCRIPT_CACHE fSC;
589 int fGlyphCount;
reed@google.com1dd17a12011-05-17 14:04:41 +0000590
bungeman@google.com6a774a12013-07-30 01:07:48 +0000591 /** The total matrix which also removes EM scale. */
reed@google.com1dd17a12011-05-17 14:04:41 +0000592 SkMatrix fHiResMatrix;
bungeman@google.com0abbff92013-07-27 20:37:56 +0000593 /** fG_inv is the inverse of the rotational part of the total matrix.
594 * Used to set the direction of advances.
595 */
596 SkMatrix fG_inv;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000597 enum Type {
bungeman@google.com4732df62014-01-23 15:22:42 +0000598 kTrueType_Type, kBitmap_Type, kLine_Type
bungeman@google.coma0319f62012-04-18 15:40:50 +0000599 } fType;
600 TEXTMETRIC fTM;
reed@google.comac6b9792011-03-11 15:42:51 +0000601};
602
reed@google.comac6b9792011-03-11 15:42:51 +0000603static FIXED float2FIXED(float x) {
604 return SkFixedToFIXED(SkFloatToFixed(x));
605}
606
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700607static inline float FIXED2float(FIXED x) {
608 return SkFixedToFloat(SkFIXEDToFixed(x));
609}
610
Ben Wagner3e45a2f2017-11-01 11:47:39 -0400611static BYTE compute_quality(const SkScalerContextRec& rec) {
reed@google.com82a34d82011-07-26 19:33:08 +0000612 switch (rec.fMaskFormat) {
613 case SkMask::kBW_Format:
614 return NONANTIALIASED_QUALITY;
615 case SkMask::kLCD16_Format:
reed@google.com82a34d82011-07-26 19:33:08 +0000616 return CLEARTYPE_QUALITY;
617 default:
reed@google.com8351aab2012-01-18 17:06:35 +0000618 if (rec.fFlags & SkScalerContext::kGenA8FromLCD_Flag) {
619 return CLEARTYPE_QUALITY;
620 } else {
621 return ANTIALIASED_QUALITY;
622 }
reed@google.com82a34d82011-07-26 19:33:08 +0000623 }
624}
625
bungeman7cfd46a2016-10-20 16:06:52 -0400626SkScalerContext_GDI::SkScalerContext_GDI(sk_sp<LogFontTypeface> rawTypeface,
reeda9322c22016-04-12 06:47:05 -0700627 const SkScalerContextEffects& effects,
628 const SkDescriptor* desc)
bungeman7cfd46a2016-10-20 16:06:52 -0400629 : SkScalerContext(std::move(rawTypeface), effects, desc)
reed@google.com055180c2013-03-21 18:46:35 +0000630 , fDDC(0)
reed@google.com055180c2013-03-21 18:46:35 +0000631 , fSavefont(0)
reed@google.com84e22d82013-07-10 15:38:20 +0000632 , fFont(0)
reed@google.com055180c2013-03-21 18:46:35 +0000633 , fSC(0)
bungeman@google.com05a729f2013-06-20 15:29:16 +0000634 , fGlyphCount(-1)
635{
bungeman7cfd46a2016-10-20 16:06:52 -0400636 LogFontTypeface* typeface = static_cast<LogFontTypeface*>(this->getTypeface());
reed@google.com055180c2013-03-21 18:46:35 +0000637
halcanary96fcdcc2015-08-27 07:41:13 -0700638 fDDC = ::CreateCompatibleDC(nullptr);
reed@google.com84e22d82013-07-10 15:38:20 +0000639 if (!fDDC) {
640 return;
641 }
reed@google.com1dd17a12011-05-17 14:04:41 +0000642 SetGraphicsMode(fDDC, GM_ADVANCED);
reed@google.comac6b9792011-03-11 15:42:51 +0000643 SetBkMode(fDDC, TRANSPARENT);
skia.committer@gmail.com27e21fe2013-07-28 07:01:03 +0000644
bungeman6f940762015-03-18 08:25:43 -0700645 // When GDI hinting, remove the entire Y scale from sA and GsA. (Prevents 'linear' metrics.)
646 // When not hinting, remove only the integer Y scale from sA and GsA. (Applied by GDI.)
bungeman5f14c5e2014-12-05 12:26:44 -0800647 SkScalerContextRec::PreMatrixScale scaleConstraints =
Ben Wagner5785e4a2019-05-07 16:50:29 -0400648 (fRec.getHinting() == SkFontHinting::kNone || fRec.getHinting() == SkFontHinting::kSlight)
bungeman5f14c5e2014-12-05 12:26:44 -0800649 ? SkScalerContextRec::kVerticalInteger_PreMatrixScale
650 : SkScalerContextRec::kVertical_PreMatrixScale;
651 SkVector scale;
652 SkMatrix sA;
653 SkMatrix GsA;
654 SkMatrix A;
655 fRec.computeMatrices(scaleConstraints, &scale, &sA, &GsA, &fG_inv, &A);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000656
657 fGsA.eM11 = SkScalarToFIXED(GsA.get(SkMatrix::kMScaleX));
658 fGsA.eM12 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewY)); // This should be ~0.
659 fGsA.eM21 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewX));
660 fGsA.eM22 = SkScalarToFIXED(GsA.get(SkMatrix::kMScaleY));
661
bungeman6f940762015-03-18 08:25:43 -0700662 // When not hinting, scale was computed with kVerticalInteger, so is already an integer.
663 // The sA and GsA transforms will be used to create 'linear' metrics.
664
665 // When hinting, scale was computed with kVertical, stating that our port can handle
666 // non-integer scales. This is done so that sA and GsA are computed without any 'residual'
667 // scale in them, preventing 'linear' metrics. However, GDI cannot actually handle non-integer
668 // scales so we need to round in this case. This is fine, since all of the scale has been
669 // removed from sA and GsA, so GDI will be handling the scale completely.
670 SkScalar gdiTextSize = SkScalarRoundToScalar(scale.fY);
671
672 // GDI will not accept a size of zero, so round the range [0, 1] to 1.
673 // If the size was non-zero, the scale factors will also be non-zero and 1px tall text is drawn.
674 // If the size actually was zero, the scale factors will also be zero, so GDI will draw nothing.
bungeman5f14c5e2014-12-05 12:26:44 -0800675 if (gdiTextSize == 0) {
676 gdiTextSize = SK_Scalar1;
677 }
bungeman@google.com0abbff92013-07-27 20:37:56 +0000678
reed@google.com055180c2013-03-21 18:46:35 +0000679 LOGFONT lf = typeface->fLogFont;
bungeman@google.com11ba3192013-10-03 20:17:51 +0000680 lf.lfHeight = -SkScalarTruncToInt(gdiTextSize);
reed@google.com82a34d82011-07-26 19:33:08 +0000681 lf.lfQuality = compute_quality(fRec);
reed@google.comac6b9792011-03-11 15:42:51 +0000682 fFont = CreateFontIndirect(&lf);
reed@google.com84e22d82013-07-10 15:38:20 +0000683 if (!fFont) {
684 return;
685 }
reed@google.com1dd17a12011-05-17 14:04:41 +0000686
reed@google.comac6b9792011-03-11 15:42:51 +0000687 fSavefont = (HFONT)SelectObject(fDDC, fFont);
reed@google.coma767fa02011-08-05 21:40:26 +0000688
bungeman@google.coma0319f62012-04-18 15:40:50 +0000689 if (0 == GetTextMetrics(fDDC, &fTM)) {
reed@google.com055180c2013-03-21 18:46:35 +0000690 call_ensure_accessible(lf);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000691 if (0 == GetTextMetrics(fDDC, &fTM)) {
692 fTM.tmPitchAndFamily = TMPF_TRUETYPE;
693 }
694 }
bungeman@google.com90b7e382012-04-20 15:26:28 +0000695
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000696 XFORM xform;
bungeman@google.com90b7e382012-04-20 15:26:28 +0000697 if (fTM.tmPitchAndFamily & TMPF_VECTOR) {
bungeman@google.com4732df62014-01-23 15:22:42 +0000698 // Used a logfont on a memory context, should never get a device font.
699 // Therefore all TMPF_DEVICE will be PostScript fonts.
700
701 // If TMPF_VECTOR is set, one of TMPF_TRUETYPE or TMPF_DEVICE means that
702 // we have an outline font. Otherwise we have a vector FON, which is
703 // scalable, but not an outline font.
704 // This was determined by testing with Type1 PFM/PFB and
705 // OpenTypeCFF OTF, as well as looking at Wine bugs and sources.
706 if (fTM.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_DEVICE)) {
707 // Truetype or PostScript.
708 fType = SkScalerContext_GDI::kTrueType_Type;
709 } else {
710 // Stroked FON.
711 fType = SkScalerContext_GDI::kLine_Type;
712 }
bungeman@google.coma0319f62012-04-18 15:40:50 +0000713
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000714 // fPost2x2 is column-major, left handed (y down).
715 // XFORM 2x2 is row-major, left handed (y down).
bungeman@google.com0abbff92013-07-27 20:37:56 +0000716 xform.eM11 = SkScalarToFloat(sA.get(SkMatrix::kMScaleX));
717 xform.eM12 = SkScalarToFloat(sA.get(SkMatrix::kMSkewY));
718 xform.eM21 = SkScalarToFloat(sA.get(SkMatrix::kMSkewX));
719 xform.eM22 = SkScalarToFloat(sA.get(SkMatrix::kMScaleY));
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000720 xform.eDx = 0;
721 xform.eDy = 0;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000722
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000723 // MAT2 is row major, right handed (y up).
724 fMat22.eM11 = float2FIXED(xform.eM11);
725 fMat22.eM12 = float2FIXED(-xform.eM12);
726 fMat22.eM21 = float2FIXED(-xform.eM21);
727 fMat22.eM22 = float2FIXED(xform.eM22);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000728
729 if (needToRenderWithSkia(fRec)) {
730 this->forceGenerateImageFromPath();
731 }
732
bungeman@google.com11ba3192013-10-03 20:17:51 +0000733 // Create a hires matrix if we need linear metrics.
Ben Wagnerf460eee2019-04-18 16:37:45 -0400734 if (this->isLinearMetrics()) {
bungeman@google.com0abbff92013-07-27 20:37:56 +0000735 OUTLINETEXTMETRIC otm;
736 UINT success = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
737 if (0 == success) {
738 call_ensure_accessible(lf);
739 success = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
740 }
741 if (0 != success) {
bungeman@google.com11ba3192013-10-03 20:17:51 +0000742 SkScalar upem = SkIntToScalar(otm.otmEMSquare);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000743
bungeman@google.com11ba3192013-10-03 20:17:51 +0000744 SkScalar gdiTextSizeToEMScale = upem / gdiTextSize;
745 fHighResMat22.eM11 = float2FIXED(gdiTextSizeToEMScale);
bungeman@google.com6a774a12013-07-30 01:07:48 +0000746 fHighResMat22.eM12 = float2FIXED(0);
747 fHighResMat22.eM21 = float2FIXED(0);
bungeman@google.com11ba3192013-10-03 20:17:51 +0000748 fHighResMat22.eM22 = float2FIXED(gdiTextSizeToEMScale);
bungeman@google.com6a774a12013-07-30 01:07:48 +0000749
bungeman@google.com11ba3192013-10-03 20:17:51 +0000750 SkScalar removeEMScale = SkScalarInvert(upem);
bungeman@google.com6a774a12013-07-30 01:07:48 +0000751 fHiResMatrix = A;
bungeman@google.com11ba3192013-10-03 20:17:51 +0000752 fHiResMatrix.preScale(removeEMScale, removeEMScale);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000753 }
754 }
755
bungeman@google.coma0319f62012-04-18 15:40:50 +0000756 } else {
757 // Assume bitmap
reed@google.com30ddd612013-07-30 17:47:39 +0000758 fType = SkScalerContext_GDI::kBitmap_Type;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000759
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000760 xform.eM11 = 1.0f;
761 xform.eM12 = 0.0f;
762 xform.eM21 = 0.0f;
763 xform.eM22 = 1.0f;
764 xform.eDx = 0.0f;
765 xform.eDy = 0.0f;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000766
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000767 // fPost2x2 is column-major, left handed (y down).
768 // MAT2 is row major, right handed (y up).
bungeman@google.coma0319f62012-04-18 15:40:50 +0000769 fMat22.eM11 = SkScalarToFIXED(fRec.fPost2x2[0][0]);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000770 fMat22.eM12 = SkScalarToFIXED(-fRec.fPost2x2[1][0]);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000771 fMat22.eM21 = SkScalarToFIXED(-fRec.fPost2x2[0][1]);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000772 fMat22.eM22 = SkScalarToFIXED(fRec.fPost2x2[1][1]);
reed@google.coma767fa02011-08-05 21:40:26 +0000773 }
reed@google.com99edd432011-09-09 14:59:59 +0000774
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000775 fOffscreen.init(fFont, xform);
reed@google.comac6b9792011-03-11 15:42:51 +0000776}
777
reed@google.com30ddd612013-07-30 17:47:39 +0000778SkScalerContext_GDI::~SkScalerContext_GDI() {
reed@google.comac6b9792011-03-11 15:42:51 +0000779 if (fDDC) {
780 ::SelectObject(fDDC, fSavefont);
781 ::DeleteDC(fDDC);
782 }
783 if (fFont) {
784 ::DeleteObject(fFont);
785 }
786 if (fSC) {
787 ::ScriptFreeCache(&fSC);
788 }
789}
790
reed@google.com30ddd612013-07-30 17:47:39 +0000791bool SkScalerContext_GDI::isValid() const {
reed@google.com84e22d82013-07-10 15:38:20 +0000792 return fDDC && fFont;
793}
794
reed@google.com30ddd612013-07-30 17:47:39 +0000795unsigned SkScalerContext_GDI::generateGlyphCount() {
reed@google.comac6b9792011-03-11 15:42:51 +0000796 if (fGlyphCount < 0) {
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000797 fGlyphCount = calculateGlyphCount(
798 fDDC, static_cast<const LogFontTypeface*>(this->getTypeface())->fLogFont);
reed@google.comac6b9792011-03-11 15:42:51 +0000799 }
800 return fGlyphCount;
801}
802
Ben Wagnere5416452018-08-09 14:03:42 -0400803bool SkScalerContext_GDI::generateAdvance(SkGlyph* glyph) {
804 return false;
reed@google.comac6b9792011-03-11 15:42:51 +0000805}
806
reed@google.com30ddd612013-07-30 17:47:39 +0000807void SkScalerContext_GDI::generateMetrics(SkGlyph* glyph) {
reed@google.comac6b9792011-03-11 15:42:51 +0000808 SkASSERT(fDDC);
809
Ben Wagnere5416452018-08-09 14:03:42 -0400810 glyph->fMaskFormat = fRec.fMaskFormat;
811
bungeman@google.com4732df62014-01-23 15:22:42 +0000812 if (fType == SkScalerContext_GDI::kBitmap_Type || fType == SkScalerContext_GDI::kLine_Type) {
bungeman@google.coma0319f62012-04-18 15:40:50 +0000813 SIZE size;
djsollen1b277042014-08-06 06:58:06 -0700814 WORD glyphs = glyph->getGlyphID();
bungeman@google.coma0319f62012-04-18 15:40:50 +0000815 if (0 == GetTextExtentPointI(fDDC, &glyphs, 1, &size)) {
816 glyph->fWidth = SkToS16(fTM.tmMaxCharWidth);
817 } else {
818 glyph->fWidth = SkToS16(size.cx);
819 }
820 glyph->fHeight = SkToS16(size.cy);
821
822 glyph->fTop = SkToS16(-fTM.tmAscent);
bungeman@google.com4732df62014-01-23 15:22:42 +0000823 // Bitmap FON cannot underhang, but vector FON may.
824 // There appears no means of determining underhang of vector FON.
bungeman@google.coma0319f62012-04-18 15:40:50 +0000825 glyph->fLeft = SkToS16(0);
Herb Derby793818b2019-06-25 17:10:11 -0400826 glyph->fAdvanceX = glyph->width();
bungeman@google.coma0319f62012-04-18 15:40:50 +0000827 glyph->fAdvanceY = 0;
828
bungeman@google.com4732df62014-01-23 15:22:42 +0000829 // Vector FON will transform nicely, but bitmap FON do not.
830 if (fType == SkScalerContext_GDI::kLine_Type) {
831 SkRect bounds = SkRect::MakeXYWH(glyph->fLeft, glyph->fTop,
Herb Derby793818b2019-06-25 17:10:11 -0400832 glyph->width(), glyph->height());
bungeman@google.com4732df62014-01-23 15:22:42 +0000833 SkMatrix m;
834 m.setAll(SkFIXEDToScalar(fMat22.eM11), -SkFIXEDToScalar(fMat22.eM21), 0,
835 -SkFIXEDToScalar(fMat22.eM12), SkFIXEDToScalar(fMat22.eM22), 0,
reed3f43f8a2015-01-20 19:58:36 -0800836 0, 0, 1);
bungeman@google.com4732df62014-01-23 15:22:42 +0000837 m.mapRect(&bounds);
reedd02cf262014-11-18 18:06:45 -0800838 bounds.roundOut(&bounds);
bungeman@google.com4732df62014-01-23 15:22:42 +0000839 glyph->fLeft = SkScalarTruncToInt(bounds.fLeft);
840 glyph->fTop = SkScalarTruncToInt(bounds.fTop);
841 glyph->fWidth = SkScalarTruncToInt(bounds.width());
842 glyph->fHeight = SkScalarTruncToInt(bounds.height());
843 }
844
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000845 // Apply matrix to advance.
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700846 glyph->fAdvanceY = -FIXED2float(fMat22.eM12) * glyph->fAdvanceX;
847 glyph->fAdvanceX *= FIXED2float(fMat22.eM11);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000848
849 return;
850 }
851
djsollen1b277042014-08-06 06:58:06 -0700852 UINT glyphId = glyph->getGlyphID();
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000853
reed@google.comac6b9792011-03-11 15:42:51 +0000854 GLYPHMETRICS gm;
reed@google.com1dd17a12011-05-17 14:04:41 +0000855 sk_bzero(&gm, sizeof(gm));
reed@google.comac6b9792011-03-11 15:42:51 +0000856
halcanary96fcdcc2015-08-27 07:41:13 -0700857 DWORD status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fMat22);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000858 if (GDI_ERROR == status) {
reed@google.com055180c2013-03-21 18:46:35 +0000859 LogFontTypeface::EnsureAccessible(this->getTypeface());
halcanary96fcdcc2015-08-27 07:41:13 -0700860 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fMat22);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000861 if (GDI_ERROR == status) {
862 glyph->zeroMetrics();
863 return;
864 }
865 }
866
867 bool empty = false;
868 // The black box is either the embedded bitmap size or the outline extent.
869 // It is 1x1 if nothing is to be drawn, but will also be 1x1 if something very small
870 // is to be drawn, like a '.'. We need to outset '.' but do not wish to outset ' '.
871 if (1 == gm.gmBlackBoxX && 1 == gm.gmBlackBoxY) {
872 // If GetGlyphOutline with GGO_NATIVE returns 0, we know there was no outline.
halcanary96fcdcc2015-08-27 07:41:13 -0700873 DWORD bufferSize = GetGlyphOutlineW(fDDC, glyphId, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fMat22);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000874 empty = (0 == bufferSize);
875 }
876
877 glyph->fTop = SkToS16(-gm.gmptGlyphOrigin.y);
878 glyph->fLeft = SkToS16(gm.gmptGlyphOrigin.x);
879 if (empty) {
880 glyph->fWidth = 0;
881 glyph->fHeight = 0;
882 } else {
883 // Outset, since the image may bleed out of the black box.
884 // For embedded bitmaps the black box should be exact.
885 // For outlines we need to outset by 1 in all directions for bleed.
886 // For ClearType we need to outset by 2 for bleed.
887 glyph->fWidth = gm.gmBlackBoxX + 4;
888 glyph->fHeight = gm.gmBlackBoxY + 4;
889 glyph->fTop -= 2;
890 glyph->fLeft -= 2;
891 }
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700892 // TODO(benjaminwagner): What is the type of gm.gmCellInc[XY]?
893 glyph->fAdvanceX = (float)((int)gm.gmCellIncX);
894 glyph->fAdvanceY = (float)((int)gm.gmCellIncY);
reed@google.comac6b9792011-03-11 15:42:51 +0000895
Ben Wagnerf460eee2019-04-18 16:37:45 -0400896 if ((fTM.tmPitchAndFamily & TMPF_VECTOR) && this->isLinearMetrics()) {
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000897 sk_bzero(&gm, sizeof(gm));
halcanary96fcdcc2015-08-27 07:41:13 -0700898 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fHighResMat22);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000899 if (GDI_ERROR != status) {
900 SkPoint advance;
901 fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700902 glyph->fAdvanceX = SkScalarToFloat(advance.fX);
903 glyph->fAdvanceY = SkScalarToFloat(advance.fY);
reed@google.comac6b9792011-03-11 15:42:51 +0000904 }
bungeman@google.com0abbff92013-07-27 20:37:56 +0000905 } else if (!isAxisAligned(this->fRec)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700906 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fGsA);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000907 if (GDI_ERROR != status) {
908 SkPoint advance;
909 fG_inv.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700910 glyph->fAdvanceX = SkScalarToFloat(advance.fX);
911 glyph->fAdvanceY = SkScalarToFloat(advance.fY);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000912 }
reed@google.comac6b9792011-03-11 15:42:51 +0000913 }
914}
915
bungeman@google.com6a774a12013-07-30 01:07:48 +0000916static const MAT2 gMat2Identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
Mike Reedb5784ac2018-11-12 09:35:15 -0500917void SkScalerContext_GDI::generateFontMetrics(SkFontMetrics* metrics) {
halcanary96fcdcc2015-08-27 07:41:13 -0700918 if (nullptr == metrics) {
bungeman41078062014-07-07 08:16:37 -0700919 return;
reed@google.com60af92c2013-05-08 14:11:28 +0000920 }
bungeman41078062014-07-07 08:16:37 -0700921 sk_bzero(metrics, sizeof(*metrics));
reed@google.comac6b9792011-03-11 15:42:51 +0000922
923 SkASSERT(fDDC);
924
bungeman@google.come9d83192013-06-21 05:31:38 +0000925#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
bungeman@google.com4732df62014-01-23 15:22:42 +0000926 if (fType == SkScalerContext_GDI::kBitmap_Type || fType == SkScalerContext_GDI::kLine_Type) {
bungeman@google.come9d83192013-06-21 05:31:38 +0000927#endif
bungeman41078062014-07-07 08:16:37 -0700928 metrics->fTop = SkIntToScalar(-fTM.tmAscent);
929 metrics->fAscent = SkIntToScalar(-fTM.tmAscent);
930 metrics->fDescent = SkIntToScalar(fTM.tmDescent);
931 metrics->fBottom = SkIntToScalar(fTM.tmDescent);
932 metrics->fLeading = SkIntToScalar(fTM.tmExternalLeading);
933 metrics->fAvgCharWidth = SkIntToScalar(fTM.tmAveCharWidth);
934 metrics->fMaxCharWidth = SkIntToScalar(fTM.tmMaxCharWidth);
935 metrics->fXMin = 0;
936 metrics->fXMax = metrics->fMaxCharWidth;
937 //metrics->fXHeight = 0;
bungeman@google.come9d83192013-06-21 05:31:38 +0000938#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
bungeman@google.coma0319f62012-04-18 15:40:50 +0000939 return;
940 }
bungeman@google.come9d83192013-06-21 05:31:38 +0000941#endif
bungeman@google.coma0319f62012-04-18 15:40:50 +0000942
reed@google.comac6b9792011-03-11 15:42:51 +0000943 OUTLINETEXTMETRIC otm;
944
945 uint32_t ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000946 if (0 == ret) {
reed@google.com055180c2013-03-21 18:46:35 +0000947 LogFontTypeface::EnsureAccessible(this->getTypeface());
bungeman@google.com39698b12011-11-15 22:26:41 +0000948 ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
949 }
bungeman@google.com0abbff92013-07-27 20:37:56 +0000950 if (0 == ret) {
bungeman@google.coma0319f62012-04-18 15:40:50 +0000951 return;
reed@google.comac6b9792011-03-11 15:42:51 +0000952 }
953
bungeman@google.come9d83192013-06-21 05:31:38 +0000954#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
bungeman41078062014-07-07 08:16:37 -0700955 metrics->fTop = SkIntToScalar(-otm.otmrcFontBox.top);
956 metrics->fAscent = SkIntToScalar(-otm.otmAscent);
957 metrics->fDescent = SkIntToScalar(-otm.otmDescent);
958 metrics->fBottom = SkIntToScalar(-otm.otmrcFontBox.bottom);
959 metrics->fLeading = SkIntToScalar(otm.otmLineGap);
960 metrics->fAvgCharWidth = SkIntToScalar(otm.otmTextMetrics.tmAveCharWidth);
961 metrics->fMaxCharWidth = SkIntToScalar(otm.otmTextMetrics.tmMaxCharWidth);
962 metrics->fXMin = SkIntToScalar(otm.otmrcFontBox.left);
963 metrics->fXMax = SkIntToScalar(otm.otmrcFontBox.right);
commit-bot@chromium.orgd3031aa2014-05-14 14:54:51 +0000964#endif
bungeman41078062014-07-07 08:16:37 -0700965 metrics->fUnderlineThickness = SkIntToScalar(otm.otmsUnderscoreSize);
966 metrics->fUnderlinePosition = -SkIntToScalar(otm.otmsUnderscorePosition);
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +0000967
Mike Reedb5784ac2018-11-12 09:35:15 -0500968 metrics->fFlags |= SkFontMetrics::kUnderlineThicknessIsValid_Flag;
969 metrics->fFlags |= SkFontMetrics::kUnderlinePositionIsValid_Flag;
bungeman@google.com4a867a62014-05-22 17:59:21 +0000970
bungeman41078062014-07-07 08:16:37 -0700971 metrics->fXHeight = SkIntToScalar(otm.otmsXHeight);
972 GLYPHMETRICS gm;
973 sk_bzero(&gm, sizeof(gm));
974 DWORD len = GetGlyphOutlineW(fDDC, 'x', GGO_METRICS, &gm, 0, 0, &gMat2Identity);
975 if (len != GDI_ERROR && gm.gmBlackBoxY > 0) {
976 metrics->fXHeight = SkIntToScalar(gm.gmBlackBoxY);
reed@google.comac6b9792011-03-11 15:42:51 +0000977 }
978}
979
reed@google.com7430a332011-10-03 14:37:38 +0000980////////////////////////////////////////////////////////////////////////////////////////
981
bungeman@google.com0abbff92013-07-27 20:37:56 +0000982#define SK_SHOW_TEXT_BLIT_COVERAGE 0
983
reed@google.com7430a332011-10-03 14:37:38 +0000984static void build_power_table(uint8_t table[], float ee) {
985 for (int i = 0; i < 256; i++) {
986 float x = i / 255.f;
bungeman@google.com97efada2012-07-30 20:40:50 +0000987 x = sk_float_pow(x, ee);
reed@google.come1ca7052013-12-17 19:22:07 +0000988 int xx = SkScalarRoundToInt(x * 255);
reed@google.com7430a332011-10-03 14:37:38 +0000989 table[i] = SkToU8(xx);
990 }
991}
992
bungeman@google.com97efada2012-07-30 20:40:50 +0000993/**
994 * This will invert the gamma applied by GDI (gray-scale antialiased), so we
995 * can get linear values.
996 *
997 * GDI grayscale appears to use a hard-coded gamma of 2.3.
998 *
999 * GDI grayscale appears to draw using the black and white rasterizer at four
1000 * times the size and then downsamples to compute the coverage mask. As a
1001 * result there are only seventeen total grays. This lack of fidelity means
1002 * that shifting into other color spaces is imprecise.
1003 */
1004static const uint8_t* getInverseGammaTableGDI() {
Mike Klein0341f162017-10-18 09:58:54 -04001005 static SkOnce once;
bungeman@google.com97efada2012-07-30 20:40:50 +00001006 static uint8_t gTableGdi[256];
Mike Klein0341f162017-10-18 09:58:54 -04001007 once([]{
bungeman@google.com97efada2012-07-30 20:40:50 +00001008 build_power_table(gTableGdi, 2.3f);
Mike Klein0341f162017-10-18 09:58:54 -04001009 });
bungeman@google.com97efada2012-07-30 20:40:50 +00001010 return gTableGdi;
1011}
1012
1013/**
1014 * This will invert the gamma applied by GDI ClearType, so we can get linear
1015 * values.
1016 *
1017 * GDI ClearType uses SPI_GETFONTSMOOTHINGCONTRAST / 1000 as the gamma value.
1018 * If this value is not specified, the default is a gamma of 1.4.
1019 */
1020static const uint8_t* getInverseGammaTableClearType() {
Mike Klein0341f162017-10-18 09:58:54 -04001021 static SkOnce once;
bungeman@google.com97efada2012-07-30 20:40:50 +00001022 static uint8_t gTableClearType[256];
Mike Klein0341f162017-10-18 09:58:54 -04001023 once([]{
reed@google.com7430a332011-10-03 14:37:38 +00001024 UINT level = 0;
1025 if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &level, 0) || !level) {
1026 // can't get the data, so use a default
1027 level = 1400;
1028 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001029 build_power_table(gTableClearType, level / 1000.0f);
Mike Klein0341f162017-10-18 09:58:54 -04001030 });
bungeman@google.com97efada2012-07-30 20:40:50 +00001031 return gTableClearType;
reed@google.com7430a332011-10-03 14:37:38 +00001032}
1033
Mike Kleinc0bd9f92019-04-23 12:05:21 -05001034#include "include/private/SkColorData.h"
reed@google.comac6b9792011-03-11 15:42:51 +00001035
bungeman@google.com63853142012-08-01 15:36:46 +00001036//Cannot assume that the input rgb is gray due to possible setting of kGenA8FromLCD_Flag.
bungeman@google.com97efada2012-07-30 20:40:50 +00001037template<bool APPLY_PREBLEND>
1038static inline uint8_t rgb_to_a8(SkGdiRGB rgb, const uint8_t* table8) {
bungeman@google.com63853142012-08-01 15:36:46 +00001039 U8CPU r = (rgb >> 16) & 0xFF;
1040 U8CPU g = (rgb >> 8) & 0xFF;
1041 U8CPU b = (rgb >> 0) & 0xFF;
bungeman@google.com1bfe01d2012-08-28 16:02:42 +00001042 return sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
reed@google.com8351aab2012-01-18 17:06:35 +00001043}
1044
bungeman@google.com97efada2012-07-30 20:40:50 +00001045template<bool APPLY_PREBLEND>
1046static inline uint16_t rgb_to_lcd16(SkGdiRGB rgb, const uint8_t* tableR,
1047 const uint8_t* tableG,
1048 const uint8_t* tableB) {
1049 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1050 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1051 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001052#if SK_SHOW_TEXT_BLIT_COVERAGE
1053 r = SkMax32(r, 10); g = SkMax32(g, 10); b = SkMax32(b, 10);
1054#endif
bungeman@google.com97efada2012-07-30 20:40:50 +00001055 return SkPack888ToRGB16(r, g, b);
reed@google.com82a34d82011-07-26 19:33:08 +00001056}
1057
bungeman@google.com97efada2012-07-30 20:40:50 +00001058template<bool APPLY_PREBLEND>
Herb Derby793818b2019-06-25 17:10:11 -04001059void SkScalerContext_GDI::RGBToA8(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
1060 const SkGlyph& glyph, const uint8_t* table8) {
reed@google.com5e2df642011-09-21 18:42:09 +00001061 const size_t dstRB = glyph.rowBytes();
Herb Derby793818b2019-06-25 17:10:11 -04001062 const int width = glyph.width();
1063 uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.height() - 1) * dstRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001064
1065 for (int y = 0; y < glyph.fHeight; y++) {
1066 for (int i = 0; i < width; i++) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001067 dst[i] = rgb_to_a8<APPLY_PREBLEND>(src[i], table8);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001068#if SK_SHOW_TEXT_BLIT_COVERAGE
1069 dst[i] = SkMax32(dst[i], 10);
1070#endif
reed@google.com5e2df642011-09-21 18:42:09 +00001071 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001072 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001073 dst -= dstRB;
1074 }
1075}
1076
bungeman@google.com97efada2012-07-30 20:40:50 +00001077template<bool APPLY_PREBLEND>
Herb Derby793818b2019-06-25 17:10:11 -04001078void SkScalerContext_GDI::RGBToLcd16(
1079 const SkGdiRGB* SK_RESTRICT src, size_t srcRB, const SkGlyph& glyph,
1080 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
reed@google.com5e2df642011-09-21 18:42:09 +00001081 const size_t dstRB = glyph.rowBytes();
Herb Derby793818b2019-06-25 17:10:11 -04001082 const int width = glyph.width();
1083 uint16_t* SK_RESTRICT dst = (uint16_t*)((char*)glyph.fImage + (glyph.height() - 1) * dstRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001084
1085 for (int y = 0; y < glyph.fHeight; y++) {
1086 for (int i = 0; i < width; i++) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001087 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(src[i], tableR, tableG, tableB);
reed@google.com5e2df642011-09-21 18:42:09 +00001088 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001089 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001090 dst = (uint16_t*)((char*)dst - dstRB);
1091 }
1092}
1093
reed@google.com30ddd612013-07-30 17:47:39 +00001094void SkScalerContext_GDI::generateImage(const SkGlyph& glyph) {
reed@google.comac6b9792011-03-11 15:42:51 +00001095 SkASSERT(fDDC);
1096
reed@google.com62711172011-05-18 15:08:10 +00001097 const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
reed@google.com82a34d82011-07-26 19:33:08 +00001098 const bool isAA = !isLCD(fRec);
reed@google.comac6b9792011-03-11 15:42:51 +00001099
reed@google.com99edd432011-09-09 14:59:59 +00001100 size_t srcRB;
bungeman@google.com97efada2012-07-30 20:40:50 +00001101 const void* bits = fOffscreen.draw(glyph, isBW, &srcRB);
halcanary96fcdcc2015-08-27 07:41:13 -07001102 if (nullptr == bits) {
reed@google.com055180c2013-03-21 18:46:35 +00001103 LogFontTypeface::EnsureAccessible(this->getTypeface());
bungeman@google.com97efada2012-07-30 20:40:50 +00001104 bits = fOffscreen.draw(glyph, isBW, &srcRB);
halcanary96fcdcc2015-08-27 07:41:13 -07001105 if (nullptr == bits) {
Herb Derby9b06f212019-06-21 14:25:47 -04001106 sk_bzero(glyph.fImage, glyph.imageSize());
bungeman@google.com39698b12011-11-15 22:26:41 +00001107 return;
1108 }
reed@google.com82a34d82011-07-26 19:33:08 +00001109 }
reed@google.comac6b9792011-03-11 15:42:51 +00001110
bungeman@google.com97efada2012-07-30 20:40:50 +00001111 if (!isBW) {
bungeman@google.com1bd2d672012-08-13 20:01:51 +00001112 const uint8_t* table;
1113 //The offscreen contains a GDI blit if isAA and kGenA8FromLCD_Flag is not set.
1114 //Otherwise the offscreen contains a ClearType blit.
1115 if (isAA && !(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag)) {
1116 table = getInverseGammaTableGDI();
1117 } else {
1118 table = getInverseGammaTableClearType();
bungeman@google.com97efada2012-07-30 20:40:50 +00001119 }
1120 //Note that the following cannot really be integrated into the
1121 //pre-blend, since we may not be applying the pre-blend; when we aren't
1122 //applying the pre-blend it means that a filter wants linear anyway.
1123 //Other code may also be applying the pre-blend, so we'd need another
1124 //one with this and one without.
reed@google.com6f5df482011-09-28 20:33:24 +00001125 SkGdiRGB* addr = (SkGdiRGB*)bits;
1126 for (int y = 0; y < glyph.fHeight; ++y) {
Herb Derby793818b2019-06-25 17:10:11 -04001127 for (int x = 0; x < glyph.width(); ++x) {
reed@google.com6f5df482011-09-28 20:33:24 +00001128 int r = (addr[x] >> 16) & 0xFF;
1129 int g = (addr[x] >> 8) & 0xFF;
1130 int b = (addr[x] >> 0) & 0xFF;
reed@google.com7430a332011-10-03 14:37:38 +00001131 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
reed@google.com6f5df482011-09-28 20:33:24 +00001132 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001133 addr = SkTAddOffset<SkGdiRGB>(addr, srcRB);
reed@google.com6f5df482011-09-28 20:33:24 +00001134 }
1135 }
1136
reed@google.com82a34d82011-07-26 19:33:08 +00001137 size_t dstRB = glyph.rowBytes();
1138 if (isBW) {
1139 const uint8_t* src = (const uint8_t*)bits;
reed@google.com82a34d82011-07-26 19:33:08 +00001140 uint8_t* dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1141 for (int y = 0; y < glyph.fHeight; y++) {
1142 memcpy(dst, src, dstRB);
1143 src += srcRB;
1144 dst -= dstRB;
reed@google.comac6b9792011-03-11 15:42:51 +00001145 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001146#if SK_SHOW_TEXT_BLIT_COVERAGE
Herb Derby793818b2019-06-25 17:10:11 -04001147 if (glyph.width() > 0 && glyph.fHeight > 0) {
1148 int bitCount = glyph.width() & 7;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001149 uint8_t* first = (uint8_t*)glyph.fImage;
Herb Derby793818b2019-06-25 17:10:11 -04001150 uint8_t* last = (uint8_t*)((char*)glyph.fImage + glyph.height() * dstRB - 1);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001151 *first |= 1 << 7;
1152 *last |= bitCount == 0 ? 1 : 1 << (8 - bitCount);
1153 }
1154#endif
reed@google.com82a34d82011-07-26 19:33:08 +00001155 } else if (isAA) {
reed@google.com6f5df482011-09-28 20:33:24 +00001156 // since the caller may require A8 for maskfilters, we can't check for BW
1157 // ... until we have the caller tell us that explicitly
reed@google.com5e2df642011-09-21 18:42:09 +00001158 const SkGdiRGB* src = (const SkGdiRGB*)bits;
bungeman@google.coma76de722012-10-26 19:35:54 +00001159 if (fPreBlend.isApplicable()) {
Herb Derby793818b2019-06-25 17:10:11 -04001160 RGBToA8<true>(src, srcRB, glyph, fPreBlend.fG);
bungeman@google.com97efada2012-07-30 20:40:50 +00001161 } else {
Herb Derby793818b2019-06-25 17:10:11 -04001162 RGBToA8<false>(src, srcRB, glyph, fPreBlend.fG);
bungeman@google.com97efada2012-07-30 20:40:50 +00001163 }
reed@google.com82a34d82011-07-26 19:33:08 +00001164 } else { // LCD16
reed@google.com5e2df642011-09-21 18:42:09 +00001165 const SkGdiRGB* src = (const SkGdiRGB*)bits;
Ben Wagner968664b2018-12-04 14:06:37 -05001166 SkASSERT(SkMask::kLCD16_Format == glyph.fMaskFormat);
1167 if (fPreBlend.isApplicable()) {
Herb Derby793818b2019-06-25 17:10:11 -04001168 RGBToLcd16<true>(src, srcRB, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
reed@google.com5e2df642011-09-21 18:42:09 +00001169 } else {
Herb Derby793818b2019-06-25 17:10:11 -04001170 RGBToLcd16<false>(src, srcRB, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
reed@google.comac6b9792011-03-11 15:42:51 +00001171 }
1172 }
reed@google.comac6b9792011-03-11 15:42:51 +00001173}
1174
bungeman@google.com0abbff92013-07-27 20:37:56 +00001175class GDIGlyphbufferPointIter {
1176public:
skia.committer@gmail.com27e21fe2013-07-28 07:01:03 +00001177 GDIGlyphbufferPointIter(const uint8_t* glyphbuf, DWORD total_size)
bungeman@google.com0abbff92013-07-27 20:37:56 +00001178 : fHeaderIter(glyphbuf, total_size), fCurveIter(), fPointIter()
1179 { }
reed@google.comac6b9792011-03-11 15:42:51 +00001180
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001181 POINTFX const * next() {
bungeman@google.com0abbff92013-07-27 20:37:56 +00001182nextHeader:
1183 if (!fCurveIter.isSet()) {
1184 const TTPOLYGONHEADER* header = fHeaderIter.next();
halcanary96fcdcc2015-08-27 07:41:13 -07001185 if (nullptr == header) {
1186 return nullptr;
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001187 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001188 fCurveIter.set(header);
1189 const TTPOLYCURVE* curve = fCurveIter.next();
halcanary96fcdcc2015-08-27 07:41:13 -07001190 if (nullptr == curve) {
1191 return nullptr;
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001192 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001193 fPointIter.set(curve);
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001194 return &header->pfxStart;
bungeman@google.com05a729f2013-06-20 15:29:16 +00001195 }
1196
bungeman@google.com0abbff92013-07-27 20:37:56 +00001197 const POINTFX* nextPoint = fPointIter.next();
halcanary96fcdcc2015-08-27 07:41:13 -07001198 if (nullptr == nextPoint) {
bungeman@google.com0abbff92013-07-27 20:37:56 +00001199 const TTPOLYCURVE* curve = fCurveIter.next();
halcanary96fcdcc2015-08-27 07:41:13 -07001200 if (nullptr == curve) {
bungeman@google.com0abbff92013-07-27 20:37:56 +00001201 fCurveIter.set();
1202 goto nextHeader;
1203 } else {
1204 fPointIter.set(curve);
bungeman@google.com05a729f2013-06-20 15:29:16 +00001205 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001206 nextPoint = fPointIter.next();
reed@google.comac6b9792011-03-11 15:42:51 +00001207 }
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001208 return nextPoint;
reed@google.comac6b9792011-03-11 15:42:51 +00001209 }
bungeman@google.comd1c7f712013-03-11 18:51:19 +00001210
bungeman@google.com0abbff92013-07-27 20:37:56 +00001211 WORD currentCurveType() {
1212 return fPointIter.fCurveType;
1213 }
1214
1215private:
1216 /** Iterates over all of the polygon headers in a glyphbuf. */
1217 class GDIPolygonHeaderIter {
1218 public:
skia.committer@gmail.com27e21fe2013-07-28 07:01:03 +00001219 GDIPolygonHeaderIter(const uint8_t* glyphbuf, DWORD total_size)
bungeman@google.com0abbff92013-07-27 20:37:56 +00001220 : fCurPolygon(reinterpret_cast<const TTPOLYGONHEADER*>(glyphbuf))
1221 , fEndPolygon(SkTAddOffset<const TTPOLYGONHEADER>(glyphbuf, total_size))
1222 { }
1223
1224 const TTPOLYGONHEADER* next() {
1225 if (fCurPolygon >= fEndPolygon) {
halcanary96fcdcc2015-08-27 07:41:13 -07001226 return nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001227 }
1228 const TTPOLYGONHEADER* thisPolygon = fCurPolygon;
1229 fCurPolygon = SkTAddOffset<const TTPOLYGONHEADER>(fCurPolygon, fCurPolygon->cb);
1230 return thisPolygon;
1231 }
1232 private:
1233 const TTPOLYGONHEADER* fCurPolygon;
1234 const TTPOLYGONHEADER* fEndPolygon;
1235 };
1236
1237 /** Iterates over all of the polygon curves in a polygon header. */
1238 class GDIPolygonCurveIter {
1239 public:
halcanary96fcdcc2015-08-27 07:41:13 -07001240 GDIPolygonCurveIter() : fCurCurve(nullptr), fEndCurve(nullptr) { }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001241
1242 GDIPolygonCurveIter(const TTPOLYGONHEADER* curPolygon)
1243 : fCurCurve(SkTAddOffset<const TTPOLYCURVE>(curPolygon, sizeof(TTPOLYGONHEADER)))
1244 , fEndCurve(SkTAddOffset<const TTPOLYCURVE>(curPolygon, curPolygon->cb))
1245 { }
1246
halcanary96fcdcc2015-08-27 07:41:13 -07001247 bool isSet() { return fCurCurve != nullptr; }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001248
1249 void set(const TTPOLYGONHEADER* curPolygon) {
1250 fCurCurve = SkTAddOffset<const TTPOLYCURVE>(curPolygon, sizeof(TTPOLYGONHEADER));
1251 fEndCurve = SkTAddOffset<const TTPOLYCURVE>(curPolygon, curPolygon->cb);
1252 }
1253 void set() {
halcanary96fcdcc2015-08-27 07:41:13 -07001254 fCurCurve = nullptr;
1255 fEndCurve = nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001256 }
1257
1258 const TTPOLYCURVE* next() {
1259 if (fCurCurve >= fEndCurve) {
halcanary96fcdcc2015-08-27 07:41:13 -07001260 return nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001261 }
1262 const TTPOLYCURVE* thisCurve = fCurCurve;
1263 fCurCurve = SkTAddOffset<const TTPOLYCURVE>(fCurCurve, size_of_TTPOLYCURVE(*fCurCurve));
1264 return thisCurve;
1265 }
1266 private:
1267 size_t size_of_TTPOLYCURVE(const TTPOLYCURVE& curve) {
1268 return 2*sizeof(WORD) + curve.cpfx*sizeof(POINTFX);
1269 }
1270 const TTPOLYCURVE* fCurCurve;
1271 const TTPOLYCURVE* fEndCurve;
1272 };
1273
1274 /** Iterates over all of the polygon points in a polygon curve. */
1275 class GDIPolygonCurvePointIter {
1276 public:
halcanary96fcdcc2015-08-27 07:41:13 -07001277 GDIPolygonCurvePointIter() : fCurveType(0), fCurPoint(nullptr), fEndPoint(nullptr) { }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001278
1279 GDIPolygonCurvePointIter(const TTPOLYCURVE* curPolygon)
1280 : fCurveType(curPolygon->wType)
1281 , fCurPoint(&curPolygon->apfx[0])
1282 , fEndPoint(&curPolygon->apfx[curPolygon->cpfx])
1283 { }
1284
halcanary96fcdcc2015-08-27 07:41:13 -07001285 bool isSet() { return fCurPoint != nullptr; }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001286
1287 void set(const TTPOLYCURVE* curPolygon) {
1288 fCurveType = curPolygon->wType;
1289 fCurPoint = &curPolygon->apfx[0];
1290 fEndPoint = &curPolygon->apfx[curPolygon->cpfx];
1291 }
1292 void set() {
halcanary96fcdcc2015-08-27 07:41:13 -07001293 fCurPoint = nullptr;
1294 fEndPoint = nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001295 }
1296
1297 const POINTFX* next() {
1298 if (fCurPoint >= fEndPoint) {
halcanary96fcdcc2015-08-27 07:41:13 -07001299 return nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001300 }
1301 const POINTFX* thisPoint = fCurPoint;
1302 ++fCurPoint;
1303 return thisPoint;
1304 }
1305
1306 WORD fCurveType;
1307 private:
1308 const POINTFX* fCurPoint;
1309 const POINTFX* fEndPoint;
1310 };
1311
1312 GDIPolygonHeaderIter fHeaderIter;
1313 GDIPolygonCurveIter fCurveIter;
1314 GDIPolygonCurvePointIter fPointIter;
1315};
1316
1317static void sk_path_from_gdi_path(SkPath* path, const uint8_t* glyphbuf, DWORD total_size) {
bungeman@google.comd1c7f712013-03-11 18:51:19 +00001318 const uint8_t* cur_glyph = glyphbuf;
1319 const uint8_t* end_glyph = glyphbuf + total_size;
1320
1321 while (cur_glyph < end_glyph) {
1322 const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
1323
1324 const uint8_t* end_poly = cur_glyph + th->cb;
1325 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
1326
1327 path->moveTo(SkFixedToScalar( SkFIXEDToFixed(th->pfxStart.x)),
1328 SkFixedToScalar(-SkFIXEDToFixed(th->pfxStart.y)));
1329
1330 while (cur_poly < end_poly) {
1331 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
1332
1333 if (pc->wType == TT_PRIM_LINE) {
1334 for (uint16_t i = 0; i < pc->cpfx; i++) {
1335 path->lineTo(SkFixedToScalar( SkFIXEDToFixed(pc->apfx[i].x)),
1336 SkFixedToScalar(-SkFIXEDToFixed(pc->apfx[i].y)));
1337 }
1338 }
1339
1340 if (pc->wType == TT_PRIM_QSPLINE) {
1341 for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
1342 POINTFX pnt_b = pc->apfx[u]; // B is always the current point
1343 POINTFX pnt_c = pc->apfx[u+1];
1344
1345 if (u < pc->cpfx - 2) { // If not on last spline, compute C
1346 pnt_c.x = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.x),
1347 SkFIXEDToFixed(pnt_c.x)));
1348 pnt_c.y = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.y),
1349 SkFIXEDToFixed(pnt_c.y)));
1350 }
1351
1352 path->quadTo(SkFixedToScalar( SkFIXEDToFixed(pnt_b.x)),
1353 SkFixedToScalar(-SkFIXEDToFixed(pnt_b.y)),
1354 SkFixedToScalar( SkFIXEDToFixed(pnt_c.x)),
1355 SkFixedToScalar(-SkFIXEDToFixed(pnt_c.y)));
1356 }
1357 }
1358 // Advance past this TTPOLYCURVE.
1359 cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx;
1360 }
1361 cur_glyph += th->cb;
1362 path->close();
reed@google.comac6b9792011-03-11 15:42:51 +00001363 }
reed@google.comac6b9792011-03-11 15:42:51 +00001364}
1365
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001366#define move_next_expected_hinted_point(iter, pElem) do {\
1367 pElem = iter.next(); \
halcanary96fcdcc2015-08-27 07:41:13 -07001368 if (nullptr == pElem) return false; \
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001369} while(0)
1370
1371// It is possible for the hinted and unhinted versions of the same path to have
1372// a different number of points due to GDI's handling of flipped points.
1373// If this is detected, this will return false.
1374static bool sk_path_from_gdi_paths(SkPath* path, const uint8_t* glyphbuf, DWORD total_size,
bungeman@google.com0abbff92013-07-27 20:37:56 +00001375 GDIGlyphbufferPointIter hintedYs) {
1376 const uint8_t* cur_glyph = glyphbuf;
1377 const uint8_t* end_glyph = glyphbuf + total_size;
1378
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001379 POINTFX const * hintedPoint;
1380
bungeman@google.com0abbff92013-07-27 20:37:56 +00001381 while (cur_glyph < end_glyph) {
1382 const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
1383
1384 const uint8_t* end_poly = cur_glyph + th->cb;
1385 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
1386
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001387 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001388 path->moveTo(SkFixedToScalar( SkFIXEDToFixed(th->pfxStart.x)),
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001389 SkFixedToScalar(-SkFIXEDToFixed(hintedPoint->y)));
bungeman@google.com0abbff92013-07-27 20:37:56 +00001390
1391 while (cur_poly < end_poly) {
1392 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
1393
1394 if (pc->wType == TT_PRIM_LINE) {
1395 for (uint16_t i = 0; i < pc->cpfx; i++) {
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001396 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001397 path->lineTo(SkFixedToScalar( SkFIXEDToFixed(pc->apfx[i].x)),
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001398 SkFixedToScalar(-SkFIXEDToFixed(hintedPoint->y)));
bungeman@google.com0abbff92013-07-27 20:37:56 +00001399 }
1400 }
1401
1402 if (pc->wType == TT_PRIM_QSPLINE) {
1403 POINTFX currentPoint = pc->apfx[0];
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001404 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001405 // only take the hinted y if it wasn't flipped
1406 if (hintedYs.currentCurveType() == TT_PRIM_QSPLINE) {
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001407 currentPoint.y = hintedPoint->y;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001408 }
1409 for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
1410 POINTFX pnt_b = currentPoint;//pc->apfx[u]; // B is always the current point
1411 POINTFX pnt_c = pc->apfx[u+1];
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001412 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001413 // only take the hinted y if it wasn't flipped
1414 if (hintedYs.currentCurveType() == TT_PRIM_QSPLINE) {
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001415 pnt_c.y = hintedPoint->y;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001416 }
1417 currentPoint.x = pnt_c.x;
1418 currentPoint.y = pnt_c.y;
1419
1420 if (u < pc->cpfx - 2) { // If not on last spline, compute C
1421 pnt_c.x = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.x),
1422 SkFIXEDToFixed(pnt_c.x)));
1423 pnt_c.y = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.y),
1424 SkFIXEDToFixed(pnt_c.y)));
1425 }
1426
1427 path->quadTo(SkFixedToScalar( SkFIXEDToFixed(pnt_b.x)),
1428 SkFixedToScalar(-SkFIXEDToFixed(pnt_b.y)),
1429 SkFixedToScalar( SkFIXEDToFixed(pnt_c.x)),
1430 SkFixedToScalar(-SkFIXEDToFixed(pnt_c.y)));
1431 }
1432 }
1433 // Advance past this TTPOLYCURVE.
1434 cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx;
1435 }
1436 cur_glyph += th->cb;
1437 path->close();
1438 }
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001439 return true;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001440}
1441
Ben Wagner6e9ac122016-11-11 14:31:06 -05001442DWORD SkScalerContext_GDI::getGDIGlyphPath(SkGlyphID glyph, UINT flags,
1443 SkAutoSTMalloc<BUFFERSIZE, uint8_t>* glyphbuf)
bungeman@google.com0abbff92013-07-27 20:37:56 +00001444{
1445 GLYPHMETRICS gm;
1446
Ben Wagner6e9ac122016-11-11 14:31:06 -05001447 DWORD total_size = GetGlyphOutlineW(fDDC, glyph, flags, &gm, BUFFERSIZE, glyphbuf->get(), &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001448 // Sometimes GetGlyphOutlineW returns a number larger than BUFFERSIZE even if BUFFERSIZE > 0.
1449 // It has been verified that this does not involve a buffer overrun.
1450 if (GDI_ERROR == total_size || total_size > BUFFERSIZE) {
1451 // GDI_ERROR because the BUFFERSIZE was too small, or because the data was not accessible.
1452 // When the data is not accessable GetGlyphOutlineW fails rather quickly,
1453 // so just try to get the size. If that fails then ensure the data is accessible.
Ben Wagner6e9ac122016-11-11 14:31:06 -05001454 total_size = GetGlyphOutlineW(fDDC, glyph, flags, &gm, 0, nullptr, &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001455 if (GDI_ERROR == total_size) {
1456 LogFontTypeface::EnsureAccessible(this->getTypeface());
Ben Wagner6e9ac122016-11-11 14:31:06 -05001457 total_size = GetGlyphOutlineW(fDDC, glyph, flags, &gm, 0, nullptr, &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001458 if (GDI_ERROR == total_size) {
kkinnunenc6cb56f2014-06-24 00:12:27 -07001459 // GetGlyphOutlineW is known to fail for some characters, such as spaces.
1460 // In these cases, just return that the glyph does not have a shape.
bungeman@google.com0abbff92013-07-27 20:37:56 +00001461 return 0;
1462 }
1463 }
1464
1465 glyphbuf->reset(total_size);
1466
Ben Wagner6e9ac122016-11-11 14:31:06 -05001467 DWORD ret = GetGlyphOutlineW(fDDC, glyph, flags, &gm, total_size, glyphbuf->get(), &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001468 if (GDI_ERROR == ret) {
1469 LogFontTypeface::EnsureAccessible(this->getTypeface());
Ben Wagner6e9ac122016-11-11 14:31:06 -05001470 ret = GetGlyphOutlineW(fDDC, glyph, flags, &gm, total_size, glyphbuf->get(), &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001471 if (GDI_ERROR == ret) {
1472 SkASSERT(false);
1473 return 0;
1474 }
1475 }
1476 }
1477 return total_size;
1478}
1479
Ben Wagner5ddb3082018-03-29 11:18:06 -04001480bool SkScalerContext_GDI::generatePath(SkGlyphID glyph, SkPath* path) {
dcheng3ba043f2015-07-08 13:25:23 -07001481 SkASSERT(path);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001482 SkASSERT(fDDC);
1483
1484 path->reset();
1485
1486 // Out of all the fonts on a typical Windows box,
1487 // 25% of glyphs require more than 2KB.
1488 // 1% of glyphs require more than 4KB.
1489 // 0.01% of glyphs require more than 8KB.
1490 // 8KB is less than 1% of the normal 1MB stack on Windows.
1491 // Note that some web fonts glyphs require more than 20KB.
1492 //static const DWORD BUFFERSIZE = (1 << 13);
1493
1494 //GDI only uses hinted outlines when axis aligned.
1495 UINT format = GGO_NATIVE | GGO_GLYPH_INDEX;
Ben Wagner5785e4a2019-05-07 16:50:29 -04001496 if (fRec.getHinting() == SkFontHinting::kNone || fRec.getHinting() == SkFontHinting::kSlight){
bungeman@google.com0abbff92013-07-27 20:37:56 +00001497 format |= GGO_UNHINTED;
1498 }
1499 SkAutoSTMalloc<BUFFERSIZE, uint8_t> glyphbuf(BUFFERSIZE);
1500 DWORD total_size = getGDIGlyphPath(glyph, format, &glyphbuf);
1501 if (0 == total_size) {
Ben Wagner5ddb3082018-03-29 11:18:06 -04001502 return false;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001503 }
1504
Ben Wagner5785e4a2019-05-07 16:50:29 -04001505 if (fRec.getHinting() != SkFontHinting::kSlight) {
bungeman@google.com0abbff92013-07-27 20:37:56 +00001506 sk_path_from_gdi_path(path, glyphbuf, total_size);
1507 } else {
1508 //GDI only uses hinted outlines when axis aligned.
1509 UINT format = GGO_NATIVE | GGO_GLYPH_INDEX;
1510
1511 SkAutoSTMalloc<BUFFERSIZE, uint8_t> hintedGlyphbuf(BUFFERSIZE);
1512 DWORD hinted_total_size = getGDIGlyphPath(glyph, format, &hintedGlyphbuf);
1513 if (0 == hinted_total_size) {
Ben Wagner5ddb3082018-03-29 11:18:06 -04001514 return false;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001515 }
1516
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001517 if (!sk_path_from_gdi_paths(path, glyphbuf, total_size,
1518 GDIGlyphbufferPointIter(hintedGlyphbuf, hinted_total_size)))
1519 {
1520 path->reset();
1521 sk_path_from_gdi_path(path, glyphbuf, total_size);
1522 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001523 }
Ben Wagner5ddb3082018-03-29 11:18:06 -04001524 return true;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001525}
1526
reed@google.com484f5bc2013-04-24 19:14:56 +00001527static void logfont_for_name(const char* familyName, LOGFONT* lf) {
1528 sk_bzero(lf, sizeof(LOGFONT));
bungeman@google.come70f7982012-06-01 19:38:19 +00001529#ifdef UNICODE
reed@google.com484f5bc2013-04-24 19:14:56 +00001530 // Get the buffer size needed first.
1531 size_t str_len = ::MultiByteToWideChar(CP_UTF8, 0, familyName,
halcanary96fcdcc2015-08-27 07:41:13 -07001532 -1, nullptr, 0);
reed@google.com484f5bc2013-04-24 19:14:56 +00001533 // Allocate a buffer (str_len already has terminating null
1534 // accounted for).
1535 wchar_t *wideFamilyName = new wchar_t[str_len];
1536 // Now actually convert the string.
1537 ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1,
1538 wideFamilyName, str_len);
1539 ::wcsncpy(lf->lfFaceName, wideFamilyName, LF_FACESIZE - 1);
1540 delete [] wideFamilyName;
1541 lf->lfFaceName[LF_FACESIZE-1] = L'\0';
bungeman@google.come70f7982012-06-01 19:38:19 +00001542#else
reed@google.com484f5bc2013-04-24 19:14:56 +00001543 ::strncpy(lf->lfFaceName, familyName, LF_FACESIZE - 1);
1544 lf->lfFaceName[LF_FACESIZE - 1] = '\0';
bungeman@google.come70f7982012-06-01 19:38:19 +00001545#endif
1546}
1547
bungemanb374d6a2014-09-17 07:48:59 -07001548void LogFontTypeface::onGetFamilyName(SkString* familyName) const {
bungeman@google.com7103f182012-10-31 20:53:49 +00001549 // Get the actual name of the typeface. The logfont may not know this.
reed@google.com5526ede2013-03-25 13:03:37 +00001550 HFONT font = CreateFontIndirect(&fLogFont);
bungeman@google.com7103f182012-10-31 20:53:49 +00001551
halcanary96fcdcc2015-08-27 07:41:13 -07001552 HDC deviceContext = ::CreateCompatibleDC(nullptr);
bungeman@google.com7103f182012-10-31 20:53:49 +00001553 HFONT savefont = (HFONT)SelectObject(deviceContext, font);
1554
bungemanb374d6a2014-09-17 07:48:59 -07001555 dcfontname_to_skstring(deviceContext, fLogFont, familyName);
bungeman@google.com7103f182012-10-31 20:53:49 +00001556
1557 if (deviceContext) {
1558 ::SelectObject(deviceContext, savefont);
1559 ::DeleteDC(deviceContext);
1560 }
1561 if (font) {
1562 ::DeleteObject(font);
1563 }
bungemanb374d6a2014-09-17 07:48:59 -07001564}
bungeman@google.com7103f182012-10-31 20:53:49 +00001565
bungemanb374d6a2014-09-17 07:48:59 -07001566void LogFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc,
1567 bool* isLocalStream) const {
1568 SkString familyName;
1569 this->onGetFamilyName(&familyName);
reed@google.com5526ede2013-03-25 13:03:37 +00001570 desc->setFamilyName(familyName.c_str());
bungemanb8113782016-07-25 16:54:59 -07001571 desc->setStyle(this->fontStyle());
reed@google.com5526ede2013-03-25 13:03:37 +00001572 *isLocalStream = this->fSerializeAsStream;
reed@google.comac6b9792011-03-11 15:42:51 +00001573}
1574
Hal Canary46cc3da2018-05-09 11:50:34 -04001575void LogFontTypeface::getGlyphToUnicodeMap(SkUnichar* dstArray) const {
1576 HDC hdc = ::CreateCompatibleDC(nullptr);
1577 HFONT font = CreateFontIndirect(&fLogFont);
1578 HFONT savefont = (HFONT)SelectObject(hdc, font);
1579 LOGFONT lf = fLogFont;
1580 HFONT designFont = CreateFontIndirect(&lf);
1581 SelectObject(hdc, designFont);
1582
1583 unsigned int glyphCount = calculateGlyphCount(hdc, fLogFont);
1584 populate_glyph_to_unicode(hdc, glyphCount, dstArray);
1585
1586 SelectObject(hdc, savefont);
1587 DeleteObject(designFont);
1588 DeleteObject(font);
1589 DeleteDC(hdc);
1590}
1591
Hal Canary209e4b12017-05-04 14:23:55 -04001592std::unique_ptr<SkAdvancedTypefaceMetrics> LogFontTypeface::onGetAdvancedMetrics() const {
reed@google.com2689f612013-03-20 20:01:47 +00001593 LOGFONT lf = fLogFont;
Hal Canary209e4b12017-05-04 14:23:55 -04001594 std::unique_ptr<SkAdvancedTypefaceMetrics> info(nullptr);
reed@google.comac6b9792011-03-11 15:42:51 +00001595
halcanary96fcdcc2015-08-27 07:41:13 -07001596 HDC hdc = CreateCompatibleDC(nullptr);
reed@google.comac6b9792011-03-11 15:42:51 +00001597 HFONT font = CreateFontIndirect(&lf);
1598 HFONT savefont = (HFONT)SelectObject(hdc, font);
halcanary96fcdcc2015-08-27 07:41:13 -07001599 HFONT designFont = nullptr;
reed@google.comac6b9792011-03-11 15:42:51 +00001600
reed@google.com05b6f3a2011-11-28 15:30:28 +00001601 const char stem_chars[] = {'i', 'I', '!', '1'};
1602 int16_t min_width;
1603 unsigned glyphCount;
1604
reed@google.comac6b9792011-03-11 15:42:51 +00001605 // To request design units, create a logical font whose height is specified
1606 // as unitsPerEm.
1607 OUTLINETEXTMETRIC otm;
bungeman@google.com39698b12011-11-15 22:26:41 +00001608 unsigned int otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
1609 if (0 == otmRet) {
reed@google.com055180c2013-03-21 18:46:35 +00001610 call_ensure_accessible(lf);
bungeman@google.com39698b12011-11-15 22:26:41 +00001611 otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
1612 }
1613 if (!otmRet || !GetTextFace(hdc, LF_FACESIZE, lf.lfFaceName)) {
reed@google.comac6b9792011-03-11 15:42:51 +00001614 goto Error;
1615 }
1616 lf.lfHeight = -SkToS32(otm.otmEMSquare);
1617 designFont = CreateFontIndirect(&lf);
1618 SelectObject(hdc, designFont);
1619 if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) {
1620 goto Error;
1621 }
bungeman@google.com7bdd6142013-07-15 19:42:57 +00001622 glyphCount = calculateGlyphCount(hdc, fLogFont);
reed@google.comac6b9792011-03-11 15:42:51 +00001623
Hal Canary209e4b12017-05-04 14:23:55 -04001624 info.reset(new SkAdvancedTypefaceMetrics);
bungeman@google.com7103f182012-10-31 20:53:49 +00001625 tchar_to_skstring(lf.lfFaceName, &info->fFontName);
Hal Canarya865d252017-11-09 16:16:09 -05001626
1627 SkOTTableOS2_V4::Type fsType;
1628 if (sizeof(fsType) == this->getTableData(SkTEndian_SwapBE32(SkOTTableOS2::TAG),
1629 offsetof(SkOTTableOS2_V4, fsType),
1630 sizeof(fsType),
1631 &fsType)) {
1632 SkOTUtils::SetAdvancedTypefaceFlags(fsType, info.get());
1633 } else {
1634 // If bit 1 is set, the font may not be embedded in a document.
1635 // If bit 1 is clear, the font can be embedded.
1636 // If bit 2 is set, the embedding is read-only.
1637 if (otm.otmfsType & 0x1) {
1638 info->fFlags |= SkAdvancedTypefaceMetrics::kNotEmbeddable_FontFlag;
1639 }
vandebo0f9bad02014-06-19 11:05:39 -07001640 }
reed@google.comac6b9792011-03-11 15:42:51 +00001641
vandebo@chromium.orgd41e70d2012-03-08 19:41:01 +00001642 if (glyphCount > 0 &&
1643 (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE)) {
reed@google.comac6b9792011-03-11 15:42:51 +00001644 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
1645 } else {
edisonn@google.com390c6d72013-04-06 20:26:15 +00001646 goto ReturnInfo;
reed@google.comac6b9792011-03-11 15:42:51 +00001647 }
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +00001648
reed@google.comac6b9792011-03-11 15:42:51 +00001649 // If this bit is clear the font is a fixed pitch font.
1650 if (!(otm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
1651 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1652 }
1653 if (otm.otmTextMetrics.tmItalic) {
1654 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1655 }
reed@google.comac6b9792011-03-11 15:42:51 +00001656 if (otm.otmTextMetrics.tmPitchAndFamily & FF_ROMAN) {
1657 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1658 } else if (otm.otmTextMetrics.tmPitchAndFamily & FF_SCRIPT) {
1659 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1660 }
1661
1662 // The main italic angle of the font, in tenths of a degree counterclockwise
1663 // from vertical.
1664 info->fItalicAngle = otm.otmItalicAngle / 10;
1665 info->fAscent = SkToS16(otm.otmTextMetrics.tmAscent);
1666 info->fDescent = SkToS16(-otm.otmTextMetrics.tmDescent);
1667 // TODO(ctguil): Use alternate cap height calculation.
1668 // MSDN says otmsCapEmHeight is not support but it is returning a value on
1669 // my Win7 box.
1670 info->fCapHeight = otm.otmsCapEmHeight;
1671 info->fBBox =
1672 SkIRect::MakeLTRB(otm.otmrcFontBox.left, otm.otmrcFontBox.top,
1673 otm.otmrcFontBox.right, otm.otmrcFontBox.bottom);
1674
1675 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1676 // This probably isn't very good with an italic font.
reed@google.com05b6f3a2011-11-28 15:30:28 +00001677 min_width = SHRT_MAX;
reed@google.comac6b9792011-03-11 15:42:51 +00001678 info->fStemV = 0;
reed@google.comac6b9792011-03-11 15:42:51 +00001679 for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {
1680 ABC abcWidths;
1681 if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) {
1682 int16_t width = abcWidths.abcB;
1683 if (width > 0 && width < min_width) {
1684 min_width = width;
1685 info->fStemV = min_width;
1686 }
1687 }
1688 }
1689
reed@google.comac6b9792011-03-11 15:42:51 +00001690Error:
edisonn@google.com390c6d72013-04-06 20:26:15 +00001691ReturnInfo:
reed@google.comac6b9792011-03-11 15:42:51 +00001692 SelectObject(hdc, savefont);
1693 DeleteObject(designFont);
1694 DeleteObject(font);
1695 DeleteDC(hdc);
1696
1697 return info;
1698}
1699
bungeman@google.coma5501992012-05-18 19:06:41 +00001700//Dummy representation of a Base64 encoded GUID from create_unique_font_name.
1701#define BASE64_GUID_ID "XXXXXXXXXXXXXXXXXXXXXXXX"
halcanary96fcdcc2015-08-27 07:41:13 -07001702//Length of GUID representation from create_id, including nullptr terminator.
bungeman@google.coma5501992012-05-18 19:06:41 +00001703#define BASE64_GUID_ID_LEN SK_ARRAY_COUNT(BASE64_GUID_ID)
reed@google.comac6b9792011-03-11 15:42:51 +00001704
bungeman99fe8222015-08-20 07:57:51 -07001705static_assert(BASE64_GUID_ID_LEN < LF_FACESIZE, "GUID_longer_than_facesize");
bungeman@google.coma5501992012-05-18 19:06:41 +00001706
1707/**
1708 NameID 6 Postscript names cannot have the character '/'.
1709 It would be easier to hex encode the GUID, but that is 32 bytes,
1710 and many systems have issues with names longer than 28 bytes.
1711 The following need not be any standard base64 encoding.
1712 The encoded value is never decoded.
1713*/
rmistry@google.comd6176b02012-08-23 18:14:13 +00001714static const char postscript_safe_base64_encode[] =
bungeman@google.coma5501992012-05-18 19:06:41 +00001715 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1716 "abcdefghijklmnopqrstuvwxyz"
1717 "0123456789-_=";
1718
1719/**
1720 Formats a GUID into Base64 and places it into buffer.
1721 buffer should have space for at least BASE64_GUID_ID_LEN characters.
1722 The string will always be null terminated.
1723 XXXXXXXXXXXXXXXXXXXXXXXX0
1724 */
1725static void format_guid_b64(const GUID& guid, char* buffer, size_t bufferSize) {
1726 SkASSERT(bufferSize >= BASE64_GUID_ID_LEN);
1727 size_t written = SkBase64::Encode(&guid, sizeof(guid), buffer, postscript_safe_base64_encode);
1728 SkASSERT(written < LF_FACESIZE);
1729 buffer[written] = '\0';
1730}
1731
1732/**
1733 Creates a Base64 encoded GUID and places it into buffer.
1734 buffer should have space for at least BASE64_GUID_ID_LEN characters.
1735 The string will always be null terminated.
1736 XXXXXXXXXXXXXXXXXXXXXXXX0
1737 */
1738static HRESULT create_unique_font_name(char* buffer, size_t bufferSize) {
1739 GUID guid = {};
1740 if (FAILED(CoCreateGuid(&guid))) {
1741 return E_UNEXPECTED;
1742 }
1743 format_guid_b64(guid, buffer, bufferSize);
1744
1745 return S_OK;
1746}
1747
1748/**
halcanary96fcdcc2015-08-27 07:41:13 -07001749 Introduces a font to GDI. On failure will return nullptr. The returned handle
bungeman@google.coma5501992012-05-18 19:06:41 +00001750 should eventually be passed to RemoveFontMemResourceEx.
1751*/
1752static HANDLE activate_font(SkData* fontData) {
1753 DWORD numFonts = 0;
1754 //AddFontMemResourceEx just copies the data, but does not specify const.
1755 HANDLE fontHandle = AddFontMemResourceEx(const_cast<void*>(fontData->data()),
bungeman@google.com4b18f572013-07-22 15:21:23 +00001756 static_cast<DWORD>(fontData->size()),
bungeman@google.coma5501992012-05-18 19:06:41 +00001757 0,
1758 &numFonts);
1759
halcanary96fcdcc2015-08-27 07:41:13 -07001760 if (fontHandle != nullptr && numFonts < 1) {
bungeman@google.coma5501992012-05-18 19:06:41 +00001761 RemoveFontMemResourceEx(fontHandle);
halcanary96fcdcc2015-08-27 07:41:13 -07001762 return nullptr;
bungeman@google.coma5501992012-05-18 19:06:41 +00001763 }
1764
1765 return fontHandle;
1766}
1767
scroggoa1193e42015-01-21 12:09:53 -08001768// Does not affect ownership of stream.
Mike Reed59227392017-09-26 09:46:08 -04001769static sk_sp<SkTypeface> create_from_stream(std::unique_ptr<SkStreamAsset> stream) {
bungeman@google.coma5501992012-05-18 19:06:41 +00001770 // Create a unique and unpredictable font name.
1771 // Avoids collisions and access from CSS.
1772 char familyName[BASE64_GUID_ID_LEN];
1773 const int familyNameSize = SK_ARRAY_COUNT(familyName);
1774 if (FAILED(create_unique_font_name(familyName, familyNameSize))) {
halcanary96fcdcc2015-08-27 07:41:13 -07001775 return nullptr;
bungeman@google.coma5501992012-05-18 19:06:41 +00001776 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001777
bungeman@google.coma5501992012-05-18 19:06:41 +00001778 // Change the name of the font.
Mike Reed59227392017-09-26 09:46:08 -04001779 sk_sp<SkData> rewrittenFontData(SkOTUtils::RenameFont(stream.get(), familyName, familyNameSize-1));
halcanary96fcdcc2015-08-27 07:41:13 -07001780 if (nullptr == rewrittenFontData.get()) {
1781 return nullptr;
bungeman@google.coma5501992012-05-18 19:06:41 +00001782 }
bungeman@google.coma5501992012-05-18 19:06:41 +00001783
1784 // Register the font with GDI.
bungeman@google.come9bbee32012-05-21 13:46:13 +00001785 HANDLE fontReference = activate_font(rewrittenFontData.get());
halcanary96fcdcc2015-08-27 07:41:13 -07001786 if (nullptr == fontReference) {
1787 return nullptr;
bungeman@google.coma5501992012-05-18 19:06:41 +00001788 }
1789
1790 // Create the typeface.
1791 LOGFONT lf;
reed@google.com484f5bc2013-04-24 19:14:56 +00001792 logfont_for_name(familyName, &lf);
bungeman@google.coma5501992012-05-18 19:06:41 +00001793
Mike Reed59227392017-09-26 09:46:08 -04001794 return sk_sp<SkTypeface>(SkCreateFontMemResourceTypefaceFromLOGFONT(lf, fontReference));
reed@google.comac6b9792011-03-11 15:42:51 +00001795}
1796
Ben Wagner4212a7d2019-02-25 14:27:46 -05001797std::unique_ptr<SkStreamAsset> LogFontTypeface::onOpenStream(int* ttcIndex) const {
reed@google.com0042b9c2013-03-21 20:16:04 +00001798 *ttcIndex = 0;
1799
Ben Wagner4212a7d2019-02-25 14:27:46 -05001800 const DWORD kTTCTag = SkEndian_SwapBE32(SkSetFourByteTag('t', 't', 'c', 'f'));
reed@google.com0042b9c2013-03-21 20:16:04 +00001801 LOGFONT lf = fLogFont;
reed@google.comac6b9792011-03-11 15:42:51 +00001802
halcanary96fcdcc2015-08-27 07:41:13 -07001803 HDC hdc = ::CreateCompatibleDC(nullptr);
reed@google.com59d2f632011-05-02 19:36:59 +00001804 HFONT font = CreateFontIndirect(&lf);
reed@google.comac6b9792011-03-11 15:42:51 +00001805 HFONT savefont = (HFONT)SelectObject(hdc, font);
1806
Ben Wagner4212a7d2019-02-25 14:27:46 -05001807 std::unique_ptr<SkStreamAsset> stream;
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001808 DWORD tables[2] = {kTTCTag, 0};
Chris Dalton1ef80942017-12-04 12:01:30 -07001809 for (size_t i = 0; i < SK_ARRAY_COUNT(tables); i++) {
halcanary96fcdcc2015-08-27 07:41:13 -07001810 DWORD bufferSize = GetFontData(hdc, tables[i], 0, nullptr, 0);
bungeman@google.com39698b12011-11-15 22:26:41 +00001811 if (bufferSize == GDI_ERROR) {
reed@google.com055180c2013-03-21 18:46:35 +00001812 call_ensure_accessible(lf);
halcanary96fcdcc2015-08-27 07:41:13 -07001813 bufferSize = GetFontData(hdc, tables[i], 0, nullptr, 0);
bungeman@google.com39698b12011-11-15 22:26:41 +00001814 }
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001815 if (bufferSize != GDI_ERROR) {
Ben Wagner4212a7d2019-02-25 14:27:46 -05001816 stream.reset(new SkMemoryStream(bufferSize));
bungeman@google.com4b18f572013-07-22 15:21:23 +00001817 if (GetFontData(hdc, tables[i], 0, (void*)stream->getMemoryBase(), bufferSize)) {
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001818 break;
1819 } else {
Ben Wagner4212a7d2019-02-25 14:27:46 -05001820 stream.reset();
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001821 }
1822 }
reed@google.comac6b9792011-03-11 15:42:51 +00001823 }
1824
1825 SelectObject(hdc, savefont);
1826 DeleteObject(font);
1827 DeleteDC(hdc);
1828
1829 return stream;
1830}
1831
Bruce Wang536ad2c2018-06-25 11:37:25 -04001832sk_sp<SkTypeface> LogFontTypeface::onMakeClone(const SkFontArguments& args) const {
1833 return sk_ref_sp(this);
1834}
1835
bungeman@google.com3c996f82013-10-24 21:39:35 +00001836static void bmpCharsToGlyphs(HDC hdc, const WCHAR* bmpChars, int count, uint16_t* glyphs,
1837 bool Ox1FHack)
1838{
Ben Wagner0a8da212019-04-16 14:01:06 -04001839 // Type1 fonts fail with uniscribe API. Use GetGlyphIndices for plane 0.
1840
1841 /** Real documentation for GetGlyphIndicesW:
1842 *
1843 * When GGI_MARK_NONEXISTING_GLYPHS is not specified and a character does not map to a
1844 * glyph, then the 'default character's glyph is returned instead. The 'default character'
1845 * is available in fTM.tmDefaultChar. FON fonts have a default character, and there exists
1846 * a usDefaultChar in the 'OS/2' table, version 2 and later. If there is no
1847 * 'default character' specified by the font, then often the first character found is used.
1848 *
1849 * When GGI_MARK_NONEXISTING_GLYPHS is specified and a character does not map to a glyph,
1850 * then the glyph 0xFFFF is used. In Windows XP and earlier, Bitmap/Vector FON usually use
1851 * glyph 0x1F instead ('Terminal' appears to be special, returning 0xFFFF).
1852 * Type1 PFM/PFB, TT, OT TT, OT CFF all appear to use 0xFFFF, even on XP.
1853 */
bungeman@google.com3c996f82013-10-24 21:39:35 +00001854 DWORD result = GetGlyphIndicesW(hdc, bmpChars, count, glyphs, GGI_MARK_NONEXISTING_GLYPHS);
1855 if (GDI_ERROR == result) {
1856 for (int i = 0; i < count; ++i) {
1857 glyphs[i] = 0;
1858 }
1859 return;
1860 }
1861
1862 if (Ox1FHack) {
1863 for (int i = 0; i < count; ++i) {
1864 if (0xFFFF == glyphs[i] || 0x1F == glyphs[i]) {
1865 glyphs[i] = 0;
1866 }
1867 }
1868 } else {
1869 for (int i = 0; i < count; ++i) {
1870 if (0xFFFF == glyphs[i]){
1871 glyphs[i] = 0;
1872 }
1873 }
1874 }
1875}
1876
1877static uint16_t nonBmpCharToGlyph(HDC hdc, SCRIPT_CACHE* scriptCache, const WCHAR utf16[2]) {
1878 uint16_t index = 0;
1879 // Use uniscribe to detemine glyph index for non-BMP characters.
1880 static const int numWCHAR = 2;
1881 static const int maxItems = 2;
halcanary96fcdcc2015-08-27 07:41:13 -07001882 // MSDN states that this can be nullptr, but some things don't work then.
Chris Dalton1ef80942017-12-04 12:01:30 -07001883 SCRIPT_CONTROL scriptControl;
1884 memset(&scriptControl, 0, sizeof(scriptControl));
bungeman@google.com3c996f82013-10-24 21:39:35 +00001885 // Add extra item to SCRIPT_ITEM to work around a bug (now documented).
1886 // https://bugzilla.mozilla.org/show_bug.cgi?id=366643
1887 SCRIPT_ITEM si[maxItems + 1];
1888 int numItems;
halcanary96fcdcc2015-08-27 07:41:13 -07001889 HRZM(ScriptItemize(utf16, numWCHAR, maxItems, &scriptControl, nullptr, si, &numItems),
bungeman@google.com3c996f82013-10-24 21:39:35 +00001890 "Could not itemize character.");
1891
1892 // Sometimes ScriptShape cannot find a glyph for a non-BMP and returns 2 space glyphs.
1893 static const int maxGlyphs = 2;
1894 SCRIPT_VISATTR vsa[maxGlyphs];
1895 WORD outGlyphs[maxGlyphs];
1896 WORD logClust[numWCHAR];
1897 int numGlyphs;
Ben Wagnera541db4d2019-05-22 17:33:07 -04001898 SCRIPT_ANALYSIS& script = si[0].a;
1899 script.eScript = SCRIPT_UNDEFINED;
1900 script.fRTL = FALSE;
1901 script.fLayoutRTL = FALSE;
1902 script.fLinkBefore = FALSE;
1903 script.fLinkAfter = FALSE;
1904 script.fLogicalOrder = FALSE;
1905 script.fNoGlyphIndex = FALSE;
1906 script.s.uBidiLevel = 0;
1907 script.s.fOverrideDirection = 0;
1908 script.s.fInhibitSymSwap = TRUE;
1909 script.s.fCharShape = FALSE;
1910 script.s.fDigitSubstitute = FALSE;
1911 script.s.fInhibitLigate = FALSE;
1912 script.s.fDisplayZWG = TRUE;
1913 script.s.fArabicNumContext = FALSE;
1914 script.s.fGcpClusters = FALSE;
1915 script.s.fReserved = 0;
1916 script.s.fEngineReserved = 0;
1917 // For the future, 0x80040200 from here is USP_E_SCRIPT_NOT_IN_FONT
1918 HRZM(ScriptShape(hdc, scriptCache, utf16, numWCHAR, maxGlyphs, &script,
bungeman@google.com3c996f82013-10-24 21:39:35 +00001919 outGlyphs, logClust, vsa, &numGlyphs),
1920 "Could not shape character.");
1921 if (1 == numGlyphs) {
1922 index = outGlyphs[0];
1923 }
1924 return index;
1925}
1926
1927class SkAutoHDC {
1928public:
1929 SkAutoHDC(const LOGFONT& lf)
halcanary96fcdcc2015-08-27 07:41:13 -07001930 : fHdc(::CreateCompatibleDC(nullptr))
bungeman@google.com3c996f82013-10-24 21:39:35 +00001931 , fFont(::CreateFontIndirect(&lf))
1932 , fSavefont((HFONT)SelectObject(fHdc, fFont))
1933 { }
1934 ~SkAutoHDC() {
1935 SelectObject(fHdc, fSavefont);
1936 DeleteObject(fFont);
1937 DeleteDC(fHdc);
1938 }
1939 operator HDC() { return fHdc; }
1940private:
1941 HDC fHdc;
1942 HFONT fFont;
1943 HFONT fSavefont;
1944};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +00001945#define SkAutoHDC(...) SK_REQUIRE_LOCAL_VAR(SkAutoHDC)
bungeman@google.com3c996f82013-10-24 21:39:35 +00001946
Mike Reed64670cb2019-04-16 11:37:38 -07001947void LogFontTypeface::onCharsToGlyphs(const SkUnichar uni[], int glyphCount,
1948 SkGlyphID glyphs[]) const
bungeman@google.com3c996f82013-10-24 21:39:35 +00001949{
1950 SkAutoHDC hdc(fLogFont);
1951
1952 TEXTMETRIC tm;
1953 if (0 == GetTextMetrics(hdc, &tm)) {
1954 call_ensure_accessible(fLogFont);
1955 if (0 == GetTextMetrics(hdc, &tm)) {
1956 tm.tmPitchAndFamily = TMPF_TRUETYPE;
1957 }
1958 }
1959 bool Ox1FHack = !(tm.tmPitchAndFamily & TMPF_VECTOR) /*&& winVer < Vista */;
1960
Robert Phillips33906ad2019-04-17 12:03:45 +00001961 SCRIPT_CACHE sc = 0;
Mike Reed64670cb2019-04-16 11:37:38 -07001962 static const int scratchCount = 256;
1963 WCHAR scratch[scratchCount];
1964 int glyphIndex = 0;
1965 const uint32_t* utf32 = reinterpret_cast<const uint32_t*>(uni);
1966 while (glyphIndex < glyphCount) {
1967 // Try a run of bmp.
1968 int glyphsLeft = SkTMin(glyphCount - glyphIndex, scratchCount);
1969 int runLength = 0;
1970 while (runLength < glyphsLeft && utf32[glyphIndex + runLength] <= 0xFFFF) {
1971 scratch[runLength] = static_cast<WCHAR>(utf32[glyphIndex + runLength]);
1972 ++runLength;
Mike Reed70ed05e2019-04-16 23:58:59 +00001973 }
Mike Reed64670cb2019-04-16 11:37:38 -07001974 if (runLength) {
1975 bmpCharsToGlyphs(hdc, scratch, runLength, &glyphs[glyphIndex], Ox1FHack);
1976 glyphIndex += runLength;
1977 }
Robert Phillips33906ad2019-04-17 12:03:45 +00001978
Mike Reed64670cb2019-04-16 11:37:38 -07001979 // Try a run of non-bmp.
1980 while (glyphIndex < glyphCount && utf32[glyphIndex] > 0xFFFF) {
1981 SkUTF::ToUTF16(utf32[glyphIndex], reinterpret_cast<uint16_t*>(scratch));
1982 glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, scratch);
1983 ++glyphIndex;
Robert Phillips33906ad2019-04-17 12:03:45 +00001984 }
bungeman@google.com3c996f82013-10-24 21:39:35 +00001985 }
1986
1987 if (sc) {
1988 ::ScriptFreeCache(&sc);
1989 }
bungeman@google.com3c996f82013-10-24 21:39:35 +00001990}
1991
bungeman@google.com7bdd6142013-07-15 19:42:57 +00001992int LogFontTypeface::onCountGlyphs() const {
halcanary96fcdcc2015-08-27 07:41:13 -07001993 HDC hdc = ::CreateCompatibleDC(nullptr);
bungeman@google.com7bdd6142013-07-15 19:42:57 +00001994 HFONT font = CreateFontIndirect(&fLogFont);
1995 HFONT savefont = (HFONT)SelectObject(hdc, font);
1996
1997 unsigned int glyphCount = calculateGlyphCount(hdc, fLogFont);
1998
1999 SelectObject(hdc, savefont);
2000 DeleteObject(font);
2001 DeleteDC(hdc);
2002
2003 return glyphCount;
2004}
2005
Ben Wagner2c2240f2019-04-17 16:04:30 -04002006void LogFontTypeface::getPostScriptGlyphNames(SkString*) const {}
2007
bungeman@google.com7bdd6142013-07-15 19:42:57 +00002008int LogFontTypeface::onGetUPEM() const {
halcanary96fcdcc2015-08-27 07:41:13 -07002009 HDC hdc = ::CreateCompatibleDC(nullptr);
bungeman@google.com7bdd6142013-07-15 19:42:57 +00002010 HFONT font = CreateFontIndirect(&fLogFont);
2011 HFONT savefont = (HFONT)SelectObject(hdc, font);
2012
2013 unsigned int upem = calculateUPEM(hdc, fLogFont);
2014
2015 SelectObject(hdc, savefont);
2016 DeleteObject(font);
2017 DeleteDC(hdc);
2018
2019 return upem;
2020}
2021
bungeman@google.com839702b2013-08-07 17:09:22 +00002022SkTypeface::LocalizedStrings* LogFontTypeface::onCreateFamilyNameIterator() const {
Ben Wagnerad031f52018-08-20 13:45:57 -04002023 sk_sp<SkTypeface::LocalizedStrings> nameIter =
2024 SkOTUtils::LocalizedStrings_NameTable::MakeForFamilyNames(*this);
2025 if (!nameIter) {
bungeman@google.coma9802692013-08-07 02:45:25 +00002026 SkString familyName;
2027 this->getFamilyName(&familyName);
2028 SkString language("und"); //undetermined
Ben Wagnerad031f52018-08-20 13:45:57 -04002029 nameIter = sk_make_sp<SkOTUtils::LocalizedStrings_SingleName>(familyName, language);
bungeman@google.coma9802692013-08-07 02:45:25 +00002030 }
Ben Wagnerad031f52018-08-20 13:45:57 -04002031 return nameIter.release();
bungeman@google.coma9802692013-08-07 02:45:25 +00002032}
2033
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002034int LogFontTypeface::onGetTableTags(SkFontTableTag tags[]) const {
2035 SkSFNTHeader header;
2036 if (sizeof(header) != this->onGetTableData(0, 0, sizeof(header), &header)) {
2037 return 0;
2038 }
2039
2040 int numTables = SkEndian_SwapBE16(header.numTables);
2041
2042 if (tags) {
2043 size_t size = numTables * sizeof(SkSFNTHeader::TableDirectoryEntry);
2044 SkAutoSTMalloc<0x20, SkSFNTHeader::TableDirectoryEntry> dir(numTables);
2045 if (size != this->onGetTableData(0, sizeof(header), size, dir.get())) {
2046 return 0;
2047 }
2048
2049 for (int i = 0; i < numTables; ++i) {
2050 tags[i] = SkEndian_SwapBE32(dir[i].tag);
2051 }
2052 }
2053 return numTables;
2054}
2055
2056size_t LogFontTypeface::onGetTableData(SkFontTableTag tag, size_t offset,
2057 size_t length, void* data) const
2058{
2059 LOGFONT lf = fLogFont;
2060
halcanary96fcdcc2015-08-27 07:41:13 -07002061 HDC hdc = ::CreateCompatibleDC(nullptr);
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002062 HFONT font = CreateFontIndirect(&lf);
2063 HFONT savefont = (HFONT)SelectObject(hdc, font);
2064
2065 tag = SkEndian_SwapBE32(tag);
halcanary96fcdcc2015-08-27 07:41:13 -07002066 if (nullptr == data) {
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002067 length = 0;
2068 }
robertphillips@google.com8b169312013-10-15 17:47:36 +00002069 DWORD bufferSize = GetFontData(hdc, tag, (DWORD) offset, data, (DWORD) length);
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002070 if (bufferSize == GDI_ERROR) {
2071 call_ensure_accessible(lf);
robertphillips@google.com8b169312013-10-15 17:47:36 +00002072 bufferSize = GetFontData(hdc, tag, (DWORD) offset, data, (DWORD) length);
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002073 }
2074
2075 SelectObject(hdc, savefont);
2076 DeleteObject(font);
2077 DeleteDC(hdc);
2078
2079 return bufferSize == GDI_ERROR ? 0 : bufferSize;
2080}
2081
reeda9322c22016-04-12 06:47:05 -07002082SkScalerContext* LogFontTypeface::onCreateScalerContext(const SkScalerContextEffects& effects,
2083 const SkDescriptor* desc) const {
bungeman7cfd46a2016-10-20 16:06:52 -04002084 auto ctx = skstd::make_unique<SkScalerContext_GDI>(
2085 sk_ref_sp(const_cast<LogFontTypeface*>(this)), effects, desc);
reed@google.com84e22d82013-07-10 15:38:20 +00002086 if (!ctx->isValid()) {
Ben Wagnerc05b2bf2016-11-03 16:51:26 -04002087 return nullptr;
reed@google.com84e22d82013-07-10 15:38:20 +00002088 }
bungeman7cfd46a2016-10-20 16:06:52 -04002089 return ctx.release();
reed@google.comac6b9792011-03-11 15:42:51 +00002090}
2091
reed@google.com0da48612013-03-19 16:06:52 +00002092void LogFontTypeface::onFilterRec(SkScalerContextRec* rec) const {
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00002093 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
2094 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
2095 {
2096 rec->fMaskFormat = SkMask::kA8_Format;
2097 rec->fFlags |= SkScalerContext::kGenA8FromLCD_Flag;
2098 }
2099
Mike Reedc88cc772018-10-23 12:05:47 -04002100 unsigned flagsWeDontSupport = SkScalerContext::kForceAutohinting_Flag |
reed@google.come8fab012011-07-13 15:25:33 +00002101 SkScalerContext::kEmbeddedBitmapText_Flag |
2102 SkScalerContext::kEmbolden_Flag |
2103 SkScalerContext::kLCD_BGROrder_Flag |
2104 SkScalerContext::kLCD_Vertical_Flag;
2105 rec->fFlags &= ~flagsWeDontSupport;
2106
Mike Reed04346d52018-11-05 12:45:32 -05002107 SkFontHinting h = rec->getHinting();
reed@google.come8fab012011-07-13 15:25:33 +00002108 switch (h) {
Ben Wagner5785e4a2019-05-07 16:50:29 -04002109 case SkFontHinting::kNone:
bungeman@google.com0abbff92013-07-27 20:37:56 +00002110 break;
Ben Wagner5785e4a2019-05-07 16:50:29 -04002111 case SkFontHinting::kSlight:
bungeman@google.com0abbff92013-07-27 20:37:56 +00002112 // Only do slight hinting when axis aligned.
bungeman@google.com7c86d8e2013-07-27 21:42:21 +00002113 // TODO: re-enable slight hinting when FontHostTest can pass.
2114 //if (!isAxisAligned(*rec)) {
Ben Wagner5785e4a2019-05-07 16:50:29 -04002115 h = SkFontHinting::kNone;
bungeman@google.com7c86d8e2013-07-27 21:42:21 +00002116 //}
reed@google.come8fab012011-07-13 15:25:33 +00002117 break;
Ben Wagner5785e4a2019-05-07 16:50:29 -04002118 case SkFontHinting::kNormal:
2119 case SkFontHinting::kFull:
bungeman@google.com0abbff92013-07-27 20:37:56 +00002120 // TODO: need to be able to distinguish subpixel positioned glyphs
2121 // and linear metrics.
2122 //rec->fFlags &= ~SkScalerContext::kSubpixelPositioning_Flag;
Ben Wagner5785e4a2019-05-07 16:50:29 -04002123 h = SkFontHinting::kNormal;
reed@google.come8fab012011-07-13 15:25:33 +00002124 break;
2125 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002126 SkDEBUGFAIL("unknown hinting");
reed@google.come8fab012011-07-13 15:25:33 +00002127 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00002128 //TODO: if this is a bitmap font, squash hinting and subpixel.
reed@google.come8fab012011-07-13 15:25:33 +00002129 rec->setHinting(h);
reed@google.comda440672011-07-13 18:02:28 +00002130
reed@google.com9181aa82011-08-05 14:28:31 +00002131// turn this off since GDI might turn A8 into BW! Need a bigger fix.
2132#if 0
reed@google.comda440672011-07-13 18:02:28 +00002133 // Disable LCD when rotated, since GDI's output is ugly
2134 if (isLCD(*rec) && !isAxisAligned(*rec)) {
2135 rec->fMaskFormat = SkMask::kA8_Format;
2136 }
reed@google.com9181aa82011-08-05 14:28:31 +00002137#endif
reed@google.com754e4eb2011-09-26 13:21:39 +00002138
reed@google.com0da48612013-03-19 16:06:52 +00002139 if (!fCanBeLCD && isLCD(*rec)) {
bungeman@google.comcb1bbb32012-10-12 18:48:35 +00002140 rec->fMaskFormat = SkMask::kA8_Format;
2141 rec->fFlags &= ~SkScalerContext::kGenA8FromLCD_Flag;
reed@google.com754e4eb2011-09-26 13:21:39 +00002142 }
reed@google.com754e4eb2011-09-26 13:21:39 +00002143}
reed@google.com070da5e2013-03-27 20:01:49 +00002144
2145///////////////////////////////////////////////////////////////////////////////
2146
Mike Kleinc0bd9f92019-04-23 12:05:21 -05002147#include "include/core/SkDataTable.h"
2148#include "include/core/SkFontMgr.h"
reed@google.com484f5bc2013-04-24 19:14:56 +00002149
bungeman@google.coma9802692013-08-07 02:45:25 +00002150static bool valid_logfont_for_enum(const LOGFONT& lf) {
2151 // TODO: Vector FON is unsupported and should not be listed.
2152 return
2153 // Ignore implicit vertical variants.
2154 lf.lfFaceName[0] && lf.lfFaceName[0] != '@'
2155
2156 // DEFAULT_CHARSET is used to get all fonts, but also implies all
2157 // character sets. Filter assuming all fonts support ANSI_CHARSET.
2158 && ANSI_CHARSET == lf.lfCharSet
2159 ;
reed@google.com484f5bc2013-04-24 19:14:56 +00002160}
2161
bungeman@google.coma9802692013-08-07 02:45:25 +00002162/** An EnumFontFamExProc implementation which interprets builderParam as
2163 * an SkTDArray<ENUMLOGFONTEX>* and appends logfonts which
2164 * pass the valid_logfont_for_enum predicate.
2165 */
2166static int CALLBACK enum_family_proc(const LOGFONT* lf, const TEXTMETRIC*,
2167 DWORD fontType, LPARAM builderParam) {
2168 if (valid_logfont_for_enum(*lf)) {
reed@google.coma65a6812013-05-02 19:47:24 +00002169 SkTDArray<ENUMLOGFONTEX>* array = (SkTDArray<ENUMLOGFONTEX>*)builderParam;
2170 *array->append() = *(ENUMLOGFONTEX*)lf;
reed@google.com484f5bc2013-04-24 19:14:56 +00002171 }
2172 return 1; // non-zero means continue
2173}
2174
reed@google.com484f5bc2013-04-24 19:14:56 +00002175class SkFontStyleSetGDI : public SkFontStyleSet {
2176public:
reed@google.coma65a6812013-05-02 19:47:24 +00002177 SkFontStyleSetGDI(const TCHAR familyName[]) {
bungeman@google.coma9802692013-08-07 02:45:25 +00002178 LOGFONT lf;
2179 sk_bzero(&lf, sizeof(lf));
2180 lf.lfCharSet = DEFAULT_CHARSET;
2181 _tcscpy_s(lf.lfFaceName, familyName);
2182
halcanary96fcdcc2015-08-27 07:41:13 -07002183 HDC hdc = ::CreateCompatibleDC(nullptr);
bungeman@google.coma9802692013-08-07 02:45:25 +00002184 ::EnumFontFamiliesEx(hdc, &lf, enum_family_proc, (LPARAM)&fArray, 0);
reed@google.com484f5bc2013-04-24 19:14:56 +00002185 ::DeleteDC(hdc);
2186 }
2187
mtklein36352bf2015-03-25 18:17:31 -07002188 int count() override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002189 return fArray.count();
2190 }
2191
mtklein36352bf2015-03-25 18:17:31 -07002192 void getStyle(int index, SkFontStyle* fs, SkString* styleName) override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002193 if (fs) {
bungemana4c4a2d2014-10-20 13:33:19 -07002194 *fs = get_style(fArray[index].elfLogFont);
reed@google.com484f5bc2013-04-24 19:14:56 +00002195 }
reed@google.coma65a6812013-05-02 19:47:24 +00002196 if (styleName) {
2197 const ENUMLOGFONTEX& ref = fArray[index];
2198 // For some reason, ENUMLOGFONTEX and LOGFONT disagree on their type in the
2199 // non-unicode version.
2200 // ENUMLOGFONTEX uses BYTE
2201 // LOGFONT uses CHAR
2202 // Here we assert they that the style name is logically the same (size) as
2203 // a TCHAR, so we can use the same converter function.
2204 SkASSERT(sizeof(TCHAR) == sizeof(ref.elfStyle[0]));
2205 tchar_to_skstring((const TCHAR*)ref.elfStyle, styleName);
2206 }
reed@google.com484f5bc2013-04-24 19:14:56 +00002207 }
2208
mtklein36352bf2015-03-25 18:17:31 -07002209 SkTypeface* createTypeface(int index) override {
reed@google.coma65a6812013-05-02 19:47:24 +00002210 return SkCreateTypefaceFromLOGFONT(fArray[index].elfLogFont);
reed@google.com484f5bc2013-04-24 19:14:56 +00002211 }
2212
mtklein36352bf2015-03-25 18:17:31 -07002213 SkTypeface* matchStyle(const SkFontStyle& pattern) override {
bungeman147ea2f2015-11-12 09:50:08 -08002214 return this->matchStyleCSS3(pattern);
reed@google.com484f5bc2013-04-24 19:14:56 +00002215 }
2216
2217private:
reed@google.coma65a6812013-05-02 19:47:24 +00002218 SkTDArray<ENUMLOGFONTEX> fArray;
reed@google.com484f5bc2013-04-24 19:14:56 +00002219};
2220
reed@google.com484f5bc2013-04-24 19:14:56 +00002221class SkFontMgrGDI : public SkFontMgr {
bungeman@google.coma9802692013-08-07 02:45:25 +00002222public:
2223 SkFontMgrGDI() {
reed@google.com484f5bc2013-04-24 19:14:56 +00002224 LOGFONT lf;
2225 sk_bzero(&lf, sizeof(lf));
2226 lf.lfCharSet = DEFAULT_CHARSET;
2227
halcanary96fcdcc2015-08-27 07:41:13 -07002228 HDC hdc = ::CreateCompatibleDC(nullptr);
reed@google.com484f5bc2013-04-24 19:14:56 +00002229 ::EnumFontFamiliesEx(hdc, &lf, enum_family_proc, (LPARAM)&fLogFontArray, 0);
2230 ::DeleteDC(hdc);
2231 }
2232
reed@google.com484f5bc2013-04-24 19:14:56 +00002233protected:
mtklein36352bf2015-03-25 18:17:31 -07002234 int onCountFamilies() const override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002235 return fLogFontArray.count();
2236 }
2237
mtklein36352bf2015-03-25 18:17:31 -07002238 void onGetFamilyName(int index, SkString* familyName) const override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002239 SkASSERT((unsigned)index < (unsigned)fLogFontArray.count());
reed@google.coma65a6812013-05-02 19:47:24 +00002240 tchar_to_skstring(fLogFontArray[index].elfLogFont.lfFaceName, familyName);
reed@google.com484f5bc2013-04-24 19:14:56 +00002241 }
2242
mtklein36352bf2015-03-25 18:17:31 -07002243 SkFontStyleSet* onCreateStyleSet(int index) const override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002244 SkASSERT((unsigned)index < (unsigned)fLogFontArray.count());
halcanary385fe4d2015-08-26 13:07:48 -07002245 return new SkFontStyleSetGDI(fLogFontArray[index].elfLogFont.lfFaceName);
reed@google.com484f5bc2013-04-24 19:14:56 +00002246 }
2247
mtklein36352bf2015-03-25 18:17:31 -07002248 SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
halcanary96fcdcc2015-08-27 07:41:13 -07002249 if (nullptr == familyName) {
reed@google.com484f5bc2013-04-24 19:14:56 +00002250 familyName = ""; // do we need this check???
2251 }
2252 LOGFONT lf;
2253 logfont_for_name(familyName, &lf);
halcanary385fe4d2015-08-26 13:07:48 -07002254 return new SkFontStyleSetGDI(lf.lfFaceName);
reed@google.com484f5bc2013-04-24 19:14:56 +00002255 }
2256
reed@google.com484f5bc2013-04-24 19:14:56 +00002257 virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
mtklein36352bf2015-03-25 18:17:31 -07002258 const SkFontStyle& fontstyle) const override {
reed@google.com437eea12013-04-25 20:40:02 +00002259 // could be in base impl
Hal Canary67b39de2016-11-07 11:47:44 -05002260 sk_sp<SkFontStyleSet> sset(this->matchFamily(familyName));
reed@google.com484f5bc2013-04-24 19:14:56 +00002261 return sset->matchStyle(fontstyle);
2262 }
2263
djsollen33068c12014-11-14 10:52:53 -08002264 virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
2265 const char* bcp47[], int bcp47Count,
mtklein36352bf2015-03-25 18:17:31 -07002266 SkUnichar character) const override {
halcanary96fcdcc2015-08-27 07:41:13 -07002267 return nullptr;
djsollen33068c12014-11-14 10:52:53 -08002268 }
2269
reed@google.com484f5bc2013-04-24 19:14:56 +00002270 virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
mtklein36352bf2015-03-25 18:17:31 -07002271 const SkFontStyle& fontstyle) const override {
reed@google.com437eea12013-04-25 20:40:02 +00002272 // could be in base impl
reed@google.com484f5bc2013-04-24 19:14:56 +00002273 SkString familyName;
2274 ((LogFontTypeface*)familyMember)->getFamilyName(&familyName);
2275 return this->matchFamilyStyle(familyName.c_str(), fontstyle);
2276 }
2277
Mike Reed59227392017-09-26 09:46:08 -04002278 sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,
2279 int ttcIndex) const override {
Ben Wagnerfc497342017-02-24 11:15:26 -05002280 if (ttcIndex != 0) {
2281 return nullptr;
2282 }
Mike Reed59227392017-09-26 09:46:08 -04002283 return create_from_stream(std::move(stream));
reed@google.com484f5bc2013-04-24 19:14:56 +00002284 }
reed@google.com437eea12013-04-25 20:40:02 +00002285
Mike Reed59227392017-09-26 09:46:08 -04002286 sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData> data, int ttcIndex) const override {
reed@google.com437eea12013-04-25 20:40:02 +00002287 // could be in base impl
Mike Reed59227392017-09-26 09:46:08 -04002288 return this->makeFromStream(std::unique_ptr<SkStreamAsset>(new SkMemoryStream(std::move(data))),
2289 ttcIndex);
reed@google.com484f5bc2013-04-24 19:14:56 +00002290 }
reed@google.com437eea12013-04-25 20:40:02 +00002291
Mike Reed59227392017-09-26 09:46:08 -04002292 sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override {
reed@google.com437eea12013-04-25 20:40:02 +00002293 // could be in base impl
Mike Reed59227392017-09-26 09:46:08 -04002294 auto stream = SkStream::MakeFromFile(path);
2295 return stream ? this->makeFromStream(std::move(stream), ttcIndex) : nullptr;
reed@google.com484f5bc2013-04-24 19:14:56 +00002296 }
2297
Mike Reed59227392017-09-26 09:46:08 -04002298 sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const override {
bungeman@google.comf7159bb2013-11-20 15:11:05 +00002299 LOGFONT lf;
halcanary96fcdcc2015-08-27 07:41:13 -07002300 if (nullptr == familyName) {
bungeman@google.comf7159bb2013-11-20 15:11:05 +00002301 lf = get_default_font();
2302 } else {
2303 logfont_for_name(familyName, &lf);
2304 }
bungemana4c4a2d2014-10-20 13:33:19 -07002305
bungeman11a77c62016-04-12 13:45:06 -07002306 lf.lfWeight = style.weight();
bungemanb4bb7d82016-04-27 10:21:04 -07002307 lf.lfItalic = style.slant() == SkFontStyle::kUpright_Slant ? FALSE : TRUE;
Mike Reed59227392017-09-26 09:46:08 -04002308 return sk_sp<SkTypeface>(SkCreateTypefaceFromLOGFONT(lf));
reed@google.com30ddd612013-07-30 17:47:39 +00002309 }
2310
reed@google.com484f5bc2013-04-24 19:14:56 +00002311private:
reed@google.coma65a6812013-05-02 19:47:24 +00002312 SkTDArray<ENUMLOGFONTEX> fLogFontArray;
reed@google.com484f5bc2013-04-24 19:14:56 +00002313};
reed@google.com070da5e2013-03-27 20:01:49 +00002314
reed@google.com30ddd612013-07-30 17:47:39 +00002315///////////////////////////////////////////////////////////////////////////////
2316
Ben Wagner3546ff12017-01-03 13:32:36 -05002317sk_sp<SkFontMgr> SkFontMgr_New_GDI() { return sk_make_sp<SkFontMgrGDI>(); }
mtklein1ee76512015-11-02 10:20:27 -08002318
Mike Klein8f11d4d2018-01-24 12:42:55 -05002319#endif//defined(SK_BUILD_FOR_WIN)