blob: a2d9da7a39461ce4d7e1d209a744e74816ccbb3e [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"
9#if defined(SK_BUILD_FOR_WIN32)
10
reed@google.comac6b9792011-03-11 15:42:51 +000011#include "SkAdvancedTypefaceMetrics.h"
bungeman@google.coma5501992012-05-18 19:06:41 +000012#include "SkBase64.h"
bungeman@google.com1bfe01d2012-08-28 16:02:42 +000013#include "SkColorPriv.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"
bungeman@google.com97efada2012-07-30 20:40:50 +000019#include "SkMaskGamma.h"
bungeman@google.comd3fbd342014-04-15 15:52:07 +000020#include "SkMatrix22.h"
bungeman@google.com7bdd6142013-07-15 19:42:57 +000021#include "SkOTTable_maxp.h"
bungeman@google.coma9802692013-08-07 02:45:25 +000022#include "SkOTTable_name.h"
bungeman@google.coma5501992012-05-18 19:06:41 +000023#include "SkOTUtils.h"
reed@google.com27889872012-08-07 16:15:13 +000024#include "SkPath.h"
bungeman@google.comb10b51f2013-08-01 20:18:41 +000025#include "SkSFNTHeader.h"
reed@google.comac6b9792011-03-11 15:42:51 +000026#include "SkStream.h"
bungeman@google.coma5501992012-05-18 19:06:41 +000027#include "SkString.h"
bungeman@google.com05a729f2013-06-20 15:29:16 +000028#include "SkTemplates.h"
reed@google.comac6b9792011-03-11 15:42:51 +000029#include "SkTypeface_win.h"
reed@google.com59d2f632011-05-02 19:36:59 +000030#include "SkTypefaceCache.h"
reed@google.comac6b9792011-03-11 15:42:51 +000031#include "SkUtils.h"
32
bungeman@google.coma5501992012-05-18 19:06:41 +000033#include "SkTypes.h"
34#include <tchar.h>
35#include <usp10.h>
36#include <objbase.h>
reed@google.comac6b9792011-03-11 15:42:51 +000037
reed@google.comf210f502013-03-20 14:15:52 +000038static void (*gEnsureLOGFONTAccessibleProc)(const LOGFONT&);
39
40void SkTypeface_SetEnsureLOGFONTAccessibleProc(void (*proc)(const LOGFONT&)) {
41 gEnsureLOGFONTAccessibleProc = proc;
42}
43
reed@google.com055180c2013-03-21 18:46:35 +000044static void call_ensure_accessible(const LOGFONT& lf) {
45 if (gEnsureLOGFONTAccessibleProc) {
46 gEnsureLOGFONTAccessibleProc(lf);
47 }
48}
49
50///////////////////////////////////////////////////////////////////////////////
51
reed@google.com6f5df482011-09-28 20:33:24 +000052// always packed xxRRGGBB
53typedef uint32_t SkGdiRGB;
54
reed@google.coma767fa02011-08-05 21:40:26 +000055// define this in your Makefile or .gyp to enforce AA requests
56// which GDI ignores at small sizes. This flag guarantees AA
57// for rotated text, regardless of GDI's notions.
58//#define SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
59
reed@google.com82a34d82011-07-26 19:33:08 +000060static bool isLCD(const SkScalerContext::Rec& rec) {
reedd54d3fc2014-11-13 14:39:58 -080061 return SkMask::kLCD16_Format == rec.fMaskFormat;
reed@google.com82a34d82011-07-26 19:33:08 +000062}
63
reed@google.coma767fa02011-08-05 21:40:26 +000064static bool bothZero(SkScalar a, SkScalar b) {
65 return 0 == a && 0 == b;
66}
67
68// returns false if there is any non-90-rotation or skew
69static bool isAxisAligned(const SkScalerContext::Rec& rec) {
70 return 0 == rec.fPreSkewX &&
71 (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
72 bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
73}
74
75static bool needToRenderWithSkia(const SkScalerContext::Rec& rec) {
76#ifdef SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
77 // What we really want to catch is when GDI will ignore the AA request and give
78 // us BW instead. Smallish rotated text is one heuristic, so this code is just
79 // an approximation. We shouldn't need to do this for larger sizes, but at those
80 // sizes, the quality difference gets less and less between our general
81 // scanconverter and GDI's.
82 if (SkMask::kA8_Format == rec.fMaskFormat && !isAxisAligned(rec)) {
83 return true;
84 }
85#endif
bungeman@google.com0abbff92013-07-27 20:37:56 +000086 return rec.getHinting() == SkPaint::kNo_Hinting || rec.getHinting() == SkPaint::kSlight_Hinting;
reed@google.coma767fa02011-08-05 21:40:26 +000087}
88
reed@google.coma65a6812013-05-02 19:47:24 +000089static void tchar_to_skstring(const TCHAR t[], SkString* s) {
reed@google.com484f5bc2013-04-24 19:14:56 +000090#ifdef UNICODE
halcanary96fcdcc2015-08-27 07:41:13 -070091 size_t sSize = WideCharToMultiByte(CP_UTF8, 0, t, -1, nullptr, 0, nullptr, nullptr);
reed@google.com484f5bc2013-04-24 19:14:56 +000092 s->resize(sSize);
halcanary96fcdcc2015-08-27 07:41:13 -070093 WideCharToMultiByte(CP_UTF8, 0, t, -1, s->writable_str(), sSize, nullptr, nullptr);
reed@google.com484f5bc2013-04-24 19:14:56 +000094#else
95 s->set(t);
96#endif
97}
98
bungeman@google.coma9802692013-08-07 02:45:25 +000099static void dcfontname_to_skstring(HDC deviceContext, const LOGFONT& lf, SkString* familyName) {
100 int fontNameLen; //length of fontName in TCHARS.
halcanary96fcdcc2015-08-27 07:41:13 -0700101 if (0 == (fontNameLen = GetTextFace(deviceContext, 0, nullptr))) {
bungeman@google.coma9802692013-08-07 02:45:25 +0000102 call_ensure_accessible(lf);
halcanary96fcdcc2015-08-27 07:41:13 -0700103 if (0 == (fontNameLen = GetTextFace(deviceContext, 0, nullptr))) {
bungeman@google.coma9802692013-08-07 02:45:25 +0000104 fontNameLen = 0;
105 }
106 }
107
108 SkAutoSTArray<LF_FULLFACESIZE, TCHAR> fontName(fontNameLen+1);
109 if (0 == GetTextFace(deviceContext, fontNameLen, fontName.get())) {
110 call_ensure_accessible(lf);
111 if (0 == GetTextFace(deviceContext, fontNameLen, fontName.get())) {
112 fontName[0] = 0;
113 }
114 }
115
116 tchar_to_skstring(fontName.get(), familyName);
117}
118
reed@google.comac6b9792011-03-11 15:42:51 +0000119static void make_canonical(LOGFONT* lf) {
bungeman@google.com53cbb0b2013-09-08 19:36:58 +0000120 lf->lfHeight = -64;
bungeman59f093d2016-03-22 10:59:09 -0700121 lf->lfWidth = 0; // lfWidth is related to lfHeight, not to the OS/2::usWidthClass.
reed@google.com59d2f632011-05-02 19:36:59 +0000122 lf->lfQuality = CLEARTYPE_QUALITY;//PROOF_QUALITY;
123 lf->lfCharSet = DEFAULT_CHARSET;
reed@google.com82a34d82011-07-26 19:33:08 +0000124// lf->lfClipPrecision = 64;
reed@google.com59d2f632011-05-02 19:36:59 +0000125}
126
bungemana4c4a2d2014-10-20 13:33:19 -0700127static SkFontStyle get_style(const LOGFONT& lf) {
128 return SkFontStyle(lf.lfWeight,
bungeman59f093d2016-03-22 10:59:09 -0700129 SkFontStyle::kNormal_Width,
bungemana4c4a2d2014-10-20 13:33:19 -0700130 lf.lfItalic ? SkFontStyle::kItalic_Slant : SkFontStyle::kUpright_Slant);
reed@google.comac6b9792011-03-11 15:42:51 +0000131}
132
133static inline FIXED SkFixedToFIXED(SkFixed x) {
134 return *(FIXED*)(&x);
135}
bungeman@google.coma0319f62012-04-18 15:40:50 +0000136static inline SkFixed SkFIXEDToFixed(FIXED x) {
137 return *(SkFixed*)(&x);
138}
reed@google.comac6b9792011-03-11 15:42:51 +0000139
140static inline FIXED SkScalarToFIXED(SkScalar x) {
141 return SkFixedToFIXED(SkScalarToFixed(x));
142}
143
bungeman@google.com4732df62014-01-23 15:22:42 +0000144static inline SkScalar SkFIXEDToScalar(FIXED x) {
145 return SkFixedToScalar(SkFIXEDToFixed(x));
146}
147
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000148static unsigned calculateGlyphCount(HDC hdc, const LOGFONT& lf) {
149 TEXTMETRIC textMetric;
150 if (0 == GetTextMetrics(hdc, &textMetric)) {
151 textMetric.tmPitchAndFamily = TMPF_VECTOR;
152 call_ensure_accessible(lf);
153 GetTextMetrics(hdc, &textMetric);
154 }
155
156 if (!(textMetric.tmPitchAndFamily & TMPF_VECTOR)) {
157 return textMetric.tmLastChar;
158 }
159
reed@google.comac6b9792011-03-11 15:42:51 +0000160 // The 'maxp' table stores the number of glyphs at offset 4, in 2 bytes.
reed@google.comac6b9792011-03-11 15:42:51 +0000161 uint16_t glyphs;
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000162 if (GDI_ERROR != GetFontData(hdc, SkOTTableMaximumProfile::TAG, 4, &glyphs, sizeof(glyphs))) {
reed@google.comac6b9792011-03-11 15:42:51 +0000163 return SkEndian_SwapBE16(glyphs);
164 }
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000165
reed@google.comac6b9792011-03-11 15:42:51 +0000166 // Binary search for glyph count.
167 static const MAT2 mat2 = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
168 int32_t max = SK_MaxU16 + 1;
169 int32_t min = 0;
170 GLYPHMETRICS gm;
171 while (min < max) {
172 int32_t mid = min + ((max - min) / 2);
173 if (GetGlyphOutlineW(hdc, mid, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0,
halcanary96fcdcc2015-08-27 07:41:13 -0700174 nullptr, &mat2) == GDI_ERROR) {
reed@google.comac6b9792011-03-11 15:42:51 +0000175 max = mid;
176 } else {
177 min = mid + 1;
178 }
179 }
180 SkASSERT(min == max);
181 return min;
182}
183
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000184static unsigned calculateUPEM(HDC hdc, const LOGFONT& lf) {
185 TEXTMETRIC textMetric;
186 if (0 == GetTextMetrics(hdc, &textMetric)) {
187 textMetric.tmPitchAndFamily = TMPF_VECTOR;
188 call_ensure_accessible(lf);
189 GetTextMetrics(hdc, &textMetric);
190 }
191
192 if (!(textMetric.tmPitchAndFamily & TMPF_VECTOR)) {
193 return textMetric.tmMaxCharWidth;
194 }
195
196 OUTLINETEXTMETRIC otm;
197 unsigned int otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
198 if (0 == otmRet) {
199 call_ensure_accessible(lf);
200 otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
201 }
202
203 return (0 == otmRet) ? 0 : otm.otmEMSquare;
204}
205
reed@google.comac6b9792011-03-11 15:42:51 +0000206class LogFontTypeface : public SkTypeface {
reed@google.comac6b9792011-03-11 15:42:51 +0000207public:
bungemana4c4a2d2014-10-20 13:33:19 -0700208 LogFontTypeface(const SkFontStyle& style, const LOGFONT& lf, bool serializeAsStream)
bungemane3aea102016-07-13 05:16:58 -0700209 : SkTypeface(style, false)
bungemana4c4a2d2014-10-20 13:33:19 -0700210 , fLogFont(lf)
211 , fSerializeAsStream(serializeAsStream)
212 {
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000213 HFONT font = CreateFontIndirect(&lf);
214
halcanary96fcdcc2015-08-27 07:41:13 -0700215 HDC deviceContext = ::CreateCompatibleDC(nullptr);
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000216 HFONT savefont = (HFONT)SelectObject(deviceContext, font);
217
218 TEXTMETRIC textMetric;
219 if (0 == GetTextMetrics(deviceContext, &textMetric)) {
reed@google.com055180c2013-03-21 18:46:35 +0000220 call_ensure_accessible(lf);
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000221 if (0 == GetTextMetrics(deviceContext, &textMetric)) {
222 textMetric.tmPitchAndFamily = TMPF_TRUETYPE;
223 }
224 }
225 if (deviceContext) {
226 ::SelectObject(deviceContext, savefont);
227 ::DeleteDC(deviceContext);
228 }
229 if (font) {
230 ::DeleteObject(font);
231 }
232
bungeman@google.comfe747652013-03-25 19:36:11 +0000233 // The fixed pitch bit is set if the font is *not* fixed pitch.
234 this->setIsFixedPitch((textMetric.tmPitchAndFamily & TMPF_FIXED_PITCH) == 0);
bungeman6e45bda2016-07-25 15:11:49 -0700235 this->setFontStyle(SkFontStyle(textMetric.tmWeight, style.width(), style.slant()));
bungeman@google.comfe747652013-03-25 19:36:11 +0000236
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000237 // Used a logfont on a memory context, should never get a device font.
238 // Therefore all TMPF_DEVICE will be PostScript (cubic) fonts.
bungeman6e45bda2016-07-25 15:11:49 -0700239 // If the font has cubic outlines, it will not be rendered with ClearType.
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000240 fCanBeLCD = !((textMetric.tmPitchAndFamily & TMPF_VECTOR) &&
241 (textMetric.tmPitchAndFamily & TMPF_DEVICE));
242 }
reed@google.comac6b9792011-03-11 15:42:51 +0000243
reed@google.com59d2f632011-05-02 19:36:59 +0000244 LOGFONT fLogFont;
bungeman@google.come70f7982012-06-01 19:38:19 +0000245 bool fSerializeAsStream;
bungeman@google.comcb1bbb32012-10-12 18:48:35 +0000246 bool fCanBeLCD;
reed@google.comac6b9792011-03-11 15:42:51 +0000247
reed@google.com59d2f632011-05-02 19:36:59 +0000248 static LogFontTypeface* Create(const LOGFONT& lf) {
bungemana4c4a2d2014-10-20 13:33:19 -0700249 return new LogFontTypeface(get_style(lf), lf, false);
reed@google.comac6b9792011-03-11 15:42:51 +0000250 }
reed@google.com0da48612013-03-19 16:06:52 +0000251
reed@google.com055180c2013-03-21 18:46:35 +0000252 static void EnsureAccessible(const SkTypeface* face) {
253 call_ensure_accessible(static_cast<const LogFontTypeface*>(face)->fLogFont);
254 }
255
reed@google.com0da48612013-03-19 16:06:52 +0000256protected:
mtklein36352bf2015-03-25 18:17:31 -0700257 SkStreamAsset* onOpenStream(int* ttcIndex) const override;
reeda9322c22016-04-12 06:47:05 -0700258 SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
259 const SkDescriptor*) const override;
mtklein36352bf2015-03-25 18:17:31 -0700260 void onFilterRec(SkScalerContextRec*) const override;
reed39a9a502015-05-12 09:50:04 -0700261 SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
262 PerGlyphInfo, const uint32_t*, uint32_t) const override;
mtklein36352bf2015-03-25 18:17:31 -0700263 void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
bungeman@google.com3c996f82013-10-24 21:39:35 +0000264 virtual int onCharsToGlyphs(const void* chars, Encoding encoding,
mtklein36352bf2015-03-25 18:17:31 -0700265 uint16_t glyphs[], int glyphCount) const override;
266 int onCountGlyphs() const override;
267 int onGetUPEM() const override;
268 void onGetFamilyName(SkString* familyName) const override;
269 SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
270 int onGetTableTags(SkFontTableTag tags[]) const override;
bungeman@google.comb10b51f2013-08-01 20:18:41 +0000271 virtual size_t onGetTableData(SkFontTableTag, size_t offset,
mtklein36352bf2015-03-25 18:17:31 -0700272 size_t length, void* data) const override;
reed@google.comac6b9792011-03-11 15:42:51 +0000273};
274
bungeman@google.coma5501992012-05-18 19:06:41 +0000275class FontMemResourceTypeface : public LogFontTypeface {
276public:
277 /**
bungeman@google.coma5501992012-05-18 19:06:41 +0000278 * The created FontMemResourceTypeface takes ownership of fontMemResource.
279 */
280 static FontMemResourceTypeface* Create(const LOGFONT& lf, HANDLE fontMemResource) {
bungemana4c4a2d2014-10-20 13:33:19 -0700281 return new FontMemResourceTypeface(get_style(lf), lf, fontMemResource);
bungeman@google.coma5501992012-05-18 19:06:41 +0000282 }
283
284protected:
mtklein36352bf2015-03-25 18:17:31 -0700285 void weak_dispose() const override {
bungeman@google.coma5501992012-05-18 19:06:41 +0000286 RemoveFontMemResourceEx(fFontMemResource);
287 //SkTypefaceCache::Remove(this);
288 INHERITED::weak_dispose();
289 }
290
291private:
bungeman@google.com72cf4fc2014-03-21 22:48:32 +0000292 /**
293 * Takes ownership of fontMemResource.
294 */
bungemana4c4a2d2014-10-20 13:33:19 -0700295 FontMemResourceTypeface(const SkFontStyle& style, const LOGFONT& lf, HANDLE fontMemResource)
296 : LogFontTypeface(style, lf, true), fFontMemResource(fontMemResource)
297 { }
bungeman@google.com72cf4fc2014-03-21 22:48:32 +0000298
299 HANDLE fFontMemResource;
300
bungeman@google.coma5501992012-05-18 19:06:41 +0000301 typedef LogFontTypeface INHERITED;
302};
303
reed@google.comac6b9792011-03-11 15:42:51 +0000304static const LOGFONT& get_default_font() {
305 static LOGFONT gDefaultFont;
reed@google.comac6b9792011-03-11 15:42:51 +0000306 return gDefaultFont;
307}
308
bungeman82a455f2016-04-14 08:04:45 -0700309static bool FindByLogFont(SkTypeface* face, void* ctx) {
bungeman@google.coma5501992012-05-18 19:06:41 +0000310 LogFontTypeface* lface = static_cast<LogFontTypeface*>(face);
reed@google.com59d2f632011-05-02 19:36:59 +0000311 const LOGFONT* lf = reinterpret_cast<const LOGFONT*>(ctx);
reed@google.comac6b9792011-03-11 15:42:51 +0000312
bungeman82a455f2016-04-14 08:04:45 -0700313 return !memcmp(&lface->fLogFont, lf, sizeof(LOGFONT));
reed@google.com59d2f632011-05-02 19:36:59 +0000314}
315
316/**
317 * This guy is public. It first searches the cache, and if a match is not found,
318 * it creates a new face.
319 */
320SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& origLF) {
321 LOGFONT lf = origLF;
322 make_canonical(&lf);
bungeman@google.comee51d1a2012-02-16 12:40:48 +0000323 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByLogFont, &lf);
halcanary96fcdcc2015-08-27 07:41:13 -0700324 if (nullptr == face) {
reed@google.com59d2f632011-05-02 19:36:59 +0000325 face = LogFontTypeface::Create(lf);
bungeman82a455f2016-04-14 08:04:45 -0700326 SkTypefaceCache::Add(face);
reed@google.com59d2f632011-05-02 19:36:59 +0000327 }
328 return face;
reed@google.comac6b9792011-03-11 15:42:51 +0000329}
330
reed@google.comdb77a6a2011-07-19 19:08:33 +0000331/**
bungeman@google.coma5501992012-05-18 19:06:41 +0000332 * The created SkTypeface takes ownership of fontMemResource.
333 */
334SkTypeface* SkCreateFontMemResourceTypefaceFromLOGFONT(const LOGFONT& origLF, HANDLE fontMemResource) {
335 LOGFONT lf = origLF;
336 make_canonical(&lf);
mtklein60b6e9d2014-10-24 10:43:15 -0700337 // We'll never get a cache hit, so no point in putting this in SkTypefaceCache.
338 return FontMemResourceTypeface::Create(lf, fontMemResource);
bungeman@google.coma5501992012-05-18 19:06:41 +0000339}
340
341/**
reed@google.comdb77a6a2011-07-19 19:08:33 +0000342 * This guy is public
343 */
344void SkLOGFONTFromTypeface(const SkTypeface* face, LOGFONT* lf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700345 if (nullptr == face) {
reed@google.comdb77a6a2011-07-19 19:08:33 +0000346 *lf = get_default_font();
347 } else {
bungeman@google.coma5501992012-05-18 19:06:41 +0000348 *lf = static_cast<const LogFontTypeface*>(face)->fLogFont;
reed@google.comdb77a6a2011-07-19 19:08:33 +0000349 }
350}
351
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000352// Construct Glyph to Unicode table.
353// Unicode code points that require conjugate pairs in utf16 are not
354// supported.
355// TODO(arthurhsu): Add support for conjugate pairs. It looks like that may
356// require parsing the TTF cmap table (platform 4, encoding 12) directly instead
357// of calling GetFontUnicodeRange().
358static void populate_glyph_to_unicode(HDC fontHdc, const unsigned glyphCount,
359 SkTDArray<SkUnichar>* glyphToUnicode) {
halcanary96fcdcc2015-08-27 07:41:13 -0700360 DWORD glyphSetBufferSize = GetFontUnicodeRanges(fontHdc, nullptr);
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000361 if (!glyphSetBufferSize) {
362 return;
363 }
364
365 SkAutoTDeleteArray<BYTE> glyphSetBuffer(new BYTE[glyphSetBufferSize]);
366 GLYPHSET* glyphSet =
367 reinterpret_cast<LPGLYPHSET>(glyphSetBuffer.get());
368 if (GetFontUnicodeRanges(fontHdc, glyphSet) != glyphSetBufferSize) {
369 return;
370 }
371
372 glyphToUnicode->setCount(glyphCount);
373 memset(glyphToUnicode->begin(), 0, glyphCount * sizeof(SkUnichar));
374 for (DWORD i = 0; i < glyphSet->cRanges; ++i) {
375 // There is no guarantee that within a Unicode range, the corresponding
376 // glyph id in a font file are continuous. So, even if we have ranges,
377 // we can't just use the first and last entry of the range to compute
378 // result. We need to enumerate them one by one.
379 int count = glyphSet->ranges[i].cGlyphs;
380 SkAutoTArray<WCHAR> chars(count + 1);
381 chars[count] = 0; // termintate string
382 SkAutoTArray<WORD> glyph(count);
383 for (USHORT j = 0; j < count; ++j) {
384 chars[j] = glyphSet->ranges[i].wcLow + j;
385 }
386 GetGlyphIndicesW(fontHdc, chars.get(), count, glyph.get(),
387 GGI_MARK_NONEXISTING_GLYPHS);
388 // If the glyph ID is valid, and the glyph is not mapped, then we will
389 // fill in the char id into the vector. If the glyph is mapped already,
390 // skip it.
391 // TODO(arthurhsu): better improve this. e.g. Get all used char ids from
392 // font cache, then generate this mapping table from there. It's
393 // unlikely to have collisions since glyph reuse happens mostly for
394 // different Unicode pages.
395 for (USHORT j = 0; j < count; ++j) {
396 if (glyph[j] != 0xffff && glyph[j] < glyphCount &&
397 (*glyphToUnicode)[glyph[j]] == 0) {
398 (*glyphToUnicode)[glyph[j]] = chars[j];
399 }
400 }
401 }
402}
403
reed@google.com99edd432011-09-09 14:59:59 +0000404//////////////////////////////////////////////////////////////////////////////////////
405
406static int alignTo32(int n) {
407 return (n + 31) & ~31;
408}
409
410struct MyBitmapInfo : public BITMAPINFO {
411 RGBQUAD fMoreSpaceForColors[1];
412};
413
414class HDCOffscreen {
415public:
416 HDCOffscreen() {
417 fFont = 0;
418 fDC = 0;
419 fBM = 0;
halcanary96fcdcc2015-08-27 07:41:13 -0700420 fBits = nullptr;
reed@google.com99edd432011-09-09 14:59:59 +0000421 fWidth = fHeight = 0;
422 fIsBW = false;
423 }
424
425 ~HDCOffscreen() {
426 if (fDC) {
427 DeleteDC(fDC);
428 }
429 if (fBM) {
430 DeleteObject(fBM);
431 }
432 }
433
434 void init(HFONT font, const XFORM& xform) {
435 fFont = font;
436 fXform = xform;
437 }
438
bungeman@google.com97efada2012-07-30 20:40:50 +0000439 const void* draw(const SkGlyph&, bool isBW, size_t* srcRBPtr);
reed@google.com99edd432011-09-09 14:59:59 +0000440
441private:
442 HDC fDC;
443 HBITMAP fBM;
444 HFONT fFont;
445 XFORM fXform;
446 void* fBits; // points into fBM
447 int fWidth;
448 int fHeight;
449 bool fIsBW;
450};
451
reed@google.com754e4eb2011-09-26 13:21:39 +0000452const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW,
bungeman@google.com97efada2012-07-30 20:40:50 +0000453 size_t* srcRBPtr) {
reed@google.com84e22d82013-07-10 15:38:20 +0000454 // Can we share the scalercontext's fDDC, so we don't need to create
455 // a separate fDC here?
reed@google.com99edd432011-09-09 14:59:59 +0000456 if (0 == fDC) {
457 fDC = CreateCompatibleDC(0);
458 if (0 == fDC) {
halcanary96fcdcc2015-08-27 07:41:13 -0700459 return nullptr;
reed@google.com99edd432011-09-09 14:59:59 +0000460 }
461 SetGraphicsMode(fDC, GM_ADVANCED);
462 SetBkMode(fDC, TRANSPARENT);
463 SetTextAlign(fDC, TA_LEFT | TA_BASELINE);
464 SelectObject(fDC, fFont);
bungeman@google.com97efada2012-07-30 20:40:50 +0000465
466 COLORREF color = 0x00FFFFFF;
bsalomon@google.comb58a6392013-03-21 20:29:05 +0000467 SkDEBUGCODE(COLORREF prev =) SetTextColor(fDC, color);
bungeman@google.com97efada2012-07-30 20:40:50 +0000468 SkASSERT(prev != CLR_INVALID);
reed@google.com99edd432011-09-09 14:59:59 +0000469 }
470
471 if (fBM && (fIsBW != isBW || fWidth < glyph.fWidth || fHeight < glyph.fHeight)) {
472 DeleteObject(fBM);
473 fBM = 0;
474 }
reed@google.com754e4eb2011-09-26 13:21:39 +0000475 fIsBW = isBW;
reed@google.com99edd432011-09-09 14:59:59 +0000476
reed@google.com99edd432011-09-09 14:59:59 +0000477 fWidth = SkMax32(fWidth, glyph.fWidth);
478 fHeight = SkMax32(fHeight, glyph.fHeight);
479
480 int biWidth = isBW ? alignTo32(fWidth) : fWidth;
481
482 if (0 == fBM) {
483 MyBitmapInfo info;
484 sk_bzero(&info, sizeof(info));
485 if (isBW) {
486 RGBQUAD blackQuad = { 0, 0, 0, 0 };
487 RGBQUAD whiteQuad = { 0xFF, 0xFF, 0xFF, 0 };
488 info.bmiColors[0] = blackQuad;
489 info.bmiColors[1] = whiteQuad;
490 }
491 info.bmiHeader.biSize = sizeof(info.bmiHeader);
492 info.bmiHeader.biWidth = biWidth;
493 info.bmiHeader.biHeight = fHeight;
494 info.bmiHeader.biPlanes = 1;
495 info.bmiHeader.biBitCount = isBW ? 1 : 32;
496 info.bmiHeader.biCompression = BI_RGB;
497 if (isBW) {
498 info.bmiHeader.biClrUsed = 2;
499 }
500 fBM = CreateDIBSection(fDC, &info, DIB_RGB_COLORS, &fBits, 0, 0);
501 if (0 == fBM) {
halcanary96fcdcc2015-08-27 07:41:13 -0700502 return nullptr;
reed@google.com99edd432011-09-09 14:59:59 +0000503 }
504 SelectObject(fDC, fBM);
505 }
506
507 // erase
508 size_t srcRB = isBW ? (biWidth >> 3) : (fWidth << 2);
509 size_t size = fHeight * srcRB;
bungeman@google.com97efada2012-07-30 20:40:50 +0000510 memset(fBits, 0, size);
reed@google.com99edd432011-09-09 14:59:59 +0000511
512 XFORM xform = fXform;
513 xform.eDx = (float)-glyph.fLeft;
514 xform.eDy = (float)-glyph.fTop;
515 SetWorldTransform(fDC, &xform);
516
517 uint16_t glyphID = glyph.getGlyphID();
halcanary96fcdcc2015-08-27 07:41:13 -0700518 BOOL ret = ExtTextOutW(fDC, 0, 0, ETO_GLYPH_INDEX, nullptr, reinterpret_cast<LPCWSTR>(&glyphID), 1, nullptr);
reed@google.com99edd432011-09-09 14:59:59 +0000519 GdiFlush();
bungeman@google.com39698b12011-11-15 22:26:41 +0000520 if (0 == ret) {
halcanary96fcdcc2015-08-27 07:41:13 -0700521 return nullptr;
bungeman@google.com39698b12011-11-15 22:26:41 +0000522 }
reed@google.com99edd432011-09-09 14:59:59 +0000523 *srcRBPtr = srcRB;
524 // offset to the start of the image
525 return (const char*)fBits + (fHeight - glyph.fHeight) * srcRB;
526}
527
reed@google.comb8a5c612012-06-13 20:01:44 +0000528//////////////////////////////////////////////////////////////////////////////
bungeman@google.com0abbff92013-07-27 20:37:56 +0000529#define BUFFERSIZE (1 << 13)
reed@google.com59d2f632011-05-02 19:36:59 +0000530
reed@google.com30ddd612013-07-30 17:47:39 +0000531class SkScalerContext_GDI : public SkScalerContext {
reed@google.comac6b9792011-03-11 15:42:51 +0000532public:
reeda9322c22016-04-12 06:47:05 -0700533 SkScalerContext_GDI(SkTypeface*, const SkScalerContextEffects&, const SkDescriptor* desc);
reed@google.com30ddd612013-07-30 17:47:39 +0000534 virtual ~SkScalerContext_GDI();
reed@google.comac6b9792011-03-11 15:42:51 +0000535
reed@google.com84e22d82013-07-10 15:38:20 +0000536 // Returns true if the constructor was able to complete all of its
537 // initializations (which may include calling GDI).
538 bool isValid() const;
539
reed@google.comac6b9792011-03-11 15:42:51 +0000540protected:
mtklein36352bf2015-03-25 18:17:31 -0700541 unsigned generateGlyphCount() override;
542 uint16_t generateCharToGlyph(SkUnichar uni) override;
543 void generateAdvance(SkGlyph* glyph) override;
544 void generateMetrics(SkGlyph* glyph) override;
545 void generateImage(const SkGlyph& glyph) override;
546 void generatePath(const SkGlyph& glyph, SkPath* path) override;
547 void generateFontMetrics(SkPaint::FontMetrics*) override;
reed@google.com99edd432011-09-09 14:59:59 +0000548
reed@google.comac6b9792011-03-11 15:42:51 +0000549private:
bungeman@google.com0abbff92013-07-27 20:37:56 +0000550 DWORD getGDIGlyphPath(const SkGlyph& glyph, UINT flags,
551 SkAutoSTMalloc<BUFFERSIZE, uint8_t>* glyphbuf);
552
reed@google.com99edd432011-09-09 14:59:59 +0000553 HDCOffscreen fOffscreen;
bungeman@google.com0abbff92013-07-27 20:37:56 +0000554 /** fGsA is the non-rotational part of total matrix without the text height scale.
555 * Used to find the magnitude of advances.
556 */
557 MAT2 fGsA;
bungeman@google.com6a774a12013-07-30 01:07:48 +0000558 /** The total matrix without the textSize. */
reed@google.comac6b9792011-03-11 15:42:51 +0000559 MAT2 fMat22;
bungeman@google.com6a774a12013-07-30 01:07:48 +0000560 /** Scales font to EM size. */
561 MAT2 fHighResMat22;
reed@google.comac6b9792011-03-11 15:42:51 +0000562 HDC fDDC;
563 HFONT fSavefont;
564 HFONT fFont;
565 SCRIPT_CACHE fSC;
566 int fGlyphCount;
reed@google.com1dd17a12011-05-17 14:04:41 +0000567
bungeman@google.com6a774a12013-07-30 01:07:48 +0000568 /** The total matrix which also removes EM scale. */
reed@google.com1dd17a12011-05-17 14:04:41 +0000569 SkMatrix fHiResMatrix;
bungeman@google.com0abbff92013-07-27 20:37:56 +0000570 /** fG_inv is the inverse of the rotational part of the total matrix.
571 * Used to set the direction of advances.
572 */
573 SkMatrix fG_inv;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000574 enum Type {
bungeman@google.com4732df62014-01-23 15:22:42 +0000575 kTrueType_Type, kBitmap_Type, kLine_Type
bungeman@google.coma0319f62012-04-18 15:40:50 +0000576 } fType;
577 TEXTMETRIC fTM;
reed@google.comac6b9792011-03-11 15:42:51 +0000578};
579
reed@google.comac6b9792011-03-11 15:42:51 +0000580static FIXED float2FIXED(float x) {
581 return SkFixedToFIXED(SkFloatToFixed(x));
582}
583
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700584static inline float FIXED2float(FIXED x) {
585 return SkFixedToFloat(SkFIXEDToFixed(x));
586}
587
reed@google.com82a34d82011-07-26 19:33:08 +0000588static BYTE compute_quality(const SkScalerContext::Rec& rec) {
589 switch (rec.fMaskFormat) {
590 case SkMask::kBW_Format:
591 return NONANTIALIASED_QUALITY;
592 case SkMask::kLCD16_Format:
reed@google.com82a34d82011-07-26 19:33:08 +0000593 return CLEARTYPE_QUALITY;
594 default:
reed@google.com8351aab2012-01-18 17:06:35 +0000595 if (rec.fFlags & SkScalerContext::kGenA8FromLCD_Flag) {
596 return CLEARTYPE_QUALITY;
597 } else {
598 return ANTIALIASED_QUALITY;
599 }
reed@google.com82a34d82011-07-26 19:33:08 +0000600 }
601}
602
reed@google.com30ddd612013-07-30 17:47:39 +0000603SkScalerContext_GDI::SkScalerContext_GDI(SkTypeface* rawTypeface,
reeda9322c22016-04-12 06:47:05 -0700604 const SkScalerContextEffects& effects,
605 const SkDescriptor* desc)
606 : SkScalerContext(rawTypeface, effects, desc)
reed@google.com055180c2013-03-21 18:46:35 +0000607 , fDDC(0)
reed@google.com055180c2013-03-21 18:46:35 +0000608 , fSavefont(0)
reed@google.com84e22d82013-07-10 15:38:20 +0000609 , fFont(0)
reed@google.com055180c2013-03-21 18:46:35 +0000610 , fSC(0)
bungeman@google.com05a729f2013-06-20 15:29:16 +0000611 , fGlyphCount(-1)
612{
reed@google.com055180c2013-03-21 18:46:35 +0000613 LogFontTypeface* typeface = reinterpret_cast<LogFontTypeface*>(rawTypeface);
614
halcanary96fcdcc2015-08-27 07:41:13 -0700615 fDDC = ::CreateCompatibleDC(nullptr);
reed@google.com84e22d82013-07-10 15:38:20 +0000616 if (!fDDC) {
617 return;
618 }
reed@google.com1dd17a12011-05-17 14:04:41 +0000619 SetGraphicsMode(fDDC, GM_ADVANCED);
reed@google.comac6b9792011-03-11 15:42:51 +0000620 SetBkMode(fDDC, TRANSPARENT);
skia.committer@gmail.com27e21fe2013-07-28 07:01:03 +0000621
bungeman6f940762015-03-18 08:25:43 -0700622 // When GDI hinting, remove the entire Y scale from sA and GsA. (Prevents 'linear' metrics.)
623 // When not hinting, remove only the integer Y scale from sA and GsA. (Applied by GDI.)
bungeman5f14c5e2014-12-05 12:26:44 -0800624 SkScalerContextRec::PreMatrixScale scaleConstraints =
625 (fRec.getHinting() == SkPaint::kNo_Hinting || fRec.getHinting() == SkPaint::kSlight_Hinting)
626 ? SkScalerContextRec::kVerticalInteger_PreMatrixScale
627 : SkScalerContextRec::kVertical_PreMatrixScale;
628 SkVector scale;
629 SkMatrix sA;
630 SkMatrix GsA;
631 SkMatrix A;
632 fRec.computeMatrices(scaleConstraints, &scale, &sA, &GsA, &fG_inv, &A);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000633
634 fGsA.eM11 = SkScalarToFIXED(GsA.get(SkMatrix::kMScaleX));
635 fGsA.eM12 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewY)); // This should be ~0.
636 fGsA.eM21 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewX));
637 fGsA.eM22 = SkScalarToFIXED(GsA.get(SkMatrix::kMScaleY));
638
bungeman6f940762015-03-18 08:25:43 -0700639 // When not hinting, scale was computed with kVerticalInteger, so is already an integer.
640 // The sA and GsA transforms will be used to create 'linear' metrics.
641
642 // When hinting, scale was computed with kVertical, stating that our port can handle
643 // non-integer scales. This is done so that sA and GsA are computed without any 'residual'
644 // scale in them, preventing 'linear' metrics. However, GDI cannot actually handle non-integer
645 // scales so we need to round in this case. This is fine, since all of the scale has been
646 // removed from sA and GsA, so GDI will be handling the scale completely.
647 SkScalar gdiTextSize = SkScalarRoundToScalar(scale.fY);
648
649 // GDI will not accept a size of zero, so round the range [0, 1] to 1.
650 // If the size was non-zero, the scale factors will also be non-zero and 1px tall text is drawn.
651 // If the size actually was zero, the scale factors will also be zero, so GDI will draw nothing.
bungeman5f14c5e2014-12-05 12:26:44 -0800652 if (gdiTextSize == 0) {
653 gdiTextSize = SK_Scalar1;
654 }
bungeman@google.com0abbff92013-07-27 20:37:56 +0000655
reed@google.com055180c2013-03-21 18:46:35 +0000656 LOGFONT lf = typeface->fLogFont;
bungeman@google.com11ba3192013-10-03 20:17:51 +0000657 lf.lfHeight = -SkScalarTruncToInt(gdiTextSize);
reed@google.com82a34d82011-07-26 19:33:08 +0000658 lf.lfQuality = compute_quality(fRec);
reed@google.comac6b9792011-03-11 15:42:51 +0000659 fFont = CreateFontIndirect(&lf);
reed@google.com84e22d82013-07-10 15:38:20 +0000660 if (!fFont) {
661 return;
662 }
reed@google.com1dd17a12011-05-17 14:04:41 +0000663
reed@google.comac6b9792011-03-11 15:42:51 +0000664 fSavefont = (HFONT)SelectObject(fDDC, fFont);
reed@google.coma767fa02011-08-05 21:40:26 +0000665
bungeman@google.coma0319f62012-04-18 15:40:50 +0000666 if (0 == GetTextMetrics(fDDC, &fTM)) {
reed@google.com055180c2013-03-21 18:46:35 +0000667 call_ensure_accessible(lf);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000668 if (0 == GetTextMetrics(fDDC, &fTM)) {
669 fTM.tmPitchAndFamily = TMPF_TRUETYPE;
670 }
671 }
bungeman@google.com90b7e382012-04-20 15:26:28 +0000672
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000673 XFORM xform;
bungeman@google.com90b7e382012-04-20 15:26:28 +0000674 if (fTM.tmPitchAndFamily & TMPF_VECTOR) {
bungeman@google.com4732df62014-01-23 15:22:42 +0000675 // Used a logfont on a memory context, should never get a device font.
676 // Therefore all TMPF_DEVICE will be PostScript fonts.
677
678 // If TMPF_VECTOR is set, one of TMPF_TRUETYPE or TMPF_DEVICE means that
679 // we have an outline font. Otherwise we have a vector FON, which is
680 // scalable, but not an outline font.
681 // This was determined by testing with Type1 PFM/PFB and
682 // OpenTypeCFF OTF, as well as looking at Wine bugs and sources.
683 if (fTM.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_DEVICE)) {
684 // Truetype or PostScript.
685 fType = SkScalerContext_GDI::kTrueType_Type;
686 } else {
687 // Stroked FON.
688 fType = SkScalerContext_GDI::kLine_Type;
689 }
bungeman@google.coma0319f62012-04-18 15:40:50 +0000690
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000691 // fPost2x2 is column-major, left handed (y down).
692 // XFORM 2x2 is row-major, left handed (y down).
bungeman@google.com0abbff92013-07-27 20:37:56 +0000693 xform.eM11 = SkScalarToFloat(sA.get(SkMatrix::kMScaleX));
694 xform.eM12 = SkScalarToFloat(sA.get(SkMatrix::kMSkewY));
695 xform.eM21 = SkScalarToFloat(sA.get(SkMatrix::kMSkewX));
696 xform.eM22 = SkScalarToFloat(sA.get(SkMatrix::kMScaleY));
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000697 xform.eDx = 0;
698 xform.eDy = 0;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000699
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000700 // MAT2 is row major, right handed (y up).
701 fMat22.eM11 = float2FIXED(xform.eM11);
702 fMat22.eM12 = float2FIXED(-xform.eM12);
703 fMat22.eM21 = float2FIXED(-xform.eM21);
704 fMat22.eM22 = float2FIXED(xform.eM22);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000705
706 if (needToRenderWithSkia(fRec)) {
707 this->forceGenerateImageFromPath();
708 }
709
bungeman@google.com11ba3192013-10-03 20:17:51 +0000710 // Create a hires matrix if we need linear metrics.
bungeman@google.com0abbff92013-07-27 20:37:56 +0000711 if (this->isSubpixel()) {
712 OUTLINETEXTMETRIC otm;
713 UINT success = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
714 if (0 == success) {
715 call_ensure_accessible(lf);
716 success = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
717 }
718 if (0 != success) {
bungeman@google.com11ba3192013-10-03 20:17:51 +0000719 SkScalar upem = SkIntToScalar(otm.otmEMSquare);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000720
bungeman@google.com11ba3192013-10-03 20:17:51 +0000721 SkScalar gdiTextSizeToEMScale = upem / gdiTextSize;
722 fHighResMat22.eM11 = float2FIXED(gdiTextSizeToEMScale);
bungeman@google.com6a774a12013-07-30 01:07:48 +0000723 fHighResMat22.eM12 = float2FIXED(0);
724 fHighResMat22.eM21 = float2FIXED(0);
bungeman@google.com11ba3192013-10-03 20:17:51 +0000725 fHighResMat22.eM22 = float2FIXED(gdiTextSizeToEMScale);
bungeman@google.com6a774a12013-07-30 01:07:48 +0000726
bungeman@google.com11ba3192013-10-03 20:17:51 +0000727 SkScalar removeEMScale = SkScalarInvert(upem);
bungeman@google.com6a774a12013-07-30 01:07:48 +0000728 fHiResMatrix = A;
bungeman@google.com11ba3192013-10-03 20:17:51 +0000729 fHiResMatrix.preScale(removeEMScale, removeEMScale);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000730 }
731 }
732
bungeman@google.coma0319f62012-04-18 15:40:50 +0000733 } else {
734 // Assume bitmap
reed@google.com30ddd612013-07-30 17:47:39 +0000735 fType = SkScalerContext_GDI::kBitmap_Type;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000736
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000737 xform.eM11 = 1.0f;
738 xform.eM12 = 0.0f;
739 xform.eM21 = 0.0f;
740 xform.eM22 = 1.0f;
741 xform.eDx = 0.0f;
742 xform.eDy = 0.0f;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000743
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000744 // fPost2x2 is column-major, left handed (y down).
745 // MAT2 is row major, right handed (y up).
bungeman@google.coma0319f62012-04-18 15:40:50 +0000746 fMat22.eM11 = SkScalarToFIXED(fRec.fPost2x2[0][0]);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000747 fMat22.eM12 = SkScalarToFIXED(-fRec.fPost2x2[1][0]);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000748 fMat22.eM21 = SkScalarToFIXED(-fRec.fPost2x2[0][1]);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000749 fMat22.eM22 = SkScalarToFIXED(fRec.fPost2x2[1][1]);
reed@google.coma767fa02011-08-05 21:40:26 +0000750 }
reed@google.com99edd432011-09-09 14:59:59 +0000751
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000752 fOffscreen.init(fFont, xform);
reed@google.comac6b9792011-03-11 15:42:51 +0000753}
754
reed@google.com30ddd612013-07-30 17:47:39 +0000755SkScalerContext_GDI::~SkScalerContext_GDI() {
reed@google.comac6b9792011-03-11 15:42:51 +0000756 if (fDDC) {
757 ::SelectObject(fDDC, fSavefont);
758 ::DeleteDC(fDDC);
759 }
760 if (fFont) {
761 ::DeleteObject(fFont);
762 }
763 if (fSC) {
764 ::ScriptFreeCache(&fSC);
765 }
766}
767
reed@google.com30ddd612013-07-30 17:47:39 +0000768bool SkScalerContext_GDI::isValid() const {
reed@google.com84e22d82013-07-10 15:38:20 +0000769 return fDDC && fFont;
770}
771
reed@google.com30ddd612013-07-30 17:47:39 +0000772unsigned SkScalerContext_GDI::generateGlyphCount() {
reed@google.comac6b9792011-03-11 15:42:51 +0000773 if (fGlyphCount < 0) {
bungeman@google.com7bdd6142013-07-15 19:42:57 +0000774 fGlyphCount = calculateGlyphCount(
775 fDDC, static_cast<const LogFontTypeface*>(this->getTypeface())->fLogFont);
reed@google.comac6b9792011-03-11 15:42:51 +0000776 }
777 return fGlyphCount;
778}
779
bungeman@google.com33346482013-08-27 19:05:32 +0000780uint16_t SkScalerContext_GDI::generateCharToGlyph(SkUnichar utf32) {
reed@google.comac6b9792011-03-11 15:42:51 +0000781 uint16_t index = 0;
bungeman@google.com33346482013-08-27 19:05:32 +0000782 WCHAR utf16[2];
reed@google.comac6b9792011-03-11 15:42:51 +0000783 // TODO(ctguil): Support characters that generate more than one glyph.
bungeman@google.com33346482013-08-27 19:05:32 +0000784 if (SkUTF16_FromUnichar(utf32, (uint16_t*)utf16) == 1) {
reed@google.comac6b9792011-03-11 15:42:51 +0000785 // Type1 fonts fail with uniscribe API. Use GetGlyphIndices for plane 0.
skia.committer@gmail.com7bd141d2013-08-28 07:01:18 +0000786
bungeman@google.com33346482013-08-27 19:05:32 +0000787 /** Real documentation for GetGlyphIndiciesW:
788 *
789 * When GGI_MARK_NONEXISTING_GLYPHS is not specified and a character does not map to a
790 * glyph, then the 'default character's glyph is returned instead. The 'default character'
bungeman@google.com4732df62014-01-23 15:22:42 +0000791 * is available in fTM.tmDefaultChar. FON fonts have a default character, and there exists
792 * a usDefaultChar in the 'OS/2' table, version 2 and later. If there is no
bungeman@google.com33346482013-08-27 19:05:32 +0000793 * 'default character' specified by the font, then often the first character found is used.
794 *
795 * When GGI_MARK_NONEXISTING_GLYPHS is specified and a character does not map to a glyph,
796 * then the glyph 0xFFFF is used. In Windows XP and earlier, Bitmap/Vector FON usually use
797 * glyph 0x1F instead ('Terminal' appears to be special, returning 0xFFFF).
798 * Type1 PFM/PFB, TT, OT TT, OT CFF all appear to use 0xFFFF, even on XP.
799 */
800 DWORD result = GetGlyphIndicesW(fDDC, utf16, 1, &index, GGI_MARK_NONEXISTING_GLYPHS);
801 if (result == GDI_ERROR
802 || 0xFFFF == index
bungeman@google.com4732df62014-01-23 15:22:42 +0000803 || (0x1F == index &&
804 (fType == SkScalerContext_GDI::kBitmap_Type ||
805 fType == SkScalerContext_GDI::kLine_Type)
806 /*&& winVer < Vista */)
807 )
bungeman@google.com33346482013-08-27 19:05:32 +0000808 {
809 index = 0;
810 }
reed@google.comac6b9792011-03-11 15:42:51 +0000811 } else {
812 // Use uniscribe to detemine glyph index for non-BMP characters.
bungeman@google.com27f74aa2013-10-08 21:32:15 +0000813 static const int numWCHAR = 2;
814 static const int maxItems = 2;
halcanary96fcdcc2015-08-27 07:41:13 -0700815 // MSDN states that this can be nullptr, but some things don't work then.
bungeman@google.com27f74aa2013-10-08 21:32:15 +0000816 SCRIPT_CONTROL sc = { 0 };
817 // Add extra item to SCRIPT_ITEM to work around a bug (now documented).
818 // https://bugzilla.mozilla.org/show_bug.cgi?id=366643
819 SCRIPT_ITEM si[maxItems + 1];
820 int numItems;
halcanary96fcdcc2015-08-27 07:41:13 -0700821 HRZM(ScriptItemize(utf16, numWCHAR, maxItems, &sc, nullptr, si, &numItems),
bungeman@google.com27f74aa2013-10-08 21:32:15 +0000822 "Could not itemize character.");
reed@google.comac6b9792011-03-11 15:42:51 +0000823
bungeman@google.com27f74aa2013-10-08 21:32:15 +0000824 // Sometimes ScriptShape cannot find a glyph for a non-BMP and returns 2 space glyphs.
825 static const int maxGlyphs = 2;
826 SCRIPT_VISATTR vsa[maxGlyphs];
827 WORD outGlyphs[maxGlyphs];
828 WORD logClust[numWCHAR];
829 int numGlyphs;
830 HRZM(ScriptShape(fDDC, &fSC, utf16, numWCHAR, maxGlyphs, &si[0].a,
831 outGlyphs, logClust, vsa, &numGlyphs),
832 "Could not shape character.");
833 if (1 == numGlyphs) {
834 index = outGlyphs[0];
835 }
reed@google.comac6b9792011-03-11 15:42:51 +0000836 }
837 return index;
838}
839
reed@google.com30ddd612013-07-30 17:47:39 +0000840void SkScalerContext_GDI::generateAdvance(SkGlyph* glyph) {
reed@google.comac6b9792011-03-11 15:42:51 +0000841 this->generateMetrics(glyph);
842}
843
reed@google.com30ddd612013-07-30 17:47:39 +0000844void SkScalerContext_GDI::generateMetrics(SkGlyph* glyph) {
reed@google.comac6b9792011-03-11 15:42:51 +0000845 SkASSERT(fDDC);
846
bungeman@google.com4732df62014-01-23 15:22:42 +0000847 if (fType == SkScalerContext_GDI::kBitmap_Type || fType == SkScalerContext_GDI::kLine_Type) {
bungeman@google.coma0319f62012-04-18 15:40:50 +0000848 SIZE size;
djsollen1b277042014-08-06 06:58:06 -0700849 WORD glyphs = glyph->getGlyphID();
bungeman@google.coma0319f62012-04-18 15:40:50 +0000850 if (0 == GetTextExtentPointI(fDDC, &glyphs, 1, &size)) {
851 glyph->fWidth = SkToS16(fTM.tmMaxCharWidth);
852 } else {
853 glyph->fWidth = SkToS16(size.cx);
854 }
855 glyph->fHeight = SkToS16(size.cy);
856
857 glyph->fTop = SkToS16(-fTM.tmAscent);
bungeman@google.com4732df62014-01-23 15:22:42 +0000858 // Bitmap FON cannot underhang, but vector FON may.
859 // There appears no means of determining underhang of vector FON.
bungeman@google.coma0319f62012-04-18 15:40:50 +0000860 glyph->fLeft = SkToS16(0);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700861 glyph->fAdvanceX = glyph->fWidth;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000862 glyph->fAdvanceY = 0;
863
bungeman@google.com4732df62014-01-23 15:22:42 +0000864 // Vector FON will transform nicely, but bitmap FON do not.
865 if (fType == SkScalerContext_GDI::kLine_Type) {
866 SkRect bounds = SkRect::MakeXYWH(glyph->fLeft, glyph->fTop,
867 glyph->fWidth, glyph->fHeight);
868 SkMatrix m;
869 m.setAll(SkFIXEDToScalar(fMat22.eM11), -SkFIXEDToScalar(fMat22.eM21), 0,
870 -SkFIXEDToScalar(fMat22.eM12), SkFIXEDToScalar(fMat22.eM22), 0,
reed3f43f8a2015-01-20 19:58:36 -0800871 0, 0, 1);
bungeman@google.com4732df62014-01-23 15:22:42 +0000872 m.mapRect(&bounds);
reedd02cf262014-11-18 18:06:45 -0800873 bounds.roundOut(&bounds);
bungeman@google.com4732df62014-01-23 15:22:42 +0000874 glyph->fLeft = SkScalarTruncToInt(bounds.fLeft);
875 glyph->fTop = SkScalarTruncToInt(bounds.fTop);
876 glyph->fWidth = SkScalarTruncToInt(bounds.width());
877 glyph->fHeight = SkScalarTruncToInt(bounds.height());
878 }
879
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000880 // Apply matrix to advance.
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700881 glyph->fAdvanceY = -FIXED2float(fMat22.eM12) * glyph->fAdvanceX;
882 glyph->fAdvanceX *= FIXED2float(fMat22.eM11);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000883
884 return;
885 }
886
djsollen1b277042014-08-06 06:58:06 -0700887 UINT glyphId = glyph->getGlyphID();
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000888
reed@google.comac6b9792011-03-11 15:42:51 +0000889 GLYPHMETRICS gm;
reed@google.com1dd17a12011-05-17 14:04:41 +0000890 sk_bzero(&gm, sizeof(gm));
reed@google.comac6b9792011-03-11 15:42:51 +0000891
halcanary96fcdcc2015-08-27 07:41:13 -0700892 DWORD status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fMat22);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000893 if (GDI_ERROR == status) {
reed@google.com055180c2013-03-21 18:46:35 +0000894 LogFontTypeface::EnsureAccessible(this->getTypeface());
halcanary96fcdcc2015-08-27 07:41:13 -0700895 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fMat22);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000896 if (GDI_ERROR == status) {
897 glyph->zeroMetrics();
898 return;
899 }
900 }
901
902 bool empty = false;
903 // The black box is either the embedded bitmap size or the outline extent.
904 // It is 1x1 if nothing is to be drawn, but will also be 1x1 if something very small
905 // is to be drawn, like a '.'. We need to outset '.' but do not wish to outset ' '.
906 if (1 == gm.gmBlackBoxX && 1 == gm.gmBlackBoxY) {
907 // If GetGlyphOutline with GGO_NATIVE returns 0, we know there was no outline.
halcanary96fcdcc2015-08-27 07:41:13 -0700908 DWORD bufferSize = GetGlyphOutlineW(fDDC, glyphId, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fMat22);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000909 empty = (0 == bufferSize);
910 }
911
912 glyph->fTop = SkToS16(-gm.gmptGlyphOrigin.y);
913 glyph->fLeft = SkToS16(gm.gmptGlyphOrigin.x);
914 if (empty) {
915 glyph->fWidth = 0;
916 glyph->fHeight = 0;
917 } else {
918 // Outset, since the image may bleed out of the black box.
919 // For embedded bitmaps the black box should be exact.
920 // For outlines we need to outset by 1 in all directions for bleed.
921 // For ClearType we need to outset by 2 for bleed.
922 glyph->fWidth = gm.gmBlackBoxX + 4;
923 glyph->fHeight = gm.gmBlackBoxY + 4;
924 glyph->fTop -= 2;
925 glyph->fLeft -= 2;
926 }
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700927 // TODO(benjaminwagner): What is the type of gm.gmCellInc[XY]?
928 glyph->fAdvanceX = (float)((int)gm.gmCellIncX);
929 glyph->fAdvanceY = (float)((int)gm.gmCellIncY);
reed@google.comac6b9792011-03-11 15:42:51 +0000930 glyph->fRsbDelta = 0;
931 glyph->fLsbDelta = 0;
932
bungeman@google.com6a774a12013-07-30 01:07:48 +0000933 if (this->isSubpixel()) {
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000934 sk_bzero(&gm, sizeof(gm));
halcanary96fcdcc2015-08-27 07:41:13 -0700935 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fHighResMat22);
bungeman@google.comd1c7f712013-03-11 18:51:19 +0000936 if (GDI_ERROR != status) {
937 SkPoint advance;
938 fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700939 glyph->fAdvanceX = SkScalarToFloat(advance.fX);
940 glyph->fAdvanceY = SkScalarToFloat(advance.fY);
reed@google.comac6b9792011-03-11 15:42:51 +0000941 }
bungeman@google.com0abbff92013-07-27 20:37:56 +0000942 } else if (!isAxisAligned(this->fRec)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700943 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fGsA);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000944 if (GDI_ERROR != status) {
945 SkPoint advance;
946 fG_inv.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700947 glyph->fAdvanceX = SkScalarToFloat(advance.fX);
948 glyph->fAdvanceY = SkScalarToFloat(advance.fY);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000949 }
reed@google.comac6b9792011-03-11 15:42:51 +0000950 }
951}
952
bungeman@google.com6a774a12013-07-30 01:07:48 +0000953static const MAT2 gMat2Identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
bungeman41078062014-07-07 08:16:37 -0700954void SkScalerContext_GDI::generateFontMetrics(SkPaint::FontMetrics* metrics) {
halcanary96fcdcc2015-08-27 07:41:13 -0700955 if (nullptr == metrics) {
bungeman41078062014-07-07 08:16:37 -0700956 return;
reed@google.com60af92c2013-05-08 14:11:28 +0000957 }
bungeman41078062014-07-07 08:16:37 -0700958 sk_bzero(metrics, sizeof(*metrics));
reed@google.comac6b9792011-03-11 15:42:51 +0000959
960 SkASSERT(fDDC);
961
bungeman@google.come9d83192013-06-21 05:31:38 +0000962#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
bungeman@google.com4732df62014-01-23 15:22:42 +0000963 if (fType == SkScalerContext_GDI::kBitmap_Type || fType == SkScalerContext_GDI::kLine_Type) {
bungeman@google.come9d83192013-06-21 05:31:38 +0000964#endif
bungeman41078062014-07-07 08:16:37 -0700965 metrics->fTop = SkIntToScalar(-fTM.tmAscent);
966 metrics->fAscent = SkIntToScalar(-fTM.tmAscent);
967 metrics->fDescent = SkIntToScalar(fTM.tmDescent);
968 metrics->fBottom = SkIntToScalar(fTM.tmDescent);
969 metrics->fLeading = SkIntToScalar(fTM.tmExternalLeading);
970 metrics->fAvgCharWidth = SkIntToScalar(fTM.tmAveCharWidth);
971 metrics->fMaxCharWidth = SkIntToScalar(fTM.tmMaxCharWidth);
972 metrics->fXMin = 0;
973 metrics->fXMax = metrics->fMaxCharWidth;
974 //metrics->fXHeight = 0;
bungeman@google.come9d83192013-06-21 05:31:38 +0000975#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
bungeman@google.coma0319f62012-04-18 15:40:50 +0000976 return;
977 }
bungeman@google.come9d83192013-06-21 05:31:38 +0000978#endif
bungeman@google.coma0319f62012-04-18 15:40:50 +0000979
reed@google.comac6b9792011-03-11 15:42:51 +0000980 OUTLINETEXTMETRIC otm;
981
982 uint32_t ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000983 if (0 == ret) {
reed@google.com055180c2013-03-21 18:46:35 +0000984 LogFontTypeface::EnsureAccessible(this->getTypeface());
bungeman@google.com39698b12011-11-15 22:26:41 +0000985 ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
986 }
bungeman@google.com0abbff92013-07-27 20:37:56 +0000987 if (0 == ret) {
bungeman@google.coma0319f62012-04-18 15:40:50 +0000988 return;
reed@google.comac6b9792011-03-11 15:42:51 +0000989 }
990
bungeman@google.come9d83192013-06-21 05:31:38 +0000991#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
bungeman41078062014-07-07 08:16:37 -0700992 metrics->fTop = SkIntToScalar(-otm.otmrcFontBox.top);
993 metrics->fAscent = SkIntToScalar(-otm.otmAscent);
994 metrics->fDescent = SkIntToScalar(-otm.otmDescent);
995 metrics->fBottom = SkIntToScalar(-otm.otmrcFontBox.bottom);
996 metrics->fLeading = SkIntToScalar(otm.otmLineGap);
997 metrics->fAvgCharWidth = SkIntToScalar(otm.otmTextMetrics.tmAveCharWidth);
998 metrics->fMaxCharWidth = SkIntToScalar(otm.otmTextMetrics.tmMaxCharWidth);
999 metrics->fXMin = SkIntToScalar(otm.otmrcFontBox.left);
1000 metrics->fXMax = SkIntToScalar(otm.otmrcFontBox.right);
commit-bot@chromium.orgd3031aa2014-05-14 14:54:51 +00001001#endif
bungeman41078062014-07-07 08:16:37 -07001002 metrics->fUnderlineThickness = SkIntToScalar(otm.otmsUnderscoreSize);
1003 metrics->fUnderlinePosition = -SkIntToScalar(otm.otmsUnderscorePosition);
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001004
bungeman41078062014-07-07 08:16:37 -07001005 metrics->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag;
1006 metrics->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
bungeman@google.com4a867a62014-05-22 17:59:21 +00001007
bungeman41078062014-07-07 08:16:37 -07001008 metrics->fXHeight = SkIntToScalar(otm.otmsXHeight);
1009 GLYPHMETRICS gm;
1010 sk_bzero(&gm, sizeof(gm));
1011 DWORD len = GetGlyphOutlineW(fDDC, 'x', GGO_METRICS, &gm, 0, 0, &gMat2Identity);
1012 if (len != GDI_ERROR && gm.gmBlackBoxY > 0) {
1013 metrics->fXHeight = SkIntToScalar(gm.gmBlackBoxY);
reed@google.comac6b9792011-03-11 15:42:51 +00001014 }
1015}
1016
reed@google.com7430a332011-10-03 14:37:38 +00001017////////////////////////////////////////////////////////////////////////////////////////
1018
bungeman@google.com0abbff92013-07-27 20:37:56 +00001019#define SK_SHOW_TEXT_BLIT_COVERAGE 0
1020
reed@google.com7430a332011-10-03 14:37:38 +00001021static void build_power_table(uint8_t table[], float ee) {
1022 for (int i = 0; i < 256; i++) {
1023 float x = i / 255.f;
bungeman@google.com97efada2012-07-30 20:40:50 +00001024 x = sk_float_pow(x, ee);
reed@google.come1ca7052013-12-17 19:22:07 +00001025 int xx = SkScalarRoundToInt(x * 255);
reed@google.com7430a332011-10-03 14:37:38 +00001026 table[i] = SkToU8(xx);
1027 }
1028}
1029
bungeman@google.com97efada2012-07-30 20:40:50 +00001030/**
1031 * This will invert the gamma applied by GDI (gray-scale antialiased), so we
1032 * can get linear values.
1033 *
1034 * GDI grayscale appears to use a hard-coded gamma of 2.3.
1035 *
1036 * GDI grayscale appears to draw using the black and white rasterizer at four
1037 * times the size and then downsamples to compute the coverage mask. As a
1038 * result there are only seventeen total grays. This lack of fidelity means
1039 * that shifting into other color spaces is imprecise.
1040 */
1041static const uint8_t* getInverseGammaTableGDI() {
bungeman@google.com05a729f2013-06-20 15:29:16 +00001042 // Since build_power_table is idempotent, many threads can build gTableGdi
1043 // simultaneously.
1044
1045 // Microsoft Specific:
1046 // Making gInited volatile provides read-aquire and write-release in vc++.
1047 // In VS2012, see compiler option /volatile:(ms|iso).
1048 // Replace with C++11 atomics when possible.
1049 static volatile bool gInited;
bungeman@google.com97efada2012-07-30 20:40:50 +00001050 static uint8_t gTableGdi[256];
bungeman@google.com05a729f2013-06-20 15:29:16 +00001051 if (gInited) {
1052 // Need a L/L (read) barrier (full acquire not needed). If gInited is observed
1053 // true then gTableGdi is observable, but it must be requested.
1054 } else {
bungeman@google.com97efada2012-07-30 20:40:50 +00001055 build_power_table(gTableGdi, 2.3f);
bungeman@google.com05a729f2013-06-20 15:29:16 +00001056 // Need a S/S (write) barrier (full release not needed) here so that this
1057 // write to gInited becomes observable after gTableGdi.
bungeman@google.com97efada2012-07-30 20:40:50 +00001058 gInited = true;
1059 }
1060 return gTableGdi;
1061}
1062
1063/**
1064 * This will invert the gamma applied by GDI ClearType, so we can get linear
1065 * values.
1066 *
1067 * GDI ClearType uses SPI_GETFONTSMOOTHINGCONTRAST / 1000 as the gamma value.
1068 * If this value is not specified, the default is a gamma of 1.4.
1069 */
1070static const uint8_t* getInverseGammaTableClearType() {
bungeman@google.com05a729f2013-06-20 15:29:16 +00001071 // We don't expect SPI_GETFONTSMOOTHINGCONTRAST to ever change, so building
1072 // gTableClearType with build_power_table is effectively idempotent.
1073
1074 // Microsoft Specific:
1075 // Making gInited volatile provides read-aquire and write-release in vc++.
1076 // In VS2012, see compiler option /volatile:(ms|iso).
1077 // Replace with C++11 atomics when possible.
1078 static volatile bool gInited;
bungeman@google.com97efada2012-07-30 20:40:50 +00001079 static uint8_t gTableClearType[256];
bungeman@google.com05a729f2013-06-20 15:29:16 +00001080 if (gInited) {
1081 // Need a L/L (read) barrier (acquire not needed). If gInited is observed
1082 // true then gTableClearType is observable, but it must be requested.
1083 } else {
reed@google.com7430a332011-10-03 14:37:38 +00001084 UINT level = 0;
1085 if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &level, 0) || !level) {
1086 // can't get the data, so use a default
1087 level = 1400;
1088 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001089 build_power_table(gTableClearType, level / 1000.0f);
bungeman@google.com05a729f2013-06-20 15:29:16 +00001090 // Need a S/S (write) barrier (release not needed) here so that this
1091 // write to gInited becomes observable after gTableClearType.
reed@google.com7430a332011-10-03 14:37:38 +00001092 gInited = true;
1093 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001094 return gTableClearType;
reed@google.com7430a332011-10-03 14:37:38 +00001095}
1096
reed@google.comac6b9792011-03-11 15:42:51 +00001097#include "SkColorPriv.h"
1098
bungeman@google.com63853142012-08-01 15:36:46 +00001099//Cannot assume that the input rgb is gray due to possible setting of kGenA8FromLCD_Flag.
bungeman@google.com97efada2012-07-30 20:40:50 +00001100template<bool APPLY_PREBLEND>
1101static inline uint8_t rgb_to_a8(SkGdiRGB rgb, const uint8_t* table8) {
bungeman@google.com63853142012-08-01 15:36:46 +00001102 U8CPU r = (rgb >> 16) & 0xFF;
1103 U8CPU g = (rgb >> 8) & 0xFF;
1104 U8CPU b = (rgb >> 0) & 0xFF;
bungeman@google.com1bfe01d2012-08-28 16:02:42 +00001105 return sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
reed@google.com8351aab2012-01-18 17:06:35 +00001106}
1107
bungeman@google.com97efada2012-07-30 20:40:50 +00001108template<bool APPLY_PREBLEND>
1109static inline uint16_t rgb_to_lcd16(SkGdiRGB rgb, const uint8_t* tableR,
1110 const uint8_t* tableG,
1111 const uint8_t* tableB) {
1112 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1113 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1114 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001115#if SK_SHOW_TEXT_BLIT_COVERAGE
1116 r = SkMax32(r, 10); g = SkMax32(g, 10); b = SkMax32(b, 10);
1117#endif
bungeman@google.com97efada2012-07-30 20:40:50 +00001118 return SkPack888ToRGB16(r, g, b);
reed@google.com82a34d82011-07-26 19:33:08 +00001119}
1120
reed@google.com82cff022011-09-22 14:33:40 +00001121// Is this GDI color neither black nor white? If so, we have to keep this
1122// image as is, rather than smashing it down to a BW mask.
1123//
1124// returns int instead of bool, since we don't want/have to pay to convert
1125// the zero/non-zero value into a bool
1126static int is_not_black_or_white(SkGdiRGB c) {
1127 // same as (but faster than)
1128 // c &= 0x00FFFFFF;
1129 // return 0 == c || 0x00FFFFFF == c;
1130 return (c + (c & 1)) & 0x00FFFFFF;
reed@google.com5e2df642011-09-21 18:42:09 +00001131}
1132
bungeman@google.com4b18f572013-07-22 15:21:23 +00001133static bool is_rgb_really_bw(const SkGdiRGB* src, int width, int height, size_t srcRB) {
reed@google.com5e2df642011-09-21 18:42:09 +00001134 for (int y = 0; y < height; ++y) {
1135 for (int x = 0; x < width; ++x) {
reed@google.com82cff022011-09-22 14:33:40 +00001136 if (is_not_black_or_white(src[x])) {
reed@google.com5e2df642011-09-21 18:42:09 +00001137 return false;
1138 }
1139 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001140 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001141 }
1142 return true;
1143}
1144
bungeman@google.com97efada2012-07-30 20:40:50 +00001145// gdi's bitmap is upside-down, so we reverse dst walking in Y
1146// whenever we copy it into skia's buffer
reed@google.com5e2df642011-09-21 18:42:09 +00001147static void rgb_to_bw(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
bungeman@google.com97efada2012-07-30 20:40:50 +00001148 const SkGlyph& glyph) {
reed@google.com5e2df642011-09-21 18:42:09 +00001149 const int width = glyph.fWidth;
1150 const size_t dstRB = (width + 7) >> 3;
1151 uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1152
1153 int byteCount = width >> 3;
1154 int bitCount = width & 7;
1155
1156 // adjust srcRB to skip the values in our byteCount loop,
1157 // since we increment src locally there
1158 srcRB -= byteCount * 8 * sizeof(SkGdiRGB);
1159
1160 for (int y = 0; y < glyph.fHeight; ++y) {
1161 if (byteCount > 0) {
reed@google.com5e2df642011-09-21 18:42:09 +00001162 for (int i = 0; i < byteCount; ++i) {
reed@google.com7a230142011-09-27 14:07:21 +00001163 unsigned byte = 0;
bungeman@google.com97efada2012-07-30 20:40:50 +00001164 byte |= src[0] & (1 << 7);
1165 byte |= src[1] & (1 << 6);
1166 byte |= src[2] & (1 << 5);
1167 byte |= src[3] & (1 << 4);
1168 byte |= src[4] & (1 << 3);
1169 byte |= src[5] & (1 << 2);
1170 byte |= src[6] & (1 << 1);
1171 byte |= src[7] & (1 << 0);
reed@google.com6a8f14d2011-09-27 12:54:24 +00001172 dst[i] = byte;
reed@google.com5e2df642011-09-21 18:42:09 +00001173 src += 8;
1174 }
1175 }
1176 if (bitCount > 0) {
1177 unsigned byte = 0;
1178 unsigned mask = 0x80;
1179 for (int i = 0; i < bitCount; i++) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001180 byte |= src[i] & mask;
reed@google.com5e2df642011-09-21 18:42:09 +00001181 mask >>= 1;
1182 }
1183 dst[byteCount] = byte;
1184 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001185 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001186 dst -= dstRB;
1187 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001188#if SK_SHOW_TEXT_BLIT_COVERAGE
1189 if (glyph.fWidth > 0 && glyph.fHeight > 0) {
1190 uint8_t* first = (uint8_t*)glyph.fImage;
1191 uint8_t* last = (uint8_t*)((char*)glyph.fImage + glyph.fHeight * dstRB - 1);
1192 *first |= 1 << 7;
1193 *last |= bitCount == 0 ? 1 : 1 << (8 - bitCount);
1194 }
1195#endif
reed@google.com5e2df642011-09-21 18:42:09 +00001196}
1197
bungeman@google.com97efada2012-07-30 20:40:50 +00001198template<bool APPLY_PREBLEND>
reed@google.com5e2df642011-09-21 18:42:09 +00001199static void rgb_to_a8(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
bungeman@google.com97efada2012-07-30 20:40:50 +00001200 const SkGlyph& glyph, const uint8_t* table8) {
reed@google.com5e2df642011-09-21 18:42:09 +00001201 const size_t dstRB = glyph.rowBytes();
1202 const int width = glyph.fWidth;
1203 uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1204
1205 for (int y = 0; y < glyph.fHeight; y++) {
1206 for (int i = 0; i < width; i++) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001207 dst[i] = rgb_to_a8<APPLY_PREBLEND>(src[i], table8);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001208#if SK_SHOW_TEXT_BLIT_COVERAGE
1209 dst[i] = SkMax32(dst[i], 10);
1210#endif
reed@google.com5e2df642011-09-21 18:42:09 +00001211 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001212 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001213 dst -= dstRB;
1214 }
1215}
1216
bungeman@google.com97efada2012-07-30 20:40:50 +00001217template<bool APPLY_PREBLEND>
1218static void rgb_to_lcd16(const SkGdiRGB* SK_RESTRICT src, size_t srcRB, const SkGlyph& glyph,
1219 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
reed@google.com5e2df642011-09-21 18:42:09 +00001220 const size_t dstRB = glyph.rowBytes();
1221 const int width = glyph.fWidth;
1222 uint16_t* SK_RESTRICT dst = (uint16_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1223
1224 for (int y = 0; y < glyph.fHeight; y++) {
1225 for (int i = 0; i < width; i++) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001226 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(src[i], tableR, tableG, tableB);
reed@google.com5e2df642011-09-21 18:42:09 +00001227 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001228 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001229 dst = (uint16_t*)((char*)dst - dstRB);
1230 }
1231}
1232
reed@google.com30ddd612013-07-30 17:47:39 +00001233void SkScalerContext_GDI::generateImage(const SkGlyph& glyph) {
reed@google.comac6b9792011-03-11 15:42:51 +00001234 SkASSERT(fDDC);
1235
reed@google.com62711172011-05-18 15:08:10 +00001236 const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
reed@google.com82a34d82011-07-26 19:33:08 +00001237 const bool isAA = !isLCD(fRec);
reed@google.comac6b9792011-03-11 15:42:51 +00001238
reed@google.com99edd432011-09-09 14:59:59 +00001239 size_t srcRB;
bungeman@google.com97efada2012-07-30 20:40:50 +00001240 const void* bits = fOffscreen.draw(glyph, isBW, &srcRB);
halcanary96fcdcc2015-08-27 07:41:13 -07001241 if (nullptr == bits) {
reed@google.com055180c2013-03-21 18:46:35 +00001242 LogFontTypeface::EnsureAccessible(this->getTypeface());
bungeman@google.com97efada2012-07-30 20:40:50 +00001243 bits = fOffscreen.draw(glyph, isBW, &srcRB);
halcanary96fcdcc2015-08-27 07:41:13 -07001244 if (nullptr == bits) {
bungeman@google.com39698b12011-11-15 22:26:41 +00001245 sk_bzero(glyph.fImage, glyph.computeImageSize());
1246 return;
1247 }
reed@google.com82a34d82011-07-26 19:33:08 +00001248 }
reed@google.comac6b9792011-03-11 15:42:51 +00001249
bungeman@google.com97efada2012-07-30 20:40:50 +00001250 if (!isBW) {
bungeman@google.com1bd2d672012-08-13 20:01:51 +00001251 const uint8_t* table;
1252 //The offscreen contains a GDI blit if isAA and kGenA8FromLCD_Flag is not set.
1253 //Otherwise the offscreen contains a ClearType blit.
1254 if (isAA && !(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag)) {
1255 table = getInverseGammaTableGDI();
1256 } else {
1257 table = getInverseGammaTableClearType();
bungeman@google.com97efada2012-07-30 20:40:50 +00001258 }
1259 //Note that the following cannot really be integrated into the
1260 //pre-blend, since we may not be applying the pre-blend; when we aren't
1261 //applying the pre-blend it means that a filter wants linear anyway.
1262 //Other code may also be applying the pre-blend, so we'd need another
1263 //one with this and one without.
reed@google.com6f5df482011-09-28 20:33:24 +00001264 SkGdiRGB* addr = (SkGdiRGB*)bits;
1265 for (int y = 0; y < glyph.fHeight; ++y) {
1266 for (int x = 0; x < glyph.fWidth; ++x) {
1267 int r = (addr[x] >> 16) & 0xFF;
1268 int g = (addr[x] >> 8) & 0xFF;
1269 int b = (addr[x] >> 0) & 0xFF;
reed@google.com7430a332011-10-03 14:37:38 +00001270 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
reed@google.com6f5df482011-09-28 20:33:24 +00001271 }
bungeman@google.com05a729f2013-06-20 15:29:16 +00001272 addr = SkTAddOffset<SkGdiRGB>(addr, srcRB);
reed@google.com6f5df482011-09-28 20:33:24 +00001273 }
1274 }
1275
reed@google.com82a34d82011-07-26 19:33:08 +00001276 int width = glyph.fWidth;
1277 size_t dstRB = glyph.rowBytes();
1278 if (isBW) {
1279 const uint8_t* src = (const uint8_t*)bits;
reed@google.com82a34d82011-07-26 19:33:08 +00001280 uint8_t* dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1281 for (int y = 0; y < glyph.fHeight; y++) {
1282 memcpy(dst, src, dstRB);
1283 src += srcRB;
1284 dst -= dstRB;
reed@google.comac6b9792011-03-11 15:42:51 +00001285 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001286#if SK_SHOW_TEXT_BLIT_COVERAGE
1287 if (glyph.fWidth > 0 && glyph.fHeight > 0) {
1288 int bitCount = width & 7;
1289 uint8_t* first = (uint8_t*)glyph.fImage;
1290 uint8_t* last = (uint8_t*)((char*)glyph.fImage + glyph.fHeight * dstRB - 1);
1291 *first |= 1 << 7;
1292 *last |= bitCount == 0 ? 1 : 1 << (8 - bitCount);
1293 }
1294#endif
reed@google.com82a34d82011-07-26 19:33:08 +00001295 } else if (isAA) {
reed@google.com6f5df482011-09-28 20:33:24 +00001296 // since the caller may require A8 for maskfilters, we can't check for BW
1297 // ... until we have the caller tell us that explicitly
reed@google.com5e2df642011-09-21 18:42:09 +00001298 const SkGdiRGB* src = (const SkGdiRGB*)bits;
bungeman@google.coma76de722012-10-26 19:35:54 +00001299 if (fPreBlend.isApplicable()) {
1300 rgb_to_a8<true>(src, srcRB, glyph, fPreBlend.fG);
bungeman@google.com97efada2012-07-30 20:40:50 +00001301 } else {
bungeman@google.coma76de722012-10-26 19:35:54 +00001302 rgb_to_a8<false>(src, srcRB, glyph, fPreBlend.fG);
bungeman@google.com97efada2012-07-30 20:40:50 +00001303 }
reed@google.com82a34d82011-07-26 19:33:08 +00001304 } else { // LCD16
reed@google.com5e2df642011-09-21 18:42:09 +00001305 const SkGdiRGB* src = (const SkGdiRGB*)bits;
1306 if (is_rgb_really_bw(src, width, glyph.fHeight, srcRB)) {
bungeman@google.com97efada2012-07-30 20:40:50 +00001307 rgb_to_bw(src, srcRB, glyph);
reed@google.com5e2df642011-09-21 18:42:09 +00001308 ((SkGlyph*)&glyph)->fMaskFormat = SkMask::kBW_Format;
1309 } else {
reedd54d3fc2014-11-13 14:39:58 -08001310 SkASSERT(SkMask::kLCD16_Format == glyph.fMaskFormat);
1311 if (fPreBlend.isApplicable()) {
1312 rgb_to_lcd16<true>(src, srcRB, glyph,
1313 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
reed@google.com754e4eb2011-09-26 13:21:39 +00001314 } else {
reedd54d3fc2014-11-13 14:39:58 -08001315 rgb_to_lcd16<false>(src, srcRB, glyph,
1316 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
reed@google.com754e4eb2011-09-26 13:21:39 +00001317 }
reed@google.comac6b9792011-03-11 15:42:51 +00001318 }
1319 }
reed@google.comac6b9792011-03-11 15:42:51 +00001320}
1321
bungeman@google.com0abbff92013-07-27 20:37:56 +00001322class GDIGlyphbufferPointIter {
1323public:
skia.committer@gmail.com27e21fe2013-07-28 07:01:03 +00001324 GDIGlyphbufferPointIter(const uint8_t* glyphbuf, DWORD total_size)
bungeman@google.com0abbff92013-07-27 20:37:56 +00001325 : fHeaderIter(glyphbuf, total_size), fCurveIter(), fPointIter()
1326 { }
reed@google.comac6b9792011-03-11 15:42:51 +00001327
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001328 POINTFX const * next() {
bungeman@google.com0abbff92013-07-27 20:37:56 +00001329nextHeader:
1330 if (!fCurveIter.isSet()) {
1331 const TTPOLYGONHEADER* header = fHeaderIter.next();
halcanary96fcdcc2015-08-27 07:41:13 -07001332 if (nullptr == header) {
1333 return nullptr;
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001334 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001335 fCurveIter.set(header);
1336 const TTPOLYCURVE* curve = fCurveIter.next();
halcanary96fcdcc2015-08-27 07:41:13 -07001337 if (nullptr == curve) {
1338 return nullptr;
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001339 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001340 fPointIter.set(curve);
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001341 return &header->pfxStart;
bungeman@google.com05a729f2013-06-20 15:29:16 +00001342 }
1343
bungeman@google.com0abbff92013-07-27 20:37:56 +00001344 const POINTFX* nextPoint = fPointIter.next();
halcanary96fcdcc2015-08-27 07:41:13 -07001345 if (nullptr == nextPoint) {
bungeman@google.com0abbff92013-07-27 20:37:56 +00001346 const TTPOLYCURVE* curve = fCurveIter.next();
halcanary96fcdcc2015-08-27 07:41:13 -07001347 if (nullptr == curve) {
bungeman@google.com0abbff92013-07-27 20:37:56 +00001348 fCurveIter.set();
1349 goto nextHeader;
1350 } else {
1351 fPointIter.set(curve);
bungeman@google.com05a729f2013-06-20 15:29:16 +00001352 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001353 nextPoint = fPointIter.next();
reed@google.comac6b9792011-03-11 15:42:51 +00001354 }
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001355 return nextPoint;
reed@google.comac6b9792011-03-11 15:42:51 +00001356 }
bungeman@google.comd1c7f712013-03-11 18:51:19 +00001357
bungeman@google.com0abbff92013-07-27 20:37:56 +00001358 WORD currentCurveType() {
1359 return fPointIter.fCurveType;
1360 }
1361
1362private:
1363 /** Iterates over all of the polygon headers in a glyphbuf. */
1364 class GDIPolygonHeaderIter {
1365 public:
skia.committer@gmail.com27e21fe2013-07-28 07:01:03 +00001366 GDIPolygonHeaderIter(const uint8_t* glyphbuf, DWORD total_size)
bungeman@google.com0abbff92013-07-27 20:37:56 +00001367 : fCurPolygon(reinterpret_cast<const TTPOLYGONHEADER*>(glyphbuf))
1368 , fEndPolygon(SkTAddOffset<const TTPOLYGONHEADER>(glyphbuf, total_size))
1369 { }
1370
1371 const TTPOLYGONHEADER* next() {
1372 if (fCurPolygon >= fEndPolygon) {
halcanary96fcdcc2015-08-27 07:41:13 -07001373 return nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001374 }
1375 const TTPOLYGONHEADER* thisPolygon = fCurPolygon;
1376 fCurPolygon = SkTAddOffset<const TTPOLYGONHEADER>(fCurPolygon, fCurPolygon->cb);
1377 return thisPolygon;
1378 }
1379 private:
1380 const TTPOLYGONHEADER* fCurPolygon;
1381 const TTPOLYGONHEADER* fEndPolygon;
1382 };
1383
1384 /** Iterates over all of the polygon curves in a polygon header. */
1385 class GDIPolygonCurveIter {
1386 public:
halcanary96fcdcc2015-08-27 07:41:13 -07001387 GDIPolygonCurveIter() : fCurCurve(nullptr), fEndCurve(nullptr) { }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001388
1389 GDIPolygonCurveIter(const TTPOLYGONHEADER* curPolygon)
1390 : fCurCurve(SkTAddOffset<const TTPOLYCURVE>(curPolygon, sizeof(TTPOLYGONHEADER)))
1391 , fEndCurve(SkTAddOffset<const TTPOLYCURVE>(curPolygon, curPolygon->cb))
1392 { }
1393
halcanary96fcdcc2015-08-27 07:41:13 -07001394 bool isSet() { return fCurCurve != nullptr; }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001395
1396 void set(const TTPOLYGONHEADER* curPolygon) {
1397 fCurCurve = SkTAddOffset<const TTPOLYCURVE>(curPolygon, sizeof(TTPOLYGONHEADER));
1398 fEndCurve = SkTAddOffset<const TTPOLYCURVE>(curPolygon, curPolygon->cb);
1399 }
1400 void set() {
halcanary96fcdcc2015-08-27 07:41:13 -07001401 fCurCurve = nullptr;
1402 fEndCurve = nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001403 }
1404
1405 const TTPOLYCURVE* next() {
1406 if (fCurCurve >= fEndCurve) {
halcanary96fcdcc2015-08-27 07:41:13 -07001407 return nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001408 }
1409 const TTPOLYCURVE* thisCurve = fCurCurve;
1410 fCurCurve = SkTAddOffset<const TTPOLYCURVE>(fCurCurve, size_of_TTPOLYCURVE(*fCurCurve));
1411 return thisCurve;
1412 }
1413 private:
1414 size_t size_of_TTPOLYCURVE(const TTPOLYCURVE& curve) {
1415 return 2*sizeof(WORD) + curve.cpfx*sizeof(POINTFX);
1416 }
1417 const TTPOLYCURVE* fCurCurve;
1418 const TTPOLYCURVE* fEndCurve;
1419 };
1420
1421 /** Iterates over all of the polygon points in a polygon curve. */
1422 class GDIPolygonCurvePointIter {
1423 public:
halcanary96fcdcc2015-08-27 07:41:13 -07001424 GDIPolygonCurvePointIter() : fCurveType(0), fCurPoint(nullptr), fEndPoint(nullptr) { }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001425
1426 GDIPolygonCurvePointIter(const TTPOLYCURVE* curPolygon)
1427 : fCurveType(curPolygon->wType)
1428 , fCurPoint(&curPolygon->apfx[0])
1429 , fEndPoint(&curPolygon->apfx[curPolygon->cpfx])
1430 { }
1431
halcanary96fcdcc2015-08-27 07:41:13 -07001432 bool isSet() { return fCurPoint != nullptr; }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001433
1434 void set(const TTPOLYCURVE* curPolygon) {
1435 fCurveType = curPolygon->wType;
1436 fCurPoint = &curPolygon->apfx[0];
1437 fEndPoint = &curPolygon->apfx[curPolygon->cpfx];
1438 }
1439 void set() {
halcanary96fcdcc2015-08-27 07:41:13 -07001440 fCurPoint = nullptr;
1441 fEndPoint = nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001442 }
1443
1444 const POINTFX* next() {
1445 if (fCurPoint >= fEndPoint) {
halcanary96fcdcc2015-08-27 07:41:13 -07001446 return nullptr;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001447 }
1448 const POINTFX* thisPoint = fCurPoint;
1449 ++fCurPoint;
1450 return thisPoint;
1451 }
1452
1453 WORD fCurveType;
1454 private:
1455 const POINTFX* fCurPoint;
1456 const POINTFX* fEndPoint;
1457 };
1458
1459 GDIPolygonHeaderIter fHeaderIter;
1460 GDIPolygonCurveIter fCurveIter;
1461 GDIPolygonCurvePointIter fPointIter;
1462};
1463
1464static void sk_path_from_gdi_path(SkPath* path, const uint8_t* glyphbuf, DWORD total_size) {
bungeman@google.comd1c7f712013-03-11 18:51:19 +00001465 const uint8_t* cur_glyph = glyphbuf;
1466 const uint8_t* end_glyph = glyphbuf + total_size;
1467
1468 while (cur_glyph < end_glyph) {
1469 const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
1470
1471 const uint8_t* end_poly = cur_glyph + th->cb;
1472 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
1473
1474 path->moveTo(SkFixedToScalar( SkFIXEDToFixed(th->pfxStart.x)),
1475 SkFixedToScalar(-SkFIXEDToFixed(th->pfxStart.y)));
1476
1477 while (cur_poly < end_poly) {
1478 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
1479
1480 if (pc->wType == TT_PRIM_LINE) {
1481 for (uint16_t i = 0; i < pc->cpfx; i++) {
1482 path->lineTo(SkFixedToScalar( SkFIXEDToFixed(pc->apfx[i].x)),
1483 SkFixedToScalar(-SkFIXEDToFixed(pc->apfx[i].y)));
1484 }
1485 }
1486
1487 if (pc->wType == TT_PRIM_QSPLINE) {
1488 for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
1489 POINTFX pnt_b = pc->apfx[u]; // B is always the current point
1490 POINTFX pnt_c = pc->apfx[u+1];
1491
1492 if (u < pc->cpfx - 2) { // If not on last spline, compute C
1493 pnt_c.x = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.x),
1494 SkFIXEDToFixed(pnt_c.x)));
1495 pnt_c.y = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.y),
1496 SkFIXEDToFixed(pnt_c.y)));
1497 }
1498
1499 path->quadTo(SkFixedToScalar( SkFIXEDToFixed(pnt_b.x)),
1500 SkFixedToScalar(-SkFIXEDToFixed(pnt_b.y)),
1501 SkFixedToScalar( SkFIXEDToFixed(pnt_c.x)),
1502 SkFixedToScalar(-SkFIXEDToFixed(pnt_c.y)));
1503 }
1504 }
1505 // Advance past this TTPOLYCURVE.
1506 cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx;
1507 }
1508 cur_glyph += th->cb;
1509 path->close();
reed@google.comac6b9792011-03-11 15:42:51 +00001510 }
reed@google.comac6b9792011-03-11 15:42:51 +00001511}
1512
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001513#define move_next_expected_hinted_point(iter, pElem) do {\
1514 pElem = iter.next(); \
halcanary96fcdcc2015-08-27 07:41:13 -07001515 if (nullptr == pElem) return false; \
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001516} while(0)
1517
1518// It is possible for the hinted and unhinted versions of the same path to have
1519// a different number of points due to GDI's handling of flipped points.
1520// If this is detected, this will return false.
1521static bool sk_path_from_gdi_paths(SkPath* path, const uint8_t* glyphbuf, DWORD total_size,
bungeman@google.com0abbff92013-07-27 20:37:56 +00001522 GDIGlyphbufferPointIter hintedYs) {
1523 const uint8_t* cur_glyph = glyphbuf;
1524 const uint8_t* end_glyph = glyphbuf + total_size;
1525
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001526 POINTFX const * hintedPoint;
1527
bungeman@google.com0abbff92013-07-27 20:37:56 +00001528 while (cur_glyph < end_glyph) {
1529 const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
1530
1531 const uint8_t* end_poly = cur_glyph + th->cb;
1532 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
1533
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 path->moveTo(SkFixedToScalar( SkFIXEDToFixed(th->pfxStart.x)),
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001536 SkFixedToScalar(-SkFIXEDToFixed(hintedPoint->y)));
bungeman@google.com0abbff92013-07-27 20:37:56 +00001537
1538 while (cur_poly < end_poly) {
1539 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
1540
1541 if (pc->wType == TT_PRIM_LINE) {
1542 for (uint16_t i = 0; i < pc->cpfx; i++) {
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001543 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001544 path->lineTo(SkFixedToScalar( SkFIXEDToFixed(pc->apfx[i].x)),
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001545 SkFixedToScalar(-SkFIXEDToFixed(hintedPoint->y)));
bungeman@google.com0abbff92013-07-27 20:37:56 +00001546 }
1547 }
1548
1549 if (pc->wType == TT_PRIM_QSPLINE) {
1550 POINTFX currentPoint = pc->apfx[0];
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001551 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001552 // only take the hinted y if it wasn't flipped
1553 if (hintedYs.currentCurveType() == TT_PRIM_QSPLINE) {
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001554 currentPoint.y = hintedPoint->y;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001555 }
1556 for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
1557 POINTFX pnt_b = currentPoint;//pc->apfx[u]; // B is always the current point
1558 POINTFX pnt_c = pc->apfx[u+1];
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001559 move_next_expected_hinted_point(hintedYs, hintedPoint);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001560 // only take the hinted y if it wasn't flipped
1561 if (hintedYs.currentCurveType() == TT_PRIM_QSPLINE) {
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001562 pnt_c.y = hintedPoint->y;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001563 }
1564 currentPoint.x = pnt_c.x;
1565 currentPoint.y = pnt_c.y;
1566
1567 if (u < pc->cpfx - 2) { // If not on last spline, compute C
1568 pnt_c.x = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.x),
1569 SkFIXEDToFixed(pnt_c.x)));
1570 pnt_c.y = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.y),
1571 SkFIXEDToFixed(pnt_c.y)));
1572 }
1573
1574 path->quadTo(SkFixedToScalar( SkFIXEDToFixed(pnt_b.x)),
1575 SkFixedToScalar(-SkFIXEDToFixed(pnt_b.y)),
1576 SkFixedToScalar( SkFIXEDToFixed(pnt_c.x)),
1577 SkFixedToScalar(-SkFIXEDToFixed(pnt_c.y)));
1578 }
1579 }
1580 // Advance past this TTPOLYCURVE.
1581 cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx;
1582 }
1583 cur_glyph += th->cb;
1584 path->close();
1585 }
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001586 return true;
bungeman@google.com0abbff92013-07-27 20:37:56 +00001587}
1588
reed@google.com30ddd612013-07-30 17:47:39 +00001589DWORD SkScalerContext_GDI::getGDIGlyphPath(const SkGlyph& glyph, UINT flags,
bungeman@google.com0abbff92013-07-27 20:37:56 +00001590 SkAutoSTMalloc<BUFFERSIZE, uint8_t>* glyphbuf)
1591{
1592 GLYPHMETRICS gm;
1593
herbb69d0e02015-02-25 06:47:06 -08001594 DWORD total_size = GetGlyphOutlineW(fDDC, glyph.getGlyphID(), flags, &gm, BUFFERSIZE, glyphbuf->get(), &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001595 // Sometimes GetGlyphOutlineW returns a number larger than BUFFERSIZE even if BUFFERSIZE > 0.
1596 // It has been verified that this does not involve a buffer overrun.
1597 if (GDI_ERROR == total_size || total_size > BUFFERSIZE) {
1598 // GDI_ERROR because the BUFFERSIZE was too small, or because the data was not accessible.
1599 // When the data is not accessable GetGlyphOutlineW fails rather quickly,
1600 // so just try to get the size. If that fails then ensure the data is accessible.
halcanary96fcdcc2015-08-27 07:41:13 -07001601 total_size = GetGlyphOutlineW(fDDC, glyph.getGlyphID(), flags, &gm, 0, nullptr, &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001602 if (GDI_ERROR == total_size) {
1603 LogFontTypeface::EnsureAccessible(this->getTypeface());
halcanary96fcdcc2015-08-27 07:41:13 -07001604 total_size = GetGlyphOutlineW(fDDC, glyph.getGlyphID(), flags, &gm, 0, nullptr, &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001605 if (GDI_ERROR == total_size) {
kkinnunenc6cb56f2014-06-24 00:12:27 -07001606 // GetGlyphOutlineW is known to fail for some characters, such as spaces.
1607 // In these cases, just return that the glyph does not have a shape.
bungeman@google.com0abbff92013-07-27 20:37:56 +00001608 return 0;
1609 }
1610 }
1611
1612 glyphbuf->reset(total_size);
1613
herbb69d0e02015-02-25 06:47:06 -08001614 DWORD ret = GetGlyphOutlineW(fDDC, glyph.getGlyphID(), flags, &gm, total_size, glyphbuf->get(), &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001615 if (GDI_ERROR == ret) {
1616 LogFontTypeface::EnsureAccessible(this->getTypeface());
herbb69d0e02015-02-25 06:47:06 -08001617 ret = GetGlyphOutlineW(fDDC, glyph.getGlyphID(), flags, &gm, total_size, glyphbuf->get(), &fMat22);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001618 if (GDI_ERROR == ret) {
1619 SkASSERT(false);
1620 return 0;
1621 }
1622 }
1623 }
1624 return total_size;
1625}
1626
reed@google.com30ddd612013-07-30 17:47:39 +00001627void SkScalerContext_GDI::generatePath(const SkGlyph& glyph, SkPath* path) {
dcheng3ba043f2015-07-08 13:25:23 -07001628 SkASSERT(path);
bungeman@google.com0abbff92013-07-27 20:37:56 +00001629 SkASSERT(fDDC);
1630
1631 path->reset();
1632
1633 // Out of all the fonts on a typical Windows box,
1634 // 25% of glyphs require more than 2KB.
1635 // 1% of glyphs require more than 4KB.
1636 // 0.01% of glyphs require more than 8KB.
1637 // 8KB is less than 1% of the normal 1MB stack on Windows.
1638 // Note that some web fonts glyphs require more than 20KB.
1639 //static const DWORD BUFFERSIZE = (1 << 13);
1640
1641 //GDI only uses hinted outlines when axis aligned.
1642 UINT format = GGO_NATIVE | GGO_GLYPH_INDEX;
1643 if (fRec.getHinting() == SkPaint::kNo_Hinting || fRec.getHinting() == SkPaint::kSlight_Hinting){
1644 format |= GGO_UNHINTED;
1645 }
1646 SkAutoSTMalloc<BUFFERSIZE, uint8_t> glyphbuf(BUFFERSIZE);
1647 DWORD total_size = getGDIGlyphPath(glyph, format, &glyphbuf);
1648 if (0 == total_size) {
1649 return;
1650 }
1651
1652 if (fRec.getHinting() != SkPaint::kSlight_Hinting) {
1653 sk_path_from_gdi_path(path, glyphbuf, total_size);
1654 } else {
1655 //GDI only uses hinted outlines when axis aligned.
1656 UINT format = GGO_NATIVE | GGO_GLYPH_INDEX;
1657
1658 SkAutoSTMalloc<BUFFERSIZE, uint8_t> hintedGlyphbuf(BUFFERSIZE);
1659 DWORD hinted_total_size = getGDIGlyphPath(glyph, format, &hintedGlyphbuf);
1660 if (0 == hinted_total_size) {
1661 return;
1662 }
1663
bungeman@google.comdd88ecc2013-09-20 17:25:31 +00001664 if (!sk_path_from_gdi_paths(path, glyphbuf, total_size,
1665 GDIGlyphbufferPointIter(hintedGlyphbuf, hinted_total_size)))
1666 {
1667 path->reset();
1668 sk_path_from_gdi_path(path, glyphbuf, total_size);
1669 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00001670 }
1671}
1672
reed@google.com484f5bc2013-04-24 19:14:56 +00001673static void logfont_for_name(const char* familyName, LOGFONT* lf) {
1674 sk_bzero(lf, sizeof(LOGFONT));
bungeman@google.come70f7982012-06-01 19:38:19 +00001675#ifdef UNICODE
reed@google.com484f5bc2013-04-24 19:14:56 +00001676 // Get the buffer size needed first.
1677 size_t str_len = ::MultiByteToWideChar(CP_UTF8, 0, familyName,
halcanary96fcdcc2015-08-27 07:41:13 -07001678 -1, nullptr, 0);
reed@google.com484f5bc2013-04-24 19:14:56 +00001679 // Allocate a buffer (str_len already has terminating null
1680 // accounted for).
1681 wchar_t *wideFamilyName = new wchar_t[str_len];
1682 // Now actually convert the string.
1683 ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1,
1684 wideFamilyName, str_len);
1685 ::wcsncpy(lf->lfFaceName, wideFamilyName, LF_FACESIZE - 1);
1686 delete [] wideFamilyName;
1687 lf->lfFaceName[LF_FACESIZE-1] = L'\0';
bungeman@google.come70f7982012-06-01 19:38:19 +00001688#else
reed@google.com484f5bc2013-04-24 19:14:56 +00001689 ::strncpy(lf->lfFaceName, familyName, LF_FACESIZE - 1);
1690 lf->lfFaceName[LF_FACESIZE - 1] = '\0';
bungeman@google.come70f7982012-06-01 19:38:19 +00001691#endif
1692}
1693
bungemanb374d6a2014-09-17 07:48:59 -07001694void LogFontTypeface::onGetFamilyName(SkString* familyName) const {
bungeman@google.com7103f182012-10-31 20:53:49 +00001695 // Get the actual name of the typeface. The logfont may not know this.
reed@google.com5526ede2013-03-25 13:03:37 +00001696 HFONT font = CreateFontIndirect(&fLogFont);
bungeman@google.com7103f182012-10-31 20:53:49 +00001697
halcanary96fcdcc2015-08-27 07:41:13 -07001698 HDC deviceContext = ::CreateCompatibleDC(nullptr);
bungeman@google.com7103f182012-10-31 20:53:49 +00001699 HFONT savefont = (HFONT)SelectObject(deviceContext, font);
1700
bungemanb374d6a2014-09-17 07:48:59 -07001701 dcfontname_to_skstring(deviceContext, fLogFont, familyName);
bungeman@google.com7103f182012-10-31 20:53:49 +00001702
1703 if (deviceContext) {
1704 ::SelectObject(deviceContext, savefont);
1705 ::DeleteDC(deviceContext);
1706 }
1707 if (font) {
1708 ::DeleteObject(font);
1709 }
bungemanb374d6a2014-09-17 07:48:59 -07001710}
bungeman@google.com7103f182012-10-31 20:53:49 +00001711
bungemanb374d6a2014-09-17 07:48:59 -07001712void LogFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc,
1713 bool* isLocalStream) const {
1714 SkString familyName;
1715 this->onGetFamilyName(&familyName);
reed@google.com5526ede2013-03-25 13:03:37 +00001716 desc->setFamilyName(familyName.c_str());
bungemanb8113782016-07-25 16:54:59 -07001717 desc->setStyle(this->fontStyle());
reed@google.com5526ede2013-03-25 13:03:37 +00001718 *isLocalStream = this->fSerializeAsStream;
reed@google.comac6b9792011-03-11 15:42:51 +00001719}
1720
reed@google.com2689f612013-03-20 20:01:47 +00001721SkAdvancedTypefaceMetrics* LogFontTypeface::onGetAdvancedTypefaceMetrics(
reed39a9a502015-05-12 09:50:04 -07001722 PerGlyphInfo perGlyphInfo,
vandebo@chromium.org37ad8fb2011-08-18 02:38:50 +00001723 const uint32_t* glyphIDs,
reed@google.com2689f612013-03-20 20:01:47 +00001724 uint32_t glyphIDsCount) const {
1725 LOGFONT lf = fLogFont;
halcanary96fcdcc2015-08-27 07:41:13 -07001726 SkAdvancedTypefaceMetrics* info = nullptr;
reed@google.comac6b9792011-03-11 15:42:51 +00001727
halcanary96fcdcc2015-08-27 07:41:13 -07001728 HDC hdc = CreateCompatibleDC(nullptr);
reed@google.comac6b9792011-03-11 15:42:51 +00001729 HFONT font = CreateFontIndirect(&lf);
1730 HFONT savefont = (HFONT)SelectObject(hdc, font);
halcanary96fcdcc2015-08-27 07:41:13 -07001731 HFONT designFont = nullptr;
reed@google.comac6b9792011-03-11 15:42:51 +00001732
reed@google.com05b6f3a2011-11-28 15:30:28 +00001733 const char stem_chars[] = {'i', 'I', '!', '1'};
1734 int16_t min_width;
1735 unsigned glyphCount;
1736
reed@google.comac6b9792011-03-11 15:42:51 +00001737 // To request design units, create a logical font whose height is specified
1738 // as unitsPerEm.
1739 OUTLINETEXTMETRIC otm;
bungeman@google.com39698b12011-11-15 22:26:41 +00001740 unsigned int otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
1741 if (0 == otmRet) {
reed@google.com055180c2013-03-21 18:46:35 +00001742 call_ensure_accessible(lf);
bungeman@google.com39698b12011-11-15 22:26:41 +00001743 otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
1744 }
1745 if (!otmRet || !GetTextFace(hdc, LF_FACESIZE, lf.lfFaceName)) {
reed@google.comac6b9792011-03-11 15:42:51 +00001746 goto Error;
1747 }
1748 lf.lfHeight = -SkToS32(otm.otmEMSquare);
1749 designFont = CreateFontIndirect(&lf);
1750 SelectObject(hdc, designFont);
1751 if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) {
1752 goto Error;
1753 }
bungeman@google.com7bdd6142013-07-15 19:42:57 +00001754 glyphCount = calculateGlyphCount(hdc, fLogFont);
reed@google.comac6b9792011-03-11 15:42:51 +00001755
1756 info = new SkAdvancedTypefaceMetrics;
1757 info->fEmSize = otm.otmEMSquare;
reed@google.comac6b9792011-03-11 15:42:51 +00001758 info->fLastGlyphID = SkToU16(glyphCount - 1);
bungeman@google.com7103f182012-10-31 20:53:49 +00001759 tchar_to_skstring(lf.lfFaceName, &info->fFontName);
vandebo0f9bad02014-06-19 11:05:39 -07001760 // If bit 1 is set, the font may not be embedded in a document.
1761 // If bit 1 is clear, the font can be embedded.
1762 // If bit 2 is set, the embedding is read-only.
1763 if (otm.otmfsType & 0x1) {
1764 info->fFlags = SkTBitOr<SkAdvancedTypefaceMetrics::FontFlags>(
1765 info->fFlags,
1766 SkAdvancedTypefaceMetrics::kNotEmbeddable_FontFlag);
1767 }
reed@google.comac6b9792011-03-11 15:42:51 +00001768
reed39a9a502015-05-12 09:50:04 -07001769 if (perGlyphInfo & kToUnicode_PerGlyphInfo) {
vandebo@chromium.org6744d492011-05-09 18:13:47 +00001770 populate_glyph_to_unicode(hdc, glyphCount, &(info->fGlyphToUnicode));
1771 }
1772
vandebo@chromium.orgd41e70d2012-03-08 19:41:01 +00001773 if (glyphCount > 0 &&
1774 (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE)) {
reed@google.comac6b9792011-03-11 15:42:51 +00001775 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
1776 } else {
edisonn@google.com390c6d72013-04-06 20:26:15 +00001777 goto ReturnInfo;
reed@google.comac6b9792011-03-11 15:42:51 +00001778 }
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +00001779
reed@google.comac6b9792011-03-11 15:42:51 +00001780 // If this bit is clear the font is a fixed pitch font.
1781 if (!(otm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
1782 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1783 }
1784 if (otm.otmTextMetrics.tmItalic) {
1785 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1786 }
reed@google.comac6b9792011-03-11 15:42:51 +00001787 if (otm.otmTextMetrics.tmPitchAndFamily & FF_ROMAN) {
1788 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1789 } else if (otm.otmTextMetrics.tmPitchAndFamily & FF_SCRIPT) {
1790 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1791 }
1792
1793 // The main italic angle of the font, in tenths of a degree counterclockwise
1794 // from vertical.
1795 info->fItalicAngle = otm.otmItalicAngle / 10;
1796 info->fAscent = SkToS16(otm.otmTextMetrics.tmAscent);
1797 info->fDescent = SkToS16(-otm.otmTextMetrics.tmDescent);
1798 // TODO(ctguil): Use alternate cap height calculation.
1799 // MSDN says otmsCapEmHeight is not support but it is returning a value on
1800 // my Win7 box.
1801 info->fCapHeight = otm.otmsCapEmHeight;
1802 info->fBBox =
1803 SkIRect::MakeLTRB(otm.otmrcFontBox.left, otm.otmrcFontBox.top,
1804 otm.otmrcFontBox.right, otm.otmrcFontBox.bottom);
1805
1806 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1807 // This probably isn't very good with an italic font.
reed@google.com05b6f3a2011-11-28 15:30:28 +00001808 min_width = SHRT_MAX;
reed@google.comac6b9792011-03-11 15:42:51 +00001809 info->fStemV = 0;
reed@google.comac6b9792011-03-11 15:42:51 +00001810 for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {
1811 ABC abcWidths;
1812 if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) {
1813 int16_t width = abcWidths.abcB;
1814 if (width > 0 && width < min_width) {
1815 min_width = width;
1816 info->fStemV = min_width;
1817 }
1818 }
1819 }
1820
reed@google.comac6b9792011-03-11 15:42:51 +00001821Error:
edisonn@google.com390c6d72013-04-06 20:26:15 +00001822ReturnInfo:
reed@google.comac6b9792011-03-11 15:42:51 +00001823 SelectObject(hdc, savefont);
1824 DeleteObject(designFont);
1825 DeleteObject(font);
1826 DeleteDC(hdc);
1827
1828 return info;
1829}
1830
bungeman@google.coma5501992012-05-18 19:06:41 +00001831//Dummy representation of a Base64 encoded GUID from create_unique_font_name.
1832#define BASE64_GUID_ID "XXXXXXXXXXXXXXXXXXXXXXXX"
halcanary96fcdcc2015-08-27 07:41:13 -07001833//Length of GUID representation from create_id, including nullptr terminator.
bungeman@google.coma5501992012-05-18 19:06:41 +00001834#define BASE64_GUID_ID_LEN SK_ARRAY_COUNT(BASE64_GUID_ID)
reed@google.comac6b9792011-03-11 15:42:51 +00001835
bungeman99fe8222015-08-20 07:57:51 -07001836static_assert(BASE64_GUID_ID_LEN < LF_FACESIZE, "GUID_longer_than_facesize");
bungeman@google.coma5501992012-05-18 19:06:41 +00001837
1838/**
1839 NameID 6 Postscript names cannot have the character '/'.
1840 It would be easier to hex encode the GUID, but that is 32 bytes,
1841 and many systems have issues with names longer than 28 bytes.
1842 The following need not be any standard base64 encoding.
1843 The encoded value is never decoded.
1844*/
rmistry@google.comd6176b02012-08-23 18:14:13 +00001845static const char postscript_safe_base64_encode[] =
bungeman@google.coma5501992012-05-18 19:06:41 +00001846 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1847 "abcdefghijklmnopqrstuvwxyz"
1848 "0123456789-_=";
1849
1850/**
1851 Formats a GUID into Base64 and places it into buffer.
1852 buffer should have space for at least BASE64_GUID_ID_LEN characters.
1853 The string will always be null terminated.
1854 XXXXXXXXXXXXXXXXXXXXXXXX0
1855 */
1856static void format_guid_b64(const GUID& guid, char* buffer, size_t bufferSize) {
1857 SkASSERT(bufferSize >= BASE64_GUID_ID_LEN);
1858 size_t written = SkBase64::Encode(&guid, sizeof(guid), buffer, postscript_safe_base64_encode);
1859 SkASSERT(written < LF_FACESIZE);
1860 buffer[written] = '\0';
1861}
1862
1863/**
1864 Creates a Base64 encoded GUID and places it into buffer.
1865 buffer should have space for at least BASE64_GUID_ID_LEN characters.
1866 The string will always be null terminated.
1867 XXXXXXXXXXXXXXXXXXXXXXXX0
1868 */
1869static HRESULT create_unique_font_name(char* buffer, size_t bufferSize) {
1870 GUID guid = {};
1871 if (FAILED(CoCreateGuid(&guid))) {
1872 return E_UNEXPECTED;
1873 }
1874 format_guid_b64(guid, buffer, bufferSize);
1875
1876 return S_OK;
1877}
1878
1879/**
halcanary96fcdcc2015-08-27 07:41:13 -07001880 Introduces a font to GDI. On failure will return nullptr. The returned handle
bungeman@google.coma5501992012-05-18 19:06:41 +00001881 should eventually be passed to RemoveFontMemResourceEx.
1882*/
1883static HANDLE activate_font(SkData* fontData) {
1884 DWORD numFonts = 0;
1885 //AddFontMemResourceEx just copies the data, but does not specify const.
1886 HANDLE fontHandle = AddFontMemResourceEx(const_cast<void*>(fontData->data()),
bungeman@google.com4b18f572013-07-22 15:21:23 +00001887 static_cast<DWORD>(fontData->size()),
bungeman@google.coma5501992012-05-18 19:06:41 +00001888 0,
1889 &numFonts);
1890
halcanary96fcdcc2015-08-27 07:41:13 -07001891 if (fontHandle != nullptr && numFonts < 1) {
bungeman@google.coma5501992012-05-18 19:06:41 +00001892 RemoveFontMemResourceEx(fontHandle);
halcanary96fcdcc2015-08-27 07:41:13 -07001893 return nullptr;
bungeman@google.coma5501992012-05-18 19:06:41 +00001894 }
1895
1896 return fontHandle;
1897}
1898
scroggoa1193e42015-01-21 12:09:53 -08001899// Does not affect ownership of stream.
bungeman5f213d92015-01-27 05:39:10 -08001900static SkTypeface* create_from_stream(SkStreamAsset* stream) {
bungeman@google.coma5501992012-05-18 19:06:41 +00001901 // Create a unique and unpredictable font name.
1902 // Avoids collisions and access from CSS.
1903 char familyName[BASE64_GUID_ID_LEN];
1904 const int familyNameSize = SK_ARRAY_COUNT(familyName);
1905 if (FAILED(create_unique_font_name(familyName, familyNameSize))) {
halcanary96fcdcc2015-08-27 07:41:13 -07001906 return nullptr;
bungeman@google.coma5501992012-05-18 19:06:41 +00001907 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001908
bungeman@google.coma5501992012-05-18 19:06:41 +00001909 // Change the name of the font.
bungemanffae30d2016-08-03 13:32:32 -07001910 sk_sp<SkData> rewrittenFontData(SkOTUtils::RenameFont(stream, familyName, familyNameSize-1));
halcanary96fcdcc2015-08-27 07:41:13 -07001911 if (nullptr == rewrittenFontData.get()) {
1912 return nullptr;
bungeman@google.coma5501992012-05-18 19:06:41 +00001913 }
bungeman@google.coma5501992012-05-18 19:06:41 +00001914
1915 // Register the font with GDI.
bungeman@google.come9bbee32012-05-21 13:46:13 +00001916 HANDLE fontReference = activate_font(rewrittenFontData.get());
halcanary96fcdcc2015-08-27 07:41:13 -07001917 if (nullptr == fontReference) {
1918 return nullptr;
bungeman@google.coma5501992012-05-18 19:06:41 +00001919 }
1920
1921 // Create the typeface.
1922 LOGFONT lf;
reed@google.com484f5bc2013-04-24 19:14:56 +00001923 logfont_for_name(familyName, &lf);
bungeman@google.coma5501992012-05-18 19:06:41 +00001924
1925 return SkCreateFontMemResourceTypefaceFromLOGFONT(lf, fontReference);
reed@google.comac6b9792011-03-11 15:42:51 +00001926}
1927
bungeman5f213d92015-01-27 05:39:10 -08001928SkStreamAsset* LogFontTypeface::onOpenStream(int* ttcIndex) const {
reed@google.com0042b9c2013-03-21 20:16:04 +00001929 *ttcIndex = 0;
1930
ctguil@chromium.orgf4c26222011-05-16 22:00:05 +00001931 const DWORD kTTCTag =
1932 SkEndian_SwapBE32(SkSetFourByteTag('t', 't', 'c', 'f'));
reed@google.com0042b9c2013-03-21 20:16:04 +00001933 LOGFONT lf = fLogFont;
reed@google.comac6b9792011-03-11 15:42:51 +00001934
halcanary96fcdcc2015-08-27 07:41:13 -07001935 HDC hdc = ::CreateCompatibleDC(nullptr);
reed@google.com59d2f632011-05-02 19:36:59 +00001936 HFONT font = CreateFontIndirect(&lf);
reed@google.comac6b9792011-03-11 15:42:51 +00001937 HFONT savefont = (HFONT)SelectObject(hdc, font);
1938
halcanary96fcdcc2015-08-27 07:41:13 -07001939 SkMemoryStream* stream = nullptr;
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001940 DWORD tables[2] = {kTTCTag, 0};
1941 for (int i = 0; i < SK_ARRAY_COUNT(tables); i++) {
halcanary96fcdcc2015-08-27 07:41:13 -07001942 DWORD bufferSize = GetFontData(hdc, tables[i], 0, nullptr, 0);
bungeman@google.com39698b12011-11-15 22:26:41 +00001943 if (bufferSize == GDI_ERROR) {
reed@google.com055180c2013-03-21 18:46:35 +00001944 call_ensure_accessible(lf);
halcanary96fcdcc2015-08-27 07:41:13 -07001945 bufferSize = GetFontData(hdc, tables[i], 0, nullptr, 0);
bungeman@google.com39698b12011-11-15 22:26:41 +00001946 }
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001947 if (bufferSize != GDI_ERROR) {
1948 stream = new SkMemoryStream(bufferSize);
bungeman@google.com4b18f572013-07-22 15:21:23 +00001949 if (GetFontData(hdc, tables[i], 0, (void*)stream->getMemoryBase(), bufferSize)) {
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001950 break;
1951 } else {
1952 delete stream;
halcanary96fcdcc2015-08-27 07:41:13 -07001953 stream = nullptr;
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001954 }
1955 }
reed@google.comac6b9792011-03-11 15:42:51 +00001956 }
1957
1958 SelectObject(hdc, savefont);
1959 DeleteObject(font);
1960 DeleteDC(hdc);
1961
1962 return stream;
1963}
1964
bungeman@google.com3c996f82013-10-24 21:39:35 +00001965static void bmpCharsToGlyphs(HDC hdc, const WCHAR* bmpChars, int count, uint16_t* glyphs,
1966 bool Ox1FHack)
1967{
1968 DWORD result = GetGlyphIndicesW(hdc, bmpChars, count, glyphs, GGI_MARK_NONEXISTING_GLYPHS);
1969 if (GDI_ERROR == result) {
1970 for (int i = 0; i < count; ++i) {
1971 glyphs[i] = 0;
1972 }
1973 return;
1974 }
1975
1976 if (Ox1FHack) {
1977 for (int i = 0; i < count; ++i) {
1978 if (0xFFFF == glyphs[i] || 0x1F == glyphs[i]) {
1979 glyphs[i] = 0;
1980 }
1981 }
1982 } else {
1983 for (int i = 0; i < count; ++i) {
1984 if (0xFFFF == glyphs[i]){
1985 glyphs[i] = 0;
1986 }
1987 }
1988 }
1989}
1990
1991static uint16_t nonBmpCharToGlyph(HDC hdc, SCRIPT_CACHE* scriptCache, const WCHAR utf16[2]) {
1992 uint16_t index = 0;
1993 // Use uniscribe to detemine glyph index for non-BMP characters.
1994 static const int numWCHAR = 2;
1995 static const int maxItems = 2;
halcanary96fcdcc2015-08-27 07:41:13 -07001996 // MSDN states that this can be nullptr, but some things don't work then.
bungeman@google.com3c996f82013-10-24 21:39:35 +00001997 SCRIPT_CONTROL scriptControl = { 0 };
1998 // Add extra item to SCRIPT_ITEM to work around a bug (now documented).
1999 // https://bugzilla.mozilla.org/show_bug.cgi?id=366643
2000 SCRIPT_ITEM si[maxItems + 1];
2001 int numItems;
halcanary96fcdcc2015-08-27 07:41:13 -07002002 HRZM(ScriptItemize(utf16, numWCHAR, maxItems, &scriptControl, nullptr, si, &numItems),
bungeman@google.com3c996f82013-10-24 21:39:35 +00002003 "Could not itemize character.");
2004
2005 // Sometimes ScriptShape cannot find a glyph for a non-BMP and returns 2 space glyphs.
2006 static const int maxGlyphs = 2;
2007 SCRIPT_VISATTR vsa[maxGlyphs];
2008 WORD outGlyphs[maxGlyphs];
2009 WORD logClust[numWCHAR];
2010 int numGlyphs;
2011 HRZM(ScriptShape(hdc, scriptCache, utf16, numWCHAR, maxGlyphs, &si[0].a,
2012 outGlyphs, logClust, vsa, &numGlyphs),
2013 "Could not shape character.");
2014 if (1 == numGlyphs) {
2015 index = outGlyphs[0];
2016 }
2017 return index;
2018}
2019
2020class SkAutoHDC {
2021public:
2022 SkAutoHDC(const LOGFONT& lf)
halcanary96fcdcc2015-08-27 07:41:13 -07002023 : fHdc(::CreateCompatibleDC(nullptr))
bungeman@google.com3c996f82013-10-24 21:39:35 +00002024 , fFont(::CreateFontIndirect(&lf))
2025 , fSavefont((HFONT)SelectObject(fHdc, fFont))
2026 { }
2027 ~SkAutoHDC() {
2028 SelectObject(fHdc, fSavefont);
2029 DeleteObject(fFont);
2030 DeleteDC(fHdc);
2031 }
2032 operator HDC() { return fHdc; }
2033private:
2034 HDC fHdc;
2035 HFONT fFont;
2036 HFONT fSavefont;
2037};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +00002038#define SkAutoHDC(...) SK_REQUIRE_LOCAL_VAR(SkAutoHDC)
bungeman@google.com3c996f82013-10-24 21:39:35 +00002039
2040int LogFontTypeface::onCharsToGlyphs(const void* chars, Encoding encoding,
2041 uint16_t userGlyphs[], int glyphCount) const
2042{
2043 SkAutoHDC hdc(fLogFont);
2044
2045 TEXTMETRIC tm;
2046 if (0 == GetTextMetrics(hdc, &tm)) {
2047 call_ensure_accessible(fLogFont);
2048 if (0 == GetTextMetrics(hdc, &tm)) {
2049 tm.tmPitchAndFamily = TMPF_TRUETYPE;
2050 }
2051 }
2052 bool Ox1FHack = !(tm.tmPitchAndFamily & TMPF_VECTOR) /*&& winVer < Vista */;
2053
2054 SkAutoSTMalloc<256, uint16_t> scratchGlyphs;
2055 uint16_t* glyphs;
halcanary96fcdcc2015-08-27 07:41:13 -07002056 if (userGlyphs != nullptr) {
bungeman@google.com3c996f82013-10-24 21:39:35 +00002057 glyphs = userGlyphs;
2058 } else {
2059 glyphs = scratchGlyphs.reset(glyphCount);
2060 }
2061
2062 SCRIPT_CACHE sc = 0;
2063 switch (encoding) {
2064 case SkTypeface::kUTF8_Encoding: {
2065 static const int scratchCount = 256;
2066 WCHAR scratch[scratchCount];
2067 int glyphIndex = 0;
2068 const char* currentUtf8 = reinterpret_cast<const char*>(chars);
2069 SkUnichar currentChar;
2070 if (glyphCount) {
2071 currentChar = SkUTF8_NextUnichar(&currentUtf8);
2072 }
2073 while (glyphIndex < glyphCount) {
2074 // Try a run of bmp.
2075 int glyphsLeft = SkTMin(glyphCount - glyphIndex, scratchCount);
2076 int runLength = 0;
2077 while (runLength < glyphsLeft && currentChar <= 0xFFFF) {
2078 scratch[runLength] = static_cast<WCHAR>(currentChar);
2079 ++runLength;
2080 if (runLength < glyphsLeft) {
2081 currentChar = SkUTF8_NextUnichar(&currentUtf8);
2082 }
2083 }
2084 if (runLength) {
2085 bmpCharsToGlyphs(hdc, scratch, runLength, &glyphs[glyphIndex], Ox1FHack);
2086 glyphIndex += runLength;
2087 }
2088
2089 // Try a run of non-bmp.
2090 while (glyphIndex < glyphCount && currentChar > 0xFFFF) {
2091 SkUTF16_FromUnichar(currentChar, reinterpret_cast<uint16_t*>(scratch));
2092 glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, scratch);
2093 ++glyphIndex;
2094 if (glyphIndex < glyphCount) {
2095 currentChar = SkUTF8_NextUnichar(&currentUtf8);
2096 }
2097 }
2098 }
2099 break;
2100 }
2101 case SkTypeface::kUTF16_Encoding: {
2102 int glyphIndex = 0;
2103 const WCHAR* currentUtf16 = reinterpret_cast<const WCHAR*>(chars);
2104 while (glyphIndex < glyphCount) {
2105 // Try a run of bmp.
2106 int glyphsLeft = glyphCount - glyphIndex;
2107 int runLength = 0;
2108 while (runLength < glyphsLeft && !SkUTF16_IsHighSurrogate(currentUtf16[runLength])) {
2109 ++runLength;
2110 }
2111 if (runLength) {
2112 bmpCharsToGlyphs(hdc, currentUtf16, runLength, &glyphs[glyphIndex], Ox1FHack);
2113 glyphIndex += runLength;
2114 currentUtf16 += runLength;
2115 }
2116
2117 // Try a run of non-bmp.
2118 while (glyphIndex < glyphCount && SkUTF16_IsHighSurrogate(*currentUtf16)) {
2119 glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, currentUtf16);
2120 ++glyphIndex;
2121 currentUtf16 += 2;
2122 }
2123 }
2124 break;
2125 }
2126 case SkTypeface::kUTF32_Encoding: {
2127 static const int scratchCount = 256;
2128 WCHAR scratch[scratchCount];
2129 int glyphIndex = 0;
2130 const uint32_t* utf32 = reinterpret_cast<const uint32_t*>(chars);
2131 while (glyphIndex < glyphCount) {
2132 // Try a run of bmp.
2133 int glyphsLeft = SkTMin(glyphCount - glyphIndex, scratchCount);
2134 int runLength = 0;
2135 while (runLength < glyphsLeft && utf32[glyphIndex + runLength] <= 0xFFFF) {
2136 scratch[runLength] = static_cast<WCHAR>(utf32[glyphIndex + runLength]);
2137 ++runLength;
2138 }
2139 if (runLength) {
2140 bmpCharsToGlyphs(hdc, scratch, runLength, &glyphs[glyphIndex], Ox1FHack);
2141 glyphIndex += runLength;
2142 }
2143
2144 // Try a run of non-bmp.
2145 while (glyphIndex < glyphCount && utf32[glyphIndex] > 0xFFFF) {
2146 SkUTF16_FromUnichar(utf32[glyphIndex], reinterpret_cast<uint16_t*>(scratch));
2147 glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, scratch);
2148 ++glyphIndex;
2149 }
2150 }
2151 break;
2152 }
2153 default:
djsollenf2b340f2016-01-29 08:51:04 -08002154 SK_ABORT("Invalid Text Encoding");
bungeman@google.com3c996f82013-10-24 21:39:35 +00002155 }
2156
2157 if (sc) {
2158 ::ScriptFreeCache(&sc);
2159 }
2160
2161 for (int i = 0; i < glyphCount; ++i) {
2162 if (0 == glyphs[i]) {
2163 return i;
2164 }
2165 }
2166 return glyphCount;
2167}
2168
bungeman@google.com7bdd6142013-07-15 19:42:57 +00002169int LogFontTypeface::onCountGlyphs() const {
halcanary96fcdcc2015-08-27 07:41:13 -07002170 HDC hdc = ::CreateCompatibleDC(nullptr);
bungeman@google.com7bdd6142013-07-15 19:42:57 +00002171 HFONT font = CreateFontIndirect(&fLogFont);
2172 HFONT savefont = (HFONT)SelectObject(hdc, font);
2173
2174 unsigned int glyphCount = calculateGlyphCount(hdc, fLogFont);
2175
2176 SelectObject(hdc, savefont);
2177 DeleteObject(font);
2178 DeleteDC(hdc);
2179
2180 return glyphCount;
2181}
2182
2183int LogFontTypeface::onGetUPEM() const {
halcanary96fcdcc2015-08-27 07:41:13 -07002184 HDC hdc = ::CreateCompatibleDC(nullptr);
bungeman@google.com7bdd6142013-07-15 19:42:57 +00002185 HFONT font = CreateFontIndirect(&fLogFont);
2186 HFONT savefont = (HFONT)SelectObject(hdc, font);
2187
2188 unsigned int upem = calculateUPEM(hdc, fLogFont);
2189
2190 SelectObject(hdc, savefont);
2191 DeleteObject(font);
2192 DeleteDC(hdc);
2193
2194 return upem;
2195}
2196
bungeman@google.com839702b2013-08-07 17:09:22 +00002197SkTypeface::LocalizedStrings* LogFontTypeface::onCreateFamilyNameIterator() const {
bungeman@google.coma9802692013-08-07 02:45:25 +00002198 SkTypeface::LocalizedStrings* nameIter =
2199 SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*this);
halcanary96fcdcc2015-08-27 07:41:13 -07002200 if (nullptr == nameIter) {
bungeman@google.coma9802692013-08-07 02:45:25 +00002201 SkString familyName;
2202 this->getFamilyName(&familyName);
2203 SkString language("und"); //undetermined
2204 nameIter = new SkOTUtils::LocalizedStrings_SingleName(familyName, language);
2205 }
2206 return nameIter;
2207}
2208
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002209int LogFontTypeface::onGetTableTags(SkFontTableTag tags[]) const {
2210 SkSFNTHeader header;
2211 if (sizeof(header) != this->onGetTableData(0, 0, sizeof(header), &header)) {
2212 return 0;
2213 }
2214
2215 int numTables = SkEndian_SwapBE16(header.numTables);
2216
2217 if (tags) {
2218 size_t size = numTables * sizeof(SkSFNTHeader::TableDirectoryEntry);
2219 SkAutoSTMalloc<0x20, SkSFNTHeader::TableDirectoryEntry> dir(numTables);
2220 if (size != this->onGetTableData(0, sizeof(header), size, dir.get())) {
2221 return 0;
2222 }
2223
2224 for (int i = 0; i < numTables; ++i) {
2225 tags[i] = SkEndian_SwapBE32(dir[i].tag);
2226 }
2227 }
2228 return numTables;
2229}
2230
2231size_t LogFontTypeface::onGetTableData(SkFontTableTag tag, size_t offset,
2232 size_t length, void* data) const
2233{
2234 LOGFONT lf = fLogFont;
2235
halcanary96fcdcc2015-08-27 07:41:13 -07002236 HDC hdc = ::CreateCompatibleDC(nullptr);
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002237 HFONT font = CreateFontIndirect(&lf);
2238 HFONT savefont = (HFONT)SelectObject(hdc, font);
2239
2240 tag = SkEndian_SwapBE32(tag);
halcanary96fcdcc2015-08-27 07:41:13 -07002241 if (nullptr == data) {
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002242 length = 0;
2243 }
robertphillips@google.com8b169312013-10-15 17:47:36 +00002244 DWORD bufferSize = GetFontData(hdc, tag, (DWORD) offset, data, (DWORD) length);
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002245 if (bufferSize == GDI_ERROR) {
2246 call_ensure_accessible(lf);
robertphillips@google.com8b169312013-10-15 17:47:36 +00002247 bufferSize = GetFontData(hdc, tag, (DWORD) offset, data, (DWORD) length);
bungeman@google.comb10b51f2013-08-01 20:18:41 +00002248 }
2249
2250 SelectObject(hdc, savefont);
2251 DeleteObject(font);
2252 DeleteDC(hdc);
2253
2254 return bufferSize == GDI_ERROR ? 0 : bufferSize;
2255}
2256
reeda9322c22016-04-12 06:47:05 -07002257SkScalerContext* LogFontTypeface::onCreateScalerContext(const SkScalerContextEffects& effects,
2258 const SkDescriptor* desc) const {
2259 SkScalerContext_GDI* ctx = new SkScalerContext_GDI(const_cast<LogFontTypeface*>(this),
2260 effects, desc);
reed@google.com84e22d82013-07-10 15:38:20 +00002261 if (!ctx->isValid()) {
halcanary385fe4d2015-08-26 13:07:48 -07002262 delete ctx;
halcanary96fcdcc2015-08-27 07:41:13 -07002263 ctx = nullptr;
reed@google.com84e22d82013-07-10 15:38:20 +00002264 }
2265 return ctx;
reed@google.comac6b9792011-03-11 15:42:51 +00002266}
2267
reed@google.com0da48612013-03-19 16:06:52 +00002268void LogFontTypeface::onFilterRec(SkScalerContextRec* rec) const {
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00002269 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
2270 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
2271 {
2272 rec->fMaskFormat = SkMask::kA8_Format;
2273 rec->fFlags |= SkScalerContext::kGenA8FromLCD_Flag;
2274 }
2275
bungeman41078062014-07-07 08:16:37 -07002276 unsigned flagsWeDontSupport = SkScalerContext::kVertical_Flag |
2277 SkScalerContext::kDevKernText_Flag |
bungeman@google.comf6f56872014-01-23 19:01:36 +00002278 SkScalerContext::kForceAutohinting_Flag |
reed@google.come8fab012011-07-13 15:25:33 +00002279 SkScalerContext::kEmbeddedBitmapText_Flag |
2280 SkScalerContext::kEmbolden_Flag |
2281 SkScalerContext::kLCD_BGROrder_Flag |
2282 SkScalerContext::kLCD_Vertical_Flag;
2283 rec->fFlags &= ~flagsWeDontSupport;
2284
reed@google.come8fab012011-07-13 15:25:33 +00002285 SkPaint::Hinting h = rec->getHinting();
2286 switch (h) {
2287 case SkPaint::kNo_Hinting:
bungeman@google.com0abbff92013-07-27 20:37:56 +00002288 break;
reed@google.come8fab012011-07-13 15:25:33 +00002289 case SkPaint::kSlight_Hinting:
bungeman@google.com0abbff92013-07-27 20:37:56 +00002290 // Only do slight hinting when axis aligned.
bungeman@google.com7c86d8e2013-07-27 21:42:21 +00002291 // TODO: re-enable slight hinting when FontHostTest can pass.
2292 //if (!isAxisAligned(*rec)) {
bungeman@google.com0abbff92013-07-27 20:37:56 +00002293 h = SkPaint::kNo_Hinting;
bungeman@google.com7c86d8e2013-07-27 21:42:21 +00002294 //}
reed@google.come8fab012011-07-13 15:25:33 +00002295 break;
2296 case SkPaint::kNormal_Hinting:
2297 case SkPaint::kFull_Hinting:
bungeman@google.com0abbff92013-07-27 20:37:56 +00002298 // TODO: need to be able to distinguish subpixel positioned glyphs
2299 // and linear metrics.
2300 //rec->fFlags &= ~SkScalerContext::kSubpixelPositioning_Flag;
reed@google.come8fab012011-07-13 15:25:33 +00002301 h = SkPaint::kNormal_Hinting;
2302 break;
2303 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002304 SkDEBUGFAIL("unknown hinting");
reed@google.come8fab012011-07-13 15:25:33 +00002305 }
bungeman@google.com0abbff92013-07-27 20:37:56 +00002306 //TODO: if this is a bitmap font, squash hinting and subpixel.
reed@google.come8fab012011-07-13 15:25:33 +00002307 rec->setHinting(h);
reed@google.comda440672011-07-13 18:02:28 +00002308
reed@google.com9181aa82011-08-05 14:28:31 +00002309// turn this off since GDI might turn A8 into BW! Need a bigger fix.
2310#if 0
reed@google.comda440672011-07-13 18:02:28 +00002311 // Disable LCD when rotated, since GDI's output is ugly
2312 if (isLCD(*rec) && !isAxisAligned(*rec)) {
2313 rec->fMaskFormat = SkMask::kA8_Format;
2314 }
reed@google.com9181aa82011-08-05 14:28:31 +00002315#endif
reed@google.com754e4eb2011-09-26 13:21:39 +00002316
reed@google.com0da48612013-03-19 16:06:52 +00002317 if (!fCanBeLCD && isLCD(*rec)) {
bungeman@google.comcb1bbb32012-10-12 18:48:35 +00002318 rec->fMaskFormat = SkMask::kA8_Format;
2319 rec->fFlags &= ~SkScalerContext::kGenA8FromLCD_Flag;
reed@google.com754e4eb2011-09-26 13:21:39 +00002320 }
reed@google.com754e4eb2011-09-26 13:21:39 +00002321}
reed@google.com070da5e2013-03-27 20:01:49 +00002322
2323///////////////////////////////////////////////////////////////////////////////
2324
2325#include "SkFontMgr.h"
reed@google.com484f5bc2013-04-24 19:14:56 +00002326#include "SkDataTable.h"
2327
bungeman@google.coma9802692013-08-07 02:45:25 +00002328static bool valid_logfont_for_enum(const LOGFONT& lf) {
2329 // TODO: Vector FON is unsupported and should not be listed.
2330 return
2331 // Ignore implicit vertical variants.
2332 lf.lfFaceName[0] && lf.lfFaceName[0] != '@'
2333
2334 // DEFAULT_CHARSET is used to get all fonts, but also implies all
2335 // character sets. Filter assuming all fonts support ANSI_CHARSET.
2336 && ANSI_CHARSET == lf.lfCharSet
2337 ;
reed@google.com484f5bc2013-04-24 19:14:56 +00002338}
2339
bungeman@google.coma9802692013-08-07 02:45:25 +00002340/** An EnumFontFamExProc implementation which interprets builderParam as
2341 * an SkTDArray<ENUMLOGFONTEX>* and appends logfonts which
2342 * pass the valid_logfont_for_enum predicate.
2343 */
2344static int CALLBACK enum_family_proc(const LOGFONT* lf, const TEXTMETRIC*,
2345 DWORD fontType, LPARAM builderParam) {
2346 if (valid_logfont_for_enum(*lf)) {
reed@google.coma65a6812013-05-02 19:47:24 +00002347 SkTDArray<ENUMLOGFONTEX>* array = (SkTDArray<ENUMLOGFONTEX>*)builderParam;
2348 *array->append() = *(ENUMLOGFONTEX*)lf;
reed@google.com484f5bc2013-04-24 19:14:56 +00002349 }
2350 return 1; // non-zero means continue
2351}
2352
reed@google.com484f5bc2013-04-24 19:14:56 +00002353class SkFontStyleSetGDI : public SkFontStyleSet {
2354public:
reed@google.coma65a6812013-05-02 19:47:24 +00002355 SkFontStyleSetGDI(const TCHAR familyName[]) {
bungeman@google.coma9802692013-08-07 02:45:25 +00002356 LOGFONT lf;
2357 sk_bzero(&lf, sizeof(lf));
2358 lf.lfCharSet = DEFAULT_CHARSET;
2359 _tcscpy_s(lf.lfFaceName, familyName);
2360
halcanary96fcdcc2015-08-27 07:41:13 -07002361 HDC hdc = ::CreateCompatibleDC(nullptr);
bungeman@google.coma9802692013-08-07 02:45:25 +00002362 ::EnumFontFamiliesEx(hdc, &lf, enum_family_proc, (LPARAM)&fArray, 0);
reed@google.com484f5bc2013-04-24 19:14:56 +00002363 ::DeleteDC(hdc);
2364 }
2365
mtklein36352bf2015-03-25 18:17:31 -07002366 int count() override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002367 return fArray.count();
2368 }
2369
mtklein36352bf2015-03-25 18:17:31 -07002370 void getStyle(int index, SkFontStyle* fs, SkString* styleName) override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002371 if (fs) {
bungemana4c4a2d2014-10-20 13:33:19 -07002372 *fs = get_style(fArray[index].elfLogFont);
reed@google.com484f5bc2013-04-24 19:14:56 +00002373 }
reed@google.coma65a6812013-05-02 19:47:24 +00002374 if (styleName) {
2375 const ENUMLOGFONTEX& ref = fArray[index];
2376 // For some reason, ENUMLOGFONTEX and LOGFONT disagree on their type in the
2377 // non-unicode version.
2378 // ENUMLOGFONTEX uses BYTE
2379 // LOGFONT uses CHAR
2380 // Here we assert they that the style name is logically the same (size) as
2381 // a TCHAR, so we can use the same converter function.
2382 SkASSERT(sizeof(TCHAR) == sizeof(ref.elfStyle[0]));
2383 tchar_to_skstring((const TCHAR*)ref.elfStyle, styleName);
2384 }
reed@google.com484f5bc2013-04-24 19:14:56 +00002385 }
2386
mtklein36352bf2015-03-25 18:17:31 -07002387 SkTypeface* createTypeface(int index) override {
reed@google.coma65a6812013-05-02 19:47:24 +00002388 return SkCreateTypefaceFromLOGFONT(fArray[index].elfLogFont);
reed@google.com484f5bc2013-04-24 19:14:56 +00002389 }
2390
mtklein36352bf2015-03-25 18:17:31 -07002391 SkTypeface* matchStyle(const SkFontStyle& pattern) override {
bungeman147ea2f2015-11-12 09:50:08 -08002392 return this->matchStyleCSS3(pattern);
reed@google.com484f5bc2013-04-24 19:14:56 +00002393 }
2394
2395private:
reed@google.coma65a6812013-05-02 19:47:24 +00002396 SkTDArray<ENUMLOGFONTEX> fArray;
reed@google.com484f5bc2013-04-24 19:14:56 +00002397};
2398
reed@google.com484f5bc2013-04-24 19:14:56 +00002399class SkFontMgrGDI : public SkFontMgr {
bungeman@google.coma9802692013-08-07 02:45:25 +00002400public:
2401 SkFontMgrGDI() {
reed@google.com484f5bc2013-04-24 19:14:56 +00002402 LOGFONT lf;
2403 sk_bzero(&lf, sizeof(lf));
2404 lf.lfCharSet = DEFAULT_CHARSET;
2405
halcanary96fcdcc2015-08-27 07:41:13 -07002406 HDC hdc = ::CreateCompatibleDC(nullptr);
reed@google.com484f5bc2013-04-24 19:14:56 +00002407 ::EnumFontFamiliesEx(hdc, &lf, enum_family_proc, (LPARAM)&fLogFontArray, 0);
2408 ::DeleteDC(hdc);
2409 }
2410
reed@google.com484f5bc2013-04-24 19:14:56 +00002411protected:
mtklein36352bf2015-03-25 18:17:31 -07002412 int onCountFamilies() const override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002413 return fLogFontArray.count();
2414 }
2415
mtklein36352bf2015-03-25 18:17:31 -07002416 void onGetFamilyName(int index, SkString* familyName) const override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002417 SkASSERT((unsigned)index < (unsigned)fLogFontArray.count());
reed@google.coma65a6812013-05-02 19:47:24 +00002418 tchar_to_skstring(fLogFontArray[index].elfLogFont.lfFaceName, familyName);
reed@google.com484f5bc2013-04-24 19:14:56 +00002419 }
2420
mtklein36352bf2015-03-25 18:17:31 -07002421 SkFontStyleSet* onCreateStyleSet(int index) const override {
reed@google.com484f5bc2013-04-24 19:14:56 +00002422 SkASSERT((unsigned)index < (unsigned)fLogFontArray.count());
halcanary385fe4d2015-08-26 13:07:48 -07002423 return new SkFontStyleSetGDI(fLogFontArray[index].elfLogFont.lfFaceName);
reed@google.com484f5bc2013-04-24 19:14:56 +00002424 }
2425
mtklein36352bf2015-03-25 18:17:31 -07002426 SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
halcanary96fcdcc2015-08-27 07:41:13 -07002427 if (nullptr == familyName) {
reed@google.com484f5bc2013-04-24 19:14:56 +00002428 familyName = ""; // do we need this check???
2429 }
2430 LOGFONT lf;
2431 logfont_for_name(familyName, &lf);
halcanary385fe4d2015-08-26 13:07:48 -07002432 return new SkFontStyleSetGDI(lf.lfFaceName);
reed@google.com484f5bc2013-04-24 19:14:56 +00002433 }
2434
reed@google.com484f5bc2013-04-24 19:14:56 +00002435 virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
mtklein36352bf2015-03-25 18:17:31 -07002436 const SkFontStyle& fontstyle) const override {
reed@google.com437eea12013-04-25 20:40:02 +00002437 // could be in base impl
reed@google.com484f5bc2013-04-24 19:14:56 +00002438 SkAutoTUnref<SkFontStyleSet> sset(this->matchFamily(familyName));
2439 return sset->matchStyle(fontstyle);
2440 }
2441
djsollen33068c12014-11-14 10:52:53 -08002442 virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
2443 const char* bcp47[], int bcp47Count,
mtklein36352bf2015-03-25 18:17:31 -07002444 SkUnichar character) const override {
halcanary96fcdcc2015-08-27 07:41:13 -07002445 return nullptr;
djsollen33068c12014-11-14 10:52:53 -08002446 }
2447
reed@google.com484f5bc2013-04-24 19:14:56 +00002448 virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
mtklein36352bf2015-03-25 18:17:31 -07002449 const SkFontStyle& fontstyle) const override {
reed@google.com437eea12013-04-25 20:40:02 +00002450 // could be in base impl
reed@google.com484f5bc2013-04-24 19:14:56 +00002451 SkString familyName;
2452 ((LogFontTypeface*)familyMember)->getFamilyName(&familyName);
2453 return this->matchFamilyStyle(familyName.c_str(), fontstyle);
2454 }
2455
mtklein36352bf2015-03-25 18:17:31 -07002456 SkTypeface* onCreateFromStream(SkStreamAsset* bareStream, int ttcIndex) const override {
bungeman5f213d92015-01-27 05:39:10 -08002457 SkAutoTDelete<SkStreamAsset> stream(bareStream);
reed@google.com437eea12013-04-25 20:40:02 +00002458 return create_from_stream(stream);
reed@google.com484f5bc2013-04-24 19:14:56 +00002459 }
reed@google.com437eea12013-04-25 20:40:02 +00002460
mtklein36352bf2015-03-25 18:17:31 -07002461 SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const override {
reed@google.com437eea12013-04-25 20:40:02 +00002462 // could be in base impl
halcanary385fe4d2015-08-26 13:07:48 -07002463 return this->createFromStream(new SkMemoryStream(data));
reed@google.com484f5bc2013-04-24 19:14:56 +00002464 }
reed@google.com437eea12013-04-25 20:40:02 +00002465
mtklein36352bf2015-03-25 18:17:31 -07002466 SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const override {
reed@google.com437eea12013-04-25 20:40:02 +00002467 // could be in base impl
scroggoa1193e42015-01-21 12:09:53 -08002468 return this->createFromStream(SkStream::NewFromFile(path));
reed@google.com484f5bc2013-04-24 19:14:56 +00002469 }
2470
bungeman11a77c62016-04-12 13:45:06 -07002471 SkTypeface* onLegacyCreateTypeface(const char familyName[], SkFontStyle style) const override {
bungeman@google.comf7159bb2013-11-20 15:11:05 +00002472 LOGFONT lf;
halcanary96fcdcc2015-08-27 07:41:13 -07002473 if (nullptr == familyName) {
bungeman@google.comf7159bb2013-11-20 15:11:05 +00002474 lf = get_default_font();
2475 } else {
2476 logfont_for_name(familyName, &lf);
2477 }
bungemana4c4a2d2014-10-20 13:33:19 -07002478
bungeman11a77c62016-04-12 13:45:06 -07002479 lf.lfWeight = style.weight();
bungemanb4bb7d82016-04-27 10:21:04 -07002480 lf.lfItalic = style.slant() == SkFontStyle::kUpright_Slant ? FALSE : TRUE;
bungeman@google.comf7159bb2013-11-20 15:11:05 +00002481 return SkCreateTypefaceFromLOGFONT(lf);
reed@google.com30ddd612013-07-30 17:47:39 +00002482 }
2483
reed@google.com484f5bc2013-04-24 19:14:56 +00002484private:
reed@google.coma65a6812013-05-02 19:47:24 +00002485 SkTDArray<ENUMLOGFONTEX> fLogFontArray;
reed@google.com484f5bc2013-04-24 19:14:56 +00002486};
reed@google.com070da5e2013-03-27 20:01:49 +00002487
reed@google.com30ddd612013-07-30 17:47:39 +00002488///////////////////////////////////////////////////////////////////////////////
2489
halcanary385fe4d2015-08-26 13:07:48 -07002490SkFontMgr* SkFontMgr_New_GDI() { return new SkFontMgrGDI; }
mtklein1ee76512015-11-02 10:20:27 -08002491
2492#endif//defined(SK_BUILD_FOR_WIN32)