blob: e546f812e192b31e722f1ef22c9c121847b8e2b4 [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
mtklein1ee76512015-11-02 10:20:27 -08008#include "SkTypes.h"
Mike Klein8f11d4d2018-01-24 12:42:55 -05009#if defined(SK_BUILD_FOR_WIN)
mtklein1ee76512015-11-02 10:20:27 -080010
reed@google.comac6b9792011-03-11 15:42:51 +000011#include "SkAdvancedTypefaceMetrics.h"
bungeman@google.coma5501992012-05-18 19:06:41 +000012#include "SkBase64.h"
Cary Clarka4083c92017-09-15 11:59:23 -040013#include "SkColorData.h"
bungeman@google.coma5501992012-05-18 19:06:41 +000014#include "SkData.h"
15#include "SkDescriptor.h"
bungeman@google.come70f7982012-06-01 19:38:19 +000016#include "SkFontDescriptor.h"
Mike Reed77f94ea2019-01-22 16:30:40 -050017#include "SkFontMetrics.h"
bungeman@google.combbe50132012-07-24 20:33:21 +000018#include "SkGlyph.h"
bungeman@google.com27f74aa2013-10-08 21:32:15 +000019#include "SkHRESULT.h"
Hal Canary22be4c42018-06-12 12:37:31 -040020#include "SkMacros.h"
bungeman7cfd46a2016-10-20 16:06:52 -040021#include "SkMakeUnique.h"
bungeman@google.com97efada2012-07-30 20:40:50 +000022#include "SkMaskGamma.h"
bungeman@google.comd3fbd342014-04-15 15:52:07 +000023#include "SkMatrix22.h"
Hal Canarya865d252017-11-09 16:16:09 -050024#include "SkOTTable_OS_2.h"
bungeman@google.com7bdd6142013-07-15 19:42:57 +000025#include "SkOTTable_maxp.h"
bungeman@google.coma9802692013-08-07 02:45:25 +000026#include "SkOTTable_name.h"
bungeman@google.coma5501992012-05-18 19:06:41 +000027#include "SkOTUtils.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040028#include "SkOnce.h"
reed@google.com27889872012-08-07 16:15:13 +000029#include "SkPath.h"
bungeman@google.comb10b51f2013-08-01 20:18:41 +000030#include "SkSFNTHeader.h"
reed@google.comac6b9792011-03-11 15:42:51 +000031#include "SkStream.h"
bungeman@google.coma5501992012-05-18 19:06:41 +000032#include "SkString.h"
bungeman@google.com05a729f2013-06-20 15:29:16 +000033#include "SkTemplates.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040034#include "SkTo.h"
Hal Canaryfdcfb8b2018-06-13 09:42:32 -040035#include "SkTypefaceCache.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040036#include "SkTypeface_win.h"
reed@google.comac6b9792011-03-11 15:42:51 +000037#include "SkUtils.h"
38
bungeman@google.coma5501992012-05-18 19:06:41 +000039#include <tchar.h>
40#include <usp10.h>
41#include <objbase.h>
reed@google.comac6b9792011-03-11 15:42:51 +000042
reed@google.comf210f502013-03-20 14:15:52 +000043static void (*gEnsureLOGFONTAccessibleProc)(const LOGFONT&);
44
45void SkTypeface_SetEnsureLOGFONTAccessibleProc(void (*proc)(const LOGFONT&)) {
46 gEnsureLOGFONTAccessibleProc = proc;
47}
48
reed@google.com055180c2013-03-21 18:46:35 +000049static void call_ensure_accessible(const LOGFONT& lf) {
50 if (gEnsureLOGFONTAccessibleProc) {
51 gEnsureLOGFONTAccessibleProc(lf);
52 }
53}
54
55///////////////////////////////////////////////////////////////////////////////
56
reed@google.com6f5df482011-09-28 20:33:24 +000057// always packed xxRRGGBB
58typedef uint32_t SkGdiRGB;
59
reed@google.coma767fa02011-08-05 21:40:26 +000060// define this in your Makefile or .gyp to enforce AA requests
61// which GDI ignores at small sizes. This flag guarantees AA
62// for rotated text, regardless of GDI's notions.
63//#define SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
64
Ben Wagner3e45a2f2017-11-01 11:47:39 -040065static bool isLCD(const SkScalerContextRec& rec) {
reedd54d3fc2014-11-13 14:39:58 -080066 return SkMask::kLCD16_Format == rec.fMaskFormat;
reed@google.com82a34d82011-07-26 19:33:08 +000067}
68
reed@google.coma767fa02011-08-05 21:40:26 +000069static bool bothZero(SkScalar a, SkScalar b) {
70 return 0 == a && 0 == b;
71}
72
73// returns false if there is any non-90-rotation or skew
Ben Wagner3e45a2f2017-11-01 11:47:39 -040074static bool isAxisAligned(const SkScalerContextRec& rec) {
reed@google.coma767fa02011-08-05 21:40:26 +000075 return 0 == rec.fPreSkewX &&
76 (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
77 bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
78}
79
Ben Wagner3e45a2f2017-11-01 11:47:39 -040080static bool needToRenderWithSkia(const SkScalerContextRec& rec) {
reed@google.coma767fa02011-08-05 21:40:26 +000081#ifdef SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
82 // What we really want to catch is when GDI will ignore the AA request and give
83 // us BW instead. Smallish rotated text is one heuristic, so this code is just
84 // an approximation. We shouldn't need to do this for larger sizes, but at those
85 // sizes, the quality difference gets less and less between our general
86 // scanconverter and GDI's.
87 if (SkMask::kA8_Format == rec.fMaskFormat && !isAxisAligned(rec)) {
88 return true;
89 }
90#endif
Mike Reed04346d52018-11-05 12:45:32 -050091 return rec.getHinting() == kNo_SkFontHinting || rec.getHinting() == kSlight_SkFontHinting;
reed@google.coma767fa02011-08-05 21:40:26 +000092}
93
reed@google.coma65a6812013-05-02 19:47:24 +000094static void tchar_to_skstring(const TCHAR t[], SkString* s) {
reed@google.com484f5bc2013-04-24 19:14:56 +000095#ifdef UNICODE
halcanary96fcdcc2015-08-27 07:41:13 -070096 size_t sSize = WideCharToMultiByte(CP_UTF8, 0, t, -1, nullptr, 0, nullptr, nullptr);
reed@google.com484f5bc2013-04-24 19:14:56 +000097 s->resize(sSize);
halcanary96fcdcc2015-08-27 07:41:13 -070098 WideCharToMultiByte(CP_UTF8, 0, t, -1, s->writable_str(), sSize, nullptr, nullptr);
reed@google.com484f5bc2013-04-24 19:14:56 +000099#else
100 s->set(t);
101#endif
102}
103
bungeman@google.coma9802692013-08-07 02:45:25 +0000104static void dcfontname_to_skstring(HDC deviceContext, const LOGFONT& lf, SkString* familyName) {
105 int fontNameLen; //length of fontName in TCHARS.
halcanary96fcdcc2015-08-27 07:41:13 -0700106 if (0 == (fontNameLen = GetTextFace(deviceContext, 0, nullptr))) {
bungeman@google.coma9802692013-08-07 02:45:25 +0000107 call_ensure_accessible(lf);
halcanary96fcdcc2015-08-27 07:41:13 -0700108 if (0 == (fontNameLen = GetTextFace(deviceContext, 0, nullptr))) {
bungeman@google.coma9802692013-08-07 02:45:25 +0000109 fontNameLen = 0;
110 }
111 }
112
113 SkAutoSTArray<LF_FULLFACESIZE, TCHAR> fontName(fontNameLen+1);
114 if (0 == GetTextFace(deviceContext, fontNameLen, fontName.get())) {
115 call_ensure_accessible(lf);
116 if (0 == GetTextFace(deviceContext, fontNameLen, fontName.get())) {
117 fontName[0] = 0;
118 }
119 }
120
121 tchar_to_skstring(fontName.get(), familyName);
122}
123
reed@google.comac6b9792011-03-11 15:42:51 +0000124static void make_canonical(LOGFONT* lf) {
bungeman@google.com53cbb0b2013-09-08 19:36:58 +0000125 lf->lfHeight = -64;
bungeman59f093d2016-03-22 10:59:09 -0700126 lf->lfWidth = 0; // lfWidth is related to lfHeight, not to the OS/2::usWidthClass.
reed@google.com59d2f632011-05-02 19:36:59 +0000127 lf->lfQuality = CLEARTYPE_QUALITY;//PROOF_QUALITY;
128 lf->lfCharSet = DEFAULT_CHARSET;
reed@google.com82a34d82011-07-26 19:33:08 +0000129// lf->lfClipPrecision = 64;
reed@google.com59d2f632011-05-02 19:36:59 +0000130}
131
bungemana4c4a2d2014-10-20 13:33:19 -0700132static SkFontStyle get_style(const LOGFONT& lf) {
133 return SkFontStyle(lf.lfWeight,
bungeman59f093d2016-03-22 10:59:09 -0700134 SkFontStyle::kNormal_Width,
bungemana4c4a2d2014-10-20 13:33:19 -0700135 lf.lfItalic ? SkFontStyle::kItalic_Slant : SkFontStyle::kUpright_Slant);
reed@google.comac6b9792011-03-11 15:42:51 +0000136}
137
138static inline FIXED SkFixedToFIXED(SkFixed x) {
139 return *(FIXED*)(&x);
140}
bungeman@google.coma0319f62012-04-18 15:40:50 +0000141static inline SkFixed SkFIXEDToFixed(FIXED x) {
142 return *(SkFixed*)(&x);
143}
reed@google.comac6b9792011-03-11 15:42:51 +0000144
145static inline FIXED SkScalarToFIXED(SkScalar x) {
146 return SkFixedToFIXED(SkScalarToFixed(x));
147}
148
bungeman@google.com4732df62014-01-23 15:22:42 +0000149static inline SkScalar SkFIXEDToScalar(FIXED x) {
150 return SkFixedToScalar(SkFIXEDToFixed(x));
151}
152
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000153static unsigned calculateGlyphCount(HDC hdc, const LOGFONT& lf) {
154 TEXTMETRIC textMetric;
155 if (0 == GetTextMetrics(hdc, &textMetric)) {
156 textMetric.tmPitchAndFamily = TMPF_VECTOR;
157 call_ensure_accessible(lf);
158 GetTextMetrics(hdc, &textMetric);
159 }
160
161 if (!(textMetric.tmPitchAndFamily & TMPF_VECTOR)) {
162 return textMetric.tmLastChar;
163 }
164
reed@google.comac6b9792011-03-11 15:42:51 +0000165 // The 'maxp' table stores the number of glyphs at offset 4, in 2 bytes.
reed@google.comac6b9792011-03-11 15:42:51 +0000166 uint16_t glyphs;
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000167 if (GDI_ERROR != GetFontData(hdc, SkOTTableMaximumProfile::TAG, 4, &glyphs, sizeof(glyphs))) {
reed@google.comac6b9792011-03-11 15:42:51 +0000168 return SkEndian_SwapBE16(glyphs);
169 }
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000170
reed@google.comac6b9792011-03-11 15:42:51 +0000171 // Binary search for glyph count.
172 static const MAT2 mat2 = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
Ben Wagnerb0897652018-06-15 15:37:57 +0000173 int32_t max = UINT16_MAX + 1;
reed@google.comac6b9792011-03-11 15:42:51 +0000174 int32_t min = 0;
175 GLYPHMETRICS gm;
176 while (min < max) {
177 int32_t mid = min + ((max - min) / 2);
178 if (GetGlyphOutlineW(hdc, mid, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0,
halcanary96fcdcc2015-08-27 07:41:13 -0700179 nullptr, &mat2) == GDI_ERROR) {
reed@google.comac6b9792011-03-11 15:42:51 +0000180 max = mid;
181 } else {
182 min = mid + 1;
183 }
184 }
185 SkASSERT(min == max);
186 return min;
187}
188
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000189static unsigned calculateUPEM(HDC hdc, const LOGFONT& lf) {
190 TEXTMETRIC textMetric;
191 if (0 == GetTextMetrics(hdc, &textMetric)) {
192 textMetric.tmPitchAndFamily = TMPF_VECTOR;
193 call_ensure_accessible(lf);
194 GetTextMetrics(hdc, &textMetric);
195 }
196
197 if (!(textMetric.tmPitchAndFamily & TMPF_VECTOR)) {
198 return textMetric.tmMaxCharWidth;
199 }
200
201 OUTLINETEXTMETRIC otm;
202 unsigned int otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
203 if (0 == otmRet) {
204 call_ensure_accessible(lf);
205 otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
206 }
207
208 return (0 == otmRet) ? 0 : otm.otmEMSquare;
209}
210
reed@google.comac6b9792011-03-11 15:42:51 +0000211class LogFontTypeface : public SkTypeface {
reed@google.comac6b9792011-03-11 15:42:51 +0000212public:
bungemana4c4a2d2014-10-20 13:33:19 -0700213 LogFontTypeface(const SkFontStyle& style, const LOGFONT& lf, bool serializeAsStream)
bungemane3aea102016-07-13 05:16:58 -0700214 : SkTypeface(style, false)
bungemana4c4a2d2014-10-20 13:33:19 -0700215 , fLogFont(lf)
216 , fSerializeAsStream(serializeAsStream)
217 {
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000218 HFONT font = CreateFontIndirect(&lf);
219
halcanary96fcdcc2015-08-27 07:41:13 -0700220 HDC deviceContext = ::CreateCompatibleDC(nullptr);
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000221 HFONT savefont = (HFONT)SelectObject(deviceContext, font);
222
223 TEXTMETRIC textMetric;
224 if (0 == GetTextMetrics(deviceContext, &textMetric)) {
reed@google.com055180c2013-03-21 18:46:35 +0000225 call_ensure_accessible(lf);
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000226 if (0 == GetTextMetrics(deviceContext, &textMetric)) {
227 textMetric.tmPitchAndFamily = TMPF_TRUETYPE;
228 }
229 }
230 if (deviceContext) {
231 ::SelectObject(deviceContext, savefont);
232 ::DeleteDC(deviceContext);
233 }
234 if (font) {
235 ::DeleteObject(font);
236 }
237
bungeman@google.comfe747652013-03-25 19:36:11 +0000238 // The fixed pitch bit is set if the font is *not* fixed pitch.
239 this->setIsFixedPitch((textMetric.tmPitchAndFamily & TMPF_FIXED_PITCH) == 0);
bungeman6e45bda2016-07-25 15:11:49 -0700240 this->setFontStyle(SkFontStyle(textMetric.tmWeight, style.width(), style.slant()));
bungeman@google.comfe747652013-03-25 19:36:11 +0000241
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000242 // Used a logfont on a memory context, should never get a device font.
243 // Therefore all TMPF_DEVICE will be PostScript (cubic) fonts.
bungeman6e45bda2016-07-25 15:11:49 -0700244 // If the font has cubic outlines, it will not be rendered with ClearType.
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000245 fCanBeLCD = !((textMetric.tmPitchAndFamily & TMPF_VECTOR) &&
246 (textMetric.tmPitchAndFamily & TMPF_DEVICE));
247 }
reed@google.comac6b9792011-03-11 15:42:51 +0000248
reed@google.com59d2f632011-05-02 19:36:59 +0000249 LOGFONT fLogFont;
bungeman@google.come70f7982012-06-01 19:38:19 +0000250 bool fSerializeAsStream;
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000251 bool fCanBeLCD;
reed@google.comac6b9792011-03-11 15:42:51 +0000252
reed@google.com59d2f632011-05-02 19:36:59 +0000253 static LogFontTypeface* Create(const LOGFONT& lf) {
bungemana4c4a2d2014-10-20 13:33:19 -0700254 return new LogFontTypeface(get_style(lf), lf, false);
reed@google.comac6b9792011-03-11 15:42:51 +0000255 }
reed@google.com0da48612013-03-19 16:06:52 +0000256
reed@google.com055180c2013-03-21 18:46:35 +0000257 static void EnsureAccessible(const SkTypeface* face) {
258 call_ensure_accessible(static_cast<const LogFontTypeface*>(face)->fLogFont);
259 }
260
reed@google.com0da48612013-03-19 16:06:52 +0000261protected:
mtklein36352bf2015-03-25 18:17:31 -0700262 SkStreamAsset* onOpenStream(int* ttcIndex) const override;
Bruce Wang536ad2c2018-06-25 11:37:25 -0400263 sk_sp<SkTypeface> onMakeClone(const SkFontArguments& args) const override;
reeda9322c22016-04-12 06:47:05 -0700264 SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
265 const SkDescriptor*) const override;
mtklein36352bf2015-03-25 18:17:31 -0700266 void onFilterRec(SkScalerContextRec*) const override;
Hal Canary46cc3da2018-05-09 11:50:34 -0400267 void getGlyphToUnicodeMap(SkUnichar*) const override;
Hal Canary209e4b12017-05-04 14:23:55 -0400268 std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
mtklein36352bf2015-03-25 18:17:31 -0700269 void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
Ben Wagnerfc497342017-02-24 11:15:26 -0500270 int onCharsToGlyphs(const void* chars, Encoding encoding,
271 uint16_t glyphs[], int glyphCount) const override;
mtklein36352bf2015-03-25 18:17:31 -0700272 int onCountGlyphs() const override;
273 int onGetUPEM() const override;
274 void onGetFamilyName(SkString* familyName) const override;
275 SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
Ben Wagnerfc497342017-02-24 11:15:26 -0500276 int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],
277 int coordinateCount) const override
278 {
279 return -1;
280 }
Ben Wagnere346b1e2018-06-26 11:22:37 -0400281 int onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[],
282 int parameterCount) const override
283 {
284 return -1;
285 }
mtklein36352bf2015-03-25 18:17:31 -0700286 int onGetTableTags(SkFontTableTag tags[]) const override;
Ben Wagnerfc497342017-02-24 11:15:26 -0500287 size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override;
reed@google.comac6b9792011-03-11 15:42:51 +0000288};
289
bungeman@google.coma5501992012-05-18 19:06:41 +0000290class FontMemResourceTypeface : public LogFontTypeface {
291public:
292 /**
bungeman@google.coma5501992012-05-18 19:06:41 +0000293 * The created FontMemResourceTypeface takes ownership of fontMemResource.
294 */
295 static FontMemResourceTypeface* Create(const LOGFONT& lf, HANDLE fontMemResource) {
bungemana4c4a2d2014-10-20 13:33:19 -0700296 return new FontMemResourceTypeface(get_style(lf), lf, fontMemResource);
bungeman@google.coma5501992012-05-18 19:06:41 +0000297 }
298
299protected:
mtklein36352bf2015-03-25 18:17:31 -0700300 void weak_dispose() const override {
bungeman@google.coma5501992012-05-18 19:06:41 +0000301 RemoveFontMemResourceEx(fFontMemResource);
302 //SkTypefaceCache::Remove(this);
303 INHERITED::weak_dispose();
304 }
305
306private:
bungeman@google.com72cf4fc2014-03-21 22:48:32 +0000307 /**
308 * Takes ownership of fontMemResource.
309 */
bungemana4c4a2d2014-10-20 13:33:19 -0700310 FontMemResourceTypeface(const SkFontStyle& style, const LOGFONT& lf, HANDLE fontMemResource)
311 : LogFontTypeface(style, lf, true), fFontMemResource(fontMemResource)
312 { }
bungeman@google.com72cf4fc2014-03-21 22:48:32 +0000313
314 HANDLE fFontMemResource;
315
bungeman@google.coma5501992012-05-18 19:06:41 +0000316 typedef LogFontTypeface INHERITED;
317};
318
reed@google.comac6b9792011-03-11 15:42:51 +0000319static const LOGFONT& get_default_font() {
320 static LOGFONT gDefaultFont;
reed@google.comac6b9792011-03-11 15:42:51 +0000321 return gDefaultFont;
322}
323
bungeman82a455f2016-04-14 08:04:45 -0700324static bool FindByLogFont(SkTypeface* face, void* ctx) {
bungeman@google.coma5501992012-05-18 19:06:41 +0000325 LogFontTypeface* lface = static_cast<LogFontTypeface*>(face);
reed@google.com59d2f632011-05-02 19:36:59 +0000326 const LOGFONT* lf = reinterpret_cast<const LOGFONT*>(ctx);
reed@google.comac6b9792011-03-11 15:42:51 +0000327
bungeman82a455f2016-04-14 08:04:45 -0700328 return !memcmp(&lface->fLogFont, lf, sizeof(LOGFONT));
reed@google.com59d2f632011-05-02 19:36:59 +0000329}
330
331/**
332 * This guy is public. It first searches the cache, and if a match is not found,
333 * it creates a new face.
334 */
335SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& origLF) {
336 LOGFONT lf = origLF;
337 make_canonical(&lf);
bungeman@google.comee51d1a2012-02-16 12:40:48 +0000338 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByLogFont, &lf);
halcanary96fcdcc2015-08-27 07:41:13 -0700339 if (nullptr == face) {
reed@google.com59d2f632011-05-02 19:36:59 +0000340 face = LogFontTypeface::Create(lf);
bungeman82a455f2016-04-14 08:04:45 -0700341 SkTypefaceCache::Add(face);
reed@google.com59d2f632011-05-02 19:36:59 +0000342 }
343 return face;
reed@google.comac6b9792011-03-11 15:42:51 +0000344}
345
reed@google.comdb77a6a2011-07-19 19:08:33 +0000346/**
bungeman@google.coma5501992012-05-18 19:06:41 +0000347 * The created SkTypeface takes ownership of fontMemResource.
348 */
349SkTypeface* SkCreateFontMemResourceTypefaceFromLOGFONT(const LOGFONT& origLF, HANDLE fontMemResource) {
350 LOGFONT lf = origLF;
351 make_canonical(&lf);
mtklein60b6e9d2014-10-24 10:43:15 -0700352 // We'll never get a cache hit, so no point in putting this in SkTypefaceCache.
353 return FontMemResourceTypeface::Create(lf, fontMemResource);
bungeman@google.coma5501992012-05-18 19:06:41 +0000354}
355
356/**
reed@google.comdb77a6a2011-07-19 19:08:33 +0000357 * This guy is public
358 */
359void SkLOGFONTFromTypeface(const SkTypeface* face, LOGFONT* lf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700360 if (nullptr == face) {
reed@google.comdb77a6a2011-07-19 19:08:33 +0000361 *lf = get_default_font();
362 } else {
bungeman@google.coma5501992012-05-18 19:06:41 +0000363 *lf = static_cast<const LogFontTypeface*>(face)->fLogFont;
reed@google.comdb77a6a2011-07-19 19:08:33 +0000364 }
365}
366
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000367// Construct Glyph to Unicode table.
368// Unicode code points that require conjugate pairs in utf16 are not
369// supported.
370// TODO(arthurhsu): Add support for conjugate pairs. It looks like that may
371// require parsing the TTF cmap table (platform 4, encoding 12) directly instead
372// of calling GetFontUnicodeRange().
373static void populate_glyph_to_unicode(HDC fontHdc, const unsigned glyphCount,
Hal Canary46cc3da2018-05-09 11:50:34 -0400374 SkUnichar* glyphToUnicode) {
375 sk_bzero(glyphToUnicode, sizeof(SkUnichar) * glyphCount);
halcanary96fcdcc2015-08-27 07:41:13 -0700376 DWORD glyphSetBufferSize = GetFontUnicodeRanges(fontHdc, nullptr);
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000377 if (!glyphSetBufferSize) {
378 return;
379 }
380
Ben Wagner7ecc5962016-11-02 17:07:33 -0400381 std::unique_ptr<BYTE[]> glyphSetBuffer(new BYTE[glyphSetBufferSize]);
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000382 GLYPHSET* glyphSet =
383 reinterpret_cast<LPGLYPHSET>(glyphSetBuffer.get());
384 if (GetFontUnicodeRanges(fontHdc, glyphSet) != glyphSetBufferSize) {
385 return;
386 }
387
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000388 for (DWORD i = 0; i < glyphSet->cRanges; ++i) {
389 // There is no guarantee that within a Unicode range, the corresponding
390 // glyph id in a font file are continuous. So, even if we have ranges,
391 // we can't just use the first and last entry of the range to compute
392 // result. We need to enumerate them one by one.
393 int count = glyphSet->ranges[i].cGlyphs;
394 SkAutoTArray<WCHAR> chars(count + 1);
395 chars[count] = 0; // termintate string
396 SkAutoTArray<WORD> glyph(count);
397 for (USHORT j = 0; j < count; ++j) {
398 chars[j] = glyphSet->ranges[i].wcLow + j;
399 }
400 GetGlyphIndicesW(fontHdc, chars.get(), count, glyph.get(),
401 GGI_MARK_NONEXISTING_GLYPHS);
402 // If the glyph ID is valid, and the glyph is not mapped, then we will
403 // fill in the char id into the vector. If the glyph is mapped already,
404 // skip it.
405 // TODO(arthurhsu): better improve this. e.g. Get all used char ids from
406 // font cache, then generate this mapping table from there. It's
407 // unlikely to have collisions since glyph reuse happens mostly for
408 // different Unicode pages.
409 for (USHORT j = 0; j < count; ++j) {
Hal Canary46cc3da2018-05-09 11:50:34 -0400410 if (glyph[j] != 0xFFFF && glyph[j] < glyphCount && glyphToUnicode[glyph[j]] == 0) {
411 glyphToUnicode[glyph[j]] = chars[j];
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000412 }
413 }
414 }
415}
416
reed@google.com99edd432011-09-09 14:59:59 +0000417//////////////////////////////////////////////////////////////////////////////////////
418
419static int alignTo32(int n) {
420 return (n + 31) & ~31;
421}
422
423struct MyBitmapInfo : public BITMAPINFO {
424 RGBQUAD fMoreSpaceForColors[1];
425};
426
427class HDCOffscreen {
428public:
429 HDCOffscreen() {
430 fFont = 0;
431 fDC = 0;
432 fBM = 0;
halcanary96fcdcc2015-08-27 07:41:13 -0700433 fBits = nullptr;
reed@google.com99edd432011-09-09 14:59:59 +0000434 fWidth = fHeight = 0;
435 fIsBW = false;
436 }
437
438 ~HDCOffscreen() {
439 if (fDC) {
440 DeleteDC(fDC);
441 }
442 if (fBM) {
443 DeleteObject(fBM);
444 }
445 }
446
447 void init(HFONT font, const XFORM& xform) {
448 fFont = font;
449 fXform = xform;
450 }
451
bungeman@google.com97efada2012-07-30 20:40:50 +0000452 const void* draw(const SkGlyph&, bool isBW, size_t* srcRBPtr);
reed@google.com99edd432011-09-09 14:59:59 +0000453
454private:
455 HDC fDC;
456 HBITMAP fBM;
457 HFONT fFont;
458 XFORM fXform;
459 void* fBits; // points into fBM
460 int fWidth;
461 int fHeight;
462 bool fIsBW;
463};
464
reed@google.com754e4eb2011-09-26 13:21:39 +0000465const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW,
bungeman@google.com97efada2012-07-30 20:40:50 +0000466 size_t* srcRBPtr) {
reed@google.com84e22d82013-07-10 15:38:20 +0000467 // Can we share the scalercontext's fDDC, so we don't need to create
468 // a separate fDC here?
reed@google.com99edd432011-09-09 14:59:59 +0000469 if (0 == fDC) {
470 fDC = CreateCompatibleDC(0);
471 if (0 == fDC) {
halcanary96fcdcc2015-08-27 07:41:13 -0700472 return nullptr;
reed@google.com99edd432011-09-09 14:59:59 +0000473 }
474 SetGraphicsMode(fDC, GM_ADVANCED);
475 SetBkMode(fDC, TRANSPARENT);
476 SetTextAlign(fDC, TA_LEFT | TA_BASELINE);
477 SelectObject(fDC, fFont);
bungeman@google.com97efada2012-07-30 20:40:50 +0000478
479 COLORREF color = 0x00FFFFFF;
bsalomon@google.comb58a6392013-03-21 20:29:05 +0000480 SkDEBUGCODE(COLORREF prev =) SetTextColor(fDC, color);
bungeman@google.com97efada2012-07-30 20:40:50 +0000481 SkASSERT(prev != CLR_INVALID);
reed@google.com99edd432011-09-09 14:59:59 +0000482 }
483
484 if (fBM && (fIsBW != isBW || fWidth < glyph.fWidth || fHeight < glyph.fHeight)) {
485 DeleteObject(fBM);
486 fBM = 0;
487 }
reed@google.com754e4eb2011-09-26 13:21:39 +0000488 fIsBW = isBW;
reed@google.com99edd432011-09-09 14:59:59 +0000489
reed@google.com99edd432011-09-09 14:59:59 +0000490 fWidth = SkMax32(fWidth, glyph.fWidth);
491 fHeight = SkMax32(fHeight, glyph.fHeight);
492
493 int biWidth = isBW ? alignTo32(fWidth) : fWidth;
494
495 if (0 == fBM) {
496 MyBitmapInfo info;
497 sk_bzero(&info, sizeof(info));
498 if (isBW) {
499 RGBQUAD blackQuad = { 0, 0, 0, 0 };
500 RGBQUAD whiteQuad = { 0xFF, 0xFF, 0xFF, 0 };
501 info.bmiColors[0] = blackQuad;
502 info.bmiColors[1] = whiteQuad;
503 }
504 info.bmiHeader.biSize = sizeof(info.bmiHeader);
505 info.bmiHeader.biWidth = biWidth;
506 info.bmiHeader.biHeight = fHeight;
507 info.bmiHeader.biPlanes = 1;
508 info.bmiHeader.biBitCount = isBW ? 1 : 32;
509 info.bmiHeader.biCompression = BI_RGB;
510 if (isBW) {
511 info.bmiHeader.biClrUsed = 2;
512 }
513 fBM = CreateDIBSection(fDC, &info, DIB_RGB_COLORS, &fBits, 0, 0);
514 if (0 == fBM) {
halcanary96fcdcc2015-08-27 07:41:13 -0700515 return nullptr;
reed@google.com99edd432011-09-09 14:59:59 +0000516 }
517 SelectObject(fDC, fBM);
518 }
519
520 // erase
521 size_t srcRB = isBW ? (biWidth >> 3) : (fWidth << 2);
522 size_t size = fHeight * srcRB;
bungeman@google.com97efada2012-07-30 20:40:50 +0000523 memset(fBits, 0, size);
reed@google.com99edd432011-09-09 14:59:59 +0000524
525 XFORM xform = fXform;
526 xform.eDx = (float)-glyph.fLeft;
527 xform.eDy = (float)-glyph.fTop;
528 SetWorldTransform(fDC, &xform);
529
530 uint16_t glyphID = glyph.getGlyphID();
halcanary96fcdcc2015-08-27 07:41:13 -0700531 BOOL ret = ExtTextOutW(fDC, 0, 0, ETO_GLYPH_INDEX, nullptr, reinterpret_cast<LPCWSTR>(&glyphID), 1, nullptr);
reed@google.com99edd432011-09-09 14:59:59 +0000532 GdiFlush();
bungeman@google.com39698b12011-11-15 22:26:41 +0000533 if (0 == ret) {
halcanary96fcdcc2015-08-27 07:41:13 -0700534 return nullptr;
bungeman@google.com39698b12011-11-15 22:26:41 +0000535 }
reed@google.com99edd432011-09-09 14:59:59 +0000536 *srcRBPtr = srcRB;
537 // offset to the start of the image
538 return (const char*)fBits + (fHeight - glyph.fHeight) * srcRB;
539}
540
reed@google.comb8a5c612012-06-13 20:01:44 +0000541//////////////////////////////////////////////////////////////////////////////
bungeman@google.com0abbff92013-07-27 20:37:56 +0000542#define BUFFERSIZE (1 << 13)
reed@google.com59d2f632011-05-02 19:36:59 +0000543
reed@google.com30ddd612013-07-30 17:47:39 +0000544class SkScalerContext_GDI : public SkScalerContext {
reed@google.comac6b9792011-03-11 15:42:51 +0000545public:
bungeman7cfd46a2016-10-20 16:06:52 -0400546 SkScalerContext_GDI(sk_sp<LogFontTypeface>,
547 const SkScalerContextEffects&,
548 const SkDescriptor* desc);
Chris Dalton1ef80942017-12-04 12:01:30 -0700549 ~SkScalerContext_GDI() override;
reed@google.comac6b9792011-03-11 15:42:51 +0000550
reed@google.com84e22d82013-07-10 15:38:20 +0000551 // Returns true if the constructor was able to complete all of its
552 // initializations (which may include calling GDI).
553 bool isValid() const;
554
reed@google.comac6b9792011-03-11 15:42:51 +0000555protected:
mtklein36352bf2015-03-25 18:17:31 -0700556 unsigned generateGlyphCount() override;
557 uint16_t generateCharToGlyph(SkUnichar uni) override;
Ben Wagnere5416452018-08-09 14:03:42 -0400558 bool generateAdvance(SkGlyph* glyph) override;
mtklein36352bf2015-03-25 18:17:31 -0700559 void generateMetrics(SkGlyph* glyph) override;
560 void generateImage(const SkGlyph& glyph) override;
Ben Wagner5ddb3082018-03-29 11:18:06 -0400561 bool generatePath(SkGlyphID glyph, SkPath* path) override;
Mike Reedb5784ac2018-11-12 09:35:15 -0500562 void generateFontMetrics(SkFontMetrics*) override;
reed@google.com99edd432011-09-09 14:59:59 +0000563
reed@google.comac6b9792011-03-11 15:42:51 +0000564private:
Ben Wagner6e9ac122016-11-11 14:31:06 -0500565 DWORD getGDIGlyphPath(SkGlyphID glyph, UINT flags,
bungeman@google.com0abbff92013-07-27 20:37:56 +0000566 SkAutoSTMalloc<BUFFERSIZE, uint8_t>* glyphbuf);
567
reed@google.com99edd432011-09-09 14:59:59 +0000568 HDCOffscreen fOffscreen;
bungeman@google.com0abbff92013-07-27 20:37:56 +0000569 /** fGsA is the non-rotational part of total matrix without the text height scale.
570 * Used to find the magnitude of advances.
571 */
572 MAT2 fGsA;
bungeman@google.com6a774a12013-07-30 01:07:48 +0000573 /** The total matrix without the textSize. */
reed@google.comac6b9792011-03-11 15:42:51 +0000574 MAT2 fMat22;
bungeman@google.com6a774a12013-07-30 01:07:48 +0000575 /** Scales font to EM size. */
576 MAT2 fHighResMat22;
reed@google.comac6b9792011-03-11 15:42:51 +0000577 HDC fDDC;
578 HFONT fSavefont;
579 HFONT fFont;
580 SCRIPT_CACHE fSC;
581 int fGlyphCount;
reed@google.com1dd17a12011-05-17 14:04:41 +0000582
bungeman@google.com6a774a12013-07-30 01:07:48 +0000583 /** The total matrix which also removes EM scale. */
reed@google.com1dd17a12011-05-17 14:04:41 +0000584 SkMatrix fHiResMatrix;
bungeman@google.com0abbff92013-07-27 20:37:56 +0000585 /** fG_inv is the inverse of the rotational part of the total matrix.
586 * Used to set the direction of advances.
587 */
588 SkMatrix fG_inv;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000589 enum Type {
bungeman@google.com4732df62014-01-23 15:22:42 +0000590 kTrueType_Type, kBitmap_Type, kLine_Type
bungeman@google.coma0319f62012-04-18 15:40:50 +0000591 } fType;
592 TEXTMETRIC fTM;
reed@google.comac6b9792011-03-11 15:42:51 +0000593};
594
reed@google.comac6b9792011-03-11 15:42:51 +0000595static FIXED float2FIXED(float x) {
596 return SkFixedToFIXED(SkFloatToFixed(x));
597}
598
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700599static inline float FIXED2float(FIXED x) {
600 return SkFixedToFloat(SkFIXEDToFixed(x));
601}
602
Ben Wagner3e45a2f2017-11-01 11:47:39 -0400603static BYTE compute_quality(const SkScalerContextRec& rec) {
reed@google.com82a34d82011-07-26 19:33:08 +0000604 switch (rec.fMaskFormat) {
605 case SkMask::kBW_Format:
606 return NONANTIALIASED_QUALITY;
607 case SkMask::kLCD16_Format:
reed@google.com82a34d82011-07-26 19:33:08 +0000608 return CLEARTYPE_QUALITY;
609 default:
reed@google.com8351aab2012-01-18 17:06:35 +0000610 if (rec.fFlags & SkScalerContext::kGenA8FromLCD_Flag) {
611 return CLEARTYPE_QUALITY;
612 } else {
613 return ANTIALIASED_QUALITY;
614 }
reed@google.com82a34d82011-07-26 19:33:08 +0000615 }
616}
617
bungeman7cfd46a2016-10-20 16:06:52 -0400618SkScalerContext_GDI::SkScalerContext_GDI(sk_sp<LogFontTypeface> rawTypeface,
reeda9322c22016-04-12 06:47:05 -0700619 const SkScalerContextEffects& effects,
620 const SkDescriptor* desc)
bungeman7cfd46a2016-10-20 16:06:52 -0400621 : SkScalerContext(std::move(rawTypeface), effects, desc)
reed@google.com055180c2013-03-21 18:46:35 +0000622 , fDDC(0)
reed@google.com055180c2013-03-21 18:46:35 +0000623 , fSavefont(0)
reed@google.com84e22d82013-07-10 15:38:20 +0000624 , fFont(0)
reed@google.com055180c2013-03-21 18:46:35 +0000625 , fSC(0)
bungeman@google.com05a729f2013-06-20 15:29:16 +0000626 , fGlyphCount(-1)
627{
bungeman7cfd46a2016-10-20 16:06:52 -0400628 LogFontTypeface* typeface = static_cast<LogFontTypeface*>(this->getTypeface());
reed@google.com055180c2013-03-21 18:46:35 +0000629
halcanary96fcdcc2015-08-27 07:41:13 -0700630 fDDC = ::CreateCompatibleDC(nullptr);
reed@google.com84e22d82013-07-10 15:38:20 +0000631 if (!fDDC) {
632 return;
633 }
reed@google.com1dd17a12011-05-17 14:04:41 +0000634 SetGraphicsMode(fDDC, GM_ADVANCED);
reed@google.comac6b9792011-03-11 15:42:51 +0000635 SetBkMode(fDDC, TRANSPARENT);
skia.committer@gmail.com27e21fe2013-07-28 07:01:03 +0000636
bungeman6f940762015-03-18 08:25:43 -0700637 // When GDI hinting, remove the entire Y scale from sA and GsA. (Prevents 'linear' metrics.)
638 // When not hinting, remove only the integer Y scale from sA and GsA. (Applied by GDI.)
bungeman5f14c5e2014-12-05 12:26:44 -0800639 SkScalerContextRec::PreMatrixScale scaleConstraints =
Mike Reed04346d52018-11-05 12:45:32 -0500640 (fRec.getHinting() == kNo_SkFontHinting || fRec.getHinting() == kSlight_SkFontHinting)
bungeman5f14c5e2014-12-05 12:26:44 -0800641 ? SkScalerContextRec::kVerticalInteger_PreMatrixScale
642 : SkScalerContextRec::kVertical_PreMatrixScale;
643 SkVector scale;
644 SkMatrix sA;
645 SkMatrix GsA;
646 SkMatrix A;
647 fRec.computeMatrices(scaleConstraints, &scale, &sA, &GsA, &fG_inv, &A);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000648
649 fGsA.eM11 = SkScalarToFIXED(GsA.get(SkMatrix::kMScaleX));
650 fGsA.eM12 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewY)); // This should be ~0.
651 fGsA.eM21 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewX));
652 fGsA.eM22 = SkScalarToFIXED(GsA.get(SkMatrix::kMScaleY));
653
bungeman6f940762015-03-18 08:25:43 -0700654 // When not hinting, scale was computed with kVerticalInteger, so is already an integer.
655 // The sA and GsA transforms will be used to create 'linear' metrics.
656
657 // When hinting, scale was computed with kVertical, stating that our port can handle
658 // non-integer scales. This is done so that sA and GsA are computed without any 'residual'
659 // scale in them, preventing 'linear' metrics. However, GDI cannot actually handle non-integer
660 // scales so we need to round in this case. This is fine, since all of the scale has been
661 // removed from sA and GsA, so GDI will be handling the scale completely.
662 SkScalar gdiTextSize = SkScalarRoundToScalar(scale.fY);
663
664 // GDI will not accept a size of zero, so round the range [0, 1] to 1.
665 // If the size was non-zero, the scale factors will also be non-zero and 1px tall text is drawn.
666 // If the size actually was zero, the scale factors will also be zero, so GDI will draw nothing.
bungeman5f14c5e2014-12-05 12:26:44 -0800667 if (gdiTextSize == 0) {
668 gdiTextSize = SK_Scalar1;
669 }
bungeman@google.com0abbff92013-07-27 20:37:56 +0000670
reed@google.com055180c2013-03-21 18:46:35 +0000671 LOGFONT lf = typeface->fLogFont;
bungeman@google.com11ba3192013-10-03 20:17:51 +0000672 lf.lfHeight = -SkScalarTruncToInt(gdiTextSize);
reed@google.com82a34d82011-07-26 19:33:08 +0000673 lf.lfQuality = compute_quality(fRec);
reed@google.comac6b9792011-03-11 15:42:51 +0000674 fFont = CreateFontIndirect(&lf);
reed@google.com84e22d82013-07-10 15:38:20 +0000675 if (!fFont) {
676 return;
677 }
reed@google.com1dd17a12011-05-17 14:04:41 +0000678
reed@google.comac6b9792011-03-11 15:42:51 +0000679 fSavefont = (HFONT)SelectObject(fDDC, fFont);
reed@google.coma767fa02011-08-05 21:40:26 +0000680
bungeman@google.coma0319f62012-04-18 15:40:50 +0000681 if (0 == GetTextMetrics(fDDC, &fTM)) {
reed@google.com055180c2013-03-21 18:46:35 +0000682 call_ensure_accessible(lf);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000683 if (0 == GetTextMetrics(fDDC, &fTM)) {
684 fTM.tmPitchAndFamily = TMPF_TRUETYPE;
685 }
686 }
bungeman@google.com90b7e382012-04-20 15:26:28 +0000687
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000688 XFORM xform;
bungeman@google.com90b7e382012-04-20 15:26:28 +0000689 if (fTM.tmPitchAndFamily & TMPF_VECTOR) {
bungeman@google.com4732df62014-01-23 15:22:42 +0000690 // Used a logfont on a memory context, should never get a device font.
691 // Therefore all TMPF_DEVICE will be PostScript fonts.
692
693 // If TMPF_VECTOR is set, one of TMPF_TRUETYPE or TMPF_DEVICE means that
694 // we have an outline font. Otherwise we have a vector FON, which is
695 // scalable, but not an outline font.
696 // This was determined by testing with Type1 PFM/PFB and
697 // OpenTypeCFF OTF, as well as looking at Wine bugs and sources.
698 if (fTM.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_DEVICE)) {
699 // Truetype or PostScript.
700 fType = SkScalerContext_GDI::kTrueType_Type;
701 } else {
702 // Stroked FON.
703 fType = SkScalerContext_GDI::kLine_Type;
704 }
bungeman@google.coma0319f62012-04-18 15:40:50 +0000705
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000706 // fPost2x2 is column-major, left handed (y down).
707 // XFORM 2x2 is row-major, left handed (y down).
bungeman@google.com0abbff92013-07-27 20:37:56 +0000708 xform.eM11 = SkScalarToFloat(sA.get(SkMatrix::kMScaleX));
709 xform.eM12 = SkScalarToFloat(sA.get(SkMatrix::kMSkewY));
710 xform.eM21 = SkScalarToFloat(sA.get(SkMatrix::kMSkewX));
711 xform.eM22 = SkScalarToFloat(sA.get(SkMatrix::kMScaleY));
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000712 xform.eDx = 0;
713 xform.eDy = 0;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000714
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000715 // MAT2 is row major, right handed (y up).
716 fMat22.eM11 = float2FIXED(xform.eM11);
717 fMat22.eM12 = float2FIXED(-xform.eM12);
718 fMat22.eM21 = float2FIXED(-xform.eM21);
719 fMat22.eM22 = float2FIXED(xform.eM22);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000720
721 if (needToRenderWithSkia(fRec)) {
722 this->forceGenerateImageFromPath();
723 }
724
bungeman@google.com11ba3192013-10-03 20:17:51 +0000725 // Create a hires matrix if we need linear metrics.
bungeman@google.com0abbff92013-07-27 20:37:56 +0000726 if (this->isSubpixel()) {
727 OUTLINETEXTMETRIC otm;
728 UINT success = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
729 if (0 == success) {
730 call_ensure_accessible(lf);
731 success = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
732 }
733 if (0 != success) {
bungeman@google.com11ba3192013-10-03 20:17:51 +0000734 SkScalar upem = SkIntToScalar(otm.otmEMSquare);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000735
bungeman@google.com11ba3192013-10-03 20:17:51 +0000736 SkScalar gdiTextSizeToEMScale = upem / gdiTextSize;
737 fHighResMat22.eM11 = float2FIXED(gdiTextSizeToEMScale);
bungeman@google.com6a774a12013-07-30 01:07:48 +0000738 fHighResMat22.eM12 = float2FIXED(0);
739 fHighResMat22.eM21 = float2FIXED(0);
bungeman@google.com11ba3192013-10-03 20:17:51 +0000740 fHighResMat22.eM22 = float2FIXED(gdiTextSizeToEMScale);
bungeman@google.com6a774a12013-07-30 01:07:48 +0000741
bungeman@google.com11ba3192013-10-03 20:17:51 +0000742 SkScalar removeEMScale = SkScalarInvert(upem);
bungeman@google.com6a774a12013-07-30 01:07:48 +0000743 fHiResMatrix = A;
bungeman@google.com11ba3192013-10-03 20:17:51 +0000744 fHiResMatrix.preScale(removeEMScale, removeEMScale);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000745 }
746 }
747
bungeman@google.coma0319f62012-04-18 15:40:50 +0000748 } else {
749 // Assume bitmap
reed@google.com30ddd612013-07-30 17:47:39 +0000750 fType = SkScalerContext_GDI::kBitmap_Type;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000751
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000752 xform.eM11 = 1.0f;
753 xform.eM12 = 0.0f;
754 xform.eM21 = 0.0f;
755 xform.eM22 = 1.0f;
756 xform.eDx = 0.0f;
757 xform.eDy = 0.0f;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000758
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000759 // fPost2x2 is column-major, left handed (y down).
760 // MAT2 is row major, right handed (y up).
bungeman@google.coma0319f62012-04-18 15:40:50 +0000761 fMat22.eM11 = SkScalarToFIXED(fRec.fPost2x2[0][0]);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000762 fMat22.eM12 = SkScalarToFIXED(-fRec.fPost2x2[1][0]);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000763 fMat22.eM21 = SkScalarToFIXED(-fRec.fPost2x2[0][1]);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000764 fMat22.eM22 = SkScalarToFIXED(fRec.fPost2x2[1][1]);
reed@google.coma767fa02011-08-05 21:40:26 +0000765 }
reed@google.com99edd432011-09-09 14:59:59 +0000766
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000767 fOffscreen.init(fFont, xform);
reed@google.comac6b9792011-03-11 15:42:51 +0000768}
769
reed@google.com30ddd612013-07-30 17:47:39 +0000770SkScalerContext_GDI::~SkScalerContext_GDI() {
reed@google.comac6b9792011-03-11 15:42:51 +0000771 if (fDDC) {
772 ::SelectObject(fDDC, fSavefont);
773 ::DeleteDC(fDDC);
774 }
775 if (fFont) {
776 ::DeleteObject(fFont);
777 }
778 if (fSC) {
779 ::ScriptFreeCache(&fSC);
780 }
781}
782
reed@google.com30ddd612013-07-30 17:47:39 +0000783bool SkScalerContext_GDI::isValid() const {
reed@google.com84e22d82013-07-10 15:38:20 +0000784 return fDDC && fFont;
785}
786
reed@google.com30ddd612013-07-30 17:47:39 +0000787unsigned SkScalerContext_GDI::generateGlyphCount() {
reed@google.comac6b9792011-03-11 15:42:51 +0000788 if (fGlyphCount < 0) {
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000789 fGlyphCount = calculateGlyphCount(
790 fDDC, static_cast<const LogFontTypeface*>(this->getTypeface())->fLogFont);
reed@google.comac6b9792011-03-11 15:42:51 +0000791 }
792 return fGlyphCount;
793}
794
bungeman@google.com33346482013-08-27 19:05:32 +0000795uint16_t SkScalerContext_GDI::generateCharToGlyph(SkUnichar utf32) {
reed@google.comac6b9792011-03-11 15:42:51 +0000796 uint16_t index = 0;
bungeman@google.com33346482013-08-27 19:05:32 +0000797 WCHAR utf16[2];
reed@google.comac6b9792011-03-11 15:42:51 +0000798 // TODO(ctguil): Support characters that generate more than one glyph.
Hal Canaryf107a2f2018-07-25 16:52:48 -0400799 if (SkUTF::ToUTF16(utf32, (uint16_t*)utf16) == 1) {
reed@google.comac6b9792011-03-11 15:42:51 +0000800 // Type1 fonts fail with uniscribe API. Use GetGlyphIndices for plane 0.
skia.committer@gmail.com7bd141d2013-08-28 07:01:18 +0000801
bungeman@google.com33346482013-08-27 19:05:32 +0000802 /** Real documentation for GetGlyphIndiciesW:
803 *
804 * When GGI_MARK_NONEXISTING_GLYPHS is not specified and a character does not map to a
805 * glyph, then the 'default character's glyph is returned instead. The 'default character'
bungeman@google.com4732df62014-01-23 15:22:42 +0000806 * is available in fTM.tmDefaultChar. FON fonts have a default character, and there exists
807 * a usDefaultChar in the 'OS/2' table, version 2 and later. If there is no
bungeman@google.com33346482013-08-27 19:05:32 +0000808 * 'default character' specified by the font, then often the first character found is used.
809 *
810 * When GGI_MARK_NONEXISTING_GLYPHS is specified and a character does not map to a glyph,
811 * then the glyph 0xFFFF is used. In Windows XP and earlier, Bitmap/Vector FON usually use
812 * glyph 0x1F instead ('Terminal' appears to be special, returning 0xFFFF).
813 * Type1 PFM/PFB, TT, OT TT, OT CFF all appear to use 0xFFFF, even on XP.
814 */
815 DWORD result = GetGlyphIndicesW(fDDC, utf16, 1, &index, GGI_MARK_NONEXISTING_GLYPHS);
816 if (result == GDI_ERROR
817 || 0xFFFF == index
bungeman@google.com4732df62014-01-23 15:22:42 +0000818 || (0x1F == index &&
819 (fType == SkScalerContext_GDI::kBitmap_Type ||
820 fType == SkScalerContext_GDI::kLine_Type)
821 /*&& winVer < Vista */)
822 )
bungeman@google.com33346482013-08-27 19:05:32 +0000823 {
824 index = 0;
825 }
reed@google.comac6b9792011-03-11 15:42:51 +0000826 } else {
827 // Use uniscribe to detemine glyph index for non-BMP characters.
bungeman@google.com27f74aa2013-10-08 21:32:15 +0000828 static const int numWCHAR = 2;
829 static const int maxItems = 2;
halcanary96fcdcc2015-08-27 07:41:13 -0700830 // MSDN states that this can be nullptr, but some things don't work then.
Chris Dalton1ef80942017-12-04 12:01:30 -0700831 SCRIPT_CONTROL sc;
832 memset(&sc, 0, sizeof(sc));
bungeman@google.com27f74aa2013-10-08 21:32:15 +0000833 // Add extra item to SCRIPT_ITEM to work around a bug (now documented).
834 // https://bugzilla.mozilla.org/show_bug.cgi?id=366643
835 SCRIPT_ITEM si[maxItems + 1];
836 int numItems;
halcanary96fcdcc2015-08-27 07:41:13 -0700837 HRZM(ScriptItemize(utf16, numWCHAR, maxItems, &sc, nullptr, si, &numItems),
bungeman@google.com27f74aa2013-10-08 21:32:15 +0000838 "Could not itemize character.");
reed@google.comac6b9792011-03-11 15:42:51 +0000839
Ben Wagner97182cc2018-02-15 10:20:04 -0500840 // Disable any attempt at shaping.
841 // Without this ScriptShape may return 0x80040200 (USP_E_SCRIPT_NOT_IN_FONT)
842 // when all that is desired here is a simple cmap lookup.
843 for (SCRIPT_ITEM& item : si) {
844 item.a.eScript = SCRIPT_UNDEFINED;
845 }
846
bungeman@google.com27f74aa2013-10-08 21:32:15 +0000847 // Sometimes ScriptShape cannot find a glyph for a non-BMP and returns 2 space glyphs.
848 static const int maxGlyphs = 2;
849 SCRIPT_VISATTR vsa[maxGlyphs];
850 WORD outGlyphs[maxGlyphs];
851 WORD logClust[numWCHAR];
852 int numGlyphs;
853 HRZM(ScriptShape(fDDC, &fSC, utf16, numWCHAR, maxGlyphs, &si[0].a,
854 outGlyphs, logClust, vsa, &numGlyphs),
855 "Could not shape character.");
856 if (1 == numGlyphs) {
857 index = outGlyphs[0];
858 }
reed@google.comac6b9792011-03-11 15:42:51 +0000859 }
860 return index;
861}
862
Ben Wagnere5416452018-08-09 14:03:42 -0400863bool SkScalerContext_GDI::generateAdvance(SkGlyph* glyph) {
864 return false;
reed@google.comac6b9792011-03-11 15:42:51 +0000865}
866
reed@google.com30ddd612013-07-30 17:47:39 +0000867void SkScalerContext_GDI::generateMetrics(SkGlyph* glyph) {
reed@google.comac6b9792011-03-11 15:42:51 +0000868 SkASSERT(fDDC);
869
Ben Wagnere5416452018-08-09 14:03:42 -0400870 glyph->fMaskFormat = fRec.fMaskFormat;
871
bungeman@google.com4732df62014-01-23 15:22:42 +0000872 if (fType == SkScalerContext_GDI::kBitmap_Type || fType == SkScalerContext_GDI::kLine_Type) {
bungeman@google.coma0319f62012-04-18 15:40:50 +0000873 SIZE size;
djsollen1b277042014-08-06 06:58:06 -0700874 WORD glyphs = glyph->getGlyphID();
bungeman@google.coma0319f62012-04-18 15:40:50 +0000875 if (0 == GetTextExtentPointI(fDDC, &glyphs, 1, &size)) {
876 glyph->fWidth = SkToS16(fTM.tmMaxCharWidth);
877 } else {
878 glyph->fWidth = SkToS16(size.cx);
879 }
880 glyph->fHeight = SkToS16(size.cy);
881
882 glyph->fTop = SkToS16(-fTM.tmAscent);
bungeman@google.com4732df62014-01-23 15:22:42 +0000883 // Bitmap FON cannot underhang, but vector FON may.
884 // There appears no means of determining underhang of vector FON.
bungeman@google.coma0319f62012-04-18 15:40:50 +0000885 glyph->fLeft = SkToS16(0);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700886 glyph->fAdvanceX = glyph->fWidth;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000887 glyph->fAdvanceY = 0;
888
bungeman@google.com4732df62014-01-23 15:22:42 +0000889 // Vector FON will transform nicely, but bitmap FON do not.
890 if (fType == SkScalerContext_GDI::kLine_Type) {
891 SkRect bounds = SkRect::MakeXYWH(glyph->fLeft, glyph->fTop,
892 glyph->fWidth, glyph->fHeight);
893 SkMatrix m;
894 m.setAll(SkFIXEDToScalar(fMat22.eM11), -SkFIXEDToScalar(fMat22.eM21), 0,
895 -SkFIXEDToScalar(fMat22.eM12), SkFIXEDToScalar(fMat22.eM22), 0,
reed3f43f8a2015-01-20 19:58:36 -0800896 0, 0, 1);
bungeman@google.com4732df62014-01-23 15:22:42 +0000897 m.mapRect(&bounds);
reedd02cf262014-11-18 18:06:45 -0800898 bounds.roundOut(&bounds);
bungeman@google.com4732df62014-01-23 15:22:42 +0000899 glyph->fLeft = SkScalarTruncToInt(bounds.fLeft);
900 glyph->fTop = SkScalarTruncToInt(bounds.fTop);
901 glyph->fWidth = SkScalarTruncToInt(bounds.width());
902 glyph->fHeight = SkScalarTruncToInt(bounds.height());
903 }
904
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000905 // Apply matrix to advance.
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700906 glyph->fAdvanceY = -FIXED2float(fMat22.eM12) * glyph->fAdvanceX;
907 glyph->fAdvanceX *= FIXED2float(fMat22.eM11);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000908
909 return;
910 }
911
djsollen1b277042014-08-06 06:58:06 -0700912 UINT glyphId = glyph->getGlyphID();
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000913
reed@google.comac6b9792011-03-11 15:42:51 +0000914 GLYPHMETRICS gm;
reed@google.com1dd17a12011-05-17 14:04:41 +0000915 sk_bzero(&gm, sizeof(gm));
reed@google.comac6b9792011-03-11 15:42:51 +0000916
halcanary96fcdcc2015-08-27 07:41:13 -0700917 DWORD status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fMat22);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000918 if (GDI_ERROR == status) {
reed@google.com055180c2013-03-21 18:46:35 +0000919 LogFontTypeface::EnsureAccessible(this->getTypeface());
halcanary96fcdcc2015-08-27 07:41:13 -0700920 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fMat22);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000921 if (GDI_ERROR == status) {
922 glyph->zeroMetrics();
923 return;
924 }
925 }
926
927 bool empty = false;
928 // The black box is either the embedded bitmap size or the outline extent.
929 // It is 1x1 if nothing is to be drawn, but will also be 1x1 if something very small
930 // is to be drawn, like a '.'. We need to outset '.' but do not wish to outset ' '.
931 if (1 == gm.gmBlackBoxX && 1 == gm.gmBlackBoxY) {
932 // If GetGlyphOutline with GGO_NATIVE returns 0, we know there was no outline.
halcanary96fcdcc2015-08-27 07:41:13 -0700933 DWORD bufferSize = GetGlyphOutlineW(fDDC, glyphId, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fMat22);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000934 empty = (0 == bufferSize);
935 }
936
937 glyph->fTop = SkToS16(-gm.gmptGlyphOrigin.y);
938 glyph->fLeft = SkToS16(gm.gmptGlyphOrigin.x);
939 if (empty) {
940 glyph->fWidth = 0;
941 glyph->fHeight = 0;
942 } else {
943 // Outset, since the image may bleed out of the black box.
944 // For embedded bitmaps the black box should be exact.
945 // For outlines we need to outset by 1 in all directions for bleed.
946 // For ClearType we need to outset by 2 for bleed.
947 glyph->fWidth = gm.gmBlackBoxX + 4;
948 glyph->fHeight = gm.gmBlackBoxY + 4;
949 glyph->fTop -= 2;
950 glyph->fLeft -= 2;
951 }
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700952 // TODO(benjaminwagner): What is the type of gm.gmCellInc[XY]?
953 glyph->fAdvanceX = (float)((int)gm.gmCellIncX);
954 glyph->fAdvanceY = (float)((int)gm.gmCellIncY);
reed@google.comac6b9792011-03-11 15:42:51 +0000955
bungeman@google.com6a774a12013-07-30 01:07:48 +0000956 if (this->isSubpixel()) {
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000957 sk_bzero(&gm, sizeof(gm));
halcanary96fcdcc2015-08-27 07:41:13 -0700958 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fHighResMat22);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000959 if (GDI_ERROR != status) {
960 SkPoint advance;
961 fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700962 glyph->fAdvanceX = SkScalarToFloat(advance.fX);
963 glyph->fAdvanceY = SkScalarToFloat(advance.fY);
reed@google.comac6b9792011-03-11 15:42:51 +0000964 }
bungeman@google.com0abbff92013-07-27 20:37:56 +0000965 } else if (!isAxisAligned(this->fRec)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700966 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fGsA);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000967 if (GDI_ERROR != status) {
968 SkPoint advance;
969 fG_inv.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700970 glyph->fAdvanceX = SkScalarToFloat(advance.fX);
971 glyph->fAdvanceY = SkScalarToFloat(advance.fY);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000972 }
reed@google.comac6b9792011-03-11 15:42:51 +0000973 }
974}
975
bungeman@google.com6a774a12013-07-30 01:07:48 +0000976static const MAT2 gMat2Identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
Mike Reedb5784ac2018-11-12 09:35:15 -0500977void SkScalerContext_GDI::generateFontMetrics(SkFontMetrics* metrics) {
halcanary96fcdcc2015-08-27 07:41:13 -0700978 if (nullptr == metrics) {
bungeman41078062014-07-07 08:16:37 -0700979 return;
reed@google.com60af92c2013-05-08 14:11:28 +0000980 }
bungeman41078062014-07-07 08:16:37 -0700981 sk_bzero(metrics, sizeof(*metrics));
reed@google.comac6b9792011-03-11 15:42:51 +0000982
983 SkASSERT(fDDC);
984
bungeman@google.come9d83192013-06-21 05:31:38 +0000985#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
bungeman@google.com4732df62014-01-23 15:22:42 +0000986 if (fType == SkScalerContext_GDI::kBitmap_Type || fType == SkScalerContext_GDI::kLine_Type) {
bungeman@google.come9d83192013-06-21 05:31:38 +0000987#endif
bungeman41078062014-07-07 08:16:37 -0700988 metrics->fTop = SkIntToScalar(-fTM.tmAscent);
989 metrics->fAscent = SkIntToScalar(-fTM.tmAscent);
990 metrics->fDescent = SkIntToScalar(fTM.tmDescent);
991 metrics->fBottom = SkIntToScalar(fTM.tmDescent);
992 metrics->fLeading = SkIntToScalar(fTM.tmExternalLeading);
993 metrics->fAvgCharWidth = SkIntToScalar(fTM.tmAveCharWidth);
994 metrics->fMaxCharWidth = SkIntToScalar(fTM.tmMaxCharWidth);
995 metrics->fXMin = 0;
996 metrics->fXMax = metrics->fMaxCharWidth;
997 //metrics->fXHeight = 0;
bungeman@google.come9d83192013-06-21 05:31:38 +0000998#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
bungeman@google.coma0319f62012-04-18 15:40:50 +0000999 return;
1000 }
bungeman@google.come9d83192013-06-21 05:31:38 +00001001#endif
bungeman@google.coma0319f62012-04-18 15:40:50 +00001002
reed@google.comac6b9792011-03-11 15:42:51 +00001003 OUTLINETEXTMETRIC otm;
1004
1005 uint32_t ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001006 if (0 == ret) {
reed@google.com055180c2013-03-21 18:46:35 +00001007 LogFontTypeface::EnsureAccessible(this->getTypeface());
bungeman@google.com39698b12011-11-15 22:26:41 +00001008 ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
1009 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001010 if (0 == ret) {
bungeman@google.coma0319f62012-04-18 15:40:50 +00001011 return;
reed@google.comac6b9792011-03-11 15:42:51 +00001012 }
1013
bungeman@google.come9d83192013-06-21 05:31:38 +00001014#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
bungeman41078062014-07-07 08:16:37 -07001015 metrics->fTop = SkIntToScalar(-otm.otmrcFontBox.top);
1016 metrics->fAscent = SkIntToScalar(-otm.otmAscent);
1017 metrics->fDescent = SkIntToScalar(-otm.otmDescent);
1018 metrics->fBottom = SkIntToScalar(-otm.otmrcFontBox.bottom);
1019 metrics->fLeading = SkIntToScalar(otm.otmLineGap);
1020 metrics->fAvgCharWidth = SkIntToScalar(otm.otmTextMetrics.tmAveCharWidth);
1021 metrics->fMaxCharWidth = SkIntToScalar(otm.otmTextMetrics.tmMaxCharWidth);
1022 metrics->fXMin = SkIntToScalar(otm.otmrcFontBox.left);
1023 metrics->fXMax = SkIntToScalar(otm.otmrcFontBox.right);
commit-bot@chromium.orgd3031aa2014-05-14 14:54:51 +00001024#endif
bungeman41078062014-07-07 08:16:37 -07001025 metrics->fUnderlineThickness = SkIntToScalar(otm.otmsUnderscoreSize);
1026 metrics->fUnderlinePosition = -SkIntToScalar(otm.otmsUnderscorePosition);
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001027
Mike Reedb5784ac2018-11-12 09:35:15 -05001028 metrics->fFlags |= SkFontMetrics::kUnderlineThicknessIsValid_Flag;
1029 metrics->fFlags |= SkFontMetrics::kUnderlinePositionIsValid_Flag;
bungeman@google.com4a867a62014-05-22 17:59:21 +00001030
bungeman41078062014-07-07 08:16:37 -07001031 metrics->fXHeight = SkIntToScalar(otm.otmsXHeight);
1032 GLYPHMETRICS gm;
1033 sk_bzero(&gm, sizeof(gm));
1034 DWORD len = GetGlyphOutlineW(fDDC, 'x', GGO_METRICS, &gm, 0, 0, &gMat2Identity);
1035 if (len != GDI_ERROR && gm.gmBlackBoxY > 0) {
1036 metrics->fXHeight = SkIntToScalar(gm.gmBlackBoxY);
reed@google.comac6b9792011-03-11 15:42:51 +00001037 }
1038}
1039
reed@google.com7430a332011-10-03 14:37:38 +00001040////////////////////////////////////////////////////////////////////////////////////////
1041
bungeman@google.com0abbff92013-07-27 20:37:56 +00001042#define SK_SHOW_TEXT_BLIT_COVERAGE 0
1043
reed@google.com7430a332011-10-03 14:37:38 +00001044static void build_power_table(uint8_t table[], float ee) {
1045 for (int i = 0; i < 256; i++) {
1046 float x = i / 255.f;
bungeman@google.com97efada2012-07-30 20:40:50 +00001047 x = sk_float_pow(x, ee);
reed@google.come1ca7052013-12-17 19:22:07 +00001048 int xx = SkScalarRoundToInt(x * 255);
reed@google.com7430a332011-10-03 14:37:38 +00001049 table[i] = SkToU8(xx);
1050 }
1051}
1052
bungeman@google.com97efada2012-07-30 20:40:50 +00001053/**
1054 * This will invert the gamma applied by GDI (gray-scale antialiased), so we
1055 * can get linear values.
1056 *
1057 * GDI grayscale appears to use a hard-coded gamma of 2.3.
1058 *
1059 * GDI grayscale appears to draw using the black and white rasterizer at four
1060 * times the size and then downsamples to compute the coverage mask. As a
1061 * result there are only seventeen total grays. This lack of fidelity means
1062 * that shifting into other color spaces is imprecise.
1063 */
1064static const uint8_t* getInverseGammaTableGDI() {
Mike Klein0341f162017-10-18 09:58:54 -04001065 static SkOnce once;
bungeman@google.com97efada2012-07-30 20:40:50 +00001066 static uint8_t gTableGdi[256];
Mike Klein0341f162017-10-18 09:58:54 -04001067 once([]{
bungeman@google.com97efada2012-07-30 20:40:50 +00001068 build_power_table(gTableGdi, 2.3f);
Mike Klein0341f162017-10-18 09:58:54 -04001069 });
bungeman@google.com97efada2012-07-30 20:40:50 +00001070 return gTableGdi;
1071}
1072
1073/**
1074 * This will invert the gamma applied by GDI ClearType, so we can get linear
1075 * values.
1076 *
1077 * GDI ClearType uses SPI_GETFONTSMOOTHINGCONTRAST / 1000 as the gamma value.
1078 * If this value is not specified, the default is a gamma of 1.4.
1079 */
1080static const uint8_t* getInverseGammaTableClearType() {
Mike Klein0341f162017-10-18 09:58:54 -04001081 static SkOnce once;
bungeman@google.com97efada2012-07-30 20:40:50 +00001082 static uint8_t gTableClearType[256];
Mike Klein0341f162017-10-18 09:58:54 -04001083 once([]{
reed@google.com7430a332011-10-03 14:37:38 +00001084 UINT level = 0;
1085 if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &level, 0) || !level) {
1086 // can't get the data, so use a default
1087 level = 1400;
1088 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001089 build_power_table(gTableClearType, level / 1000.0f);
Mike Klein0341f162017-10-18 09:58:54 -04001090 });
bungeman@google.com97efada2012-07-30 20:40:50 +00001091 return gTableClearType;
reed@google.com7430a332011-10-03 14:37:38 +00001092}
1093
Cary Clarka4083c92017-09-15 11:59:23 -04001094#include "SkColorData.h"
reed@google.comac6b9792011-03-11 15:42:51 +00001095
bungeman@google.com63853142012-08-01 15:36:46 +00001096//Cannot assume that the input rgb is gray due to possible setting of kGenA8FromLCD_Flag.
bungeman@google.com97efada2012-07-30 20:40:50 +00001097template<bool APPLY_PREBLEND>
1098static inline uint8_t rgb_to_a8(SkGdiRGB rgb, const uint8_t* table8) {
bungeman@google.com63853142012-08-01 15:36:46 +00001099 U8CPU r = (rgb >> 16) & 0xFF;
1100 U8CPU g = (rgb >> 8) & 0xFF;
1101 U8CPU b = (rgb >> 0) & 0xFF;
bungeman@google.com1bfe01d2012-08-28 16:02:42 +00001102 return sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
reed@google.com8351aab2012-01-18 17:06:35 +00001103}
1104
bungeman@google.com97efada2012-07-30 20:40:50 +00001105template<bool APPLY_PREBLEND>
1106static inline uint16_t rgb_to_lcd16(SkGdiRGB rgb, const uint8_t* tableR,
1107 const uint8_t* tableG,
1108 const uint8_t* tableB) {
1109 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1110 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1111 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001112#if SK_SHOW_TEXT_BLIT_COVERAGE
1113 r = SkMax32(r, 10); g = SkMax32(g, 10); b = SkMax32(b, 10);
1114#endif
bungeman@google.com97efada2012-07-30 20:40:50 +00001115 return SkPack888ToRGB16(r, g, b);
reed@google.com82a34d82011-07-26 19:33:08 +00001116}
1117
bungeman@google.com97efada2012-07-30 20:40:50 +00001118template<bool APPLY_PREBLEND>
reed@google.com5e2df642011-09-21 18:42:09 +00001119static void rgb_to_a8(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
bungeman@google.com97efada2012-07-30 20:40:50 +00001120 const SkGlyph& glyph, const uint8_t* table8) {
reed@google.com5e2df642011-09-21 18:42:09 +00001121 const size_t dstRB = glyph.rowBytes();
1122 const int width = glyph.fWidth;
1123 uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1124
1125 for (int y = 0; y < glyph.fHeight; y++) {
1126 for (int i = 0; i < width; i++) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001127 dst[i] = rgb_to_a8<APPLY_PREBLEND>(src[i], table8);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001128#if SK_SHOW_TEXT_BLIT_COVERAGE
1129 dst[i] = SkMax32(dst[i], 10);
1130#endif
reed@google.com5e2df642011-09-21 18:42:09 +00001131 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001132 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001133 dst -= dstRB;
1134 }
1135}
1136
bungeman@google.com97efada2012-07-30 20:40:50 +00001137template<bool APPLY_PREBLEND>
1138static void rgb_to_lcd16(const SkGdiRGB* SK_RESTRICT src, size_t srcRB, const SkGlyph& glyph,
1139 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
reed@google.com5e2df642011-09-21 18:42:09 +00001140 const size_t dstRB = glyph.rowBytes();
1141 const int width = glyph.fWidth;
1142 uint16_t* SK_RESTRICT dst = (uint16_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1143
1144 for (int y = 0; y < glyph.fHeight; y++) {
1145 for (int i = 0; i < width; i++) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001146 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(src[i], tableR, tableG, tableB);
reed@google.com5e2df642011-09-21 18:42:09 +00001147 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001148 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001149 dst = (uint16_t*)((char*)dst - dstRB);
1150 }
1151}
1152
reed@google.com30ddd612013-07-30 17:47:39 +00001153void SkScalerContext_GDI::generateImage(const SkGlyph& glyph) {
reed@google.comac6b9792011-03-11 15:42:51 +00001154 SkASSERT(fDDC);
1155
reed@google.com62711172011-05-18 15:08:10 +00001156 const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
reed@google.com82a34d82011-07-26 19:33:08 +00001157 const bool isAA = !isLCD(fRec);
reed@google.comac6b9792011-03-11 15:42:51 +00001158
reed@google.com99edd432011-09-09 14:59:59 +00001159 size_t srcRB;
bungeman@google.com97efada2012-07-30 20:40:50 +00001160 const void* bits = fOffscreen.draw(glyph, isBW, &srcRB);
halcanary96fcdcc2015-08-27 07:41:13 -07001161 if (nullptr == bits) {
reed@google.com055180c2013-03-21 18:46:35 +00001162 LogFontTypeface::EnsureAccessible(this->getTypeface());
bungeman@google.com97efada2012-07-30 20:40:50 +00001163 bits = fOffscreen.draw(glyph, isBW, &srcRB);
halcanary96fcdcc2015-08-27 07:41:13 -07001164 if (nullptr == bits) {
bungeman@google.com39698b12011-11-15 22:26:41 +00001165 sk_bzero(glyph.fImage, glyph.computeImageSize());
1166 return;
1167 }
reed@google.com82a34d82011-07-26 19:33:08 +00001168 }
reed@google.comac6b9792011-03-11 15:42:51 +00001169
bungeman@google.com97efada2012-07-30 20:40:50 +00001170 if (!isBW) {
bungeman@google.com1bd2d672012-08-13 20:01:51 +00001171 const uint8_t* table;
1172 //The offscreen contains a GDI blit if isAA and kGenA8FromLCD_Flag is not set.
1173 //Otherwise the offscreen contains a ClearType blit.
1174 if (isAA && !(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag)) {
1175 table = getInverseGammaTableGDI();
1176 } else {
1177 table = getInverseGammaTableClearType();
bungeman@google.com97efada2012-07-30 20:40:50 +00001178 }
1179 //Note that the following cannot really be integrated into the
1180 //pre-blend, since we may not be applying the pre-blend; when we aren't
1181 //applying the pre-blend it means that a filter wants linear anyway.
1182 //Other code may also be applying the pre-blend, so we'd need another
1183 //one with this and one without.
reed@google.com6f5df482011-09-28 20:33:24 +00001184 SkGdiRGB* addr = (SkGdiRGB*)bits;
1185 for (int y = 0; y < glyph.fHeight; ++y) {
1186 for (int x = 0; x < glyph.fWidth; ++x) {
1187 int r = (addr[x] >> 16) & 0xFF;
1188 int g = (addr[x] >> 8) & 0xFF;
1189 int b = (addr[x] >> 0) & 0xFF;
reed@google.com7430a332011-10-03 14:37:38 +00001190 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
reed@google.com6f5df482011-09-28 20:33:24 +00001191 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001192 addr = SkTAddOffset<SkGdiRGB>(addr, srcRB);
reed@google.com6f5df482011-09-28 20:33:24 +00001193 }
1194 }
1195
reed@google.com82a34d82011-07-26 19:33:08 +00001196 size_t dstRB = glyph.rowBytes();
1197 if (isBW) {
1198 const uint8_t* src = (const uint8_t*)bits;
reed@google.com82a34d82011-07-26 19:33:08 +00001199 uint8_t* dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1200 for (int y = 0; y < glyph.fHeight; y++) {
1201 memcpy(dst, src, dstRB);
1202 src += srcRB;
1203 dst -= dstRB;
reed@google.comac6b9792011-03-11 15:42:51 +00001204 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001205#if SK_SHOW_TEXT_BLIT_COVERAGE
1206 if (glyph.fWidth > 0 && glyph.fHeight > 0) {
Ben Wagner968664b2018-12-04 14:06:37 -05001207 int bitCount = glyph.fWidth & 7;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001208 uint8_t* first = (uint8_t*)glyph.fImage;
1209 uint8_t* last = (uint8_t*)((char*)glyph.fImage + glyph.fHeight * dstRB - 1);
1210 *first |= 1 << 7;
1211 *last |= bitCount == 0 ? 1 : 1 << (8 - bitCount);
1212 }
1213#endif
reed@google.com82a34d82011-07-26 19:33:08 +00001214 } else if (isAA) {
reed@google.com6f5df482011-09-28 20:33:24 +00001215 // since the caller may require A8 for maskfilters, we can't check for BW
1216 // ... until we have the caller tell us that explicitly
reed@google.com5e2df642011-09-21 18:42:09 +00001217 const SkGdiRGB* src = (const SkGdiRGB*)bits;
bungeman@google.coma76de722012-10-26 19:35:54 +00001218 if (fPreBlend.isApplicable()) {
1219 rgb_to_a8<true>(src, srcRB, glyph, fPreBlend.fG);
bungeman@google.com97efada2012-07-30 20:40:50 +00001220 } else {
bungeman@google.coma76de722012-10-26 19:35:54 +00001221 rgb_to_a8<false>(src, srcRB, glyph, fPreBlend.fG);
bungeman@google.com97efada2012-07-30 20:40:50 +00001222 }
reed@google.com82a34d82011-07-26 19:33:08 +00001223 } else { // LCD16
reed@google.com5e2df642011-09-21 18:42:09 +00001224 const SkGdiRGB* src = (const SkGdiRGB*)bits;
Ben Wagner968664b2018-12-04 14:06:37 -05001225 SkASSERT(SkMask::kLCD16_Format == glyph.fMaskFormat);
1226 if (fPreBlend.isApplicable()) {
1227 rgb_to_lcd16<true>(src, srcRB, glyph,
1228 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
reed@google.com5e2df642011-09-21 18:42:09 +00001229 } else {
Ben Wagner968664b2018-12-04 14:06:37 -05001230 rgb_to_lcd16<false>(src, srcRB, glyph,
1231 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
reed@google.comac6b9792011-03-11 15:42:51 +00001232 }
1233 }
reed@google.comac6b9792011-03-11 15:42:51 +00001234}
1235
bungeman@google.com0abbff92013-07-27 20:37:56 +00001236class GDIGlyphbufferPointIter {
1237public:
skia.committer@gmail.com27e21fe2013-07-28 07:01:03 +00001238 GDIGlyphbufferPointIter(const uint8_t* glyphbuf, DWORD total_size)
bungeman@google.com0abbff92013-07-27 20:37:56 +00001239 : fHeaderIter(glyphbuf, total_size), fCurveIter(), fPointIter()
1240 { }
reed@google.comac6b9792011-03-11 15:42:51 +00001241
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001242 POINTFX const * next() {
bungeman@google.com0abbff92013-07-27 20:37:56 +00001243nextHeader:
1244 if (!fCurveIter.isSet()) {
1245 const TTPOLYGONHEADER* header = fHeaderIter.next();
halcanary96fcdcc2015-08-27 07:41:13 -07001246 if (nullptr == header) {
1247 return nullptr;
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001248 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001249 fCurveIter.set(header);
1250 const TTPOLYCURVE* curve = fCurveIter.next();
halcanary96fcdcc2015-08-27 07:41:13 -07001251 if (nullptr == curve) {
1252 return nullptr;
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001253 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001254 fPointIter.set(curve);
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001255 return &header->pfxStart;
bungeman@google.com05a729f2013-06-20 15:29:16 +00001256 }
1257
bungeman@google.com0abbff92013-07-27 20:37:56 +00001258 const POINTFX* nextPoint = fPointIter.next();
halcanary96fcdcc2015-08-27 07:41:13 -07001259 if (nullptr == nextPoint) {
bungeman@google.com0abbff92013-07-27 20:37:56 +00001260 const TTPOLYCURVE* curve = fCurveIter.next();
halcanary96fcdcc2015-08-27 07:41:13 -07001261 if (nullptr == curve) {
bungeman@google.com0abbff92013-07-27 20:37:56 +00001262 fCurveIter.set();
1263 goto nextHeader;
1264 } else {
1265 fPointIter.set(curve);
bungeman@google.com05a729f2013-06-20 15:29:16 +00001266 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001267 nextPoint = fPointIter.next();
reed@google.comac6b9792011-03-11 15:42:51 +00001268 }
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001269 return nextPoint;
reed@google.comac6b9792011-03-11 15:42:51 +00001270 }
bungeman@google.comd1c7f712013-03-11 18:51:19 +00001271
bungeman@google.com0abbff92013-07-27 20:37:56 +00001272 WORD currentCurveType() {
1273 return fPointIter.fCurveType;
1274 }
1275
1276private:
1277 /** Iterates over all of the polygon headers in a glyphbuf. */
1278 class GDIPolygonHeaderIter {
1279 public:
skia.committer@gmail.com27e21fe2013-07-28 07:01:03 +00001280 GDIPolygonHeaderIter(const uint8_t* glyphbuf, DWORD total_size)
bungeman@google.com0abbff92013-07-27 20:37:56 +00001281 : fCurPolygon(reinterpret_cast<const TTPOLYGONHEADER*>(glyphbuf))
1282 , fEndPolygon(SkTAddOffset<const TTPOLYGONHEADER>(glyphbuf, total_size))
1283 { }
1284
1285 const TTPOLYGONHEADER* next() {
1286 if (fCurPolygon >= fEndPolygon) {
halcanary96fcdcc2015-08-27 07:41:13 -07001287 return nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001288 }
1289 const TTPOLYGONHEADER* thisPolygon = fCurPolygon;
1290 fCurPolygon = SkTAddOffset<const TTPOLYGONHEADER>(fCurPolygon, fCurPolygon->cb);
1291 return thisPolygon;
1292 }
1293 private:
1294 const TTPOLYGONHEADER* fCurPolygon;
1295 const TTPOLYGONHEADER* fEndPolygon;
1296 };
1297
1298 /** Iterates over all of the polygon curves in a polygon header. */
1299 class GDIPolygonCurveIter {
1300 public:
halcanary96fcdcc2015-08-27 07:41:13 -07001301 GDIPolygonCurveIter() : fCurCurve(nullptr), fEndCurve(nullptr) { }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001302
1303 GDIPolygonCurveIter(const TTPOLYGONHEADER* curPolygon)
1304 : fCurCurve(SkTAddOffset<const TTPOLYCURVE>(curPolygon, sizeof(TTPOLYGONHEADER)))
1305 , fEndCurve(SkTAddOffset<const TTPOLYCURVE>(curPolygon, curPolygon->cb))
1306 { }
1307
halcanary96fcdcc2015-08-27 07:41:13 -07001308 bool isSet() { return fCurCurve != nullptr; }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001309
1310 void set(const TTPOLYGONHEADER* curPolygon) {
1311 fCurCurve = SkTAddOffset<const TTPOLYCURVE>(curPolygon, sizeof(TTPOLYGONHEADER));
1312 fEndCurve = SkTAddOffset<const TTPOLYCURVE>(curPolygon, curPolygon->cb);
1313 }
1314 void set() {
halcanary96fcdcc2015-08-27 07:41:13 -07001315 fCurCurve = nullptr;
1316 fEndCurve = nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001317 }
1318
1319 const TTPOLYCURVE* next() {
1320 if (fCurCurve >= fEndCurve) {
halcanary96fcdcc2015-08-27 07:41:13 -07001321 return nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001322 }
1323 const TTPOLYCURVE* thisCurve = fCurCurve;
1324 fCurCurve = SkTAddOffset<const TTPOLYCURVE>(fCurCurve, size_of_TTPOLYCURVE(*fCurCurve));
1325 return thisCurve;
1326 }
1327 private:
1328 size_t size_of_TTPOLYCURVE(const TTPOLYCURVE& curve) {
1329 return 2*sizeof(WORD) + curve.cpfx*sizeof(POINTFX);
1330 }
1331 const TTPOLYCURVE* fCurCurve;
1332 const TTPOLYCURVE* fEndCurve;
1333 };
1334
1335 /** Iterates over all of the polygon points in a polygon curve. */
1336 class GDIPolygonCurvePointIter {
1337 public:
halcanary96fcdcc2015-08-27 07:41:13 -07001338 GDIPolygonCurvePointIter() : fCurveType(0), fCurPoint(nullptr), fEndPoint(nullptr) { }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001339
1340 GDIPolygonCurvePointIter(const TTPOLYCURVE* curPolygon)
1341 : fCurveType(curPolygon->wType)
1342 , fCurPoint(&curPolygon->apfx[0])
1343 , fEndPoint(&curPolygon->apfx[curPolygon->cpfx])
1344 { }
1345
halcanary96fcdcc2015-08-27 07:41:13 -07001346 bool isSet() { return fCurPoint != nullptr; }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001347
1348 void set(const TTPOLYCURVE* curPolygon) {
1349 fCurveType = curPolygon->wType;
1350 fCurPoint = &curPolygon->apfx[0];
1351 fEndPoint = &curPolygon->apfx[curPolygon->cpfx];
1352 }
1353 void set() {
halcanary96fcdcc2015-08-27 07:41:13 -07001354 fCurPoint = nullptr;
1355 fEndPoint = nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001356 }
1357
1358 const POINTFX* next() {
1359 if (fCurPoint >= fEndPoint) {
halcanary96fcdcc2015-08-27 07:41:13 -07001360 return nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001361 }
1362 const POINTFX* thisPoint = fCurPoint;
1363 ++fCurPoint;
1364 return thisPoint;
1365 }
1366
1367 WORD fCurveType;
1368 private:
1369 const POINTFX* fCurPoint;
1370 const POINTFX* fEndPoint;
1371 };
1372
1373 GDIPolygonHeaderIter fHeaderIter;
1374 GDIPolygonCurveIter fCurveIter;
1375 GDIPolygonCurvePointIter fPointIter;
1376};
1377
1378static void sk_path_from_gdi_path(SkPath* path, const uint8_t* glyphbuf, DWORD total_size) {
bungeman@google.comd1c7f712013-03-11 18:51:19 +00001379 const uint8_t* cur_glyph = glyphbuf;
1380 const uint8_t* end_glyph = glyphbuf + total_size;
1381
1382 while (cur_glyph < end_glyph) {
1383 const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
1384
1385 const uint8_t* end_poly = cur_glyph + th->cb;
1386 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
1387
1388 path->moveTo(SkFixedToScalar( SkFIXEDToFixed(th->pfxStart.x)),
1389 SkFixedToScalar(-SkFIXEDToFixed(th->pfxStart.y)));
1390
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++) {
1396 path->lineTo(SkFixedToScalar( SkFIXEDToFixed(pc->apfx[i].x)),
1397 SkFixedToScalar(-SkFIXEDToFixed(pc->apfx[i].y)));
1398 }
1399 }
1400
1401 if (pc->wType == TT_PRIM_QSPLINE) {
1402 for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
1403 POINTFX pnt_b = pc->apfx[u]; // B is always the current point
1404 POINTFX pnt_c = pc->apfx[u+1];
1405
1406 if (u < pc->cpfx - 2) { // If not on last spline, compute C
1407 pnt_c.x = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.x),
1408 SkFIXEDToFixed(pnt_c.x)));
1409 pnt_c.y = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.y),
1410 SkFIXEDToFixed(pnt_c.y)));
1411 }
1412
1413 path->quadTo(SkFixedToScalar( SkFIXEDToFixed(pnt_b.x)),
1414 SkFixedToScalar(-SkFIXEDToFixed(pnt_b.y)),
1415 SkFixedToScalar( SkFIXEDToFixed(pnt_c.x)),
1416 SkFixedToScalar(-SkFIXEDToFixed(pnt_c.y)));
1417 }
1418 }
1419 // Advance past this TTPOLYCURVE.
1420 cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx;
1421 }
1422 cur_glyph += th->cb;
1423 path->close();
reed@google.comac6b9792011-03-11 15:42:51 +00001424 }
reed@google.comac6b9792011-03-11 15:42:51 +00001425}
1426
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001427#define move_next_expected_hinted_point(iter, pElem) do {\
1428 pElem = iter.next(); \
halcanary96fcdcc2015-08-27 07:41:13 -07001429 if (nullptr == pElem) return false; \
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001430} while(0)
1431
1432// It is possible for the hinted and unhinted versions of the same path to have
1433// a different number of points due to GDI's handling of flipped points.
1434// If this is detected, this will return false.
1435static bool sk_path_from_gdi_paths(SkPath* path, const uint8_t* glyphbuf, DWORD total_size,
bungeman@google.com0abbff92013-07-27 20:37:56 +00001436 GDIGlyphbufferPointIter hintedYs) {
1437 const uint8_t* cur_glyph = glyphbuf;
1438 const uint8_t* end_glyph = glyphbuf + total_size;
1439
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001440 POINTFX const * hintedPoint;
1441
bungeman@google.com0abbff92013-07-27 20:37:56 +00001442 while (cur_glyph < end_glyph) {
1443 const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
1444
1445 const uint8_t* end_poly = cur_glyph + th->cb;
1446 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
1447
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001448 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001449 path->moveTo(SkFixedToScalar( SkFIXEDToFixed(th->pfxStart.x)),
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001450 SkFixedToScalar(-SkFIXEDToFixed(hintedPoint->y)));
bungeman@google.com0abbff92013-07-27 20:37:56 +00001451
1452 while (cur_poly < end_poly) {
1453 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
1454
1455 if (pc->wType == TT_PRIM_LINE) {
1456 for (uint16_t i = 0; i < pc->cpfx; i++) {
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001457 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001458 path->lineTo(SkFixedToScalar( SkFIXEDToFixed(pc->apfx[i].x)),
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001459 SkFixedToScalar(-SkFIXEDToFixed(hintedPoint->y)));
bungeman@google.com0abbff92013-07-27 20:37:56 +00001460 }
1461 }
1462
1463 if (pc->wType == TT_PRIM_QSPLINE) {
1464 POINTFX currentPoint = pc->apfx[0];
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001465 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001466 // only take the hinted y if it wasn't flipped
1467 if (hintedYs.currentCurveType() == TT_PRIM_QSPLINE) {
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001468 currentPoint.y = hintedPoint->y;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001469 }
1470 for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
1471 POINTFX pnt_b = currentPoint;//pc->apfx[u]; // B is always the current point
1472 POINTFX pnt_c = pc->apfx[u+1];
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001473 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001474 // only take the hinted y if it wasn't flipped
1475 if (hintedYs.currentCurveType() == TT_PRIM_QSPLINE) {
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001476 pnt_c.y = hintedPoint->y;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001477 }
1478 currentPoint.x = pnt_c.x;
1479 currentPoint.y = pnt_c.y;
1480
1481 if (u < pc->cpfx - 2) { // If not on last spline, compute C
1482 pnt_c.x = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.x),
1483 SkFIXEDToFixed(pnt_c.x)));
1484 pnt_c.y = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.y),
1485 SkFIXEDToFixed(pnt_c.y)));
1486 }
1487
1488 path->quadTo(SkFixedToScalar( SkFIXEDToFixed(pnt_b.x)),
1489 SkFixedToScalar(-SkFIXEDToFixed(pnt_b.y)),
1490 SkFixedToScalar( SkFIXEDToFixed(pnt_c.x)),
1491 SkFixedToScalar(-SkFIXEDToFixed(pnt_c.y)));
1492 }
1493 }
1494 // Advance past this TTPOLYCURVE.
1495 cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx;
1496 }
1497 cur_glyph += th->cb;
1498 path->close();
1499 }
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001500 return true;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001501}
1502
Ben Wagner6e9ac122016-11-11 14:31:06 -05001503DWORD SkScalerContext_GDI::getGDIGlyphPath(SkGlyphID glyph, UINT flags,
1504 SkAutoSTMalloc<BUFFERSIZE, uint8_t>* glyphbuf)
bungeman@google.com0abbff92013-07-27 20:37:56 +00001505{
1506 GLYPHMETRICS gm;
1507
Ben Wagner6e9ac122016-11-11 14:31:06 -05001508 DWORD total_size = GetGlyphOutlineW(fDDC, glyph, flags, &gm, BUFFERSIZE, glyphbuf->get(), &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001509 // Sometimes GetGlyphOutlineW returns a number larger than BUFFERSIZE even if BUFFERSIZE > 0.
1510 // It has been verified that this does not involve a buffer overrun.
1511 if (GDI_ERROR == total_size || total_size > BUFFERSIZE) {
1512 // GDI_ERROR because the BUFFERSIZE was too small, or because the data was not accessible.
1513 // When the data is not accessable GetGlyphOutlineW fails rather quickly,
1514 // so just try to get the size. If that fails then ensure the data is accessible.
Ben Wagner6e9ac122016-11-11 14:31:06 -05001515 total_size = GetGlyphOutlineW(fDDC, glyph, flags, &gm, 0, nullptr, &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001516 if (GDI_ERROR == total_size) {
1517 LogFontTypeface::EnsureAccessible(this->getTypeface());
Ben Wagner6e9ac122016-11-11 14:31:06 -05001518 total_size = GetGlyphOutlineW(fDDC, glyph, flags, &gm, 0, nullptr, &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001519 if (GDI_ERROR == total_size) {
kkinnunenc6cb56f2014-06-24 00:12:27 -07001520 // GetGlyphOutlineW is known to fail for some characters, such as spaces.
1521 // In these cases, just return that the glyph does not have a shape.
bungeman@google.com0abbff92013-07-27 20:37:56 +00001522 return 0;
1523 }
1524 }
1525
1526 glyphbuf->reset(total_size);
1527
Ben Wagner6e9ac122016-11-11 14:31:06 -05001528 DWORD ret = GetGlyphOutlineW(fDDC, glyph, flags, &gm, total_size, glyphbuf->get(), &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001529 if (GDI_ERROR == ret) {
1530 LogFontTypeface::EnsureAccessible(this->getTypeface());
Ben Wagner6e9ac122016-11-11 14:31:06 -05001531 ret = GetGlyphOutlineW(fDDC, glyph, flags, &gm, total_size, glyphbuf->get(), &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001532 if (GDI_ERROR == ret) {
1533 SkASSERT(false);
1534 return 0;
1535 }
1536 }
1537 }
1538 return total_size;
1539}
1540
Ben Wagner5ddb3082018-03-29 11:18:06 -04001541bool SkScalerContext_GDI::generatePath(SkGlyphID glyph, SkPath* path) {
dcheng3ba043f2015-07-08 13:25:23 -07001542 SkASSERT(path);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001543 SkASSERT(fDDC);
1544
1545 path->reset();
1546
1547 // Out of all the fonts on a typical Windows box,
1548 // 25% of glyphs require more than 2KB.
1549 // 1% of glyphs require more than 4KB.
1550 // 0.01% of glyphs require more than 8KB.
1551 // 8KB is less than 1% of the normal 1MB stack on Windows.
1552 // Note that some web fonts glyphs require more than 20KB.
1553 //static const DWORD BUFFERSIZE = (1 << 13);
1554
1555 //GDI only uses hinted outlines when axis aligned.
1556 UINT format = GGO_NATIVE | GGO_GLYPH_INDEX;
Mike Reed04346d52018-11-05 12:45:32 -05001557 if (fRec.getHinting() == kNo_SkFontHinting || fRec.getHinting() == kSlight_SkFontHinting){
bungeman@google.com0abbff92013-07-27 20:37:56 +00001558 format |= GGO_UNHINTED;
1559 }
1560 SkAutoSTMalloc<BUFFERSIZE, uint8_t> glyphbuf(BUFFERSIZE);
1561 DWORD total_size = getGDIGlyphPath(glyph, format, &glyphbuf);
1562 if (0 == total_size) {
Ben Wagner5ddb3082018-03-29 11:18:06 -04001563 return false;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001564 }
1565
Mike Reed04346d52018-11-05 12:45:32 -05001566 if (fRec.getHinting() != kSlight_SkFontHinting) {
bungeman@google.com0abbff92013-07-27 20:37:56 +00001567 sk_path_from_gdi_path(path, glyphbuf, total_size);
1568 } else {
1569 //GDI only uses hinted outlines when axis aligned.
1570 UINT format = GGO_NATIVE | GGO_GLYPH_INDEX;
1571
1572 SkAutoSTMalloc<BUFFERSIZE, uint8_t> hintedGlyphbuf(BUFFERSIZE);
1573 DWORD hinted_total_size = getGDIGlyphPath(glyph, format, &hintedGlyphbuf);
1574 if (0 == hinted_total_size) {
Ben Wagner5ddb3082018-03-29 11:18:06 -04001575 return false;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001576 }
1577
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001578 if (!sk_path_from_gdi_paths(path, glyphbuf, total_size,
1579 GDIGlyphbufferPointIter(hintedGlyphbuf, hinted_total_size)))
1580 {
1581 path->reset();
1582 sk_path_from_gdi_path(path, glyphbuf, total_size);
1583 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001584 }
Ben Wagner5ddb3082018-03-29 11:18:06 -04001585 return true;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001586}
1587
reed@google.com484f5bc2013-04-24 19:14:56 +00001588static void logfont_for_name(const char* familyName, LOGFONT* lf) {
1589 sk_bzero(lf, sizeof(LOGFONT));
bungeman@google.come70f7982012-06-01 19:38:19 +00001590#ifdef UNICODE
reed@google.com484f5bc2013-04-24 19:14:56 +00001591 // Get the buffer size needed first.
1592 size_t str_len = ::MultiByteToWideChar(CP_UTF8, 0, familyName,
halcanary96fcdcc2015-08-27 07:41:13 -07001593 -1, nullptr, 0);
reed@google.com484f5bc2013-04-24 19:14:56 +00001594 // Allocate a buffer (str_len already has terminating null
1595 // accounted for).
1596 wchar_t *wideFamilyName = new wchar_t[str_len];
1597 // Now actually convert the string.
1598 ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1,
1599 wideFamilyName, str_len);
1600 ::wcsncpy(lf->lfFaceName, wideFamilyName, LF_FACESIZE - 1);
1601 delete [] wideFamilyName;
1602 lf->lfFaceName[LF_FACESIZE-1] = L'\0';
bungeman@google.come70f7982012-06-01 19:38:19 +00001603#else
reed@google.com484f5bc2013-04-24 19:14:56 +00001604 ::strncpy(lf->lfFaceName, familyName, LF_FACESIZE - 1);
1605 lf->lfFaceName[LF_FACESIZE - 1] = '\0';
bungeman@google.come70f7982012-06-01 19:38:19 +00001606#endif
1607}
1608
bungemanb374d6a2014-09-17 07:48:59 -07001609void LogFontTypeface::onGetFamilyName(SkString* familyName) const {
bungeman@google.com7103f182012-10-31 20:53:49 +00001610 // Get the actual name of the typeface. The logfont may not know this.
reed@google.com5526ede2013-03-25 13:03:37 +00001611 HFONT font = CreateFontIndirect(&fLogFont);
bungeman@google.com7103f182012-10-31 20:53:49 +00001612
halcanary96fcdcc2015-08-27 07:41:13 -07001613 HDC deviceContext = ::CreateCompatibleDC(nullptr);
bungeman@google.com7103f182012-10-31 20:53:49 +00001614 HFONT savefont = (HFONT)SelectObject(deviceContext, font);
1615
bungemanb374d6a2014-09-17 07:48:59 -07001616 dcfontname_to_skstring(deviceContext, fLogFont, familyName);
bungeman@google.com7103f182012-10-31 20:53:49 +00001617
1618 if (deviceContext) {
1619 ::SelectObject(deviceContext, savefont);
1620 ::DeleteDC(deviceContext);
1621 }
1622 if (font) {
1623 ::DeleteObject(font);
1624 }
bungemanb374d6a2014-09-17 07:48:59 -07001625}
bungeman@google.com7103f182012-10-31 20:53:49 +00001626
bungemanb374d6a2014-09-17 07:48:59 -07001627void LogFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc,
1628 bool* isLocalStream) const {
1629 SkString familyName;
1630 this->onGetFamilyName(&familyName);
reed@google.com5526ede2013-03-25 13:03:37 +00001631 desc->setFamilyName(familyName.c_str());
bungemanb8113782016-07-25 16:54:59 -07001632 desc->setStyle(this->fontStyle());
reed@google.com5526ede2013-03-25 13:03:37 +00001633 *isLocalStream = this->fSerializeAsStream;
reed@google.comac6b9792011-03-11 15:42:51 +00001634}
1635
Hal Canary46cc3da2018-05-09 11:50:34 -04001636void LogFontTypeface::getGlyphToUnicodeMap(SkUnichar* dstArray) const {
1637 HDC hdc = ::CreateCompatibleDC(nullptr);
1638 HFONT font = CreateFontIndirect(&fLogFont);
1639 HFONT savefont = (HFONT)SelectObject(hdc, font);
1640 LOGFONT lf = fLogFont;
1641 HFONT designFont = CreateFontIndirect(&lf);
1642 SelectObject(hdc, designFont);
1643
1644 unsigned int glyphCount = calculateGlyphCount(hdc, fLogFont);
1645 populate_glyph_to_unicode(hdc, glyphCount, dstArray);
1646
1647 SelectObject(hdc, savefont);
1648 DeleteObject(designFont);
1649 DeleteObject(font);
1650 DeleteDC(hdc);
1651}
1652
Hal Canary209e4b12017-05-04 14:23:55 -04001653std::unique_ptr<SkAdvancedTypefaceMetrics> LogFontTypeface::onGetAdvancedMetrics() const {
reed@google.com2689f612013-03-20 20:01:47 +00001654 LOGFONT lf = fLogFont;
Hal Canary209e4b12017-05-04 14:23:55 -04001655 std::unique_ptr<SkAdvancedTypefaceMetrics> info(nullptr);
reed@google.comac6b9792011-03-11 15:42:51 +00001656
halcanary96fcdcc2015-08-27 07:41:13 -07001657 HDC hdc = CreateCompatibleDC(nullptr);
reed@google.comac6b9792011-03-11 15:42:51 +00001658 HFONT font = CreateFontIndirect(&lf);
1659 HFONT savefont = (HFONT)SelectObject(hdc, font);
halcanary96fcdcc2015-08-27 07:41:13 -07001660 HFONT designFont = nullptr;
reed@google.comac6b9792011-03-11 15:42:51 +00001661
reed@google.com05b6f3a2011-11-28 15:30:28 +00001662 const char stem_chars[] = {'i', 'I', '!', '1'};
1663 int16_t min_width;
1664 unsigned glyphCount;
1665
reed@google.comac6b9792011-03-11 15:42:51 +00001666 // To request design units, create a logical font whose height is specified
1667 // as unitsPerEm.
1668 OUTLINETEXTMETRIC otm;
bungeman@google.com39698b12011-11-15 22:26:41 +00001669 unsigned int otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
1670 if (0 == otmRet) {
reed@google.com055180c2013-03-21 18:46:35 +00001671 call_ensure_accessible(lf);
bungeman@google.com39698b12011-11-15 22:26:41 +00001672 otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
1673 }
1674 if (!otmRet || !GetTextFace(hdc, LF_FACESIZE, lf.lfFaceName)) {
reed@google.comac6b9792011-03-11 15:42:51 +00001675 goto Error;
1676 }
1677 lf.lfHeight = -SkToS32(otm.otmEMSquare);
1678 designFont = CreateFontIndirect(&lf);
1679 SelectObject(hdc, designFont);
1680 if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) {
1681 goto Error;
1682 }
bungeman@google.com7bdd6142013-07-15 19:42:57 +00001683 glyphCount = calculateGlyphCount(hdc, fLogFont);
reed@google.comac6b9792011-03-11 15:42:51 +00001684
Hal Canary209e4b12017-05-04 14:23:55 -04001685 info.reset(new SkAdvancedTypefaceMetrics);
bungeman@google.com7103f182012-10-31 20:53:49 +00001686 tchar_to_skstring(lf.lfFaceName, &info->fFontName);
Hal Canarya865d252017-11-09 16:16:09 -05001687
1688 SkOTTableOS2_V4::Type fsType;
1689 if (sizeof(fsType) == this->getTableData(SkTEndian_SwapBE32(SkOTTableOS2::TAG),
1690 offsetof(SkOTTableOS2_V4, fsType),
1691 sizeof(fsType),
1692 &fsType)) {
1693 SkOTUtils::SetAdvancedTypefaceFlags(fsType, info.get());
1694 } else {
1695 // If bit 1 is set, the font may not be embedded in a document.
1696 // If bit 1 is clear, the font can be embedded.
1697 // If bit 2 is set, the embedding is read-only.
1698 if (otm.otmfsType & 0x1) {
1699 info->fFlags |= SkAdvancedTypefaceMetrics::kNotEmbeddable_FontFlag;
1700 }
vandebo0f9bad02014-06-19 11:05:39 -07001701 }
reed@google.comac6b9792011-03-11 15:42:51 +00001702
vandebo@chromium.orgd41e70d2012-03-08 19:41:01 +00001703 if (glyphCount > 0 &&
1704 (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE)) {
reed@google.comac6b9792011-03-11 15:42:51 +00001705 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
1706 } else {
edisonn@google.com390c6d72013-04-06 20:26:15 +00001707 goto ReturnInfo;
reed@google.comac6b9792011-03-11 15:42:51 +00001708 }
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +00001709
reed@google.comac6b9792011-03-11 15:42:51 +00001710 // If this bit is clear the font is a fixed pitch font.
1711 if (!(otm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
1712 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1713 }
1714 if (otm.otmTextMetrics.tmItalic) {
1715 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1716 }
reed@google.comac6b9792011-03-11 15:42:51 +00001717 if (otm.otmTextMetrics.tmPitchAndFamily & FF_ROMAN) {
1718 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1719 } else if (otm.otmTextMetrics.tmPitchAndFamily & FF_SCRIPT) {
1720 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1721 }
1722
1723 // The main italic angle of the font, in tenths of a degree counterclockwise
1724 // from vertical.
1725 info->fItalicAngle = otm.otmItalicAngle / 10;
1726 info->fAscent = SkToS16(otm.otmTextMetrics.tmAscent);
1727 info->fDescent = SkToS16(-otm.otmTextMetrics.tmDescent);
1728 // TODO(ctguil): Use alternate cap height calculation.
1729 // MSDN says otmsCapEmHeight is not support but it is returning a value on
1730 // my Win7 box.
1731 info->fCapHeight = otm.otmsCapEmHeight;
1732 info->fBBox =
1733 SkIRect::MakeLTRB(otm.otmrcFontBox.left, otm.otmrcFontBox.top,
1734 otm.otmrcFontBox.right, otm.otmrcFontBox.bottom);
1735
1736 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1737 // This probably isn't very good with an italic font.
reed@google.com05b6f3a2011-11-28 15:30:28 +00001738 min_width = SHRT_MAX;
reed@google.comac6b9792011-03-11 15:42:51 +00001739 info->fStemV = 0;
reed@google.comac6b9792011-03-11 15:42:51 +00001740 for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {
1741 ABC abcWidths;
1742 if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) {
1743 int16_t width = abcWidths.abcB;
1744 if (width > 0 && width < min_width) {
1745 min_width = width;
1746 info->fStemV = min_width;
1747 }
1748 }
1749 }
1750
reed@google.comac6b9792011-03-11 15:42:51 +00001751Error:
edisonn@google.com390c6d72013-04-06 20:26:15 +00001752ReturnInfo:
reed@google.comac6b9792011-03-11 15:42:51 +00001753 SelectObject(hdc, savefont);
1754 DeleteObject(designFont);
1755 DeleteObject(font);
1756 DeleteDC(hdc);
1757
1758 return info;
1759}
1760
bungeman@google.coma5501992012-05-18 19:06:41 +00001761//Dummy representation of a Base64 encoded GUID from create_unique_font_name.
1762#define BASE64_GUID_ID "XXXXXXXXXXXXXXXXXXXXXXXX"
halcanary96fcdcc2015-08-27 07:41:13 -07001763//Length of GUID representation from create_id, including nullptr terminator.
bungeman@google.coma5501992012-05-18 19:06:41 +00001764#define BASE64_GUID_ID_LEN SK_ARRAY_COUNT(BASE64_GUID_ID)
reed@google.comac6b9792011-03-11 15:42:51 +00001765
bungeman99fe8222015-08-20 07:57:51 -07001766static_assert(BASE64_GUID_ID_LEN < LF_FACESIZE, "GUID_longer_than_facesize");
bungeman@google.coma5501992012-05-18 19:06:41 +00001767
1768/**
1769 NameID 6 Postscript names cannot have the character '/'.
1770 It would be easier to hex encode the GUID, but that is 32 bytes,
1771 and many systems have issues with names longer than 28 bytes.
1772 The following need not be any standard base64 encoding.
1773 The encoded value is never decoded.
1774*/
rmistry@google.comd6176b02012-08-23 18:14:13 +00001775static const char postscript_safe_base64_encode[] =
bungeman@google.coma5501992012-05-18 19:06:41 +00001776 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1777 "abcdefghijklmnopqrstuvwxyz"
1778 "0123456789-_=";
1779
1780/**
1781 Formats a GUID into Base64 and places it into buffer.
1782 buffer should have space for at least BASE64_GUID_ID_LEN characters.
1783 The string will always be null terminated.
1784 XXXXXXXXXXXXXXXXXXXXXXXX0
1785 */
1786static void format_guid_b64(const GUID& guid, char* buffer, size_t bufferSize) {
1787 SkASSERT(bufferSize >= BASE64_GUID_ID_LEN);
1788 size_t written = SkBase64::Encode(&guid, sizeof(guid), buffer, postscript_safe_base64_encode);
1789 SkASSERT(written < LF_FACESIZE);
1790 buffer[written] = '\0';
1791}
1792
1793/**
1794 Creates a Base64 encoded GUID and places it into buffer.
1795 buffer should have space for at least BASE64_GUID_ID_LEN characters.
1796 The string will always be null terminated.
1797 XXXXXXXXXXXXXXXXXXXXXXXX0
1798 */
1799static HRESULT create_unique_font_name(char* buffer, size_t bufferSize) {
1800 GUID guid = {};
1801 if (FAILED(CoCreateGuid(&guid))) {
1802 return E_UNEXPECTED;
1803 }
1804 format_guid_b64(guid, buffer, bufferSize);
1805
1806 return S_OK;
1807}
1808
1809/**
halcanary96fcdcc2015-08-27 07:41:13 -07001810 Introduces a font to GDI. On failure will return nullptr. The returned handle
bungeman@google.coma5501992012-05-18 19:06:41 +00001811 should eventually be passed to RemoveFontMemResourceEx.
1812*/
1813static HANDLE activate_font(SkData* fontData) {
1814 DWORD numFonts = 0;
1815 //AddFontMemResourceEx just copies the data, but does not specify const.
1816 HANDLE fontHandle = AddFontMemResourceEx(const_cast<void*>(fontData->data()),
bungeman@google.com4b18f572013-07-22 15:21:23 +00001817 static_cast<DWORD>(fontData->size()),
bungeman@google.coma5501992012-05-18 19:06:41 +00001818 0,
1819 &numFonts);
1820
halcanary96fcdcc2015-08-27 07:41:13 -07001821 if (fontHandle != nullptr && numFonts < 1) {
bungeman@google.coma5501992012-05-18 19:06:41 +00001822 RemoveFontMemResourceEx(fontHandle);
halcanary96fcdcc2015-08-27 07:41:13 -07001823 return nullptr;
bungeman@google.coma5501992012-05-18 19:06:41 +00001824 }
1825
1826 return fontHandle;
1827}
1828
scroggoa1193e42015-01-21 12:09:53 -08001829// Does not affect ownership of stream.
Mike Reed59227392017-09-26 09:46:08 -04001830static sk_sp<SkTypeface> create_from_stream(std::unique_ptr<SkStreamAsset> stream) {
bungeman@google.coma5501992012-05-18 19:06:41 +00001831 // Create a unique and unpredictable font name.
1832 // Avoids collisions and access from CSS.
1833 char familyName[BASE64_GUID_ID_LEN];
1834 const int familyNameSize = SK_ARRAY_COUNT(familyName);
1835 if (FAILED(create_unique_font_name(familyName, familyNameSize))) {
halcanary96fcdcc2015-08-27 07:41:13 -07001836 return nullptr;
bungeman@google.coma5501992012-05-18 19:06:41 +00001837 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001838
bungeman@google.coma5501992012-05-18 19:06:41 +00001839 // Change the name of the font.
Mike Reed59227392017-09-26 09:46:08 -04001840 sk_sp<SkData> rewrittenFontData(SkOTUtils::RenameFont(stream.get(), familyName, familyNameSize-1));
halcanary96fcdcc2015-08-27 07:41:13 -07001841 if (nullptr == rewrittenFontData.get()) {
1842 return nullptr;
bungeman@google.coma5501992012-05-18 19:06:41 +00001843 }
bungeman@google.coma5501992012-05-18 19:06:41 +00001844
1845 // Register the font with GDI.
bungeman@google.come9bbee32012-05-21 13:46:13 +00001846 HANDLE fontReference = activate_font(rewrittenFontData.get());
halcanary96fcdcc2015-08-27 07:41:13 -07001847 if (nullptr == fontReference) {
1848 return nullptr;
bungeman@google.coma5501992012-05-18 19:06:41 +00001849 }
1850
1851 // Create the typeface.
1852 LOGFONT lf;
reed@google.com484f5bc2013-04-24 19:14:56 +00001853 logfont_for_name(familyName, &lf);
bungeman@google.coma5501992012-05-18 19:06:41 +00001854
Mike Reed59227392017-09-26 09:46:08 -04001855 return sk_sp<SkTypeface>(SkCreateFontMemResourceTypefaceFromLOGFONT(lf, fontReference));
reed@google.comac6b9792011-03-11 15:42:51 +00001856}
1857
bungeman5f213d92015-01-27 05:39:10 -08001858SkStreamAsset* LogFontTypeface::onOpenStream(int* ttcIndex) const {
reed@google.com0042b9c2013-03-21 20:16:04 +00001859 *ttcIndex = 0;
1860
ctguil@chromium.orgf4c26222011-05-16 22:00:05 +00001861 const DWORD kTTCTag =
1862 SkEndian_SwapBE32(SkSetFourByteTag('t', 't', 'c', 'f'));
reed@google.com0042b9c2013-03-21 20:16:04 +00001863 LOGFONT lf = fLogFont;
reed@google.comac6b9792011-03-11 15:42:51 +00001864
halcanary96fcdcc2015-08-27 07:41:13 -07001865 HDC hdc = ::CreateCompatibleDC(nullptr);
reed@google.com59d2f632011-05-02 19:36:59 +00001866 HFONT font = CreateFontIndirect(&lf);
reed@google.comac6b9792011-03-11 15:42:51 +00001867 HFONT savefont = (HFONT)SelectObject(hdc, font);
1868
halcanary96fcdcc2015-08-27 07:41:13 -07001869 SkMemoryStream* stream = nullptr;
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001870 DWORD tables[2] = {kTTCTag, 0};
Chris Dalton1ef80942017-12-04 12:01:30 -07001871 for (size_t i = 0; i < SK_ARRAY_COUNT(tables); i++) {
halcanary96fcdcc2015-08-27 07:41:13 -07001872 DWORD bufferSize = GetFontData(hdc, tables[i], 0, nullptr, 0);
bungeman@google.com39698b12011-11-15 22:26:41 +00001873 if (bufferSize == GDI_ERROR) {
reed@google.com055180c2013-03-21 18:46:35 +00001874 call_ensure_accessible(lf);
halcanary96fcdcc2015-08-27 07:41:13 -07001875 bufferSize = GetFontData(hdc, tables[i], 0, nullptr, 0);
bungeman@google.com39698b12011-11-15 22:26:41 +00001876 }
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001877 if (bufferSize != GDI_ERROR) {
1878 stream = new SkMemoryStream(bufferSize);
bungeman@google.com4b18f572013-07-22 15:21:23 +00001879 if (GetFontData(hdc, tables[i], 0, (void*)stream->getMemoryBase(), bufferSize)) {
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001880 break;
1881 } else {
1882 delete stream;
halcanary96fcdcc2015-08-27 07:41:13 -07001883 stream = nullptr;
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001884 }
1885 }
reed@google.comac6b9792011-03-11 15:42:51 +00001886 }
1887
1888 SelectObject(hdc, savefont);
1889 DeleteObject(font);
1890 DeleteDC(hdc);
1891
1892 return stream;
1893}
1894
Bruce Wang536ad2c2018-06-25 11:37:25 -04001895sk_sp<SkTypeface> LogFontTypeface::onMakeClone(const SkFontArguments& args) const {
1896 return sk_ref_sp(this);
1897}
1898
bungeman@google.com3c996f82013-10-24 21:39:35 +00001899static void bmpCharsToGlyphs(HDC hdc, const WCHAR* bmpChars, int count, uint16_t* glyphs,
1900 bool Ox1FHack)
1901{
1902 DWORD result = GetGlyphIndicesW(hdc, bmpChars, count, glyphs, GGI_MARK_NONEXISTING_GLYPHS);
1903 if (GDI_ERROR == result) {
1904 for (int i = 0; i < count; ++i) {
1905 glyphs[i] = 0;
1906 }
1907 return;
1908 }
1909
1910 if (Ox1FHack) {
1911 for (int i = 0; i < count; ++i) {
1912 if (0xFFFF == glyphs[i] || 0x1F == glyphs[i]) {
1913 glyphs[i] = 0;
1914 }
1915 }
1916 } else {
1917 for (int i = 0; i < count; ++i) {
1918 if (0xFFFF == glyphs[i]){
1919 glyphs[i] = 0;
1920 }
1921 }
1922 }
1923}
1924
1925static uint16_t nonBmpCharToGlyph(HDC hdc, SCRIPT_CACHE* scriptCache, const WCHAR utf16[2]) {
1926 uint16_t index = 0;
1927 // Use uniscribe to detemine glyph index for non-BMP characters.
1928 static const int numWCHAR = 2;
1929 static const int maxItems = 2;
halcanary96fcdcc2015-08-27 07:41:13 -07001930 // MSDN states that this can be nullptr, but some things don't work then.
Chris Dalton1ef80942017-12-04 12:01:30 -07001931 SCRIPT_CONTROL scriptControl;
1932 memset(&scriptControl, 0, sizeof(scriptControl));
bungeman@google.com3c996f82013-10-24 21:39:35 +00001933 // Add extra item to SCRIPT_ITEM to work around a bug (now documented).
1934 // https://bugzilla.mozilla.org/show_bug.cgi?id=366643
1935 SCRIPT_ITEM si[maxItems + 1];
1936 int numItems;
halcanary96fcdcc2015-08-27 07:41:13 -07001937 HRZM(ScriptItemize(utf16, numWCHAR, maxItems, &scriptControl, nullptr, si, &numItems),
bungeman@google.com3c996f82013-10-24 21:39:35 +00001938 "Could not itemize character.");
1939
1940 // Sometimes ScriptShape cannot find a glyph for a non-BMP and returns 2 space glyphs.
1941 static const int maxGlyphs = 2;
1942 SCRIPT_VISATTR vsa[maxGlyphs];
1943 WORD outGlyphs[maxGlyphs];
1944 WORD logClust[numWCHAR];
1945 int numGlyphs;
1946 HRZM(ScriptShape(hdc, scriptCache, utf16, numWCHAR, maxGlyphs, &si[0].a,
1947 outGlyphs, logClust, vsa, &numGlyphs),
1948 "Could not shape character.");
1949 if (1 == numGlyphs) {
1950 index = outGlyphs[0];
1951 }
1952 return index;
1953}
1954
1955class SkAutoHDC {
1956public:
1957 SkAutoHDC(const LOGFONT& lf)
halcanary96fcdcc2015-08-27 07:41:13 -07001958 : fHdc(::CreateCompatibleDC(nullptr))
bungeman@google.com3c996f82013-10-24 21:39:35 +00001959 , fFont(::CreateFontIndirect(&lf))
1960 , fSavefont((HFONT)SelectObject(fHdc, fFont))
1961 { }
1962 ~SkAutoHDC() {
1963 SelectObject(fHdc, fSavefont);
1964 DeleteObject(fFont);
1965 DeleteDC(fHdc);
1966 }
1967 operator HDC() { return fHdc; }
1968private:
1969 HDC fHdc;
1970 HFONT fFont;
1971 HFONT fSavefont;
1972};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +00001973#define SkAutoHDC(...) SK_REQUIRE_LOCAL_VAR(SkAutoHDC)
bungeman@google.com3c996f82013-10-24 21:39:35 +00001974
1975int LogFontTypeface::onCharsToGlyphs(const void* chars, Encoding encoding,
1976 uint16_t userGlyphs[], int glyphCount) const
1977{
1978 SkAutoHDC hdc(fLogFont);
1979
1980 TEXTMETRIC tm;
1981 if (0 == GetTextMetrics(hdc, &tm)) {
1982 call_ensure_accessible(fLogFont);
1983 if (0 == GetTextMetrics(hdc, &tm)) {
1984 tm.tmPitchAndFamily = TMPF_TRUETYPE;
1985 }
1986 }
1987 bool Ox1FHack = !(tm.tmPitchAndFamily & TMPF_VECTOR) /*&& winVer < Vista */;
1988
1989 SkAutoSTMalloc<256, uint16_t> scratchGlyphs;
1990 uint16_t* glyphs;
halcanary96fcdcc2015-08-27 07:41:13 -07001991 if (userGlyphs != nullptr) {
bungeman@google.com3c996f82013-10-24 21:39:35 +00001992 glyphs = userGlyphs;
1993 } else {
1994 glyphs = scratchGlyphs.reset(glyphCount);
1995 }
1996
1997 SCRIPT_CACHE sc = 0;
1998 switch (encoding) {
1999 case SkTypeface::kUTF8_Encoding: {
2000 static const int scratchCount = 256;
2001 WCHAR scratch[scratchCount];
2002 int glyphIndex = 0;
2003 const char* currentUtf8 = reinterpret_cast<const char*>(chars);
Kevin Lubick42846132018-01-05 10:11:11 -05002004 SkUnichar currentChar = 0;
bungeman@google.com3c996f82013-10-24 21:39:35 +00002005 if (glyphCount) {
2006 currentChar = SkUTF8_NextUnichar(&currentUtf8);
2007 }
2008 while (glyphIndex < glyphCount) {
2009 // Try a run of bmp.
2010 int glyphsLeft = SkTMin(glyphCount - glyphIndex, scratchCount);
2011 int runLength = 0;
2012 while (runLength < glyphsLeft && currentChar <= 0xFFFF) {
2013 scratch[runLength] = static_cast<WCHAR>(currentChar);
2014 ++runLength;
2015 if (runLength < glyphsLeft) {
2016 currentChar = SkUTF8_NextUnichar(&currentUtf8);
2017 }
2018 }
2019 if (runLength) {
2020 bmpCharsToGlyphs(hdc, scratch, runLength, &glyphs[glyphIndex], Ox1FHack);
2021 glyphIndex += runLength;
2022 }
2023
2024 // Try a run of non-bmp.
2025 while (glyphIndex < glyphCount && currentChar > 0xFFFF) {
Hal Canaryf107a2f2018-07-25 16:52:48 -04002026 SkUTF::ToUTF16(currentChar, reinterpret_cast<uint16_t*>(scratch));
bungeman@google.com3c996f82013-10-24 21:39:35 +00002027 glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, scratch);
2028 ++glyphIndex;
2029 if (glyphIndex < glyphCount) {
2030 currentChar = SkUTF8_NextUnichar(&currentUtf8);
2031 }
2032 }
2033 }
2034 break;
2035 }
2036 case SkTypeface::kUTF16_Encoding: {
2037 int glyphIndex = 0;
2038 const WCHAR* currentUtf16 = reinterpret_cast<const WCHAR*>(chars);
2039 while (glyphIndex < glyphCount) {
2040 // Try a run of bmp.
2041 int glyphsLeft = glyphCount - glyphIndex;
2042 int runLength = 0;
Ben Wagnerad031f52018-08-20 13:45:57 -04002043 while (runLength < glyphsLeft && !SkUTF16_IsLeadingSurrogate(currentUtf16[runLength])) {
bungeman@google.com3c996f82013-10-24 21:39:35 +00002044 ++runLength;
2045 }
2046 if (runLength) {
2047 bmpCharsToGlyphs(hdc, currentUtf16, runLength, &glyphs[glyphIndex], Ox1FHack);
2048 glyphIndex += runLength;
2049 currentUtf16 += runLength;
2050 }
2051
2052 // Try a run of non-bmp.
Ben Wagnerad031f52018-08-20 13:45:57 -04002053 while (glyphIndex < glyphCount && SkUTF16_IsLeadingSurrogate(*currentUtf16)) {
bungeman@google.com3c996f82013-10-24 21:39:35 +00002054 glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, currentUtf16);
2055 ++glyphIndex;
2056 currentUtf16 += 2;
2057 }
2058 }
2059 break;
2060 }
2061 case SkTypeface::kUTF32_Encoding: {
2062 static const int scratchCount = 256;
2063 WCHAR scratch[scratchCount];
2064 int glyphIndex = 0;
2065 const uint32_t* utf32 = reinterpret_cast<const uint32_t*>(chars);
2066 while (glyphIndex < glyphCount) {
2067 // Try a run of bmp.
2068 int glyphsLeft = SkTMin(glyphCount - glyphIndex, scratchCount);
2069 int runLength = 0;
2070 while (runLength < glyphsLeft && utf32[glyphIndex + runLength] <= 0xFFFF) {
2071 scratch[runLength] = static_cast<WCHAR>(utf32[glyphIndex + runLength]);
2072 ++runLength;
2073 }
2074 if (runLength) {
2075 bmpCharsToGlyphs(hdc, scratch, runLength, &glyphs[glyphIndex], Ox1FHack);
2076 glyphIndex += runLength;
2077 }
2078
2079 // Try a run of non-bmp.
2080 while (glyphIndex < glyphCount && utf32[glyphIndex] > 0xFFFF) {
Hal Canaryf107a2f2018-07-25 16:52:48 -04002081 SkUTF::ToUTF16(utf32[glyphIndex], reinterpret_cast<uint16_t*>(scratch));
bungeman@google.com3c996f82013-10-24 21:39:35 +00002082 glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, scratch);
2083 ++glyphIndex;
2084 }
2085 }
2086 break;
2087 }
2088 default:
djsollenf2b340f2016-01-29 08:51:04 -08002089 SK_ABORT("Invalid Text Encoding");
bungeman@google.com3c996f82013-10-24 21:39:35 +00002090 }
2091
2092 if (sc) {
2093 ::ScriptFreeCache(&sc);
2094 }
2095
2096 for (int i = 0; i < glyphCount; ++i) {
2097 if (0 == glyphs[i]) {
2098 return i;
2099 }
2100 }
2101 return glyphCount;
2102}
2103
bungeman@google.com7bdd6142013-07-15 19:42:57 +00002104int LogFontTypeface::onCountGlyphs() const {
halcanary96fcdcc2015-08-27 07:41:13 -07002105 HDC hdc = ::CreateCompatibleDC(nullptr);
bungeman@google.com7bdd6142013-07-15 19:42:57 +00002106 HFONT font = CreateFontIndirect(&fLogFont);
2107 HFONT savefont = (HFONT)SelectObject(hdc, font);
2108
2109 unsigned int glyphCount = calculateGlyphCount(hdc, fLogFont);
2110
2111 SelectObject(hdc, savefont);
2112 DeleteObject(font);
2113 DeleteDC(hdc);
2114
2115 return glyphCount;
2116}
2117
2118int LogFontTypeface::onGetUPEM() const {
halcanary96fcdcc2015-08-27 07:41:13 -07002119 HDC hdc = ::CreateCompatibleDC(nullptr);
bungeman@google.com7bdd6142013-07-15 19:42:57 +00002120 HFONT font = CreateFontIndirect(&fLogFont);
2121 HFONT savefont = (HFONT)SelectObject(hdc, font);
2122
2123 unsigned int upem = calculateUPEM(hdc, fLogFont);
2124
2125 SelectObject(hdc, savefont);
2126 DeleteObject(font);
2127 DeleteDC(hdc);
2128
2129 return upem;
2130}
2131
bungeman@google.com839702b2013-08-07 17:09:22 +00002132SkTypeface::LocalizedStrings* LogFontTypeface::onCreateFamilyNameIterator() const {
Ben Wagnerad031f52018-08-20 13:45:57 -04002133 sk_sp<SkTypeface::LocalizedStrings> nameIter =
2134 SkOTUtils::LocalizedStrings_NameTable::MakeForFamilyNames(*this);
2135 if (!nameIter) {
bungeman@google.coma9802692013-08-07 02:45:25 +00002136 SkString familyName;
2137 this->getFamilyName(&familyName);
2138 SkString language("und"); //undetermined
Ben Wagnerad031f52018-08-20 13:45:57 -04002139 nameIter = sk_make_sp<SkOTUtils::LocalizedStrings_SingleName>(familyName, language);
bungeman@google.coma9802692013-08-07 02:45:25 +00002140 }
Ben Wagnerad031f52018-08-20 13:45:57 -04002141 return nameIter.release();
bungeman@google.coma9802692013-08-07 02:45:25 +00002142}
2143
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002144int LogFontTypeface::onGetTableTags(SkFontTableTag tags[]) const {
2145 SkSFNTHeader header;
2146 if (sizeof(header) != this->onGetTableData(0, 0, sizeof(header), &header)) {
2147 return 0;
2148 }
2149
2150 int numTables = SkEndian_SwapBE16(header.numTables);
2151
2152 if (tags) {
2153 size_t size = numTables * sizeof(SkSFNTHeader::TableDirectoryEntry);
2154 SkAutoSTMalloc<0x20, SkSFNTHeader::TableDirectoryEntry> dir(numTables);
2155 if (size != this->onGetTableData(0, sizeof(header), size, dir.get())) {
2156 return 0;
2157 }
2158
2159 for (int i = 0; i < numTables; ++i) {
2160 tags[i] = SkEndian_SwapBE32(dir[i].tag);
2161 }
2162 }
2163 return numTables;
2164}
2165
2166size_t LogFontTypeface::onGetTableData(SkFontTableTag tag, size_t offset,
2167 size_t length, void* data) const
2168{
2169 LOGFONT lf = fLogFont;
2170
halcanary96fcdcc2015-08-27 07:41:13 -07002171 HDC hdc = ::CreateCompatibleDC(nullptr);
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002172 HFONT font = CreateFontIndirect(&lf);
2173 HFONT savefont = (HFONT)SelectObject(hdc, font);
2174
2175 tag = SkEndian_SwapBE32(tag);
halcanary96fcdcc2015-08-27 07:41:13 -07002176 if (nullptr == data) {
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002177 length = 0;
2178 }
robertphillips@google.com8b169312013-10-15 17:47:36 +00002179 DWORD bufferSize = GetFontData(hdc, tag, (DWORD) offset, data, (DWORD) length);
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002180 if (bufferSize == GDI_ERROR) {
2181 call_ensure_accessible(lf);
robertphillips@google.com8b169312013-10-15 17:47:36 +00002182 bufferSize = GetFontData(hdc, tag, (DWORD) offset, data, (DWORD) length);
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002183 }
2184
2185 SelectObject(hdc, savefont);
2186 DeleteObject(font);
2187 DeleteDC(hdc);
2188
2189 return bufferSize == GDI_ERROR ? 0 : bufferSize;
2190}
2191
reeda9322c22016-04-12 06:47:05 -07002192SkScalerContext* LogFontTypeface::onCreateScalerContext(const SkScalerContextEffects& effects,
2193 const SkDescriptor* desc) const {
bungeman7cfd46a2016-10-20 16:06:52 -04002194 auto ctx = skstd::make_unique<SkScalerContext_GDI>(
2195 sk_ref_sp(const_cast<LogFontTypeface*>(this)), effects, desc);
reed@google.com84e22d82013-07-10 15:38:20 +00002196 if (!ctx->isValid()) {
Ben Wagnerc05b2bf2016-11-03 16:51:26 -04002197 return nullptr;
reed@google.com84e22d82013-07-10 15:38:20 +00002198 }
bungeman7cfd46a2016-10-20 16:06:52 -04002199 return ctx.release();
reed@google.comac6b9792011-03-11 15:42:51 +00002200}
2201
reed@google.com0da48612013-03-19 16:06:52 +00002202void LogFontTypeface::onFilterRec(SkScalerContextRec* rec) const {
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00002203 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
2204 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
2205 {
2206 rec->fMaskFormat = SkMask::kA8_Format;
2207 rec->fFlags |= SkScalerContext::kGenA8FromLCD_Flag;
2208 }
2209
Mike Reedc88cc772018-10-23 12:05:47 -04002210 unsigned flagsWeDontSupport = SkScalerContext::kForceAutohinting_Flag |
reed@google.come8fab012011-07-13 15:25:33 +00002211 SkScalerContext::kEmbeddedBitmapText_Flag |
2212 SkScalerContext::kEmbolden_Flag |
2213 SkScalerContext::kLCD_BGROrder_Flag |
2214 SkScalerContext::kLCD_Vertical_Flag;
2215 rec->fFlags &= ~flagsWeDontSupport;
2216
Mike Reed04346d52018-11-05 12:45:32 -05002217 SkFontHinting h = rec->getHinting();
reed@google.come8fab012011-07-13 15:25:33 +00002218 switch (h) {
Mike Reed04346d52018-11-05 12:45:32 -05002219 case kNo_SkFontHinting:
bungeman@google.com0abbff92013-07-27 20:37:56 +00002220 break;
Mike Reed04346d52018-11-05 12:45:32 -05002221 case kSlight_SkFontHinting:
bungeman@google.com0abbff92013-07-27 20:37:56 +00002222 // Only do slight hinting when axis aligned.
bungeman@google.com7c86d8e2013-07-27 21:42:21 +00002223 // TODO: re-enable slight hinting when FontHostTest can pass.
2224 //if (!isAxisAligned(*rec)) {
Mike Reed04346d52018-11-05 12:45:32 -05002225 h = kNo_SkFontHinting;
bungeman@google.com7c86d8e2013-07-27 21:42:21 +00002226 //}
reed@google.come8fab012011-07-13 15:25:33 +00002227 break;
Mike Reed04346d52018-11-05 12:45:32 -05002228 case kNormal_SkFontHinting:
2229 case kFull_SkFontHinting:
bungeman@google.com0abbff92013-07-27 20:37:56 +00002230 // TODO: need to be able to distinguish subpixel positioned glyphs
2231 // and linear metrics.
2232 //rec->fFlags &= ~SkScalerContext::kSubpixelPositioning_Flag;
Mike Reed04346d52018-11-05 12:45:32 -05002233 h = kNormal_SkFontHinting;
reed@google.come8fab012011-07-13 15:25:33 +00002234 break;
2235 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002236 SkDEBUGFAIL("unknown hinting");
reed@google.come8fab012011-07-13 15:25:33 +00002237 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00002238 //TODO: if this is a bitmap font, squash hinting and subpixel.
reed@google.come8fab012011-07-13 15:25:33 +00002239 rec->setHinting(h);
reed@google.comda440672011-07-13 18:02:28 +00002240
reed@google.com9181aa82011-08-05 14:28:31 +00002241// turn this off since GDI might turn A8 into BW! Need a bigger fix.
2242#if 0
reed@google.comda440672011-07-13 18:02:28 +00002243 // Disable LCD when rotated, since GDI's output is ugly
2244 if (isLCD(*rec) && !isAxisAligned(*rec)) {
2245 rec->fMaskFormat = SkMask::kA8_Format;
2246 }
reed@google.com9181aa82011-08-05 14:28:31 +00002247#endif
reed@google.com754e4eb2011-09-26 13:21:39 +00002248
reed@google.com0da48612013-03-19 16:06:52 +00002249 if (!fCanBeLCD && isLCD(*rec)) {
bungeman@google.comcb1bbb32012-10-12 18:48:35 +00002250 rec->fMaskFormat = SkMask::kA8_Format;
2251 rec->fFlags &= ~SkScalerContext::kGenA8FromLCD_Flag;
reed@google.com754e4eb2011-09-26 13:21:39 +00002252 }
reed@google.com754e4eb2011-09-26 13:21:39 +00002253}
reed@google.com070da5e2013-03-27 20:01:49 +00002254
2255///////////////////////////////////////////////////////////////////////////////
2256
2257#include "SkFontMgr.h"
reed@google.com484f5bc2013-04-24 19:14:56 +00002258#include "SkDataTable.h"
2259
bungeman@google.coma9802692013-08-07 02:45:25 +00002260static bool valid_logfont_for_enum(const LOGFONT& lf) {
2261 // TODO: Vector FON is unsupported and should not be listed.
2262 return
2263 // Ignore implicit vertical variants.
2264 lf.lfFaceName[0] && lf.lfFaceName[0] != '@'
2265
2266 // DEFAULT_CHARSET is used to get all fonts, but also implies all
2267 // character sets. Filter assuming all fonts support ANSI_CHARSET.
2268 && ANSI_CHARSET == lf.lfCharSet
2269 ;
reed@google.com484f5bc2013-04-24 19:14:56 +00002270}
2271
bungeman@google.coma9802692013-08-07 02:45:25 +00002272/** An EnumFontFamExProc implementation which interprets builderParam as
2273 * an SkTDArray<ENUMLOGFONTEX>* and appends logfonts which
2274 * pass the valid_logfont_for_enum predicate.
2275 */
2276static int CALLBACK enum_family_proc(const LOGFONT* lf, const TEXTMETRIC*,
2277 DWORD fontType, LPARAM builderParam) {
2278 if (valid_logfont_for_enum(*lf)) {
reed@google.coma65a6812013-05-02 19:47:24 +00002279 SkTDArray<ENUMLOGFONTEX>* array = (SkTDArray<ENUMLOGFONTEX>*)builderParam;
2280 *array->append() = *(ENUMLOGFONTEX*)lf;
reed@google.com484f5bc2013-04-24 19:14:56 +00002281 }
2282 return 1; // non-zero means continue
2283}
2284
reed@google.com484f5bc2013-04-24 19:14:56 +00002285class SkFontStyleSetGDI : public SkFontStyleSet {
2286public:
reed@google.coma65a6812013-05-02 19:47:24 +00002287 SkFontStyleSetGDI(const TCHAR familyName[]) {
bungeman@google.coma9802692013-08-07 02:45:25 +00002288 LOGFONT lf;
2289 sk_bzero(&lf, sizeof(lf));
2290 lf.lfCharSet = DEFAULT_CHARSET;
2291 _tcscpy_s(lf.lfFaceName, familyName);
2292
halcanary96fcdcc2015-08-27 07:41:13 -07002293 HDC hdc = ::CreateCompatibleDC(nullptr);
bungeman@google.coma9802692013-08-07 02:45:25 +00002294 ::EnumFontFamiliesEx(hdc, &lf, enum_family_proc, (LPARAM)&fArray, 0);
reed@google.com484f5bc2013-04-24 19:14:56 +00002295 ::DeleteDC(hdc);
2296 }
2297
mtklein36352bf2015-03-25 18:17:31 -07002298 int count() override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002299 return fArray.count();
2300 }
2301
mtklein36352bf2015-03-25 18:17:31 -07002302 void getStyle(int index, SkFontStyle* fs, SkString* styleName) override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002303 if (fs) {
bungemana4c4a2d2014-10-20 13:33:19 -07002304 *fs = get_style(fArray[index].elfLogFont);
reed@google.com484f5bc2013-04-24 19:14:56 +00002305 }
reed@google.coma65a6812013-05-02 19:47:24 +00002306 if (styleName) {
2307 const ENUMLOGFONTEX& ref = fArray[index];
2308 // For some reason, ENUMLOGFONTEX and LOGFONT disagree on their type in the
2309 // non-unicode version.
2310 // ENUMLOGFONTEX uses BYTE
2311 // LOGFONT uses CHAR
2312 // Here we assert they that the style name is logically the same (size) as
2313 // a TCHAR, so we can use the same converter function.
2314 SkASSERT(sizeof(TCHAR) == sizeof(ref.elfStyle[0]));
2315 tchar_to_skstring((const TCHAR*)ref.elfStyle, styleName);
2316 }
reed@google.com484f5bc2013-04-24 19:14:56 +00002317 }
2318
mtklein36352bf2015-03-25 18:17:31 -07002319 SkTypeface* createTypeface(int index) override {
reed@google.coma65a6812013-05-02 19:47:24 +00002320 return SkCreateTypefaceFromLOGFONT(fArray[index].elfLogFont);
reed@google.com484f5bc2013-04-24 19:14:56 +00002321 }
2322
mtklein36352bf2015-03-25 18:17:31 -07002323 SkTypeface* matchStyle(const SkFontStyle& pattern) override {
bungeman147ea2f2015-11-12 09:50:08 -08002324 return this->matchStyleCSS3(pattern);
reed@google.com484f5bc2013-04-24 19:14:56 +00002325 }
2326
2327private:
reed@google.coma65a6812013-05-02 19:47:24 +00002328 SkTDArray<ENUMLOGFONTEX> fArray;
reed@google.com484f5bc2013-04-24 19:14:56 +00002329};
2330
reed@google.com484f5bc2013-04-24 19:14:56 +00002331class SkFontMgrGDI : public SkFontMgr {
bungeman@google.coma9802692013-08-07 02:45:25 +00002332public:
2333 SkFontMgrGDI() {
reed@google.com484f5bc2013-04-24 19:14:56 +00002334 LOGFONT lf;
2335 sk_bzero(&lf, sizeof(lf));
2336 lf.lfCharSet = DEFAULT_CHARSET;
2337
halcanary96fcdcc2015-08-27 07:41:13 -07002338 HDC hdc = ::CreateCompatibleDC(nullptr);
reed@google.com484f5bc2013-04-24 19:14:56 +00002339 ::EnumFontFamiliesEx(hdc, &lf, enum_family_proc, (LPARAM)&fLogFontArray, 0);
2340 ::DeleteDC(hdc);
2341 }
2342
reed@google.com484f5bc2013-04-24 19:14:56 +00002343protected:
mtklein36352bf2015-03-25 18:17:31 -07002344 int onCountFamilies() const override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002345 return fLogFontArray.count();
2346 }
2347
mtklein36352bf2015-03-25 18:17:31 -07002348 void onGetFamilyName(int index, SkString* familyName) const override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002349 SkASSERT((unsigned)index < (unsigned)fLogFontArray.count());
reed@google.coma65a6812013-05-02 19:47:24 +00002350 tchar_to_skstring(fLogFontArray[index].elfLogFont.lfFaceName, familyName);
reed@google.com484f5bc2013-04-24 19:14:56 +00002351 }
2352
mtklein36352bf2015-03-25 18:17:31 -07002353 SkFontStyleSet* onCreateStyleSet(int index) const override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002354 SkASSERT((unsigned)index < (unsigned)fLogFontArray.count());
halcanary385fe4d2015-08-26 13:07:48 -07002355 return new SkFontStyleSetGDI(fLogFontArray[index].elfLogFont.lfFaceName);
reed@google.com484f5bc2013-04-24 19:14:56 +00002356 }
2357
mtklein36352bf2015-03-25 18:17:31 -07002358 SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
halcanary96fcdcc2015-08-27 07:41:13 -07002359 if (nullptr == familyName) {
reed@google.com484f5bc2013-04-24 19:14:56 +00002360 familyName = ""; // do we need this check???
2361 }
2362 LOGFONT lf;
2363 logfont_for_name(familyName, &lf);
halcanary385fe4d2015-08-26 13:07:48 -07002364 return new SkFontStyleSetGDI(lf.lfFaceName);
reed@google.com484f5bc2013-04-24 19:14:56 +00002365 }
2366
reed@google.com484f5bc2013-04-24 19:14:56 +00002367 virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
mtklein36352bf2015-03-25 18:17:31 -07002368 const SkFontStyle& fontstyle) const override {
reed@google.com437eea12013-04-25 20:40:02 +00002369 // could be in base impl
Hal Canary67b39de2016-11-07 11:47:44 -05002370 sk_sp<SkFontStyleSet> sset(this->matchFamily(familyName));
reed@google.com484f5bc2013-04-24 19:14:56 +00002371 return sset->matchStyle(fontstyle);
2372 }
2373
djsollen33068c12014-11-14 10:52:53 -08002374 virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
2375 const char* bcp47[], int bcp47Count,
mtklein36352bf2015-03-25 18:17:31 -07002376 SkUnichar character) const override {
halcanary96fcdcc2015-08-27 07:41:13 -07002377 return nullptr;
djsollen33068c12014-11-14 10:52:53 -08002378 }
2379
reed@google.com484f5bc2013-04-24 19:14:56 +00002380 virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
mtklein36352bf2015-03-25 18:17:31 -07002381 const SkFontStyle& fontstyle) const override {
reed@google.com437eea12013-04-25 20:40:02 +00002382 // could be in base impl
reed@google.com484f5bc2013-04-24 19:14:56 +00002383 SkString familyName;
2384 ((LogFontTypeface*)familyMember)->getFamilyName(&familyName);
2385 return this->matchFamilyStyle(familyName.c_str(), fontstyle);
2386 }
2387
Mike Reed59227392017-09-26 09:46:08 -04002388 sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,
2389 int ttcIndex) const override {
Ben Wagnerfc497342017-02-24 11:15:26 -05002390 if (ttcIndex != 0) {
2391 return nullptr;
2392 }
Mike Reed59227392017-09-26 09:46:08 -04002393 return create_from_stream(std::move(stream));
reed@google.com484f5bc2013-04-24 19:14:56 +00002394 }
reed@google.com437eea12013-04-25 20:40:02 +00002395
Mike Reed59227392017-09-26 09:46:08 -04002396 sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData> data, int ttcIndex) const override {
reed@google.com437eea12013-04-25 20:40:02 +00002397 // could be in base impl
Mike Reed59227392017-09-26 09:46:08 -04002398 return this->makeFromStream(std::unique_ptr<SkStreamAsset>(new SkMemoryStream(std::move(data))),
2399 ttcIndex);
reed@google.com484f5bc2013-04-24 19:14:56 +00002400 }
reed@google.com437eea12013-04-25 20:40:02 +00002401
Mike Reed59227392017-09-26 09:46:08 -04002402 sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override {
reed@google.com437eea12013-04-25 20:40:02 +00002403 // could be in base impl
Mike Reed59227392017-09-26 09:46:08 -04002404 auto stream = SkStream::MakeFromFile(path);
2405 return stream ? this->makeFromStream(std::move(stream), ttcIndex) : nullptr;
reed@google.com484f5bc2013-04-24 19:14:56 +00002406 }
2407
Mike Reed59227392017-09-26 09:46:08 -04002408 sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const override {
bungeman@google.comf7159bb2013-11-20 15:11:05 +00002409 LOGFONT lf;
halcanary96fcdcc2015-08-27 07:41:13 -07002410 if (nullptr == familyName) {
bungeman@google.comf7159bb2013-11-20 15:11:05 +00002411 lf = get_default_font();
2412 } else {
2413 logfont_for_name(familyName, &lf);
2414 }
bungemana4c4a2d2014-10-20 13:33:19 -07002415
bungeman11a77c62016-04-12 13:45:06 -07002416 lf.lfWeight = style.weight();
bungemanb4bb7d82016-04-27 10:21:04 -07002417 lf.lfItalic = style.slant() == SkFontStyle::kUpright_Slant ? FALSE : TRUE;
Mike Reed59227392017-09-26 09:46:08 -04002418 return sk_sp<SkTypeface>(SkCreateTypefaceFromLOGFONT(lf));
reed@google.com30ddd612013-07-30 17:47:39 +00002419 }
2420
reed@google.com484f5bc2013-04-24 19:14:56 +00002421private:
reed@google.coma65a6812013-05-02 19:47:24 +00002422 SkTDArray<ENUMLOGFONTEX> fLogFontArray;
reed@google.com484f5bc2013-04-24 19:14:56 +00002423};
reed@google.com070da5e2013-03-27 20:01:49 +00002424
reed@google.com30ddd612013-07-30 17:47:39 +00002425///////////////////////////////////////////////////////////////////////////////
2426
Ben Wagner3546ff12017-01-03 13:32:36 -05002427sk_sp<SkFontMgr> SkFontMgr_New_GDI() { return sk_make_sp<SkFontMgrGDI>(); }
mtklein1ee76512015-11-02 10:20:27 -08002428
Mike Klein8f11d4d2018-01-24 12:42:55 -05002429#endif//defined(SK_BUILD_FOR_WIN)