blob: 5627f0130aa859342cbc4e25f190a1fb57bcc8ea [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@google.comac6b9792011-03-11 15:42:51 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@google.comac6b9792011-03-11 15:42:51 +00007 */
8
reed@google.comac6b9792011-03-11 15:42:51 +00009#include "SkAdvancedTypefaceMetrics.h"
bungeman@google.coma5501992012-05-18 19:06:41 +000010#include "SkBase64.h"
11#include "SkData.h"
12#include "SkDescriptor.h"
bungeman@google.come70f7982012-06-01 19:38:19 +000013#include "SkFontDescriptor.h"
bungeman@google.coma5501992012-05-18 19:06:41 +000014#include "SkFontHost.h"
15#include "SkOTUtils.h"
reed@google.comac6b9792011-03-11 15:42:51 +000016#include "SkStream.h"
bungeman@google.coma5501992012-05-18 19:06:41 +000017#include "SkString.h"
reed@google.comac6b9792011-03-11 15:42:51 +000018#include "SkThread.h"
19#include "SkTypeface_win.h"
reed@google.com59d2f632011-05-02 19:36:59 +000020#include "SkTypefaceCache.h"
reed@google.comac6b9792011-03-11 15:42:51 +000021#include "SkUtils.h"
22
bungeman@google.coma5501992012-05-18 19:06:41 +000023#include "SkTypes.h"
24#include <tchar.h>
25#include <usp10.h>
26#include <objbase.h>
reed@google.comac6b9792011-03-11 15:42:51 +000027
reed@google.com81275392012-06-18 14:08:56 +000028static bool compute_bounds_outset(const LOGFONT& lf, SkIRect* outset) {
reed@google.com5a121ad2012-06-15 14:27:15 +000029
reed@google.comb8a5c612012-06-13 20:01:44 +000030 static const struct {
reed@google.com5a121ad2012-06-15 14:27:15 +000031 const char* fUCName; // UTF8 encoded, ascii is upper-case
reed@google.com81275392012-06-18 14:08:56 +000032 SkIRect fOutset; // these are deltas for the glyph's bounds
reed@google.comb8a5c612012-06-13 20:01:44 +000033 } gData[] = {
reed@google.com5a121ad2012-06-15 14:27:15 +000034 // http://code.google.com/p/chromium/issues/detail?id=130842
reed@google.com81275392012-06-18 14:08:56 +000035 { "DOTUM", { 0, 0, 0, 1 } },
36 { "DOTUMCHE", { 0, 0, 0, 1 } },
37 { "\xEB\x8F\x8B\xEC\x9B\x80", { 0, 0, 0, 1 } },
38 { "\xEB\x8F\x8B\xEC\x9B\x80\xEC\xB2\xB4", { 0, 0, 0, 1 } },
39 { "MS UI GOTHIC", { 1, 0, 0, 0 } },
reed@google.comb8a5c612012-06-13 20:01:44 +000040 };
41
reed@google.com5a121ad2012-06-15 14:27:15 +000042 /**
43 * We convert the target name into upper-case (for ascii chars) UTF8.
44 * Our database is already stored in this fashion, and it allows us to
45 * search it with straight memcmp, since everyone is in this canonical
46 * form.
47 */
reed@google.comb8a5c612012-06-13 20:01:44 +000048
reed@google.com5a121ad2012-06-15 14:27:15 +000049 // temp storage is max # TCHARs * max expantion for UTF8 + null
50 char name[kMaxBytesInUTF8Sequence * LF_FACESIZE + 1];
51 int index = 0;
52 for (int i = 0; i < LF_FACESIZE; ++i) {
53 uint16_t c = lf.lfFaceName[i];
reed@google.comb8a5c612012-06-13 20:01:44 +000054 if (c >= 'a' && c <= 'z') {
55 c = c - 'a' + 'A';
56 }
reed@google.com5a121ad2012-06-15 14:27:15 +000057 size_t n = SkUTF16_ToUTF8(&c, 1, &name[index]);
58 index += n;
reed@google.comb8a5c612012-06-13 20:01:44 +000059 if (0 == c) {
60 break;
61 }
62 }
63
64 for (size_t j = 0; j < SK_ARRAY_COUNT(gData); ++j) {
65 if (!strcmp(gData[j].fUCName, name)) {
reed@google.com81275392012-06-18 14:08:56 +000066 *outset = gData[j].fOutset;
67 return true;
reed@google.comb8a5c612012-06-13 20:01:44 +000068 }
69 }
reed@google.com81275392012-06-18 14:08:56 +000070 return false;
71}
72
73// outset isn't really a rect, but 4 (non-negative) values to outset the
74// glyph's metrics by. For "normal" fonts, all these values should be 0.
75static void apply_outset(SkGlyph* glyph, const SkIRect& outset) {
76 SkASSERT(outset.fLeft >= 0);
77 SkASSERT(outset.fTop >= 0);
78 SkASSERT(outset.fRight >= 0);
79 SkASSERT(outset.fBottom >= 0);
80
81 glyph->fLeft -= outset.fLeft;
82 glyph->fTop -= outset.fTop;
83 glyph->fWidth += outset.fLeft + outset.fRight;
84 glyph->fHeight += outset.fTop + outset.fBottom;
reed@google.comb8a5c612012-06-13 20:01:44 +000085}
86
reed@google.com6f5df482011-09-28 20:33:24 +000087// always packed xxRRGGBB
88typedef uint32_t SkGdiRGB;
89
90template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) {
91 return (T*)((char*)ptr + byteOffset);
92}
93
reed@google.coma767fa02011-08-05 21:40:26 +000094// define this in your Makefile or .gyp to enforce AA requests
95// which GDI ignores at small sizes. This flag guarantees AA
96// for rotated text, regardless of GDI's notions.
97//#define SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
98
reed@google.comac6b9792011-03-11 15:42:51 +000099// client3d has to undefine this for now
100#define CAN_USE_LOGFONT_NAME
101
reed@google.com82a34d82011-07-26 19:33:08 +0000102static bool isLCD(const SkScalerContext::Rec& rec) {
103 return SkMask::kLCD16_Format == rec.fMaskFormat ||
104 SkMask::kLCD32_Format == rec.fMaskFormat;
105}
106
reed@google.coma767fa02011-08-05 21:40:26 +0000107static bool bothZero(SkScalar a, SkScalar b) {
108 return 0 == a && 0 == b;
109}
110
111// returns false if there is any non-90-rotation or skew
112static bool isAxisAligned(const SkScalerContext::Rec& rec) {
113 return 0 == rec.fPreSkewX &&
114 (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
115 bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
116}
117
118static bool needToRenderWithSkia(const SkScalerContext::Rec& rec) {
119#ifdef SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
120 // What we really want to catch is when GDI will ignore the AA request and give
121 // us BW instead. Smallish rotated text is one heuristic, so this code is just
122 // an approximation. We shouldn't need to do this for larger sizes, but at those
123 // sizes, the quality difference gets less and less between our general
124 // scanconverter and GDI's.
125 if (SkMask::kA8_Format == rec.fMaskFormat && !isAxisAligned(rec)) {
126 return true;
127 }
128#endif
129 // false means allow GDI to generate the bits
130 return false;
131}
132
reed@google.comac6b9792011-03-11 15:42:51 +0000133using namespace skia_advanced_typeface_metrics_utils;
134
reed@google.comac6b9792011-03-11 15:42:51 +0000135static const uint16_t BUFFERSIZE = (16384 - 32);
136static uint8_t glyphbuf[BUFFERSIZE];
137
reed@google.comac6b9792011-03-11 15:42:51 +0000138/**
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000139 * Since LOGFONT wants its textsize as an int, and we support fractional sizes,
reed@google.comac6b9792011-03-11 15:42:51 +0000140 * and since we have a cache of LOGFONTs for our tyepfaces, we always set the
141 * lfHeight to a canonical size, and then we use the 2x2 matrix to achieve the
142 * actual requested size.
143 */
144static const int gCanonicalTextSize = 64;
145
146static void make_canonical(LOGFONT* lf) {
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000147 lf->lfHeight = -gCanonicalTextSize;
reed@google.com59d2f632011-05-02 19:36:59 +0000148 lf->lfQuality = CLEARTYPE_QUALITY;//PROOF_QUALITY;
149 lf->lfCharSet = DEFAULT_CHARSET;
reed@google.com82a34d82011-07-26 19:33:08 +0000150// lf->lfClipPrecision = 64;
reed@google.com59d2f632011-05-02 19:36:59 +0000151}
152
bungeman@google.com90d812b2011-10-24 21:25:01 +0000153static SkTypeface::Style get_style(const LOGFONT& lf) {
reed@google.com59d2f632011-05-02 19:36:59 +0000154 unsigned style = 0;
155 if (lf.lfWeight >= FW_BOLD) {
156 style |= SkTypeface::kBold;
157 }
158 if (lf.lfItalic) {
159 style |= SkTypeface::kItalic;
160 }
bungeman@google.com90d812b2011-10-24 21:25:01 +0000161 return static_cast<SkTypeface::Style>(style);
reed@google.com59d2f632011-05-02 19:36:59 +0000162}
163
164static void setStyle(LOGFONT* lf, SkTypeface::Style style) {
165 lf->lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL ;
166 lf->lfItalic = ((style & SkTypeface::kItalic) != 0);
reed@google.comac6b9792011-03-11 15:42:51 +0000167}
168
169static inline FIXED SkFixedToFIXED(SkFixed x) {
170 return *(FIXED*)(&x);
171}
bungeman@google.coma0319f62012-04-18 15:40:50 +0000172static inline SkFixed SkFIXEDToFixed(FIXED x) {
173 return *(SkFixed*)(&x);
174}
reed@google.comac6b9792011-03-11 15:42:51 +0000175
176static inline FIXED SkScalarToFIXED(SkScalar x) {
177 return SkFixedToFIXED(SkScalarToFixed(x));
178}
179
bungeman@google.coma0319f62012-04-18 15:40:50 +0000180static unsigned calculateOutlineGlyphCount(HDC hdc) {
reed@google.comac6b9792011-03-11 15:42:51 +0000181 // The 'maxp' table stores the number of glyphs at offset 4, in 2 bytes.
ctguil@chromium.orgf4c26222011-05-16 22:00:05 +0000182 const DWORD maxpTag =
183 SkEndian_SwapBE32(SkSetFourByteTag('m', 'a', 'x', 'p'));
reed@google.comac6b9792011-03-11 15:42:51 +0000184 uint16_t glyphs;
185 if (GetFontData(hdc, maxpTag, 4, &glyphs, sizeof(glyphs)) != GDI_ERROR) {
186 return SkEndian_SwapBE16(glyphs);
187 }
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000188
reed@google.comac6b9792011-03-11 15:42:51 +0000189 // Binary search for glyph count.
190 static const MAT2 mat2 = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
191 int32_t max = SK_MaxU16 + 1;
192 int32_t min = 0;
193 GLYPHMETRICS gm;
194 while (min < max) {
195 int32_t mid = min + ((max - min) / 2);
196 if (GetGlyphOutlineW(hdc, mid, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0,
197 NULL, &mat2) == GDI_ERROR) {
198 max = mid;
199 } else {
200 min = mid + 1;
201 }
202 }
203 SkASSERT(min == max);
204 return min;
205}
206
reed@google.comac6b9792011-03-11 15:42:51 +0000207class LogFontTypeface : public SkTypeface {
reed@google.comac6b9792011-03-11 15:42:51 +0000208public:
reed@google.com59d2f632011-05-02 19:36:59 +0000209 LogFontTypeface(SkTypeface::Style style, SkFontID fontID, const LOGFONT& lf) :
bungeman@google.come70f7982012-06-01 19:38:19 +0000210 SkTypeface(style, fontID, false), fLogFont(lf), fSerializeAsStream(false) {}
reed@google.comac6b9792011-03-11 15:42:51 +0000211
reed@google.com59d2f632011-05-02 19:36:59 +0000212 LOGFONT fLogFont;
bungeman@google.come70f7982012-06-01 19:38:19 +0000213 bool fSerializeAsStream;
reed@google.comac6b9792011-03-11 15:42:51 +0000214
reed@google.com59d2f632011-05-02 19:36:59 +0000215 static LogFontTypeface* Create(const LOGFONT& lf) {
bungeman@google.com90d812b2011-10-24 21:25:01 +0000216 SkTypeface::Style style = get_style(lf);
reed@google.com59d2f632011-05-02 19:36:59 +0000217 SkFontID fontID = SkTypefaceCache::NewFontID();
218 return new LogFontTypeface(style, fontID, lf);
reed@google.comac6b9792011-03-11 15:42:51 +0000219 }
220};
221
bungeman@google.coma5501992012-05-18 19:06:41 +0000222class FontMemResourceTypeface : public LogFontTypeface {
223public:
224 /**
225 * Takes ownership of fontMemResource.
226 */
227 FontMemResourceTypeface(SkTypeface::Style style, SkFontID fontID, const LOGFONT& lf, HANDLE fontMemResource) :
bungeman@google.come70f7982012-06-01 19:38:19 +0000228 LogFontTypeface(style, fontID, lf), fFontMemResource(fontMemResource) {
229 fSerializeAsStream = true;
230 }
bungeman@google.coma5501992012-05-18 19:06:41 +0000231
232 HANDLE fFontMemResource;
233
234 /**
235 * The created FontMemResourceTypeface takes ownership of fontMemResource.
236 */
237 static FontMemResourceTypeface* Create(const LOGFONT& lf, HANDLE fontMemResource) {
238 SkTypeface::Style style = get_style(lf);
239 SkFontID fontID = SkTypefaceCache::NewFontID();
240 return new FontMemResourceTypeface(style, fontID, lf, fontMemResource);
241 }
242
243protected:
244 virtual void weak_dispose() const SK_OVERRIDE {
245 RemoveFontMemResourceEx(fFontMemResource);
246 //SkTypefaceCache::Remove(this);
247 INHERITED::weak_dispose();
248 }
249
250private:
251 typedef LogFontTypeface INHERITED;
252};
253
reed@google.comac6b9792011-03-11 15:42:51 +0000254static const LOGFONT& get_default_font() {
255 static LOGFONT gDefaultFont;
reed@google.comac6b9792011-03-11 15:42:51 +0000256 return gDefaultFont;
257}
258
reed@google.com59d2f632011-05-02 19:36:59 +0000259static bool FindByLogFont(SkTypeface* face, SkTypeface::Style requestedStyle, void* ctx) {
bungeman@google.coma5501992012-05-18 19:06:41 +0000260 LogFontTypeface* lface = static_cast<LogFontTypeface*>(face);
reed@google.com59d2f632011-05-02 19:36:59 +0000261 const LOGFONT* lf = reinterpret_cast<const LOGFONT*>(ctx);
reed@google.comac6b9792011-03-11 15:42:51 +0000262
bungeman@google.coma5501992012-05-18 19:06:41 +0000263 return lface &&
264 get_style(lface->fLogFont) == requestedStyle &&
reed@google.com59d2f632011-05-02 19:36:59 +0000265 !memcmp(&lface->fLogFont, lf, sizeof(LOGFONT));
266}
267
268/**
269 * This guy is public. It first searches the cache, and if a match is not found,
270 * it creates a new face.
271 */
272SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& origLF) {
273 LOGFONT lf = origLF;
274 make_canonical(&lf);
bungeman@google.comee51d1a2012-02-16 12:40:48 +0000275 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByLogFont, &lf);
276 if (NULL == face) {
reed@google.com59d2f632011-05-02 19:36:59 +0000277 face = LogFontTypeface::Create(lf);
bungeman@google.com90d812b2011-10-24 21:25:01 +0000278 SkTypefaceCache::Add(face, get_style(lf));
reed@google.com59d2f632011-05-02 19:36:59 +0000279 }
280 return face;
reed@google.comac6b9792011-03-11 15:42:51 +0000281}
282
reed@google.comdb77a6a2011-07-19 19:08:33 +0000283/**
bungeman@google.coma5501992012-05-18 19:06:41 +0000284 * The created SkTypeface takes ownership of fontMemResource.
285 */
286SkTypeface* SkCreateFontMemResourceTypefaceFromLOGFONT(const LOGFONT& origLF, HANDLE fontMemResource) {
287 LOGFONT lf = origLF;
288 make_canonical(&lf);
289 FontMemResourceTypeface* face = FontMemResourceTypeface::Create(lf, fontMemResource);
290 SkTypefaceCache::Add(face, get_style(lf), false);
291 return face;
292}
293
294/**
reed@google.comdb77a6a2011-07-19 19:08:33 +0000295 * This guy is public
296 */
297void SkLOGFONTFromTypeface(const SkTypeface* face, LOGFONT* lf) {
298 if (NULL == face) {
299 *lf = get_default_font();
300 } else {
bungeman@google.coma5501992012-05-18 19:06:41 +0000301 *lf = static_cast<const LogFontTypeface*>(face)->fLogFont;
reed@google.comdb77a6a2011-07-19 19:08:33 +0000302 }
303}
304
reed@google.com7d26c592011-06-13 13:01:10 +0000305SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
reed@google.comac6b9792011-03-11 15:42:51 +0000306 // Zero means that we don't have any fallback fonts for this fontID.
307 // This function is implemented on Android, but doesn't have much
308 // meaning here.
309 return 0;
310}
311
bungeman@google.com39698b12011-11-15 22:26:41 +0000312static void ensure_typeface_accessible(SkFontID fontID) {
bungeman@google.coma5501992012-05-18 19:06:41 +0000313 LogFontTypeface* face = static_cast<LogFontTypeface*>(SkTypefaceCache::FindByID(fontID));
bungeman@google.comee51d1a2012-02-16 12:40:48 +0000314 if (face) {
315 SkFontHost::EnsureTypefaceAccessible(*face);
316 }
bungeman@google.com39698b12011-11-15 22:26:41 +0000317}
318
reed@google.com59d2f632011-05-02 19:36:59 +0000319static void GetLogFontByID(SkFontID fontID, LOGFONT* lf) {
bungeman@google.coma5501992012-05-18 19:06:41 +0000320 LogFontTypeface* face = static_cast<LogFontTypeface*>(SkTypefaceCache::FindByID(fontID));
reed@google.com59d2f632011-05-02 19:36:59 +0000321 if (face) {
322 *lf = face->fLogFont;
323 } else {
324 sk_bzero(lf, sizeof(LOGFONT));
325 }
326}
327
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000328// Construct Glyph to Unicode table.
329// Unicode code points that require conjugate pairs in utf16 are not
330// supported.
331// TODO(arthurhsu): Add support for conjugate pairs. It looks like that may
332// require parsing the TTF cmap table (platform 4, encoding 12) directly instead
333// of calling GetFontUnicodeRange().
334static void populate_glyph_to_unicode(HDC fontHdc, const unsigned glyphCount,
335 SkTDArray<SkUnichar>* glyphToUnicode) {
336 DWORD glyphSetBufferSize = GetFontUnicodeRanges(fontHdc, NULL);
337 if (!glyphSetBufferSize) {
338 return;
339 }
340
341 SkAutoTDeleteArray<BYTE> glyphSetBuffer(new BYTE[glyphSetBufferSize]);
342 GLYPHSET* glyphSet =
343 reinterpret_cast<LPGLYPHSET>(glyphSetBuffer.get());
344 if (GetFontUnicodeRanges(fontHdc, glyphSet) != glyphSetBufferSize) {
345 return;
346 }
347
348 glyphToUnicode->setCount(glyphCount);
349 memset(glyphToUnicode->begin(), 0, glyphCount * sizeof(SkUnichar));
350 for (DWORD i = 0; i < glyphSet->cRanges; ++i) {
351 // There is no guarantee that within a Unicode range, the corresponding
352 // glyph id in a font file are continuous. So, even if we have ranges,
353 // we can't just use the first and last entry of the range to compute
354 // result. We need to enumerate them one by one.
355 int count = glyphSet->ranges[i].cGlyphs;
356 SkAutoTArray<WCHAR> chars(count + 1);
357 chars[count] = 0; // termintate string
358 SkAutoTArray<WORD> glyph(count);
359 for (USHORT j = 0; j < count; ++j) {
360 chars[j] = glyphSet->ranges[i].wcLow + j;
361 }
362 GetGlyphIndicesW(fontHdc, chars.get(), count, glyph.get(),
363 GGI_MARK_NONEXISTING_GLYPHS);
364 // If the glyph ID is valid, and the glyph is not mapped, then we will
365 // fill in the char id into the vector. If the glyph is mapped already,
366 // skip it.
367 // TODO(arthurhsu): better improve this. e.g. Get all used char ids from
368 // font cache, then generate this mapping table from there. It's
369 // unlikely to have collisions since glyph reuse happens mostly for
370 // different Unicode pages.
371 for (USHORT j = 0; j < count; ++j) {
372 if (glyph[j] != 0xffff && glyph[j] < glyphCount &&
373 (*glyphToUnicode)[glyph[j]] == 0) {
374 (*glyphToUnicode)[glyph[j]] = chars[j];
375 }
376 }
377 }
378}
379
reed@google.com99edd432011-09-09 14:59:59 +0000380//////////////////////////////////////////////////////////////////////////////////////
381
382static int alignTo32(int n) {
383 return (n + 31) & ~31;
384}
385
386struct MyBitmapInfo : public BITMAPINFO {
387 RGBQUAD fMoreSpaceForColors[1];
388};
389
390class HDCOffscreen {
391public:
392 HDCOffscreen() {
393 fFont = 0;
394 fDC = 0;
395 fBM = 0;
396 fBits = NULL;
397 fWidth = fHeight = 0;
398 fIsBW = false;
reed@google.com754e4eb2011-09-26 13:21:39 +0000399 fColor = kInvalid_Color;
reed@google.com99edd432011-09-09 14:59:59 +0000400 }
401
402 ~HDCOffscreen() {
403 if (fDC) {
404 DeleteDC(fDC);
405 }
406 if (fBM) {
407 DeleteObject(fBM);
408 }
409 }
410
411 void init(HFONT font, const XFORM& xform) {
412 fFont = font;
413 fXform = xform;
414 }
415
reed@google.com6f5df482011-09-28 20:33:24 +0000416 const void* draw(const SkGlyph&, bool isBW, SkGdiRGB fgColor,
reed@google.com754e4eb2011-09-26 13:21:39 +0000417 size_t* srcRBPtr);
reed@google.com99edd432011-09-09 14:59:59 +0000418
419private:
420 HDC fDC;
421 HBITMAP fBM;
422 HFONT fFont;
423 XFORM fXform;
424 void* fBits; // points into fBM
reed@google.com754e4eb2011-09-26 13:21:39 +0000425 COLORREF fColor;
reed@google.com99edd432011-09-09 14:59:59 +0000426 int fWidth;
427 int fHeight;
428 bool fIsBW;
reed@google.com754e4eb2011-09-26 13:21:39 +0000429
430 enum {
431 // will always trigger us to reset the color, since we
reed@google.com6f5df482011-09-28 20:33:24 +0000432 // should only store 0 or 0x00FFFFFF or gray (0x007F7F7F)
reed@google.com754e4eb2011-09-26 13:21:39 +0000433 kInvalid_Color = 12345
434 };
reed@google.com99edd432011-09-09 14:59:59 +0000435};
436
reed@google.com754e4eb2011-09-26 13:21:39 +0000437const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW,
reed@google.com6f5df482011-09-28 20:33:24 +0000438 SkGdiRGB fgColor, size_t* srcRBPtr) {
reed@google.com99edd432011-09-09 14:59:59 +0000439 if (0 == fDC) {
440 fDC = CreateCompatibleDC(0);
441 if (0 == fDC) {
442 return NULL;
443 }
444 SetGraphicsMode(fDC, GM_ADVANCED);
445 SetBkMode(fDC, TRANSPARENT);
446 SetTextAlign(fDC, TA_LEFT | TA_BASELINE);
447 SelectObject(fDC, fFont);
reed@google.com754e4eb2011-09-26 13:21:39 +0000448 fColor = kInvalid_Color;
reed@google.com99edd432011-09-09 14:59:59 +0000449 }
450
451 if (fBM && (fIsBW != isBW || fWidth < glyph.fWidth || fHeight < glyph.fHeight)) {
452 DeleteObject(fBM);
453 fBM = 0;
454 }
reed@google.com754e4eb2011-09-26 13:21:39 +0000455 fIsBW = isBW;
reed@google.com99edd432011-09-09 14:59:59 +0000456
reed@google.com6f5df482011-09-28 20:33:24 +0000457 COLORREF color = fgColor;
458 if (fIsBW) {
reed@google.com754e4eb2011-09-26 13:21:39 +0000459 color = 0xFFFFFF;
reed@google.com99edd432011-09-09 14:59:59 +0000460 }
reed@google.com754e4eb2011-09-26 13:21:39 +0000461 if (fColor != color) {
462 fColor = color;
463 COLORREF prev = SetTextColor(fDC, color);
464 SkASSERT(prev != CLR_INVALID);
465 }
466
reed@google.com99edd432011-09-09 14:59:59 +0000467 fWidth = SkMax32(fWidth, glyph.fWidth);
468 fHeight = SkMax32(fHeight, glyph.fHeight);
469
470 int biWidth = isBW ? alignTo32(fWidth) : fWidth;
471
472 if (0 == fBM) {
473 MyBitmapInfo info;
474 sk_bzero(&info, sizeof(info));
475 if (isBW) {
476 RGBQUAD blackQuad = { 0, 0, 0, 0 };
477 RGBQUAD whiteQuad = { 0xFF, 0xFF, 0xFF, 0 };
478 info.bmiColors[0] = blackQuad;
479 info.bmiColors[1] = whiteQuad;
480 }
481 info.bmiHeader.biSize = sizeof(info.bmiHeader);
482 info.bmiHeader.biWidth = biWidth;
483 info.bmiHeader.biHeight = fHeight;
484 info.bmiHeader.biPlanes = 1;
485 info.bmiHeader.biBitCount = isBW ? 1 : 32;
486 info.bmiHeader.biCompression = BI_RGB;
487 if (isBW) {
488 info.bmiHeader.biClrUsed = 2;
489 }
490 fBM = CreateDIBSection(fDC, &info, DIB_RGB_COLORS, &fBits, 0, 0);
491 if (0 == fBM) {
492 return NULL;
493 }
494 SelectObject(fDC, fBM);
495 }
496
497 // erase
498 size_t srcRB = isBW ? (biWidth >> 3) : (fWidth << 2);
499 size_t size = fHeight * srcRB;
reed@google.com6f5df482011-09-28 20:33:24 +0000500 unsigned bg = (0 == color) ? 0xFF : 0;
501 memset(fBits, bg, size);
reed@google.com99edd432011-09-09 14:59:59 +0000502
503 XFORM xform = fXform;
504 xform.eDx = (float)-glyph.fLeft;
505 xform.eDy = (float)-glyph.fTop;
506 SetWorldTransform(fDC, &xform);
507
508 uint16_t glyphID = glyph.getGlyphID();
bungeman@google.com39698b12011-11-15 22:26:41 +0000509 BOOL ret = ExtTextOutW(fDC, 0, 0, ETO_GLYPH_INDEX, NULL, reinterpret_cast<LPCWSTR>(&glyphID), 1, NULL);
reed@google.com99edd432011-09-09 14:59:59 +0000510 GdiFlush();
bungeman@google.com39698b12011-11-15 22:26:41 +0000511 if (0 == ret) {
512 return NULL;
513 }
reed@google.com99edd432011-09-09 14:59:59 +0000514 *srcRBPtr = srcRB;
515 // offset to the start of the image
516 return (const char*)fBits + (fHeight - glyph.fHeight) * srcRB;
517}
518
reed@google.comb8a5c612012-06-13 20:01:44 +0000519//////////////////////////////////////////////////////////////////////////////
reed@google.com59d2f632011-05-02 19:36:59 +0000520
reed@google.comac6b9792011-03-11 15:42:51 +0000521class SkScalerContext_Windows : public SkScalerContext {
522public:
523 SkScalerContext_Windows(const SkDescriptor* desc);
524 virtual ~SkScalerContext_Windows();
525
526protected:
527 virtual unsigned generateGlyphCount();
528 virtual uint16_t generateCharToGlyph(SkUnichar uni);
529 virtual void generateAdvance(SkGlyph* glyph);
530 virtual void generateMetrics(SkGlyph* glyph);
531 virtual void generateImage(const SkGlyph& glyph);
532 virtual void generatePath(const SkGlyph& glyph, SkPath* path);
reed@google.comb8a5c612012-06-13 20:01:44 +0000533 virtual void generateFontMetrics(SkPaint::FontMetrics* mX,
534 SkPaint::FontMetrics* mY);
reed@google.com99edd432011-09-09 14:59:59 +0000535
reed@google.comac6b9792011-03-11 15:42:51 +0000536private:
reed@google.com99edd432011-09-09 14:59:59 +0000537 HDCOffscreen fOffscreen;
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000538 SkScalar fScale; // to get from canonical size to real size
reed@google.comac6b9792011-03-11 15:42:51 +0000539 MAT2 fMat22;
540 XFORM fXform;
541 HDC fDDC;
542 HFONT fSavefont;
543 HFONT fFont;
544 SCRIPT_CACHE fSC;
545 int fGlyphCount;
reed@google.com1dd17a12011-05-17 14:04:41 +0000546
reed@google.comb8a5c612012-06-13 20:01:44 +0000547 /**
548 * Some fonts need extra pixels added to avoid clipping, as the bounds
549 * returned by getOutlineMetrics does not match what GDI draws. Since
550 * this costs more RAM and therefore slower blits, we have a table to
551 * only do this for known "bad" fonts.
552 */
reed@google.com81275392012-06-18 14:08:56 +0000553 SkIRect fOutset;
reed@google.comb8a5c612012-06-13 20:01:44 +0000554
reed@google.com1dd17a12011-05-17 14:04:41 +0000555 HFONT fHiResFont;
556 MAT2 fMat22Identity;
557 SkMatrix fHiResMatrix;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000558 enum Type {
559 kTrueType_Type, kBitmap_Type,
560 } fType;
561 TEXTMETRIC fTM;
reed@google.comac6b9792011-03-11 15:42:51 +0000562};
563
564static float mul2float(SkScalar a, SkScalar b) {
565 return SkScalarToFloat(SkScalarMul(a, b));
566}
567
568static FIXED float2FIXED(float x) {
569 return SkFixedToFIXED(SkFloatToFixed(x));
570}
571
digit@google.com1771cbf2012-01-26 21:26:40 +0000572SK_DECLARE_STATIC_MUTEX(gFTMutex);
reed@google.com59d2f632011-05-02 19:36:59 +0000573
reed@google.com1dd17a12011-05-17 14:04:41 +0000574#define HIRES_TEXTSIZE 2048
575#define HIRES_SHIFT 11
576static inline SkFixed HiResToFixed(int value) {
577 return value << (16 - HIRES_SHIFT);
578}
579
580static bool needHiResMetrics(const SkScalar mat[2][2]) {
581 return mat[1][0] || mat[0][1];
582}
583
reed@google.com82a34d82011-07-26 19:33:08 +0000584static BYTE compute_quality(const SkScalerContext::Rec& rec) {
585 switch (rec.fMaskFormat) {
586 case SkMask::kBW_Format:
587 return NONANTIALIASED_QUALITY;
588 case SkMask::kLCD16_Format:
589 case SkMask::kLCD32_Format:
590 return CLEARTYPE_QUALITY;
591 default:
reed@google.com8351aab2012-01-18 17:06:35 +0000592 if (rec.fFlags & SkScalerContext::kGenA8FromLCD_Flag) {
593 return CLEARTYPE_QUALITY;
594 } else {
595 return ANTIALIASED_QUALITY;
596 }
reed@google.com82a34d82011-07-26 19:33:08 +0000597 }
598}
599
reed@google.comac6b9792011-03-11 15:42:51 +0000600SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc)
601 : SkScalerContext(desc), fDDC(0), fFont(0), fSavefont(0), fSC(0)
602 , fGlyphCount(-1) {
603 SkAutoMutexAcquire ac(gFTMutex);
604
reed@google.comac6b9792011-03-11 15:42:51 +0000605 fDDC = ::CreateCompatibleDC(NULL);
reed@google.com1dd17a12011-05-17 14:04:41 +0000606 SetGraphicsMode(fDDC, GM_ADVANCED);
reed@google.comac6b9792011-03-11 15:42:51 +0000607 SetBkMode(fDDC, TRANSPARENT);
608
609 // Scaling by the DPI is inconsistent with how Skia draws elsewhere
610 //SkScalar height = -(fRec.fTextSize * GetDeviceCaps(ddc, LOGPIXELSY) / 72);
reed@google.com59d2f632011-05-02 19:36:59 +0000611 LOGFONT lf;
612 GetLogFontByID(fRec.fFontID, &lf);
reed@google.comac6b9792011-03-11 15:42:51 +0000613 lf.lfHeight = -gCanonicalTextSize;
reed@google.com82a34d82011-07-26 19:33:08 +0000614 lf.lfQuality = compute_quality(fRec);
reed@google.comac6b9792011-03-11 15:42:51 +0000615 fFont = CreateFontIndirect(&lf);
reed@google.com1dd17a12011-05-17 14:04:41 +0000616
reed@google.com81275392012-06-18 14:08:56 +0000617 if (!compute_bounds_outset(lf, &fOutset)) {
618 fOutset.setEmpty();
619 }
reed@google.comb8a5c612012-06-13 20:01:44 +0000620
reed@google.com1dd17a12011-05-17 14:04:41 +0000621 // if we're rotated, or want fractional widths, create a hires font
622 fHiResFont = 0;
bungeman@google.comc84547a2012-01-05 20:18:06 +0000623 if (needHiResMetrics(fRec.fPost2x2)) {
reed@google.com1dd17a12011-05-17 14:04:41 +0000624 lf.lfHeight = -HIRES_TEXTSIZE;
625 fHiResFont = CreateFontIndirect(&lf);
626
627 fMat22Identity.eM11 = fMat22Identity.eM22 = SkFixedToFIXED(SK_Fixed1);
628 fMat22Identity.eM12 = fMat22Identity.eM21 = SkFixedToFIXED(0);
629
630 // construct a matrix to go from HIRES logical units to our device units
631 fRec.getSingleMatrix(&fHiResMatrix);
632 SkScalar scale = SkScalarInvert(SkIntToScalar(HIRES_TEXTSIZE));
633 fHiResMatrix.preScale(scale, scale);
634 }
reed@google.comac6b9792011-03-11 15:42:51 +0000635 fSavefont = (HFONT)SelectObject(fDDC, fFont);
reed@google.coma767fa02011-08-05 21:40:26 +0000636
bungeman@google.coma0319f62012-04-18 15:40:50 +0000637 if (0 == GetTextMetrics(fDDC, &fTM)) {
638 ensure_typeface_accessible(fRec.fFontID);
639 if (0 == GetTextMetrics(fDDC, &fTM)) {
640 fTM.tmPitchAndFamily = TMPF_TRUETYPE;
641 }
642 }
643 // Used a logfont on a memory context, should never get a device font.
bungeman@google.com90b7e382012-04-20 15:26:28 +0000644 // Therefore all TMPF_DEVICE will be PostScript fonts.
645
646 // If TMPF_VECTOR is set, one of TMPF_TRUETYPE or TMPF_DEVICE must be set,
647 // otherwise we have a vector FON, which we don't support.
648 // This was determined by testing with Type1 PFM/PFB and OpenTypeCFF OTF,
649 // as well as looking at Wine bugs and sources.
650 SkASSERT(!(fTM.tmPitchAndFamily & TMPF_VECTOR) ||
651 (fTM.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_DEVICE)));
652
653 if (fTM.tmPitchAndFamily & TMPF_VECTOR) {
bungeman@google.coma0319f62012-04-18 15:40:50 +0000654 // Truetype or PostScript.
655 // Stroked FON also gets here (TMPF_VECTOR), but we don't handle it.
656 fType = SkScalerContext_Windows::kTrueType_Type;
657 fScale = fRec.fTextSize / gCanonicalTextSize;
658
659 fXform.eM11 = mul2float(fScale, fRec.fPost2x2[0][0]);
660 fXform.eM12 = mul2float(fScale, fRec.fPost2x2[1][0]);
661 fXform.eM21 = mul2float(fScale, fRec.fPost2x2[0][1]);
662 fXform.eM22 = mul2float(fScale, fRec.fPost2x2[1][1]);
663 fXform.eDx = 0;
664 fXform.eDy = 0;
665
666 fMat22.eM11 = float2FIXED(fXform.eM11);
667 fMat22.eM12 = float2FIXED(fXform.eM12);
668 fMat22.eM21 = float2FIXED(-fXform.eM21);
669 fMat22.eM22 = float2FIXED(-fXform.eM22);
670
671 if (needToRenderWithSkia(fRec)) {
672 this->forceGenerateImageFromPath();
673 }
674
675 } else {
676 // Assume bitmap
677 fType = SkScalerContext_Windows::kBitmap_Type;
678 fScale = SK_Scalar1;
679
680 fXform.eM11 = 1.0f;
681 fXform.eM12 = 0.0f;
682 fXform.eM21 = 0.0f;
683 fXform.eM22 = 1.0f;
684 fXform.eDx = 0.0f;
685 fXform.eDy = 0.0f;
686
687 fMat22.eM11 = SkScalarToFIXED(fRec.fPost2x2[0][0]);
688 fMat22.eM12 = SkScalarToFIXED(fRec.fPost2x2[1][0]);
689 fMat22.eM21 = SkScalarToFIXED(-fRec.fPost2x2[0][1]);
690 fMat22.eM22 = SkScalarToFIXED(-fRec.fPost2x2[1][1]);
691
692 lf.lfHeight = -SkScalarCeilToInt(fRec.fTextSize);
693 HFONT bitmapFont = CreateFontIndirect(&lf);
694 SelectObject(fDDC, bitmapFont);
695 ::DeleteObject(fFont);
696 fFont = bitmapFont;
697
698 if (0 == GetTextMetrics(fDDC, &fTM)) {
699 ensure_typeface_accessible(fRec.fFontID);
700 //if the following fails, we'll just draw at gCanonicalTextSize.
701 GetTextMetrics(fDDC, &fTM);
702 }
reed@google.coma767fa02011-08-05 21:40:26 +0000703 }
reed@google.com99edd432011-09-09 14:59:59 +0000704
705 fOffscreen.init(fFont, fXform);
reed@google.comac6b9792011-03-11 15:42:51 +0000706}
707
708SkScalerContext_Windows::~SkScalerContext_Windows() {
709 if (fDDC) {
710 ::SelectObject(fDDC, fSavefont);
711 ::DeleteDC(fDDC);
712 }
713 if (fFont) {
714 ::DeleteObject(fFont);
715 }
reed@google.com1dd17a12011-05-17 14:04:41 +0000716 if (fHiResFont) {
717 ::DeleteObject(fHiResFont);
718 }
reed@google.comac6b9792011-03-11 15:42:51 +0000719 if (fSC) {
720 ::ScriptFreeCache(&fSC);
721 }
722}
723
724unsigned SkScalerContext_Windows::generateGlyphCount() {
725 if (fGlyphCount < 0) {
bungeman@google.coma0319f62012-04-18 15:40:50 +0000726 if (fType == SkScalerContext_Windows::kBitmap_Type) {
727 return fTM.tmLastChar;
728 }
729 fGlyphCount = calculateOutlineGlyphCount(fDDC);
reed@google.comac6b9792011-03-11 15:42:51 +0000730 }
731 return fGlyphCount;
732}
733
734uint16_t SkScalerContext_Windows::generateCharToGlyph(SkUnichar uni) {
735 uint16_t index = 0;
736 WCHAR c[2];
737 // TODO(ctguil): Support characters that generate more than one glyph.
738 if (SkUTF16_FromUnichar(uni, (uint16_t*)c) == 1) {
739 // Type1 fonts fail with uniscribe API. Use GetGlyphIndices for plane 0.
740 SkAssertResult(GetGlyphIndicesW(fDDC, c, 1, &index, 0));
741 } else {
742 // Use uniscribe to detemine glyph index for non-BMP characters.
743 // Need to add extra item to SCRIPT_ITEM to work around a bug in older
744 // windows versions. https://bugzilla.mozilla.org/show_bug.cgi?id=366643
745 SCRIPT_ITEM si[2 + 1];
746 int items;
747 SkAssertResult(
748 SUCCEEDED(ScriptItemize(c, 2, 2, NULL, NULL, si, &items)));
749
750 WORD log[2];
751 SCRIPT_VISATTR vsa;
752 int glyphs;
753 SkAssertResult(SUCCEEDED(ScriptShape(
754 fDDC, &fSC, c, 2, 1, &si[0].a, &index, log, &vsa, &glyphs)));
755 }
756 return index;
757}
758
759void SkScalerContext_Windows::generateAdvance(SkGlyph* glyph) {
760 this->generateMetrics(glyph);
761}
762
763void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) {
764
765 SkASSERT(fDDC);
766
bungeman@google.coma0319f62012-04-18 15:40:50 +0000767 if (fType == SkScalerContext_Windows::kBitmap_Type) {
768 SIZE size;
769 WORD glyphs = glyph->getGlyphID(0);
770 if (0 == GetTextExtentPointI(fDDC, &glyphs, 1, &size)) {
771 glyph->fWidth = SkToS16(fTM.tmMaxCharWidth);
772 } else {
773 glyph->fWidth = SkToS16(size.cx);
774 }
775 glyph->fHeight = SkToS16(size.cy);
776
777 glyph->fTop = SkToS16(-fTM.tmAscent);
778 glyph->fLeft = SkToS16(0);
779 glyph->fAdvanceX = SkIntToFixed(glyph->fWidth);
780 glyph->fAdvanceY = 0;
781
782 //Apply matrix to values.
783 glyph->fAdvanceY = SkFixedMul(SkFIXEDToFixed(fMat22.eM21), glyph->fAdvanceX);
784 glyph->fAdvanceX = SkFixedMul(SkFIXEDToFixed(fMat22.eM11), glyph->fAdvanceX);
785
reed@google.com81275392012-06-18 14:08:56 +0000786 apply_outset(glyph, fOutset);
bungeman@google.coma0319f62012-04-18 15:40:50 +0000787 return;
788 }
789
reed@google.comac6b9792011-03-11 15:42:51 +0000790 GLYPHMETRICS gm;
reed@google.com1dd17a12011-05-17 14:04:41 +0000791 sk_bzero(&gm, sizeof(gm));
reed@google.comac6b9792011-03-11 15:42:51 +0000792
793 glyph->fRsbDelta = 0;
794 glyph->fLsbDelta = 0;
795
796 // Note: need to use GGO_GRAY8_BITMAP instead of GGO_METRICS because GGO_METRICS returns a smaller
797 // BlackBlox; we need the bigger one in case we need the image. fAdvance is the same.
798 uint32_t ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
bungeman@google.com39698b12011-11-15 22:26:41 +0000799 if (GDI_ERROR == ret) {
800 ensure_typeface_accessible(fRec.fFontID);
801 ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
802 }
reed@google.comac6b9792011-03-11 15:42:51 +0000803
804 if (GDI_ERROR != ret) {
805 if (ret == 0) {
806 // for white space, ret is zero and gmBlackBoxX, gmBlackBoxY are 1 incorrectly!
807 gm.gmBlackBoxX = gm.gmBlackBoxY = 0;
808 }
809 glyph->fWidth = gm.gmBlackBoxX;
810 glyph->fHeight = gm.gmBlackBoxY;
811 glyph->fTop = SkToS16(gm.gmptGlyphOrigin.y - gm.gmBlackBoxY);
812 glyph->fLeft = SkToS16(gm.gmptGlyphOrigin.x);
813 glyph->fAdvanceX = SkIntToFixed(gm.gmCellIncX);
814 glyph->fAdvanceY = -SkIntToFixed(gm.gmCellIncY);
815
reed@google.comf8cead52011-09-02 20:08:11 +0000816 // we outset in all dimensions, since the image may bleed outside
reed@google.comac6b9792011-03-11 15:42:51 +0000817 // of the computed bounds returned by GetGlyphOutline.
818 // This was deduced by trial and error for small text (e.g. 8pt), so there
819 // maybe a more precise way to make this adjustment...
reed@google.comf8cead52011-09-02 20:08:11 +0000820 //
821 // This test shows us clipping the tops of some of the CJK fonts unless we
822 // increase the top of the box by 2, hence the height by 4. This seems to
823 // correspond to an embedded bitmap font, but not sure.
824 // LayoutTests/fast/text/backslash-to-yen-sign-euc.html
825 //
reed@google.com5e2df642011-09-21 18:42:09 +0000826 if (glyph->fWidth) { // don't outset an empty glyph
827 glyph->fWidth += 4;
828 glyph->fHeight += 4;
829 glyph->fTop -= 2;
830 glyph->fLeft -= 2;
reed@google.comb8a5c612012-06-13 20:01:44 +0000831
reed@google.com81275392012-06-18 14:08:56 +0000832 apply_outset(glyph, fOutset);
reed@google.com5e2df642011-09-21 18:42:09 +0000833 }
reed@google.com1dd17a12011-05-17 14:04:41 +0000834
835 if (fHiResFont) {
836 SelectObject(fDDC, fHiResFont);
837 sk_bzero(&gm, sizeof(gm));
838 ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22Identity);
839 if (GDI_ERROR != ret) {
840 SkPoint advance;
841 fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
842 glyph->fAdvanceX = SkScalarToFixed(advance.fX);
843 glyph->fAdvanceY = SkScalarToFixed(advance.fY);
844 }
845 SelectObject(fDDC, fFont);
846 }
reed@google.comac6b9792011-03-11 15:42:51 +0000847 } else {
bungeman@google.coma0319f62012-04-18 15:40:50 +0000848 glyph->zeroMetrics();
reed@google.comac6b9792011-03-11 15:42:51 +0000849 }
850}
851
852void SkScalerContext_Windows::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) {
853// Note: This code was borrowed from generateLineHeight, which has a note
854// stating that it may be incorrect.
855 if (!(mx || my))
856 return;
857
858 SkASSERT(fDDC);
859
bungeman@google.coma0319f62012-04-18 15:40:50 +0000860 if (fType == SkScalerContext_Windows::kBitmap_Type) {
861 if (mx) {
862 mx->fTop = SkIntToScalar(-fTM.tmAscent);
863 mx->fAscent = SkIntToScalar(-fTM.tmAscent);
864 mx->fDescent = -SkIntToScalar(fTM.tmDescent);
865 mx->fBottom = SkIntToScalar(fTM.tmDescent);
866 mx->fLeading = SkIntToScalar(fTM.tmInternalLeading
867 + fTM.tmExternalLeading);
868 }
869
870 if (my) {
871 my->fTop = SkIntToScalar(-fTM.tmAscent);
872 my->fAscent = SkIntToScalar(-fTM.tmAscent);
873 my->fDescent = SkIntToScalar(-fTM.tmDescent);
874 my->fBottom = SkIntToScalar(fTM.tmDescent);
875 my->fLeading = SkIntToScalar(fTM.tmInternalLeading
876 + fTM.tmExternalLeading);
877 }
878 return;
879 }
880
reed@google.comac6b9792011-03-11 15:42:51 +0000881 OUTLINETEXTMETRIC otm;
882
883 uint32_t ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
bungeman@google.com39698b12011-11-15 22:26:41 +0000884 if (GDI_ERROR == ret) {
885 ensure_typeface_accessible(fRec.fFontID);
886 ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
887 }
reed@google.comac6b9792011-03-11 15:42:51 +0000888 if (sizeof(otm) != ret) {
bungeman@google.coma0319f62012-04-18 15:40:50 +0000889 return;
reed@google.comac6b9792011-03-11 15:42:51 +0000890 }
891
892 if (mx) {
893 mx->fTop = -fScale * otm.otmTextMetrics.tmAscent;
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000894 mx->fAscent = -fScale * otm.otmAscent;
895 mx->fDescent = -fScale * otm.otmDescent;
896 mx->fBottom = fScale * otm.otmTextMetrics.tmDescent;
897 mx->fLeading = fScale * (otm.otmTextMetrics.tmInternalLeading
898 + otm.otmTextMetrics.tmExternalLeading);
reed@google.comac6b9792011-03-11 15:42:51 +0000899 }
900
901 if (my) {
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000902 my->fTop = -fScale * otm.otmTextMetrics.tmAscent;
903 my->fAscent = -fScale * otm.otmAscent;
904 my->fDescent = -fScale * otm.otmDescent;
905 my->fBottom = fScale * otm.otmTextMetrics.tmDescent;
906 my->fLeading = fScale * (otm.otmTextMetrics.tmInternalLeading
907 + otm.otmTextMetrics.tmExternalLeading);
reed@google.comac6b9792011-03-11 15:42:51 +0000908 }
909}
910
reed@google.com7430a332011-10-03 14:37:38 +0000911////////////////////////////////////////////////////////////////////////////////////////
912
913static void build_power_table(uint8_t table[], float ee) {
914 for (int i = 0; i < 256; i++) {
915 float x = i / 255.f;
916 x = powf(x, ee);
917 int xx = SkScalarRound(SkFloatToScalar(x * 255));
918 table[i] = SkToU8(xx);
919 }
920}
921
922// This will invert the gamma applied by GDI, so we can sort-of get linear values.
923// Needed when we draw non-black, non-white text, and don't know how to bias it.
924static const uint8_t* getInverseGammaTable() {
925 static bool gInited;
926 static uint8_t gTable[256];
927 if (!gInited) {
928 UINT level = 0;
929 if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &level, 0) || !level) {
930 // can't get the data, so use a default
931 level = 1400;
932 }
933 build_power_table(gTable, level / 1000.0f);
934 gInited = true;
935 }
936 return gTable;
937}
938
reed@google.comac6b9792011-03-11 15:42:51 +0000939#include "SkColorPriv.h"
940
reed@google.com5e2df642011-09-21 18:42:09 +0000941// gdi's bitmap is upside-down, so we reverse dst walking in Y
942// whenever we copy it into skia's buffer
943
reed@google.com8351aab2012-01-18 17:06:35 +0000944static int compute_luminance(int r, int g, int b) {
945// return (r * 2 + g * 5 + b) >> 3;
946 return (r * 27 + g * 92 + b * 9) >> 7;
947}
948
reed@google.com5e2df642011-09-21 18:42:09 +0000949static inline uint8_t rgb_to_a8(SkGdiRGB rgb) {
reed@google.com6f5df482011-09-28 20:33:24 +0000950 int r = (rgb >> 16) & 0xFF;
951 int g = (rgb >> 8) & 0xFF;
952 int b = (rgb >> 0) & 0xFF;
reed@google.com8351aab2012-01-18 17:06:35 +0000953 return compute_luminance(r, g, b);
reed@google.com82a34d82011-07-26 19:33:08 +0000954}
955
reed@google.com5e2df642011-09-21 18:42:09 +0000956static inline uint16_t rgb_to_lcd16(SkGdiRGB rgb) {
reed@google.comac6b9792011-03-11 15:42:51 +0000957 int r = (rgb >> 16) & 0xFF;
958 int g = (rgb >> 8) & 0xFF;
959 int b = (rgb >> 0) & 0xFF;
reed@google.comac6b9792011-03-11 15:42:51 +0000960 return SkPackRGB16(SkR32ToR16(r), SkG32ToG16(g), SkB32ToB16(b));
961}
962
reed@google.com754e4eb2011-09-26 13:21:39 +0000963static inline SkPMColor rgb_to_lcd32(SkGdiRGB rgb) {
964 int r = (rgb >> 16) & 0xFF;
965 int g = (rgb >> 8) & 0xFF;
966 int b = (rgb >> 0) & 0xFF;
967 int a = SkMax32(r, SkMax32(g, b));
968 return SkPackARGB32(a, r, g, b);
969}
970
reed@google.com82cff022011-09-22 14:33:40 +0000971// Is this GDI color neither black nor white? If so, we have to keep this
972// image as is, rather than smashing it down to a BW mask.
973//
974// returns int instead of bool, since we don't want/have to pay to convert
975// the zero/non-zero value into a bool
976static int is_not_black_or_white(SkGdiRGB c) {
977 // same as (but faster than)
978 // c &= 0x00FFFFFF;
979 // return 0 == c || 0x00FFFFFF == c;
980 return (c + (c & 1)) & 0x00FFFFFF;
reed@google.com5e2df642011-09-21 18:42:09 +0000981}
982
983static bool is_rgb_really_bw(const SkGdiRGB* src, int width, int height, int srcRB) {
984 for (int y = 0; y < height; ++y) {
985 for (int x = 0; x < width; ++x) {
reed@google.com82cff022011-09-22 14:33:40 +0000986 if (is_not_black_or_white(src[x])) {
reed@google.com5e2df642011-09-21 18:42:09 +0000987 return false;
988 }
989 }
reed@google.com6f5df482011-09-28 20:33:24 +0000990 src = SkTAddByteOffset(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +0000991 }
992 return true;
993}
994
995static void rgb_to_bw(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
reed@google.com754e4eb2011-09-26 13:21:39 +0000996 const SkGlyph& glyph, int32_t xorMask) {
reed@google.com5e2df642011-09-21 18:42:09 +0000997 const int width = glyph.fWidth;
998 const size_t dstRB = (width + 7) >> 3;
999 uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1000
1001 int byteCount = width >> 3;
1002 int bitCount = width & 7;
1003
1004 // adjust srcRB to skip the values in our byteCount loop,
1005 // since we increment src locally there
1006 srcRB -= byteCount * 8 * sizeof(SkGdiRGB);
1007
1008 for (int y = 0; y < glyph.fHeight; ++y) {
1009 if (byteCount > 0) {
reed@google.com5e2df642011-09-21 18:42:09 +00001010 for (int i = 0; i < byteCount; ++i) {
reed@google.com7a230142011-09-27 14:07:21 +00001011 unsigned byte = 0;
reed@google.com754e4eb2011-09-26 13:21:39 +00001012 byte |= (src[0] ^ xorMask) & (1 << 7);
1013 byte |= (src[1] ^ xorMask) & (1 << 6);
1014 byte |= (src[2] ^ xorMask) & (1 << 5);
1015 byte |= (src[3] ^ xorMask) & (1 << 4);
1016 byte |= (src[4] ^ xorMask) & (1 << 3);
1017 byte |= (src[5] ^ xorMask) & (1 << 2);
1018 byte |= (src[6] ^ xorMask) & (1 << 1);
1019 byte |= (src[7] ^ xorMask) & (1 << 0);
reed@google.com6a8f14d2011-09-27 12:54:24 +00001020 dst[i] = byte;
reed@google.com5e2df642011-09-21 18:42:09 +00001021 src += 8;
1022 }
1023 }
1024 if (bitCount > 0) {
1025 unsigned byte = 0;
1026 unsigned mask = 0x80;
1027 for (int i = 0; i < bitCount; i++) {
reed@google.com6a8f14d2011-09-27 12:54:24 +00001028 byte |= (src[i] ^ xorMask) & mask;
reed@google.com5e2df642011-09-21 18:42:09 +00001029 mask >>= 1;
1030 }
1031 dst[byteCount] = byte;
1032 }
reed@google.com6f5df482011-09-28 20:33:24 +00001033 src = SkTAddByteOffset(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001034 dst -= dstRB;
1035 }
1036}
1037
1038static void rgb_to_a8(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
reed@google.com754e4eb2011-09-26 13:21:39 +00001039 const SkGlyph& glyph, int32_t xorMask) {
reed@google.com5e2df642011-09-21 18:42:09 +00001040 const size_t dstRB = glyph.rowBytes();
1041 const int width = glyph.fWidth;
1042 uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1043
1044 for (int y = 0; y < glyph.fHeight; y++) {
1045 for (int i = 0; i < width; i++) {
reed@google.com754e4eb2011-09-26 13:21:39 +00001046 dst[i] = rgb_to_a8(src[i] ^ xorMask);
reed@google.com5e2df642011-09-21 18:42:09 +00001047 }
reed@google.com6f5df482011-09-28 20:33:24 +00001048 src = SkTAddByteOffset(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001049 dst -= dstRB;
1050 }
1051}
1052
1053static void rgb_to_lcd16(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
reed@google.com754e4eb2011-09-26 13:21:39 +00001054 const SkGlyph& glyph, int32_t xorMask) {
reed@google.com5e2df642011-09-21 18:42:09 +00001055 const size_t dstRB = glyph.rowBytes();
1056 const int width = glyph.fWidth;
1057 uint16_t* SK_RESTRICT dst = (uint16_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1058
1059 for (int y = 0; y < glyph.fHeight; y++) {
1060 for (int i = 0; i < width; i++) {
reed@google.com754e4eb2011-09-26 13:21:39 +00001061 dst[i] = rgb_to_lcd16(src[i] ^ xorMask);
reed@google.com5e2df642011-09-21 18:42:09 +00001062 }
reed@google.com6f5df482011-09-28 20:33:24 +00001063 src = SkTAddByteOffset(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001064 dst = (uint16_t*)((char*)dst - dstRB);
1065 }
1066}
1067
reed@google.com754e4eb2011-09-26 13:21:39 +00001068static void rgb_to_lcd32(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
1069 const SkGlyph& glyph, int32_t xorMask) {
1070 const size_t dstRB = glyph.rowBytes();
1071 const int width = glyph.fWidth;
1072 SkPMColor* SK_RESTRICT dst = (SkPMColor*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1073
1074 for (int y = 0; y < glyph.fHeight; y++) {
1075 for (int i = 0; i < width; i++) {
1076 dst[i] = rgb_to_lcd32(src[i] ^ xorMask);
1077 }
reed@google.com6f5df482011-09-28 20:33:24 +00001078 src = SkTAddByteOffset(src, srcRB);
reed@google.com754e4eb2011-09-26 13:21:39 +00001079 dst = (SkPMColor*)((char*)dst - dstRB);
1080 }
1081}
1082
reed@google.com6f5df482011-09-28 20:33:24 +00001083static inline unsigned clamp255(unsigned x) {
1084 SkASSERT(x <= 256);
1085 return x - (x >> 8);
1086}
1087
reed@google.comffe49f52011-11-22 19:42:41 +00001088#define WHITE_LUMINANCE_LIMIT 0xA0
1089#define BLACK_LUMINANCE_LIMIT 0x40
1090
reed@google.comac6b9792011-03-11 15:42:51 +00001091void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
reed@google.comac6b9792011-03-11 15:42:51 +00001092 SkAutoMutexAcquire ac(gFTMutex);
1093
1094 SkASSERT(fDDC);
1095
reed@google.com62711172011-05-18 15:08:10 +00001096 const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
reed@google.com82a34d82011-07-26 19:33:08 +00001097 const bool isAA = !isLCD(fRec);
reed@google.comffe49f52011-11-22 19:42:41 +00001098 bool isWhite = fRec.getLuminanceByte() >= WHITE_LUMINANCE_LIMIT;
1099 bool isBlack = fRec.getLuminanceByte() <= BLACK_LUMINANCE_LIMIT;
reed@google.com6f5df482011-09-28 20:33:24 +00001100
1101 SkGdiRGB fgColor;
1102 uint32_t rgbXOR;
reed@google.com7430a332011-10-03 14:37:38 +00001103 const uint8_t* table = NULL;
reed@google.com6f5df482011-09-28 20:33:24 +00001104 if (isBW || isWhite) {
1105 fgColor = 0x00FFFFFF;
1106 rgbXOR = 0;
1107 } else if (isBlack) {
1108 fgColor = 0;
1109 rgbXOR = ~0;
1110 } else {
reed@google.com7430a332011-10-03 14:37:38 +00001111 table = getInverseGammaTable();
1112 fgColor = 0x00FFFFFF;
reed@google.com6f5df482011-09-28 20:33:24 +00001113 rgbXOR = 0;
reed@google.com6f5df482011-09-28 20:33:24 +00001114 }
reed@google.comac6b9792011-03-11 15:42:51 +00001115
reed@google.com99edd432011-09-09 14:59:59 +00001116 size_t srcRB;
reed@google.com6f5df482011-09-28 20:33:24 +00001117 const void* bits = fOffscreen.draw(glyph, isBW, fgColor, &srcRB);
bungeman@google.com39698b12011-11-15 22:26:41 +00001118 if (NULL == bits) {
1119 ensure_typeface_accessible(fRec.fFontID);
1120 bits = fOffscreen.draw(glyph, isBW, fgColor, &srcRB);
1121 if (NULL == bits) {
1122 sk_bzero(glyph.fImage, glyph.computeImageSize());
1123 return;
1124 }
reed@google.com82a34d82011-07-26 19:33:08 +00001125 }
reed@google.comac6b9792011-03-11 15:42:51 +00001126
reed@google.com7430a332011-10-03 14:37:38 +00001127 if (table) {
reed@google.com6f5df482011-09-28 20:33:24 +00001128 SkGdiRGB* addr = (SkGdiRGB*)bits;
1129 for (int y = 0; y < glyph.fHeight; ++y) {
1130 for (int x = 0; x < glyph.fWidth; ++x) {
1131 int r = (addr[x] >> 16) & 0xFF;
1132 int g = (addr[x] >> 8) & 0xFF;
1133 int b = (addr[x] >> 0) & 0xFF;
reed@google.com7430a332011-10-03 14:37:38 +00001134 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
reed@google.com6f5df482011-09-28 20:33:24 +00001135 }
1136 addr = SkTAddByteOffset(addr, srcRB);
1137 }
1138 }
1139
reed@google.com82a34d82011-07-26 19:33:08 +00001140 int width = glyph.fWidth;
1141 size_t dstRB = glyph.rowBytes();
1142 if (isBW) {
1143 const uint8_t* src = (const uint8_t*)bits;
reed@google.com82a34d82011-07-26 19:33:08 +00001144 uint8_t* dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1145 for (int y = 0; y < glyph.fHeight; y++) {
1146 memcpy(dst, src, dstRB);
1147 src += srcRB;
1148 dst -= dstRB;
reed@google.comac6b9792011-03-11 15:42:51 +00001149 }
reed@google.com82a34d82011-07-26 19:33:08 +00001150 } else if (isAA) {
reed@google.com6f5df482011-09-28 20:33:24 +00001151 // since the caller may require A8 for maskfilters, we can't check for BW
1152 // ... until we have the caller tell us that explicitly
reed@google.com5e2df642011-09-21 18:42:09 +00001153 const SkGdiRGB* src = (const SkGdiRGB*)bits;
reed@google.com6f5df482011-09-28 20:33:24 +00001154 rgb_to_a8(src, srcRB, glyph, rgbXOR);
reed@google.com82a34d82011-07-26 19:33:08 +00001155 } else { // LCD16
reed@google.com5e2df642011-09-21 18:42:09 +00001156 const SkGdiRGB* src = (const SkGdiRGB*)bits;
1157 if (is_rgb_really_bw(src, width, glyph.fHeight, srcRB)) {
reed@google.com754e4eb2011-09-26 13:21:39 +00001158 rgb_to_bw(src, srcRB, glyph, rgbXOR);
reed@google.com5e2df642011-09-21 18:42:09 +00001159 ((SkGlyph*)&glyph)->fMaskFormat = SkMask::kBW_Format;
1160 } else {
reed@google.com754e4eb2011-09-26 13:21:39 +00001161 if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
1162 rgb_to_lcd16(src, srcRB, glyph, rgbXOR);
1163 } else {
1164 SkASSERT(SkMask::kLCD32_Format == glyph.fMaskFormat);
1165 rgb_to_lcd32(src, srcRB, glyph, rgbXOR);
1166 }
reed@google.comac6b9792011-03-11 15:42:51 +00001167 }
1168 }
reed@google.comac6b9792011-03-11 15:42:51 +00001169}
1170
1171void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) {
1172
1173 SkAutoMutexAcquire ac(gFTMutex);
1174
1175 SkASSERT(&glyph && path);
1176 SkASSERT(fDDC);
1177
1178 path->reset();
1179
1180#if 0
1181 char buf[1024];
1182 sprintf(buf, "generatePath: id:%d, w=%d, h=%d, font:%s,fh:%d\n", glyph.fID, glyph.fWidth, glyph.fHeight, lf.lfFaceName, lf.lfHeight);
1183 OutputDebugString(buf);
1184#endif
1185
1186 GLYPHMETRICS gm;
1187 uint32_t total_size = GetGlyphOutlineW(fDDC, glyph.fID, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, BUFFERSIZE, glyphbuf, &fMat22);
bungeman@google.com39698b12011-11-15 22:26:41 +00001188 if (GDI_ERROR == total_size) {
1189 ensure_typeface_accessible(fRec.fFontID);
1190 total_size = GetGlyphOutlineW(fDDC, glyph.fID, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, BUFFERSIZE, glyphbuf, &fMat22);
1191 }
reed@google.comac6b9792011-03-11 15:42:51 +00001192
1193 if (GDI_ERROR != total_size) {
1194
1195 const uint8_t* cur_glyph = glyphbuf;
1196 const uint8_t* end_glyph = glyphbuf + total_size;
1197
1198 while(cur_glyph < end_glyph) {
1199 const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
1200
1201 const uint8_t* end_poly = cur_glyph + th->cb;
1202 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
1203
1204 path->moveTo(SkFixedToScalar(*(SkFixed*)(&th->pfxStart.x)), SkFixedToScalar(*(SkFixed*)(&th->pfxStart.y)));
1205
1206 while(cur_poly < end_poly) {
1207 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
1208
1209 if (pc->wType == TT_PRIM_LINE) {
1210 for (uint16_t i = 0; i < pc->cpfx; i++) {
1211 path->lineTo(SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].x)), SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].y)));
1212 }
1213 }
1214
1215 if (pc->wType == TT_PRIM_QSPLINE) {
1216 for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
1217 POINTFX pnt_b = pc->apfx[u]; // B is always the current point
1218 POINTFX pnt_c = pc->apfx[u+1];
1219
1220 if (u < pc->cpfx - 2) { // If not on last spline, compute C
1221 pnt_c.x = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.x), *(SkFixed*)(&pnt_c.x)));
1222 pnt_c.y = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.y), *(SkFixed*)(&pnt_c.y)));
1223 }
1224
1225 path->quadTo(SkFixedToScalar(*(SkFixed*)(&pnt_b.x)), SkFixedToScalar(*(SkFixed*)(&pnt_b.y)), SkFixedToScalar(*(SkFixed*)(&pnt_c.x)), SkFixedToScalar(*(SkFixed*)(&pnt_c.y)));
1226 }
1227 }
1228 cur_poly += sizeof(uint16_t) * 2 + sizeof(POINTFX) * pc->cpfx;
1229 }
1230 cur_glyph += th->cb;
1231 path->close();
1232 }
1233 }
1234 else {
1235 SkASSERT(false);
1236 }
1237 //char buf[1024];
1238 //sprintf(buf, "generatePath: count:%d\n", count);
1239 //OutputDebugString(buf);
1240}
1241
bungeman@google.come70f7982012-06-01 19:38:19 +00001242static void logfont_for_name(const char* familyName, LOGFONT& lf) {
1243 memset(&lf, 0, sizeof(LOGFONT));
1244#ifdef UNICODE
1245 // Get the buffer size needed first.
1246 size_t str_len = ::MultiByteToWideChar(CP_UTF8, 0, familyName,
1247 -1, NULL, 0);
1248 // Allocate a buffer (str_len already has terminating null
1249 // accounted for).
1250 wchar_t *wideFamilyName = new wchar_t[str_len];
1251 // Now actually convert the string.
1252 ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1,
1253 wideFamilyName, str_len);
1254 ::wcsncpy(lf.lfFaceName, wideFamilyName, LF_FACESIZE - 1);
1255 delete [] wideFamilyName;
1256 lf.lfFaceName[LF_FACESIZE-1] = L'\0';
1257#else
1258 ::strncpy(lf.lfFaceName, familyName, LF_FACESIZE - 1);
1259 lf.lfFaceName[LF_FACESIZE - 1] = '\0';
1260#endif
1261}
1262
1263static void logfont_to_name(const LOGFONT& lf, SkString* s) {
1264#ifdef UNICODE
1265 // Get the buffer size needed first.
1266 size_t str_len = WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1, NULL,
1267 0, NULL, NULL);
1268 // Allocate a buffer (str_len already has terminating null accounted for).
1269 s->resize(str_len);
1270 // Now actually convert the string.
1271 WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1,
1272 s->writable_str(), str_len,
1273 NULL, NULL);
1274#else
1275 s->set(lf.lfFaceName);
1276#endif
1277}
1278
1279void SkFontHost::Serialize(const SkTypeface* rawFace, SkWStream* stream) {
1280 const LogFontTypeface* face = static_cast<const LogFontTypeface*>(rawFace);
1281 SkFontDescriptor descriptor(face->style());
1282
1283 SkString familyName;
1284 logfont_to_name(face->fLogFont, &familyName);
1285 descriptor.setFamilyName(familyName.c_str());
1286 //TODO: FileName and PostScriptName currently unsupported.
1287
1288 descriptor.serialize(stream);
1289
1290 if (face->fSerializeAsStream) {
1291 // store the entire font in the fontData
1292 SkAutoTUnref<SkStream> fontStream(SkFontHost::OpenStream(face->uniqueID()));
1293 const uint32_t length = fontStream->getLength();
1294
1295 stream->writePackedUInt(length);
1296 stream->writeStream(fontStream, length);
1297 } else {
1298 stream->writePackedUInt(0);
1299 }
reed@google.comac6b9792011-03-11 15:42:51 +00001300}
1301
1302SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
bungeman@google.come70f7982012-06-01 19:38:19 +00001303 SkFontDescriptor descriptor(stream);
1304
1305 const uint32_t customFontDataLength = stream->readPackedUInt();
1306 if (customFontDataLength > 0) {
1307 // generate a new stream to store the custom typeface
1308 SkAutoTUnref<SkMemoryStream> fontStream(SkNEW_ARGS(SkMemoryStream, (customFontDataLength - 1)));
1309 stream->read((void*)fontStream->getMemoryBase(), customFontDataLength - 1);
1310
1311 return CreateTypefaceFromStream(fontStream.get());
1312 }
1313
1314 return SkFontHost::CreateTypeface(NULL, descriptor.getFamilyName(), descriptor.getStyle());
reed@google.comac6b9792011-03-11 15:42:51 +00001315}
1316
1317static bool getWidthAdvance(HDC hdc, int gId, int16_t* advance) {
1318 // Initialize the MAT2 structure to the identify transformation matrix.
1319 static const MAT2 mat2 = {SkScalarToFIXED(1), SkScalarToFIXED(0),
1320 SkScalarToFIXED(0), SkScalarToFIXED(1)};
1321 int flags = GGO_METRICS | GGO_GLYPH_INDEX;
1322 GLYPHMETRICS gm;
1323 if (GDI_ERROR == GetGlyphOutline(hdc, gId, flags, &gm, 0, NULL, &mat2)) {
1324 return false;
1325 }
1326 SkASSERT(advance);
1327 *advance = gm.gmCellIncX;
1328 return true;
1329}
1330
1331// static
1332SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
vandebo@chromium.org325cb9a2011-03-30 18:36:29 +00001333 uint32_t fontID,
vandebo@chromium.org37ad8fb2011-08-18 02:38:50 +00001334 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
1335 const uint32_t* glyphIDs,
1336 uint32_t glyphIDsCount) {
reed@google.com59d2f632011-05-02 19:36:59 +00001337 LOGFONT lf;
1338 GetLogFontByID(fontID, &lf);
reed@google.comac6b9792011-03-11 15:42:51 +00001339 SkAdvancedTypefaceMetrics* info = NULL;
1340
1341 HDC hdc = CreateCompatibleDC(NULL);
1342 HFONT font = CreateFontIndirect(&lf);
1343 HFONT savefont = (HFONT)SelectObject(hdc, font);
1344 HFONT designFont = NULL;
1345
reed@google.com05b6f3a2011-11-28 15:30:28 +00001346 const char stem_chars[] = {'i', 'I', '!', '1'};
1347 int16_t min_width;
1348 unsigned glyphCount;
1349
reed@google.comac6b9792011-03-11 15:42:51 +00001350 // To request design units, create a logical font whose height is specified
1351 // as unitsPerEm.
1352 OUTLINETEXTMETRIC otm;
bungeman@google.com39698b12011-11-15 22:26:41 +00001353 unsigned int otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
1354 if (0 == otmRet) {
1355 ensure_typeface_accessible(fontID);
1356 otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
1357 }
1358 if (!otmRet || !GetTextFace(hdc, LF_FACESIZE, lf.lfFaceName)) {
reed@google.comac6b9792011-03-11 15:42:51 +00001359 goto Error;
1360 }
1361 lf.lfHeight = -SkToS32(otm.otmEMSquare);
1362 designFont = CreateFontIndirect(&lf);
1363 SelectObject(hdc, designFont);
1364 if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) {
1365 goto Error;
1366 }
bungeman@google.coma0319f62012-04-18 15:40:50 +00001367 glyphCount = calculateOutlineGlyphCount(hdc);
reed@google.comac6b9792011-03-11 15:42:51 +00001368
1369 info = new SkAdvancedTypefaceMetrics;
1370 info->fEmSize = otm.otmEMSquare;
1371 info->fMultiMaster = false;
1372 info->fLastGlyphID = SkToU16(glyphCount - 1);
1373 info->fStyle = 0;
bungeman@google.come70f7982012-06-01 19:38:19 +00001374 logfont_to_name(lf, &info->fFontName);
reed@google.comac6b9792011-03-11 15:42:51 +00001375
vandebo@chromium.org6744d492011-05-09 18:13:47 +00001376 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
1377 populate_glyph_to_unicode(hdc, glyphCount, &(info->fGlyphToUnicode));
1378 }
1379
vandebo@chromium.orgd41e70d2012-03-08 19:41:01 +00001380 if (glyphCount > 0 &&
1381 (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE)) {
reed@google.comac6b9792011-03-11 15:42:51 +00001382 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
1383 } else {
1384 info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
1385 info->fItalicAngle = 0;
1386 info->fAscent = 0;
1387 info->fDescent = 0;
1388 info->fStemV = 0;
1389 info->fCapHeight = 0;
1390 info->fBBox = SkIRect::MakeEmpty();
1391 return info;
1392 }
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +00001393
reed@google.comac6b9792011-03-11 15:42:51 +00001394 // If this bit is clear the font is a fixed pitch font.
1395 if (!(otm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
1396 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1397 }
1398 if (otm.otmTextMetrics.tmItalic) {
1399 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1400 }
1401 // Setting symbolic style by default for now.
1402 info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style;
1403 if (otm.otmTextMetrics.tmPitchAndFamily & FF_ROMAN) {
1404 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1405 } else if (otm.otmTextMetrics.tmPitchAndFamily & FF_SCRIPT) {
1406 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1407 }
1408
1409 // The main italic angle of the font, in tenths of a degree counterclockwise
1410 // from vertical.
1411 info->fItalicAngle = otm.otmItalicAngle / 10;
1412 info->fAscent = SkToS16(otm.otmTextMetrics.tmAscent);
1413 info->fDescent = SkToS16(-otm.otmTextMetrics.tmDescent);
1414 // TODO(ctguil): Use alternate cap height calculation.
1415 // MSDN says otmsCapEmHeight is not support but it is returning a value on
1416 // my Win7 box.
1417 info->fCapHeight = otm.otmsCapEmHeight;
1418 info->fBBox =
1419 SkIRect::MakeLTRB(otm.otmrcFontBox.left, otm.otmrcFontBox.top,
1420 otm.otmrcFontBox.right, otm.otmrcFontBox.bottom);
1421
1422 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1423 // This probably isn't very good with an italic font.
reed@google.com05b6f3a2011-11-28 15:30:28 +00001424 min_width = SHRT_MAX;
reed@google.comac6b9792011-03-11 15:42:51 +00001425 info->fStemV = 0;
reed@google.comac6b9792011-03-11 15:42:51 +00001426 for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {
1427 ABC abcWidths;
1428 if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) {
1429 int16_t width = abcWidths.abcB;
1430 if (width > 0 && width < min_width) {
1431 min_width = width;
1432 info->fStemV = min_width;
1433 }
1434 }
1435 }
1436
1437 // If bit 1 is set, the font may not be embedded in a document.
1438 // If bit 1 is clear, the font can be embedded.
1439 // If bit 2 is set, the embedding is read-only.
1440 if (otm.otmfsType & 0x1) {
1441 info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
ctguil@chromium.org0e6dc0a2011-03-30 20:41:16 +00001442 } else if (perGlyphInfo &
1443 SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +00001444 if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
1445 appendRange(&info->fGlyphWidths, 0);
1446 info->fGlyphWidths->fAdvance.append(1, &min_width);
1447 finishRange(info->fGlyphWidths.get(), 0,
1448 SkAdvancedTypefaceMetrics::WidthRange::kDefault);
1449 } else {
1450 info->fGlyphWidths.reset(
vandebo@chromium.org37ad8fb2011-08-18 02:38:50 +00001451 getAdvanceData(hdc,
1452 glyphCount,
1453 glyphIDs,
1454 glyphIDsCount,
1455 &getWidthAdvance));
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +00001456 }
reed@google.comac6b9792011-03-11 15:42:51 +00001457 }
1458
1459Error:
1460 SelectObject(hdc, savefont);
1461 DeleteObject(designFont);
1462 DeleteObject(font);
1463 DeleteDC(hdc);
1464
1465 return info;
1466}
1467
bungeman@google.coma5501992012-05-18 19:06:41 +00001468//Dummy representation of a Base64 encoded GUID from create_unique_font_name.
1469#define BASE64_GUID_ID "XXXXXXXXXXXXXXXXXXXXXXXX"
1470//Length of GUID representation from create_id, including NULL terminator.
1471#define BASE64_GUID_ID_LEN SK_ARRAY_COUNT(BASE64_GUID_ID)
reed@google.comac6b9792011-03-11 15:42:51 +00001472
bungeman@google.coma5501992012-05-18 19:06:41 +00001473SK_COMPILE_ASSERT(BASE64_GUID_ID_LEN < LF_FACESIZE, GUID_longer_than_facesize);
1474
1475/**
1476 NameID 6 Postscript names cannot have the character '/'.
1477 It would be easier to hex encode the GUID, but that is 32 bytes,
1478 and many systems have issues with names longer than 28 bytes.
1479 The following need not be any standard base64 encoding.
1480 The encoded value is never decoded.
1481*/
1482static const char postscript_safe_base64_encode[] =
1483 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1484 "abcdefghijklmnopqrstuvwxyz"
1485 "0123456789-_=";
1486
1487/**
1488 Formats a GUID into Base64 and places it into buffer.
1489 buffer should have space for at least BASE64_GUID_ID_LEN characters.
1490 The string will always be null terminated.
1491 XXXXXXXXXXXXXXXXXXXXXXXX0
1492 */
1493static void format_guid_b64(const GUID& guid, char* buffer, size_t bufferSize) {
1494 SkASSERT(bufferSize >= BASE64_GUID_ID_LEN);
1495 size_t written = SkBase64::Encode(&guid, sizeof(guid), buffer, postscript_safe_base64_encode);
1496 SkASSERT(written < LF_FACESIZE);
1497 buffer[written] = '\0';
1498}
1499
1500/**
1501 Creates a Base64 encoded GUID and places it into buffer.
1502 buffer should have space for at least BASE64_GUID_ID_LEN characters.
1503 The string will always be null terminated.
1504 XXXXXXXXXXXXXXXXXXXXXXXX0
1505 */
1506static HRESULT create_unique_font_name(char* buffer, size_t bufferSize) {
1507 GUID guid = {};
1508 if (FAILED(CoCreateGuid(&guid))) {
1509 return E_UNEXPECTED;
1510 }
1511 format_guid_b64(guid, buffer, bufferSize);
1512
1513 return S_OK;
1514}
1515
1516/**
1517 Introduces a font to GDI. On failure will return NULL. The returned handle
1518 should eventually be passed to RemoveFontMemResourceEx.
1519*/
1520static HANDLE activate_font(SkData* fontData) {
1521 DWORD numFonts = 0;
1522 //AddFontMemResourceEx just copies the data, but does not specify const.
1523 HANDLE fontHandle = AddFontMemResourceEx(const_cast<void*>(fontData->data()),
1524 fontData->size(),
1525 0,
1526 &numFonts);
1527
1528 if (fontHandle != NULL && numFonts < 1) {
1529 RemoveFontMemResourceEx(fontHandle);
1530 return NULL;
1531 }
1532
1533 return fontHandle;
1534}
1535
bungeman@google.coma5501992012-05-18 19:06:41 +00001536SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
1537 // Create a unique and unpredictable font name.
1538 // Avoids collisions and access from CSS.
1539 char familyName[BASE64_GUID_ID_LEN];
1540 const int familyNameSize = SK_ARRAY_COUNT(familyName);
1541 if (FAILED(create_unique_font_name(familyName, familyNameSize))) {
1542 return NULL;
1543 }
1544
1545 // Change the name of the font.
bungeman@google.come9bbee32012-05-21 13:46:13 +00001546 SkAutoTUnref<SkData> rewrittenFontData(SkOTUtils::RenameFont(stream, familyName, familyNameSize-1));
1547 if (NULL == rewrittenFontData.get()) {
bungeman@google.coma5501992012-05-18 19:06:41 +00001548 return NULL;
1549 }
bungeman@google.coma5501992012-05-18 19:06:41 +00001550
1551 // Register the font with GDI.
bungeman@google.come9bbee32012-05-21 13:46:13 +00001552 HANDLE fontReference = activate_font(rewrittenFontData.get());
bungeman@google.coma5501992012-05-18 19:06:41 +00001553 if (NULL == fontReference) {
1554 return NULL;
1555 }
1556
1557 // Create the typeface.
1558 LOGFONT lf;
1559 logfont_for_name(familyName, lf);
1560
1561 return SkCreateFontMemResourceTypefaceFromLOGFONT(lf, fontReference);
reed@google.comac6b9792011-03-11 15:42:51 +00001562}
1563
1564SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {
ctguil@chromium.orgf4c26222011-05-16 22:00:05 +00001565 const DWORD kTTCTag =
1566 SkEndian_SwapBE32(SkSetFourByteTag('t', 't', 'c', 'f'));
reed@google.com59d2f632011-05-02 19:36:59 +00001567 LOGFONT lf;
1568 GetLogFontByID(uniqueID, &lf);
reed@google.comac6b9792011-03-11 15:42:51 +00001569
1570 HDC hdc = ::CreateCompatibleDC(NULL);
reed@google.com59d2f632011-05-02 19:36:59 +00001571 HFONT font = CreateFontIndirect(&lf);
reed@google.comac6b9792011-03-11 15:42:51 +00001572 HFONT savefont = (HFONT)SelectObject(hdc, font);
1573
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001574 SkMemoryStream* stream = NULL;
1575 DWORD tables[2] = {kTTCTag, 0};
1576 for (int i = 0; i < SK_ARRAY_COUNT(tables); i++) {
1577 size_t bufferSize = GetFontData(hdc, tables[i], 0, NULL, 0);
bungeman@google.com39698b12011-11-15 22:26:41 +00001578 if (bufferSize == GDI_ERROR) {
1579 ensure_typeface_accessible(uniqueID);
1580 bufferSize = GetFontData(hdc, tables[i], 0, NULL, 0);
1581 }
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001582 if (bufferSize != GDI_ERROR) {
1583 stream = new SkMemoryStream(bufferSize);
1584 if (GetFontData(hdc, tables[i], 0, (void*)stream->getMemoryBase(),
1585 bufferSize)) {
1586 break;
1587 } else {
1588 delete stream;
1589 stream = NULL;
1590 }
1591 }
reed@google.comac6b9792011-03-11 15:42:51 +00001592 }
1593
1594 SelectObject(hdc, savefont);
1595 DeleteObject(font);
1596 DeleteDC(hdc);
1597
1598 return stream;
1599}
1600
1601SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
1602 return SkNEW_ARGS(SkScalerContext_Windows, (desc));
1603}
1604
1605/** Return the closest matching typeface given either an existing family
1606 (specified by a typeface in that family) or by a familyName, and a
1607 requested style.
bungeman@google.com90d812b2011-10-24 21:25:01 +00001608 1) If familyFace is null, use familyName.
1609 2) If familyName is null, use familyFace.
reed@google.comac6b9792011-03-11 15:42:51 +00001610 3) If both are null, return the default font that best matches style
1611 This MUST not return NULL.
1612 */
1613
1614SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
1615 const char familyName[],
reed@google.comac6b9792011-03-11 15:42:51 +00001616 SkTypeface::Style style) {
reed@google.com59d2f632011-05-02 19:36:59 +00001617 LOGFONT lf;
reed@google.comac6b9792011-03-11 15:42:51 +00001618 if (NULL == familyFace && NULL == familyName) {
reed@google.com59d2f632011-05-02 19:36:59 +00001619 lf = get_default_font();
1620 } else if (familyFace) {
1621 LogFontTypeface* face = (LogFontTypeface*)familyFace;
1622 lf = face->fLogFont;
reed@google.comac6b9792011-03-11 15:42:51 +00001623 } else {
bungeman@google.coma5501992012-05-18 19:06:41 +00001624 logfont_for_name(familyName, lf);
reed@google.comac6b9792011-03-11 15:42:51 +00001625 }
reed@google.com59d2f632011-05-02 19:36:59 +00001626 setStyle(&lf, style);
1627 return SkCreateTypefaceFromLOGFONT(lf);
reed@google.comac6b9792011-03-11 15:42:51 +00001628}
1629
reed@google.comac6b9792011-03-11 15:42:51 +00001630SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
1631 printf("SkFontHost::CreateTypefaceFromFile unimplemented");
1632 return NULL;
1633}
1634
1635void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
reed@google.come8fab012011-07-13 15:25:33 +00001636 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
1637 SkScalerContext::kAutohinting_Flag |
1638 SkScalerContext::kEmbeddedBitmapText_Flag |
1639 SkScalerContext::kEmbolden_Flag |
bungeman@google.comc84547a2012-01-05 20:18:06 +00001640 SkScalerContext::kSubpixelPositioning_Flag |
reed@google.come8fab012011-07-13 15:25:33 +00001641 SkScalerContext::kLCD_BGROrder_Flag |
1642 SkScalerContext::kLCD_Vertical_Flag;
1643 rec->fFlags &= ~flagsWeDontSupport;
1644
reed@google.come8fab012011-07-13 15:25:33 +00001645 SkPaint::Hinting h = rec->getHinting();
reed@google.comda440672011-07-13 18:02:28 +00001646
1647 // I think we can support no-hinting, if we get hires outlines and just
1648 // use skia to rasterize into a gray-scale mask...
1649#if 0
reed@google.come8fab012011-07-13 15:25:33 +00001650 switch (h) {
1651 case SkPaint::kNo_Hinting:
1652 case SkPaint::kSlight_Hinting:
1653 h = SkPaint::kNo_Hinting;
1654 break;
1655 case SkPaint::kNormal_Hinting:
1656 case SkPaint::kFull_Hinting:
1657 h = SkPaint::kNormal_Hinting;
1658 break;
1659 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001660 SkDEBUGFAIL("unknown hinting");
reed@google.come8fab012011-07-13 15:25:33 +00001661 }
reed@google.comda440672011-07-13 18:02:28 +00001662#else
1663 h = SkPaint::kNormal_Hinting;
1664#endif
reed@google.come8fab012011-07-13 15:25:33 +00001665 rec->setHinting(h);
reed@google.comda440672011-07-13 18:02:28 +00001666
reed@google.comffe49f52011-11-22 19:42:41 +00001667 // for compatibility at the moment, discretize luminance to 3 settings
1668 // black, white, gray. This helps with fontcache utilization, since we
1669 // won't create multiple entries that in the end map to the same results.
1670 {
1671 unsigned lum = rec->getLuminanceByte();
1672 if (lum <= BLACK_LUMINANCE_LIMIT) {
1673 lum = 0;
1674 } else if (lum >= WHITE_LUMINANCE_LIMIT) {
1675 lum = SkScalerContext::kLuminance_Max;
1676 } else {
1677 lum = SkScalerContext::kLuminance_Max >> 1;
1678 }
1679 rec->setLuminanceBits(lum);
1680 }
1681
reed@google.com9181aa82011-08-05 14:28:31 +00001682// turn this off since GDI might turn A8 into BW! Need a bigger fix.
1683#if 0
reed@google.comda440672011-07-13 18:02:28 +00001684 // Disable LCD when rotated, since GDI's output is ugly
1685 if (isLCD(*rec) && !isAxisAligned(*rec)) {
1686 rec->fMaskFormat = SkMask::kA8_Format;
1687 }
reed@google.com9181aa82011-08-05 14:28:31 +00001688#endif
reed@google.com754e4eb2011-09-26 13:21:39 +00001689
1690#if 0
1691 if (SkMask::kLCD16_Format == rec->fMaskFormat) {
1692 rec->fMaskFormat = SkMask::kLCD32_Format;
1693 }
1694#endif
reed@google.com754e4eb2011-09-26 13:21:39 +00001695}