blob: f0eb7b7d9b3d74840ac1297a0e3aee510b605907 [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 Canary209e4b12017-05-04 14:23:55 -0400264 std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
mtklein36352bf2015-03-25 18:17:31 -0700265 void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
Ben Wagnerfc497342017-02-24 11:15:26 -0500266 int onCharsToGlyphs(const void* chars, Encoding encoding,
267 uint16_t glyphs[], int glyphCount) const override;
mtklein36352bf2015-03-25 18:17:31 -0700268 int onCountGlyphs() const override;
269 int onGetUPEM() const override;
270 void onGetFamilyName(SkString* familyName) const override;
271 SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
Ben Wagnerfc497342017-02-24 11:15:26 -0500272 int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],
273 int coordinateCount) const override
274 {
275 return -1;
276 }
mtklein36352bf2015-03-25 18:17:31 -0700277 int onGetTableTags(SkFontTableTag tags[]) const override;
Ben Wagnerfc497342017-02-24 11:15:26 -0500278 size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override;
reed@google.comac6b9792011-03-11 15:42:51 +0000279};
280
bungeman@google.coma5501992012-05-18 19:06:41 +0000281class FontMemResourceTypeface : public LogFontTypeface {
282public:
283 /**
bungeman@google.coma5501992012-05-18 19:06:41 +0000284 * The created FontMemResourceTypeface takes ownership of fontMemResource.
285 */
286 static FontMemResourceTypeface* Create(const LOGFONT& lf, HANDLE fontMemResource) {
bungemana4c4a2d2014-10-20 13:33:19 -0700287 return new FontMemResourceTypeface(get_style(lf), lf, fontMemResource);
bungeman@google.coma5501992012-05-18 19:06:41 +0000288 }
289
290protected:
mtklein36352bf2015-03-25 18:17:31 -0700291 void weak_dispose() const override {
bungeman@google.coma5501992012-05-18 19:06:41 +0000292 RemoveFontMemResourceEx(fFontMemResource);
293 //SkTypefaceCache::Remove(this);
294 INHERITED::weak_dispose();
295 }
296
297private:
bungeman@google.com72cf4fc2014-03-21 22:48:32 +0000298 /**
299 * Takes ownership of fontMemResource.
300 */
bungemana4c4a2d2014-10-20 13:33:19 -0700301 FontMemResourceTypeface(const SkFontStyle& style, const LOGFONT& lf, HANDLE fontMemResource)
302 : LogFontTypeface(style, lf, true), fFontMemResource(fontMemResource)
303 { }
bungeman@google.com72cf4fc2014-03-21 22:48:32 +0000304
305 HANDLE fFontMemResource;
306
bungeman@google.coma5501992012-05-18 19:06:41 +0000307 typedef LogFontTypeface INHERITED;
308};
309
reed@google.comac6b9792011-03-11 15:42:51 +0000310static const LOGFONT& get_default_font() {
311 static LOGFONT gDefaultFont;
reed@google.comac6b9792011-03-11 15:42:51 +0000312 return gDefaultFont;
313}
314
bungeman82a455f2016-04-14 08:04:45 -0700315static bool FindByLogFont(SkTypeface* face, void* ctx) {
bungeman@google.coma5501992012-05-18 19:06:41 +0000316 LogFontTypeface* lface = static_cast<LogFontTypeface*>(face);
reed@google.com59d2f632011-05-02 19:36:59 +0000317 const LOGFONT* lf = reinterpret_cast<const LOGFONT*>(ctx);
reed@google.comac6b9792011-03-11 15:42:51 +0000318
bungeman82a455f2016-04-14 08:04:45 -0700319 return !memcmp(&lface->fLogFont, lf, sizeof(LOGFONT));
reed@google.com59d2f632011-05-02 19:36:59 +0000320}
321
322/**
323 * This guy is public. It first searches the cache, and if a match is not found,
324 * it creates a new face.
325 */
326SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& origLF) {
327 LOGFONT lf = origLF;
328 make_canonical(&lf);
bungeman@google.comee51d1a2012-02-16 12:40:48 +0000329 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByLogFont, &lf);
halcanary96fcdcc2015-08-27 07:41:13 -0700330 if (nullptr == face) {
reed@google.com59d2f632011-05-02 19:36:59 +0000331 face = LogFontTypeface::Create(lf);
bungeman82a455f2016-04-14 08:04:45 -0700332 SkTypefaceCache::Add(face);
reed@google.com59d2f632011-05-02 19:36:59 +0000333 }
334 return face;
reed@google.comac6b9792011-03-11 15:42:51 +0000335}
336
reed@google.comdb77a6a2011-07-19 19:08:33 +0000337/**
bungeman@google.coma5501992012-05-18 19:06:41 +0000338 * The created SkTypeface takes ownership of fontMemResource.
339 */
340SkTypeface* SkCreateFontMemResourceTypefaceFromLOGFONT(const LOGFONT& origLF, HANDLE fontMemResource) {
341 LOGFONT lf = origLF;
342 make_canonical(&lf);
mtklein60b6e9d2014-10-24 10:43:15 -0700343 // We'll never get a cache hit, so no point in putting this in SkTypefaceCache.
344 return FontMemResourceTypeface::Create(lf, fontMemResource);
bungeman@google.coma5501992012-05-18 19:06:41 +0000345}
346
347/**
reed@google.comdb77a6a2011-07-19 19:08:33 +0000348 * This guy is public
349 */
350void SkLOGFONTFromTypeface(const SkTypeface* face, LOGFONT* lf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700351 if (nullptr == face) {
reed@google.comdb77a6a2011-07-19 19:08:33 +0000352 *lf = get_default_font();
353 } else {
bungeman@google.coma5501992012-05-18 19:06:41 +0000354 *lf = static_cast<const LogFontTypeface*>(face)->fLogFont;
reed@google.comdb77a6a2011-07-19 19:08:33 +0000355 }
356}
357
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000358// Construct Glyph to Unicode table.
359// Unicode code points that require conjugate pairs in utf16 are not
360// supported.
361// TODO(arthurhsu): Add support for conjugate pairs. It looks like that may
362// require parsing the TTF cmap table (platform 4, encoding 12) directly instead
363// of calling GetFontUnicodeRange().
364static void populate_glyph_to_unicode(HDC fontHdc, const unsigned glyphCount,
365 SkTDArray<SkUnichar>* glyphToUnicode) {
halcanary96fcdcc2015-08-27 07:41:13 -0700366 DWORD glyphSetBufferSize = GetFontUnicodeRanges(fontHdc, nullptr);
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000367 if (!glyphSetBufferSize) {
368 return;
369 }
370
Ben Wagner7ecc5962016-11-02 17:07:33 -0400371 std::unique_ptr<BYTE[]> glyphSetBuffer(new BYTE[glyphSetBufferSize]);
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000372 GLYPHSET* glyphSet =
373 reinterpret_cast<LPGLYPHSET>(glyphSetBuffer.get());
374 if (GetFontUnicodeRanges(fontHdc, glyphSet) != glyphSetBufferSize) {
375 return;
376 }
377
378 glyphToUnicode->setCount(glyphCount);
379 memset(glyphToUnicode->begin(), 0, glyphCount * sizeof(SkUnichar));
380 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) {
402 if (glyph[j] != 0xffff && glyph[j] < glyphCount &&
403 (*glyphToUnicode)[glyph[j]] == 0) {
404 (*glyphToUnicode)[glyph[j]] = chars[j];
405 }
406 }
407 }
408}
409
reed@google.com99edd432011-09-09 14:59:59 +0000410//////////////////////////////////////////////////////////////////////////////////////
411
412static int alignTo32(int n) {
413 return (n + 31) & ~31;
414}
415
416struct MyBitmapInfo : public BITMAPINFO {
417 RGBQUAD fMoreSpaceForColors[1];
418};
419
420class HDCOffscreen {
421public:
422 HDCOffscreen() {
423 fFont = 0;
424 fDC = 0;
425 fBM = 0;
halcanary96fcdcc2015-08-27 07:41:13 -0700426 fBits = nullptr;
reed@google.com99edd432011-09-09 14:59:59 +0000427 fWidth = fHeight = 0;
428 fIsBW = false;
429 }
430
431 ~HDCOffscreen() {
432 if (fDC) {
433 DeleteDC(fDC);
434 }
435 if (fBM) {
436 DeleteObject(fBM);
437 }
438 }
439
440 void init(HFONT font, const XFORM& xform) {
441 fFont = font;
442 fXform = xform;
443 }
444
bungeman@google.com97efada2012-07-30 20:40:50 +0000445 const void* draw(const SkGlyph&, bool isBW, size_t* srcRBPtr);
reed@google.com99edd432011-09-09 14:59:59 +0000446
447private:
448 HDC fDC;
449 HBITMAP fBM;
450 HFONT fFont;
451 XFORM fXform;
452 void* fBits; // points into fBM
453 int fWidth;
454 int fHeight;
455 bool fIsBW;
456};
457
reed@google.com754e4eb2011-09-26 13:21:39 +0000458const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW,
bungeman@google.com97efada2012-07-30 20:40:50 +0000459 size_t* srcRBPtr) {
reed@google.com84e22d82013-07-10 15:38:20 +0000460 // Can we share the scalercontext's fDDC, so we don't need to create
461 // a separate fDC here?
reed@google.com99edd432011-09-09 14:59:59 +0000462 if (0 == fDC) {
463 fDC = CreateCompatibleDC(0);
464 if (0 == fDC) {
halcanary96fcdcc2015-08-27 07:41:13 -0700465 return nullptr;
reed@google.com99edd432011-09-09 14:59:59 +0000466 }
467 SetGraphicsMode(fDC, GM_ADVANCED);
468 SetBkMode(fDC, TRANSPARENT);
469 SetTextAlign(fDC, TA_LEFT | TA_BASELINE);
470 SelectObject(fDC, fFont);
bungeman@google.com97efada2012-07-30 20:40:50 +0000471
472 COLORREF color = 0x00FFFFFF;
bsalomon@google.comb58a6392013-03-21 20:29:05 +0000473 SkDEBUGCODE(COLORREF prev =) SetTextColor(fDC, color);
bungeman@google.com97efada2012-07-30 20:40:50 +0000474 SkASSERT(prev != CLR_INVALID);
reed@google.com99edd432011-09-09 14:59:59 +0000475 }
476
477 if (fBM && (fIsBW != isBW || fWidth < glyph.fWidth || fHeight < glyph.fHeight)) {
478 DeleteObject(fBM);
479 fBM = 0;
480 }
reed@google.com754e4eb2011-09-26 13:21:39 +0000481 fIsBW = isBW;
reed@google.com99edd432011-09-09 14:59:59 +0000482
reed@google.com99edd432011-09-09 14:59:59 +0000483 fWidth = SkMax32(fWidth, glyph.fWidth);
484 fHeight = SkMax32(fHeight, glyph.fHeight);
485
486 int biWidth = isBW ? alignTo32(fWidth) : fWidth;
487
488 if (0 == fBM) {
489 MyBitmapInfo info;
490 sk_bzero(&info, sizeof(info));
491 if (isBW) {
492 RGBQUAD blackQuad = { 0, 0, 0, 0 };
493 RGBQUAD whiteQuad = { 0xFF, 0xFF, 0xFF, 0 };
494 info.bmiColors[0] = blackQuad;
495 info.bmiColors[1] = whiteQuad;
496 }
497 info.bmiHeader.biSize = sizeof(info.bmiHeader);
498 info.bmiHeader.biWidth = biWidth;
499 info.bmiHeader.biHeight = fHeight;
500 info.bmiHeader.biPlanes = 1;
501 info.bmiHeader.biBitCount = isBW ? 1 : 32;
502 info.bmiHeader.biCompression = BI_RGB;
503 if (isBW) {
504 info.bmiHeader.biClrUsed = 2;
505 }
506 fBM = CreateDIBSection(fDC, &info, DIB_RGB_COLORS, &fBits, 0, 0);
507 if (0 == fBM) {
halcanary96fcdcc2015-08-27 07:41:13 -0700508 return nullptr;
reed@google.com99edd432011-09-09 14:59:59 +0000509 }
510 SelectObject(fDC, fBM);
511 }
512
513 // erase
514 size_t srcRB = isBW ? (biWidth >> 3) : (fWidth << 2);
515 size_t size = fHeight * srcRB;
bungeman@google.com97efada2012-07-30 20:40:50 +0000516 memset(fBits, 0, size);
reed@google.com99edd432011-09-09 14:59:59 +0000517
518 XFORM xform = fXform;
519 xform.eDx = (float)-glyph.fLeft;
520 xform.eDy = (float)-glyph.fTop;
521 SetWorldTransform(fDC, &xform);
522
523 uint16_t glyphID = glyph.getGlyphID();
halcanary96fcdcc2015-08-27 07:41:13 -0700524 BOOL ret = ExtTextOutW(fDC, 0, 0, ETO_GLYPH_INDEX, nullptr, reinterpret_cast<LPCWSTR>(&glyphID), 1, nullptr);
reed@google.com99edd432011-09-09 14:59:59 +0000525 GdiFlush();
bungeman@google.com39698b12011-11-15 22:26:41 +0000526 if (0 == ret) {
halcanary96fcdcc2015-08-27 07:41:13 -0700527 return nullptr;
bungeman@google.com39698b12011-11-15 22:26:41 +0000528 }
reed@google.com99edd432011-09-09 14:59:59 +0000529 *srcRBPtr = srcRB;
530 // offset to the start of the image
531 return (const char*)fBits + (fHeight - glyph.fHeight) * srcRB;
532}
533
reed@google.comb8a5c612012-06-13 20:01:44 +0000534//////////////////////////////////////////////////////////////////////////////
bungeman@google.com0abbff92013-07-27 20:37:56 +0000535#define BUFFERSIZE (1 << 13)
reed@google.com59d2f632011-05-02 19:36:59 +0000536
reed@google.com30ddd612013-07-30 17:47:39 +0000537class SkScalerContext_GDI : public SkScalerContext {
reed@google.comac6b9792011-03-11 15:42:51 +0000538public:
bungeman7cfd46a2016-10-20 16:06:52 -0400539 SkScalerContext_GDI(sk_sp<LogFontTypeface>,
540 const SkScalerContextEffects&,
541 const SkDescriptor* desc);
Chris Dalton1ef80942017-12-04 12:01:30 -0700542 ~SkScalerContext_GDI() override;
reed@google.comac6b9792011-03-11 15:42:51 +0000543
reed@google.com84e22d82013-07-10 15:38:20 +0000544 // Returns true if the constructor was able to complete all of its
545 // initializations (which may include calling GDI).
546 bool isValid() const;
547
reed@google.comac6b9792011-03-11 15:42:51 +0000548protected:
mtklein36352bf2015-03-25 18:17:31 -0700549 unsigned generateGlyphCount() override;
550 uint16_t generateCharToGlyph(SkUnichar uni) override;
551 void generateAdvance(SkGlyph* glyph) override;
552 void generateMetrics(SkGlyph* glyph) override;
553 void generateImage(const SkGlyph& glyph) override;
Ben Wagner6e9ac122016-11-11 14:31:06 -0500554 void generatePath(SkGlyphID glyph, SkPath* path) override;
mtklein36352bf2015-03-25 18:17:31 -0700555 void generateFontMetrics(SkPaint::FontMetrics*) override;
reed@google.com99edd432011-09-09 14:59:59 +0000556
reed@google.comac6b9792011-03-11 15:42:51 +0000557private:
Ben Wagner6e9ac122016-11-11 14:31:06 -0500558 DWORD getGDIGlyphPath(SkGlyphID glyph, UINT flags,
bungeman@google.com0abbff92013-07-27 20:37:56 +0000559 SkAutoSTMalloc<BUFFERSIZE, uint8_t>* glyphbuf);
560
reed@google.com99edd432011-09-09 14:59:59 +0000561 HDCOffscreen fOffscreen;
bungeman@google.com0abbff92013-07-27 20:37:56 +0000562 /** fGsA is the non-rotational part of total matrix without the text height scale.
563 * Used to find the magnitude of advances.
564 */
565 MAT2 fGsA;
bungeman@google.com6a774a12013-07-30 01:07:48 +0000566 /** The total matrix without the textSize. */
reed@google.comac6b9792011-03-11 15:42:51 +0000567 MAT2 fMat22;
bungeman@google.com6a774a12013-07-30 01:07:48 +0000568 /** Scales font to EM size. */
569 MAT2 fHighResMat22;
reed@google.comac6b9792011-03-11 15:42:51 +0000570 HDC fDDC;
571 HFONT fSavefont;
572 HFONT fFont;
573 SCRIPT_CACHE fSC;
574 int fGlyphCount;
reed@google.com1dd17a12011-05-17 14:04:41 +0000575
bungeman@google.com6a774a12013-07-30 01:07:48 +0000576 /** The total matrix which also removes EM scale. */
reed@google.com1dd17a12011-05-17 14:04:41 +0000577 SkMatrix fHiResMatrix;
bungeman@google.com0abbff92013-07-27 20:37:56 +0000578 /** fG_inv is the inverse of the rotational part of the total matrix.
579 * Used to set the direction of advances.
580 */
581 SkMatrix fG_inv;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000582 enum Type {
bungeman@google.com4732df62014-01-23 15:22:42 +0000583 kTrueType_Type, kBitmap_Type, kLine_Type
bungeman@google.coma0319f62012-04-18 15:40:50 +0000584 } fType;
585 TEXTMETRIC fTM;
reed@google.comac6b9792011-03-11 15:42:51 +0000586};
587
reed@google.comac6b9792011-03-11 15:42:51 +0000588static FIXED float2FIXED(float x) {
589 return SkFixedToFIXED(SkFloatToFixed(x));
590}
591
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700592static inline float FIXED2float(FIXED x) {
593 return SkFixedToFloat(SkFIXEDToFixed(x));
594}
595
Ben Wagner3e45a2f2017-11-01 11:47:39 -0400596static BYTE compute_quality(const SkScalerContextRec& rec) {
reed@google.com82a34d82011-07-26 19:33:08 +0000597 switch (rec.fMaskFormat) {
598 case SkMask::kBW_Format:
599 return NONANTIALIASED_QUALITY;
600 case SkMask::kLCD16_Format:
reed@google.com82a34d82011-07-26 19:33:08 +0000601 return CLEARTYPE_QUALITY;
602 default:
reed@google.com8351aab2012-01-18 17:06:35 +0000603 if (rec.fFlags & SkScalerContext::kGenA8FromLCD_Flag) {
604 return CLEARTYPE_QUALITY;
605 } else {
606 return ANTIALIASED_QUALITY;
607 }
reed@google.com82a34d82011-07-26 19:33:08 +0000608 }
609}
610
bungeman7cfd46a2016-10-20 16:06:52 -0400611SkScalerContext_GDI::SkScalerContext_GDI(sk_sp<LogFontTypeface> rawTypeface,
reeda9322c22016-04-12 06:47:05 -0700612 const SkScalerContextEffects& effects,
613 const SkDescriptor* desc)
bungeman7cfd46a2016-10-20 16:06:52 -0400614 : SkScalerContext(std::move(rawTypeface), effects, desc)
reed@google.com055180c2013-03-21 18:46:35 +0000615 , fDDC(0)
reed@google.com055180c2013-03-21 18:46:35 +0000616 , fSavefont(0)
reed@google.com84e22d82013-07-10 15:38:20 +0000617 , fFont(0)
reed@google.com055180c2013-03-21 18:46:35 +0000618 , fSC(0)
bungeman@google.com05a729f2013-06-20 15:29:16 +0000619 , fGlyphCount(-1)
620{
bungeman7cfd46a2016-10-20 16:06:52 -0400621 LogFontTypeface* typeface = static_cast<LogFontTypeface*>(this->getTypeface());
reed@google.com055180c2013-03-21 18:46:35 +0000622
halcanary96fcdcc2015-08-27 07:41:13 -0700623 fDDC = ::CreateCompatibleDC(nullptr);
reed@google.com84e22d82013-07-10 15:38:20 +0000624 if (!fDDC) {
625 return;
626 }
reed@google.com1dd17a12011-05-17 14:04:41 +0000627 SetGraphicsMode(fDDC, GM_ADVANCED);
reed@google.comac6b9792011-03-11 15:42:51 +0000628 SetBkMode(fDDC, TRANSPARENT);
skia.committer@gmail.com27e21fe2013-07-28 07:01:03 +0000629
bungeman6f940762015-03-18 08:25:43 -0700630 // When GDI hinting, remove the entire Y scale from sA and GsA. (Prevents 'linear' metrics.)
631 // When not hinting, remove only the integer Y scale from sA and GsA. (Applied by GDI.)
bungeman5f14c5e2014-12-05 12:26:44 -0800632 SkScalerContextRec::PreMatrixScale scaleConstraints =
633 (fRec.getHinting() == SkPaint::kNo_Hinting || fRec.getHinting() == SkPaint::kSlight_Hinting)
634 ? SkScalerContextRec::kVerticalInteger_PreMatrixScale
635 : SkScalerContextRec::kVertical_PreMatrixScale;
636 SkVector scale;
637 SkMatrix sA;
638 SkMatrix GsA;
639 SkMatrix A;
640 fRec.computeMatrices(scaleConstraints, &scale, &sA, &GsA, &fG_inv, &A);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000641
642 fGsA.eM11 = SkScalarToFIXED(GsA.get(SkMatrix::kMScaleX));
643 fGsA.eM12 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewY)); // This should be ~0.
644 fGsA.eM21 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewX));
645 fGsA.eM22 = SkScalarToFIXED(GsA.get(SkMatrix::kMScaleY));
646
bungeman6f940762015-03-18 08:25:43 -0700647 // When not hinting, scale was computed with kVerticalInteger, so is already an integer.
648 // The sA and GsA transforms will be used to create 'linear' metrics.
649
650 // When hinting, scale was computed with kVertical, stating that our port can handle
651 // non-integer scales. This is done so that sA and GsA are computed without any 'residual'
652 // scale in them, preventing 'linear' metrics. However, GDI cannot actually handle non-integer
653 // scales so we need to round in this case. This is fine, since all of the scale has been
654 // removed from sA and GsA, so GDI will be handling the scale completely.
655 SkScalar gdiTextSize = SkScalarRoundToScalar(scale.fY);
656
657 // GDI will not accept a size of zero, so round the range [0, 1] to 1.
658 // If the size was non-zero, the scale factors will also be non-zero and 1px tall text is drawn.
659 // If the size actually was zero, the scale factors will also be zero, so GDI will draw nothing.
bungeman5f14c5e2014-12-05 12:26:44 -0800660 if (gdiTextSize == 0) {
661 gdiTextSize = SK_Scalar1;
662 }
bungeman@google.com0abbff92013-07-27 20:37:56 +0000663
reed@google.com055180c2013-03-21 18:46:35 +0000664 LOGFONT lf = typeface->fLogFont;
bungeman@google.com11ba3192013-10-03 20:17:51 +0000665 lf.lfHeight = -SkScalarTruncToInt(gdiTextSize);
reed@google.com82a34d82011-07-26 19:33:08 +0000666 lf.lfQuality = compute_quality(fRec);
reed@google.comac6b9792011-03-11 15:42:51 +0000667 fFont = CreateFontIndirect(&lf);
reed@google.com84e22d82013-07-10 15:38:20 +0000668 if (!fFont) {
669 return;
670 }
reed@google.com1dd17a12011-05-17 14:04:41 +0000671
reed@google.comac6b9792011-03-11 15:42:51 +0000672 fSavefont = (HFONT)SelectObject(fDDC, fFont);
reed@google.coma767fa02011-08-05 21:40:26 +0000673
bungeman@google.coma0319f62012-04-18 15:40:50 +0000674 if (0 == GetTextMetrics(fDDC, &fTM)) {
reed@google.com055180c2013-03-21 18:46:35 +0000675 call_ensure_accessible(lf);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000676 if (0 == GetTextMetrics(fDDC, &fTM)) {
677 fTM.tmPitchAndFamily = TMPF_TRUETYPE;
678 }
679 }
bungeman@google.com90b7e382012-04-20 15:26:28 +0000680
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000681 XFORM xform;
bungeman@google.com90b7e382012-04-20 15:26:28 +0000682 if (fTM.tmPitchAndFamily & TMPF_VECTOR) {
bungeman@google.com4732df62014-01-23 15:22:42 +0000683 // Used a logfont on a memory context, should never get a device font.
684 // Therefore all TMPF_DEVICE will be PostScript fonts.
685
686 // If TMPF_VECTOR is set, one of TMPF_TRUETYPE or TMPF_DEVICE means that
687 // we have an outline font. Otherwise we have a vector FON, which is
688 // scalable, but not an outline font.
689 // This was determined by testing with Type1 PFM/PFB and
690 // OpenTypeCFF OTF, as well as looking at Wine bugs and sources.
691 if (fTM.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_DEVICE)) {
692 // Truetype or PostScript.
693 fType = SkScalerContext_GDI::kTrueType_Type;
694 } else {
695 // Stroked FON.
696 fType = SkScalerContext_GDI::kLine_Type;
697 }
bungeman@google.coma0319f62012-04-18 15:40:50 +0000698
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000699 // fPost2x2 is column-major, left handed (y down).
700 // XFORM 2x2 is row-major, left handed (y down).
bungeman@google.com0abbff92013-07-27 20:37:56 +0000701 xform.eM11 = SkScalarToFloat(sA.get(SkMatrix::kMScaleX));
702 xform.eM12 = SkScalarToFloat(sA.get(SkMatrix::kMSkewY));
703 xform.eM21 = SkScalarToFloat(sA.get(SkMatrix::kMSkewX));
704 xform.eM22 = SkScalarToFloat(sA.get(SkMatrix::kMScaleY));
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000705 xform.eDx = 0;
706 xform.eDy = 0;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000707
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000708 // MAT2 is row major, right handed (y up).
709 fMat22.eM11 = float2FIXED(xform.eM11);
710 fMat22.eM12 = float2FIXED(-xform.eM12);
711 fMat22.eM21 = float2FIXED(-xform.eM21);
712 fMat22.eM22 = float2FIXED(xform.eM22);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000713
714 if (needToRenderWithSkia(fRec)) {
715 this->forceGenerateImageFromPath();
716 }
717
bungeman@google.com11ba3192013-10-03 20:17:51 +0000718 // Create a hires matrix if we need linear metrics.
bungeman@google.com0abbff92013-07-27 20:37:56 +0000719 if (this->isSubpixel()) {
720 OUTLINETEXTMETRIC otm;
721 UINT success = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
722 if (0 == success) {
723 call_ensure_accessible(lf);
724 success = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
725 }
726 if (0 != success) {
bungeman@google.com11ba3192013-10-03 20:17:51 +0000727 SkScalar upem = SkIntToScalar(otm.otmEMSquare);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000728
bungeman@google.com11ba3192013-10-03 20:17:51 +0000729 SkScalar gdiTextSizeToEMScale = upem / gdiTextSize;
730 fHighResMat22.eM11 = float2FIXED(gdiTextSizeToEMScale);
bungeman@google.com6a774a12013-07-30 01:07:48 +0000731 fHighResMat22.eM12 = float2FIXED(0);
732 fHighResMat22.eM21 = float2FIXED(0);
bungeman@google.com11ba3192013-10-03 20:17:51 +0000733 fHighResMat22.eM22 = float2FIXED(gdiTextSizeToEMScale);
bungeman@google.com6a774a12013-07-30 01:07:48 +0000734
bungeman@google.com11ba3192013-10-03 20:17:51 +0000735 SkScalar removeEMScale = SkScalarInvert(upem);
bungeman@google.com6a774a12013-07-30 01:07:48 +0000736 fHiResMatrix = A;
bungeman@google.com11ba3192013-10-03 20:17:51 +0000737 fHiResMatrix.preScale(removeEMScale, removeEMScale);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000738 }
739 }
740
bungeman@google.coma0319f62012-04-18 15:40:50 +0000741 } else {
742 // Assume bitmap
reed@google.com30ddd612013-07-30 17:47:39 +0000743 fType = SkScalerContext_GDI::kBitmap_Type;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000744
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000745 xform.eM11 = 1.0f;
746 xform.eM12 = 0.0f;
747 xform.eM21 = 0.0f;
748 xform.eM22 = 1.0f;
749 xform.eDx = 0.0f;
750 xform.eDy = 0.0f;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000751
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000752 // fPost2x2 is column-major, left handed (y down).
753 // MAT2 is row major, right handed (y up).
bungeman@google.coma0319f62012-04-18 15:40:50 +0000754 fMat22.eM11 = SkScalarToFIXED(fRec.fPost2x2[0][0]);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000755 fMat22.eM12 = SkScalarToFIXED(-fRec.fPost2x2[1][0]);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000756 fMat22.eM21 = SkScalarToFIXED(-fRec.fPost2x2[0][1]);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000757 fMat22.eM22 = SkScalarToFIXED(fRec.fPost2x2[1][1]);
reed@google.coma767fa02011-08-05 21:40:26 +0000758 }
reed@google.com99edd432011-09-09 14:59:59 +0000759
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000760 fOffscreen.init(fFont, xform);
reed@google.comac6b9792011-03-11 15:42:51 +0000761}
762
reed@google.com30ddd612013-07-30 17:47:39 +0000763SkScalerContext_GDI::~SkScalerContext_GDI() {
reed@google.comac6b9792011-03-11 15:42:51 +0000764 if (fDDC) {
765 ::SelectObject(fDDC, fSavefont);
766 ::DeleteDC(fDDC);
767 }
768 if (fFont) {
769 ::DeleteObject(fFont);
770 }
771 if (fSC) {
772 ::ScriptFreeCache(&fSC);
773 }
774}
775
reed@google.com30ddd612013-07-30 17:47:39 +0000776bool SkScalerContext_GDI::isValid() const {
reed@google.com84e22d82013-07-10 15:38:20 +0000777 return fDDC && fFont;
778}
779
reed@google.com30ddd612013-07-30 17:47:39 +0000780unsigned SkScalerContext_GDI::generateGlyphCount() {
reed@google.comac6b9792011-03-11 15:42:51 +0000781 if (fGlyphCount < 0) {
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000782 fGlyphCount = calculateGlyphCount(
783 fDDC, static_cast<const LogFontTypeface*>(this->getTypeface())->fLogFont);
reed@google.comac6b9792011-03-11 15:42:51 +0000784 }
785 return fGlyphCount;
786}
787
bungeman@google.com33346482013-08-27 19:05:32 +0000788uint16_t SkScalerContext_GDI::generateCharToGlyph(SkUnichar utf32) {
reed@google.comac6b9792011-03-11 15:42:51 +0000789 uint16_t index = 0;
bungeman@google.com33346482013-08-27 19:05:32 +0000790 WCHAR utf16[2];
reed@google.comac6b9792011-03-11 15:42:51 +0000791 // TODO(ctguil): Support characters that generate more than one glyph.
bungeman@google.com33346482013-08-27 19:05:32 +0000792 if (SkUTF16_FromUnichar(utf32, (uint16_t*)utf16) == 1) {
reed@google.comac6b9792011-03-11 15:42:51 +0000793 // Type1 fonts fail with uniscribe API. Use GetGlyphIndices for plane 0.
skia.committer@gmail.com7bd141d2013-08-28 07:01:18 +0000794
bungeman@google.com33346482013-08-27 19:05:32 +0000795 /** Real documentation for GetGlyphIndiciesW:
796 *
797 * When GGI_MARK_NONEXISTING_GLYPHS is not specified and a character does not map to a
798 * glyph, then the 'default character's glyph is returned instead. The 'default character'
bungeman@google.com4732df62014-01-23 15:22:42 +0000799 * is available in fTM.tmDefaultChar. FON fonts have a default character, and there exists
800 * a usDefaultChar in the 'OS/2' table, version 2 and later. If there is no
bungeman@google.com33346482013-08-27 19:05:32 +0000801 * 'default character' specified by the font, then often the first character found is used.
802 *
803 * When GGI_MARK_NONEXISTING_GLYPHS is specified and a character does not map to a glyph,
804 * then the glyph 0xFFFF is used. In Windows XP and earlier, Bitmap/Vector FON usually use
805 * glyph 0x1F instead ('Terminal' appears to be special, returning 0xFFFF).
806 * Type1 PFM/PFB, TT, OT TT, OT CFF all appear to use 0xFFFF, even on XP.
807 */
808 DWORD result = GetGlyphIndicesW(fDDC, utf16, 1, &index, GGI_MARK_NONEXISTING_GLYPHS);
809 if (result == GDI_ERROR
810 || 0xFFFF == index
bungeman@google.com4732df62014-01-23 15:22:42 +0000811 || (0x1F == index &&
812 (fType == SkScalerContext_GDI::kBitmap_Type ||
813 fType == SkScalerContext_GDI::kLine_Type)
814 /*&& winVer < Vista */)
815 )
bungeman@google.com33346482013-08-27 19:05:32 +0000816 {
817 index = 0;
818 }
reed@google.comac6b9792011-03-11 15:42:51 +0000819 } else {
820 // Use uniscribe to detemine glyph index for non-BMP characters.
bungeman@google.com27f74aa2013-10-08 21:32:15 +0000821 static const int numWCHAR = 2;
822 static const int maxItems = 2;
halcanary96fcdcc2015-08-27 07:41:13 -0700823 // MSDN states that this can be nullptr, but some things don't work then.
Chris Dalton1ef80942017-12-04 12:01:30 -0700824 SCRIPT_CONTROL sc;
825 memset(&sc, 0, sizeof(sc));
bungeman@google.com27f74aa2013-10-08 21:32:15 +0000826 // Add extra item to SCRIPT_ITEM to work around a bug (now documented).
827 // https://bugzilla.mozilla.org/show_bug.cgi?id=366643
828 SCRIPT_ITEM si[maxItems + 1];
829 int numItems;
halcanary96fcdcc2015-08-27 07:41:13 -0700830 HRZM(ScriptItemize(utf16, numWCHAR, maxItems, &sc, nullptr, si, &numItems),
bungeman@google.com27f74aa2013-10-08 21:32:15 +0000831 "Could not itemize character.");
reed@google.comac6b9792011-03-11 15:42:51 +0000832
bungeman@google.com27f74aa2013-10-08 21:32:15 +0000833 // Sometimes ScriptShape cannot find a glyph for a non-BMP and returns 2 space glyphs.
834 static const int maxGlyphs = 2;
835 SCRIPT_VISATTR vsa[maxGlyphs];
836 WORD outGlyphs[maxGlyphs];
837 WORD logClust[numWCHAR];
838 int numGlyphs;
839 HRZM(ScriptShape(fDDC, &fSC, utf16, numWCHAR, maxGlyphs, &si[0].a,
840 outGlyphs, logClust, vsa, &numGlyphs),
841 "Could not shape character.");
842 if (1 == numGlyphs) {
843 index = outGlyphs[0];
844 }
reed@google.comac6b9792011-03-11 15:42:51 +0000845 }
846 return index;
847}
848
reed@google.com30ddd612013-07-30 17:47:39 +0000849void SkScalerContext_GDI::generateAdvance(SkGlyph* glyph) {
reed@google.comac6b9792011-03-11 15:42:51 +0000850 this->generateMetrics(glyph);
851}
852
reed@google.com30ddd612013-07-30 17:47:39 +0000853void SkScalerContext_GDI::generateMetrics(SkGlyph* glyph) {
reed@google.comac6b9792011-03-11 15:42:51 +0000854 SkASSERT(fDDC);
855
bungeman@google.com4732df62014-01-23 15:22:42 +0000856 if (fType == SkScalerContext_GDI::kBitmap_Type || fType == SkScalerContext_GDI::kLine_Type) {
bungeman@google.coma0319f62012-04-18 15:40:50 +0000857 SIZE size;
djsollen1b277042014-08-06 06:58:06 -0700858 WORD glyphs = glyph->getGlyphID();
bungeman@google.coma0319f62012-04-18 15:40:50 +0000859 if (0 == GetTextExtentPointI(fDDC, &glyphs, 1, &size)) {
860 glyph->fWidth = SkToS16(fTM.tmMaxCharWidth);
861 } else {
862 glyph->fWidth = SkToS16(size.cx);
863 }
864 glyph->fHeight = SkToS16(size.cy);
865
866 glyph->fTop = SkToS16(-fTM.tmAscent);
bungeman@google.com4732df62014-01-23 15:22:42 +0000867 // Bitmap FON cannot underhang, but vector FON may.
868 // There appears no means of determining underhang of vector FON.
bungeman@google.coma0319f62012-04-18 15:40:50 +0000869 glyph->fLeft = SkToS16(0);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700870 glyph->fAdvanceX = glyph->fWidth;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000871 glyph->fAdvanceY = 0;
872
bungeman@google.com4732df62014-01-23 15:22:42 +0000873 // Vector FON will transform nicely, but bitmap FON do not.
874 if (fType == SkScalerContext_GDI::kLine_Type) {
875 SkRect bounds = SkRect::MakeXYWH(glyph->fLeft, glyph->fTop,
876 glyph->fWidth, glyph->fHeight);
877 SkMatrix m;
878 m.setAll(SkFIXEDToScalar(fMat22.eM11), -SkFIXEDToScalar(fMat22.eM21), 0,
879 -SkFIXEDToScalar(fMat22.eM12), SkFIXEDToScalar(fMat22.eM22), 0,
reed3f43f8a2015-01-20 19:58:36 -0800880 0, 0, 1);
bungeman@google.com4732df62014-01-23 15:22:42 +0000881 m.mapRect(&bounds);
reedd02cf262014-11-18 18:06:45 -0800882 bounds.roundOut(&bounds);
bungeman@google.com4732df62014-01-23 15:22:42 +0000883 glyph->fLeft = SkScalarTruncToInt(bounds.fLeft);
884 glyph->fTop = SkScalarTruncToInt(bounds.fTop);
885 glyph->fWidth = SkScalarTruncToInt(bounds.width());
886 glyph->fHeight = SkScalarTruncToInt(bounds.height());
887 }
888
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000889 // Apply matrix to advance.
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700890 glyph->fAdvanceY = -FIXED2float(fMat22.eM12) * glyph->fAdvanceX;
891 glyph->fAdvanceX *= FIXED2float(fMat22.eM11);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000892
893 return;
894 }
895
djsollen1b277042014-08-06 06:58:06 -0700896 UINT glyphId = glyph->getGlyphID();
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000897
reed@google.comac6b9792011-03-11 15:42:51 +0000898 GLYPHMETRICS gm;
reed@google.com1dd17a12011-05-17 14:04:41 +0000899 sk_bzero(&gm, sizeof(gm));
reed@google.comac6b9792011-03-11 15:42:51 +0000900
halcanary96fcdcc2015-08-27 07:41:13 -0700901 DWORD status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fMat22);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000902 if (GDI_ERROR == status) {
reed@google.com055180c2013-03-21 18:46:35 +0000903 LogFontTypeface::EnsureAccessible(this->getTypeface());
halcanary96fcdcc2015-08-27 07:41:13 -0700904 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fMat22);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000905 if (GDI_ERROR == status) {
906 glyph->zeroMetrics();
907 return;
908 }
909 }
910
911 bool empty = false;
912 // The black box is either the embedded bitmap size or the outline extent.
913 // It is 1x1 if nothing is to be drawn, but will also be 1x1 if something very small
914 // is to be drawn, like a '.'. We need to outset '.' but do not wish to outset ' '.
915 if (1 == gm.gmBlackBoxX && 1 == gm.gmBlackBoxY) {
916 // If GetGlyphOutline with GGO_NATIVE returns 0, we know there was no outline.
halcanary96fcdcc2015-08-27 07:41:13 -0700917 DWORD bufferSize = GetGlyphOutlineW(fDDC, glyphId, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fMat22);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000918 empty = (0 == bufferSize);
919 }
920
921 glyph->fTop = SkToS16(-gm.gmptGlyphOrigin.y);
922 glyph->fLeft = SkToS16(gm.gmptGlyphOrigin.x);
923 if (empty) {
924 glyph->fWidth = 0;
925 glyph->fHeight = 0;
926 } else {
927 // Outset, since the image may bleed out of the black box.
928 // For embedded bitmaps the black box should be exact.
929 // For outlines we need to outset by 1 in all directions for bleed.
930 // For ClearType we need to outset by 2 for bleed.
931 glyph->fWidth = gm.gmBlackBoxX + 4;
932 glyph->fHeight = gm.gmBlackBoxY + 4;
933 glyph->fTop -= 2;
934 glyph->fLeft -= 2;
935 }
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700936 // TODO(benjaminwagner): What is the type of gm.gmCellInc[XY]?
937 glyph->fAdvanceX = (float)((int)gm.gmCellIncX);
938 glyph->fAdvanceY = (float)((int)gm.gmCellIncY);
reed@google.comac6b9792011-03-11 15:42:51 +0000939 glyph->fRsbDelta = 0;
940 glyph->fLsbDelta = 0;
941
bungeman@google.com6a774a12013-07-30 01:07:48 +0000942 if (this->isSubpixel()) {
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000943 sk_bzero(&gm, sizeof(gm));
halcanary96fcdcc2015-08-27 07:41:13 -0700944 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fHighResMat22);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000945 if (GDI_ERROR != status) {
946 SkPoint advance;
947 fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700948 glyph->fAdvanceX = SkScalarToFloat(advance.fX);
949 glyph->fAdvanceY = SkScalarToFloat(advance.fY);
reed@google.comac6b9792011-03-11 15:42:51 +0000950 }
bungeman@google.com0abbff92013-07-27 20:37:56 +0000951 } else if (!isAxisAligned(this->fRec)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700952 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fGsA);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000953 if (GDI_ERROR != status) {
954 SkPoint advance;
955 fG_inv.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700956 glyph->fAdvanceX = SkScalarToFloat(advance.fX);
957 glyph->fAdvanceY = SkScalarToFloat(advance.fY);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000958 }
reed@google.comac6b9792011-03-11 15:42:51 +0000959 }
960}
961
bungeman@google.com6a774a12013-07-30 01:07:48 +0000962static const MAT2 gMat2Identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
bungeman41078062014-07-07 08:16:37 -0700963void SkScalerContext_GDI::generateFontMetrics(SkPaint::FontMetrics* metrics) {
halcanary96fcdcc2015-08-27 07:41:13 -0700964 if (nullptr == metrics) {
bungeman41078062014-07-07 08:16:37 -0700965 return;
reed@google.com60af92c2013-05-08 14:11:28 +0000966 }
bungeman41078062014-07-07 08:16:37 -0700967 sk_bzero(metrics, sizeof(*metrics));
reed@google.comac6b9792011-03-11 15:42:51 +0000968
969 SkASSERT(fDDC);
970
bungeman@google.come9d83192013-06-21 05:31:38 +0000971#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
bungeman@google.com4732df62014-01-23 15:22:42 +0000972 if (fType == SkScalerContext_GDI::kBitmap_Type || fType == SkScalerContext_GDI::kLine_Type) {
bungeman@google.come9d83192013-06-21 05:31:38 +0000973#endif
bungeman41078062014-07-07 08:16:37 -0700974 metrics->fTop = SkIntToScalar(-fTM.tmAscent);
975 metrics->fAscent = SkIntToScalar(-fTM.tmAscent);
976 metrics->fDescent = SkIntToScalar(fTM.tmDescent);
977 metrics->fBottom = SkIntToScalar(fTM.tmDescent);
978 metrics->fLeading = SkIntToScalar(fTM.tmExternalLeading);
979 metrics->fAvgCharWidth = SkIntToScalar(fTM.tmAveCharWidth);
980 metrics->fMaxCharWidth = SkIntToScalar(fTM.tmMaxCharWidth);
981 metrics->fXMin = 0;
982 metrics->fXMax = metrics->fMaxCharWidth;
983 //metrics->fXHeight = 0;
bungeman@google.come9d83192013-06-21 05:31:38 +0000984#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
bungeman@google.coma0319f62012-04-18 15:40:50 +0000985 return;
986 }
bungeman@google.come9d83192013-06-21 05:31:38 +0000987#endif
bungeman@google.coma0319f62012-04-18 15:40:50 +0000988
reed@google.comac6b9792011-03-11 15:42:51 +0000989 OUTLINETEXTMETRIC otm;
990
991 uint32_t ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000992 if (0 == ret) {
reed@google.com055180c2013-03-21 18:46:35 +0000993 LogFontTypeface::EnsureAccessible(this->getTypeface());
bungeman@google.com39698b12011-11-15 22:26:41 +0000994 ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
995 }
bungeman@google.com0abbff92013-07-27 20:37:56 +0000996 if (0 == ret) {
bungeman@google.coma0319f62012-04-18 15:40:50 +0000997 return;
reed@google.comac6b9792011-03-11 15:42:51 +0000998 }
999
bungeman@google.come9d83192013-06-21 05:31:38 +00001000#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
bungeman41078062014-07-07 08:16:37 -07001001 metrics->fTop = SkIntToScalar(-otm.otmrcFontBox.top);
1002 metrics->fAscent = SkIntToScalar(-otm.otmAscent);
1003 metrics->fDescent = SkIntToScalar(-otm.otmDescent);
1004 metrics->fBottom = SkIntToScalar(-otm.otmrcFontBox.bottom);
1005 metrics->fLeading = SkIntToScalar(otm.otmLineGap);
1006 metrics->fAvgCharWidth = SkIntToScalar(otm.otmTextMetrics.tmAveCharWidth);
1007 metrics->fMaxCharWidth = SkIntToScalar(otm.otmTextMetrics.tmMaxCharWidth);
1008 metrics->fXMin = SkIntToScalar(otm.otmrcFontBox.left);
1009 metrics->fXMax = SkIntToScalar(otm.otmrcFontBox.right);
commit-bot@chromium.orgd3031aa2014-05-14 14:54:51 +00001010#endif
bungeman41078062014-07-07 08:16:37 -07001011 metrics->fUnderlineThickness = SkIntToScalar(otm.otmsUnderscoreSize);
1012 metrics->fUnderlinePosition = -SkIntToScalar(otm.otmsUnderscorePosition);
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001013
Ben Wagner3318da52017-03-23 14:01:22 -04001014 metrics->fFlags |= SkPaint::FontMetrics::kUnderlineThicknessIsValid_Flag;
bungeman41078062014-07-07 08:16:37 -07001015 metrics->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
bungeman@google.com4a867a62014-05-22 17:59:21 +00001016
bungeman41078062014-07-07 08:16:37 -07001017 metrics->fXHeight = SkIntToScalar(otm.otmsXHeight);
1018 GLYPHMETRICS gm;
1019 sk_bzero(&gm, sizeof(gm));
1020 DWORD len = GetGlyphOutlineW(fDDC, 'x', GGO_METRICS, &gm, 0, 0, &gMat2Identity);
1021 if (len != GDI_ERROR && gm.gmBlackBoxY > 0) {
1022 metrics->fXHeight = SkIntToScalar(gm.gmBlackBoxY);
reed@google.comac6b9792011-03-11 15:42:51 +00001023 }
1024}
1025
reed@google.com7430a332011-10-03 14:37:38 +00001026////////////////////////////////////////////////////////////////////////////////////////
1027
bungeman@google.com0abbff92013-07-27 20:37:56 +00001028#define SK_SHOW_TEXT_BLIT_COVERAGE 0
1029
reed@google.com7430a332011-10-03 14:37:38 +00001030static void build_power_table(uint8_t table[], float ee) {
1031 for (int i = 0; i < 256; i++) {
1032 float x = i / 255.f;
bungeman@google.com97efada2012-07-30 20:40:50 +00001033 x = sk_float_pow(x, ee);
reed@google.come1ca7052013-12-17 19:22:07 +00001034 int xx = SkScalarRoundToInt(x * 255);
reed@google.com7430a332011-10-03 14:37:38 +00001035 table[i] = SkToU8(xx);
1036 }
1037}
1038
bungeman@google.com97efada2012-07-30 20:40:50 +00001039/**
1040 * This will invert the gamma applied by GDI (gray-scale antialiased), so we
1041 * can get linear values.
1042 *
1043 * GDI grayscale appears to use a hard-coded gamma of 2.3.
1044 *
1045 * GDI grayscale appears to draw using the black and white rasterizer at four
1046 * times the size and then downsamples to compute the coverage mask. As a
1047 * result there are only seventeen total grays. This lack of fidelity means
1048 * that shifting into other color spaces is imprecise.
1049 */
1050static const uint8_t* getInverseGammaTableGDI() {
Mike Klein0341f162017-10-18 09:58:54 -04001051 static SkOnce once;
bungeman@google.com97efada2012-07-30 20:40:50 +00001052 static uint8_t gTableGdi[256];
Mike Klein0341f162017-10-18 09:58:54 -04001053 once([]{
bungeman@google.com97efada2012-07-30 20:40:50 +00001054 build_power_table(gTableGdi, 2.3f);
Mike Klein0341f162017-10-18 09:58:54 -04001055 });
bungeman@google.com97efada2012-07-30 20:40:50 +00001056 return gTableGdi;
1057}
1058
1059/**
1060 * This will invert the gamma applied by GDI ClearType, so we can get linear
1061 * values.
1062 *
1063 * GDI ClearType uses SPI_GETFONTSMOOTHINGCONTRAST / 1000 as the gamma value.
1064 * If this value is not specified, the default is a gamma of 1.4.
1065 */
1066static const uint8_t* getInverseGammaTableClearType() {
Mike Klein0341f162017-10-18 09:58:54 -04001067 static SkOnce once;
bungeman@google.com97efada2012-07-30 20:40:50 +00001068 static uint8_t gTableClearType[256];
Mike Klein0341f162017-10-18 09:58:54 -04001069 once([]{
reed@google.com7430a332011-10-03 14:37:38 +00001070 UINT level = 0;
1071 if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &level, 0) || !level) {
1072 // can't get the data, so use a default
1073 level = 1400;
1074 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001075 build_power_table(gTableClearType, level / 1000.0f);
Mike Klein0341f162017-10-18 09:58:54 -04001076 });
bungeman@google.com97efada2012-07-30 20:40:50 +00001077 return gTableClearType;
reed@google.com7430a332011-10-03 14:37:38 +00001078}
1079
Cary Clarka4083c92017-09-15 11:59:23 -04001080#include "SkColorData.h"
reed@google.comac6b9792011-03-11 15:42:51 +00001081
bungeman@google.com63853142012-08-01 15:36:46 +00001082//Cannot assume that the input rgb is gray due to possible setting of kGenA8FromLCD_Flag.
bungeman@google.com97efada2012-07-30 20:40:50 +00001083template<bool APPLY_PREBLEND>
1084static inline uint8_t rgb_to_a8(SkGdiRGB rgb, const uint8_t* table8) {
bungeman@google.com63853142012-08-01 15:36:46 +00001085 U8CPU r = (rgb >> 16) & 0xFF;
1086 U8CPU g = (rgb >> 8) & 0xFF;
1087 U8CPU b = (rgb >> 0) & 0xFF;
bungeman@google.com1bfe01d2012-08-28 16:02:42 +00001088 return sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
reed@google.com8351aab2012-01-18 17:06:35 +00001089}
1090
bungeman@google.com97efada2012-07-30 20:40:50 +00001091template<bool APPLY_PREBLEND>
1092static inline uint16_t rgb_to_lcd16(SkGdiRGB rgb, const uint8_t* tableR,
1093 const uint8_t* tableG,
1094 const uint8_t* tableB) {
1095 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1096 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1097 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001098#if SK_SHOW_TEXT_BLIT_COVERAGE
1099 r = SkMax32(r, 10); g = SkMax32(g, 10); b = SkMax32(b, 10);
1100#endif
bungeman@google.com97efada2012-07-30 20:40:50 +00001101 return SkPack888ToRGB16(r, g, b);
reed@google.com82a34d82011-07-26 19:33:08 +00001102}
1103
reed@google.com82cff022011-09-22 14:33:40 +00001104// Is this GDI color neither black nor white? If so, we have to keep this
1105// image as is, rather than smashing it down to a BW mask.
1106//
1107// returns int instead of bool, since we don't want/have to pay to convert
1108// the zero/non-zero value into a bool
1109static int is_not_black_or_white(SkGdiRGB c) {
1110 // same as (but faster than)
1111 // c &= 0x00FFFFFF;
1112 // return 0 == c || 0x00FFFFFF == c;
1113 return (c + (c & 1)) & 0x00FFFFFF;
reed@google.com5e2df642011-09-21 18:42:09 +00001114}
1115
bungeman@google.com4b18f572013-07-22 15:21:23 +00001116static bool is_rgb_really_bw(const SkGdiRGB* src, int width, int height, size_t srcRB) {
reed@google.com5e2df642011-09-21 18:42:09 +00001117 for (int y = 0; y < height; ++y) {
1118 for (int x = 0; x < width; ++x) {
reed@google.com82cff022011-09-22 14:33:40 +00001119 if (is_not_black_or_white(src[x])) {
reed@google.com5e2df642011-09-21 18:42:09 +00001120 return false;
1121 }
1122 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001123 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001124 }
1125 return true;
1126}
1127
bungeman@google.com97efada2012-07-30 20:40:50 +00001128// gdi's bitmap is upside-down, so we reverse dst walking in Y
1129// whenever we copy it into skia's buffer
reed@google.com5e2df642011-09-21 18:42:09 +00001130static void rgb_to_bw(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
bungeman@google.com97efada2012-07-30 20:40:50 +00001131 const SkGlyph& glyph) {
reed@google.com5e2df642011-09-21 18:42:09 +00001132 const int width = glyph.fWidth;
1133 const size_t dstRB = (width + 7) >> 3;
1134 uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1135
1136 int byteCount = width >> 3;
1137 int bitCount = width & 7;
1138
1139 // adjust srcRB to skip the values in our byteCount loop,
1140 // since we increment src locally there
1141 srcRB -= byteCount * 8 * sizeof(SkGdiRGB);
1142
1143 for (int y = 0; y < glyph.fHeight; ++y) {
1144 if (byteCount > 0) {
reed@google.com5e2df642011-09-21 18:42:09 +00001145 for (int i = 0; i < byteCount; ++i) {
reed@google.com7a230142011-09-27 14:07:21 +00001146 unsigned byte = 0;
bungeman@google.com97efada2012-07-30 20:40:50 +00001147 byte |= src[0] & (1 << 7);
1148 byte |= src[1] & (1 << 6);
1149 byte |= src[2] & (1 << 5);
1150 byte |= src[3] & (1 << 4);
1151 byte |= src[4] & (1 << 3);
1152 byte |= src[5] & (1 << 2);
1153 byte |= src[6] & (1 << 1);
1154 byte |= src[7] & (1 << 0);
reed@google.com6a8f14d2011-09-27 12:54:24 +00001155 dst[i] = byte;
reed@google.com5e2df642011-09-21 18:42:09 +00001156 src += 8;
1157 }
1158 }
1159 if (bitCount > 0) {
1160 unsigned byte = 0;
1161 unsigned mask = 0x80;
1162 for (int i = 0; i < bitCount; i++) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001163 byte |= src[i] & mask;
reed@google.com5e2df642011-09-21 18:42:09 +00001164 mask >>= 1;
1165 }
1166 dst[byteCount] = byte;
1167 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001168 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001169 dst -= dstRB;
1170 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001171#if SK_SHOW_TEXT_BLIT_COVERAGE
1172 if (glyph.fWidth > 0 && glyph.fHeight > 0) {
1173 uint8_t* first = (uint8_t*)glyph.fImage;
1174 uint8_t* last = (uint8_t*)((char*)glyph.fImage + glyph.fHeight * dstRB - 1);
1175 *first |= 1 << 7;
1176 *last |= bitCount == 0 ? 1 : 1 << (8 - bitCount);
1177 }
1178#endif
reed@google.com5e2df642011-09-21 18:42:09 +00001179}
1180
bungeman@google.com97efada2012-07-30 20:40:50 +00001181template<bool APPLY_PREBLEND>
reed@google.com5e2df642011-09-21 18:42:09 +00001182static void rgb_to_a8(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
bungeman@google.com97efada2012-07-30 20:40:50 +00001183 const SkGlyph& glyph, const uint8_t* table8) {
reed@google.com5e2df642011-09-21 18:42:09 +00001184 const size_t dstRB = glyph.rowBytes();
1185 const int width = glyph.fWidth;
1186 uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1187
1188 for (int y = 0; y < glyph.fHeight; y++) {
1189 for (int i = 0; i < width; i++) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001190 dst[i] = rgb_to_a8<APPLY_PREBLEND>(src[i], table8);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001191#if SK_SHOW_TEXT_BLIT_COVERAGE
1192 dst[i] = SkMax32(dst[i], 10);
1193#endif
reed@google.com5e2df642011-09-21 18:42:09 +00001194 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001195 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001196 dst -= dstRB;
1197 }
1198}
1199
bungeman@google.com97efada2012-07-30 20:40:50 +00001200template<bool APPLY_PREBLEND>
1201static void rgb_to_lcd16(const SkGdiRGB* SK_RESTRICT src, size_t srcRB, const SkGlyph& glyph,
1202 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
reed@google.com5e2df642011-09-21 18:42:09 +00001203 const size_t dstRB = glyph.rowBytes();
1204 const int width = glyph.fWidth;
1205 uint16_t* SK_RESTRICT dst = (uint16_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1206
1207 for (int y = 0; y < glyph.fHeight; y++) {
1208 for (int i = 0; i < width; i++) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001209 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(src[i], tableR, tableG, tableB);
reed@google.com5e2df642011-09-21 18:42:09 +00001210 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001211 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001212 dst = (uint16_t*)((char*)dst - dstRB);
1213 }
1214}
1215
reed@google.com30ddd612013-07-30 17:47:39 +00001216void SkScalerContext_GDI::generateImage(const SkGlyph& glyph) {
reed@google.comac6b9792011-03-11 15:42:51 +00001217 SkASSERT(fDDC);
1218
reed@google.com62711172011-05-18 15:08:10 +00001219 const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
reed@google.com82a34d82011-07-26 19:33:08 +00001220 const bool isAA = !isLCD(fRec);
reed@google.comac6b9792011-03-11 15:42:51 +00001221
reed@google.com99edd432011-09-09 14:59:59 +00001222 size_t srcRB;
bungeman@google.com97efada2012-07-30 20:40:50 +00001223 const void* bits = fOffscreen.draw(glyph, isBW, &srcRB);
halcanary96fcdcc2015-08-27 07:41:13 -07001224 if (nullptr == bits) {
reed@google.com055180c2013-03-21 18:46:35 +00001225 LogFontTypeface::EnsureAccessible(this->getTypeface());
bungeman@google.com97efada2012-07-30 20:40:50 +00001226 bits = fOffscreen.draw(glyph, isBW, &srcRB);
halcanary96fcdcc2015-08-27 07:41:13 -07001227 if (nullptr == bits) {
bungeman@google.com39698b12011-11-15 22:26:41 +00001228 sk_bzero(glyph.fImage, glyph.computeImageSize());
1229 return;
1230 }
reed@google.com82a34d82011-07-26 19:33:08 +00001231 }
reed@google.comac6b9792011-03-11 15:42:51 +00001232
bungeman@google.com97efada2012-07-30 20:40:50 +00001233 if (!isBW) {
bungeman@google.com1bd2d672012-08-13 20:01:51 +00001234 const uint8_t* table;
1235 //The offscreen contains a GDI blit if isAA and kGenA8FromLCD_Flag is not set.
1236 //Otherwise the offscreen contains a ClearType blit.
1237 if (isAA && !(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag)) {
1238 table = getInverseGammaTableGDI();
1239 } else {
1240 table = getInverseGammaTableClearType();
bungeman@google.com97efada2012-07-30 20:40:50 +00001241 }
1242 //Note that the following cannot really be integrated into the
1243 //pre-blend, since we may not be applying the pre-blend; when we aren't
1244 //applying the pre-blend it means that a filter wants linear anyway.
1245 //Other code may also be applying the pre-blend, so we'd need another
1246 //one with this and one without.
reed@google.com6f5df482011-09-28 20:33:24 +00001247 SkGdiRGB* addr = (SkGdiRGB*)bits;
1248 for (int y = 0; y < glyph.fHeight; ++y) {
1249 for (int x = 0; x < glyph.fWidth; ++x) {
1250 int r = (addr[x] >> 16) & 0xFF;
1251 int g = (addr[x] >> 8) & 0xFF;
1252 int b = (addr[x] >> 0) & 0xFF;
reed@google.com7430a332011-10-03 14:37:38 +00001253 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
reed@google.com6f5df482011-09-28 20:33:24 +00001254 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001255 addr = SkTAddOffset<SkGdiRGB>(addr, srcRB);
reed@google.com6f5df482011-09-28 20:33:24 +00001256 }
1257 }
1258
reed@google.com82a34d82011-07-26 19:33:08 +00001259 int width = glyph.fWidth;
1260 size_t dstRB = glyph.rowBytes();
1261 if (isBW) {
1262 const uint8_t* src = (const uint8_t*)bits;
reed@google.com82a34d82011-07-26 19:33:08 +00001263 uint8_t* dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1264 for (int y = 0; y < glyph.fHeight; y++) {
1265 memcpy(dst, src, dstRB);
1266 src += srcRB;
1267 dst -= dstRB;
reed@google.comac6b9792011-03-11 15:42:51 +00001268 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001269#if SK_SHOW_TEXT_BLIT_COVERAGE
1270 if (glyph.fWidth > 0 && glyph.fHeight > 0) {
1271 int bitCount = width & 7;
1272 uint8_t* first = (uint8_t*)glyph.fImage;
1273 uint8_t* last = (uint8_t*)((char*)glyph.fImage + glyph.fHeight * dstRB - 1);
1274 *first |= 1 << 7;
1275 *last |= bitCount == 0 ? 1 : 1 << (8 - bitCount);
1276 }
1277#endif
reed@google.com82a34d82011-07-26 19:33:08 +00001278 } else if (isAA) {
reed@google.com6f5df482011-09-28 20:33:24 +00001279 // since the caller may require A8 for maskfilters, we can't check for BW
1280 // ... until we have the caller tell us that explicitly
reed@google.com5e2df642011-09-21 18:42:09 +00001281 const SkGdiRGB* src = (const SkGdiRGB*)bits;
bungeman@google.coma76de722012-10-26 19:35:54 +00001282 if (fPreBlend.isApplicable()) {
1283 rgb_to_a8<true>(src, srcRB, glyph, fPreBlend.fG);
bungeman@google.com97efada2012-07-30 20:40:50 +00001284 } else {
bungeman@google.coma76de722012-10-26 19:35:54 +00001285 rgb_to_a8<false>(src, srcRB, glyph, fPreBlend.fG);
bungeman@google.com97efada2012-07-30 20:40:50 +00001286 }
reed@google.com82a34d82011-07-26 19:33:08 +00001287 } else { // LCD16
reed@google.com5e2df642011-09-21 18:42:09 +00001288 const SkGdiRGB* src = (const SkGdiRGB*)bits;
1289 if (is_rgb_really_bw(src, width, glyph.fHeight, srcRB)) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001290 rgb_to_bw(src, srcRB, glyph);
reed@google.com5e2df642011-09-21 18:42:09 +00001291 ((SkGlyph*)&glyph)->fMaskFormat = SkMask::kBW_Format;
1292 } else {
reedd54d3fc2014-11-13 14:39:58 -08001293 SkASSERT(SkMask::kLCD16_Format == glyph.fMaskFormat);
1294 if (fPreBlend.isApplicable()) {
1295 rgb_to_lcd16<true>(src, srcRB, glyph,
1296 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
reed@google.com754e4eb2011-09-26 13:21:39 +00001297 } else {
reedd54d3fc2014-11-13 14:39:58 -08001298 rgb_to_lcd16<false>(src, srcRB, glyph,
1299 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
reed@google.com754e4eb2011-09-26 13:21:39 +00001300 }
reed@google.comac6b9792011-03-11 15:42:51 +00001301 }
1302 }
reed@google.comac6b9792011-03-11 15:42:51 +00001303}
1304
bungeman@google.com0abbff92013-07-27 20:37:56 +00001305class GDIGlyphbufferPointIter {
1306public:
skia.committer@gmail.com27e21fe2013-07-28 07:01:03 +00001307 GDIGlyphbufferPointIter(const uint8_t* glyphbuf, DWORD total_size)
bungeman@google.com0abbff92013-07-27 20:37:56 +00001308 : fHeaderIter(glyphbuf, total_size), fCurveIter(), fPointIter()
1309 { }
reed@google.comac6b9792011-03-11 15:42:51 +00001310
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001311 POINTFX const * next() {
bungeman@google.com0abbff92013-07-27 20:37:56 +00001312nextHeader:
1313 if (!fCurveIter.isSet()) {
1314 const TTPOLYGONHEADER* header = fHeaderIter.next();
halcanary96fcdcc2015-08-27 07:41:13 -07001315 if (nullptr == header) {
1316 return nullptr;
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001317 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001318 fCurveIter.set(header);
1319 const TTPOLYCURVE* curve = fCurveIter.next();
halcanary96fcdcc2015-08-27 07:41:13 -07001320 if (nullptr == curve) {
1321 return nullptr;
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001322 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001323 fPointIter.set(curve);
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001324 return &header->pfxStart;
bungeman@google.com05a729f2013-06-20 15:29:16 +00001325 }
1326
bungeman@google.com0abbff92013-07-27 20:37:56 +00001327 const POINTFX* nextPoint = fPointIter.next();
halcanary96fcdcc2015-08-27 07:41:13 -07001328 if (nullptr == nextPoint) {
bungeman@google.com0abbff92013-07-27 20:37:56 +00001329 const TTPOLYCURVE* curve = fCurveIter.next();
halcanary96fcdcc2015-08-27 07:41:13 -07001330 if (nullptr == curve) {
bungeman@google.com0abbff92013-07-27 20:37:56 +00001331 fCurveIter.set();
1332 goto nextHeader;
1333 } else {
1334 fPointIter.set(curve);
bungeman@google.com05a729f2013-06-20 15:29:16 +00001335 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001336 nextPoint = fPointIter.next();
reed@google.comac6b9792011-03-11 15:42:51 +00001337 }
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001338 return nextPoint;
reed@google.comac6b9792011-03-11 15:42:51 +00001339 }
bungeman@google.comd1c7f712013-03-11 18:51:19 +00001340
bungeman@google.com0abbff92013-07-27 20:37:56 +00001341 WORD currentCurveType() {
1342 return fPointIter.fCurveType;
1343 }
1344
1345private:
1346 /** Iterates over all of the polygon headers in a glyphbuf. */
1347 class GDIPolygonHeaderIter {
1348 public:
skia.committer@gmail.com27e21fe2013-07-28 07:01:03 +00001349 GDIPolygonHeaderIter(const uint8_t* glyphbuf, DWORD total_size)
bungeman@google.com0abbff92013-07-27 20:37:56 +00001350 : fCurPolygon(reinterpret_cast<const TTPOLYGONHEADER*>(glyphbuf))
1351 , fEndPolygon(SkTAddOffset<const TTPOLYGONHEADER>(glyphbuf, total_size))
1352 { }
1353
1354 const TTPOLYGONHEADER* next() {
1355 if (fCurPolygon >= fEndPolygon) {
halcanary96fcdcc2015-08-27 07:41:13 -07001356 return nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001357 }
1358 const TTPOLYGONHEADER* thisPolygon = fCurPolygon;
1359 fCurPolygon = SkTAddOffset<const TTPOLYGONHEADER>(fCurPolygon, fCurPolygon->cb);
1360 return thisPolygon;
1361 }
1362 private:
1363 const TTPOLYGONHEADER* fCurPolygon;
1364 const TTPOLYGONHEADER* fEndPolygon;
1365 };
1366
1367 /** Iterates over all of the polygon curves in a polygon header. */
1368 class GDIPolygonCurveIter {
1369 public:
halcanary96fcdcc2015-08-27 07:41:13 -07001370 GDIPolygonCurveIter() : fCurCurve(nullptr), fEndCurve(nullptr) { }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001371
1372 GDIPolygonCurveIter(const TTPOLYGONHEADER* curPolygon)
1373 : fCurCurve(SkTAddOffset<const TTPOLYCURVE>(curPolygon, sizeof(TTPOLYGONHEADER)))
1374 , fEndCurve(SkTAddOffset<const TTPOLYCURVE>(curPolygon, curPolygon->cb))
1375 { }
1376
halcanary96fcdcc2015-08-27 07:41:13 -07001377 bool isSet() { return fCurCurve != nullptr; }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001378
1379 void set(const TTPOLYGONHEADER* curPolygon) {
1380 fCurCurve = SkTAddOffset<const TTPOLYCURVE>(curPolygon, sizeof(TTPOLYGONHEADER));
1381 fEndCurve = SkTAddOffset<const TTPOLYCURVE>(curPolygon, curPolygon->cb);
1382 }
1383 void set() {
halcanary96fcdcc2015-08-27 07:41:13 -07001384 fCurCurve = nullptr;
1385 fEndCurve = nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001386 }
1387
1388 const TTPOLYCURVE* next() {
1389 if (fCurCurve >= fEndCurve) {
halcanary96fcdcc2015-08-27 07:41:13 -07001390 return nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001391 }
1392 const TTPOLYCURVE* thisCurve = fCurCurve;
1393 fCurCurve = SkTAddOffset<const TTPOLYCURVE>(fCurCurve, size_of_TTPOLYCURVE(*fCurCurve));
1394 return thisCurve;
1395 }
1396 private:
1397 size_t size_of_TTPOLYCURVE(const TTPOLYCURVE& curve) {
1398 return 2*sizeof(WORD) + curve.cpfx*sizeof(POINTFX);
1399 }
1400 const TTPOLYCURVE* fCurCurve;
1401 const TTPOLYCURVE* fEndCurve;
1402 };
1403
1404 /** Iterates over all of the polygon points in a polygon curve. */
1405 class GDIPolygonCurvePointIter {
1406 public:
halcanary96fcdcc2015-08-27 07:41:13 -07001407 GDIPolygonCurvePointIter() : fCurveType(0), fCurPoint(nullptr), fEndPoint(nullptr) { }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001408
1409 GDIPolygonCurvePointIter(const TTPOLYCURVE* curPolygon)
1410 : fCurveType(curPolygon->wType)
1411 , fCurPoint(&curPolygon->apfx[0])
1412 , fEndPoint(&curPolygon->apfx[curPolygon->cpfx])
1413 { }
1414
halcanary96fcdcc2015-08-27 07:41:13 -07001415 bool isSet() { return fCurPoint != nullptr; }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001416
1417 void set(const TTPOLYCURVE* curPolygon) {
1418 fCurveType = curPolygon->wType;
1419 fCurPoint = &curPolygon->apfx[0];
1420 fEndPoint = &curPolygon->apfx[curPolygon->cpfx];
1421 }
1422 void set() {
halcanary96fcdcc2015-08-27 07:41:13 -07001423 fCurPoint = nullptr;
1424 fEndPoint = nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001425 }
1426
1427 const POINTFX* next() {
1428 if (fCurPoint >= fEndPoint) {
halcanary96fcdcc2015-08-27 07:41:13 -07001429 return nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001430 }
1431 const POINTFX* thisPoint = fCurPoint;
1432 ++fCurPoint;
1433 return thisPoint;
1434 }
1435
1436 WORD fCurveType;
1437 private:
1438 const POINTFX* fCurPoint;
1439 const POINTFX* fEndPoint;
1440 };
1441
1442 GDIPolygonHeaderIter fHeaderIter;
1443 GDIPolygonCurveIter fCurveIter;
1444 GDIPolygonCurvePointIter fPointIter;
1445};
1446
1447static void sk_path_from_gdi_path(SkPath* path, const uint8_t* glyphbuf, DWORD total_size) {
bungeman@google.comd1c7f712013-03-11 18:51:19 +00001448 const uint8_t* cur_glyph = glyphbuf;
1449 const uint8_t* end_glyph = glyphbuf + total_size;
1450
1451 while (cur_glyph < end_glyph) {
1452 const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
1453
1454 const uint8_t* end_poly = cur_glyph + th->cb;
1455 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
1456
1457 path->moveTo(SkFixedToScalar( SkFIXEDToFixed(th->pfxStart.x)),
1458 SkFixedToScalar(-SkFIXEDToFixed(th->pfxStart.y)));
1459
1460 while (cur_poly < end_poly) {
1461 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
1462
1463 if (pc->wType == TT_PRIM_LINE) {
1464 for (uint16_t i = 0; i < pc->cpfx; i++) {
1465 path->lineTo(SkFixedToScalar( SkFIXEDToFixed(pc->apfx[i].x)),
1466 SkFixedToScalar(-SkFIXEDToFixed(pc->apfx[i].y)));
1467 }
1468 }
1469
1470 if (pc->wType == TT_PRIM_QSPLINE) {
1471 for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
1472 POINTFX pnt_b = pc->apfx[u]; // B is always the current point
1473 POINTFX pnt_c = pc->apfx[u+1];
1474
1475 if (u < pc->cpfx - 2) { // If not on last spline, compute C
1476 pnt_c.x = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.x),
1477 SkFIXEDToFixed(pnt_c.x)));
1478 pnt_c.y = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.y),
1479 SkFIXEDToFixed(pnt_c.y)));
1480 }
1481
1482 path->quadTo(SkFixedToScalar( SkFIXEDToFixed(pnt_b.x)),
1483 SkFixedToScalar(-SkFIXEDToFixed(pnt_b.y)),
1484 SkFixedToScalar( SkFIXEDToFixed(pnt_c.x)),
1485 SkFixedToScalar(-SkFIXEDToFixed(pnt_c.y)));
1486 }
1487 }
1488 // Advance past this TTPOLYCURVE.
1489 cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx;
1490 }
1491 cur_glyph += th->cb;
1492 path->close();
reed@google.comac6b9792011-03-11 15:42:51 +00001493 }
reed@google.comac6b9792011-03-11 15:42:51 +00001494}
1495
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001496#define move_next_expected_hinted_point(iter, pElem) do {\
1497 pElem = iter.next(); \
halcanary96fcdcc2015-08-27 07:41:13 -07001498 if (nullptr == pElem) return false; \
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001499} while(0)
1500
1501// It is possible for the hinted and unhinted versions of the same path to have
1502// a different number of points due to GDI's handling of flipped points.
1503// If this is detected, this will return false.
1504static bool sk_path_from_gdi_paths(SkPath* path, const uint8_t* glyphbuf, DWORD total_size,
bungeman@google.com0abbff92013-07-27 20:37:56 +00001505 GDIGlyphbufferPointIter hintedYs) {
1506 const uint8_t* cur_glyph = glyphbuf;
1507 const uint8_t* end_glyph = glyphbuf + total_size;
1508
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001509 POINTFX const * hintedPoint;
1510
bungeman@google.com0abbff92013-07-27 20:37:56 +00001511 while (cur_glyph < end_glyph) {
1512 const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
1513
1514 const uint8_t* end_poly = cur_glyph + th->cb;
1515 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
1516
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001517 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001518 path->moveTo(SkFixedToScalar( SkFIXEDToFixed(th->pfxStart.x)),
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001519 SkFixedToScalar(-SkFIXEDToFixed(hintedPoint->y)));
bungeman@google.com0abbff92013-07-27 20:37:56 +00001520
1521 while (cur_poly < end_poly) {
1522 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
1523
1524 if (pc->wType == TT_PRIM_LINE) {
1525 for (uint16_t i = 0; i < pc->cpfx; i++) {
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001526 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001527 path->lineTo(SkFixedToScalar( SkFIXEDToFixed(pc->apfx[i].x)),
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001528 SkFixedToScalar(-SkFIXEDToFixed(hintedPoint->y)));
bungeman@google.com0abbff92013-07-27 20:37:56 +00001529 }
1530 }
1531
1532 if (pc->wType == TT_PRIM_QSPLINE) {
1533 POINTFX currentPoint = pc->apfx[0];
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001534 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001535 // only take the hinted y if it wasn't flipped
1536 if (hintedYs.currentCurveType() == TT_PRIM_QSPLINE) {
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001537 currentPoint.y = hintedPoint->y;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001538 }
1539 for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
1540 POINTFX pnt_b = currentPoint;//pc->apfx[u]; // B is always the current point
1541 POINTFX pnt_c = pc->apfx[u+1];
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001542 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001543 // only take the hinted y if it wasn't flipped
1544 if (hintedYs.currentCurveType() == TT_PRIM_QSPLINE) {
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001545 pnt_c.y = hintedPoint->y;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001546 }
1547 currentPoint.x = pnt_c.x;
1548 currentPoint.y = pnt_c.y;
1549
1550 if (u < pc->cpfx - 2) { // If not on last spline, compute C
1551 pnt_c.x = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.x),
1552 SkFIXEDToFixed(pnt_c.x)));
1553 pnt_c.y = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.y),
1554 SkFIXEDToFixed(pnt_c.y)));
1555 }
1556
1557 path->quadTo(SkFixedToScalar( SkFIXEDToFixed(pnt_b.x)),
1558 SkFixedToScalar(-SkFIXEDToFixed(pnt_b.y)),
1559 SkFixedToScalar( SkFIXEDToFixed(pnt_c.x)),
1560 SkFixedToScalar(-SkFIXEDToFixed(pnt_c.y)));
1561 }
1562 }
1563 // Advance past this TTPOLYCURVE.
1564 cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx;
1565 }
1566 cur_glyph += th->cb;
1567 path->close();
1568 }
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001569 return true;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001570}
1571
Ben Wagner6e9ac122016-11-11 14:31:06 -05001572DWORD SkScalerContext_GDI::getGDIGlyphPath(SkGlyphID glyph, UINT flags,
1573 SkAutoSTMalloc<BUFFERSIZE, uint8_t>* glyphbuf)
bungeman@google.com0abbff92013-07-27 20:37:56 +00001574{
1575 GLYPHMETRICS gm;
1576
Ben Wagner6e9ac122016-11-11 14:31:06 -05001577 DWORD total_size = GetGlyphOutlineW(fDDC, glyph, flags, &gm, BUFFERSIZE, glyphbuf->get(), &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001578 // Sometimes GetGlyphOutlineW returns a number larger than BUFFERSIZE even if BUFFERSIZE > 0.
1579 // It has been verified that this does not involve a buffer overrun.
1580 if (GDI_ERROR == total_size || total_size > BUFFERSIZE) {
1581 // GDI_ERROR because the BUFFERSIZE was too small, or because the data was not accessible.
1582 // When the data is not accessable GetGlyphOutlineW fails rather quickly,
1583 // so just try to get the size. If that fails then ensure the data is accessible.
Ben Wagner6e9ac122016-11-11 14:31:06 -05001584 total_size = GetGlyphOutlineW(fDDC, glyph, flags, &gm, 0, nullptr, &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001585 if (GDI_ERROR == total_size) {
1586 LogFontTypeface::EnsureAccessible(this->getTypeface());
Ben Wagner6e9ac122016-11-11 14:31:06 -05001587 total_size = GetGlyphOutlineW(fDDC, glyph, flags, &gm, 0, nullptr, &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001588 if (GDI_ERROR == total_size) {
kkinnunenc6cb56f2014-06-24 00:12:27 -07001589 // GetGlyphOutlineW is known to fail for some characters, such as spaces.
1590 // In these cases, just return that the glyph does not have a shape.
bungeman@google.com0abbff92013-07-27 20:37:56 +00001591 return 0;
1592 }
1593 }
1594
1595 glyphbuf->reset(total_size);
1596
Ben Wagner6e9ac122016-11-11 14:31:06 -05001597 DWORD ret = GetGlyphOutlineW(fDDC, glyph, flags, &gm, total_size, glyphbuf->get(), &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001598 if (GDI_ERROR == ret) {
1599 LogFontTypeface::EnsureAccessible(this->getTypeface());
Ben Wagner6e9ac122016-11-11 14:31:06 -05001600 ret = GetGlyphOutlineW(fDDC, glyph, flags, &gm, total_size, glyphbuf->get(), &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001601 if (GDI_ERROR == ret) {
1602 SkASSERT(false);
1603 return 0;
1604 }
1605 }
1606 }
1607 return total_size;
1608}
1609
Ben Wagner6e9ac122016-11-11 14:31:06 -05001610void SkScalerContext_GDI::generatePath(SkGlyphID glyph, SkPath* path) {
dcheng3ba043f2015-07-08 13:25:23 -07001611 SkASSERT(path);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001612 SkASSERT(fDDC);
1613
1614 path->reset();
1615
1616 // Out of all the fonts on a typical Windows box,
1617 // 25% of glyphs require more than 2KB.
1618 // 1% of glyphs require more than 4KB.
1619 // 0.01% of glyphs require more than 8KB.
1620 // 8KB is less than 1% of the normal 1MB stack on Windows.
1621 // Note that some web fonts glyphs require more than 20KB.
1622 //static const DWORD BUFFERSIZE = (1 << 13);
1623
1624 //GDI only uses hinted outlines when axis aligned.
1625 UINT format = GGO_NATIVE | GGO_GLYPH_INDEX;
1626 if (fRec.getHinting() == SkPaint::kNo_Hinting || fRec.getHinting() == SkPaint::kSlight_Hinting){
1627 format |= GGO_UNHINTED;
1628 }
1629 SkAutoSTMalloc<BUFFERSIZE, uint8_t> glyphbuf(BUFFERSIZE);
1630 DWORD total_size = getGDIGlyphPath(glyph, format, &glyphbuf);
1631 if (0 == total_size) {
1632 return;
1633 }
1634
1635 if (fRec.getHinting() != SkPaint::kSlight_Hinting) {
1636 sk_path_from_gdi_path(path, glyphbuf, total_size);
1637 } else {
1638 //GDI only uses hinted outlines when axis aligned.
1639 UINT format = GGO_NATIVE | GGO_GLYPH_INDEX;
1640
1641 SkAutoSTMalloc<BUFFERSIZE, uint8_t> hintedGlyphbuf(BUFFERSIZE);
1642 DWORD hinted_total_size = getGDIGlyphPath(glyph, format, &hintedGlyphbuf);
1643 if (0 == hinted_total_size) {
1644 return;
1645 }
1646
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001647 if (!sk_path_from_gdi_paths(path, glyphbuf, total_size,
1648 GDIGlyphbufferPointIter(hintedGlyphbuf, hinted_total_size)))
1649 {
1650 path->reset();
1651 sk_path_from_gdi_path(path, glyphbuf, total_size);
1652 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001653 }
1654}
1655
reed@google.com484f5bc2013-04-24 19:14:56 +00001656static void logfont_for_name(const char* familyName, LOGFONT* lf) {
1657 sk_bzero(lf, sizeof(LOGFONT));
bungeman@google.come70f7982012-06-01 19:38:19 +00001658#ifdef UNICODE
reed@google.com484f5bc2013-04-24 19:14:56 +00001659 // Get the buffer size needed first.
1660 size_t str_len = ::MultiByteToWideChar(CP_UTF8, 0, familyName,
halcanary96fcdcc2015-08-27 07:41:13 -07001661 -1, nullptr, 0);
reed@google.com484f5bc2013-04-24 19:14:56 +00001662 // Allocate a buffer (str_len already has terminating null
1663 // accounted for).
1664 wchar_t *wideFamilyName = new wchar_t[str_len];
1665 // Now actually convert the string.
1666 ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1,
1667 wideFamilyName, str_len);
1668 ::wcsncpy(lf->lfFaceName, wideFamilyName, LF_FACESIZE - 1);
1669 delete [] wideFamilyName;
1670 lf->lfFaceName[LF_FACESIZE-1] = L'\0';
bungeman@google.come70f7982012-06-01 19:38:19 +00001671#else
reed@google.com484f5bc2013-04-24 19:14:56 +00001672 ::strncpy(lf->lfFaceName, familyName, LF_FACESIZE - 1);
1673 lf->lfFaceName[LF_FACESIZE - 1] = '\0';
bungeman@google.come70f7982012-06-01 19:38:19 +00001674#endif
1675}
1676
bungemanb374d6a2014-09-17 07:48:59 -07001677void LogFontTypeface::onGetFamilyName(SkString* familyName) const {
bungeman@google.com7103f182012-10-31 20:53:49 +00001678 // Get the actual name of the typeface. The logfont may not know this.
reed@google.com5526ede2013-03-25 13:03:37 +00001679 HFONT font = CreateFontIndirect(&fLogFont);
bungeman@google.com7103f182012-10-31 20:53:49 +00001680
halcanary96fcdcc2015-08-27 07:41:13 -07001681 HDC deviceContext = ::CreateCompatibleDC(nullptr);
bungeman@google.com7103f182012-10-31 20:53:49 +00001682 HFONT savefont = (HFONT)SelectObject(deviceContext, font);
1683
bungemanb374d6a2014-09-17 07:48:59 -07001684 dcfontname_to_skstring(deviceContext, fLogFont, familyName);
bungeman@google.com7103f182012-10-31 20:53:49 +00001685
1686 if (deviceContext) {
1687 ::SelectObject(deviceContext, savefont);
1688 ::DeleteDC(deviceContext);
1689 }
1690 if (font) {
1691 ::DeleteObject(font);
1692 }
bungemanb374d6a2014-09-17 07:48:59 -07001693}
bungeman@google.com7103f182012-10-31 20:53:49 +00001694
bungemanb374d6a2014-09-17 07:48:59 -07001695void LogFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc,
1696 bool* isLocalStream) const {
1697 SkString familyName;
1698 this->onGetFamilyName(&familyName);
reed@google.com5526ede2013-03-25 13:03:37 +00001699 desc->setFamilyName(familyName.c_str());
bungemanb8113782016-07-25 16:54:59 -07001700 desc->setStyle(this->fontStyle());
reed@google.com5526ede2013-03-25 13:03:37 +00001701 *isLocalStream = this->fSerializeAsStream;
reed@google.comac6b9792011-03-11 15:42:51 +00001702}
1703
Hal Canary209e4b12017-05-04 14:23:55 -04001704std::unique_ptr<SkAdvancedTypefaceMetrics> LogFontTypeface::onGetAdvancedMetrics() const {
reed@google.com2689f612013-03-20 20:01:47 +00001705 LOGFONT lf = fLogFont;
Hal Canary209e4b12017-05-04 14:23:55 -04001706 std::unique_ptr<SkAdvancedTypefaceMetrics> info(nullptr);
reed@google.comac6b9792011-03-11 15:42:51 +00001707
halcanary96fcdcc2015-08-27 07:41:13 -07001708 HDC hdc = CreateCompatibleDC(nullptr);
reed@google.comac6b9792011-03-11 15:42:51 +00001709 HFONT font = CreateFontIndirect(&lf);
1710 HFONT savefont = (HFONT)SelectObject(hdc, font);
halcanary96fcdcc2015-08-27 07:41:13 -07001711 HFONT designFont = nullptr;
reed@google.comac6b9792011-03-11 15:42:51 +00001712
reed@google.com05b6f3a2011-11-28 15:30:28 +00001713 const char stem_chars[] = {'i', 'I', '!', '1'};
1714 int16_t min_width;
1715 unsigned glyphCount;
1716
reed@google.comac6b9792011-03-11 15:42:51 +00001717 // To request design units, create a logical font whose height is specified
1718 // as unitsPerEm.
1719 OUTLINETEXTMETRIC otm;
bungeman@google.com39698b12011-11-15 22:26:41 +00001720 unsigned int otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
1721 if (0 == otmRet) {
reed@google.com055180c2013-03-21 18:46:35 +00001722 call_ensure_accessible(lf);
bungeman@google.com39698b12011-11-15 22:26:41 +00001723 otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
1724 }
1725 if (!otmRet || !GetTextFace(hdc, LF_FACESIZE, lf.lfFaceName)) {
reed@google.comac6b9792011-03-11 15:42:51 +00001726 goto Error;
1727 }
1728 lf.lfHeight = -SkToS32(otm.otmEMSquare);
1729 designFont = CreateFontIndirect(&lf);
1730 SelectObject(hdc, designFont);
1731 if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) {
1732 goto Error;
1733 }
bungeman@google.com7bdd6142013-07-15 19:42:57 +00001734 glyphCount = calculateGlyphCount(hdc, fLogFont);
reed@google.comac6b9792011-03-11 15:42:51 +00001735
Hal Canary209e4b12017-05-04 14:23:55 -04001736 info.reset(new SkAdvancedTypefaceMetrics);
bungeman@google.com7103f182012-10-31 20:53:49 +00001737 tchar_to_skstring(lf.lfFaceName, &info->fFontName);
Hal Canarya865d252017-11-09 16:16:09 -05001738
1739 SkOTTableOS2_V4::Type fsType;
1740 if (sizeof(fsType) == this->getTableData(SkTEndian_SwapBE32(SkOTTableOS2::TAG),
1741 offsetof(SkOTTableOS2_V4, fsType),
1742 sizeof(fsType),
1743 &fsType)) {
1744 SkOTUtils::SetAdvancedTypefaceFlags(fsType, info.get());
1745 } else {
1746 // If bit 1 is set, the font may not be embedded in a document.
1747 // If bit 1 is clear, the font can be embedded.
1748 // If bit 2 is set, the embedding is read-only.
1749 if (otm.otmfsType & 0x1) {
1750 info->fFlags |= SkAdvancedTypefaceMetrics::kNotEmbeddable_FontFlag;
1751 }
vandebo0f9bad02014-06-19 11:05:39 -07001752 }
reed@google.comac6b9792011-03-11 15:42:51 +00001753
Hal Canary209e4b12017-05-04 14:23:55 -04001754 populate_glyph_to_unicode(hdc, glyphCount, &(info->fGlyphToUnicode));
vandebo@chromium.org6744d492011-05-09 18:13:47 +00001755
vandebo@chromium.orgd41e70d2012-03-08 19:41:01 +00001756 if (glyphCount > 0 &&
1757 (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE)) {
reed@google.comac6b9792011-03-11 15:42:51 +00001758 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
1759 } else {
edisonn@google.com390c6d72013-04-06 20:26:15 +00001760 goto ReturnInfo;
reed@google.comac6b9792011-03-11 15:42:51 +00001761 }
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +00001762
reed@google.comac6b9792011-03-11 15:42:51 +00001763 // If this bit is clear the font is a fixed pitch font.
1764 if (!(otm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
1765 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1766 }
1767 if (otm.otmTextMetrics.tmItalic) {
1768 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1769 }
reed@google.comac6b9792011-03-11 15:42:51 +00001770 if (otm.otmTextMetrics.tmPitchAndFamily & FF_ROMAN) {
1771 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1772 } else if (otm.otmTextMetrics.tmPitchAndFamily & FF_SCRIPT) {
1773 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1774 }
1775
1776 // The main italic angle of the font, in tenths of a degree counterclockwise
1777 // from vertical.
1778 info->fItalicAngle = otm.otmItalicAngle / 10;
1779 info->fAscent = SkToS16(otm.otmTextMetrics.tmAscent);
1780 info->fDescent = SkToS16(-otm.otmTextMetrics.tmDescent);
1781 // TODO(ctguil): Use alternate cap height calculation.
1782 // MSDN says otmsCapEmHeight is not support but it is returning a value on
1783 // my Win7 box.
1784 info->fCapHeight = otm.otmsCapEmHeight;
1785 info->fBBox =
1786 SkIRect::MakeLTRB(otm.otmrcFontBox.left, otm.otmrcFontBox.top,
1787 otm.otmrcFontBox.right, otm.otmrcFontBox.bottom);
1788
1789 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1790 // This probably isn't very good with an italic font.
reed@google.com05b6f3a2011-11-28 15:30:28 +00001791 min_width = SHRT_MAX;
reed@google.comac6b9792011-03-11 15:42:51 +00001792 info->fStemV = 0;
reed@google.comac6b9792011-03-11 15:42:51 +00001793 for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {
1794 ABC abcWidths;
1795 if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) {
1796 int16_t width = abcWidths.abcB;
1797 if (width > 0 && width < min_width) {
1798 min_width = width;
1799 info->fStemV = min_width;
1800 }
1801 }
1802 }
1803
reed@google.comac6b9792011-03-11 15:42:51 +00001804Error:
edisonn@google.com390c6d72013-04-06 20:26:15 +00001805ReturnInfo:
reed@google.comac6b9792011-03-11 15:42:51 +00001806 SelectObject(hdc, savefont);
1807 DeleteObject(designFont);
1808 DeleteObject(font);
1809 DeleteDC(hdc);
1810
1811 return info;
1812}
1813
bungeman@google.coma5501992012-05-18 19:06:41 +00001814//Dummy representation of a Base64 encoded GUID from create_unique_font_name.
1815#define BASE64_GUID_ID "XXXXXXXXXXXXXXXXXXXXXXXX"
halcanary96fcdcc2015-08-27 07:41:13 -07001816//Length of GUID representation from create_id, including nullptr terminator.
bungeman@google.coma5501992012-05-18 19:06:41 +00001817#define BASE64_GUID_ID_LEN SK_ARRAY_COUNT(BASE64_GUID_ID)
reed@google.comac6b9792011-03-11 15:42:51 +00001818
bungeman99fe8222015-08-20 07:57:51 -07001819static_assert(BASE64_GUID_ID_LEN < LF_FACESIZE, "GUID_longer_than_facesize");
bungeman@google.coma5501992012-05-18 19:06:41 +00001820
1821/**
1822 NameID 6 Postscript names cannot have the character '/'.
1823 It would be easier to hex encode the GUID, but that is 32 bytes,
1824 and many systems have issues with names longer than 28 bytes.
1825 The following need not be any standard base64 encoding.
1826 The encoded value is never decoded.
1827*/
rmistry@google.comd6176b02012-08-23 18:14:13 +00001828static const char postscript_safe_base64_encode[] =
bungeman@google.coma5501992012-05-18 19:06:41 +00001829 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1830 "abcdefghijklmnopqrstuvwxyz"
1831 "0123456789-_=";
1832
1833/**
1834 Formats a GUID into Base64 and places it into buffer.
1835 buffer should have space for at least BASE64_GUID_ID_LEN characters.
1836 The string will always be null terminated.
1837 XXXXXXXXXXXXXXXXXXXXXXXX0
1838 */
1839static void format_guid_b64(const GUID& guid, char* buffer, size_t bufferSize) {
1840 SkASSERT(bufferSize >= BASE64_GUID_ID_LEN);
1841 size_t written = SkBase64::Encode(&guid, sizeof(guid), buffer, postscript_safe_base64_encode);
1842 SkASSERT(written < LF_FACESIZE);
1843 buffer[written] = '\0';
1844}
1845
1846/**
1847 Creates a Base64 encoded GUID and places it into buffer.
1848 buffer should have space for at least BASE64_GUID_ID_LEN characters.
1849 The string will always be null terminated.
1850 XXXXXXXXXXXXXXXXXXXXXXXX0
1851 */
1852static HRESULT create_unique_font_name(char* buffer, size_t bufferSize) {
1853 GUID guid = {};
1854 if (FAILED(CoCreateGuid(&guid))) {
1855 return E_UNEXPECTED;
1856 }
1857 format_guid_b64(guid, buffer, bufferSize);
1858
1859 return S_OK;
1860}
1861
1862/**
halcanary96fcdcc2015-08-27 07:41:13 -07001863 Introduces a font to GDI. On failure will return nullptr. The returned handle
bungeman@google.coma5501992012-05-18 19:06:41 +00001864 should eventually be passed to RemoveFontMemResourceEx.
1865*/
1866static HANDLE activate_font(SkData* fontData) {
1867 DWORD numFonts = 0;
1868 //AddFontMemResourceEx just copies the data, but does not specify const.
1869 HANDLE fontHandle = AddFontMemResourceEx(const_cast<void*>(fontData->data()),
bungeman@google.com4b18f572013-07-22 15:21:23 +00001870 static_cast<DWORD>(fontData->size()),
bungeman@google.coma5501992012-05-18 19:06:41 +00001871 0,
1872 &numFonts);
1873
halcanary96fcdcc2015-08-27 07:41:13 -07001874 if (fontHandle != nullptr && numFonts < 1) {
bungeman@google.coma5501992012-05-18 19:06:41 +00001875 RemoveFontMemResourceEx(fontHandle);
halcanary96fcdcc2015-08-27 07:41:13 -07001876 return nullptr;
bungeman@google.coma5501992012-05-18 19:06:41 +00001877 }
1878
1879 return fontHandle;
1880}
1881
scroggoa1193e42015-01-21 12:09:53 -08001882// Does not affect ownership of stream.
Mike Reed59227392017-09-26 09:46:08 -04001883static sk_sp<SkTypeface> create_from_stream(std::unique_ptr<SkStreamAsset> stream) {
bungeman@google.coma5501992012-05-18 19:06:41 +00001884 // Create a unique and unpredictable font name.
1885 // Avoids collisions and access from CSS.
1886 char familyName[BASE64_GUID_ID_LEN];
1887 const int familyNameSize = SK_ARRAY_COUNT(familyName);
1888 if (FAILED(create_unique_font_name(familyName, familyNameSize))) {
halcanary96fcdcc2015-08-27 07:41:13 -07001889 return nullptr;
bungeman@google.coma5501992012-05-18 19:06:41 +00001890 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001891
bungeman@google.coma5501992012-05-18 19:06:41 +00001892 // Change the name of the font.
Mike Reed59227392017-09-26 09:46:08 -04001893 sk_sp<SkData> rewrittenFontData(SkOTUtils::RenameFont(stream.get(), familyName, familyNameSize-1));
halcanary96fcdcc2015-08-27 07:41:13 -07001894 if (nullptr == rewrittenFontData.get()) {
1895 return nullptr;
bungeman@google.coma5501992012-05-18 19:06:41 +00001896 }
bungeman@google.coma5501992012-05-18 19:06:41 +00001897
1898 // Register the font with GDI.
bungeman@google.come9bbee32012-05-21 13:46:13 +00001899 HANDLE fontReference = activate_font(rewrittenFontData.get());
halcanary96fcdcc2015-08-27 07:41:13 -07001900 if (nullptr == fontReference) {
1901 return nullptr;
bungeman@google.coma5501992012-05-18 19:06:41 +00001902 }
1903
1904 // Create the typeface.
1905 LOGFONT lf;
reed@google.com484f5bc2013-04-24 19:14:56 +00001906 logfont_for_name(familyName, &lf);
bungeman@google.coma5501992012-05-18 19:06:41 +00001907
Mike Reed59227392017-09-26 09:46:08 -04001908 return sk_sp<SkTypeface>(SkCreateFontMemResourceTypefaceFromLOGFONT(lf, fontReference));
reed@google.comac6b9792011-03-11 15:42:51 +00001909}
1910
bungeman5f213d92015-01-27 05:39:10 -08001911SkStreamAsset* LogFontTypeface::onOpenStream(int* ttcIndex) const {
reed@google.com0042b9c2013-03-21 20:16:04 +00001912 *ttcIndex = 0;
1913
ctguil@chromium.orgf4c26222011-05-16 22:00:05 +00001914 const DWORD kTTCTag =
1915 SkEndian_SwapBE32(SkSetFourByteTag('t', 't', 'c', 'f'));
reed@google.com0042b9c2013-03-21 20:16:04 +00001916 LOGFONT lf = fLogFont;
reed@google.comac6b9792011-03-11 15:42:51 +00001917
halcanary96fcdcc2015-08-27 07:41:13 -07001918 HDC hdc = ::CreateCompatibleDC(nullptr);
reed@google.com59d2f632011-05-02 19:36:59 +00001919 HFONT font = CreateFontIndirect(&lf);
reed@google.comac6b9792011-03-11 15:42:51 +00001920 HFONT savefont = (HFONT)SelectObject(hdc, font);
1921
halcanary96fcdcc2015-08-27 07:41:13 -07001922 SkMemoryStream* stream = nullptr;
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001923 DWORD tables[2] = {kTTCTag, 0};
Chris Dalton1ef80942017-12-04 12:01:30 -07001924 for (size_t i = 0; i < SK_ARRAY_COUNT(tables); i++) {
halcanary96fcdcc2015-08-27 07:41:13 -07001925 DWORD bufferSize = GetFontData(hdc, tables[i], 0, nullptr, 0);
bungeman@google.com39698b12011-11-15 22:26:41 +00001926 if (bufferSize == GDI_ERROR) {
reed@google.com055180c2013-03-21 18:46:35 +00001927 call_ensure_accessible(lf);
halcanary96fcdcc2015-08-27 07:41:13 -07001928 bufferSize = GetFontData(hdc, tables[i], 0, nullptr, 0);
bungeman@google.com39698b12011-11-15 22:26:41 +00001929 }
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001930 if (bufferSize != GDI_ERROR) {
1931 stream = new SkMemoryStream(bufferSize);
bungeman@google.com4b18f572013-07-22 15:21:23 +00001932 if (GetFontData(hdc, tables[i], 0, (void*)stream->getMemoryBase(), bufferSize)) {
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001933 break;
1934 } else {
1935 delete stream;
halcanary96fcdcc2015-08-27 07:41:13 -07001936 stream = nullptr;
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001937 }
1938 }
reed@google.comac6b9792011-03-11 15:42:51 +00001939 }
1940
1941 SelectObject(hdc, savefont);
1942 DeleteObject(font);
1943 DeleteDC(hdc);
1944
1945 return stream;
1946}
1947
bungeman@google.com3c996f82013-10-24 21:39:35 +00001948static void bmpCharsToGlyphs(HDC hdc, const WCHAR* bmpChars, int count, uint16_t* glyphs,
1949 bool Ox1FHack)
1950{
1951 DWORD result = GetGlyphIndicesW(hdc, bmpChars, count, glyphs, GGI_MARK_NONEXISTING_GLYPHS);
1952 if (GDI_ERROR == result) {
1953 for (int i = 0; i < count; ++i) {
1954 glyphs[i] = 0;
1955 }
1956 return;
1957 }
1958
1959 if (Ox1FHack) {
1960 for (int i = 0; i < count; ++i) {
1961 if (0xFFFF == glyphs[i] || 0x1F == glyphs[i]) {
1962 glyphs[i] = 0;
1963 }
1964 }
1965 } else {
1966 for (int i = 0; i < count; ++i) {
1967 if (0xFFFF == glyphs[i]){
1968 glyphs[i] = 0;
1969 }
1970 }
1971 }
1972}
1973
1974static uint16_t nonBmpCharToGlyph(HDC hdc, SCRIPT_CACHE* scriptCache, const WCHAR utf16[2]) {
1975 uint16_t index = 0;
1976 // Use uniscribe to detemine glyph index for non-BMP characters.
1977 static const int numWCHAR = 2;
1978 static const int maxItems = 2;
halcanary96fcdcc2015-08-27 07:41:13 -07001979 // MSDN states that this can be nullptr, but some things don't work then.
Chris Dalton1ef80942017-12-04 12:01:30 -07001980 SCRIPT_CONTROL scriptControl;
1981 memset(&scriptControl, 0, sizeof(scriptControl));
bungeman@google.com3c996f82013-10-24 21:39:35 +00001982 // Add extra item to SCRIPT_ITEM to work around a bug (now documented).
1983 // https://bugzilla.mozilla.org/show_bug.cgi?id=366643
1984 SCRIPT_ITEM si[maxItems + 1];
1985 int numItems;
halcanary96fcdcc2015-08-27 07:41:13 -07001986 HRZM(ScriptItemize(utf16, numWCHAR, maxItems, &scriptControl, nullptr, si, &numItems),
bungeman@google.com3c996f82013-10-24 21:39:35 +00001987 "Could not itemize character.");
1988
1989 // Sometimes ScriptShape cannot find a glyph for a non-BMP and returns 2 space glyphs.
1990 static const int maxGlyphs = 2;
1991 SCRIPT_VISATTR vsa[maxGlyphs];
1992 WORD outGlyphs[maxGlyphs];
1993 WORD logClust[numWCHAR];
1994 int numGlyphs;
1995 HRZM(ScriptShape(hdc, scriptCache, utf16, numWCHAR, maxGlyphs, &si[0].a,
1996 outGlyphs, logClust, vsa, &numGlyphs),
1997 "Could not shape character.");
1998 if (1 == numGlyphs) {
1999 index = outGlyphs[0];
2000 }
2001 return index;
2002}
2003
2004class SkAutoHDC {
2005public:
2006 SkAutoHDC(const LOGFONT& lf)
halcanary96fcdcc2015-08-27 07:41:13 -07002007 : fHdc(::CreateCompatibleDC(nullptr))
bungeman@google.com3c996f82013-10-24 21:39:35 +00002008 , fFont(::CreateFontIndirect(&lf))
2009 , fSavefont((HFONT)SelectObject(fHdc, fFont))
2010 { }
2011 ~SkAutoHDC() {
2012 SelectObject(fHdc, fSavefont);
2013 DeleteObject(fFont);
2014 DeleteDC(fHdc);
2015 }
2016 operator HDC() { return fHdc; }
2017private:
2018 HDC fHdc;
2019 HFONT fFont;
2020 HFONT fSavefont;
2021};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +00002022#define SkAutoHDC(...) SK_REQUIRE_LOCAL_VAR(SkAutoHDC)
bungeman@google.com3c996f82013-10-24 21:39:35 +00002023
2024int LogFontTypeface::onCharsToGlyphs(const void* chars, Encoding encoding,
2025 uint16_t userGlyphs[], int glyphCount) const
2026{
2027 SkAutoHDC hdc(fLogFont);
2028
2029 TEXTMETRIC tm;
2030 if (0 == GetTextMetrics(hdc, &tm)) {
2031 call_ensure_accessible(fLogFont);
2032 if (0 == GetTextMetrics(hdc, &tm)) {
2033 tm.tmPitchAndFamily = TMPF_TRUETYPE;
2034 }
2035 }
2036 bool Ox1FHack = !(tm.tmPitchAndFamily & TMPF_VECTOR) /*&& winVer < Vista */;
2037
2038 SkAutoSTMalloc<256, uint16_t> scratchGlyphs;
2039 uint16_t* glyphs;
halcanary96fcdcc2015-08-27 07:41:13 -07002040 if (userGlyphs != nullptr) {
bungeman@google.com3c996f82013-10-24 21:39:35 +00002041 glyphs = userGlyphs;
2042 } else {
2043 glyphs = scratchGlyphs.reset(glyphCount);
2044 }
2045
2046 SCRIPT_CACHE sc = 0;
2047 switch (encoding) {
2048 case SkTypeface::kUTF8_Encoding: {
2049 static const int scratchCount = 256;
2050 WCHAR scratch[scratchCount];
2051 int glyphIndex = 0;
2052 const char* currentUtf8 = reinterpret_cast<const char*>(chars);
Kevin Lubick42846132018-01-05 10:11:11 -05002053 SkUnichar currentChar = 0;
bungeman@google.com3c996f82013-10-24 21:39:35 +00002054 if (glyphCount) {
2055 currentChar = SkUTF8_NextUnichar(&currentUtf8);
2056 }
2057 while (glyphIndex < glyphCount) {
2058 // Try a run of bmp.
2059 int glyphsLeft = SkTMin(glyphCount - glyphIndex, scratchCount);
2060 int runLength = 0;
2061 while (runLength < glyphsLeft && currentChar <= 0xFFFF) {
2062 scratch[runLength] = static_cast<WCHAR>(currentChar);
2063 ++runLength;
2064 if (runLength < glyphsLeft) {
2065 currentChar = SkUTF8_NextUnichar(&currentUtf8);
2066 }
2067 }
2068 if (runLength) {
2069 bmpCharsToGlyphs(hdc, scratch, runLength, &glyphs[glyphIndex], Ox1FHack);
2070 glyphIndex += runLength;
2071 }
2072
2073 // Try a run of non-bmp.
2074 while (glyphIndex < glyphCount && currentChar > 0xFFFF) {
2075 SkUTF16_FromUnichar(currentChar, reinterpret_cast<uint16_t*>(scratch));
2076 glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, scratch);
2077 ++glyphIndex;
2078 if (glyphIndex < glyphCount) {
2079 currentChar = SkUTF8_NextUnichar(&currentUtf8);
2080 }
2081 }
2082 }
2083 break;
2084 }
2085 case SkTypeface::kUTF16_Encoding: {
2086 int glyphIndex = 0;
2087 const WCHAR* currentUtf16 = reinterpret_cast<const WCHAR*>(chars);
2088 while (glyphIndex < glyphCount) {
2089 // Try a run of bmp.
2090 int glyphsLeft = glyphCount - glyphIndex;
2091 int runLength = 0;
2092 while (runLength < glyphsLeft && !SkUTF16_IsHighSurrogate(currentUtf16[runLength])) {
2093 ++runLength;
2094 }
2095 if (runLength) {
2096 bmpCharsToGlyphs(hdc, currentUtf16, runLength, &glyphs[glyphIndex], Ox1FHack);
2097 glyphIndex += runLength;
2098 currentUtf16 += runLength;
2099 }
2100
2101 // Try a run of non-bmp.
2102 while (glyphIndex < glyphCount && SkUTF16_IsHighSurrogate(*currentUtf16)) {
2103 glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, currentUtf16);
2104 ++glyphIndex;
2105 currentUtf16 += 2;
2106 }
2107 }
2108 break;
2109 }
2110 case SkTypeface::kUTF32_Encoding: {
2111 static const int scratchCount = 256;
2112 WCHAR scratch[scratchCount];
2113 int glyphIndex = 0;
2114 const uint32_t* utf32 = reinterpret_cast<const uint32_t*>(chars);
2115 while (glyphIndex < glyphCount) {
2116 // Try a run of bmp.
2117 int glyphsLeft = SkTMin(glyphCount - glyphIndex, scratchCount);
2118 int runLength = 0;
2119 while (runLength < glyphsLeft && utf32[glyphIndex + runLength] <= 0xFFFF) {
2120 scratch[runLength] = static_cast<WCHAR>(utf32[glyphIndex + runLength]);
2121 ++runLength;
2122 }
2123 if (runLength) {
2124 bmpCharsToGlyphs(hdc, scratch, runLength, &glyphs[glyphIndex], Ox1FHack);
2125 glyphIndex += runLength;
2126 }
2127
2128 // Try a run of non-bmp.
2129 while (glyphIndex < glyphCount && utf32[glyphIndex] > 0xFFFF) {
2130 SkUTF16_FromUnichar(utf32[glyphIndex], reinterpret_cast<uint16_t*>(scratch));
2131 glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, scratch);
2132 ++glyphIndex;
2133 }
2134 }
2135 break;
2136 }
2137 default:
djsollenf2b340f2016-01-29 08:51:04 -08002138 SK_ABORT("Invalid Text Encoding");
bungeman@google.com3c996f82013-10-24 21:39:35 +00002139 }
2140
2141 if (sc) {
2142 ::ScriptFreeCache(&sc);
2143 }
2144
2145 for (int i = 0; i < glyphCount; ++i) {
2146 if (0 == glyphs[i]) {
2147 return i;
2148 }
2149 }
2150 return glyphCount;
2151}
2152
bungeman@google.com7bdd6142013-07-15 19:42:57 +00002153int LogFontTypeface::onCountGlyphs() const {
halcanary96fcdcc2015-08-27 07:41:13 -07002154 HDC hdc = ::CreateCompatibleDC(nullptr);
bungeman@google.com7bdd6142013-07-15 19:42:57 +00002155 HFONT font = CreateFontIndirect(&fLogFont);
2156 HFONT savefont = (HFONT)SelectObject(hdc, font);
2157
2158 unsigned int glyphCount = calculateGlyphCount(hdc, fLogFont);
2159
2160 SelectObject(hdc, savefont);
2161 DeleteObject(font);
2162 DeleteDC(hdc);
2163
2164 return glyphCount;
2165}
2166
2167int LogFontTypeface::onGetUPEM() const {
halcanary96fcdcc2015-08-27 07:41:13 -07002168 HDC hdc = ::CreateCompatibleDC(nullptr);
bungeman@google.com7bdd6142013-07-15 19:42:57 +00002169 HFONT font = CreateFontIndirect(&fLogFont);
2170 HFONT savefont = (HFONT)SelectObject(hdc, font);
2171
2172 unsigned int upem = calculateUPEM(hdc, fLogFont);
2173
2174 SelectObject(hdc, savefont);
2175 DeleteObject(font);
2176 DeleteDC(hdc);
2177
2178 return upem;
2179}
2180
bungeman@google.com839702b2013-08-07 17:09:22 +00002181SkTypeface::LocalizedStrings* LogFontTypeface::onCreateFamilyNameIterator() const {
bungeman@google.coma9802692013-08-07 02:45:25 +00002182 SkTypeface::LocalizedStrings* nameIter =
2183 SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*this);
halcanary96fcdcc2015-08-27 07:41:13 -07002184 if (nullptr == nameIter) {
bungeman@google.coma9802692013-08-07 02:45:25 +00002185 SkString familyName;
2186 this->getFamilyName(&familyName);
2187 SkString language("und"); //undetermined
2188 nameIter = new SkOTUtils::LocalizedStrings_SingleName(familyName, language);
2189 }
2190 return nameIter;
2191}
2192
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002193int LogFontTypeface::onGetTableTags(SkFontTableTag tags[]) const {
2194 SkSFNTHeader header;
2195 if (sizeof(header) != this->onGetTableData(0, 0, sizeof(header), &header)) {
2196 return 0;
2197 }
2198
2199 int numTables = SkEndian_SwapBE16(header.numTables);
2200
2201 if (tags) {
2202 size_t size = numTables * sizeof(SkSFNTHeader::TableDirectoryEntry);
2203 SkAutoSTMalloc<0x20, SkSFNTHeader::TableDirectoryEntry> dir(numTables);
2204 if (size != this->onGetTableData(0, sizeof(header), size, dir.get())) {
2205 return 0;
2206 }
2207
2208 for (int i = 0; i < numTables; ++i) {
2209 tags[i] = SkEndian_SwapBE32(dir[i].tag);
2210 }
2211 }
2212 return numTables;
2213}
2214
2215size_t LogFontTypeface::onGetTableData(SkFontTableTag tag, size_t offset,
2216 size_t length, void* data) const
2217{
2218 LOGFONT lf = fLogFont;
2219
halcanary96fcdcc2015-08-27 07:41:13 -07002220 HDC hdc = ::CreateCompatibleDC(nullptr);
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002221 HFONT font = CreateFontIndirect(&lf);
2222 HFONT savefont = (HFONT)SelectObject(hdc, font);
2223
2224 tag = SkEndian_SwapBE32(tag);
halcanary96fcdcc2015-08-27 07:41:13 -07002225 if (nullptr == data) {
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002226 length = 0;
2227 }
robertphillips@google.com8b169312013-10-15 17:47:36 +00002228 DWORD bufferSize = GetFontData(hdc, tag, (DWORD) offset, data, (DWORD) length);
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002229 if (bufferSize == GDI_ERROR) {
2230 call_ensure_accessible(lf);
robertphillips@google.com8b169312013-10-15 17:47:36 +00002231 bufferSize = GetFontData(hdc, tag, (DWORD) offset, data, (DWORD) length);
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002232 }
2233
2234 SelectObject(hdc, savefont);
2235 DeleteObject(font);
2236 DeleteDC(hdc);
2237
2238 return bufferSize == GDI_ERROR ? 0 : bufferSize;
2239}
2240
reeda9322c22016-04-12 06:47:05 -07002241SkScalerContext* LogFontTypeface::onCreateScalerContext(const SkScalerContextEffects& effects,
2242 const SkDescriptor* desc) const {
bungeman7cfd46a2016-10-20 16:06:52 -04002243 auto ctx = skstd::make_unique<SkScalerContext_GDI>(
2244 sk_ref_sp(const_cast<LogFontTypeface*>(this)), effects, desc);
reed@google.com84e22d82013-07-10 15:38:20 +00002245 if (!ctx->isValid()) {
Ben Wagnerc05b2bf2016-11-03 16:51:26 -04002246 return nullptr;
reed@google.com84e22d82013-07-10 15:38:20 +00002247 }
bungeman7cfd46a2016-10-20 16:06:52 -04002248 return ctx.release();
reed@google.comac6b9792011-03-11 15:42:51 +00002249}
2250
reed@google.com0da48612013-03-19 16:06:52 +00002251void LogFontTypeface::onFilterRec(SkScalerContextRec* rec) const {
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00002252 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
2253 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
2254 {
2255 rec->fMaskFormat = SkMask::kA8_Format;
2256 rec->fFlags |= SkScalerContext::kGenA8FromLCD_Flag;
2257 }
2258
bungeman41078062014-07-07 08:16:37 -07002259 unsigned flagsWeDontSupport = SkScalerContext::kVertical_Flag |
2260 SkScalerContext::kDevKernText_Flag |
bungeman@google.comf6f56872014-01-23 19:01:36 +00002261 SkScalerContext::kForceAutohinting_Flag |
reed@google.come8fab012011-07-13 15:25:33 +00002262 SkScalerContext::kEmbeddedBitmapText_Flag |
2263 SkScalerContext::kEmbolden_Flag |
2264 SkScalerContext::kLCD_BGROrder_Flag |
2265 SkScalerContext::kLCD_Vertical_Flag;
2266 rec->fFlags &= ~flagsWeDontSupport;
2267
reed@google.come8fab012011-07-13 15:25:33 +00002268 SkPaint::Hinting h = rec->getHinting();
2269 switch (h) {
2270 case SkPaint::kNo_Hinting:
bungeman@google.com0abbff92013-07-27 20:37:56 +00002271 break;
reed@google.come8fab012011-07-13 15:25:33 +00002272 case SkPaint::kSlight_Hinting:
bungeman@google.com0abbff92013-07-27 20:37:56 +00002273 // Only do slight hinting when axis aligned.
bungeman@google.com7c86d8e2013-07-27 21:42:21 +00002274 // TODO: re-enable slight hinting when FontHostTest can pass.
2275 //if (!isAxisAligned(*rec)) {
bungeman@google.com0abbff92013-07-27 20:37:56 +00002276 h = SkPaint::kNo_Hinting;
bungeman@google.com7c86d8e2013-07-27 21:42:21 +00002277 //}
reed@google.come8fab012011-07-13 15:25:33 +00002278 break;
2279 case SkPaint::kNormal_Hinting:
2280 case SkPaint::kFull_Hinting:
bungeman@google.com0abbff92013-07-27 20:37:56 +00002281 // TODO: need to be able to distinguish subpixel positioned glyphs
2282 // and linear metrics.
2283 //rec->fFlags &= ~SkScalerContext::kSubpixelPositioning_Flag;
reed@google.come8fab012011-07-13 15:25:33 +00002284 h = SkPaint::kNormal_Hinting;
2285 break;
2286 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002287 SkDEBUGFAIL("unknown hinting");
reed@google.come8fab012011-07-13 15:25:33 +00002288 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00002289 //TODO: if this is a bitmap font, squash hinting and subpixel.
reed@google.come8fab012011-07-13 15:25:33 +00002290 rec->setHinting(h);
reed@google.comda440672011-07-13 18:02:28 +00002291
reed@google.com9181aa82011-08-05 14:28:31 +00002292// turn this off since GDI might turn A8 into BW! Need a bigger fix.
2293#if 0
reed@google.comda440672011-07-13 18:02:28 +00002294 // Disable LCD when rotated, since GDI's output is ugly
2295 if (isLCD(*rec) && !isAxisAligned(*rec)) {
2296 rec->fMaskFormat = SkMask::kA8_Format;
2297 }
reed@google.com9181aa82011-08-05 14:28:31 +00002298#endif
reed@google.com754e4eb2011-09-26 13:21:39 +00002299
reed@google.com0da48612013-03-19 16:06:52 +00002300 if (!fCanBeLCD && isLCD(*rec)) {
bungeman@google.comcb1bbb32012-10-12 18:48:35 +00002301 rec->fMaskFormat = SkMask::kA8_Format;
2302 rec->fFlags &= ~SkScalerContext::kGenA8FromLCD_Flag;
reed@google.com754e4eb2011-09-26 13:21:39 +00002303 }
reed@google.com754e4eb2011-09-26 13:21:39 +00002304}
reed@google.com070da5e2013-03-27 20:01:49 +00002305
2306///////////////////////////////////////////////////////////////////////////////
2307
2308#include "SkFontMgr.h"
reed@google.com484f5bc2013-04-24 19:14:56 +00002309#include "SkDataTable.h"
2310
bungeman@google.coma9802692013-08-07 02:45:25 +00002311static bool valid_logfont_for_enum(const LOGFONT& lf) {
2312 // TODO: Vector FON is unsupported and should not be listed.
2313 return
2314 // Ignore implicit vertical variants.
2315 lf.lfFaceName[0] && lf.lfFaceName[0] != '@'
2316
2317 // DEFAULT_CHARSET is used to get all fonts, but also implies all
2318 // character sets. Filter assuming all fonts support ANSI_CHARSET.
2319 && ANSI_CHARSET == lf.lfCharSet
2320 ;
reed@google.com484f5bc2013-04-24 19:14:56 +00002321}
2322
bungeman@google.coma9802692013-08-07 02:45:25 +00002323/** An EnumFontFamExProc implementation which interprets builderParam as
2324 * an SkTDArray<ENUMLOGFONTEX>* and appends logfonts which
2325 * pass the valid_logfont_for_enum predicate.
2326 */
2327static int CALLBACK enum_family_proc(const LOGFONT* lf, const TEXTMETRIC*,
2328 DWORD fontType, LPARAM builderParam) {
2329 if (valid_logfont_for_enum(*lf)) {
reed@google.coma65a6812013-05-02 19:47:24 +00002330 SkTDArray<ENUMLOGFONTEX>* array = (SkTDArray<ENUMLOGFONTEX>*)builderParam;
2331 *array->append() = *(ENUMLOGFONTEX*)lf;
reed@google.com484f5bc2013-04-24 19:14:56 +00002332 }
2333 return 1; // non-zero means continue
2334}
2335
reed@google.com484f5bc2013-04-24 19:14:56 +00002336class SkFontStyleSetGDI : public SkFontStyleSet {
2337public:
reed@google.coma65a6812013-05-02 19:47:24 +00002338 SkFontStyleSetGDI(const TCHAR familyName[]) {
bungeman@google.coma9802692013-08-07 02:45:25 +00002339 LOGFONT lf;
2340 sk_bzero(&lf, sizeof(lf));
2341 lf.lfCharSet = DEFAULT_CHARSET;
2342 _tcscpy_s(lf.lfFaceName, familyName);
2343
halcanary96fcdcc2015-08-27 07:41:13 -07002344 HDC hdc = ::CreateCompatibleDC(nullptr);
bungeman@google.coma9802692013-08-07 02:45:25 +00002345 ::EnumFontFamiliesEx(hdc, &lf, enum_family_proc, (LPARAM)&fArray, 0);
reed@google.com484f5bc2013-04-24 19:14:56 +00002346 ::DeleteDC(hdc);
2347 }
2348
mtklein36352bf2015-03-25 18:17:31 -07002349 int count() override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002350 return fArray.count();
2351 }
2352
mtklein36352bf2015-03-25 18:17:31 -07002353 void getStyle(int index, SkFontStyle* fs, SkString* styleName) override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002354 if (fs) {
bungemana4c4a2d2014-10-20 13:33:19 -07002355 *fs = get_style(fArray[index].elfLogFont);
reed@google.com484f5bc2013-04-24 19:14:56 +00002356 }
reed@google.coma65a6812013-05-02 19:47:24 +00002357 if (styleName) {
2358 const ENUMLOGFONTEX& ref = fArray[index];
2359 // For some reason, ENUMLOGFONTEX and LOGFONT disagree on their type in the
2360 // non-unicode version.
2361 // ENUMLOGFONTEX uses BYTE
2362 // LOGFONT uses CHAR
2363 // Here we assert they that the style name is logically the same (size) as
2364 // a TCHAR, so we can use the same converter function.
2365 SkASSERT(sizeof(TCHAR) == sizeof(ref.elfStyle[0]));
2366 tchar_to_skstring((const TCHAR*)ref.elfStyle, styleName);
2367 }
reed@google.com484f5bc2013-04-24 19:14:56 +00002368 }
2369
mtklein36352bf2015-03-25 18:17:31 -07002370 SkTypeface* createTypeface(int index) override {
reed@google.coma65a6812013-05-02 19:47:24 +00002371 return SkCreateTypefaceFromLOGFONT(fArray[index].elfLogFont);
reed@google.com484f5bc2013-04-24 19:14:56 +00002372 }
2373
mtklein36352bf2015-03-25 18:17:31 -07002374 SkTypeface* matchStyle(const SkFontStyle& pattern) override {
bungeman147ea2f2015-11-12 09:50:08 -08002375 return this->matchStyleCSS3(pattern);
reed@google.com484f5bc2013-04-24 19:14:56 +00002376 }
2377
2378private:
reed@google.coma65a6812013-05-02 19:47:24 +00002379 SkTDArray<ENUMLOGFONTEX> fArray;
reed@google.com484f5bc2013-04-24 19:14:56 +00002380};
2381
reed@google.com484f5bc2013-04-24 19:14:56 +00002382class SkFontMgrGDI : public SkFontMgr {
bungeman@google.coma9802692013-08-07 02:45:25 +00002383public:
2384 SkFontMgrGDI() {
reed@google.com484f5bc2013-04-24 19:14:56 +00002385 LOGFONT lf;
2386 sk_bzero(&lf, sizeof(lf));
2387 lf.lfCharSet = DEFAULT_CHARSET;
2388
halcanary96fcdcc2015-08-27 07:41:13 -07002389 HDC hdc = ::CreateCompatibleDC(nullptr);
reed@google.com484f5bc2013-04-24 19:14:56 +00002390 ::EnumFontFamiliesEx(hdc, &lf, enum_family_proc, (LPARAM)&fLogFontArray, 0);
2391 ::DeleteDC(hdc);
2392 }
2393
reed@google.com484f5bc2013-04-24 19:14:56 +00002394protected:
mtklein36352bf2015-03-25 18:17:31 -07002395 int onCountFamilies() const override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002396 return fLogFontArray.count();
2397 }
2398
mtklein36352bf2015-03-25 18:17:31 -07002399 void onGetFamilyName(int index, SkString* familyName) const override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002400 SkASSERT((unsigned)index < (unsigned)fLogFontArray.count());
reed@google.coma65a6812013-05-02 19:47:24 +00002401 tchar_to_skstring(fLogFontArray[index].elfLogFont.lfFaceName, familyName);
reed@google.com484f5bc2013-04-24 19:14:56 +00002402 }
2403
mtklein36352bf2015-03-25 18:17:31 -07002404 SkFontStyleSet* onCreateStyleSet(int index) const override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002405 SkASSERT((unsigned)index < (unsigned)fLogFontArray.count());
halcanary385fe4d2015-08-26 13:07:48 -07002406 return new SkFontStyleSetGDI(fLogFontArray[index].elfLogFont.lfFaceName);
reed@google.com484f5bc2013-04-24 19:14:56 +00002407 }
2408
mtklein36352bf2015-03-25 18:17:31 -07002409 SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
halcanary96fcdcc2015-08-27 07:41:13 -07002410 if (nullptr == familyName) {
reed@google.com484f5bc2013-04-24 19:14:56 +00002411 familyName = ""; // do we need this check???
2412 }
2413 LOGFONT lf;
2414 logfont_for_name(familyName, &lf);
halcanary385fe4d2015-08-26 13:07:48 -07002415 return new SkFontStyleSetGDI(lf.lfFaceName);
reed@google.com484f5bc2013-04-24 19:14:56 +00002416 }
2417
reed@google.com484f5bc2013-04-24 19:14:56 +00002418 virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
mtklein36352bf2015-03-25 18:17:31 -07002419 const SkFontStyle& fontstyle) const override {
reed@google.com437eea12013-04-25 20:40:02 +00002420 // could be in base impl
Hal Canary67b39de2016-11-07 11:47:44 -05002421 sk_sp<SkFontStyleSet> sset(this->matchFamily(familyName));
reed@google.com484f5bc2013-04-24 19:14:56 +00002422 return sset->matchStyle(fontstyle);
2423 }
2424
djsollen33068c12014-11-14 10:52:53 -08002425 virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
2426 const char* bcp47[], int bcp47Count,
mtklein36352bf2015-03-25 18:17:31 -07002427 SkUnichar character) const override {
halcanary96fcdcc2015-08-27 07:41:13 -07002428 return nullptr;
djsollen33068c12014-11-14 10:52:53 -08002429 }
2430
reed@google.com484f5bc2013-04-24 19:14:56 +00002431 virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
mtklein36352bf2015-03-25 18:17:31 -07002432 const SkFontStyle& fontstyle) const override {
reed@google.com437eea12013-04-25 20:40:02 +00002433 // could be in base impl
reed@google.com484f5bc2013-04-24 19:14:56 +00002434 SkString familyName;
2435 ((LogFontTypeface*)familyMember)->getFamilyName(&familyName);
2436 return this->matchFamilyStyle(familyName.c_str(), fontstyle);
2437 }
2438
Mike Reed59227392017-09-26 09:46:08 -04002439 sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,
2440 int ttcIndex) const override {
Ben Wagnerfc497342017-02-24 11:15:26 -05002441 if (ttcIndex != 0) {
2442 return nullptr;
2443 }
Mike Reed59227392017-09-26 09:46:08 -04002444 return create_from_stream(std::move(stream));
reed@google.com484f5bc2013-04-24 19:14:56 +00002445 }
reed@google.com437eea12013-04-25 20:40:02 +00002446
Mike Reed59227392017-09-26 09:46:08 -04002447 sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData> data, int ttcIndex) const override {
reed@google.com437eea12013-04-25 20:40:02 +00002448 // could be in base impl
Mike Reed59227392017-09-26 09:46:08 -04002449 return this->makeFromStream(std::unique_ptr<SkStreamAsset>(new SkMemoryStream(std::move(data))),
2450 ttcIndex);
reed@google.com484f5bc2013-04-24 19:14:56 +00002451 }
reed@google.com437eea12013-04-25 20:40:02 +00002452
Mike Reed59227392017-09-26 09:46:08 -04002453 sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override {
reed@google.com437eea12013-04-25 20:40:02 +00002454 // could be in base impl
Mike Reed59227392017-09-26 09:46:08 -04002455 auto stream = SkStream::MakeFromFile(path);
2456 return stream ? this->makeFromStream(std::move(stream), ttcIndex) : nullptr;
reed@google.com484f5bc2013-04-24 19:14:56 +00002457 }
2458
Mike Reed59227392017-09-26 09:46:08 -04002459 sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const override {
bungeman@google.comf7159bb2013-11-20 15:11:05 +00002460 LOGFONT lf;
halcanary96fcdcc2015-08-27 07:41:13 -07002461 if (nullptr == familyName) {
bungeman@google.comf7159bb2013-11-20 15:11:05 +00002462 lf = get_default_font();
2463 } else {
2464 logfont_for_name(familyName, &lf);
2465 }
bungemana4c4a2d2014-10-20 13:33:19 -07002466
bungeman11a77c62016-04-12 13:45:06 -07002467 lf.lfWeight = style.weight();
bungemanb4bb7d82016-04-27 10:21:04 -07002468 lf.lfItalic = style.slant() == SkFontStyle::kUpright_Slant ? FALSE : TRUE;
Mike Reed59227392017-09-26 09:46:08 -04002469 return sk_sp<SkTypeface>(SkCreateTypefaceFromLOGFONT(lf));
reed@google.com30ddd612013-07-30 17:47:39 +00002470 }
2471
reed@google.com484f5bc2013-04-24 19:14:56 +00002472private:
reed@google.coma65a6812013-05-02 19:47:24 +00002473 SkTDArray<ENUMLOGFONTEX> fLogFontArray;
reed@google.com484f5bc2013-04-24 19:14:56 +00002474};
reed@google.com070da5e2013-03-27 20:01:49 +00002475
reed@google.com30ddd612013-07-30 17:47:39 +00002476///////////////////////////////////////////////////////////////////////////////
2477
Ben Wagner3546ff12017-01-03 13:32:36 -05002478sk_sp<SkFontMgr> SkFontMgr_New_GDI() { return sk_make_sp<SkFontMgrGDI>(); }
mtklein1ee76512015-11-02 10:20:27 -08002479
Mike Klein8f11d4d2018-01-24 12:42:55 -05002480#endif//defined(SK_BUILD_FOR_WIN)