blob: f1da8053f21d222012f2edb4b1e310ff654fe477 [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"
bungeman@google.combbe50132012-07-24 20:33:21 +000017#include "SkGlyph.h"
bungeman@google.com27f74aa2013-10-08 21:32:15 +000018#include "SkHRESULT.h"
bungeman7cfd46a2016-10-20 16:06:52 -040019#include "SkMakeUnique.h"
bungeman@google.com97efada2012-07-30 20:40:50 +000020#include "SkMaskGamma.h"
bungeman@google.comd3fbd342014-04-15 15:52:07 +000021#include "SkMatrix22.h"
Mike Klein0341f162017-10-18 09:58:54 -040022#include "SkOnce.h"
Hal Canarya865d252017-11-09 16:16:09 -050023#include "SkOTTable_OS_2.h"
bungeman@google.com7bdd6142013-07-15 19:42:57 +000024#include "SkOTTable_maxp.h"
bungeman@google.coma9802692013-08-07 02:45:25 +000025#include "SkOTTable_name.h"
bungeman@google.coma5501992012-05-18 19:06:41 +000026#include "SkOTUtils.h"
reed@google.com27889872012-08-07 16:15:13 +000027#include "SkPath.h"
bungeman@google.comb10b51f2013-08-01 20:18:41 +000028#include "SkSFNTHeader.h"
reed@google.comac6b9792011-03-11 15:42:51 +000029#include "SkStream.h"
bungeman@google.coma5501992012-05-18 19:06:41 +000030#include "SkString.h"
bungeman@google.com05a729f2013-06-20 15:29:16 +000031#include "SkTemplates.h"
reed@google.comac6b9792011-03-11 15:42:51 +000032#include "SkTypeface_win.h"
reed@google.com59d2f632011-05-02 19:36:59 +000033#include "SkTypefaceCache.h"
reed@google.comac6b9792011-03-11 15:42:51 +000034#include "SkUtils.h"
35
bungeman@google.coma5501992012-05-18 19:06:41 +000036#include "SkTypes.h"
37#include <tchar.h>
38#include <usp10.h>
39#include <objbase.h>
reed@google.comac6b9792011-03-11 15:42:51 +000040
reed@google.comf210f502013-03-20 14:15:52 +000041static void (*gEnsureLOGFONTAccessibleProc)(const LOGFONT&);
42
43void SkTypeface_SetEnsureLOGFONTAccessibleProc(void (*proc)(const LOGFONT&)) {
44 gEnsureLOGFONTAccessibleProc = proc;
45}
46
reed@google.com055180c2013-03-21 18:46:35 +000047static void call_ensure_accessible(const LOGFONT& lf) {
48 if (gEnsureLOGFONTAccessibleProc) {
49 gEnsureLOGFONTAccessibleProc(lf);
50 }
51}
52
53///////////////////////////////////////////////////////////////////////////////
54
reed@google.com6f5df482011-09-28 20:33:24 +000055// always packed xxRRGGBB
56typedef uint32_t SkGdiRGB;
57
reed@google.coma767fa02011-08-05 21:40:26 +000058// define this in your Makefile or .gyp to enforce AA requests
59// which GDI ignores at small sizes. This flag guarantees AA
60// for rotated text, regardless of GDI's notions.
61//#define SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
62
Ben Wagner3e45a2f2017-11-01 11:47:39 -040063static bool isLCD(const SkScalerContextRec& rec) {
reedd54d3fc2014-11-13 14:39:58 -080064 return SkMask::kLCD16_Format == rec.fMaskFormat;
reed@google.com82a34d82011-07-26 19:33:08 +000065}
66
reed@google.coma767fa02011-08-05 21:40:26 +000067static bool bothZero(SkScalar a, SkScalar b) {
68 return 0 == a && 0 == b;
69}
70
71// returns false if there is any non-90-rotation or skew
Ben Wagner3e45a2f2017-11-01 11:47:39 -040072static bool isAxisAligned(const SkScalerContextRec& rec) {
reed@google.coma767fa02011-08-05 21:40:26 +000073 return 0 == rec.fPreSkewX &&
74 (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
75 bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
76}
77
Ben Wagner3e45a2f2017-11-01 11:47:39 -040078static bool needToRenderWithSkia(const SkScalerContextRec& rec) {
reed@google.coma767fa02011-08-05 21:40:26 +000079#ifdef SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
80 // What we really want to catch is when GDI will ignore the AA request and give
81 // us BW instead. Smallish rotated text is one heuristic, so this code is just
82 // an approximation. We shouldn't need to do this for larger sizes, but at those
83 // sizes, the quality difference gets less and less between our general
84 // scanconverter and GDI's.
85 if (SkMask::kA8_Format == rec.fMaskFormat && !isAxisAligned(rec)) {
86 return true;
87 }
88#endif
bungeman@google.com0abbff92013-07-27 20:37:56 +000089 return rec.getHinting() == SkPaint::kNo_Hinting || rec.getHinting() == SkPaint::kSlight_Hinting;
reed@google.coma767fa02011-08-05 21:40:26 +000090}
91
reed@google.coma65a6812013-05-02 19:47:24 +000092static void tchar_to_skstring(const TCHAR t[], SkString* s) {
reed@google.com484f5bc2013-04-24 19:14:56 +000093#ifdef UNICODE
halcanary96fcdcc2015-08-27 07:41:13 -070094 size_t sSize = WideCharToMultiByte(CP_UTF8, 0, t, -1, nullptr, 0, nullptr, nullptr);
reed@google.com484f5bc2013-04-24 19:14:56 +000095 s->resize(sSize);
halcanary96fcdcc2015-08-27 07:41:13 -070096 WideCharToMultiByte(CP_UTF8, 0, t, -1, s->writable_str(), sSize, nullptr, nullptr);
reed@google.com484f5bc2013-04-24 19:14:56 +000097#else
98 s->set(t);
99#endif
100}
101
bungeman@google.coma9802692013-08-07 02:45:25 +0000102static void dcfontname_to_skstring(HDC deviceContext, const LOGFONT& lf, SkString* familyName) {
103 int fontNameLen; //length of fontName in TCHARS.
halcanary96fcdcc2015-08-27 07:41:13 -0700104 if (0 == (fontNameLen = GetTextFace(deviceContext, 0, nullptr))) {
bungeman@google.coma9802692013-08-07 02:45:25 +0000105 call_ensure_accessible(lf);
halcanary96fcdcc2015-08-27 07:41:13 -0700106 if (0 == (fontNameLen = GetTextFace(deviceContext, 0, nullptr))) {
bungeman@google.coma9802692013-08-07 02:45:25 +0000107 fontNameLen = 0;
108 }
109 }
110
111 SkAutoSTArray<LF_FULLFACESIZE, TCHAR> fontName(fontNameLen+1);
112 if (0 == GetTextFace(deviceContext, fontNameLen, fontName.get())) {
113 call_ensure_accessible(lf);
114 if (0 == GetTextFace(deviceContext, fontNameLen, fontName.get())) {
115 fontName[0] = 0;
116 }
117 }
118
119 tchar_to_skstring(fontName.get(), familyName);
120}
121
reed@google.comac6b9792011-03-11 15:42:51 +0000122static void make_canonical(LOGFONT* lf) {
bungeman@google.com53cbb0b2013-09-08 19:36:58 +0000123 lf->lfHeight = -64;
bungeman59f093d2016-03-22 10:59:09 -0700124 lf->lfWidth = 0; // lfWidth is related to lfHeight, not to the OS/2::usWidthClass.
reed@google.com59d2f632011-05-02 19:36:59 +0000125 lf->lfQuality = CLEARTYPE_QUALITY;//PROOF_QUALITY;
126 lf->lfCharSet = DEFAULT_CHARSET;
reed@google.com82a34d82011-07-26 19:33:08 +0000127// lf->lfClipPrecision = 64;
reed@google.com59d2f632011-05-02 19:36:59 +0000128}
129
bungemana4c4a2d2014-10-20 13:33:19 -0700130static SkFontStyle get_style(const LOGFONT& lf) {
131 return SkFontStyle(lf.lfWeight,
bungeman59f093d2016-03-22 10:59:09 -0700132 SkFontStyle::kNormal_Width,
bungemana4c4a2d2014-10-20 13:33:19 -0700133 lf.lfItalic ? SkFontStyle::kItalic_Slant : SkFontStyle::kUpright_Slant);
reed@google.comac6b9792011-03-11 15:42:51 +0000134}
135
136static inline FIXED SkFixedToFIXED(SkFixed x) {
137 return *(FIXED*)(&x);
138}
bungeman@google.coma0319f62012-04-18 15:40:50 +0000139static inline SkFixed SkFIXEDToFixed(FIXED x) {
140 return *(SkFixed*)(&x);
141}
reed@google.comac6b9792011-03-11 15:42:51 +0000142
143static inline FIXED SkScalarToFIXED(SkScalar x) {
144 return SkFixedToFIXED(SkScalarToFixed(x));
145}
146
bungeman@google.com4732df62014-01-23 15:22:42 +0000147static inline SkScalar SkFIXEDToScalar(FIXED x) {
148 return SkFixedToScalar(SkFIXEDToFixed(x));
149}
150
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000151static unsigned calculateGlyphCount(HDC hdc, const LOGFONT& lf) {
152 TEXTMETRIC textMetric;
153 if (0 == GetTextMetrics(hdc, &textMetric)) {
154 textMetric.tmPitchAndFamily = TMPF_VECTOR;
155 call_ensure_accessible(lf);
156 GetTextMetrics(hdc, &textMetric);
157 }
158
159 if (!(textMetric.tmPitchAndFamily & TMPF_VECTOR)) {
160 return textMetric.tmLastChar;
161 }
162
reed@google.comac6b9792011-03-11 15:42:51 +0000163 // The 'maxp' table stores the number of glyphs at offset 4, in 2 bytes.
reed@google.comac6b9792011-03-11 15:42:51 +0000164 uint16_t glyphs;
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000165 if (GDI_ERROR != GetFontData(hdc, SkOTTableMaximumProfile::TAG, 4, &glyphs, sizeof(glyphs))) {
reed@google.comac6b9792011-03-11 15:42:51 +0000166 return SkEndian_SwapBE16(glyphs);
167 }
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000168
reed@google.comac6b9792011-03-11 15:42:51 +0000169 // Binary search for glyph count.
170 static const MAT2 mat2 = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
171 int32_t max = SK_MaxU16 + 1;
172 int32_t min = 0;
173 GLYPHMETRICS gm;
174 while (min < max) {
175 int32_t mid = min + ((max - min) / 2);
176 if (GetGlyphOutlineW(hdc, mid, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0,
halcanary96fcdcc2015-08-27 07:41:13 -0700177 nullptr, &mat2) == GDI_ERROR) {
reed@google.comac6b9792011-03-11 15:42:51 +0000178 max = mid;
179 } else {
180 min = mid + 1;
181 }
182 }
183 SkASSERT(min == max);
184 return min;
185}
186
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000187static unsigned calculateUPEM(HDC hdc, const LOGFONT& lf) {
188 TEXTMETRIC textMetric;
189 if (0 == GetTextMetrics(hdc, &textMetric)) {
190 textMetric.tmPitchAndFamily = TMPF_VECTOR;
191 call_ensure_accessible(lf);
192 GetTextMetrics(hdc, &textMetric);
193 }
194
195 if (!(textMetric.tmPitchAndFamily & TMPF_VECTOR)) {
196 return textMetric.tmMaxCharWidth;
197 }
198
199 OUTLINETEXTMETRIC otm;
200 unsigned int otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
201 if (0 == otmRet) {
202 call_ensure_accessible(lf);
203 otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
204 }
205
206 return (0 == otmRet) ? 0 : otm.otmEMSquare;
207}
208
reed@google.comac6b9792011-03-11 15:42:51 +0000209class LogFontTypeface : public SkTypeface {
reed@google.comac6b9792011-03-11 15:42:51 +0000210public:
bungemana4c4a2d2014-10-20 13:33:19 -0700211 LogFontTypeface(const SkFontStyle& style, const LOGFONT& lf, bool serializeAsStream)
bungemane3aea102016-07-13 05:16:58 -0700212 : SkTypeface(style, false)
bungemana4c4a2d2014-10-20 13:33:19 -0700213 , fLogFont(lf)
214 , fSerializeAsStream(serializeAsStream)
215 {
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000216 HFONT font = CreateFontIndirect(&lf);
217
halcanary96fcdcc2015-08-27 07:41:13 -0700218 HDC deviceContext = ::CreateCompatibleDC(nullptr);
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000219 HFONT savefont = (HFONT)SelectObject(deviceContext, font);
220
221 TEXTMETRIC textMetric;
222 if (0 == GetTextMetrics(deviceContext, &textMetric)) {
reed@google.com055180c2013-03-21 18:46:35 +0000223 call_ensure_accessible(lf);
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000224 if (0 == GetTextMetrics(deviceContext, &textMetric)) {
225 textMetric.tmPitchAndFamily = TMPF_TRUETYPE;
226 }
227 }
228 if (deviceContext) {
229 ::SelectObject(deviceContext, savefont);
230 ::DeleteDC(deviceContext);
231 }
232 if (font) {
233 ::DeleteObject(font);
234 }
235
bungeman@google.comfe747652013-03-25 19:36:11 +0000236 // The fixed pitch bit is set if the font is *not* fixed pitch.
237 this->setIsFixedPitch((textMetric.tmPitchAndFamily & TMPF_FIXED_PITCH) == 0);
bungeman6e45bda2016-07-25 15:11:49 -0700238 this->setFontStyle(SkFontStyle(textMetric.tmWeight, style.width(), style.slant()));
bungeman@google.comfe747652013-03-25 19:36:11 +0000239
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000240 // Used a logfont on a memory context, should never get a device font.
241 // Therefore all TMPF_DEVICE will be PostScript (cubic) fonts.
bungeman6e45bda2016-07-25 15:11:49 -0700242 // If the font has cubic outlines, it will not be rendered with ClearType.
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000243 fCanBeLCD = !((textMetric.tmPitchAndFamily & TMPF_VECTOR) &&
244 (textMetric.tmPitchAndFamily & TMPF_DEVICE));
245 }
reed@google.comac6b9792011-03-11 15:42:51 +0000246
reed@google.com59d2f632011-05-02 19:36:59 +0000247 LOGFONT fLogFont;
bungeman@google.come70f7982012-06-01 19:38:19 +0000248 bool fSerializeAsStream;
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000249 bool fCanBeLCD;
reed@google.comac6b9792011-03-11 15:42:51 +0000250
reed@google.com59d2f632011-05-02 19:36:59 +0000251 static LogFontTypeface* Create(const LOGFONT& lf) {
bungemana4c4a2d2014-10-20 13:33:19 -0700252 return new LogFontTypeface(get_style(lf), lf, false);
reed@google.comac6b9792011-03-11 15:42:51 +0000253 }
reed@google.com0da48612013-03-19 16:06:52 +0000254
reed@google.com055180c2013-03-21 18:46:35 +0000255 static void EnsureAccessible(const SkTypeface* face) {
256 call_ensure_accessible(static_cast<const LogFontTypeface*>(face)->fLogFont);
257 }
258
reed@google.com0da48612013-03-19 16:06:52 +0000259protected:
mtklein36352bf2015-03-25 18:17:31 -0700260 SkStreamAsset* onOpenStream(int* ttcIndex) const override;
reeda9322c22016-04-12 06:47:05 -0700261 SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
262 const SkDescriptor*) const override;
mtklein36352bf2015-03-25 18:17:31 -0700263 void onFilterRec(SkScalerContextRec*) const override;
Hal Canary46cc3da2018-05-09 11:50:34 -0400264 void getGlyphToUnicodeMap(SkUnichar*) const override;
Hal Canary209e4b12017-05-04 14:23:55 -0400265 std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
mtklein36352bf2015-03-25 18:17:31 -0700266 void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
Ben Wagnerfc497342017-02-24 11:15:26 -0500267 int onCharsToGlyphs(const void* chars, Encoding encoding,
268 uint16_t glyphs[], int glyphCount) const override;
mtklein36352bf2015-03-25 18:17:31 -0700269 int onCountGlyphs() const override;
270 int onGetUPEM() const override;
271 void onGetFamilyName(SkString* familyName) const override;
272 SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
Ben Wagnerfc497342017-02-24 11:15:26 -0500273 int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],
274 int coordinateCount) const override
275 {
276 return -1;
277 }
mtklein36352bf2015-03-25 18:17:31 -0700278 int onGetTableTags(SkFontTableTag tags[]) const override;
Ben Wagnerfc497342017-02-24 11:15:26 -0500279 size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override;
reed@google.comac6b9792011-03-11 15:42:51 +0000280};
281
bungeman@google.coma5501992012-05-18 19:06:41 +0000282class FontMemResourceTypeface : public LogFontTypeface {
283public:
284 /**
bungeman@google.coma5501992012-05-18 19:06:41 +0000285 * The created FontMemResourceTypeface takes ownership of fontMemResource.
286 */
287 static FontMemResourceTypeface* Create(const LOGFONT& lf, HANDLE fontMemResource) {
bungemana4c4a2d2014-10-20 13:33:19 -0700288 return new FontMemResourceTypeface(get_style(lf), lf, fontMemResource);
bungeman@google.coma5501992012-05-18 19:06:41 +0000289 }
290
291protected:
mtklein36352bf2015-03-25 18:17:31 -0700292 void weak_dispose() const override {
bungeman@google.coma5501992012-05-18 19:06:41 +0000293 RemoveFontMemResourceEx(fFontMemResource);
294 //SkTypefaceCache::Remove(this);
295 INHERITED::weak_dispose();
296 }
297
298private:
bungeman@google.com72cf4fc2014-03-21 22:48:32 +0000299 /**
300 * Takes ownership of fontMemResource.
301 */
bungemana4c4a2d2014-10-20 13:33:19 -0700302 FontMemResourceTypeface(const SkFontStyle& style, const LOGFONT& lf, HANDLE fontMemResource)
303 : LogFontTypeface(style, lf, true), fFontMemResource(fontMemResource)
304 { }
bungeman@google.com72cf4fc2014-03-21 22:48:32 +0000305
306 HANDLE fFontMemResource;
307
bungeman@google.coma5501992012-05-18 19:06:41 +0000308 typedef LogFontTypeface INHERITED;
309};
310
reed@google.comac6b9792011-03-11 15:42:51 +0000311static const LOGFONT& get_default_font() {
312 static LOGFONT gDefaultFont;
reed@google.comac6b9792011-03-11 15:42:51 +0000313 return gDefaultFont;
314}
315
bungeman82a455f2016-04-14 08:04:45 -0700316static bool FindByLogFont(SkTypeface* face, void* ctx) {
bungeman@google.coma5501992012-05-18 19:06:41 +0000317 LogFontTypeface* lface = static_cast<LogFontTypeface*>(face);
reed@google.com59d2f632011-05-02 19:36:59 +0000318 const LOGFONT* lf = reinterpret_cast<const LOGFONT*>(ctx);
reed@google.comac6b9792011-03-11 15:42:51 +0000319
bungeman82a455f2016-04-14 08:04:45 -0700320 return !memcmp(&lface->fLogFont, lf, sizeof(LOGFONT));
reed@google.com59d2f632011-05-02 19:36:59 +0000321}
322
323/**
324 * This guy is public. It first searches the cache, and if a match is not found,
325 * it creates a new face.
326 */
327SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& origLF) {
328 LOGFONT lf = origLF;
329 make_canonical(&lf);
bungeman@google.comee51d1a2012-02-16 12:40:48 +0000330 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByLogFont, &lf);
halcanary96fcdcc2015-08-27 07:41:13 -0700331 if (nullptr == face) {
reed@google.com59d2f632011-05-02 19:36:59 +0000332 face = LogFontTypeface::Create(lf);
bungeman82a455f2016-04-14 08:04:45 -0700333 SkTypefaceCache::Add(face);
reed@google.com59d2f632011-05-02 19:36:59 +0000334 }
335 return face;
reed@google.comac6b9792011-03-11 15:42:51 +0000336}
337
reed@google.comdb77a6a2011-07-19 19:08:33 +0000338/**
bungeman@google.coma5501992012-05-18 19:06:41 +0000339 * The created SkTypeface takes ownership of fontMemResource.
340 */
341SkTypeface* SkCreateFontMemResourceTypefaceFromLOGFONT(const LOGFONT& origLF, HANDLE fontMemResource) {
342 LOGFONT lf = origLF;
343 make_canonical(&lf);
mtklein60b6e9d2014-10-24 10:43:15 -0700344 // We'll never get a cache hit, so no point in putting this in SkTypefaceCache.
345 return FontMemResourceTypeface::Create(lf, fontMemResource);
bungeman@google.coma5501992012-05-18 19:06:41 +0000346}
347
348/**
reed@google.comdb77a6a2011-07-19 19:08:33 +0000349 * This guy is public
350 */
351void SkLOGFONTFromTypeface(const SkTypeface* face, LOGFONT* lf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700352 if (nullptr == face) {
reed@google.comdb77a6a2011-07-19 19:08:33 +0000353 *lf = get_default_font();
354 } else {
bungeman@google.coma5501992012-05-18 19:06:41 +0000355 *lf = static_cast<const LogFontTypeface*>(face)->fLogFont;
reed@google.comdb77a6a2011-07-19 19:08:33 +0000356 }
357}
358
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000359// Construct Glyph to Unicode table.
360// Unicode code points that require conjugate pairs in utf16 are not
361// supported.
362// TODO(arthurhsu): Add support for conjugate pairs. It looks like that may
363// require parsing the TTF cmap table (platform 4, encoding 12) directly instead
364// of calling GetFontUnicodeRange().
365static void populate_glyph_to_unicode(HDC fontHdc, const unsigned glyphCount,
Hal Canary46cc3da2018-05-09 11:50:34 -0400366 SkUnichar* glyphToUnicode) {
367 sk_bzero(glyphToUnicode, sizeof(SkUnichar) * glyphCount);
halcanary96fcdcc2015-08-27 07:41:13 -0700368 DWORD glyphSetBufferSize = GetFontUnicodeRanges(fontHdc, nullptr);
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000369 if (!glyphSetBufferSize) {
370 return;
371 }
372
Ben Wagner7ecc5962016-11-02 17:07:33 -0400373 std::unique_ptr<BYTE[]> glyphSetBuffer(new BYTE[glyphSetBufferSize]);
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000374 GLYPHSET* glyphSet =
375 reinterpret_cast<LPGLYPHSET>(glyphSetBuffer.get());
376 if (GetFontUnicodeRanges(fontHdc, glyphSet) != glyphSetBufferSize) {
377 return;
378 }
379
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000380 for (DWORD i = 0; i < glyphSet->cRanges; ++i) {
381 // There is no guarantee that within a Unicode range, the corresponding
382 // glyph id in a font file are continuous. So, even if we have ranges,
383 // we can't just use the first and last entry of the range to compute
384 // result. We need to enumerate them one by one.
385 int count = glyphSet->ranges[i].cGlyphs;
386 SkAutoTArray<WCHAR> chars(count + 1);
387 chars[count] = 0; // termintate string
388 SkAutoTArray<WORD> glyph(count);
389 for (USHORT j = 0; j < count; ++j) {
390 chars[j] = glyphSet->ranges[i].wcLow + j;
391 }
392 GetGlyphIndicesW(fontHdc, chars.get(), count, glyph.get(),
393 GGI_MARK_NONEXISTING_GLYPHS);
394 // If the glyph ID is valid, and the glyph is not mapped, then we will
395 // fill in the char id into the vector. If the glyph is mapped already,
396 // skip it.
397 // TODO(arthurhsu): better improve this. e.g. Get all used char ids from
398 // font cache, then generate this mapping table from there. It's
399 // unlikely to have collisions since glyph reuse happens mostly for
400 // different Unicode pages.
401 for (USHORT j = 0; j < count; ++j) {
Hal Canary46cc3da2018-05-09 11:50:34 -0400402 if (glyph[j] != 0xFFFF && glyph[j] < glyphCount && glyphToUnicode[glyph[j]] == 0) {
403 glyphToUnicode[glyph[j]] = chars[j];
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000404 }
405 }
406 }
407}
408
reed@google.com99edd432011-09-09 14:59:59 +0000409//////////////////////////////////////////////////////////////////////////////////////
410
411static int alignTo32(int n) {
412 return (n + 31) & ~31;
413}
414
415struct MyBitmapInfo : public BITMAPINFO {
416 RGBQUAD fMoreSpaceForColors[1];
417};
418
419class HDCOffscreen {
420public:
421 HDCOffscreen() {
422 fFont = 0;
423 fDC = 0;
424 fBM = 0;
halcanary96fcdcc2015-08-27 07:41:13 -0700425 fBits = nullptr;
reed@google.com99edd432011-09-09 14:59:59 +0000426 fWidth = fHeight = 0;
427 fIsBW = false;
428 }
429
430 ~HDCOffscreen() {
431 if (fDC) {
432 DeleteDC(fDC);
433 }
434 if (fBM) {
435 DeleteObject(fBM);
436 }
437 }
438
439 void init(HFONT font, const XFORM& xform) {
440 fFont = font;
441 fXform = xform;
442 }
443
bungeman@google.com97efada2012-07-30 20:40:50 +0000444 const void* draw(const SkGlyph&, bool isBW, size_t* srcRBPtr);
reed@google.com99edd432011-09-09 14:59:59 +0000445
446private:
447 HDC fDC;
448 HBITMAP fBM;
449 HFONT fFont;
450 XFORM fXform;
451 void* fBits; // points into fBM
452 int fWidth;
453 int fHeight;
454 bool fIsBW;
455};
456
reed@google.com754e4eb2011-09-26 13:21:39 +0000457const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW,
bungeman@google.com97efada2012-07-30 20:40:50 +0000458 size_t* srcRBPtr) {
reed@google.com84e22d82013-07-10 15:38:20 +0000459 // Can we share the scalercontext's fDDC, so we don't need to create
460 // a separate fDC here?
reed@google.com99edd432011-09-09 14:59:59 +0000461 if (0 == fDC) {
462 fDC = CreateCompatibleDC(0);
463 if (0 == fDC) {
halcanary96fcdcc2015-08-27 07:41:13 -0700464 return nullptr;
reed@google.com99edd432011-09-09 14:59:59 +0000465 }
466 SetGraphicsMode(fDC, GM_ADVANCED);
467 SetBkMode(fDC, TRANSPARENT);
468 SetTextAlign(fDC, TA_LEFT | TA_BASELINE);
469 SelectObject(fDC, fFont);
bungeman@google.com97efada2012-07-30 20:40:50 +0000470
471 COLORREF color = 0x00FFFFFF;
bsalomon@google.comb58a6392013-03-21 20:29:05 +0000472 SkDEBUGCODE(COLORREF prev =) SetTextColor(fDC, color);
bungeman@google.com97efada2012-07-30 20:40:50 +0000473 SkASSERT(prev != CLR_INVALID);
reed@google.com99edd432011-09-09 14:59:59 +0000474 }
475
476 if (fBM && (fIsBW != isBW || fWidth < glyph.fWidth || fHeight < glyph.fHeight)) {
477 DeleteObject(fBM);
478 fBM = 0;
479 }
reed@google.com754e4eb2011-09-26 13:21:39 +0000480 fIsBW = isBW;
reed@google.com99edd432011-09-09 14:59:59 +0000481
reed@google.com99edd432011-09-09 14:59:59 +0000482 fWidth = SkMax32(fWidth, glyph.fWidth);
483 fHeight = SkMax32(fHeight, glyph.fHeight);
484
485 int biWidth = isBW ? alignTo32(fWidth) : fWidth;
486
487 if (0 == fBM) {
488 MyBitmapInfo info;
489 sk_bzero(&info, sizeof(info));
490 if (isBW) {
491 RGBQUAD blackQuad = { 0, 0, 0, 0 };
492 RGBQUAD whiteQuad = { 0xFF, 0xFF, 0xFF, 0 };
493 info.bmiColors[0] = blackQuad;
494 info.bmiColors[1] = whiteQuad;
495 }
496 info.bmiHeader.biSize = sizeof(info.bmiHeader);
497 info.bmiHeader.biWidth = biWidth;
498 info.bmiHeader.biHeight = fHeight;
499 info.bmiHeader.biPlanes = 1;
500 info.bmiHeader.biBitCount = isBW ? 1 : 32;
501 info.bmiHeader.biCompression = BI_RGB;
502 if (isBW) {
503 info.bmiHeader.biClrUsed = 2;
504 }
505 fBM = CreateDIBSection(fDC, &info, DIB_RGB_COLORS, &fBits, 0, 0);
506 if (0 == fBM) {
halcanary96fcdcc2015-08-27 07:41:13 -0700507 return nullptr;
reed@google.com99edd432011-09-09 14:59:59 +0000508 }
509 SelectObject(fDC, fBM);
510 }
511
512 // erase
513 size_t srcRB = isBW ? (biWidth >> 3) : (fWidth << 2);
514 size_t size = fHeight * srcRB;
bungeman@google.com97efada2012-07-30 20:40:50 +0000515 memset(fBits, 0, size);
reed@google.com99edd432011-09-09 14:59:59 +0000516
517 XFORM xform = fXform;
518 xform.eDx = (float)-glyph.fLeft;
519 xform.eDy = (float)-glyph.fTop;
520 SetWorldTransform(fDC, &xform);
521
522 uint16_t glyphID = glyph.getGlyphID();
halcanary96fcdcc2015-08-27 07:41:13 -0700523 BOOL ret = ExtTextOutW(fDC, 0, 0, ETO_GLYPH_INDEX, nullptr, reinterpret_cast<LPCWSTR>(&glyphID), 1, nullptr);
reed@google.com99edd432011-09-09 14:59:59 +0000524 GdiFlush();
bungeman@google.com39698b12011-11-15 22:26:41 +0000525 if (0 == ret) {
halcanary96fcdcc2015-08-27 07:41:13 -0700526 return nullptr;
bungeman@google.com39698b12011-11-15 22:26:41 +0000527 }
reed@google.com99edd432011-09-09 14:59:59 +0000528 *srcRBPtr = srcRB;
529 // offset to the start of the image
530 return (const char*)fBits + (fHeight - glyph.fHeight) * srcRB;
531}
532
reed@google.comb8a5c612012-06-13 20:01:44 +0000533//////////////////////////////////////////////////////////////////////////////
bungeman@google.com0abbff92013-07-27 20:37:56 +0000534#define BUFFERSIZE (1 << 13)
reed@google.com59d2f632011-05-02 19:36:59 +0000535
reed@google.com30ddd612013-07-30 17:47:39 +0000536class SkScalerContext_GDI : public SkScalerContext {
reed@google.comac6b9792011-03-11 15:42:51 +0000537public:
bungeman7cfd46a2016-10-20 16:06:52 -0400538 SkScalerContext_GDI(sk_sp<LogFontTypeface>,
539 const SkScalerContextEffects&,
540 const SkDescriptor* desc);
Chris Dalton1ef80942017-12-04 12:01:30 -0700541 ~SkScalerContext_GDI() override;
reed@google.comac6b9792011-03-11 15:42:51 +0000542
reed@google.com84e22d82013-07-10 15:38:20 +0000543 // Returns true if the constructor was able to complete all of its
544 // initializations (which may include calling GDI).
545 bool isValid() const;
546
reed@google.comac6b9792011-03-11 15:42:51 +0000547protected:
mtklein36352bf2015-03-25 18:17:31 -0700548 unsigned generateGlyphCount() override;
549 uint16_t generateCharToGlyph(SkUnichar uni) override;
550 void generateAdvance(SkGlyph* glyph) override;
551 void generateMetrics(SkGlyph* glyph) override;
552 void generateImage(const SkGlyph& glyph) override;
Ben Wagner5ddb3082018-03-29 11:18:06 -0400553 bool generatePath(SkGlyphID glyph, SkPath* path) override;
mtklein36352bf2015-03-25 18:17:31 -0700554 void generateFontMetrics(SkPaint::FontMetrics*) override;
reed@google.com99edd432011-09-09 14:59:59 +0000555
reed@google.comac6b9792011-03-11 15:42:51 +0000556private:
Ben Wagner6e9ac122016-11-11 14:31:06 -0500557 DWORD getGDIGlyphPath(SkGlyphID glyph, UINT flags,
bungeman@google.com0abbff92013-07-27 20:37:56 +0000558 SkAutoSTMalloc<BUFFERSIZE, uint8_t>* glyphbuf);
559
reed@google.com99edd432011-09-09 14:59:59 +0000560 HDCOffscreen fOffscreen;
bungeman@google.com0abbff92013-07-27 20:37:56 +0000561 /** fGsA is the non-rotational part of total matrix without the text height scale.
562 * Used to find the magnitude of advances.
563 */
564 MAT2 fGsA;
bungeman@google.com6a774a12013-07-30 01:07:48 +0000565 /** The total matrix without the textSize. */
reed@google.comac6b9792011-03-11 15:42:51 +0000566 MAT2 fMat22;
bungeman@google.com6a774a12013-07-30 01:07:48 +0000567 /** Scales font to EM size. */
568 MAT2 fHighResMat22;
reed@google.comac6b9792011-03-11 15:42:51 +0000569 HDC fDDC;
570 HFONT fSavefont;
571 HFONT fFont;
572 SCRIPT_CACHE fSC;
573 int fGlyphCount;
reed@google.com1dd17a12011-05-17 14:04:41 +0000574
bungeman@google.com6a774a12013-07-30 01:07:48 +0000575 /** The total matrix which also removes EM scale. */
reed@google.com1dd17a12011-05-17 14:04:41 +0000576 SkMatrix fHiResMatrix;
bungeman@google.com0abbff92013-07-27 20:37:56 +0000577 /** fG_inv is the inverse of the rotational part of the total matrix.
578 * Used to set the direction of advances.
579 */
580 SkMatrix fG_inv;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000581 enum Type {
bungeman@google.com4732df62014-01-23 15:22:42 +0000582 kTrueType_Type, kBitmap_Type, kLine_Type
bungeman@google.coma0319f62012-04-18 15:40:50 +0000583 } fType;
584 TEXTMETRIC fTM;
reed@google.comac6b9792011-03-11 15:42:51 +0000585};
586
reed@google.comac6b9792011-03-11 15:42:51 +0000587static FIXED float2FIXED(float x) {
588 return SkFixedToFIXED(SkFloatToFixed(x));
589}
590
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700591static inline float FIXED2float(FIXED x) {
592 return SkFixedToFloat(SkFIXEDToFixed(x));
593}
594
Ben Wagner3e45a2f2017-11-01 11:47:39 -0400595static BYTE compute_quality(const SkScalerContextRec& rec) {
reed@google.com82a34d82011-07-26 19:33:08 +0000596 switch (rec.fMaskFormat) {
597 case SkMask::kBW_Format:
598 return NONANTIALIASED_QUALITY;
599 case SkMask::kLCD16_Format:
reed@google.com82a34d82011-07-26 19:33:08 +0000600 return CLEARTYPE_QUALITY;
601 default:
reed@google.com8351aab2012-01-18 17:06:35 +0000602 if (rec.fFlags & SkScalerContext::kGenA8FromLCD_Flag) {
603 return CLEARTYPE_QUALITY;
604 } else {
605 return ANTIALIASED_QUALITY;
606 }
reed@google.com82a34d82011-07-26 19:33:08 +0000607 }
608}
609
bungeman7cfd46a2016-10-20 16:06:52 -0400610SkScalerContext_GDI::SkScalerContext_GDI(sk_sp<LogFontTypeface> rawTypeface,
reeda9322c22016-04-12 06:47:05 -0700611 const SkScalerContextEffects& effects,
612 const SkDescriptor* desc)
bungeman7cfd46a2016-10-20 16:06:52 -0400613 : SkScalerContext(std::move(rawTypeface), effects, desc)
reed@google.com055180c2013-03-21 18:46:35 +0000614 , fDDC(0)
reed@google.com055180c2013-03-21 18:46:35 +0000615 , fSavefont(0)
reed@google.com84e22d82013-07-10 15:38:20 +0000616 , fFont(0)
reed@google.com055180c2013-03-21 18:46:35 +0000617 , fSC(0)
bungeman@google.com05a729f2013-06-20 15:29:16 +0000618 , fGlyphCount(-1)
619{
bungeman7cfd46a2016-10-20 16:06:52 -0400620 LogFontTypeface* typeface = static_cast<LogFontTypeface*>(this->getTypeface());
reed@google.com055180c2013-03-21 18:46:35 +0000621
halcanary96fcdcc2015-08-27 07:41:13 -0700622 fDDC = ::CreateCompatibleDC(nullptr);
reed@google.com84e22d82013-07-10 15:38:20 +0000623 if (!fDDC) {
624 return;
625 }
reed@google.com1dd17a12011-05-17 14:04:41 +0000626 SetGraphicsMode(fDDC, GM_ADVANCED);
reed@google.comac6b9792011-03-11 15:42:51 +0000627 SetBkMode(fDDC, TRANSPARENT);
skia.committer@gmail.com27e21fe2013-07-28 07:01:03 +0000628
bungeman6f940762015-03-18 08:25:43 -0700629 // When GDI hinting, remove the entire Y scale from sA and GsA. (Prevents 'linear' metrics.)
630 // When not hinting, remove only the integer Y scale from sA and GsA. (Applied by GDI.)
bungeman5f14c5e2014-12-05 12:26:44 -0800631 SkScalerContextRec::PreMatrixScale scaleConstraints =
632 (fRec.getHinting() == SkPaint::kNo_Hinting || fRec.getHinting() == SkPaint::kSlight_Hinting)
633 ? SkScalerContextRec::kVerticalInteger_PreMatrixScale
634 : SkScalerContextRec::kVertical_PreMatrixScale;
635 SkVector scale;
636 SkMatrix sA;
637 SkMatrix GsA;
638 SkMatrix A;
639 fRec.computeMatrices(scaleConstraints, &scale, &sA, &GsA, &fG_inv, &A);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000640
641 fGsA.eM11 = SkScalarToFIXED(GsA.get(SkMatrix::kMScaleX));
642 fGsA.eM12 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewY)); // This should be ~0.
643 fGsA.eM21 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewX));
644 fGsA.eM22 = SkScalarToFIXED(GsA.get(SkMatrix::kMScaleY));
645
bungeman6f940762015-03-18 08:25:43 -0700646 // When not hinting, scale was computed with kVerticalInteger, so is already an integer.
647 // The sA and GsA transforms will be used to create 'linear' metrics.
648
649 // When hinting, scale was computed with kVertical, stating that our port can handle
650 // non-integer scales. This is done so that sA and GsA are computed without any 'residual'
651 // scale in them, preventing 'linear' metrics. However, GDI cannot actually handle non-integer
652 // scales so we need to round in this case. This is fine, since all of the scale has been
653 // removed from sA and GsA, so GDI will be handling the scale completely.
654 SkScalar gdiTextSize = SkScalarRoundToScalar(scale.fY);
655
656 // GDI will not accept a size of zero, so round the range [0, 1] to 1.
657 // If the size was non-zero, the scale factors will also be non-zero and 1px tall text is drawn.
658 // If the size actually was zero, the scale factors will also be zero, so GDI will draw nothing.
bungeman5f14c5e2014-12-05 12:26:44 -0800659 if (gdiTextSize == 0) {
660 gdiTextSize = SK_Scalar1;
661 }
bungeman@google.com0abbff92013-07-27 20:37:56 +0000662
reed@google.com055180c2013-03-21 18:46:35 +0000663 LOGFONT lf = typeface->fLogFont;
bungeman@google.com11ba3192013-10-03 20:17:51 +0000664 lf.lfHeight = -SkScalarTruncToInt(gdiTextSize);
reed@google.com82a34d82011-07-26 19:33:08 +0000665 lf.lfQuality = compute_quality(fRec);
reed@google.comac6b9792011-03-11 15:42:51 +0000666 fFont = CreateFontIndirect(&lf);
reed@google.com84e22d82013-07-10 15:38:20 +0000667 if (!fFont) {
668 return;
669 }
reed@google.com1dd17a12011-05-17 14:04:41 +0000670
reed@google.comac6b9792011-03-11 15:42:51 +0000671 fSavefont = (HFONT)SelectObject(fDDC, fFont);
reed@google.coma767fa02011-08-05 21:40:26 +0000672
bungeman@google.coma0319f62012-04-18 15:40:50 +0000673 if (0 == GetTextMetrics(fDDC, &fTM)) {
reed@google.com055180c2013-03-21 18:46:35 +0000674 call_ensure_accessible(lf);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000675 if (0 == GetTextMetrics(fDDC, &fTM)) {
676 fTM.tmPitchAndFamily = TMPF_TRUETYPE;
677 }
678 }
bungeman@google.com90b7e382012-04-20 15:26:28 +0000679
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000680 XFORM xform;
bungeman@google.com90b7e382012-04-20 15:26:28 +0000681 if (fTM.tmPitchAndFamily & TMPF_VECTOR) {
bungeman@google.com4732df62014-01-23 15:22:42 +0000682 // Used a logfont on a memory context, should never get a device font.
683 // Therefore all TMPF_DEVICE will be PostScript fonts.
684
685 // If TMPF_VECTOR is set, one of TMPF_TRUETYPE or TMPF_DEVICE means that
686 // we have an outline font. Otherwise we have a vector FON, which is
687 // scalable, but not an outline font.
688 // This was determined by testing with Type1 PFM/PFB and
689 // OpenTypeCFF OTF, as well as looking at Wine bugs and sources.
690 if (fTM.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_DEVICE)) {
691 // Truetype or PostScript.
692 fType = SkScalerContext_GDI::kTrueType_Type;
693 } else {
694 // Stroked FON.
695 fType = SkScalerContext_GDI::kLine_Type;
696 }
bungeman@google.coma0319f62012-04-18 15:40:50 +0000697
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000698 // fPost2x2 is column-major, left handed (y down).
699 // XFORM 2x2 is row-major, left handed (y down).
bungeman@google.com0abbff92013-07-27 20:37:56 +0000700 xform.eM11 = SkScalarToFloat(sA.get(SkMatrix::kMScaleX));
701 xform.eM12 = SkScalarToFloat(sA.get(SkMatrix::kMSkewY));
702 xform.eM21 = SkScalarToFloat(sA.get(SkMatrix::kMSkewX));
703 xform.eM22 = SkScalarToFloat(sA.get(SkMatrix::kMScaleY));
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000704 xform.eDx = 0;
705 xform.eDy = 0;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000706
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000707 // MAT2 is row major, right handed (y up).
708 fMat22.eM11 = float2FIXED(xform.eM11);
709 fMat22.eM12 = float2FIXED(-xform.eM12);
710 fMat22.eM21 = float2FIXED(-xform.eM21);
711 fMat22.eM22 = float2FIXED(xform.eM22);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000712
713 if (needToRenderWithSkia(fRec)) {
714 this->forceGenerateImageFromPath();
715 }
716
bungeman@google.com11ba3192013-10-03 20:17:51 +0000717 // Create a hires matrix if we need linear metrics.
bungeman@google.com0abbff92013-07-27 20:37:56 +0000718 if (this->isSubpixel()) {
719 OUTLINETEXTMETRIC otm;
720 UINT success = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
721 if (0 == success) {
722 call_ensure_accessible(lf);
723 success = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
724 }
725 if (0 != success) {
bungeman@google.com11ba3192013-10-03 20:17:51 +0000726 SkScalar upem = SkIntToScalar(otm.otmEMSquare);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000727
bungeman@google.com11ba3192013-10-03 20:17:51 +0000728 SkScalar gdiTextSizeToEMScale = upem / gdiTextSize;
729 fHighResMat22.eM11 = float2FIXED(gdiTextSizeToEMScale);
bungeman@google.com6a774a12013-07-30 01:07:48 +0000730 fHighResMat22.eM12 = float2FIXED(0);
731 fHighResMat22.eM21 = float2FIXED(0);
bungeman@google.com11ba3192013-10-03 20:17:51 +0000732 fHighResMat22.eM22 = float2FIXED(gdiTextSizeToEMScale);
bungeman@google.com6a774a12013-07-30 01:07:48 +0000733
bungeman@google.com11ba3192013-10-03 20:17:51 +0000734 SkScalar removeEMScale = SkScalarInvert(upem);
bungeman@google.com6a774a12013-07-30 01:07:48 +0000735 fHiResMatrix = A;
bungeman@google.com11ba3192013-10-03 20:17:51 +0000736 fHiResMatrix.preScale(removeEMScale, removeEMScale);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000737 }
738 }
739
bungeman@google.coma0319f62012-04-18 15:40:50 +0000740 } else {
741 // Assume bitmap
reed@google.com30ddd612013-07-30 17:47:39 +0000742 fType = SkScalerContext_GDI::kBitmap_Type;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000743
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000744 xform.eM11 = 1.0f;
745 xform.eM12 = 0.0f;
746 xform.eM21 = 0.0f;
747 xform.eM22 = 1.0f;
748 xform.eDx = 0.0f;
749 xform.eDy = 0.0f;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000750
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000751 // fPost2x2 is column-major, left handed (y down).
752 // MAT2 is row major, right handed (y up).
bungeman@google.coma0319f62012-04-18 15:40:50 +0000753 fMat22.eM11 = SkScalarToFIXED(fRec.fPost2x2[0][0]);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000754 fMat22.eM12 = SkScalarToFIXED(-fRec.fPost2x2[1][0]);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000755 fMat22.eM21 = SkScalarToFIXED(-fRec.fPost2x2[0][1]);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000756 fMat22.eM22 = SkScalarToFIXED(fRec.fPost2x2[1][1]);
reed@google.coma767fa02011-08-05 21:40:26 +0000757 }
reed@google.com99edd432011-09-09 14:59:59 +0000758
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000759 fOffscreen.init(fFont, xform);
reed@google.comac6b9792011-03-11 15:42:51 +0000760}
761
reed@google.com30ddd612013-07-30 17:47:39 +0000762SkScalerContext_GDI::~SkScalerContext_GDI() {
reed@google.comac6b9792011-03-11 15:42:51 +0000763 if (fDDC) {
764 ::SelectObject(fDDC, fSavefont);
765 ::DeleteDC(fDDC);
766 }
767 if (fFont) {
768 ::DeleteObject(fFont);
769 }
770 if (fSC) {
771 ::ScriptFreeCache(&fSC);
772 }
773}
774
reed@google.com30ddd612013-07-30 17:47:39 +0000775bool SkScalerContext_GDI::isValid() const {
reed@google.com84e22d82013-07-10 15:38:20 +0000776 return fDDC && fFont;
777}
778
reed@google.com30ddd612013-07-30 17:47:39 +0000779unsigned SkScalerContext_GDI::generateGlyphCount() {
reed@google.comac6b9792011-03-11 15:42:51 +0000780 if (fGlyphCount < 0) {
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000781 fGlyphCount = calculateGlyphCount(
782 fDDC, static_cast<const LogFontTypeface*>(this->getTypeface())->fLogFont);
reed@google.comac6b9792011-03-11 15:42:51 +0000783 }
784 return fGlyphCount;
785}
786
bungeman@google.com33346482013-08-27 19:05:32 +0000787uint16_t SkScalerContext_GDI::generateCharToGlyph(SkUnichar utf32) {
reed@google.comac6b9792011-03-11 15:42:51 +0000788 uint16_t index = 0;
bungeman@google.com33346482013-08-27 19:05:32 +0000789 WCHAR utf16[2];
reed@google.comac6b9792011-03-11 15:42:51 +0000790 // TODO(ctguil): Support characters that generate more than one glyph.
bungeman@google.com33346482013-08-27 19:05:32 +0000791 if (SkUTF16_FromUnichar(utf32, (uint16_t*)utf16) == 1) {
reed@google.comac6b9792011-03-11 15:42:51 +0000792 // Type1 fonts fail with uniscribe API. Use GetGlyphIndices for plane 0.
skia.committer@gmail.com7bd141d2013-08-28 07:01:18 +0000793
bungeman@google.com33346482013-08-27 19:05:32 +0000794 /** Real documentation for GetGlyphIndiciesW:
795 *
796 * When GGI_MARK_NONEXISTING_GLYPHS is not specified and a character does not map to a
797 * glyph, then the 'default character's glyph is returned instead. The 'default character'
bungeman@google.com4732df62014-01-23 15:22:42 +0000798 * is available in fTM.tmDefaultChar. FON fonts have a default character, and there exists
799 * a usDefaultChar in the 'OS/2' table, version 2 and later. If there is no
bungeman@google.com33346482013-08-27 19:05:32 +0000800 * 'default character' specified by the font, then often the first character found is used.
801 *
802 * When GGI_MARK_NONEXISTING_GLYPHS is specified and a character does not map to a glyph,
803 * then the glyph 0xFFFF is used. In Windows XP and earlier, Bitmap/Vector FON usually use
804 * glyph 0x1F instead ('Terminal' appears to be special, returning 0xFFFF).
805 * Type1 PFM/PFB, TT, OT TT, OT CFF all appear to use 0xFFFF, even on XP.
806 */
807 DWORD result = GetGlyphIndicesW(fDDC, utf16, 1, &index, GGI_MARK_NONEXISTING_GLYPHS);
808 if (result == GDI_ERROR
809 || 0xFFFF == index
bungeman@google.com4732df62014-01-23 15:22:42 +0000810 || (0x1F == index &&
811 (fType == SkScalerContext_GDI::kBitmap_Type ||
812 fType == SkScalerContext_GDI::kLine_Type)
813 /*&& winVer < Vista */)
814 )
bungeman@google.com33346482013-08-27 19:05:32 +0000815 {
816 index = 0;
817 }
reed@google.comac6b9792011-03-11 15:42:51 +0000818 } else {
819 // Use uniscribe to detemine glyph index for non-BMP characters.
bungeman@google.com27f74aa2013-10-08 21:32:15 +0000820 static const int numWCHAR = 2;
821 static const int maxItems = 2;
halcanary96fcdcc2015-08-27 07:41:13 -0700822 // MSDN states that this can be nullptr, but some things don't work then.
Chris Dalton1ef80942017-12-04 12:01:30 -0700823 SCRIPT_CONTROL sc;
824 memset(&sc, 0, sizeof(sc));
bungeman@google.com27f74aa2013-10-08 21:32:15 +0000825 // Add extra item to SCRIPT_ITEM to work around a bug (now documented).
826 // https://bugzilla.mozilla.org/show_bug.cgi?id=366643
827 SCRIPT_ITEM si[maxItems + 1];
828 int numItems;
halcanary96fcdcc2015-08-27 07:41:13 -0700829 HRZM(ScriptItemize(utf16, numWCHAR, maxItems, &sc, nullptr, si, &numItems),
bungeman@google.com27f74aa2013-10-08 21:32:15 +0000830 "Could not itemize character.");
reed@google.comac6b9792011-03-11 15:42:51 +0000831
Ben Wagner97182cc2018-02-15 10:20:04 -0500832 // Disable any attempt at shaping.
833 // Without this ScriptShape may return 0x80040200 (USP_E_SCRIPT_NOT_IN_FONT)
834 // when all that is desired here is a simple cmap lookup.
835 for (SCRIPT_ITEM& item : si) {
836 item.a.eScript = SCRIPT_UNDEFINED;
837 }
838
bungeman@google.com27f74aa2013-10-08 21:32:15 +0000839 // Sometimes ScriptShape cannot find a glyph for a non-BMP and returns 2 space glyphs.
840 static const int maxGlyphs = 2;
841 SCRIPT_VISATTR vsa[maxGlyphs];
842 WORD outGlyphs[maxGlyphs];
843 WORD logClust[numWCHAR];
844 int numGlyphs;
845 HRZM(ScriptShape(fDDC, &fSC, utf16, numWCHAR, maxGlyphs, &si[0].a,
846 outGlyphs, logClust, vsa, &numGlyphs),
847 "Could not shape character.");
848 if (1 == numGlyphs) {
849 index = outGlyphs[0];
850 }
reed@google.comac6b9792011-03-11 15:42:51 +0000851 }
852 return index;
853}
854
reed@google.com30ddd612013-07-30 17:47:39 +0000855void SkScalerContext_GDI::generateAdvance(SkGlyph* glyph) {
reed@google.comac6b9792011-03-11 15:42:51 +0000856 this->generateMetrics(glyph);
857}
858
reed@google.com30ddd612013-07-30 17:47:39 +0000859void SkScalerContext_GDI::generateMetrics(SkGlyph* glyph) {
reed@google.comac6b9792011-03-11 15:42:51 +0000860 SkASSERT(fDDC);
861
bungeman@google.com4732df62014-01-23 15:22:42 +0000862 if (fType == SkScalerContext_GDI::kBitmap_Type || fType == SkScalerContext_GDI::kLine_Type) {
bungeman@google.coma0319f62012-04-18 15:40:50 +0000863 SIZE size;
djsollen1b277042014-08-06 06:58:06 -0700864 WORD glyphs = glyph->getGlyphID();
bungeman@google.coma0319f62012-04-18 15:40:50 +0000865 if (0 == GetTextExtentPointI(fDDC, &glyphs, 1, &size)) {
866 glyph->fWidth = SkToS16(fTM.tmMaxCharWidth);
867 } else {
868 glyph->fWidth = SkToS16(size.cx);
869 }
870 glyph->fHeight = SkToS16(size.cy);
871
872 glyph->fTop = SkToS16(-fTM.tmAscent);
bungeman@google.com4732df62014-01-23 15:22:42 +0000873 // Bitmap FON cannot underhang, but vector FON may.
874 // There appears no means of determining underhang of vector FON.
bungeman@google.coma0319f62012-04-18 15:40:50 +0000875 glyph->fLeft = SkToS16(0);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700876 glyph->fAdvanceX = glyph->fWidth;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000877 glyph->fAdvanceY = 0;
878
bungeman@google.com4732df62014-01-23 15:22:42 +0000879 // Vector FON will transform nicely, but bitmap FON do not.
880 if (fType == SkScalerContext_GDI::kLine_Type) {
881 SkRect bounds = SkRect::MakeXYWH(glyph->fLeft, glyph->fTop,
882 glyph->fWidth, glyph->fHeight);
883 SkMatrix m;
884 m.setAll(SkFIXEDToScalar(fMat22.eM11), -SkFIXEDToScalar(fMat22.eM21), 0,
885 -SkFIXEDToScalar(fMat22.eM12), SkFIXEDToScalar(fMat22.eM22), 0,
reed3f43f8a2015-01-20 19:58:36 -0800886 0, 0, 1);
bungeman@google.com4732df62014-01-23 15:22:42 +0000887 m.mapRect(&bounds);
reedd02cf262014-11-18 18:06:45 -0800888 bounds.roundOut(&bounds);
bungeman@google.com4732df62014-01-23 15:22:42 +0000889 glyph->fLeft = SkScalarTruncToInt(bounds.fLeft);
890 glyph->fTop = SkScalarTruncToInt(bounds.fTop);
891 glyph->fWidth = SkScalarTruncToInt(bounds.width());
892 glyph->fHeight = SkScalarTruncToInt(bounds.height());
893 }
894
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000895 // Apply matrix to advance.
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700896 glyph->fAdvanceY = -FIXED2float(fMat22.eM12) * glyph->fAdvanceX;
897 glyph->fAdvanceX *= FIXED2float(fMat22.eM11);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000898
899 return;
900 }
901
djsollen1b277042014-08-06 06:58:06 -0700902 UINT glyphId = glyph->getGlyphID();
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000903
reed@google.comac6b9792011-03-11 15:42:51 +0000904 GLYPHMETRICS gm;
reed@google.com1dd17a12011-05-17 14:04:41 +0000905 sk_bzero(&gm, sizeof(gm));
reed@google.comac6b9792011-03-11 15:42:51 +0000906
halcanary96fcdcc2015-08-27 07:41:13 -0700907 DWORD status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fMat22);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000908 if (GDI_ERROR == status) {
reed@google.com055180c2013-03-21 18:46:35 +0000909 LogFontTypeface::EnsureAccessible(this->getTypeface());
halcanary96fcdcc2015-08-27 07:41:13 -0700910 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fMat22);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000911 if (GDI_ERROR == status) {
912 glyph->zeroMetrics();
913 return;
914 }
915 }
916
917 bool empty = false;
918 // The black box is either the embedded bitmap size or the outline extent.
919 // It is 1x1 if nothing is to be drawn, but will also be 1x1 if something very small
920 // is to be drawn, like a '.'. We need to outset '.' but do not wish to outset ' '.
921 if (1 == gm.gmBlackBoxX && 1 == gm.gmBlackBoxY) {
922 // If GetGlyphOutline with GGO_NATIVE returns 0, we know there was no outline.
halcanary96fcdcc2015-08-27 07:41:13 -0700923 DWORD bufferSize = GetGlyphOutlineW(fDDC, glyphId, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fMat22);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000924 empty = (0 == bufferSize);
925 }
926
927 glyph->fTop = SkToS16(-gm.gmptGlyphOrigin.y);
928 glyph->fLeft = SkToS16(gm.gmptGlyphOrigin.x);
929 if (empty) {
930 glyph->fWidth = 0;
931 glyph->fHeight = 0;
932 } else {
933 // Outset, since the image may bleed out of the black box.
934 // For embedded bitmaps the black box should be exact.
935 // For outlines we need to outset by 1 in all directions for bleed.
936 // For ClearType we need to outset by 2 for bleed.
937 glyph->fWidth = gm.gmBlackBoxX + 4;
938 glyph->fHeight = gm.gmBlackBoxY + 4;
939 glyph->fTop -= 2;
940 glyph->fLeft -= 2;
941 }
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700942 // TODO(benjaminwagner): What is the type of gm.gmCellInc[XY]?
943 glyph->fAdvanceX = (float)((int)gm.gmCellIncX);
944 glyph->fAdvanceY = (float)((int)gm.gmCellIncY);
reed@google.comac6b9792011-03-11 15:42:51 +0000945
bungeman@google.com6a774a12013-07-30 01:07:48 +0000946 if (this->isSubpixel()) {
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000947 sk_bzero(&gm, sizeof(gm));
halcanary96fcdcc2015-08-27 07:41:13 -0700948 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fHighResMat22);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000949 if (GDI_ERROR != status) {
950 SkPoint advance;
951 fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700952 glyph->fAdvanceX = SkScalarToFloat(advance.fX);
953 glyph->fAdvanceY = SkScalarToFloat(advance.fY);
reed@google.comac6b9792011-03-11 15:42:51 +0000954 }
bungeman@google.com0abbff92013-07-27 20:37:56 +0000955 } else if (!isAxisAligned(this->fRec)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700956 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fGsA);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000957 if (GDI_ERROR != status) {
958 SkPoint advance;
959 fG_inv.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700960 glyph->fAdvanceX = SkScalarToFloat(advance.fX);
961 glyph->fAdvanceY = SkScalarToFloat(advance.fY);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000962 }
reed@google.comac6b9792011-03-11 15:42:51 +0000963 }
964}
965
bungeman@google.com6a774a12013-07-30 01:07:48 +0000966static const MAT2 gMat2Identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
bungeman41078062014-07-07 08:16:37 -0700967void SkScalerContext_GDI::generateFontMetrics(SkPaint::FontMetrics* metrics) {
halcanary96fcdcc2015-08-27 07:41:13 -0700968 if (nullptr == metrics) {
bungeman41078062014-07-07 08:16:37 -0700969 return;
reed@google.com60af92c2013-05-08 14:11:28 +0000970 }
bungeman41078062014-07-07 08:16:37 -0700971 sk_bzero(metrics, sizeof(*metrics));
reed@google.comac6b9792011-03-11 15:42:51 +0000972
973 SkASSERT(fDDC);
974
bungeman@google.come9d83192013-06-21 05:31:38 +0000975#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
bungeman@google.com4732df62014-01-23 15:22:42 +0000976 if (fType == SkScalerContext_GDI::kBitmap_Type || fType == SkScalerContext_GDI::kLine_Type) {
bungeman@google.come9d83192013-06-21 05:31:38 +0000977#endif
bungeman41078062014-07-07 08:16:37 -0700978 metrics->fTop = SkIntToScalar(-fTM.tmAscent);
979 metrics->fAscent = SkIntToScalar(-fTM.tmAscent);
980 metrics->fDescent = SkIntToScalar(fTM.tmDescent);
981 metrics->fBottom = SkIntToScalar(fTM.tmDescent);
982 metrics->fLeading = SkIntToScalar(fTM.tmExternalLeading);
983 metrics->fAvgCharWidth = SkIntToScalar(fTM.tmAveCharWidth);
984 metrics->fMaxCharWidth = SkIntToScalar(fTM.tmMaxCharWidth);
985 metrics->fXMin = 0;
986 metrics->fXMax = metrics->fMaxCharWidth;
987 //metrics->fXHeight = 0;
bungeman@google.come9d83192013-06-21 05:31:38 +0000988#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
bungeman@google.coma0319f62012-04-18 15:40:50 +0000989 return;
990 }
bungeman@google.come9d83192013-06-21 05:31:38 +0000991#endif
bungeman@google.coma0319f62012-04-18 15:40:50 +0000992
reed@google.comac6b9792011-03-11 15:42:51 +0000993 OUTLINETEXTMETRIC otm;
994
995 uint32_t ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000996 if (0 == ret) {
reed@google.com055180c2013-03-21 18:46:35 +0000997 LogFontTypeface::EnsureAccessible(this->getTypeface());
bungeman@google.com39698b12011-11-15 22:26:41 +0000998 ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
999 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001000 if (0 == ret) {
bungeman@google.coma0319f62012-04-18 15:40:50 +00001001 return;
reed@google.comac6b9792011-03-11 15:42:51 +00001002 }
1003
bungeman@google.come9d83192013-06-21 05:31:38 +00001004#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
bungeman41078062014-07-07 08:16:37 -07001005 metrics->fTop = SkIntToScalar(-otm.otmrcFontBox.top);
1006 metrics->fAscent = SkIntToScalar(-otm.otmAscent);
1007 metrics->fDescent = SkIntToScalar(-otm.otmDescent);
1008 metrics->fBottom = SkIntToScalar(-otm.otmrcFontBox.bottom);
1009 metrics->fLeading = SkIntToScalar(otm.otmLineGap);
1010 metrics->fAvgCharWidth = SkIntToScalar(otm.otmTextMetrics.tmAveCharWidth);
1011 metrics->fMaxCharWidth = SkIntToScalar(otm.otmTextMetrics.tmMaxCharWidth);
1012 metrics->fXMin = SkIntToScalar(otm.otmrcFontBox.left);
1013 metrics->fXMax = SkIntToScalar(otm.otmrcFontBox.right);
commit-bot@chromium.orgd3031aa2014-05-14 14:54:51 +00001014#endif
bungeman41078062014-07-07 08:16:37 -07001015 metrics->fUnderlineThickness = SkIntToScalar(otm.otmsUnderscoreSize);
1016 metrics->fUnderlinePosition = -SkIntToScalar(otm.otmsUnderscorePosition);
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001017
Ben Wagner3318da52017-03-23 14:01:22 -04001018 metrics->fFlags |= SkPaint::FontMetrics::kUnderlineThicknessIsValid_Flag;
bungeman41078062014-07-07 08:16:37 -07001019 metrics->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
bungeman@google.com4a867a62014-05-22 17:59:21 +00001020
bungeman41078062014-07-07 08:16:37 -07001021 metrics->fXHeight = SkIntToScalar(otm.otmsXHeight);
1022 GLYPHMETRICS gm;
1023 sk_bzero(&gm, sizeof(gm));
1024 DWORD len = GetGlyphOutlineW(fDDC, 'x', GGO_METRICS, &gm, 0, 0, &gMat2Identity);
1025 if (len != GDI_ERROR && gm.gmBlackBoxY > 0) {
1026 metrics->fXHeight = SkIntToScalar(gm.gmBlackBoxY);
reed@google.comac6b9792011-03-11 15:42:51 +00001027 }
1028}
1029
reed@google.com7430a332011-10-03 14:37:38 +00001030////////////////////////////////////////////////////////////////////////////////////////
1031
bungeman@google.com0abbff92013-07-27 20:37:56 +00001032#define SK_SHOW_TEXT_BLIT_COVERAGE 0
1033
reed@google.com7430a332011-10-03 14:37:38 +00001034static void build_power_table(uint8_t table[], float ee) {
1035 for (int i = 0; i < 256; i++) {
1036 float x = i / 255.f;
bungeman@google.com97efada2012-07-30 20:40:50 +00001037 x = sk_float_pow(x, ee);
reed@google.come1ca7052013-12-17 19:22:07 +00001038 int xx = SkScalarRoundToInt(x * 255);
reed@google.com7430a332011-10-03 14:37:38 +00001039 table[i] = SkToU8(xx);
1040 }
1041}
1042
bungeman@google.com97efada2012-07-30 20:40:50 +00001043/**
1044 * This will invert the gamma applied by GDI (gray-scale antialiased), so we
1045 * can get linear values.
1046 *
1047 * GDI grayscale appears to use a hard-coded gamma of 2.3.
1048 *
1049 * GDI grayscale appears to draw using the black and white rasterizer at four
1050 * times the size and then downsamples to compute the coverage mask. As a
1051 * result there are only seventeen total grays. This lack of fidelity means
1052 * that shifting into other color spaces is imprecise.
1053 */
1054static const uint8_t* getInverseGammaTableGDI() {
Mike Klein0341f162017-10-18 09:58:54 -04001055 static SkOnce once;
bungeman@google.com97efada2012-07-30 20:40:50 +00001056 static uint8_t gTableGdi[256];
Mike Klein0341f162017-10-18 09:58:54 -04001057 once([]{
bungeman@google.com97efada2012-07-30 20:40:50 +00001058 build_power_table(gTableGdi, 2.3f);
Mike Klein0341f162017-10-18 09:58:54 -04001059 });
bungeman@google.com97efada2012-07-30 20:40:50 +00001060 return gTableGdi;
1061}
1062
1063/**
1064 * This will invert the gamma applied by GDI ClearType, so we can get linear
1065 * values.
1066 *
1067 * GDI ClearType uses SPI_GETFONTSMOOTHINGCONTRAST / 1000 as the gamma value.
1068 * If this value is not specified, the default is a gamma of 1.4.
1069 */
1070static const uint8_t* getInverseGammaTableClearType() {
Mike Klein0341f162017-10-18 09:58:54 -04001071 static SkOnce once;
bungeman@google.com97efada2012-07-30 20:40:50 +00001072 static uint8_t gTableClearType[256];
Mike Klein0341f162017-10-18 09:58:54 -04001073 once([]{
reed@google.com7430a332011-10-03 14:37:38 +00001074 UINT level = 0;
1075 if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &level, 0) || !level) {
1076 // can't get the data, so use a default
1077 level = 1400;
1078 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001079 build_power_table(gTableClearType, level / 1000.0f);
Mike Klein0341f162017-10-18 09:58:54 -04001080 });
bungeman@google.com97efada2012-07-30 20:40:50 +00001081 return gTableClearType;
reed@google.com7430a332011-10-03 14:37:38 +00001082}
1083
Cary Clarka4083c92017-09-15 11:59:23 -04001084#include "SkColorData.h"
reed@google.comac6b9792011-03-11 15:42:51 +00001085
bungeman@google.com63853142012-08-01 15:36:46 +00001086//Cannot assume that the input rgb is gray due to possible setting of kGenA8FromLCD_Flag.
bungeman@google.com97efada2012-07-30 20:40:50 +00001087template<bool APPLY_PREBLEND>
1088static inline uint8_t rgb_to_a8(SkGdiRGB rgb, const uint8_t* table8) {
bungeman@google.com63853142012-08-01 15:36:46 +00001089 U8CPU r = (rgb >> 16) & 0xFF;
1090 U8CPU g = (rgb >> 8) & 0xFF;
1091 U8CPU b = (rgb >> 0) & 0xFF;
bungeman@google.com1bfe01d2012-08-28 16:02:42 +00001092 return sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
reed@google.com8351aab2012-01-18 17:06:35 +00001093}
1094
bungeman@google.com97efada2012-07-30 20:40:50 +00001095template<bool APPLY_PREBLEND>
1096static inline uint16_t rgb_to_lcd16(SkGdiRGB rgb, const uint8_t* tableR,
1097 const uint8_t* tableG,
1098 const uint8_t* tableB) {
1099 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1100 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1101 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001102#if SK_SHOW_TEXT_BLIT_COVERAGE
1103 r = SkMax32(r, 10); g = SkMax32(g, 10); b = SkMax32(b, 10);
1104#endif
bungeman@google.com97efada2012-07-30 20:40:50 +00001105 return SkPack888ToRGB16(r, g, b);
reed@google.com82a34d82011-07-26 19:33:08 +00001106}
1107
reed@google.com82cff022011-09-22 14:33:40 +00001108// Is this GDI color neither black nor white? If so, we have to keep this
1109// image as is, rather than smashing it down to a BW mask.
1110//
1111// returns int instead of bool, since we don't want/have to pay to convert
1112// the zero/non-zero value into a bool
1113static int is_not_black_or_white(SkGdiRGB c) {
1114 // same as (but faster than)
1115 // c &= 0x00FFFFFF;
1116 // return 0 == c || 0x00FFFFFF == c;
1117 return (c + (c & 1)) & 0x00FFFFFF;
reed@google.com5e2df642011-09-21 18:42:09 +00001118}
1119
bungeman@google.com4b18f572013-07-22 15:21:23 +00001120static bool is_rgb_really_bw(const SkGdiRGB* src, int width, int height, size_t srcRB) {
reed@google.com5e2df642011-09-21 18:42:09 +00001121 for (int y = 0; y < height; ++y) {
1122 for (int x = 0; x < width; ++x) {
reed@google.com82cff022011-09-22 14:33:40 +00001123 if (is_not_black_or_white(src[x])) {
reed@google.com5e2df642011-09-21 18:42:09 +00001124 return false;
1125 }
1126 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001127 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001128 }
1129 return true;
1130}
1131
bungeman@google.com97efada2012-07-30 20:40:50 +00001132// gdi's bitmap is upside-down, so we reverse dst walking in Y
1133// whenever we copy it into skia's buffer
reed@google.com5e2df642011-09-21 18:42:09 +00001134static void rgb_to_bw(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
bungeman@google.com97efada2012-07-30 20:40:50 +00001135 const SkGlyph& glyph) {
reed@google.com5e2df642011-09-21 18:42:09 +00001136 const int width = glyph.fWidth;
1137 const size_t dstRB = (width + 7) >> 3;
1138 uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1139
1140 int byteCount = width >> 3;
1141 int bitCount = width & 7;
1142
1143 // adjust srcRB to skip the values in our byteCount loop,
1144 // since we increment src locally there
1145 srcRB -= byteCount * 8 * sizeof(SkGdiRGB);
1146
1147 for (int y = 0; y < glyph.fHeight; ++y) {
1148 if (byteCount > 0) {
reed@google.com5e2df642011-09-21 18:42:09 +00001149 for (int i = 0; i < byteCount; ++i) {
reed@google.com7a230142011-09-27 14:07:21 +00001150 unsigned byte = 0;
bungeman@google.com97efada2012-07-30 20:40:50 +00001151 byte |= src[0] & (1 << 7);
1152 byte |= src[1] & (1 << 6);
1153 byte |= src[2] & (1 << 5);
1154 byte |= src[3] & (1 << 4);
1155 byte |= src[4] & (1 << 3);
1156 byte |= src[5] & (1 << 2);
1157 byte |= src[6] & (1 << 1);
1158 byte |= src[7] & (1 << 0);
reed@google.com6a8f14d2011-09-27 12:54:24 +00001159 dst[i] = byte;
reed@google.com5e2df642011-09-21 18:42:09 +00001160 src += 8;
1161 }
1162 }
1163 if (bitCount > 0) {
1164 unsigned byte = 0;
1165 unsigned mask = 0x80;
1166 for (int i = 0; i < bitCount; i++) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001167 byte |= src[i] & mask;
reed@google.com5e2df642011-09-21 18:42:09 +00001168 mask >>= 1;
1169 }
1170 dst[byteCount] = byte;
1171 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001172 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001173 dst -= dstRB;
1174 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001175#if SK_SHOW_TEXT_BLIT_COVERAGE
1176 if (glyph.fWidth > 0 && glyph.fHeight > 0) {
1177 uint8_t* first = (uint8_t*)glyph.fImage;
1178 uint8_t* last = (uint8_t*)((char*)glyph.fImage + glyph.fHeight * dstRB - 1);
1179 *first |= 1 << 7;
1180 *last |= bitCount == 0 ? 1 : 1 << (8 - bitCount);
1181 }
1182#endif
reed@google.com5e2df642011-09-21 18:42:09 +00001183}
1184
bungeman@google.com97efada2012-07-30 20:40:50 +00001185template<bool APPLY_PREBLEND>
reed@google.com5e2df642011-09-21 18:42:09 +00001186static void rgb_to_a8(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
bungeman@google.com97efada2012-07-30 20:40:50 +00001187 const SkGlyph& glyph, const uint8_t* table8) {
reed@google.com5e2df642011-09-21 18:42:09 +00001188 const size_t dstRB = glyph.rowBytes();
1189 const int width = glyph.fWidth;
1190 uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1191
1192 for (int y = 0; y < glyph.fHeight; y++) {
1193 for (int i = 0; i < width; i++) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001194 dst[i] = rgb_to_a8<APPLY_PREBLEND>(src[i], table8);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001195#if SK_SHOW_TEXT_BLIT_COVERAGE
1196 dst[i] = SkMax32(dst[i], 10);
1197#endif
reed@google.com5e2df642011-09-21 18:42:09 +00001198 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001199 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001200 dst -= dstRB;
1201 }
1202}
1203
bungeman@google.com97efada2012-07-30 20:40:50 +00001204template<bool APPLY_PREBLEND>
1205static void rgb_to_lcd16(const SkGdiRGB* SK_RESTRICT src, size_t srcRB, const SkGlyph& glyph,
1206 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
reed@google.com5e2df642011-09-21 18:42:09 +00001207 const size_t dstRB = glyph.rowBytes();
1208 const int width = glyph.fWidth;
1209 uint16_t* SK_RESTRICT dst = (uint16_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1210
1211 for (int y = 0; y < glyph.fHeight; y++) {
1212 for (int i = 0; i < width; i++) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001213 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(src[i], tableR, tableG, tableB);
reed@google.com5e2df642011-09-21 18:42:09 +00001214 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001215 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001216 dst = (uint16_t*)((char*)dst - dstRB);
1217 }
1218}
1219
reed@google.com30ddd612013-07-30 17:47:39 +00001220void SkScalerContext_GDI::generateImage(const SkGlyph& glyph) {
reed@google.comac6b9792011-03-11 15:42:51 +00001221 SkASSERT(fDDC);
1222
reed@google.com62711172011-05-18 15:08:10 +00001223 const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
reed@google.com82a34d82011-07-26 19:33:08 +00001224 const bool isAA = !isLCD(fRec);
reed@google.comac6b9792011-03-11 15:42:51 +00001225
reed@google.com99edd432011-09-09 14:59:59 +00001226 size_t srcRB;
bungeman@google.com97efada2012-07-30 20:40:50 +00001227 const void* bits = fOffscreen.draw(glyph, isBW, &srcRB);
halcanary96fcdcc2015-08-27 07:41:13 -07001228 if (nullptr == bits) {
reed@google.com055180c2013-03-21 18:46:35 +00001229 LogFontTypeface::EnsureAccessible(this->getTypeface());
bungeman@google.com97efada2012-07-30 20:40:50 +00001230 bits = fOffscreen.draw(glyph, isBW, &srcRB);
halcanary96fcdcc2015-08-27 07:41:13 -07001231 if (nullptr == bits) {
bungeman@google.com39698b12011-11-15 22:26:41 +00001232 sk_bzero(glyph.fImage, glyph.computeImageSize());
1233 return;
1234 }
reed@google.com82a34d82011-07-26 19:33:08 +00001235 }
reed@google.comac6b9792011-03-11 15:42:51 +00001236
bungeman@google.com97efada2012-07-30 20:40:50 +00001237 if (!isBW) {
bungeman@google.com1bd2d672012-08-13 20:01:51 +00001238 const uint8_t* table;
1239 //The offscreen contains a GDI blit if isAA and kGenA8FromLCD_Flag is not set.
1240 //Otherwise the offscreen contains a ClearType blit.
1241 if (isAA && !(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag)) {
1242 table = getInverseGammaTableGDI();
1243 } else {
1244 table = getInverseGammaTableClearType();
bungeman@google.com97efada2012-07-30 20:40:50 +00001245 }
1246 //Note that the following cannot really be integrated into the
1247 //pre-blend, since we may not be applying the pre-blend; when we aren't
1248 //applying the pre-blend it means that a filter wants linear anyway.
1249 //Other code may also be applying the pre-blend, so we'd need another
1250 //one with this and one without.
reed@google.com6f5df482011-09-28 20:33:24 +00001251 SkGdiRGB* addr = (SkGdiRGB*)bits;
1252 for (int y = 0; y < glyph.fHeight; ++y) {
1253 for (int x = 0; x < glyph.fWidth; ++x) {
1254 int r = (addr[x] >> 16) & 0xFF;
1255 int g = (addr[x] >> 8) & 0xFF;
1256 int b = (addr[x] >> 0) & 0xFF;
reed@google.com7430a332011-10-03 14:37:38 +00001257 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
reed@google.com6f5df482011-09-28 20:33:24 +00001258 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001259 addr = SkTAddOffset<SkGdiRGB>(addr, srcRB);
reed@google.com6f5df482011-09-28 20:33:24 +00001260 }
1261 }
1262
reed@google.com82a34d82011-07-26 19:33:08 +00001263 int width = glyph.fWidth;
1264 size_t dstRB = glyph.rowBytes();
1265 if (isBW) {
1266 const uint8_t* src = (const uint8_t*)bits;
reed@google.com82a34d82011-07-26 19:33:08 +00001267 uint8_t* dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1268 for (int y = 0; y < glyph.fHeight; y++) {
1269 memcpy(dst, src, dstRB);
1270 src += srcRB;
1271 dst -= dstRB;
reed@google.comac6b9792011-03-11 15:42:51 +00001272 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001273#if SK_SHOW_TEXT_BLIT_COVERAGE
1274 if (glyph.fWidth > 0 && glyph.fHeight > 0) {
1275 int bitCount = width & 7;
1276 uint8_t* first = (uint8_t*)glyph.fImage;
1277 uint8_t* last = (uint8_t*)((char*)glyph.fImage + glyph.fHeight * dstRB - 1);
1278 *first |= 1 << 7;
1279 *last |= bitCount == 0 ? 1 : 1 << (8 - bitCount);
1280 }
1281#endif
reed@google.com82a34d82011-07-26 19:33:08 +00001282 } else if (isAA) {
reed@google.com6f5df482011-09-28 20:33:24 +00001283 // since the caller may require A8 for maskfilters, we can't check for BW
1284 // ... until we have the caller tell us that explicitly
reed@google.com5e2df642011-09-21 18:42:09 +00001285 const SkGdiRGB* src = (const SkGdiRGB*)bits;
bungeman@google.coma76de722012-10-26 19:35:54 +00001286 if (fPreBlend.isApplicable()) {
1287 rgb_to_a8<true>(src, srcRB, glyph, fPreBlend.fG);
bungeman@google.com97efada2012-07-30 20:40:50 +00001288 } else {
bungeman@google.coma76de722012-10-26 19:35:54 +00001289 rgb_to_a8<false>(src, srcRB, glyph, fPreBlend.fG);
bungeman@google.com97efada2012-07-30 20:40:50 +00001290 }
reed@google.com82a34d82011-07-26 19:33:08 +00001291 } else { // LCD16
reed@google.com5e2df642011-09-21 18:42:09 +00001292 const SkGdiRGB* src = (const SkGdiRGB*)bits;
1293 if (is_rgb_really_bw(src, width, glyph.fHeight, srcRB)) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001294 rgb_to_bw(src, srcRB, glyph);
reed@google.com5e2df642011-09-21 18:42:09 +00001295 ((SkGlyph*)&glyph)->fMaskFormat = SkMask::kBW_Format;
1296 } else {
reedd54d3fc2014-11-13 14:39:58 -08001297 SkASSERT(SkMask::kLCD16_Format == glyph.fMaskFormat);
1298 if (fPreBlend.isApplicable()) {
1299 rgb_to_lcd16<true>(src, srcRB, glyph,
1300 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
reed@google.com754e4eb2011-09-26 13:21:39 +00001301 } else {
reedd54d3fc2014-11-13 14:39:58 -08001302 rgb_to_lcd16<false>(src, srcRB, glyph,
1303 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
reed@google.com754e4eb2011-09-26 13:21:39 +00001304 }
reed@google.comac6b9792011-03-11 15:42:51 +00001305 }
1306 }
reed@google.comac6b9792011-03-11 15:42:51 +00001307}
1308
bungeman@google.com0abbff92013-07-27 20:37:56 +00001309class GDIGlyphbufferPointIter {
1310public:
skia.committer@gmail.com27e21fe2013-07-28 07:01:03 +00001311 GDIGlyphbufferPointIter(const uint8_t* glyphbuf, DWORD total_size)
bungeman@google.com0abbff92013-07-27 20:37:56 +00001312 : fHeaderIter(glyphbuf, total_size), fCurveIter(), fPointIter()
1313 { }
reed@google.comac6b9792011-03-11 15:42:51 +00001314
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001315 POINTFX const * next() {
bungeman@google.com0abbff92013-07-27 20:37:56 +00001316nextHeader:
1317 if (!fCurveIter.isSet()) {
1318 const TTPOLYGONHEADER* header = fHeaderIter.next();
halcanary96fcdcc2015-08-27 07:41:13 -07001319 if (nullptr == header) {
1320 return nullptr;
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001321 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001322 fCurveIter.set(header);
1323 const TTPOLYCURVE* curve = fCurveIter.next();
halcanary96fcdcc2015-08-27 07:41:13 -07001324 if (nullptr == curve) {
1325 return nullptr;
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001326 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001327 fPointIter.set(curve);
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001328 return &header->pfxStart;
bungeman@google.com05a729f2013-06-20 15:29:16 +00001329 }
1330
bungeman@google.com0abbff92013-07-27 20:37:56 +00001331 const POINTFX* nextPoint = fPointIter.next();
halcanary96fcdcc2015-08-27 07:41:13 -07001332 if (nullptr == nextPoint) {
bungeman@google.com0abbff92013-07-27 20:37:56 +00001333 const TTPOLYCURVE* curve = fCurveIter.next();
halcanary96fcdcc2015-08-27 07:41:13 -07001334 if (nullptr == curve) {
bungeman@google.com0abbff92013-07-27 20:37:56 +00001335 fCurveIter.set();
1336 goto nextHeader;
1337 } else {
1338 fPointIter.set(curve);
bungeman@google.com05a729f2013-06-20 15:29:16 +00001339 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001340 nextPoint = fPointIter.next();
reed@google.comac6b9792011-03-11 15:42:51 +00001341 }
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001342 return nextPoint;
reed@google.comac6b9792011-03-11 15:42:51 +00001343 }
bungeman@google.comd1c7f712013-03-11 18:51:19 +00001344
bungeman@google.com0abbff92013-07-27 20:37:56 +00001345 WORD currentCurveType() {
1346 return fPointIter.fCurveType;
1347 }
1348
1349private:
1350 /** Iterates over all of the polygon headers in a glyphbuf. */
1351 class GDIPolygonHeaderIter {
1352 public:
skia.committer@gmail.com27e21fe2013-07-28 07:01:03 +00001353 GDIPolygonHeaderIter(const uint8_t* glyphbuf, DWORD total_size)
bungeman@google.com0abbff92013-07-27 20:37:56 +00001354 : fCurPolygon(reinterpret_cast<const TTPOLYGONHEADER*>(glyphbuf))
1355 , fEndPolygon(SkTAddOffset<const TTPOLYGONHEADER>(glyphbuf, total_size))
1356 { }
1357
1358 const TTPOLYGONHEADER* next() {
1359 if (fCurPolygon >= fEndPolygon) {
halcanary96fcdcc2015-08-27 07:41:13 -07001360 return nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001361 }
1362 const TTPOLYGONHEADER* thisPolygon = fCurPolygon;
1363 fCurPolygon = SkTAddOffset<const TTPOLYGONHEADER>(fCurPolygon, fCurPolygon->cb);
1364 return thisPolygon;
1365 }
1366 private:
1367 const TTPOLYGONHEADER* fCurPolygon;
1368 const TTPOLYGONHEADER* fEndPolygon;
1369 };
1370
1371 /** Iterates over all of the polygon curves in a polygon header. */
1372 class GDIPolygonCurveIter {
1373 public:
halcanary96fcdcc2015-08-27 07:41:13 -07001374 GDIPolygonCurveIter() : fCurCurve(nullptr), fEndCurve(nullptr) { }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001375
1376 GDIPolygonCurveIter(const TTPOLYGONHEADER* curPolygon)
1377 : fCurCurve(SkTAddOffset<const TTPOLYCURVE>(curPolygon, sizeof(TTPOLYGONHEADER)))
1378 , fEndCurve(SkTAddOffset<const TTPOLYCURVE>(curPolygon, curPolygon->cb))
1379 { }
1380
halcanary96fcdcc2015-08-27 07:41:13 -07001381 bool isSet() { return fCurCurve != nullptr; }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001382
1383 void set(const TTPOLYGONHEADER* curPolygon) {
1384 fCurCurve = SkTAddOffset<const TTPOLYCURVE>(curPolygon, sizeof(TTPOLYGONHEADER));
1385 fEndCurve = SkTAddOffset<const TTPOLYCURVE>(curPolygon, curPolygon->cb);
1386 }
1387 void set() {
halcanary96fcdcc2015-08-27 07:41:13 -07001388 fCurCurve = nullptr;
1389 fEndCurve = nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001390 }
1391
1392 const TTPOLYCURVE* next() {
1393 if (fCurCurve >= fEndCurve) {
halcanary96fcdcc2015-08-27 07:41:13 -07001394 return nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001395 }
1396 const TTPOLYCURVE* thisCurve = fCurCurve;
1397 fCurCurve = SkTAddOffset<const TTPOLYCURVE>(fCurCurve, size_of_TTPOLYCURVE(*fCurCurve));
1398 return thisCurve;
1399 }
1400 private:
1401 size_t size_of_TTPOLYCURVE(const TTPOLYCURVE& curve) {
1402 return 2*sizeof(WORD) + curve.cpfx*sizeof(POINTFX);
1403 }
1404 const TTPOLYCURVE* fCurCurve;
1405 const TTPOLYCURVE* fEndCurve;
1406 };
1407
1408 /** Iterates over all of the polygon points in a polygon curve. */
1409 class GDIPolygonCurvePointIter {
1410 public:
halcanary96fcdcc2015-08-27 07:41:13 -07001411 GDIPolygonCurvePointIter() : fCurveType(0), fCurPoint(nullptr), fEndPoint(nullptr) { }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001412
1413 GDIPolygonCurvePointIter(const TTPOLYCURVE* curPolygon)
1414 : fCurveType(curPolygon->wType)
1415 , fCurPoint(&curPolygon->apfx[0])
1416 , fEndPoint(&curPolygon->apfx[curPolygon->cpfx])
1417 { }
1418
halcanary96fcdcc2015-08-27 07:41:13 -07001419 bool isSet() { return fCurPoint != nullptr; }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001420
1421 void set(const TTPOLYCURVE* curPolygon) {
1422 fCurveType = curPolygon->wType;
1423 fCurPoint = &curPolygon->apfx[0];
1424 fEndPoint = &curPolygon->apfx[curPolygon->cpfx];
1425 }
1426 void set() {
halcanary96fcdcc2015-08-27 07:41:13 -07001427 fCurPoint = nullptr;
1428 fEndPoint = nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001429 }
1430
1431 const POINTFX* next() {
1432 if (fCurPoint >= fEndPoint) {
halcanary96fcdcc2015-08-27 07:41:13 -07001433 return nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001434 }
1435 const POINTFX* thisPoint = fCurPoint;
1436 ++fCurPoint;
1437 return thisPoint;
1438 }
1439
1440 WORD fCurveType;
1441 private:
1442 const POINTFX* fCurPoint;
1443 const POINTFX* fEndPoint;
1444 };
1445
1446 GDIPolygonHeaderIter fHeaderIter;
1447 GDIPolygonCurveIter fCurveIter;
1448 GDIPolygonCurvePointIter fPointIter;
1449};
1450
1451static void sk_path_from_gdi_path(SkPath* path, const uint8_t* glyphbuf, DWORD total_size) {
bungeman@google.comd1c7f712013-03-11 18:51:19 +00001452 const uint8_t* cur_glyph = glyphbuf;
1453 const uint8_t* end_glyph = glyphbuf + total_size;
1454
1455 while (cur_glyph < end_glyph) {
1456 const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
1457
1458 const uint8_t* end_poly = cur_glyph + th->cb;
1459 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
1460
1461 path->moveTo(SkFixedToScalar( SkFIXEDToFixed(th->pfxStart.x)),
1462 SkFixedToScalar(-SkFIXEDToFixed(th->pfxStart.y)));
1463
1464 while (cur_poly < end_poly) {
1465 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
1466
1467 if (pc->wType == TT_PRIM_LINE) {
1468 for (uint16_t i = 0; i < pc->cpfx; i++) {
1469 path->lineTo(SkFixedToScalar( SkFIXEDToFixed(pc->apfx[i].x)),
1470 SkFixedToScalar(-SkFIXEDToFixed(pc->apfx[i].y)));
1471 }
1472 }
1473
1474 if (pc->wType == TT_PRIM_QSPLINE) {
1475 for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
1476 POINTFX pnt_b = pc->apfx[u]; // B is always the current point
1477 POINTFX pnt_c = pc->apfx[u+1];
1478
1479 if (u < pc->cpfx - 2) { // If not on last spline, compute C
1480 pnt_c.x = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.x),
1481 SkFIXEDToFixed(pnt_c.x)));
1482 pnt_c.y = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.y),
1483 SkFIXEDToFixed(pnt_c.y)));
1484 }
1485
1486 path->quadTo(SkFixedToScalar( SkFIXEDToFixed(pnt_b.x)),
1487 SkFixedToScalar(-SkFIXEDToFixed(pnt_b.y)),
1488 SkFixedToScalar( SkFIXEDToFixed(pnt_c.x)),
1489 SkFixedToScalar(-SkFIXEDToFixed(pnt_c.y)));
1490 }
1491 }
1492 // Advance past this TTPOLYCURVE.
1493 cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx;
1494 }
1495 cur_glyph += th->cb;
1496 path->close();
reed@google.comac6b9792011-03-11 15:42:51 +00001497 }
reed@google.comac6b9792011-03-11 15:42:51 +00001498}
1499
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001500#define move_next_expected_hinted_point(iter, pElem) do {\
1501 pElem = iter.next(); \
halcanary96fcdcc2015-08-27 07:41:13 -07001502 if (nullptr == pElem) return false; \
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001503} while(0)
1504
1505// It is possible for the hinted and unhinted versions of the same path to have
1506// a different number of points due to GDI's handling of flipped points.
1507// If this is detected, this will return false.
1508static bool sk_path_from_gdi_paths(SkPath* path, const uint8_t* glyphbuf, DWORD total_size,
bungeman@google.com0abbff92013-07-27 20:37:56 +00001509 GDIGlyphbufferPointIter hintedYs) {
1510 const uint8_t* cur_glyph = glyphbuf;
1511 const uint8_t* end_glyph = glyphbuf + total_size;
1512
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001513 POINTFX const * hintedPoint;
1514
bungeman@google.com0abbff92013-07-27 20:37:56 +00001515 while (cur_glyph < end_glyph) {
1516 const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
1517
1518 const uint8_t* end_poly = cur_glyph + th->cb;
1519 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
1520
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001521 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001522 path->moveTo(SkFixedToScalar( SkFIXEDToFixed(th->pfxStart.x)),
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001523 SkFixedToScalar(-SkFIXEDToFixed(hintedPoint->y)));
bungeman@google.com0abbff92013-07-27 20:37:56 +00001524
1525 while (cur_poly < end_poly) {
1526 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
1527
1528 if (pc->wType == TT_PRIM_LINE) {
1529 for (uint16_t i = 0; i < pc->cpfx; i++) {
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001530 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001531 path->lineTo(SkFixedToScalar( SkFIXEDToFixed(pc->apfx[i].x)),
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001532 SkFixedToScalar(-SkFIXEDToFixed(hintedPoint->y)));
bungeman@google.com0abbff92013-07-27 20:37:56 +00001533 }
1534 }
1535
1536 if (pc->wType == TT_PRIM_QSPLINE) {
1537 POINTFX currentPoint = pc->apfx[0];
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001538 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001539 // only take the hinted y if it wasn't flipped
1540 if (hintedYs.currentCurveType() == TT_PRIM_QSPLINE) {
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001541 currentPoint.y = hintedPoint->y;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001542 }
1543 for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
1544 POINTFX pnt_b = currentPoint;//pc->apfx[u]; // B is always the current point
1545 POINTFX pnt_c = pc->apfx[u+1];
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001546 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001547 // only take the hinted y if it wasn't flipped
1548 if (hintedYs.currentCurveType() == TT_PRIM_QSPLINE) {
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001549 pnt_c.y = hintedPoint->y;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001550 }
1551 currentPoint.x = pnt_c.x;
1552 currentPoint.y = pnt_c.y;
1553
1554 if (u < pc->cpfx - 2) { // If not on last spline, compute C
1555 pnt_c.x = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.x),
1556 SkFIXEDToFixed(pnt_c.x)));
1557 pnt_c.y = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.y),
1558 SkFIXEDToFixed(pnt_c.y)));
1559 }
1560
1561 path->quadTo(SkFixedToScalar( SkFIXEDToFixed(pnt_b.x)),
1562 SkFixedToScalar(-SkFIXEDToFixed(pnt_b.y)),
1563 SkFixedToScalar( SkFIXEDToFixed(pnt_c.x)),
1564 SkFixedToScalar(-SkFIXEDToFixed(pnt_c.y)));
1565 }
1566 }
1567 // Advance past this TTPOLYCURVE.
1568 cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx;
1569 }
1570 cur_glyph += th->cb;
1571 path->close();
1572 }
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001573 return true;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001574}
1575
Ben Wagner6e9ac122016-11-11 14:31:06 -05001576DWORD SkScalerContext_GDI::getGDIGlyphPath(SkGlyphID glyph, UINT flags,
1577 SkAutoSTMalloc<BUFFERSIZE, uint8_t>* glyphbuf)
bungeman@google.com0abbff92013-07-27 20:37:56 +00001578{
1579 GLYPHMETRICS gm;
1580
Ben Wagner6e9ac122016-11-11 14:31:06 -05001581 DWORD total_size = GetGlyphOutlineW(fDDC, glyph, flags, &gm, BUFFERSIZE, glyphbuf->get(), &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001582 // Sometimes GetGlyphOutlineW returns a number larger than BUFFERSIZE even if BUFFERSIZE > 0.
1583 // It has been verified that this does not involve a buffer overrun.
1584 if (GDI_ERROR == total_size || total_size > BUFFERSIZE) {
1585 // GDI_ERROR because the BUFFERSIZE was too small, or because the data was not accessible.
1586 // When the data is not accessable GetGlyphOutlineW fails rather quickly,
1587 // so just try to get the size. If that fails then ensure the data is accessible.
Ben Wagner6e9ac122016-11-11 14:31:06 -05001588 total_size = GetGlyphOutlineW(fDDC, glyph, flags, &gm, 0, nullptr, &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001589 if (GDI_ERROR == total_size) {
1590 LogFontTypeface::EnsureAccessible(this->getTypeface());
Ben Wagner6e9ac122016-11-11 14:31:06 -05001591 total_size = GetGlyphOutlineW(fDDC, glyph, flags, &gm, 0, nullptr, &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001592 if (GDI_ERROR == total_size) {
kkinnunenc6cb56f2014-06-24 00:12:27 -07001593 // GetGlyphOutlineW is known to fail for some characters, such as spaces.
1594 // In these cases, just return that the glyph does not have a shape.
bungeman@google.com0abbff92013-07-27 20:37:56 +00001595 return 0;
1596 }
1597 }
1598
1599 glyphbuf->reset(total_size);
1600
Ben Wagner6e9ac122016-11-11 14:31:06 -05001601 DWORD ret = GetGlyphOutlineW(fDDC, glyph, flags, &gm, total_size, glyphbuf->get(), &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001602 if (GDI_ERROR == ret) {
1603 LogFontTypeface::EnsureAccessible(this->getTypeface());
Ben Wagner6e9ac122016-11-11 14:31:06 -05001604 ret = GetGlyphOutlineW(fDDC, glyph, flags, &gm, total_size, glyphbuf->get(), &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001605 if (GDI_ERROR == ret) {
1606 SkASSERT(false);
1607 return 0;
1608 }
1609 }
1610 }
1611 return total_size;
1612}
1613
Ben Wagner5ddb3082018-03-29 11:18:06 -04001614bool SkScalerContext_GDI::generatePath(SkGlyphID glyph, SkPath* path) {
dcheng3ba043f2015-07-08 13:25:23 -07001615 SkASSERT(path);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001616 SkASSERT(fDDC);
1617
1618 path->reset();
1619
1620 // Out of all the fonts on a typical Windows box,
1621 // 25% of glyphs require more than 2KB.
1622 // 1% of glyphs require more than 4KB.
1623 // 0.01% of glyphs require more than 8KB.
1624 // 8KB is less than 1% of the normal 1MB stack on Windows.
1625 // Note that some web fonts glyphs require more than 20KB.
1626 //static const DWORD BUFFERSIZE = (1 << 13);
1627
1628 //GDI only uses hinted outlines when axis aligned.
1629 UINT format = GGO_NATIVE | GGO_GLYPH_INDEX;
1630 if (fRec.getHinting() == SkPaint::kNo_Hinting || fRec.getHinting() == SkPaint::kSlight_Hinting){
1631 format |= GGO_UNHINTED;
1632 }
1633 SkAutoSTMalloc<BUFFERSIZE, uint8_t> glyphbuf(BUFFERSIZE);
1634 DWORD total_size = getGDIGlyphPath(glyph, format, &glyphbuf);
1635 if (0 == total_size) {
Ben Wagner5ddb3082018-03-29 11:18:06 -04001636 return false;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001637 }
1638
1639 if (fRec.getHinting() != SkPaint::kSlight_Hinting) {
1640 sk_path_from_gdi_path(path, glyphbuf, total_size);
1641 } else {
1642 //GDI only uses hinted outlines when axis aligned.
1643 UINT format = GGO_NATIVE | GGO_GLYPH_INDEX;
1644
1645 SkAutoSTMalloc<BUFFERSIZE, uint8_t> hintedGlyphbuf(BUFFERSIZE);
1646 DWORD hinted_total_size = getGDIGlyphPath(glyph, format, &hintedGlyphbuf);
1647 if (0 == hinted_total_size) {
Ben Wagner5ddb3082018-03-29 11:18:06 -04001648 return false;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001649 }
1650
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001651 if (!sk_path_from_gdi_paths(path, glyphbuf, total_size,
1652 GDIGlyphbufferPointIter(hintedGlyphbuf, hinted_total_size)))
1653 {
1654 path->reset();
1655 sk_path_from_gdi_path(path, glyphbuf, total_size);
1656 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001657 }
Ben Wagner5ddb3082018-03-29 11:18:06 -04001658 return true;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001659}
1660
reed@google.com484f5bc2013-04-24 19:14:56 +00001661static void logfont_for_name(const char* familyName, LOGFONT* lf) {
1662 sk_bzero(lf, sizeof(LOGFONT));
bungeman@google.come70f7982012-06-01 19:38:19 +00001663#ifdef UNICODE
reed@google.com484f5bc2013-04-24 19:14:56 +00001664 // Get the buffer size needed first.
1665 size_t str_len = ::MultiByteToWideChar(CP_UTF8, 0, familyName,
halcanary96fcdcc2015-08-27 07:41:13 -07001666 -1, nullptr, 0);
reed@google.com484f5bc2013-04-24 19:14:56 +00001667 // Allocate a buffer (str_len already has terminating null
1668 // accounted for).
1669 wchar_t *wideFamilyName = new wchar_t[str_len];
1670 // Now actually convert the string.
1671 ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1,
1672 wideFamilyName, str_len);
1673 ::wcsncpy(lf->lfFaceName, wideFamilyName, LF_FACESIZE - 1);
1674 delete [] wideFamilyName;
1675 lf->lfFaceName[LF_FACESIZE-1] = L'\0';
bungeman@google.come70f7982012-06-01 19:38:19 +00001676#else
reed@google.com484f5bc2013-04-24 19:14:56 +00001677 ::strncpy(lf->lfFaceName, familyName, LF_FACESIZE - 1);
1678 lf->lfFaceName[LF_FACESIZE - 1] = '\0';
bungeman@google.come70f7982012-06-01 19:38:19 +00001679#endif
1680}
1681
bungemanb374d6a2014-09-17 07:48:59 -07001682void LogFontTypeface::onGetFamilyName(SkString* familyName) const {
bungeman@google.com7103f182012-10-31 20:53:49 +00001683 // Get the actual name of the typeface. The logfont may not know this.
reed@google.com5526ede2013-03-25 13:03:37 +00001684 HFONT font = CreateFontIndirect(&fLogFont);
bungeman@google.com7103f182012-10-31 20:53:49 +00001685
halcanary96fcdcc2015-08-27 07:41:13 -07001686 HDC deviceContext = ::CreateCompatibleDC(nullptr);
bungeman@google.com7103f182012-10-31 20:53:49 +00001687 HFONT savefont = (HFONT)SelectObject(deviceContext, font);
1688
bungemanb374d6a2014-09-17 07:48:59 -07001689 dcfontname_to_skstring(deviceContext, fLogFont, familyName);
bungeman@google.com7103f182012-10-31 20:53:49 +00001690
1691 if (deviceContext) {
1692 ::SelectObject(deviceContext, savefont);
1693 ::DeleteDC(deviceContext);
1694 }
1695 if (font) {
1696 ::DeleteObject(font);
1697 }
bungemanb374d6a2014-09-17 07:48:59 -07001698}
bungeman@google.com7103f182012-10-31 20:53:49 +00001699
bungemanb374d6a2014-09-17 07:48:59 -07001700void LogFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc,
1701 bool* isLocalStream) const {
1702 SkString familyName;
1703 this->onGetFamilyName(&familyName);
reed@google.com5526ede2013-03-25 13:03:37 +00001704 desc->setFamilyName(familyName.c_str());
bungemanb8113782016-07-25 16:54:59 -07001705 desc->setStyle(this->fontStyle());
reed@google.com5526ede2013-03-25 13:03:37 +00001706 *isLocalStream = this->fSerializeAsStream;
reed@google.comac6b9792011-03-11 15:42:51 +00001707}
1708
Hal Canary46cc3da2018-05-09 11:50:34 -04001709void LogFontTypeface::getGlyphToUnicodeMap(SkUnichar* dstArray) const {
1710 HDC hdc = ::CreateCompatibleDC(nullptr);
1711 HFONT font = CreateFontIndirect(&fLogFont);
1712 HFONT savefont = (HFONT)SelectObject(hdc, font);
1713 LOGFONT lf = fLogFont;
1714 HFONT designFont = CreateFontIndirect(&lf);
1715 SelectObject(hdc, designFont);
1716
1717 unsigned int glyphCount = calculateGlyphCount(hdc, fLogFont);
1718 populate_glyph_to_unicode(hdc, glyphCount, dstArray);
1719
1720 SelectObject(hdc, savefont);
1721 DeleteObject(designFont);
1722 DeleteObject(font);
1723 DeleteDC(hdc);
1724}
1725
Hal Canary209e4b12017-05-04 14:23:55 -04001726std::unique_ptr<SkAdvancedTypefaceMetrics> LogFontTypeface::onGetAdvancedMetrics() const {
reed@google.com2689f612013-03-20 20:01:47 +00001727 LOGFONT lf = fLogFont;
Hal Canary209e4b12017-05-04 14:23:55 -04001728 std::unique_ptr<SkAdvancedTypefaceMetrics> info(nullptr);
reed@google.comac6b9792011-03-11 15:42:51 +00001729
halcanary96fcdcc2015-08-27 07:41:13 -07001730 HDC hdc = CreateCompatibleDC(nullptr);
reed@google.comac6b9792011-03-11 15:42:51 +00001731 HFONT font = CreateFontIndirect(&lf);
1732 HFONT savefont = (HFONT)SelectObject(hdc, font);
halcanary96fcdcc2015-08-27 07:41:13 -07001733 HFONT designFont = nullptr;
reed@google.comac6b9792011-03-11 15:42:51 +00001734
reed@google.com05b6f3a2011-11-28 15:30:28 +00001735 const char stem_chars[] = {'i', 'I', '!', '1'};
1736 int16_t min_width;
1737 unsigned glyphCount;
1738
reed@google.comac6b9792011-03-11 15:42:51 +00001739 // To request design units, create a logical font whose height is specified
1740 // as unitsPerEm.
1741 OUTLINETEXTMETRIC otm;
bungeman@google.com39698b12011-11-15 22:26:41 +00001742 unsigned int otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
1743 if (0 == otmRet) {
reed@google.com055180c2013-03-21 18:46:35 +00001744 call_ensure_accessible(lf);
bungeman@google.com39698b12011-11-15 22:26:41 +00001745 otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
1746 }
1747 if (!otmRet || !GetTextFace(hdc, LF_FACESIZE, lf.lfFaceName)) {
reed@google.comac6b9792011-03-11 15:42:51 +00001748 goto Error;
1749 }
1750 lf.lfHeight = -SkToS32(otm.otmEMSquare);
1751 designFont = CreateFontIndirect(&lf);
1752 SelectObject(hdc, designFont);
1753 if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) {
1754 goto Error;
1755 }
bungeman@google.com7bdd6142013-07-15 19:42:57 +00001756 glyphCount = calculateGlyphCount(hdc, fLogFont);
reed@google.comac6b9792011-03-11 15:42:51 +00001757
Hal Canary209e4b12017-05-04 14:23:55 -04001758 info.reset(new SkAdvancedTypefaceMetrics);
bungeman@google.com7103f182012-10-31 20:53:49 +00001759 tchar_to_skstring(lf.lfFaceName, &info->fFontName);
Hal Canarya865d252017-11-09 16:16:09 -05001760
1761 SkOTTableOS2_V4::Type fsType;
1762 if (sizeof(fsType) == this->getTableData(SkTEndian_SwapBE32(SkOTTableOS2::TAG),
1763 offsetof(SkOTTableOS2_V4, fsType),
1764 sizeof(fsType),
1765 &fsType)) {
1766 SkOTUtils::SetAdvancedTypefaceFlags(fsType, info.get());
1767 } else {
1768 // If bit 1 is set, the font may not be embedded in a document.
1769 // If bit 1 is clear, the font can be embedded.
1770 // If bit 2 is set, the embedding is read-only.
1771 if (otm.otmfsType & 0x1) {
1772 info->fFlags |= SkAdvancedTypefaceMetrics::kNotEmbeddable_FontFlag;
1773 }
vandebo0f9bad02014-06-19 11:05:39 -07001774 }
reed@google.comac6b9792011-03-11 15:42:51 +00001775
vandebo@chromium.orgd41e70d2012-03-08 19:41:01 +00001776 if (glyphCount > 0 &&
1777 (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE)) {
reed@google.comac6b9792011-03-11 15:42:51 +00001778 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
1779 } else {
edisonn@google.com390c6d72013-04-06 20:26:15 +00001780 goto ReturnInfo;
reed@google.comac6b9792011-03-11 15:42:51 +00001781 }
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +00001782
reed@google.comac6b9792011-03-11 15:42:51 +00001783 // If this bit is clear the font is a fixed pitch font.
1784 if (!(otm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
1785 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1786 }
1787 if (otm.otmTextMetrics.tmItalic) {
1788 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1789 }
reed@google.comac6b9792011-03-11 15:42:51 +00001790 if (otm.otmTextMetrics.tmPitchAndFamily & FF_ROMAN) {
1791 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1792 } else if (otm.otmTextMetrics.tmPitchAndFamily & FF_SCRIPT) {
1793 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1794 }
1795
1796 // The main italic angle of the font, in tenths of a degree counterclockwise
1797 // from vertical.
1798 info->fItalicAngle = otm.otmItalicAngle / 10;
1799 info->fAscent = SkToS16(otm.otmTextMetrics.tmAscent);
1800 info->fDescent = SkToS16(-otm.otmTextMetrics.tmDescent);
1801 // TODO(ctguil): Use alternate cap height calculation.
1802 // MSDN says otmsCapEmHeight is not support but it is returning a value on
1803 // my Win7 box.
1804 info->fCapHeight = otm.otmsCapEmHeight;
1805 info->fBBox =
1806 SkIRect::MakeLTRB(otm.otmrcFontBox.left, otm.otmrcFontBox.top,
1807 otm.otmrcFontBox.right, otm.otmrcFontBox.bottom);
1808
1809 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1810 // This probably isn't very good with an italic font.
reed@google.com05b6f3a2011-11-28 15:30:28 +00001811 min_width = SHRT_MAX;
reed@google.comac6b9792011-03-11 15:42:51 +00001812 info->fStemV = 0;
reed@google.comac6b9792011-03-11 15:42:51 +00001813 for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {
1814 ABC abcWidths;
1815 if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) {
1816 int16_t width = abcWidths.abcB;
1817 if (width > 0 && width < min_width) {
1818 min_width = width;
1819 info->fStemV = min_width;
1820 }
1821 }
1822 }
1823
reed@google.comac6b9792011-03-11 15:42:51 +00001824Error:
edisonn@google.com390c6d72013-04-06 20:26:15 +00001825ReturnInfo:
reed@google.comac6b9792011-03-11 15:42:51 +00001826 SelectObject(hdc, savefont);
1827 DeleteObject(designFont);
1828 DeleteObject(font);
1829 DeleteDC(hdc);
1830
1831 return info;
1832}
1833
bungeman@google.coma5501992012-05-18 19:06:41 +00001834//Dummy representation of a Base64 encoded GUID from create_unique_font_name.
1835#define BASE64_GUID_ID "XXXXXXXXXXXXXXXXXXXXXXXX"
halcanary96fcdcc2015-08-27 07:41:13 -07001836//Length of GUID representation from create_id, including nullptr terminator.
bungeman@google.coma5501992012-05-18 19:06:41 +00001837#define BASE64_GUID_ID_LEN SK_ARRAY_COUNT(BASE64_GUID_ID)
reed@google.comac6b9792011-03-11 15:42:51 +00001838
bungeman99fe8222015-08-20 07:57:51 -07001839static_assert(BASE64_GUID_ID_LEN < LF_FACESIZE, "GUID_longer_than_facesize");
bungeman@google.coma5501992012-05-18 19:06:41 +00001840
1841/**
1842 NameID 6 Postscript names cannot have the character '/'.
1843 It would be easier to hex encode the GUID, but that is 32 bytes,
1844 and many systems have issues with names longer than 28 bytes.
1845 The following need not be any standard base64 encoding.
1846 The encoded value is never decoded.
1847*/
rmistry@google.comd6176b02012-08-23 18:14:13 +00001848static const char postscript_safe_base64_encode[] =
bungeman@google.coma5501992012-05-18 19:06:41 +00001849 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1850 "abcdefghijklmnopqrstuvwxyz"
1851 "0123456789-_=";
1852
1853/**
1854 Formats a GUID into Base64 and places it into buffer.
1855 buffer should have space for at least BASE64_GUID_ID_LEN characters.
1856 The string will always be null terminated.
1857 XXXXXXXXXXXXXXXXXXXXXXXX0
1858 */
1859static void format_guid_b64(const GUID& guid, char* buffer, size_t bufferSize) {
1860 SkASSERT(bufferSize >= BASE64_GUID_ID_LEN);
1861 size_t written = SkBase64::Encode(&guid, sizeof(guid), buffer, postscript_safe_base64_encode);
1862 SkASSERT(written < LF_FACESIZE);
1863 buffer[written] = '\0';
1864}
1865
1866/**
1867 Creates a Base64 encoded GUID and places it into buffer.
1868 buffer should have space for at least BASE64_GUID_ID_LEN characters.
1869 The string will always be null terminated.
1870 XXXXXXXXXXXXXXXXXXXXXXXX0
1871 */
1872static HRESULT create_unique_font_name(char* buffer, size_t bufferSize) {
1873 GUID guid = {};
1874 if (FAILED(CoCreateGuid(&guid))) {
1875 return E_UNEXPECTED;
1876 }
1877 format_guid_b64(guid, buffer, bufferSize);
1878
1879 return S_OK;
1880}
1881
1882/**
halcanary96fcdcc2015-08-27 07:41:13 -07001883 Introduces a font to GDI. On failure will return nullptr. The returned handle
bungeman@google.coma5501992012-05-18 19:06:41 +00001884 should eventually be passed to RemoveFontMemResourceEx.
1885*/
1886static HANDLE activate_font(SkData* fontData) {
1887 DWORD numFonts = 0;
1888 //AddFontMemResourceEx just copies the data, but does not specify const.
1889 HANDLE fontHandle = AddFontMemResourceEx(const_cast<void*>(fontData->data()),
bungeman@google.com4b18f572013-07-22 15:21:23 +00001890 static_cast<DWORD>(fontData->size()),
bungeman@google.coma5501992012-05-18 19:06:41 +00001891 0,
1892 &numFonts);
1893
halcanary96fcdcc2015-08-27 07:41:13 -07001894 if (fontHandle != nullptr && numFonts < 1) {
bungeman@google.coma5501992012-05-18 19:06:41 +00001895 RemoveFontMemResourceEx(fontHandle);
halcanary96fcdcc2015-08-27 07:41:13 -07001896 return nullptr;
bungeman@google.coma5501992012-05-18 19:06:41 +00001897 }
1898
1899 return fontHandle;
1900}
1901
scroggoa1193e42015-01-21 12:09:53 -08001902// Does not affect ownership of stream.
Mike Reed59227392017-09-26 09:46:08 -04001903static sk_sp<SkTypeface> create_from_stream(std::unique_ptr<SkStreamAsset> stream) {
bungeman@google.coma5501992012-05-18 19:06:41 +00001904 // Create a unique and unpredictable font name.
1905 // Avoids collisions and access from CSS.
1906 char familyName[BASE64_GUID_ID_LEN];
1907 const int familyNameSize = SK_ARRAY_COUNT(familyName);
1908 if (FAILED(create_unique_font_name(familyName, familyNameSize))) {
halcanary96fcdcc2015-08-27 07:41:13 -07001909 return nullptr;
bungeman@google.coma5501992012-05-18 19:06:41 +00001910 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001911
bungeman@google.coma5501992012-05-18 19:06:41 +00001912 // Change the name of the font.
Mike Reed59227392017-09-26 09:46:08 -04001913 sk_sp<SkData> rewrittenFontData(SkOTUtils::RenameFont(stream.get(), familyName, familyNameSize-1));
halcanary96fcdcc2015-08-27 07:41:13 -07001914 if (nullptr == rewrittenFontData.get()) {
1915 return nullptr;
bungeman@google.coma5501992012-05-18 19:06:41 +00001916 }
bungeman@google.coma5501992012-05-18 19:06:41 +00001917
1918 // Register the font with GDI.
bungeman@google.come9bbee32012-05-21 13:46:13 +00001919 HANDLE fontReference = activate_font(rewrittenFontData.get());
halcanary96fcdcc2015-08-27 07:41:13 -07001920 if (nullptr == fontReference) {
1921 return nullptr;
bungeman@google.coma5501992012-05-18 19:06:41 +00001922 }
1923
1924 // Create the typeface.
1925 LOGFONT lf;
reed@google.com484f5bc2013-04-24 19:14:56 +00001926 logfont_for_name(familyName, &lf);
bungeman@google.coma5501992012-05-18 19:06:41 +00001927
Mike Reed59227392017-09-26 09:46:08 -04001928 return sk_sp<SkTypeface>(SkCreateFontMemResourceTypefaceFromLOGFONT(lf, fontReference));
reed@google.comac6b9792011-03-11 15:42:51 +00001929}
1930
bungeman5f213d92015-01-27 05:39:10 -08001931SkStreamAsset* LogFontTypeface::onOpenStream(int* ttcIndex) const {
reed@google.com0042b9c2013-03-21 20:16:04 +00001932 *ttcIndex = 0;
1933
ctguil@chromium.orgf4c26222011-05-16 22:00:05 +00001934 const DWORD kTTCTag =
1935 SkEndian_SwapBE32(SkSetFourByteTag('t', 't', 'c', 'f'));
reed@google.com0042b9c2013-03-21 20:16:04 +00001936 LOGFONT lf = fLogFont;
reed@google.comac6b9792011-03-11 15:42:51 +00001937
halcanary96fcdcc2015-08-27 07:41:13 -07001938 HDC hdc = ::CreateCompatibleDC(nullptr);
reed@google.com59d2f632011-05-02 19:36:59 +00001939 HFONT font = CreateFontIndirect(&lf);
reed@google.comac6b9792011-03-11 15:42:51 +00001940 HFONT savefont = (HFONT)SelectObject(hdc, font);
1941
halcanary96fcdcc2015-08-27 07:41:13 -07001942 SkMemoryStream* stream = nullptr;
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001943 DWORD tables[2] = {kTTCTag, 0};
Chris Dalton1ef80942017-12-04 12:01:30 -07001944 for (size_t i = 0; i < SK_ARRAY_COUNT(tables); i++) {
halcanary96fcdcc2015-08-27 07:41:13 -07001945 DWORD bufferSize = GetFontData(hdc, tables[i], 0, nullptr, 0);
bungeman@google.com39698b12011-11-15 22:26:41 +00001946 if (bufferSize == GDI_ERROR) {
reed@google.com055180c2013-03-21 18:46:35 +00001947 call_ensure_accessible(lf);
halcanary96fcdcc2015-08-27 07:41:13 -07001948 bufferSize = GetFontData(hdc, tables[i], 0, nullptr, 0);
bungeman@google.com39698b12011-11-15 22:26:41 +00001949 }
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001950 if (bufferSize != GDI_ERROR) {
1951 stream = new SkMemoryStream(bufferSize);
bungeman@google.com4b18f572013-07-22 15:21:23 +00001952 if (GetFontData(hdc, tables[i], 0, (void*)stream->getMemoryBase(), bufferSize)) {
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001953 break;
1954 } else {
1955 delete stream;
halcanary96fcdcc2015-08-27 07:41:13 -07001956 stream = nullptr;
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001957 }
1958 }
reed@google.comac6b9792011-03-11 15:42:51 +00001959 }
1960
1961 SelectObject(hdc, savefont);
1962 DeleteObject(font);
1963 DeleteDC(hdc);
1964
1965 return stream;
1966}
1967
bungeman@google.com3c996f82013-10-24 21:39:35 +00001968static void bmpCharsToGlyphs(HDC hdc, const WCHAR* bmpChars, int count, uint16_t* glyphs,
1969 bool Ox1FHack)
1970{
1971 DWORD result = GetGlyphIndicesW(hdc, bmpChars, count, glyphs, GGI_MARK_NONEXISTING_GLYPHS);
1972 if (GDI_ERROR == result) {
1973 for (int i = 0; i < count; ++i) {
1974 glyphs[i] = 0;
1975 }
1976 return;
1977 }
1978
1979 if (Ox1FHack) {
1980 for (int i = 0; i < count; ++i) {
1981 if (0xFFFF == glyphs[i] || 0x1F == glyphs[i]) {
1982 glyphs[i] = 0;
1983 }
1984 }
1985 } else {
1986 for (int i = 0; i < count; ++i) {
1987 if (0xFFFF == glyphs[i]){
1988 glyphs[i] = 0;
1989 }
1990 }
1991 }
1992}
1993
1994static uint16_t nonBmpCharToGlyph(HDC hdc, SCRIPT_CACHE* scriptCache, const WCHAR utf16[2]) {
1995 uint16_t index = 0;
1996 // Use uniscribe to detemine glyph index for non-BMP characters.
1997 static const int numWCHAR = 2;
1998 static const int maxItems = 2;
halcanary96fcdcc2015-08-27 07:41:13 -07001999 // MSDN states that this can be nullptr, but some things don't work then.
Chris Dalton1ef80942017-12-04 12:01:30 -07002000 SCRIPT_CONTROL scriptControl;
2001 memset(&scriptControl, 0, sizeof(scriptControl));
bungeman@google.com3c996f82013-10-24 21:39:35 +00002002 // Add extra item to SCRIPT_ITEM to work around a bug (now documented).
2003 // https://bugzilla.mozilla.org/show_bug.cgi?id=366643
2004 SCRIPT_ITEM si[maxItems + 1];
2005 int numItems;
halcanary96fcdcc2015-08-27 07:41:13 -07002006 HRZM(ScriptItemize(utf16, numWCHAR, maxItems, &scriptControl, nullptr, si, &numItems),
bungeman@google.com3c996f82013-10-24 21:39:35 +00002007 "Could not itemize character.");
2008
2009 // Sometimes ScriptShape cannot find a glyph for a non-BMP and returns 2 space glyphs.
2010 static const int maxGlyphs = 2;
2011 SCRIPT_VISATTR vsa[maxGlyphs];
2012 WORD outGlyphs[maxGlyphs];
2013 WORD logClust[numWCHAR];
2014 int numGlyphs;
2015 HRZM(ScriptShape(hdc, scriptCache, utf16, numWCHAR, maxGlyphs, &si[0].a,
2016 outGlyphs, logClust, vsa, &numGlyphs),
2017 "Could not shape character.");
2018 if (1 == numGlyphs) {
2019 index = outGlyphs[0];
2020 }
2021 return index;
2022}
2023
2024class SkAutoHDC {
2025public:
2026 SkAutoHDC(const LOGFONT& lf)
halcanary96fcdcc2015-08-27 07:41:13 -07002027 : fHdc(::CreateCompatibleDC(nullptr))
bungeman@google.com3c996f82013-10-24 21:39:35 +00002028 , fFont(::CreateFontIndirect(&lf))
2029 , fSavefont((HFONT)SelectObject(fHdc, fFont))
2030 { }
2031 ~SkAutoHDC() {
2032 SelectObject(fHdc, fSavefont);
2033 DeleteObject(fFont);
2034 DeleteDC(fHdc);
2035 }
2036 operator HDC() { return fHdc; }
2037private:
2038 HDC fHdc;
2039 HFONT fFont;
2040 HFONT fSavefont;
2041};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +00002042#define SkAutoHDC(...) SK_REQUIRE_LOCAL_VAR(SkAutoHDC)
bungeman@google.com3c996f82013-10-24 21:39:35 +00002043
2044int LogFontTypeface::onCharsToGlyphs(const void* chars, Encoding encoding,
2045 uint16_t userGlyphs[], int glyphCount) const
2046{
2047 SkAutoHDC hdc(fLogFont);
2048
2049 TEXTMETRIC tm;
2050 if (0 == GetTextMetrics(hdc, &tm)) {
2051 call_ensure_accessible(fLogFont);
2052 if (0 == GetTextMetrics(hdc, &tm)) {
2053 tm.tmPitchAndFamily = TMPF_TRUETYPE;
2054 }
2055 }
2056 bool Ox1FHack = !(tm.tmPitchAndFamily & TMPF_VECTOR) /*&& winVer < Vista */;
2057
2058 SkAutoSTMalloc<256, uint16_t> scratchGlyphs;
2059 uint16_t* glyphs;
halcanary96fcdcc2015-08-27 07:41:13 -07002060 if (userGlyphs != nullptr) {
bungeman@google.com3c996f82013-10-24 21:39:35 +00002061 glyphs = userGlyphs;
2062 } else {
2063 glyphs = scratchGlyphs.reset(glyphCount);
2064 }
2065
2066 SCRIPT_CACHE sc = 0;
2067 switch (encoding) {
2068 case SkTypeface::kUTF8_Encoding: {
2069 static const int scratchCount = 256;
2070 WCHAR scratch[scratchCount];
2071 int glyphIndex = 0;
2072 const char* currentUtf8 = reinterpret_cast<const char*>(chars);
Kevin Lubick42846132018-01-05 10:11:11 -05002073 SkUnichar currentChar = 0;
bungeman@google.com3c996f82013-10-24 21:39:35 +00002074 if (glyphCount) {
2075 currentChar = SkUTF8_NextUnichar(&currentUtf8);
2076 }
2077 while (glyphIndex < glyphCount) {
2078 // Try a run of bmp.
2079 int glyphsLeft = SkTMin(glyphCount - glyphIndex, scratchCount);
2080 int runLength = 0;
2081 while (runLength < glyphsLeft && currentChar <= 0xFFFF) {
2082 scratch[runLength] = static_cast<WCHAR>(currentChar);
2083 ++runLength;
2084 if (runLength < glyphsLeft) {
2085 currentChar = SkUTF8_NextUnichar(&currentUtf8);
2086 }
2087 }
2088 if (runLength) {
2089 bmpCharsToGlyphs(hdc, scratch, runLength, &glyphs[glyphIndex], Ox1FHack);
2090 glyphIndex += runLength;
2091 }
2092
2093 // Try a run of non-bmp.
2094 while (glyphIndex < glyphCount && currentChar > 0xFFFF) {
2095 SkUTF16_FromUnichar(currentChar, reinterpret_cast<uint16_t*>(scratch));
2096 glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, scratch);
2097 ++glyphIndex;
2098 if (glyphIndex < glyphCount) {
2099 currentChar = SkUTF8_NextUnichar(&currentUtf8);
2100 }
2101 }
2102 }
2103 break;
2104 }
2105 case SkTypeface::kUTF16_Encoding: {
2106 int glyphIndex = 0;
2107 const WCHAR* currentUtf16 = reinterpret_cast<const WCHAR*>(chars);
2108 while (glyphIndex < glyphCount) {
2109 // Try a run of bmp.
2110 int glyphsLeft = glyphCount - glyphIndex;
2111 int runLength = 0;
2112 while (runLength < glyphsLeft && !SkUTF16_IsHighSurrogate(currentUtf16[runLength])) {
2113 ++runLength;
2114 }
2115 if (runLength) {
2116 bmpCharsToGlyphs(hdc, currentUtf16, runLength, &glyphs[glyphIndex], Ox1FHack);
2117 glyphIndex += runLength;
2118 currentUtf16 += runLength;
2119 }
2120
2121 // Try a run of non-bmp.
2122 while (glyphIndex < glyphCount && SkUTF16_IsHighSurrogate(*currentUtf16)) {
2123 glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, currentUtf16);
2124 ++glyphIndex;
2125 currentUtf16 += 2;
2126 }
2127 }
2128 break;
2129 }
2130 case SkTypeface::kUTF32_Encoding: {
2131 static const int scratchCount = 256;
2132 WCHAR scratch[scratchCount];
2133 int glyphIndex = 0;
2134 const uint32_t* utf32 = reinterpret_cast<const uint32_t*>(chars);
2135 while (glyphIndex < glyphCount) {
2136 // Try a run of bmp.
2137 int glyphsLeft = SkTMin(glyphCount - glyphIndex, scratchCount);
2138 int runLength = 0;
2139 while (runLength < glyphsLeft && utf32[glyphIndex + runLength] <= 0xFFFF) {
2140 scratch[runLength] = static_cast<WCHAR>(utf32[glyphIndex + runLength]);
2141 ++runLength;
2142 }
2143 if (runLength) {
2144 bmpCharsToGlyphs(hdc, scratch, runLength, &glyphs[glyphIndex], Ox1FHack);
2145 glyphIndex += runLength;
2146 }
2147
2148 // Try a run of non-bmp.
2149 while (glyphIndex < glyphCount && utf32[glyphIndex] > 0xFFFF) {
2150 SkUTF16_FromUnichar(utf32[glyphIndex], reinterpret_cast<uint16_t*>(scratch));
2151 glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, scratch);
2152 ++glyphIndex;
2153 }
2154 }
2155 break;
2156 }
2157 default:
djsollenf2b340f2016-01-29 08:51:04 -08002158 SK_ABORT("Invalid Text Encoding");
bungeman@google.com3c996f82013-10-24 21:39:35 +00002159 }
2160
2161 if (sc) {
2162 ::ScriptFreeCache(&sc);
2163 }
2164
2165 for (int i = 0; i < glyphCount; ++i) {
2166 if (0 == glyphs[i]) {
2167 return i;
2168 }
2169 }
2170 return glyphCount;
2171}
2172
bungeman@google.com7bdd6142013-07-15 19:42:57 +00002173int LogFontTypeface::onCountGlyphs() const {
halcanary96fcdcc2015-08-27 07:41:13 -07002174 HDC hdc = ::CreateCompatibleDC(nullptr);
bungeman@google.com7bdd6142013-07-15 19:42:57 +00002175 HFONT font = CreateFontIndirect(&fLogFont);
2176 HFONT savefont = (HFONT)SelectObject(hdc, font);
2177
2178 unsigned int glyphCount = calculateGlyphCount(hdc, fLogFont);
2179
2180 SelectObject(hdc, savefont);
2181 DeleteObject(font);
2182 DeleteDC(hdc);
2183
2184 return glyphCount;
2185}
2186
2187int LogFontTypeface::onGetUPEM() const {
halcanary96fcdcc2015-08-27 07:41:13 -07002188 HDC hdc = ::CreateCompatibleDC(nullptr);
bungeman@google.com7bdd6142013-07-15 19:42:57 +00002189 HFONT font = CreateFontIndirect(&fLogFont);
2190 HFONT savefont = (HFONT)SelectObject(hdc, font);
2191
2192 unsigned int upem = calculateUPEM(hdc, fLogFont);
2193
2194 SelectObject(hdc, savefont);
2195 DeleteObject(font);
2196 DeleteDC(hdc);
2197
2198 return upem;
2199}
2200
bungeman@google.com839702b2013-08-07 17:09:22 +00002201SkTypeface::LocalizedStrings* LogFontTypeface::onCreateFamilyNameIterator() const {
bungeman@google.coma9802692013-08-07 02:45:25 +00002202 SkTypeface::LocalizedStrings* nameIter =
2203 SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*this);
halcanary96fcdcc2015-08-27 07:41:13 -07002204 if (nullptr == nameIter) {
bungeman@google.coma9802692013-08-07 02:45:25 +00002205 SkString familyName;
2206 this->getFamilyName(&familyName);
2207 SkString language("und"); //undetermined
2208 nameIter = new SkOTUtils::LocalizedStrings_SingleName(familyName, language);
2209 }
2210 return nameIter;
2211}
2212
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002213int LogFontTypeface::onGetTableTags(SkFontTableTag tags[]) const {
2214 SkSFNTHeader header;
2215 if (sizeof(header) != this->onGetTableData(0, 0, sizeof(header), &header)) {
2216 return 0;
2217 }
2218
2219 int numTables = SkEndian_SwapBE16(header.numTables);
2220
2221 if (tags) {
2222 size_t size = numTables * sizeof(SkSFNTHeader::TableDirectoryEntry);
2223 SkAutoSTMalloc<0x20, SkSFNTHeader::TableDirectoryEntry> dir(numTables);
2224 if (size != this->onGetTableData(0, sizeof(header), size, dir.get())) {
2225 return 0;
2226 }
2227
2228 for (int i = 0; i < numTables; ++i) {
2229 tags[i] = SkEndian_SwapBE32(dir[i].tag);
2230 }
2231 }
2232 return numTables;
2233}
2234
2235size_t LogFontTypeface::onGetTableData(SkFontTableTag tag, size_t offset,
2236 size_t length, void* data) const
2237{
2238 LOGFONT lf = fLogFont;
2239
halcanary96fcdcc2015-08-27 07:41:13 -07002240 HDC hdc = ::CreateCompatibleDC(nullptr);
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002241 HFONT font = CreateFontIndirect(&lf);
2242 HFONT savefont = (HFONT)SelectObject(hdc, font);
2243
2244 tag = SkEndian_SwapBE32(tag);
halcanary96fcdcc2015-08-27 07:41:13 -07002245 if (nullptr == data) {
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002246 length = 0;
2247 }
robertphillips@google.com8b169312013-10-15 17:47:36 +00002248 DWORD bufferSize = GetFontData(hdc, tag, (DWORD) offset, data, (DWORD) length);
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002249 if (bufferSize == GDI_ERROR) {
2250 call_ensure_accessible(lf);
robertphillips@google.com8b169312013-10-15 17:47:36 +00002251 bufferSize = GetFontData(hdc, tag, (DWORD) offset, data, (DWORD) length);
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002252 }
2253
2254 SelectObject(hdc, savefont);
2255 DeleteObject(font);
2256 DeleteDC(hdc);
2257
2258 return bufferSize == GDI_ERROR ? 0 : bufferSize;
2259}
2260
reeda9322c22016-04-12 06:47:05 -07002261SkScalerContext* LogFontTypeface::onCreateScalerContext(const SkScalerContextEffects& effects,
2262 const SkDescriptor* desc) const {
bungeman7cfd46a2016-10-20 16:06:52 -04002263 auto ctx = skstd::make_unique<SkScalerContext_GDI>(
2264 sk_ref_sp(const_cast<LogFontTypeface*>(this)), effects, desc);
reed@google.com84e22d82013-07-10 15:38:20 +00002265 if (!ctx->isValid()) {
Ben Wagnerc05b2bf2016-11-03 16:51:26 -04002266 return nullptr;
reed@google.com84e22d82013-07-10 15:38:20 +00002267 }
bungeman7cfd46a2016-10-20 16:06:52 -04002268 return ctx.release();
reed@google.comac6b9792011-03-11 15:42:51 +00002269}
2270
reed@google.com0da48612013-03-19 16:06:52 +00002271void LogFontTypeface::onFilterRec(SkScalerContextRec* rec) const {
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00002272 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
2273 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
2274 {
2275 rec->fMaskFormat = SkMask::kA8_Format;
2276 rec->fFlags |= SkScalerContext::kGenA8FromLCD_Flag;
2277 }
2278
bungeman41078062014-07-07 08:16:37 -07002279 unsigned flagsWeDontSupport = SkScalerContext::kVertical_Flag |
bungeman@google.comf6f56872014-01-23 19:01:36 +00002280 SkScalerContext::kForceAutohinting_Flag |
reed@google.come8fab012011-07-13 15:25:33 +00002281 SkScalerContext::kEmbeddedBitmapText_Flag |
2282 SkScalerContext::kEmbolden_Flag |
2283 SkScalerContext::kLCD_BGROrder_Flag |
2284 SkScalerContext::kLCD_Vertical_Flag;
2285 rec->fFlags &= ~flagsWeDontSupport;
2286
reed@google.come8fab012011-07-13 15:25:33 +00002287 SkPaint::Hinting h = rec->getHinting();
2288 switch (h) {
2289 case SkPaint::kNo_Hinting:
bungeman@google.com0abbff92013-07-27 20:37:56 +00002290 break;
reed@google.come8fab012011-07-13 15:25:33 +00002291 case SkPaint::kSlight_Hinting:
bungeman@google.com0abbff92013-07-27 20:37:56 +00002292 // Only do slight hinting when axis aligned.
bungeman@google.com7c86d8e2013-07-27 21:42:21 +00002293 // TODO: re-enable slight hinting when FontHostTest can pass.
2294 //if (!isAxisAligned(*rec)) {
bungeman@google.com0abbff92013-07-27 20:37:56 +00002295 h = SkPaint::kNo_Hinting;
bungeman@google.com7c86d8e2013-07-27 21:42:21 +00002296 //}
reed@google.come8fab012011-07-13 15:25:33 +00002297 break;
2298 case SkPaint::kNormal_Hinting:
2299 case SkPaint::kFull_Hinting:
bungeman@google.com0abbff92013-07-27 20:37:56 +00002300 // TODO: need to be able to distinguish subpixel positioned glyphs
2301 // and linear metrics.
2302 //rec->fFlags &= ~SkScalerContext::kSubpixelPositioning_Flag;
reed@google.come8fab012011-07-13 15:25:33 +00002303 h = SkPaint::kNormal_Hinting;
2304 break;
2305 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002306 SkDEBUGFAIL("unknown hinting");
reed@google.come8fab012011-07-13 15:25:33 +00002307 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00002308 //TODO: if this is a bitmap font, squash hinting and subpixel.
reed@google.come8fab012011-07-13 15:25:33 +00002309 rec->setHinting(h);
reed@google.comda440672011-07-13 18:02:28 +00002310
reed@google.com9181aa82011-08-05 14:28:31 +00002311// turn this off since GDI might turn A8 into BW! Need a bigger fix.
2312#if 0
reed@google.comda440672011-07-13 18:02:28 +00002313 // Disable LCD when rotated, since GDI's output is ugly
2314 if (isLCD(*rec) && !isAxisAligned(*rec)) {
2315 rec->fMaskFormat = SkMask::kA8_Format;
2316 }
reed@google.com9181aa82011-08-05 14:28:31 +00002317#endif
reed@google.com754e4eb2011-09-26 13:21:39 +00002318
reed@google.com0da48612013-03-19 16:06:52 +00002319 if (!fCanBeLCD && isLCD(*rec)) {
bungeman@google.comcb1bbb32012-10-12 18:48:35 +00002320 rec->fMaskFormat = SkMask::kA8_Format;
2321 rec->fFlags &= ~SkScalerContext::kGenA8FromLCD_Flag;
reed@google.com754e4eb2011-09-26 13:21:39 +00002322 }
reed@google.com754e4eb2011-09-26 13:21:39 +00002323}
reed@google.com070da5e2013-03-27 20:01:49 +00002324
2325///////////////////////////////////////////////////////////////////////////////
2326
2327#include "SkFontMgr.h"
reed@google.com484f5bc2013-04-24 19:14:56 +00002328#include "SkDataTable.h"
2329
bungeman@google.coma9802692013-08-07 02:45:25 +00002330static bool valid_logfont_for_enum(const LOGFONT& lf) {
2331 // TODO: Vector FON is unsupported and should not be listed.
2332 return
2333 // Ignore implicit vertical variants.
2334 lf.lfFaceName[0] && lf.lfFaceName[0] != '@'
2335
2336 // DEFAULT_CHARSET is used to get all fonts, but also implies all
2337 // character sets. Filter assuming all fonts support ANSI_CHARSET.
2338 && ANSI_CHARSET == lf.lfCharSet
2339 ;
reed@google.com484f5bc2013-04-24 19:14:56 +00002340}
2341
bungeman@google.coma9802692013-08-07 02:45:25 +00002342/** An EnumFontFamExProc implementation which interprets builderParam as
2343 * an SkTDArray<ENUMLOGFONTEX>* and appends logfonts which
2344 * pass the valid_logfont_for_enum predicate.
2345 */
2346static int CALLBACK enum_family_proc(const LOGFONT* lf, const TEXTMETRIC*,
2347 DWORD fontType, LPARAM builderParam) {
2348 if (valid_logfont_for_enum(*lf)) {
reed@google.coma65a6812013-05-02 19:47:24 +00002349 SkTDArray<ENUMLOGFONTEX>* array = (SkTDArray<ENUMLOGFONTEX>*)builderParam;
2350 *array->append() = *(ENUMLOGFONTEX*)lf;
reed@google.com484f5bc2013-04-24 19:14:56 +00002351 }
2352 return 1; // non-zero means continue
2353}
2354
reed@google.com484f5bc2013-04-24 19:14:56 +00002355class SkFontStyleSetGDI : public SkFontStyleSet {
2356public:
reed@google.coma65a6812013-05-02 19:47:24 +00002357 SkFontStyleSetGDI(const TCHAR familyName[]) {
bungeman@google.coma9802692013-08-07 02:45:25 +00002358 LOGFONT lf;
2359 sk_bzero(&lf, sizeof(lf));
2360 lf.lfCharSet = DEFAULT_CHARSET;
2361 _tcscpy_s(lf.lfFaceName, familyName);
2362
halcanary96fcdcc2015-08-27 07:41:13 -07002363 HDC hdc = ::CreateCompatibleDC(nullptr);
bungeman@google.coma9802692013-08-07 02:45:25 +00002364 ::EnumFontFamiliesEx(hdc, &lf, enum_family_proc, (LPARAM)&fArray, 0);
reed@google.com484f5bc2013-04-24 19:14:56 +00002365 ::DeleteDC(hdc);
2366 }
2367
mtklein36352bf2015-03-25 18:17:31 -07002368 int count() override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002369 return fArray.count();
2370 }
2371
mtklein36352bf2015-03-25 18:17:31 -07002372 void getStyle(int index, SkFontStyle* fs, SkString* styleName) override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002373 if (fs) {
bungemana4c4a2d2014-10-20 13:33:19 -07002374 *fs = get_style(fArray[index].elfLogFont);
reed@google.com484f5bc2013-04-24 19:14:56 +00002375 }
reed@google.coma65a6812013-05-02 19:47:24 +00002376 if (styleName) {
2377 const ENUMLOGFONTEX& ref = fArray[index];
2378 // For some reason, ENUMLOGFONTEX and LOGFONT disagree on their type in the
2379 // non-unicode version.
2380 // ENUMLOGFONTEX uses BYTE
2381 // LOGFONT uses CHAR
2382 // Here we assert they that the style name is logically the same (size) as
2383 // a TCHAR, so we can use the same converter function.
2384 SkASSERT(sizeof(TCHAR) == sizeof(ref.elfStyle[0]));
2385 tchar_to_skstring((const TCHAR*)ref.elfStyle, styleName);
2386 }
reed@google.com484f5bc2013-04-24 19:14:56 +00002387 }
2388
mtklein36352bf2015-03-25 18:17:31 -07002389 SkTypeface* createTypeface(int index) override {
reed@google.coma65a6812013-05-02 19:47:24 +00002390 return SkCreateTypefaceFromLOGFONT(fArray[index].elfLogFont);
reed@google.com484f5bc2013-04-24 19:14:56 +00002391 }
2392
mtklein36352bf2015-03-25 18:17:31 -07002393 SkTypeface* matchStyle(const SkFontStyle& pattern) override {
bungeman147ea2f2015-11-12 09:50:08 -08002394 return this->matchStyleCSS3(pattern);
reed@google.com484f5bc2013-04-24 19:14:56 +00002395 }
2396
2397private:
reed@google.coma65a6812013-05-02 19:47:24 +00002398 SkTDArray<ENUMLOGFONTEX> fArray;
reed@google.com484f5bc2013-04-24 19:14:56 +00002399};
2400
reed@google.com484f5bc2013-04-24 19:14:56 +00002401class SkFontMgrGDI : public SkFontMgr {
bungeman@google.coma9802692013-08-07 02:45:25 +00002402public:
2403 SkFontMgrGDI() {
reed@google.com484f5bc2013-04-24 19:14:56 +00002404 LOGFONT lf;
2405 sk_bzero(&lf, sizeof(lf));
2406 lf.lfCharSet = DEFAULT_CHARSET;
2407
halcanary96fcdcc2015-08-27 07:41:13 -07002408 HDC hdc = ::CreateCompatibleDC(nullptr);
reed@google.com484f5bc2013-04-24 19:14:56 +00002409 ::EnumFontFamiliesEx(hdc, &lf, enum_family_proc, (LPARAM)&fLogFontArray, 0);
2410 ::DeleteDC(hdc);
2411 }
2412
reed@google.com484f5bc2013-04-24 19:14:56 +00002413protected:
mtklein36352bf2015-03-25 18:17:31 -07002414 int onCountFamilies() const override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002415 return fLogFontArray.count();
2416 }
2417
mtklein36352bf2015-03-25 18:17:31 -07002418 void onGetFamilyName(int index, SkString* familyName) const override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002419 SkASSERT((unsigned)index < (unsigned)fLogFontArray.count());
reed@google.coma65a6812013-05-02 19:47:24 +00002420 tchar_to_skstring(fLogFontArray[index].elfLogFont.lfFaceName, familyName);
reed@google.com484f5bc2013-04-24 19:14:56 +00002421 }
2422
mtklein36352bf2015-03-25 18:17:31 -07002423 SkFontStyleSet* onCreateStyleSet(int index) const override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002424 SkASSERT((unsigned)index < (unsigned)fLogFontArray.count());
halcanary385fe4d2015-08-26 13:07:48 -07002425 return new SkFontStyleSetGDI(fLogFontArray[index].elfLogFont.lfFaceName);
reed@google.com484f5bc2013-04-24 19:14:56 +00002426 }
2427
mtklein36352bf2015-03-25 18:17:31 -07002428 SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
halcanary96fcdcc2015-08-27 07:41:13 -07002429 if (nullptr == familyName) {
reed@google.com484f5bc2013-04-24 19:14:56 +00002430 familyName = ""; // do we need this check???
2431 }
2432 LOGFONT lf;
2433 logfont_for_name(familyName, &lf);
halcanary385fe4d2015-08-26 13:07:48 -07002434 return new SkFontStyleSetGDI(lf.lfFaceName);
reed@google.com484f5bc2013-04-24 19:14:56 +00002435 }
2436
reed@google.com484f5bc2013-04-24 19:14:56 +00002437 virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
mtklein36352bf2015-03-25 18:17:31 -07002438 const SkFontStyle& fontstyle) const override {
reed@google.com437eea12013-04-25 20:40:02 +00002439 // could be in base impl
Hal Canary67b39de2016-11-07 11:47:44 -05002440 sk_sp<SkFontStyleSet> sset(this->matchFamily(familyName));
reed@google.com484f5bc2013-04-24 19:14:56 +00002441 return sset->matchStyle(fontstyle);
2442 }
2443
djsollen33068c12014-11-14 10:52:53 -08002444 virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
2445 const char* bcp47[], int bcp47Count,
mtklein36352bf2015-03-25 18:17:31 -07002446 SkUnichar character) const override {
halcanary96fcdcc2015-08-27 07:41:13 -07002447 return nullptr;
djsollen33068c12014-11-14 10:52:53 -08002448 }
2449
reed@google.com484f5bc2013-04-24 19:14:56 +00002450 virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
mtklein36352bf2015-03-25 18:17:31 -07002451 const SkFontStyle& fontstyle) const override {
reed@google.com437eea12013-04-25 20:40:02 +00002452 // could be in base impl
reed@google.com484f5bc2013-04-24 19:14:56 +00002453 SkString familyName;
2454 ((LogFontTypeface*)familyMember)->getFamilyName(&familyName);
2455 return this->matchFamilyStyle(familyName.c_str(), fontstyle);
2456 }
2457
Mike Reed59227392017-09-26 09:46:08 -04002458 sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,
2459 int ttcIndex) const override {
Ben Wagnerfc497342017-02-24 11:15:26 -05002460 if (ttcIndex != 0) {
2461 return nullptr;
2462 }
Mike Reed59227392017-09-26 09:46:08 -04002463 return create_from_stream(std::move(stream));
reed@google.com484f5bc2013-04-24 19:14:56 +00002464 }
reed@google.com437eea12013-04-25 20:40:02 +00002465
Mike Reed59227392017-09-26 09:46:08 -04002466 sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData> data, int ttcIndex) const override {
reed@google.com437eea12013-04-25 20:40:02 +00002467 // could be in base impl
Mike Reed59227392017-09-26 09:46:08 -04002468 return this->makeFromStream(std::unique_ptr<SkStreamAsset>(new SkMemoryStream(std::move(data))),
2469 ttcIndex);
reed@google.com484f5bc2013-04-24 19:14:56 +00002470 }
reed@google.com437eea12013-04-25 20:40:02 +00002471
Mike Reed59227392017-09-26 09:46:08 -04002472 sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override {
reed@google.com437eea12013-04-25 20:40:02 +00002473 // could be in base impl
Mike Reed59227392017-09-26 09:46:08 -04002474 auto stream = SkStream::MakeFromFile(path);
2475 return stream ? this->makeFromStream(std::move(stream), ttcIndex) : nullptr;
reed@google.com484f5bc2013-04-24 19:14:56 +00002476 }
2477
Mike Reed59227392017-09-26 09:46:08 -04002478 sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const override {
bungeman@google.comf7159bb2013-11-20 15:11:05 +00002479 LOGFONT lf;
halcanary96fcdcc2015-08-27 07:41:13 -07002480 if (nullptr == familyName) {
bungeman@google.comf7159bb2013-11-20 15:11:05 +00002481 lf = get_default_font();
2482 } else {
2483 logfont_for_name(familyName, &lf);
2484 }
bungemana4c4a2d2014-10-20 13:33:19 -07002485
bungeman11a77c62016-04-12 13:45:06 -07002486 lf.lfWeight = style.weight();
bungemanb4bb7d82016-04-27 10:21:04 -07002487 lf.lfItalic = style.slant() == SkFontStyle::kUpright_Slant ? FALSE : TRUE;
Mike Reed59227392017-09-26 09:46:08 -04002488 return sk_sp<SkTypeface>(SkCreateTypefaceFromLOGFONT(lf));
reed@google.com30ddd612013-07-30 17:47:39 +00002489 }
2490
reed@google.com484f5bc2013-04-24 19:14:56 +00002491private:
reed@google.coma65a6812013-05-02 19:47:24 +00002492 SkTDArray<ENUMLOGFONTEX> fLogFontArray;
reed@google.com484f5bc2013-04-24 19:14:56 +00002493};
reed@google.com070da5e2013-03-27 20:01:49 +00002494
reed@google.com30ddd612013-07-30 17:47:39 +00002495///////////////////////////////////////////////////////////////////////////////
2496
Ben Wagner3546ff12017-01-03 13:32:36 -05002497sk_sp<SkFontMgr> SkFontMgr_New_GDI() { return sk_make_sp<SkFontMgrGDI>(); }
mtklein1ee76512015-11-02 10:20:27 -08002498
Mike Klein8f11d4d2018-01-24 12:42:55 -05002499#endif//defined(SK_BUILD_FOR_WIN)