gfx/skia/trunk/src/ports/SkFontHost_win.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rwxr-xr-x

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1
michael@0 2 /*
michael@0 3 * Copyright 2006 The Android Open Source Project
michael@0 4 *
michael@0 5 * Use of this source code is governed by a BSD-style license that can be
michael@0 6 * found in the LICENSE file.
michael@0 7 */
michael@0 8
michael@0 9 #include "SkAdvancedTypefaceMetrics.h"
michael@0 10 #include "SkBase64.h"
michael@0 11 #include "SkColorPriv.h"
michael@0 12 #include "SkData.h"
michael@0 13 #include "SkDescriptor.h"
michael@0 14 #include "SkFontDescriptor.h"
michael@0 15 #include "SkFontHost.h"
michael@0 16 #include "SkGlyph.h"
michael@0 17 #include "SkHRESULT.h"
michael@0 18 #include "SkMaskGamma.h"
michael@0 19 #include "SkOTTable_maxp.h"
michael@0 20 #include "SkOTTable_name.h"
michael@0 21 #include "SkOTUtils.h"
michael@0 22 #include "SkPath.h"
michael@0 23 #include "SkSFNTHeader.h"
michael@0 24 #include "SkStream.h"
michael@0 25 #include "SkString.h"
michael@0 26 #include "SkTemplates.h"
michael@0 27 #include "SkThread.h"
michael@0 28 #include "SkTypeface_win.h"
michael@0 29 #include "SkTypefaceCache.h"
michael@0 30 #include "SkUtils.h"
michael@0 31
michael@0 32 #include "SkTypes.h"
michael@0 33 #include <tchar.h>
michael@0 34 #include <usp10.h>
michael@0 35 #include <objbase.h>
michael@0 36
michael@0 37 static void (*gEnsureLOGFONTAccessibleProc)(const LOGFONT&);
michael@0 38
michael@0 39 void SkTypeface_SetEnsureLOGFONTAccessibleProc(void (*proc)(const LOGFONT&)) {
michael@0 40 gEnsureLOGFONTAccessibleProc = proc;
michael@0 41 }
michael@0 42
michael@0 43 static void call_ensure_accessible(const LOGFONT& lf) {
michael@0 44 if (gEnsureLOGFONTAccessibleProc) {
michael@0 45 gEnsureLOGFONTAccessibleProc(lf);
michael@0 46 }
michael@0 47 }
michael@0 48
michael@0 49 ///////////////////////////////////////////////////////////////////////////////
michael@0 50
michael@0 51 // always packed xxRRGGBB
michael@0 52 typedef uint32_t SkGdiRGB;
michael@0 53
michael@0 54 // define this in your Makefile or .gyp to enforce AA requests
michael@0 55 // which GDI ignores at small sizes. This flag guarantees AA
michael@0 56 // for rotated text, regardless of GDI's notions.
michael@0 57 //#define SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
michael@0 58
michael@0 59 static bool isLCD(const SkScalerContext::Rec& rec) {
michael@0 60 return SkMask::kLCD16_Format == rec.fMaskFormat ||
michael@0 61 SkMask::kLCD32_Format == rec.fMaskFormat;
michael@0 62 }
michael@0 63
michael@0 64 static bool bothZero(SkScalar a, SkScalar b) {
michael@0 65 return 0 == a && 0 == b;
michael@0 66 }
michael@0 67
michael@0 68 // returns false if there is any non-90-rotation or skew
michael@0 69 static bool isAxisAligned(const SkScalerContext::Rec& rec) {
michael@0 70 return 0 == rec.fPreSkewX &&
michael@0 71 (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
michael@0 72 bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
michael@0 73 }
michael@0 74
michael@0 75 static bool needToRenderWithSkia(const SkScalerContext::Rec& rec) {
michael@0 76 #ifdef SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
michael@0 77 // What we really want to catch is when GDI will ignore the AA request and give
michael@0 78 // us BW instead. Smallish rotated text is one heuristic, so this code is just
michael@0 79 // an approximation. We shouldn't need to do this for larger sizes, but at those
michael@0 80 // sizes, the quality difference gets less and less between our general
michael@0 81 // scanconverter and GDI's.
michael@0 82 if (SkMask::kA8_Format == rec.fMaskFormat && !isAxisAligned(rec)) {
michael@0 83 return true;
michael@0 84 }
michael@0 85 #endif
michael@0 86 return rec.getHinting() == SkPaint::kNo_Hinting || rec.getHinting() == SkPaint::kSlight_Hinting;
michael@0 87 }
michael@0 88
michael@0 89 using namespace skia_advanced_typeface_metrics_utils;
michael@0 90
michael@0 91 static void tchar_to_skstring(const TCHAR t[], SkString* s) {
michael@0 92 #ifdef UNICODE
michael@0 93 size_t sSize = WideCharToMultiByte(CP_UTF8, 0, t, -1, NULL, 0, NULL, NULL);
michael@0 94 s->resize(sSize);
michael@0 95 WideCharToMultiByte(CP_UTF8, 0, t, -1, s->writable_str(), sSize, NULL, NULL);
michael@0 96 #else
michael@0 97 s->set(t);
michael@0 98 #endif
michael@0 99 }
michael@0 100
michael@0 101 static void dcfontname_to_skstring(HDC deviceContext, const LOGFONT& lf, SkString* familyName) {
michael@0 102 int fontNameLen; //length of fontName in TCHARS.
michael@0 103 if (0 == (fontNameLen = GetTextFace(deviceContext, 0, NULL))) {
michael@0 104 call_ensure_accessible(lf);
michael@0 105 if (0 == (fontNameLen = GetTextFace(deviceContext, 0, NULL))) {
michael@0 106 fontNameLen = 0;
michael@0 107 }
michael@0 108 }
michael@0 109
michael@0 110 SkAutoSTArray<LF_FULLFACESIZE, TCHAR> fontName(fontNameLen+1);
michael@0 111 if (0 == GetTextFace(deviceContext, fontNameLen, fontName.get())) {
michael@0 112 call_ensure_accessible(lf);
michael@0 113 if (0 == GetTextFace(deviceContext, fontNameLen, fontName.get())) {
michael@0 114 fontName[0] = 0;
michael@0 115 }
michael@0 116 }
michael@0 117
michael@0 118 tchar_to_skstring(fontName.get(), familyName);
michael@0 119 }
michael@0 120
michael@0 121 static void make_canonical(LOGFONT* lf) {
michael@0 122 lf->lfHeight = -64;
michael@0 123 lf->lfQuality = CLEARTYPE_QUALITY;//PROOF_QUALITY;
michael@0 124 lf->lfCharSet = DEFAULT_CHARSET;
michael@0 125 // lf->lfClipPrecision = 64;
michael@0 126 }
michael@0 127
michael@0 128 static SkTypeface::Style get_style(const LOGFONT& lf) {
michael@0 129 unsigned style = 0;
michael@0 130 if (lf.lfWeight >= FW_BOLD) {
michael@0 131 style |= SkTypeface::kBold;
michael@0 132 }
michael@0 133 if (lf.lfItalic) {
michael@0 134 style |= SkTypeface::kItalic;
michael@0 135 }
michael@0 136 return static_cast<SkTypeface::Style>(style);
michael@0 137 }
michael@0 138
michael@0 139 static void setStyle(LOGFONT* lf, SkTypeface::Style style) {
michael@0 140 lf->lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL ;
michael@0 141 lf->lfItalic = ((style & SkTypeface::kItalic) != 0);
michael@0 142 }
michael@0 143
michael@0 144 static inline FIXED SkFixedToFIXED(SkFixed x) {
michael@0 145 return *(FIXED*)(&x);
michael@0 146 }
michael@0 147 static inline SkFixed SkFIXEDToFixed(FIXED x) {
michael@0 148 return *(SkFixed*)(&x);
michael@0 149 }
michael@0 150
michael@0 151 static inline FIXED SkScalarToFIXED(SkScalar x) {
michael@0 152 return SkFixedToFIXED(SkScalarToFixed(x));
michael@0 153 }
michael@0 154
michael@0 155 static inline SkScalar SkFIXEDToScalar(FIXED x) {
michael@0 156 return SkFixedToScalar(SkFIXEDToFixed(x));
michael@0 157 }
michael@0 158
michael@0 159 static unsigned calculateGlyphCount(HDC hdc, const LOGFONT& lf) {
michael@0 160 TEXTMETRIC textMetric;
michael@0 161 if (0 == GetTextMetrics(hdc, &textMetric)) {
michael@0 162 textMetric.tmPitchAndFamily = TMPF_VECTOR;
michael@0 163 call_ensure_accessible(lf);
michael@0 164 GetTextMetrics(hdc, &textMetric);
michael@0 165 }
michael@0 166
michael@0 167 if (!(textMetric.tmPitchAndFamily & TMPF_VECTOR)) {
michael@0 168 return textMetric.tmLastChar;
michael@0 169 }
michael@0 170
michael@0 171 // The 'maxp' table stores the number of glyphs at offset 4, in 2 bytes.
michael@0 172 uint16_t glyphs;
michael@0 173 if (GDI_ERROR != GetFontData(hdc, SkOTTableMaximumProfile::TAG, 4, &glyphs, sizeof(glyphs))) {
michael@0 174 return SkEndian_SwapBE16(glyphs);
michael@0 175 }
michael@0 176
michael@0 177 // Binary search for glyph count.
michael@0 178 static const MAT2 mat2 = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
michael@0 179 int32_t max = SK_MaxU16 + 1;
michael@0 180 int32_t min = 0;
michael@0 181 GLYPHMETRICS gm;
michael@0 182 while (min < max) {
michael@0 183 int32_t mid = min + ((max - min) / 2);
michael@0 184 if (GetGlyphOutlineW(hdc, mid, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0,
michael@0 185 NULL, &mat2) == GDI_ERROR) {
michael@0 186 max = mid;
michael@0 187 } else {
michael@0 188 min = mid + 1;
michael@0 189 }
michael@0 190 }
michael@0 191 SkASSERT(min == max);
michael@0 192 return min;
michael@0 193 }
michael@0 194
michael@0 195 static unsigned calculateUPEM(HDC hdc, const LOGFONT& lf) {
michael@0 196 TEXTMETRIC textMetric;
michael@0 197 if (0 == GetTextMetrics(hdc, &textMetric)) {
michael@0 198 textMetric.tmPitchAndFamily = TMPF_VECTOR;
michael@0 199 call_ensure_accessible(lf);
michael@0 200 GetTextMetrics(hdc, &textMetric);
michael@0 201 }
michael@0 202
michael@0 203 if (!(textMetric.tmPitchAndFamily & TMPF_VECTOR)) {
michael@0 204 return textMetric.tmMaxCharWidth;
michael@0 205 }
michael@0 206
michael@0 207 OUTLINETEXTMETRIC otm;
michael@0 208 unsigned int otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
michael@0 209 if (0 == otmRet) {
michael@0 210 call_ensure_accessible(lf);
michael@0 211 otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
michael@0 212 }
michael@0 213
michael@0 214 return (0 == otmRet) ? 0 : otm.otmEMSquare;
michael@0 215 }
michael@0 216
michael@0 217 class LogFontTypeface : public SkTypeface {
michael@0 218 public:
michael@0 219 LogFontTypeface(SkTypeface::Style style, SkFontID fontID, const LOGFONT& lf, bool serializeAsStream = false) :
michael@0 220 SkTypeface(style, fontID, false), fLogFont(lf), fSerializeAsStream(serializeAsStream) {
michael@0 221
michael@0 222 // If the font has cubic outlines, it will not be rendered with ClearType.
michael@0 223 HFONT font = CreateFontIndirect(&lf);
michael@0 224
michael@0 225 HDC deviceContext = ::CreateCompatibleDC(NULL);
michael@0 226 HFONT savefont = (HFONT)SelectObject(deviceContext, font);
michael@0 227
michael@0 228 TEXTMETRIC textMetric;
michael@0 229 if (0 == GetTextMetrics(deviceContext, &textMetric)) {
michael@0 230 call_ensure_accessible(lf);
michael@0 231 if (0 == GetTextMetrics(deviceContext, &textMetric)) {
michael@0 232 textMetric.tmPitchAndFamily = TMPF_TRUETYPE;
michael@0 233 }
michael@0 234 }
michael@0 235 if (deviceContext) {
michael@0 236 ::SelectObject(deviceContext, savefont);
michael@0 237 ::DeleteDC(deviceContext);
michael@0 238 }
michael@0 239 if (font) {
michael@0 240 ::DeleteObject(font);
michael@0 241 }
michael@0 242
michael@0 243 // The fixed pitch bit is set if the font is *not* fixed pitch.
michael@0 244 this->setIsFixedPitch((textMetric.tmPitchAndFamily & TMPF_FIXED_PITCH) == 0);
michael@0 245
michael@0 246 // Used a logfont on a memory context, should never get a device font.
michael@0 247 // Therefore all TMPF_DEVICE will be PostScript (cubic) fonts.
michael@0 248 fCanBeLCD = !((textMetric.tmPitchAndFamily & TMPF_VECTOR) &&
michael@0 249 (textMetric.tmPitchAndFamily & TMPF_DEVICE));
michael@0 250 }
michael@0 251
michael@0 252 LOGFONT fLogFont;
michael@0 253 bool fSerializeAsStream;
michael@0 254 bool fCanBeLCD;
michael@0 255
michael@0 256 static LogFontTypeface* Create(const LOGFONT& lf) {
michael@0 257 SkTypeface::Style style = get_style(lf);
michael@0 258 SkFontID fontID = SkTypefaceCache::NewFontID();
michael@0 259 return new LogFontTypeface(style, fontID, lf);
michael@0 260 }
michael@0 261
michael@0 262 static void EnsureAccessible(const SkTypeface* face) {
michael@0 263 call_ensure_accessible(static_cast<const LogFontTypeface*>(face)->fLogFont);
michael@0 264 }
michael@0 265
michael@0 266 protected:
michael@0 267 virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE;
michael@0 268 virtual SkScalerContext* onCreateScalerContext(const SkDescriptor*) const SK_OVERRIDE;
michael@0 269 virtual void onFilterRec(SkScalerContextRec*) const SK_OVERRIDE;
michael@0 270 virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
michael@0 271 SkAdvancedTypefaceMetrics::PerGlyphInfo,
michael@0 272 const uint32_t*, uint32_t) const SK_OVERRIDE;
michael@0 273 virtual void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE;
michael@0 274 virtual int onCharsToGlyphs(const void* chars, Encoding encoding,
michael@0 275 uint16_t glyphs[], int glyphCount) const SK_OVERRIDE;
michael@0 276 virtual int onCountGlyphs() const SK_OVERRIDE;
michael@0 277 virtual int onGetUPEM() const SK_OVERRIDE;
michael@0 278 virtual SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const SK_OVERRIDE;
michael@0 279 virtual int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
michael@0 280 virtual size_t onGetTableData(SkFontTableTag, size_t offset,
michael@0 281 size_t length, void* data) const SK_OVERRIDE;
michael@0 282 };
michael@0 283
michael@0 284 class FontMemResourceTypeface : public LogFontTypeface {
michael@0 285 public:
michael@0 286 /**
michael@0 287 * Takes ownership of fontMemResource.
michael@0 288 */
michael@0 289 FontMemResourceTypeface(SkTypeface::Style style, SkFontID fontID, const LOGFONT& lf, HANDLE fontMemResource) :
michael@0 290 LogFontTypeface(style, fontID, lf, true), fFontMemResource(fontMemResource) {
michael@0 291 }
michael@0 292
michael@0 293 HANDLE fFontMemResource;
michael@0 294
michael@0 295 /**
michael@0 296 * The created FontMemResourceTypeface takes ownership of fontMemResource.
michael@0 297 */
michael@0 298 static FontMemResourceTypeface* Create(const LOGFONT& lf, HANDLE fontMemResource) {
michael@0 299 SkTypeface::Style style = get_style(lf);
michael@0 300 SkFontID fontID = SkTypefaceCache::NewFontID();
michael@0 301 return new FontMemResourceTypeface(style, fontID, lf, fontMemResource);
michael@0 302 }
michael@0 303
michael@0 304 protected:
michael@0 305 virtual void weak_dispose() const SK_OVERRIDE {
michael@0 306 RemoveFontMemResourceEx(fFontMemResource);
michael@0 307 //SkTypefaceCache::Remove(this);
michael@0 308 INHERITED::weak_dispose();
michael@0 309 }
michael@0 310
michael@0 311 private:
michael@0 312 typedef LogFontTypeface INHERITED;
michael@0 313 };
michael@0 314
michael@0 315 static const LOGFONT& get_default_font() {
michael@0 316 static LOGFONT gDefaultFont;
michael@0 317 return gDefaultFont;
michael@0 318 }
michael@0 319
michael@0 320 static bool FindByLogFont(SkTypeface* face, SkTypeface::Style requestedStyle, void* ctx) {
michael@0 321 LogFontTypeface* lface = static_cast<LogFontTypeface*>(face);
michael@0 322 const LOGFONT* lf = reinterpret_cast<const LOGFONT*>(ctx);
michael@0 323
michael@0 324 return lface &&
michael@0 325 get_style(lface->fLogFont) == requestedStyle &&
michael@0 326 !memcmp(&lface->fLogFont, lf, sizeof(LOGFONT));
michael@0 327 }
michael@0 328
michael@0 329 /**
michael@0 330 * This guy is public. It first searches the cache, and if a match is not found,
michael@0 331 * it creates a new face.
michael@0 332 */
michael@0 333 SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& origLF) {
michael@0 334 LOGFONT lf = origLF;
michael@0 335 make_canonical(&lf);
michael@0 336 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByLogFont, &lf);
michael@0 337 if (NULL == face) {
michael@0 338 face = LogFontTypeface::Create(lf);
michael@0 339 SkTypefaceCache::Add(face, get_style(lf));
michael@0 340 }
michael@0 341 return face;
michael@0 342 }
michael@0 343
michael@0 344 /**
michael@0 345 * The created SkTypeface takes ownership of fontMemResource.
michael@0 346 */
michael@0 347 SkTypeface* SkCreateFontMemResourceTypefaceFromLOGFONT(const LOGFONT& origLF, HANDLE fontMemResource) {
michael@0 348 LOGFONT lf = origLF;
michael@0 349 make_canonical(&lf);
michael@0 350 FontMemResourceTypeface* face = FontMemResourceTypeface::Create(lf, fontMemResource);
michael@0 351 SkTypefaceCache::Add(face, get_style(lf), false);
michael@0 352 return face;
michael@0 353 }
michael@0 354
michael@0 355 /**
michael@0 356 * This guy is public
michael@0 357 */
michael@0 358 void SkLOGFONTFromTypeface(const SkTypeface* face, LOGFONT* lf) {
michael@0 359 if (NULL == face) {
michael@0 360 *lf = get_default_font();
michael@0 361 } else {
michael@0 362 *lf = static_cast<const LogFontTypeface*>(face)->fLogFont;
michael@0 363 }
michael@0 364 }
michael@0 365
michael@0 366 // Construct Glyph to Unicode table.
michael@0 367 // Unicode code points that require conjugate pairs in utf16 are not
michael@0 368 // supported.
michael@0 369 // TODO(arthurhsu): Add support for conjugate pairs. It looks like that may
michael@0 370 // require parsing the TTF cmap table (platform 4, encoding 12) directly instead
michael@0 371 // of calling GetFontUnicodeRange().
michael@0 372 static void populate_glyph_to_unicode(HDC fontHdc, const unsigned glyphCount,
michael@0 373 SkTDArray<SkUnichar>* glyphToUnicode) {
michael@0 374 DWORD glyphSetBufferSize = GetFontUnicodeRanges(fontHdc, NULL);
michael@0 375 if (!glyphSetBufferSize) {
michael@0 376 return;
michael@0 377 }
michael@0 378
michael@0 379 SkAutoTDeleteArray<BYTE> glyphSetBuffer(new BYTE[glyphSetBufferSize]);
michael@0 380 GLYPHSET* glyphSet =
michael@0 381 reinterpret_cast<LPGLYPHSET>(glyphSetBuffer.get());
michael@0 382 if (GetFontUnicodeRanges(fontHdc, glyphSet) != glyphSetBufferSize) {
michael@0 383 return;
michael@0 384 }
michael@0 385
michael@0 386 glyphToUnicode->setCount(glyphCount);
michael@0 387 memset(glyphToUnicode->begin(), 0, glyphCount * sizeof(SkUnichar));
michael@0 388 for (DWORD i = 0; i < glyphSet->cRanges; ++i) {
michael@0 389 // There is no guarantee that within a Unicode range, the corresponding
michael@0 390 // glyph id in a font file are continuous. So, even if we have ranges,
michael@0 391 // we can't just use the first and last entry of the range to compute
michael@0 392 // result. We need to enumerate them one by one.
michael@0 393 int count = glyphSet->ranges[i].cGlyphs;
michael@0 394 SkAutoTArray<WCHAR> chars(count + 1);
michael@0 395 chars[count] = 0; // termintate string
michael@0 396 SkAutoTArray<WORD> glyph(count);
michael@0 397 for (USHORT j = 0; j < count; ++j) {
michael@0 398 chars[j] = glyphSet->ranges[i].wcLow + j;
michael@0 399 }
michael@0 400 GetGlyphIndicesW(fontHdc, chars.get(), count, glyph.get(),
michael@0 401 GGI_MARK_NONEXISTING_GLYPHS);
michael@0 402 // If the glyph ID is valid, and the glyph is not mapped, then we will
michael@0 403 // fill in the char id into the vector. If the glyph is mapped already,
michael@0 404 // skip it.
michael@0 405 // TODO(arthurhsu): better improve this. e.g. Get all used char ids from
michael@0 406 // font cache, then generate this mapping table from there. It's
michael@0 407 // unlikely to have collisions since glyph reuse happens mostly for
michael@0 408 // different Unicode pages.
michael@0 409 for (USHORT j = 0; j < count; ++j) {
michael@0 410 if (glyph[j] != 0xffff && glyph[j] < glyphCount &&
michael@0 411 (*glyphToUnicode)[glyph[j]] == 0) {
michael@0 412 (*glyphToUnicode)[glyph[j]] = chars[j];
michael@0 413 }
michael@0 414 }
michael@0 415 }
michael@0 416 }
michael@0 417
michael@0 418 //////////////////////////////////////////////////////////////////////////////////////
michael@0 419
michael@0 420 static int alignTo32(int n) {
michael@0 421 return (n + 31) & ~31;
michael@0 422 }
michael@0 423
michael@0 424 struct MyBitmapInfo : public BITMAPINFO {
michael@0 425 RGBQUAD fMoreSpaceForColors[1];
michael@0 426 };
michael@0 427
michael@0 428 class HDCOffscreen {
michael@0 429 public:
michael@0 430 HDCOffscreen() {
michael@0 431 fFont = 0;
michael@0 432 fDC = 0;
michael@0 433 fBM = 0;
michael@0 434 fBits = NULL;
michael@0 435 fWidth = fHeight = 0;
michael@0 436 fIsBW = false;
michael@0 437 }
michael@0 438
michael@0 439 ~HDCOffscreen() {
michael@0 440 if (fDC) {
michael@0 441 DeleteDC(fDC);
michael@0 442 }
michael@0 443 if (fBM) {
michael@0 444 DeleteObject(fBM);
michael@0 445 }
michael@0 446 }
michael@0 447
michael@0 448 void init(HFONT font, const XFORM& xform) {
michael@0 449 fFont = font;
michael@0 450 fXform = xform;
michael@0 451 }
michael@0 452
michael@0 453 const void* draw(const SkGlyph&, bool isBW, size_t* srcRBPtr);
michael@0 454
michael@0 455 private:
michael@0 456 HDC fDC;
michael@0 457 HBITMAP fBM;
michael@0 458 HFONT fFont;
michael@0 459 XFORM fXform;
michael@0 460 void* fBits; // points into fBM
michael@0 461 int fWidth;
michael@0 462 int fHeight;
michael@0 463 bool fIsBW;
michael@0 464 };
michael@0 465
michael@0 466 const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW,
michael@0 467 size_t* srcRBPtr) {
michael@0 468 // Can we share the scalercontext's fDDC, so we don't need to create
michael@0 469 // a separate fDC here?
michael@0 470 if (0 == fDC) {
michael@0 471 fDC = CreateCompatibleDC(0);
michael@0 472 if (0 == fDC) {
michael@0 473 return NULL;
michael@0 474 }
michael@0 475 SetGraphicsMode(fDC, GM_ADVANCED);
michael@0 476 SetBkMode(fDC, TRANSPARENT);
michael@0 477 SetTextAlign(fDC, TA_LEFT | TA_BASELINE);
michael@0 478 SelectObject(fDC, fFont);
michael@0 479
michael@0 480 COLORREF color = 0x00FFFFFF;
michael@0 481 SkDEBUGCODE(COLORREF prev =) SetTextColor(fDC, color);
michael@0 482 SkASSERT(prev != CLR_INVALID);
michael@0 483 }
michael@0 484
michael@0 485 if (fBM && (fIsBW != isBW || fWidth < glyph.fWidth || fHeight < glyph.fHeight)) {
michael@0 486 DeleteObject(fBM);
michael@0 487 fBM = 0;
michael@0 488 }
michael@0 489 fIsBW = isBW;
michael@0 490
michael@0 491 fWidth = SkMax32(fWidth, glyph.fWidth);
michael@0 492 fHeight = SkMax32(fHeight, glyph.fHeight);
michael@0 493
michael@0 494 int biWidth = isBW ? alignTo32(fWidth) : fWidth;
michael@0 495
michael@0 496 if (0 == fBM) {
michael@0 497 MyBitmapInfo info;
michael@0 498 sk_bzero(&info, sizeof(info));
michael@0 499 if (isBW) {
michael@0 500 RGBQUAD blackQuad = { 0, 0, 0, 0 };
michael@0 501 RGBQUAD whiteQuad = { 0xFF, 0xFF, 0xFF, 0 };
michael@0 502 info.bmiColors[0] = blackQuad;
michael@0 503 info.bmiColors[1] = whiteQuad;
michael@0 504 }
michael@0 505 info.bmiHeader.biSize = sizeof(info.bmiHeader);
michael@0 506 info.bmiHeader.biWidth = biWidth;
michael@0 507 info.bmiHeader.biHeight = fHeight;
michael@0 508 info.bmiHeader.biPlanes = 1;
michael@0 509 info.bmiHeader.biBitCount = isBW ? 1 : 32;
michael@0 510 info.bmiHeader.biCompression = BI_RGB;
michael@0 511 if (isBW) {
michael@0 512 info.bmiHeader.biClrUsed = 2;
michael@0 513 }
michael@0 514 fBM = CreateDIBSection(fDC, &info, DIB_RGB_COLORS, &fBits, 0, 0);
michael@0 515 if (0 == fBM) {
michael@0 516 return NULL;
michael@0 517 }
michael@0 518 SelectObject(fDC, fBM);
michael@0 519 }
michael@0 520
michael@0 521 // erase
michael@0 522 size_t srcRB = isBW ? (biWidth >> 3) : (fWidth << 2);
michael@0 523 size_t size = fHeight * srcRB;
michael@0 524 memset(fBits, 0, size);
michael@0 525
michael@0 526 XFORM xform = fXform;
michael@0 527 xform.eDx = (float)-glyph.fLeft;
michael@0 528 xform.eDy = (float)-glyph.fTop;
michael@0 529 SetWorldTransform(fDC, &xform);
michael@0 530
michael@0 531 uint16_t glyphID = glyph.getGlyphID();
michael@0 532 BOOL ret = ExtTextOutW(fDC, 0, 0, ETO_GLYPH_INDEX, NULL, reinterpret_cast<LPCWSTR>(&glyphID), 1, NULL);
michael@0 533 GdiFlush();
michael@0 534 if (0 == ret) {
michael@0 535 return NULL;
michael@0 536 }
michael@0 537 *srcRBPtr = srcRB;
michael@0 538 // offset to the start of the image
michael@0 539 return (const char*)fBits + (fHeight - glyph.fHeight) * srcRB;
michael@0 540 }
michael@0 541
michael@0 542 //////////////////////////////////////////////////////////////////////////////
michael@0 543 #define BUFFERSIZE (1 << 13)
michael@0 544
michael@0 545 class SkScalerContext_GDI : public SkScalerContext {
michael@0 546 public:
michael@0 547 SkScalerContext_GDI(SkTypeface*, const SkDescriptor* desc);
michael@0 548 virtual ~SkScalerContext_GDI();
michael@0 549
michael@0 550 // Returns true if the constructor was able to complete all of its
michael@0 551 // initializations (which may include calling GDI).
michael@0 552 bool isValid() const;
michael@0 553
michael@0 554 protected:
michael@0 555 virtual unsigned generateGlyphCount() SK_OVERRIDE;
michael@0 556 virtual uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
michael@0 557 virtual void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
michael@0 558 virtual void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
michael@0 559 virtual void generateImage(const SkGlyph& glyph) SK_OVERRIDE;
michael@0 560 virtual void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
michael@0 561 virtual void generateFontMetrics(SkPaint::FontMetrics* mX,
michael@0 562 SkPaint::FontMetrics* mY) SK_OVERRIDE;
michael@0 563
michael@0 564 private:
michael@0 565 DWORD getGDIGlyphPath(const SkGlyph& glyph, UINT flags,
michael@0 566 SkAutoSTMalloc<BUFFERSIZE, uint8_t>* glyphbuf);
michael@0 567
michael@0 568 HDCOffscreen fOffscreen;
michael@0 569 /** fGsA is the non-rotational part of total matrix without the text height scale.
michael@0 570 * Used to find the magnitude of advances.
michael@0 571 */
michael@0 572 MAT2 fGsA;
michael@0 573 /** The total matrix without the textSize. */
michael@0 574 MAT2 fMat22;
michael@0 575 /** Scales font to EM size. */
michael@0 576 MAT2 fHighResMat22;
michael@0 577 HDC fDDC;
michael@0 578 HFONT fSavefont;
michael@0 579 HFONT fFont;
michael@0 580 SCRIPT_CACHE fSC;
michael@0 581 int fGlyphCount;
michael@0 582
michael@0 583 /** The total matrix which also removes EM scale. */
michael@0 584 SkMatrix fHiResMatrix;
michael@0 585 /** fG_inv is the inverse of the rotational part of the total matrix.
michael@0 586 * Used to set the direction of advances.
michael@0 587 */
michael@0 588 SkMatrix fG_inv;
michael@0 589 enum Type {
michael@0 590 kTrueType_Type, kBitmap_Type, kLine_Type
michael@0 591 } fType;
michael@0 592 TEXTMETRIC fTM;
michael@0 593 };
michael@0 594
michael@0 595 static FIXED float2FIXED(float x) {
michael@0 596 return SkFixedToFIXED(SkFloatToFixed(x));
michael@0 597 }
michael@0 598
michael@0 599 static BYTE compute_quality(const SkScalerContext::Rec& rec) {
michael@0 600 switch (rec.fMaskFormat) {
michael@0 601 case SkMask::kBW_Format:
michael@0 602 return NONANTIALIASED_QUALITY;
michael@0 603 case SkMask::kLCD16_Format:
michael@0 604 case SkMask::kLCD32_Format:
michael@0 605 return CLEARTYPE_QUALITY;
michael@0 606 default:
michael@0 607 if (rec.fFlags & SkScalerContext::kGenA8FromLCD_Flag) {
michael@0 608 return CLEARTYPE_QUALITY;
michael@0 609 } else {
michael@0 610 return ANTIALIASED_QUALITY;
michael@0 611 }
michael@0 612 }
michael@0 613 }
michael@0 614
michael@0 615 SkScalerContext_GDI::SkScalerContext_GDI(SkTypeface* rawTypeface,
michael@0 616 const SkDescriptor* desc)
michael@0 617 : SkScalerContext(rawTypeface, desc)
michael@0 618 , fDDC(0)
michael@0 619 , fSavefont(0)
michael@0 620 , fFont(0)
michael@0 621 , fSC(0)
michael@0 622 , fGlyphCount(-1)
michael@0 623 {
michael@0 624 LogFontTypeface* typeface = reinterpret_cast<LogFontTypeface*>(rawTypeface);
michael@0 625
michael@0 626 fDDC = ::CreateCompatibleDC(NULL);
michael@0 627 if (!fDDC) {
michael@0 628 return;
michael@0 629 }
michael@0 630 SetGraphicsMode(fDDC, GM_ADVANCED);
michael@0 631 SetBkMode(fDDC, TRANSPARENT);
michael@0 632
michael@0 633 SkPoint h = SkPoint::Make(SK_Scalar1, 0);
michael@0 634 // A is the total matrix.
michael@0 635 SkMatrix A;
michael@0 636 fRec.getSingleMatrix(&A);
michael@0 637 A.mapPoints(&h, 1);
michael@0 638
michael@0 639 // Find the Given's matrix [[c, -s],[s, c]] which rotates the baseline vector h
michael@0 640 // (where the baseline is mapped to) to the positive horizontal axis.
michael@0 641 const SkScalar& a = h.fX;
michael@0 642 const SkScalar& b = h.fY;
michael@0 643 SkScalar c, s;
michael@0 644 if (0 == b) {
michael@0 645 c = SkDoubleToScalar(_copysign(SK_Scalar1, a));
michael@0 646 s = 0;
michael@0 647 } else if (0 == a) {
michael@0 648 c = 0;
michael@0 649 s = SkDoubleToScalar(-_copysign(SK_Scalar1, b));
michael@0 650 } else if (SkScalarAbs(b) > SkScalarAbs(a)) {
michael@0 651 SkScalar t = a / b;
michael@0 652 SkScalar u = SkDoubleToScalar(_copysign(SkScalarSqrt(SK_Scalar1 + t*t), b));
michael@0 653 s = -1 / u;
michael@0 654 c = -s * t;
michael@0 655 } else {
michael@0 656 SkScalar t = b / a;
michael@0 657 SkScalar u = SkDoubleToScalar(_copysign(SkScalarSqrt(SK_Scalar1 + t*t), a));
michael@0 658 c = 1 / u;
michael@0 659 s = -c * t;
michael@0 660 }
michael@0 661
michael@0 662 // G is the Given's Matrix for A (rotational matrix such that GA[0][1] == 0).
michael@0 663 SkMatrix G;
michael@0 664 G.setAll(c, -s, 0,
michael@0 665 s, c, 0,
michael@0 666 0, 0, SkScalarToPersp(SK_Scalar1));
michael@0 667
michael@0 668 // GA is the matrix A with rotation removed.
michael@0 669 SkMatrix GA(G);
michael@0 670 GA.preConcat(A);
michael@0 671
michael@0 672 // realTextSize is the actual device size we want (as opposed to the size the user requested).
michael@0 673 // gdiTextSide is the size we request from GDI.
michael@0 674 // If the scale is negative, this means the matrix will do the flip anyway.
michael@0 675 SkScalar realTextSize = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
michael@0 676 SkScalar gdiTextSize = SkScalarRoundToScalar(realTextSize);
michael@0 677 if (gdiTextSize == 0) {
michael@0 678 gdiTextSize = SK_Scalar1;
michael@0 679 }
michael@0 680
michael@0 681 // When not hinting, remove only the gdiTextSize scale which will be applied by GDI.
michael@0 682 // When GDI hinting, remove the entire Y scale to prevent 'subpixel' metrics.
michael@0 683 SkScalar scale = (fRec.getHinting() == SkPaint::kNo_Hinting ||
michael@0 684 fRec.getHinting() == SkPaint::kSlight_Hinting)
michael@0 685 ? SkScalarInvert(gdiTextSize)
michael@0 686 : SkScalarInvert(realTextSize);
michael@0 687
michael@0 688 // sA is the total matrix A without the textSize (so GDI knows the text size separately).
michael@0 689 // When this matrix is used with GetGlyphOutline, no further processing is needed.
michael@0 690 SkMatrix sA(A);
michael@0 691 sA.preScale(scale, scale); //remove text size
michael@0 692
michael@0 693 // GsA is the non-rotational part of A without the text height scale.
michael@0 694 // This is what is used to find the magnitude of advances.
michael@0 695 SkMatrix GsA(GA);
michael@0 696 GsA.preScale(scale, scale); //remove text size, G is rotational so reorders with the scale.
michael@0 697
michael@0 698 fGsA.eM11 = SkScalarToFIXED(GsA.get(SkMatrix::kMScaleX));
michael@0 699 fGsA.eM12 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewY)); // This should be ~0.
michael@0 700 fGsA.eM21 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewX));
michael@0 701 fGsA.eM22 = SkScalarToFIXED(GsA.get(SkMatrix::kMScaleY));
michael@0 702
michael@0 703 // fG_inv is G inverse, which is fairly simple since G is 2x2 rotational.
michael@0 704 fG_inv.setAll(G.get(SkMatrix::kMScaleX), -G.get(SkMatrix::kMSkewX), G.get(SkMatrix::kMTransX),
michael@0 705 -G.get(SkMatrix::kMSkewY), G.get(SkMatrix::kMScaleY), G.get(SkMatrix::kMTransY),
michael@0 706 G.get(SkMatrix::kMPersp0), G.get(SkMatrix::kMPersp1), G.get(SkMatrix::kMPersp2));
michael@0 707
michael@0 708 LOGFONT lf = typeface->fLogFont;
michael@0 709 lf.lfHeight = -SkScalarTruncToInt(gdiTextSize);
michael@0 710 lf.lfQuality = compute_quality(fRec);
michael@0 711 fFont = CreateFontIndirect(&lf);
michael@0 712 if (!fFont) {
michael@0 713 return;
michael@0 714 }
michael@0 715
michael@0 716 fSavefont = (HFONT)SelectObject(fDDC, fFont);
michael@0 717
michael@0 718 if (0 == GetTextMetrics(fDDC, &fTM)) {
michael@0 719 call_ensure_accessible(lf);
michael@0 720 if (0 == GetTextMetrics(fDDC, &fTM)) {
michael@0 721 fTM.tmPitchAndFamily = TMPF_TRUETYPE;
michael@0 722 }
michael@0 723 }
michael@0 724
michael@0 725 XFORM xform;
michael@0 726 if (fTM.tmPitchAndFamily & TMPF_VECTOR) {
michael@0 727 // Used a logfont on a memory context, should never get a device font.
michael@0 728 // Therefore all TMPF_DEVICE will be PostScript fonts.
michael@0 729
michael@0 730 // If TMPF_VECTOR is set, one of TMPF_TRUETYPE or TMPF_DEVICE means that
michael@0 731 // we have an outline font. Otherwise we have a vector FON, which is
michael@0 732 // scalable, but not an outline font.
michael@0 733 // This was determined by testing with Type1 PFM/PFB and
michael@0 734 // OpenTypeCFF OTF, as well as looking at Wine bugs and sources.
michael@0 735 if (fTM.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_DEVICE)) {
michael@0 736 // Truetype or PostScript.
michael@0 737 fType = SkScalerContext_GDI::kTrueType_Type;
michael@0 738 } else {
michael@0 739 // Stroked FON.
michael@0 740 fType = SkScalerContext_GDI::kLine_Type;
michael@0 741 }
michael@0 742
michael@0 743 // fPost2x2 is column-major, left handed (y down).
michael@0 744 // XFORM 2x2 is row-major, left handed (y down).
michael@0 745 xform.eM11 = SkScalarToFloat(sA.get(SkMatrix::kMScaleX));
michael@0 746 xform.eM12 = SkScalarToFloat(sA.get(SkMatrix::kMSkewY));
michael@0 747 xform.eM21 = SkScalarToFloat(sA.get(SkMatrix::kMSkewX));
michael@0 748 xform.eM22 = SkScalarToFloat(sA.get(SkMatrix::kMScaleY));
michael@0 749 xform.eDx = 0;
michael@0 750 xform.eDy = 0;
michael@0 751
michael@0 752 // MAT2 is row major, right handed (y up).
michael@0 753 fMat22.eM11 = float2FIXED(xform.eM11);
michael@0 754 fMat22.eM12 = float2FIXED(-xform.eM12);
michael@0 755 fMat22.eM21 = float2FIXED(-xform.eM21);
michael@0 756 fMat22.eM22 = float2FIXED(xform.eM22);
michael@0 757
michael@0 758 if (needToRenderWithSkia(fRec)) {
michael@0 759 this->forceGenerateImageFromPath();
michael@0 760 }
michael@0 761
michael@0 762 // Create a hires matrix if we need linear metrics.
michael@0 763 if (this->isSubpixel()) {
michael@0 764 OUTLINETEXTMETRIC otm;
michael@0 765 UINT success = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
michael@0 766 if (0 == success) {
michael@0 767 call_ensure_accessible(lf);
michael@0 768 success = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
michael@0 769 }
michael@0 770 if (0 != success) {
michael@0 771 SkScalar upem = SkIntToScalar(otm.otmEMSquare);
michael@0 772
michael@0 773 SkScalar gdiTextSizeToEMScale = upem / gdiTextSize;
michael@0 774 fHighResMat22.eM11 = float2FIXED(gdiTextSizeToEMScale);
michael@0 775 fHighResMat22.eM12 = float2FIXED(0);
michael@0 776 fHighResMat22.eM21 = float2FIXED(0);
michael@0 777 fHighResMat22.eM22 = float2FIXED(gdiTextSizeToEMScale);
michael@0 778
michael@0 779 SkScalar removeEMScale = SkScalarInvert(upem);
michael@0 780 fHiResMatrix = A;
michael@0 781 fHiResMatrix.preScale(removeEMScale, removeEMScale);
michael@0 782 }
michael@0 783 }
michael@0 784
michael@0 785 } else {
michael@0 786 // Assume bitmap
michael@0 787 fType = SkScalerContext_GDI::kBitmap_Type;
michael@0 788
michael@0 789 xform.eM11 = 1.0f;
michael@0 790 xform.eM12 = 0.0f;
michael@0 791 xform.eM21 = 0.0f;
michael@0 792 xform.eM22 = 1.0f;
michael@0 793 xform.eDx = 0.0f;
michael@0 794 xform.eDy = 0.0f;
michael@0 795
michael@0 796 // fPost2x2 is column-major, left handed (y down).
michael@0 797 // MAT2 is row major, right handed (y up).
michael@0 798 fMat22.eM11 = SkScalarToFIXED(fRec.fPost2x2[0][0]);
michael@0 799 fMat22.eM12 = SkScalarToFIXED(-fRec.fPost2x2[1][0]);
michael@0 800 fMat22.eM21 = SkScalarToFIXED(-fRec.fPost2x2[0][1]);
michael@0 801 fMat22.eM22 = SkScalarToFIXED(fRec.fPost2x2[1][1]);
michael@0 802 }
michael@0 803
michael@0 804 fOffscreen.init(fFont, xform);
michael@0 805 }
michael@0 806
michael@0 807 SkScalerContext_GDI::~SkScalerContext_GDI() {
michael@0 808 if (fDDC) {
michael@0 809 ::SelectObject(fDDC, fSavefont);
michael@0 810 ::DeleteDC(fDDC);
michael@0 811 }
michael@0 812 if (fFont) {
michael@0 813 ::DeleteObject(fFont);
michael@0 814 }
michael@0 815 if (fSC) {
michael@0 816 ::ScriptFreeCache(&fSC);
michael@0 817 }
michael@0 818 }
michael@0 819
michael@0 820 bool SkScalerContext_GDI::isValid() const {
michael@0 821 return fDDC && fFont;
michael@0 822 }
michael@0 823
michael@0 824 unsigned SkScalerContext_GDI::generateGlyphCount() {
michael@0 825 if (fGlyphCount < 0) {
michael@0 826 fGlyphCount = calculateGlyphCount(
michael@0 827 fDDC, static_cast<const LogFontTypeface*>(this->getTypeface())->fLogFont);
michael@0 828 }
michael@0 829 return fGlyphCount;
michael@0 830 }
michael@0 831
michael@0 832 uint16_t SkScalerContext_GDI::generateCharToGlyph(SkUnichar utf32) {
michael@0 833 uint16_t index = 0;
michael@0 834 WCHAR utf16[2];
michael@0 835 // TODO(ctguil): Support characters that generate more than one glyph.
michael@0 836 if (SkUTF16_FromUnichar(utf32, (uint16_t*)utf16) == 1) {
michael@0 837 // Type1 fonts fail with uniscribe API. Use GetGlyphIndices for plane 0.
michael@0 838
michael@0 839 /** Real documentation for GetGlyphIndiciesW:
michael@0 840 *
michael@0 841 * When GGI_MARK_NONEXISTING_GLYPHS is not specified and a character does not map to a
michael@0 842 * glyph, then the 'default character's glyph is returned instead. The 'default character'
michael@0 843 * is available in fTM.tmDefaultChar. FON fonts have a default character, and there exists
michael@0 844 * a usDefaultChar in the 'OS/2' table, version 2 and later. If there is no
michael@0 845 * 'default character' specified by the font, then often the first character found is used.
michael@0 846 *
michael@0 847 * When GGI_MARK_NONEXISTING_GLYPHS is specified and a character does not map to a glyph,
michael@0 848 * then the glyph 0xFFFF is used. In Windows XP and earlier, Bitmap/Vector FON usually use
michael@0 849 * glyph 0x1F instead ('Terminal' appears to be special, returning 0xFFFF).
michael@0 850 * Type1 PFM/PFB, TT, OT TT, OT CFF all appear to use 0xFFFF, even on XP.
michael@0 851 */
michael@0 852 DWORD result = GetGlyphIndicesW(fDDC, utf16, 1, &index, GGI_MARK_NONEXISTING_GLYPHS);
michael@0 853 if (result == GDI_ERROR
michael@0 854 || 0xFFFF == index
michael@0 855 || (0x1F == index &&
michael@0 856 (fType == SkScalerContext_GDI::kBitmap_Type ||
michael@0 857 fType == SkScalerContext_GDI::kLine_Type)
michael@0 858 /*&& winVer < Vista */)
michael@0 859 )
michael@0 860 {
michael@0 861 index = 0;
michael@0 862 }
michael@0 863 } else {
michael@0 864 // Use uniscribe to detemine glyph index for non-BMP characters.
michael@0 865 static const int numWCHAR = 2;
michael@0 866 static const int maxItems = 2;
michael@0 867 // MSDN states that this can be NULL, but some things don't work then.
michael@0 868 SCRIPT_CONTROL sc = { 0 };
michael@0 869 // Add extra item to SCRIPT_ITEM to work around a bug (now documented).
michael@0 870 // https://bugzilla.mozilla.org/show_bug.cgi?id=366643
michael@0 871 SCRIPT_ITEM si[maxItems + 1];
michael@0 872 int numItems;
michael@0 873 HRZM(ScriptItemize(utf16, numWCHAR, maxItems, &sc, NULL, si, &numItems),
michael@0 874 "Could not itemize character.");
michael@0 875
michael@0 876 // Sometimes ScriptShape cannot find a glyph for a non-BMP and returns 2 space glyphs.
michael@0 877 static const int maxGlyphs = 2;
michael@0 878 SCRIPT_VISATTR vsa[maxGlyphs];
michael@0 879 WORD outGlyphs[maxGlyphs];
michael@0 880 WORD logClust[numWCHAR];
michael@0 881 int numGlyphs;
michael@0 882 HRZM(ScriptShape(fDDC, &fSC, utf16, numWCHAR, maxGlyphs, &si[0].a,
michael@0 883 outGlyphs, logClust, vsa, &numGlyphs),
michael@0 884 "Could not shape character.");
michael@0 885 if (1 == numGlyphs) {
michael@0 886 index = outGlyphs[0];
michael@0 887 }
michael@0 888 }
michael@0 889 return index;
michael@0 890 }
michael@0 891
michael@0 892 void SkScalerContext_GDI::generateAdvance(SkGlyph* glyph) {
michael@0 893 this->generateMetrics(glyph);
michael@0 894 }
michael@0 895
michael@0 896 void SkScalerContext_GDI::generateMetrics(SkGlyph* glyph) {
michael@0 897 SkASSERT(fDDC);
michael@0 898
michael@0 899 if (fType == SkScalerContext_GDI::kBitmap_Type || fType == SkScalerContext_GDI::kLine_Type) {
michael@0 900 SIZE size;
michael@0 901 WORD glyphs = glyph->getGlyphID(0);
michael@0 902 if (0 == GetTextExtentPointI(fDDC, &glyphs, 1, &size)) {
michael@0 903 glyph->fWidth = SkToS16(fTM.tmMaxCharWidth);
michael@0 904 } else {
michael@0 905 glyph->fWidth = SkToS16(size.cx);
michael@0 906 }
michael@0 907 glyph->fHeight = SkToS16(size.cy);
michael@0 908
michael@0 909 glyph->fTop = SkToS16(-fTM.tmAscent);
michael@0 910 // Bitmap FON cannot underhang, but vector FON may.
michael@0 911 // There appears no means of determining underhang of vector FON.
michael@0 912 glyph->fLeft = SkToS16(0);
michael@0 913 glyph->fAdvanceX = SkIntToFixed(glyph->fWidth);
michael@0 914 glyph->fAdvanceY = 0;
michael@0 915
michael@0 916 // Vector FON will transform nicely, but bitmap FON do not.
michael@0 917 if (fType == SkScalerContext_GDI::kLine_Type) {
michael@0 918 SkRect bounds = SkRect::MakeXYWH(glyph->fLeft, glyph->fTop,
michael@0 919 glyph->fWidth, glyph->fHeight);
michael@0 920 SkMatrix m;
michael@0 921 m.setAll(SkFIXEDToScalar(fMat22.eM11), -SkFIXEDToScalar(fMat22.eM21), 0,
michael@0 922 -SkFIXEDToScalar(fMat22.eM12), SkFIXEDToScalar(fMat22.eM22), 0,
michael@0 923 0, 0, SkScalarToPersp(SK_Scalar1));
michael@0 924 m.mapRect(&bounds);
michael@0 925 bounds.roundOut();
michael@0 926 glyph->fLeft = SkScalarTruncToInt(bounds.fLeft);
michael@0 927 glyph->fTop = SkScalarTruncToInt(bounds.fTop);
michael@0 928 glyph->fWidth = SkScalarTruncToInt(bounds.width());
michael@0 929 glyph->fHeight = SkScalarTruncToInt(bounds.height());
michael@0 930 }
michael@0 931
michael@0 932 // Apply matrix to advance.
michael@0 933 glyph->fAdvanceY = SkFixedMul(-SkFIXEDToFixed(fMat22.eM12), glyph->fAdvanceX);
michael@0 934 glyph->fAdvanceX = SkFixedMul(SkFIXEDToFixed(fMat22.eM11), glyph->fAdvanceX);
michael@0 935
michael@0 936 return;
michael@0 937 }
michael@0 938
michael@0 939 UINT glyphId = glyph->getGlyphID(0);
michael@0 940
michael@0 941 GLYPHMETRICS gm;
michael@0 942 sk_bzero(&gm, sizeof(gm));
michael@0 943
michael@0 944 DWORD status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
michael@0 945 if (GDI_ERROR == status) {
michael@0 946 LogFontTypeface::EnsureAccessible(this->getTypeface());
michael@0 947 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
michael@0 948 if (GDI_ERROR == status) {
michael@0 949 glyph->zeroMetrics();
michael@0 950 return;
michael@0 951 }
michael@0 952 }
michael@0 953
michael@0 954 bool empty = false;
michael@0 955 // The black box is either the embedded bitmap size or the outline extent.
michael@0 956 // It is 1x1 if nothing is to be drawn, but will also be 1x1 if something very small
michael@0 957 // is to be drawn, like a '.'. We need to outset '.' but do not wish to outset ' '.
michael@0 958 if (1 == gm.gmBlackBoxX && 1 == gm.gmBlackBoxY) {
michael@0 959 // If GetGlyphOutline with GGO_NATIVE returns 0, we know there was no outline.
michael@0 960 DWORD bufferSize = GetGlyphOutlineW(fDDC, glyphId, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
michael@0 961 empty = (0 == bufferSize);
michael@0 962 }
michael@0 963
michael@0 964 glyph->fTop = SkToS16(-gm.gmptGlyphOrigin.y);
michael@0 965 glyph->fLeft = SkToS16(gm.gmptGlyphOrigin.x);
michael@0 966 if (empty) {
michael@0 967 glyph->fWidth = 0;
michael@0 968 glyph->fHeight = 0;
michael@0 969 } else {
michael@0 970 // Outset, since the image may bleed out of the black box.
michael@0 971 // For embedded bitmaps the black box should be exact.
michael@0 972 // For outlines we need to outset by 1 in all directions for bleed.
michael@0 973 // For ClearType we need to outset by 2 for bleed.
michael@0 974 glyph->fWidth = gm.gmBlackBoxX + 4;
michael@0 975 glyph->fHeight = gm.gmBlackBoxY + 4;
michael@0 976 glyph->fTop -= 2;
michael@0 977 glyph->fLeft -= 2;
michael@0 978 }
michael@0 979 glyph->fAdvanceX = SkIntToFixed(gm.gmCellIncX);
michael@0 980 glyph->fAdvanceY = SkIntToFixed(gm.gmCellIncY);
michael@0 981 glyph->fRsbDelta = 0;
michael@0 982 glyph->fLsbDelta = 0;
michael@0 983
michael@0 984 if (this->isSubpixel()) {
michael@0 985 sk_bzero(&gm, sizeof(gm));
michael@0 986 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fHighResMat22);
michael@0 987 if (GDI_ERROR != status) {
michael@0 988 SkPoint advance;
michael@0 989 fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
michael@0 990 glyph->fAdvanceX = SkScalarToFixed(advance.fX);
michael@0 991 glyph->fAdvanceY = SkScalarToFixed(advance.fY);
michael@0 992 }
michael@0 993 } else if (!isAxisAligned(this->fRec)) {
michael@0 994 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fGsA);
michael@0 995 if (GDI_ERROR != status) {
michael@0 996 SkPoint advance;
michael@0 997 fG_inv.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
michael@0 998 glyph->fAdvanceX = SkScalarToFixed(advance.fX);
michael@0 999 glyph->fAdvanceY = SkScalarToFixed(advance.fY);
michael@0 1000 }
michael@0 1001 }
michael@0 1002 }
michael@0 1003
michael@0 1004 static const MAT2 gMat2Identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
michael@0 1005 void SkScalerContext_GDI::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) {
michael@0 1006 if (!(mx || my)) {
michael@0 1007 return;
michael@0 1008 }
michael@0 1009
michael@0 1010 if (mx) {
michael@0 1011 sk_bzero(mx, sizeof(*mx));
michael@0 1012 }
michael@0 1013 if (my) {
michael@0 1014 sk_bzero(my, sizeof(*my));
michael@0 1015 }
michael@0 1016
michael@0 1017 SkASSERT(fDDC);
michael@0 1018
michael@0 1019 #ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
michael@0 1020 if (fType == SkScalerContext_GDI::kBitmap_Type || fType == SkScalerContext_GDI::kLine_Type) {
michael@0 1021 #endif
michael@0 1022 if (mx) {
michael@0 1023 mx->fTop = SkIntToScalar(-fTM.tmAscent);
michael@0 1024 mx->fAscent = SkIntToScalar(-fTM.tmAscent);
michael@0 1025 mx->fDescent = SkIntToScalar(fTM.tmDescent);
michael@0 1026 mx->fBottom = SkIntToScalar(fTM.tmDescent);
michael@0 1027 mx->fLeading = SkIntToScalar(fTM.tmExternalLeading);
michael@0 1028 }
michael@0 1029
michael@0 1030 if (my) {
michael@0 1031 my->fTop = SkIntToScalar(-fTM.tmAscent);
michael@0 1032 my->fAscent = SkIntToScalar(-fTM.tmAscent);
michael@0 1033 my->fDescent = SkIntToScalar(fTM.tmDescent);
michael@0 1034 my->fBottom = SkIntToScalar(fTM.tmDescent);
michael@0 1035 my->fLeading = SkIntToScalar(fTM.tmExternalLeading);
michael@0 1036 my->fAvgCharWidth = SkIntToScalar(fTM.tmAveCharWidth);
michael@0 1037 my->fMaxCharWidth = SkIntToScalar(fTM.tmMaxCharWidth);
michael@0 1038 my->fXMin = 0;
michael@0 1039 my->fXMax = my->fMaxCharWidth;
michael@0 1040 //my->fXHeight = 0;
michael@0 1041 }
michael@0 1042 #ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
michael@0 1043 return;
michael@0 1044 }
michael@0 1045 #endif
michael@0 1046
michael@0 1047 OUTLINETEXTMETRIC otm;
michael@0 1048
michael@0 1049 uint32_t ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
michael@0 1050 if (0 == ret) {
michael@0 1051 LogFontTypeface::EnsureAccessible(this->getTypeface());
michael@0 1052 ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
michael@0 1053 }
michael@0 1054 if (0 == ret) {
michael@0 1055 return;
michael@0 1056 }
michael@0 1057
michael@0 1058 if (mx) {
michael@0 1059 mx->fTop = SkIntToScalar(-otm.otmrcFontBox.left);
michael@0 1060 mx->fAscent = SkIntToScalar(-otm.otmAscent);
michael@0 1061 mx->fDescent = SkIntToScalar(-otm.otmDescent);
michael@0 1062 mx->fBottom = SkIntToScalar(otm.otmrcFontBox.right);
michael@0 1063 mx->fLeading = SkIntToScalar(otm.otmLineGap);
michael@0 1064 mx->fUnderlineThickness = SkIntToScalar(otm.otmsUnderscoreSize);
michael@0 1065 mx->fUnderlinePosition = -SkIntToScalar(otm.otmsUnderscorePosition);
michael@0 1066
michael@0 1067 mx->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag;
michael@0 1068 mx->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
michael@0 1069 }
michael@0 1070
michael@0 1071 if (my) {
michael@0 1072 #ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
michael@0 1073 my->fTop = SkIntToScalar(-otm.otmrcFontBox.top);
michael@0 1074 my->fAscent = SkIntToScalar(-otm.otmAscent);
michael@0 1075 my->fDescent = SkIntToScalar(-otm.otmDescent);
michael@0 1076 my->fBottom = SkIntToScalar(-otm.otmrcFontBox.bottom);
michael@0 1077 my->fLeading = SkIntToScalar(otm.otmLineGap);
michael@0 1078 my->fAvgCharWidth = SkIntToScalar(otm.otmTextMetrics.tmAveCharWidth);
michael@0 1079 my->fMaxCharWidth = SkIntToScalar(otm.otmTextMetrics.tmMaxCharWidth);
michael@0 1080 my->fXMin = SkIntToScalar(otm.otmrcFontBox.left);
michael@0 1081 my->fXMax = SkIntToScalar(otm.otmrcFontBox.right);
michael@0 1082 my->fUnderlineThickness = SkIntToScalar(otm.otmsUnderscoreSize);
michael@0 1083 my->fUnderlinePosition = -SkIntToScalar(otm.otmsUnderscorePosition);
michael@0 1084
michael@0 1085 my->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag;
michael@0 1086 my->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
michael@0 1087 #endif
michael@0 1088 my->fXHeight = SkIntToScalar(otm.otmsXHeight);
michael@0 1089
michael@0 1090 GLYPHMETRICS gm;
michael@0 1091 sk_bzero(&gm, sizeof(gm));
michael@0 1092 DWORD len = GetGlyphOutlineW(fDDC, 'x', GGO_METRICS, &gm, 0, 0, &gMat2Identity);
michael@0 1093 if (len != GDI_ERROR && gm.gmBlackBoxY > 0) {
michael@0 1094 my->fXHeight = SkIntToScalar(gm.gmBlackBoxY);
michael@0 1095 }
michael@0 1096 }
michael@0 1097 }
michael@0 1098
michael@0 1099 ////////////////////////////////////////////////////////////////////////////////////////
michael@0 1100
michael@0 1101 #define SK_SHOW_TEXT_BLIT_COVERAGE 0
michael@0 1102
michael@0 1103 static void build_power_table(uint8_t table[], float ee) {
michael@0 1104 for (int i = 0; i < 256; i++) {
michael@0 1105 float x = i / 255.f;
michael@0 1106 x = sk_float_pow(x, ee);
michael@0 1107 int xx = SkScalarRoundToInt(x * 255);
michael@0 1108 table[i] = SkToU8(xx);
michael@0 1109 }
michael@0 1110 }
michael@0 1111
michael@0 1112 /**
michael@0 1113 * This will invert the gamma applied by GDI (gray-scale antialiased), so we
michael@0 1114 * can get linear values.
michael@0 1115 *
michael@0 1116 * GDI grayscale appears to use a hard-coded gamma of 2.3.
michael@0 1117 *
michael@0 1118 * GDI grayscale appears to draw using the black and white rasterizer at four
michael@0 1119 * times the size and then downsamples to compute the coverage mask. As a
michael@0 1120 * result there are only seventeen total grays. This lack of fidelity means
michael@0 1121 * that shifting into other color spaces is imprecise.
michael@0 1122 */
michael@0 1123 static const uint8_t* getInverseGammaTableGDI() {
michael@0 1124 // Since build_power_table is idempotent, many threads can build gTableGdi
michael@0 1125 // simultaneously.
michael@0 1126
michael@0 1127 // Microsoft Specific:
michael@0 1128 // Making gInited volatile provides read-aquire and write-release in vc++.
michael@0 1129 // In VS2012, see compiler option /volatile:(ms|iso).
michael@0 1130 // Replace with C++11 atomics when possible.
michael@0 1131 static volatile bool gInited;
michael@0 1132 static uint8_t gTableGdi[256];
michael@0 1133 if (gInited) {
michael@0 1134 // Need a L/L (read) barrier (full acquire not needed). If gInited is observed
michael@0 1135 // true then gTableGdi is observable, but it must be requested.
michael@0 1136 } else {
michael@0 1137 build_power_table(gTableGdi, 2.3f);
michael@0 1138 // Need a S/S (write) barrier (full release not needed) here so that this
michael@0 1139 // write to gInited becomes observable after gTableGdi.
michael@0 1140 gInited = true;
michael@0 1141 }
michael@0 1142 return gTableGdi;
michael@0 1143 }
michael@0 1144
michael@0 1145 /**
michael@0 1146 * This will invert the gamma applied by GDI ClearType, so we can get linear
michael@0 1147 * values.
michael@0 1148 *
michael@0 1149 * GDI ClearType uses SPI_GETFONTSMOOTHINGCONTRAST / 1000 as the gamma value.
michael@0 1150 * If this value is not specified, the default is a gamma of 1.4.
michael@0 1151 */
michael@0 1152 static const uint8_t* getInverseGammaTableClearType() {
michael@0 1153 // We don't expect SPI_GETFONTSMOOTHINGCONTRAST to ever change, so building
michael@0 1154 // gTableClearType with build_power_table is effectively idempotent.
michael@0 1155
michael@0 1156 // Microsoft Specific:
michael@0 1157 // Making gInited volatile provides read-aquire and write-release in vc++.
michael@0 1158 // In VS2012, see compiler option /volatile:(ms|iso).
michael@0 1159 // Replace with C++11 atomics when possible.
michael@0 1160 static volatile bool gInited;
michael@0 1161 static uint8_t gTableClearType[256];
michael@0 1162 if (gInited) {
michael@0 1163 // Need a L/L (read) barrier (acquire not needed). If gInited is observed
michael@0 1164 // true then gTableClearType is observable, but it must be requested.
michael@0 1165 } else {
michael@0 1166 UINT level = 0;
michael@0 1167 if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &level, 0) || !level) {
michael@0 1168 // can't get the data, so use a default
michael@0 1169 level = 1400;
michael@0 1170 }
michael@0 1171 build_power_table(gTableClearType, level / 1000.0f);
michael@0 1172 // Need a S/S (write) barrier (release not needed) here so that this
michael@0 1173 // write to gInited becomes observable after gTableClearType.
michael@0 1174 gInited = true;
michael@0 1175 }
michael@0 1176 return gTableClearType;
michael@0 1177 }
michael@0 1178
michael@0 1179 #include "SkColorPriv.h"
michael@0 1180
michael@0 1181 //Cannot assume that the input rgb is gray due to possible setting of kGenA8FromLCD_Flag.
michael@0 1182 template<bool APPLY_PREBLEND>
michael@0 1183 static inline uint8_t rgb_to_a8(SkGdiRGB rgb, const uint8_t* table8) {
michael@0 1184 U8CPU r = (rgb >> 16) & 0xFF;
michael@0 1185 U8CPU g = (rgb >> 8) & 0xFF;
michael@0 1186 U8CPU b = (rgb >> 0) & 0xFF;
michael@0 1187 return sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
michael@0 1188 }
michael@0 1189
michael@0 1190 template<bool APPLY_PREBLEND>
michael@0 1191 static inline uint16_t rgb_to_lcd16(SkGdiRGB rgb, const uint8_t* tableR,
michael@0 1192 const uint8_t* tableG,
michael@0 1193 const uint8_t* tableB) {
michael@0 1194 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
michael@0 1195 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
michael@0 1196 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
michael@0 1197 #if SK_SHOW_TEXT_BLIT_COVERAGE
michael@0 1198 r = SkMax32(r, 10); g = SkMax32(g, 10); b = SkMax32(b, 10);
michael@0 1199 #endif
michael@0 1200 return SkPack888ToRGB16(r, g, b);
michael@0 1201 }
michael@0 1202
michael@0 1203 template<bool APPLY_PREBLEND>
michael@0 1204 static inline SkPMColor rgb_to_lcd32(SkGdiRGB rgb, const uint8_t* tableR,
michael@0 1205 const uint8_t* tableG,
michael@0 1206 const uint8_t* tableB) {
michael@0 1207 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
michael@0 1208 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
michael@0 1209 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
michael@0 1210 #if SK_SHOW_TEXT_BLIT_COVERAGE
michael@0 1211 r = SkMax32(r, 10); g = SkMax32(g, 10); b = SkMax32(b, 10);
michael@0 1212 #endif
michael@0 1213 return SkPackARGB32(0xFF, r, g, b);
michael@0 1214 }
michael@0 1215
michael@0 1216 // Is this GDI color neither black nor white? If so, we have to keep this
michael@0 1217 // image as is, rather than smashing it down to a BW mask.
michael@0 1218 //
michael@0 1219 // returns int instead of bool, since we don't want/have to pay to convert
michael@0 1220 // the zero/non-zero value into a bool
michael@0 1221 static int is_not_black_or_white(SkGdiRGB c) {
michael@0 1222 // same as (but faster than)
michael@0 1223 // c &= 0x00FFFFFF;
michael@0 1224 // return 0 == c || 0x00FFFFFF == c;
michael@0 1225 return (c + (c & 1)) & 0x00FFFFFF;
michael@0 1226 }
michael@0 1227
michael@0 1228 static bool is_rgb_really_bw(const SkGdiRGB* src, int width, int height, size_t srcRB) {
michael@0 1229 for (int y = 0; y < height; ++y) {
michael@0 1230 for (int x = 0; x < width; ++x) {
michael@0 1231 if (is_not_black_or_white(src[x])) {
michael@0 1232 return false;
michael@0 1233 }
michael@0 1234 }
michael@0 1235 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
michael@0 1236 }
michael@0 1237 return true;
michael@0 1238 }
michael@0 1239
michael@0 1240 // gdi's bitmap is upside-down, so we reverse dst walking in Y
michael@0 1241 // whenever we copy it into skia's buffer
michael@0 1242 static void rgb_to_bw(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
michael@0 1243 const SkGlyph& glyph) {
michael@0 1244 const int width = glyph.fWidth;
michael@0 1245 const size_t dstRB = (width + 7) >> 3;
michael@0 1246 uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
michael@0 1247
michael@0 1248 int byteCount = width >> 3;
michael@0 1249 int bitCount = width & 7;
michael@0 1250
michael@0 1251 // adjust srcRB to skip the values in our byteCount loop,
michael@0 1252 // since we increment src locally there
michael@0 1253 srcRB -= byteCount * 8 * sizeof(SkGdiRGB);
michael@0 1254
michael@0 1255 for (int y = 0; y < glyph.fHeight; ++y) {
michael@0 1256 if (byteCount > 0) {
michael@0 1257 for (int i = 0; i < byteCount; ++i) {
michael@0 1258 unsigned byte = 0;
michael@0 1259 byte |= src[0] & (1 << 7);
michael@0 1260 byte |= src[1] & (1 << 6);
michael@0 1261 byte |= src[2] & (1 << 5);
michael@0 1262 byte |= src[3] & (1 << 4);
michael@0 1263 byte |= src[4] & (1 << 3);
michael@0 1264 byte |= src[5] & (1 << 2);
michael@0 1265 byte |= src[6] & (1 << 1);
michael@0 1266 byte |= src[7] & (1 << 0);
michael@0 1267 dst[i] = byte;
michael@0 1268 src += 8;
michael@0 1269 }
michael@0 1270 }
michael@0 1271 if (bitCount > 0) {
michael@0 1272 unsigned byte = 0;
michael@0 1273 unsigned mask = 0x80;
michael@0 1274 for (int i = 0; i < bitCount; i++) {
michael@0 1275 byte |= src[i] & mask;
michael@0 1276 mask >>= 1;
michael@0 1277 }
michael@0 1278 dst[byteCount] = byte;
michael@0 1279 }
michael@0 1280 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
michael@0 1281 dst -= dstRB;
michael@0 1282 }
michael@0 1283 #if SK_SHOW_TEXT_BLIT_COVERAGE
michael@0 1284 if (glyph.fWidth > 0 && glyph.fHeight > 0) {
michael@0 1285 uint8_t* first = (uint8_t*)glyph.fImage;
michael@0 1286 uint8_t* last = (uint8_t*)((char*)glyph.fImage + glyph.fHeight * dstRB - 1);
michael@0 1287 *first |= 1 << 7;
michael@0 1288 *last |= bitCount == 0 ? 1 : 1 << (8 - bitCount);
michael@0 1289 }
michael@0 1290 #endif
michael@0 1291 }
michael@0 1292
michael@0 1293 template<bool APPLY_PREBLEND>
michael@0 1294 static void rgb_to_a8(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
michael@0 1295 const SkGlyph& glyph, const uint8_t* table8) {
michael@0 1296 const size_t dstRB = glyph.rowBytes();
michael@0 1297 const int width = glyph.fWidth;
michael@0 1298 uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
michael@0 1299
michael@0 1300 for (int y = 0; y < glyph.fHeight; y++) {
michael@0 1301 for (int i = 0; i < width; i++) {
michael@0 1302 dst[i] = rgb_to_a8<APPLY_PREBLEND>(src[i], table8);
michael@0 1303 #if SK_SHOW_TEXT_BLIT_COVERAGE
michael@0 1304 dst[i] = SkMax32(dst[i], 10);
michael@0 1305 #endif
michael@0 1306 }
michael@0 1307 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
michael@0 1308 dst -= dstRB;
michael@0 1309 }
michael@0 1310 }
michael@0 1311
michael@0 1312 template<bool APPLY_PREBLEND>
michael@0 1313 static void rgb_to_lcd16(const SkGdiRGB* SK_RESTRICT src, size_t srcRB, const SkGlyph& glyph,
michael@0 1314 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
michael@0 1315 const size_t dstRB = glyph.rowBytes();
michael@0 1316 const int width = glyph.fWidth;
michael@0 1317 uint16_t* SK_RESTRICT dst = (uint16_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
michael@0 1318
michael@0 1319 for (int y = 0; y < glyph.fHeight; y++) {
michael@0 1320 for (int i = 0; i < width; i++) {
michael@0 1321 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(src[i], tableR, tableG, tableB);
michael@0 1322 }
michael@0 1323 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
michael@0 1324 dst = (uint16_t*)((char*)dst - dstRB);
michael@0 1325 }
michael@0 1326 }
michael@0 1327
michael@0 1328 template<bool APPLY_PREBLEND>
michael@0 1329 static void rgb_to_lcd32(const SkGdiRGB* SK_RESTRICT src, size_t srcRB, const SkGlyph& glyph,
michael@0 1330 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
michael@0 1331 const size_t dstRB = glyph.rowBytes();
michael@0 1332 const int width = glyph.fWidth;
michael@0 1333 uint32_t* SK_RESTRICT dst = (uint32_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
michael@0 1334
michael@0 1335 for (int y = 0; y < glyph.fHeight; y++) {
michael@0 1336 for (int i = 0; i < width; i++) {
michael@0 1337 dst[i] = rgb_to_lcd32<APPLY_PREBLEND>(src[i], tableR, tableG, tableB);
michael@0 1338 }
michael@0 1339 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
michael@0 1340 dst = (uint32_t*)((char*)dst - dstRB);
michael@0 1341 }
michael@0 1342 }
michael@0 1343
michael@0 1344 static inline unsigned clamp255(unsigned x) {
michael@0 1345 SkASSERT(x <= 256);
michael@0 1346 return x - (x >> 8);
michael@0 1347 }
michael@0 1348
michael@0 1349 void SkScalerContext_GDI::generateImage(const SkGlyph& glyph) {
michael@0 1350 SkASSERT(fDDC);
michael@0 1351
michael@0 1352 const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
michael@0 1353 const bool isAA = !isLCD(fRec);
michael@0 1354
michael@0 1355 size_t srcRB;
michael@0 1356 const void* bits = fOffscreen.draw(glyph, isBW, &srcRB);
michael@0 1357 if (NULL == bits) {
michael@0 1358 LogFontTypeface::EnsureAccessible(this->getTypeface());
michael@0 1359 bits = fOffscreen.draw(glyph, isBW, &srcRB);
michael@0 1360 if (NULL == bits) {
michael@0 1361 sk_bzero(glyph.fImage, glyph.computeImageSize());
michael@0 1362 return;
michael@0 1363 }
michael@0 1364 }
michael@0 1365
michael@0 1366 if (!isBW) {
michael@0 1367 const uint8_t* table;
michael@0 1368 //The offscreen contains a GDI blit if isAA and kGenA8FromLCD_Flag is not set.
michael@0 1369 //Otherwise the offscreen contains a ClearType blit.
michael@0 1370 if (isAA && !(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag)) {
michael@0 1371 table = getInverseGammaTableGDI();
michael@0 1372 } else {
michael@0 1373 table = getInverseGammaTableClearType();
michael@0 1374 }
michael@0 1375 //Note that the following cannot really be integrated into the
michael@0 1376 //pre-blend, since we may not be applying the pre-blend; when we aren't
michael@0 1377 //applying the pre-blend it means that a filter wants linear anyway.
michael@0 1378 //Other code may also be applying the pre-blend, so we'd need another
michael@0 1379 //one with this and one without.
michael@0 1380 SkGdiRGB* addr = (SkGdiRGB*)bits;
michael@0 1381 for (int y = 0; y < glyph.fHeight; ++y) {
michael@0 1382 for (int x = 0; x < glyph.fWidth; ++x) {
michael@0 1383 int r = (addr[x] >> 16) & 0xFF;
michael@0 1384 int g = (addr[x] >> 8) & 0xFF;
michael@0 1385 int b = (addr[x] >> 0) & 0xFF;
michael@0 1386 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
michael@0 1387 }
michael@0 1388 addr = SkTAddOffset<SkGdiRGB>(addr, srcRB);
michael@0 1389 }
michael@0 1390 }
michael@0 1391
michael@0 1392 int width = glyph.fWidth;
michael@0 1393 size_t dstRB = glyph.rowBytes();
michael@0 1394 if (isBW) {
michael@0 1395 const uint8_t* src = (const uint8_t*)bits;
michael@0 1396 uint8_t* dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
michael@0 1397 for (int y = 0; y < glyph.fHeight; y++) {
michael@0 1398 memcpy(dst, src, dstRB);
michael@0 1399 src += srcRB;
michael@0 1400 dst -= dstRB;
michael@0 1401 }
michael@0 1402 #if SK_SHOW_TEXT_BLIT_COVERAGE
michael@0 1403 if (glyph.fWidth > 0 && glyph.fHeight > 0) {
michael@0 1404 int bitCount = width & 7;
michael@0 1405 uint8_t* first = (uint8_t*)glyph.fImage;
michael@0 1406 uint8_t* last = (uint8_t*)((char*)glyph.fImage + glyph.fHeight * dstRB - 1);
michael@0 1407 *first |= 1 << 7;
michael@0 1408 *last |= bitCount == 0 ? 1 : 1 << (8 - bitCount);
michael@0 1409 }
michael@0 1410 #endif
michael@0 1411 } else if (isAA) {
michael@0 1412 // since the caller may require A8 for maskfilters, we can't check for BW
michael@0 1413 // ... until we have the caller tell us that explicitly
michael@0 1414 const SkGdiRGB* src = (const SkGdiRGB*)bits;
michael@0 1415 if (fPreBlend.isApplicable()) {
michael@0 1416 rgb_to_a8<true>(src, srcRB, glyph, fPreBlend.fG);
michael@0 1417 } else {
michael@0 1418 rgb_to_a8<false>(src, srcRB, glyph, fPreBlend.fG);
michael@0 1419 }
michael@0 1420 } else { // LCD16
michael@0 1421 const SkGdiRGB* src = (const SkGdiRGB*)bits;
michael@0 1422 if (is_rgb_really_bw(src, width, glyph.fHeight, srcRB)) {
michael@0 1423 rgb_to_bw(src, srcRB, glyph);
michael@0 1424 ((SkGlyph*)&glyph)->fMaskFormat = SkMask::kBW_Format;
michael@0 1425 } else {
michael@0 1426 if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
michael@0 1427 if (fPreBlend.isApplicable()) {
michael@0 1428 rgb_to_lcd16<true>(src, srcRB, glyph,
michael@0 1429 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
michael@0 1430 } else {
michael@0 1431 rgb_to_lcd16<false>(src, srcRB, glyph,
michael@0 1432 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
michael@0 1433 }
michael@0 1434 } else {
michael@0 1435 SkASSERT(SkMask::kLCD32_Format == glyph.fMaskFormat);
michael@0 1436 if (fPreBlend.isApplicable()) {
michael@0 1437 rgb_to_lcd32<true>(src, srcRB, glyph,
michael@0 1438 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
michael@0 1439 } else {
michael@0 1440 rgb_to_lcd32<false>(src, srcRB, glyph,
michael@0 1441 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
michael@0 1442 }
michael@0 1443 }
michael@0 1444 }
michael@0 1445 }
michael@0 1446 }
michael@0 1447
michael@0 1448 class GDIGlyphbufferPointIter {
michael@0 1449 public:
michael@0 1450 GDIGlyphbufferPointIter(const uint8_t* glyphbuf, DWORD total_size)
michael@0 1451 : fHeaderIter(glyphbuf, total_size), fCurveIter(), fPointIter()
michael@0 1452 { }
michael@0 1453
michael@0 1454 POINTFX const * next() {
michael@0 1455 nextHeader:
michael@0 1456 if (!fCurveIter.isSet()) {
michael@0 1457 const TTPOLYGONHEADER* header = fHeaderIter.next();
michael@0 1458 if (NULL == header) {
michael@0 1459 return NULL;
michael@0 1460 }
michael@0 1461 fCurveIter.set(header);
michael@0 1462 const TTPOLYCURVE* curve = fCurveIter.next();
michael@0 1463 if (NULL == curve) {
michael@0 1464 return NULL;
michael@0 1465 }
michael@0 1466 fPointIter.set(curve);
michael@0 1467 return &header->pfxStart;
michael@0 1468 }
michael@0 1469
michael@0 1470 const POINTFX* nextPoint = fPointIter.next();
michael@0 1471 if (NULL == nextPoint) {
michael@0 1472 const TTPOLYCURVE* curve = fCurveIter.next();
michael@0 1473 if (NULL == curve) {
michael@0 1474 fCurveIter.set();
michael@0 1475 goto nextHeader;
michael@0 1476 } else {
michael@0 1477 fPointIter.set(curve);
michael@0 1478 }
michael@0 1479 nextPoint = fPointIter.next();
michael@0 1480 }
michael@0 1481 return nextPoint;
michael@0 1482 }
michael@0 1483
michael@0 1484 WORD currentCurveType() {
michael@0 1485 return fPointIter.fCurveType;
michael@0 1486 }
michael@0 1487
michael@0 1488 private:
michael@0 1489 /** Iterates over all of the polygon headers in a glyphbuf. */
michael@0 1490 class GDIPolygonHeaderIter {
michael@0 1491 public:
michael@0 1492 GDIPolygonHeaderIter(const uint8_t* glyphbuf, DWORD total_size)
michael@0 1493 : fCurPolygon(reinterpret_cast<const TTPOLYGONHEADER*>(glyphbuf))
michael@0 1494 , fEndPolygon(SkTAddOffset<const TTPOLYGONHEADER>(glyphbuf, total_size))
michael@0 1495 { }
michael@0 1496
michael@0 1497 const TTPOLYGONHEADER* next() {
michael@0 1498 if (fCurPolygon >= fEndPolygon) {
michael@0 1499 return NULL;
michael@0 1500 }
michael@0 1501 const TTPOLYGONHEADER* thisPolygon = fCurPolygon;
michael@0 1502 fCurPolygon = SkTAddOffset<const TTPOLYGONHEADER>(fCurPolygon, fCurPolygon->cb);
michael@0 1503 return thisPolygon;
michael@0 1504 }
michael@0 1505 private:
michael@0 1506 const TTPOLYGONHEADER* fCurPolygon;
michael@0 1507 const TTPOLYGONHEADER* fEndPolygon;
michael@0 1508 };
michael@0 1509
michael@0 1510 /** Iterates over all of the polygon curves in a polygon header. */
michael@0 1511 class GDIPolygonCurveIter {
michael@0 1512 public:
michael@0 1513 GDIPolygonCurveIter() : fCurCurve(NULL), fEndCurve(NULL) { }
michael@0 1514
michael@0 1515 GDIPolygonCurveIter(const TTPOLYGONHEADER* curPolygon)
michael@0 1516 : fCurCurve(SkTAddOffset<const TTPOLYCURVE>(curPolygon, sizeof(TTPOLYGONHEADER)))
michael@0 1517 , fEndCurve(SkTAddOffset<const TTPOLYCURVE>(curPolygon, curPolygon->cb))
michael@0 1518 { }
michael@0 1519
michael@0 1520 bool isSet() { return fCurCurve != NULL; }
michael@0 1521
michael@0 1522 void set(const TTPOLYGONHEADER* curPolygon) {
michael@0 1523 fCurCurve = SkTAddOffset<const TTPOLYCURVE>(curPolygon, sizeof(TTPOLYGONHEADER));
michael@0 1524 fEndCurve = SkTAddOffset<const TTPOLYCURVE>(curPolygon, curPolygon->cb);
michael@0 1525 }
michael@0 1526 void set() {
michael@0 1527 fCurCurve = NULL;
michael@0 1528 fEndCurve = NULL;
michael@0 1529 }
michael@0 1530
michael@0 1531 const TTPOLYCURVE* next() {
michael@0 1532 if (fCurCurve >= fEndCurve) {
michael@0 1533 return NULL;
michael@0 1534 }
michael@0 1535 const TTPOLYCURVE* thisCurve = fCurCurve;
michael@0 1536 fCurCurve = SkTAddOffset<const TTPOLYCURVE>(fCurCurve, size_of_TTPOLYCURVE(*fCurCurve));
michael@0 1537 return thisCurve;
michael@0 1538 }
michael@0 1539 private:
michael@0 1540 size_t size_of_TTPOLYCURVE(const TTPOLYCURVE& curve) {
michael@0 1541 return 2*sizeof(WORD) + curve.cpfx*sizeof(POINTFX);
michael@0 1542 }
michael@0 1543 const TTPOLYCURVE* fCurCurve;
michael@0 1544 const TTPOLYCURVE* fEndCurve;
michael@0 1545 };
michael@0 1546
michael@0 1547 /** Iterates over all of the polygon points in a polygon curve. */
michael@0 1548 class GDIPolygonCurvePointIter {
michael@0 1549 public:
michael@0 1550 GDIPolygonCurvePointIter() : fCurveType(0), fCurPoint(NULL), fEndPoint(NULL) { }
michael@0 1551
michael@0 1552 GDIPolygonCurvePointIter(const TTPOLYCURVE* curPolygon)
michael@0 1553 : fCurveType(curPolygon->wType)
michael@0 1554 , fCurPoint(&curPolygon->apfx[0])
michael@0 1555 , fEndPoint(&curPolygon->apfx[curPolygon->cpfx])
michael@0 1556 { }
michael@0 1557
michael@0 1558 bool isSet() { return fCurPoint != NULL; }
michael@0 1559
michael@0 1560 void set(const TTPOLYCURVE* curPolygon) {
michael@0 1561 fCurveType = curPolygon->wType;
michael@0 1562 fCurPoint = &curPolygon->apfx[0];
michael@0 1563 fEndPoint = &curPolygon->apfx[curPolygon->cpfx];
michael@0 1564 }
michael@0 1565 void set() {
michael@0 1566 fCurPoint = NULL;
michael@0 1567 fEndPoint = NULL;
michael@0 1568 }
michael@0 1569
michael@0 1570 const POINTFX* next() {
michael@0 1571 if (fCurPoint >= fEndPoint) {
michael@0 1572 return NULL;
michael@0 1573 }
michael@0 1574 const POINTFX* thisPoint = fCurPoint;
michael@0 1575 ++fCurPoint;
michael@0 1576 return thisPoint;
michael@0 1577 }
michael@0 1578
michael@0 1579 WORD fCurveType;
michael@0 1580 private:
michael@0 1581 const POINTFX* fCurPoint;
michael@0 1582 const POINTFX* fEndPoint;
michael@0 1583 };
michael@0 1584
michael@0 1585 GDIPolygonHeaderIter fHeaderIter;
michael@0 1586 GDIPolygonCurveIter fCurveIter;
michael@0 1587 GDIPolygonCurvePointIter fPointIter;
michael@0 1588 };
michael@0 1589
michael@0 1590 static void sk_path_from_gdi_path(SkPath* path, const uint8_t* glyphbuf, DWORD total_size) {
michael@0 1591 const uint8_t* cur_glyph = glyphbuf;
michael@0 1592 const uint8_t* end_glyph = glyphbuf + total_size;
michael@0 1593
michael@0 1594 while (cur_glyph < end_glyph) {
michael@0 1595 const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
michael@0 1596
michael@0 1597 const uint8_t* end_poly = cur_glyph + th->cb;
michael@0 1598 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
michael@0 1599
michael@0 1600 path->moveTo(SkFixedToScalar( SkFIXEDToFixed(th->pfxStart.x)),
michael@0 1601 SkFixedToScalar(-SkFIXEDToFixed(th->pfxStart.y)));
michael@0 1602
michael@0 1603 while (cur_poly < end_poly) {
michael@0 1604 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
michael@0 1605
michael@0 1606 if (pc->wType == TT_PRIM_LINE) {
michael@0 1607 for (uint16_t i = 0; i < pc->cpfx; i++) {
michael@0 1608 path->lineTo(SkFixedToScalar( SkFIXEDToFixed(pc->apfx[i].x)),
michael@0 1609 SkFixedToScalar(-SkFIXEDToFixed(pc->apfx[i].y)));
michael@0 1610 }
michael@0 1611 }
michael@0 1612
michael@0 1613 if (pc->wType == TT_PRIM_QSPLINE) {
michael@0 1614 for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
michael@0 1615 POINTFX pnt_b = pc->apfx[u]; // B is always the current point
michael@0 1616 POINTFX pnt_c = pc->apfx[u+1];
michael@0 1617
michael@0 1618 if (u < pc->cpfx - 2) { // If not on last spline, compute C
michael@0 1619 pnt_c.x = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.x),
michael@0 1620 SkFIXEDToFixed(pnt_c.x)));
michael@0 1621 pnt_c.y = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.y),
michael@0 1622 SkFIXEDToFixed(pnt_c.y)));
michael@0 1623 }
michael@0 1624
michael@0 1625 path->quadTo(SkFixedToScalar( SkFIXEDToFixed(pnt_b.x)),
michael@0 1626 SkFixedToScalar(-SkFIXEDToFixed(pnt_b.y)),
michael@0 1627 SkFixedToScalar( SkFIXEDToFixed(pnt_c.x)),
michael@0 1628 SkFixedToScalar(-SkFIXEDToFixed(pnt_c.y)));
michael@0 1629 }
michael@0 1630 }
michael@0 1631 // Advance past this TTPOLYCURVE.
michael@0 1632 cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx;
michael@0 1633 }
michael@0 1634 cur_glyph += th->cb;
michael@0 1635 path->close();
michael@0 1636 }
michael@0 1637 }
michael@0 1638
michael@0 1639 #define move_next_expected_hinted_point(iter, pElem) do {\
michael@0 1640 pElem = iter.next(); \
michael@0 1641 if (NULL == pElem) return false; \
michael@0 1642 } while(0)
michael@0 1643
michael@0 1644 // It is possible for the hinted and unhinted versions of the same path to have
michael@0 1645 // a different number of points due to GDI's handling of flipped points.
michael@0 1646 // If this is detected, this will return false.
michael@0 1647 static bool sk_path_from_gdi_paths(SkPath* path, const uint8_t* glyphbuf, DWORD total_size,
michael@0 1648 GDIGlyphbufferPointIter hintedYs) {
michael@0 1649 const uint8_t* cur_glyph = glyphbuf;
michael@0 1650 const uint8_t* end_glyph = glyphbuf + total_size;
michael@0 1651
michael@0 1652 POINTFX const * hintedPoint;
michael@0 1653
michael@0 1654 while (cur_glyph < end_glyph) {
michael@0 1655 const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
michael@0 1656
michael@0 1657 const uint8_t* end_poly = cur_glyph + th->cb;
michael@0 1658 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
michael@0 1659
michael@0 1660 move_next_expected_hinted_point(hintedYs, hintedPoint);
michael@0 1661 path->moveTo(SkFixedToScalar( SkFIXEDToFixed(th->pfxStart.x)),
michael@0 1662 SkFixedToScalar(-SkFIXEDToFixed(hintedPoint->y)));
michael@0 1663
michael@0 1664 while (cur_poly < end_poly) {
michael@0 1665 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
michael@0 1666
michael@0 1667 if (pc->wType == TT_PRIM_LINE) {
michael@0 1668 for (uint16_t i = 0; i < pc->cpfx; i++) {
michael@0 1669 move_next_expected_hinted_point(hintedYs, hintedPoint);
michael@0 1670 path->lineTo(SkFixedToScalar( SkFIXEDToFixed(pc->apfx[i].x)),
michael@0 1671 SkFixedToScalar(-SkFIXEDToFixed(hintedPoint->y)));
michael@0 1672 }
michael@0 1673 }
michael@0 1674
michael@0 1675 if (pc->wType == TT_PRIM_QSPLINE) {
michael@0 1676 POINTFX currentPoint = pc->apfx[0];
michael@0 1677 move_next_expected_hinted_point(hintedYs, hintedPoint);
michael@0 1678 // only take the hinted y if it wasn't flipped
michael@0 1679 if (hintedYs.currentCurveType() == TT_PRIM_QSPLINE) {
michael@0 1680 currentPoint.y = hintedPoint->y;
michael@0 1681 }
michael@0 1682 for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
michael@0 1683 POINTFX pnt_b = currentPoint;//pc->apfx[u]; // B is always the current point
michael@0 1684 POINTFX pnt_c = pc->apfx[u+1];
michael@0 1685 move_next_expected_hinted_point(hintedYs, hintedPoint);
michael@0 1686 // only take the hinted y if it wasn't flipped
michael@0 1687 if (hintedYs.currentCurveType() == TT_PRIM_QSPLINE) {
michael@0 1688 pnt_c.y = hintedPoint->y;
michael@0 1689 }
michael@0 1690 currentPoint.x = pnt_c.x;
michael@0 1691 currentPoint.y = pnt_c.y;
michael@0 1692
michael@0 1693 if (u < pc->cpfx - 2) { // If not on last spline, compute C
michael@0 1694 pnt_c.x = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.x),
michael@0 1695 SkFIXEDToFixed(pnt_c.x)));
michael@0 1696 pnt_c.y = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.y),
michael@0 1697 SkFIXEDToFixed(pnt_c.y)));
michael@0 1698 }
michael@0 1699
michael@0 1700 path->quadTo(SkFixedToScalar( SkFIXEDToFixed(pnt_b.x)),
michael@0 1701 SkFixedToScalar(-SkFIXEDToFixed(pnt_b.y)),
michael@0 1702 SkFixedToScalar( SkFIXEDToFixed(pnt_c.x)),
michael@0 1703 SkFixedToScalar(-SkFIXEDToFixed(pnt_c.y)));
michael@0 1704 }
michael@0 1705 }
michael@0 1706 // Advance past this TTPOLYCURVE.
michael@0 1707 cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx;
michael@0 1708 }
michael@0 1709 cur_glyph += th->cb;
michael@0 1710 path->close();
michael@0 1711 }
michael@0 1712 return true;
michael@0 1713 }
michael@0 1714
michael@0 1715 DWORD SkScalerContext_GDI::getGDIGlyphPath(const SkGlyph& glyph, UINT flags,
michael@0 1716 SkAutoSTMalloc<BUFFERSIZE, uint8_t>* glyphbuf)
michael@0 1717 {
michael@0 1718 GLYPHMETRICS gm;
michael@0 1719
michael@0 1720 DWORD total_size = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, BUFFERSIZE, glyphbuf->get(), &fMat22);
michael@0 1721 // Sometimes GetGlyphOutlineW returns a number larger than BUFFERSIZE even if BUFFERSIZE > 0.
michael@0 1722 // It has been verified that this does not involve a buffer overrun.
michael@0 1723 if (GDI_ERROR == total_size || total_size > BUFFERSIZE) {
michael@0 1724 // GDI_ERROR because the BUFFERSIZE was too small, or because the data was not accessible.
michael@0 1725 // When the data is not accessable GetGlyphOutlineW fails rather quickly,
michael@0 1726 // so just try to get the size. If that fails then ensure the data is accessible.
michael@0 1727 total_size = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, 0, NULL, &fMat22);
michael@0 1728 if (GDI_ERROR == total_size) {
michael@0 1729 LogFontTypeface::EnsureAccessible(this->getTypeface());
michael@0 1730 total_size = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, 0, NULL, &fMat22);
michael@0 1731 if (GDI_ERROR == total_size) {
michael@0 1732 SkASSERT(false);
michael@0 1733 return 0;
michael@0 1734 }
michael@0 1735 }
michael@0 1736
michael@0 1737 glyphbuf->reset(total_size);
michael@0 1738
michael@0 1739 DWORD ret = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, total_size, glyphbuf->get(), &fMat22);
michael@0 1740 if (GDI_ERROR == ret) {
michael@0 1741 LogFontTypeface::EnsureAccessible(this->getTypeface());
michael@0 1742 ret = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, total_size, glyphbuf->get(), &fMat22);
michael@0 1743 if (GDI_ERROR == ret) {
michael@0 1744 SkASSERT(false);
michael@0 1745 return 0;
michael@0 1746 }
michael@0 1747 }
michael@0 1748 }
michael@0 1749 return total_size;
michael@0 1750 }
michael@0 1751
michael@0 1752 void SkScalerContext_GDI::generatePath(const SkGlyph& glyph, SkPath* path) {
michael@0 1753 SkASSERT(&glyph && path);
michael@0 1754 SkASSERT(fDDC);
michael@0 1755
michael@0 1756 path->reset();
michael@0 1757
michael@0 1758 // Out of all the fonts on a typical Windows box,
michael@0 1759 // 25% of glyphs require more than 2KB.
michael@0 1760 // 1% of glyphs require more than 4KB.
michael@0 1761 // 0.01% of glyphs require more than 8KB.
michael@0 1762 // 8KB is less than 1% of the normal 1MB stack on Windows.
michael@0 1763 // Note that some web fonts glyphs require more than 20KB.
michael@0 1764 //static const DWORD BUFFERSIZE = (1 << 13);
michael@0 1765
michael@0 1766 //GDI only uses hinted outlines when axis aligned.
michael@0 1767 UINT format = GGO_NATIVE | GGO_GLYPH_INDEX;
michael@0 1768 if (fRec.getHinting() == SkPaint::kNo_Hinting || fRec.getHinting() == SkPaint::kSlight_Hinting){
michael@0 1769 format |= GGO_UNHINTED;
michael@0 1770 }
michael@0 1771 SkAutoSTMalloc<BUFFERSIZE, uint8_t> glyphbuf(BUFFERSIZE);
michael@0 1772 DWORD total_size = getGDIGlyphPath(glyph, format, &glyphbuf);
michael@0 1773 if (0 == total_size) {
michael@0 1774 return;
michael@0 1775 }
michael@0 1776
michael@0 1777 if (fRec.getHinting() != SkPaint::kSlight_Hinting) {
michael@0 1778 sk_path_from_gdi_path(path, glyphbuf, total_size);
michael@0 1779 } else {
michael@0 1780 //GDI only uses hinted outlines when axis aligned.
michael@0 1781 UINT format = GGO_NATIVE | GGO_GLYPH_INDEX;
michael@0 1782
michael@0 1783 SkAutoSTMalloc<BUFFERSIZE, uint8_t> hintedGlyphbuf(BUFFERSIZE);
michael@0 1784 DWORD hinted_total_size = getGDIGlyphPath(glyph, format, &hintedGlyphbuf);
michael@0 1785 if (0 == hinted_total_size) {
michael@0 1786 return;
michael@0 1787 }
michael@0 1788
michael@0 1789 if (!sk_path_from_gdi_paths(path, glyphbuf, total_size,
michael@0 1790 GDIGlyphbufferPointIter(hintedGlyphbuf, hinted_total_size)))
michael@0 1791 {
michael@0 1792 path->reset();
michael@0 1793 sk_path_from_gdi_path(path, glyphbuf, total_size);
michael@0 1794 }
michael@0 1795 }
michael@0 1796 }
michael@0 1797
michael@0 1798 static void logfont_for_name(const char* familyName, LOGFONT* lf) {
michael@0 1799 sk_bzero(lf, sizeof(LOGFONT));
michael@0 1800 #ifdef UNICODE
michael@0 1801 // Get the buffer size needed first.
michael@0 1802 size_t str_len = ::MultiByteToWideChar(CP_UTF8, 0, familyName,
michael@0 1803 -1, NULL, 0);
michael@0 1804 // Allocate a buffer (str_len already has terminating null
michael@0 1805 // accounted for).
michael@0 1806 wchar_t *wideFamilyName = new wchar_t[str_len];
michael@0 1807 // Now actually convert the string.
michael@0 1808 ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1,
michael@0 1809 wideFamilyName, str_len);
michael@0 1810 ::wcsncpy(lf->lfFaceName, wideFamilyName, LF_FACESIZE - 1);
michael@0 1811 delete [] wideFamilyName;
michael@0 1812 lf->lfFaceName[LF_FACESIZE-1] = L'\0';
michael@0 1813 #else
michael@0 1814 ::strncpy(lf->lfFaceName, familyName, LF_FACESIZE - 1);
michael@0 1815 lf->lfFaceName[LF_FACESIZE - 1] = '\0';
michael@0 1816 #endif
michael@0 1817 }
michael@0 1818
michael@0 1819 void LogFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc,
michael@0 1820 bool* isLocalStream) const {
michael@0 1821 // Get the actual name of the typeface. The logfont may not know this.
michael@0 1822 HFONT font = CreateFontIndirect(&fLogFont);
michael@0 1823
michael@0 1824 HDC deviceContext = ::CreateCompatibleDC(NULL);
michael@0 1825 HFONT savefont = (HFONT)SelectObject(deviceContext, font);
michael@0 1826
michael@0 1827 SkString familyName;
michael@0 1828 dcfontname_to_skstring(deviceContext, fLogFont, &familyName);
michael@0 1829
michael@0 1830 if (deviceContext) {
michael@0 1831 ::SelectObject(deviceContext, savefont);
michael@0 1832 ::DeleteDC(deviceContext);
michael@0 1833 }
michael@0 1834 if (font) {
michael@0 1835 ::DeleteObject(font);
michael@0 1836 }
michael@0 1837
michael@0 1838 desc->setFamilyName(familyName.c_str());
michael@0 1839 *isLocalStream = this->fSerializeAsStream;
michael@0 1840 }
michael@0 1841
michael@0 1842 static bool getWidthAdvance(HDC hdc, int gId, int16_t* advance) {
michael@0 1843 // Initialize the MAT2 structure to the identify transformation matrix.
michael@0 1844 static const MAT2 mat2 = {SkScalarToFIXED(1), SkScalarToFIXED(0),
michael@0 1845 SkScalarToFIXED(0), SkScalarToFIXED(1)};
michael@0 1846 int flags = GGO_METRICS | GGO_GLYPH_INDEX;
michael@0 1847 GLYPHMETRICS gm;
michael@0 1848 if (GDI_ERROR == GetGlyphOutline(hdc, gId, flags, &gm, 0, NULL, &mat2)) {
michael@0 1849 return false;
michael@0 1850 }
michael@0 1851 SkASSERT(advance);
michael@0 1852 *advance = gm.gmCellIncX;
michael@0 1853 return true;
michael@0 1854 }
michael@0 1855
michael@0 1856 SkAdvancedTypefaceMetrics* LogFontTypeface::onGetAdvancedTypefaceMetrics(
michael@0 1857 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
michael@0 1858 const uint32_t* glyphIDs,
michael@0 1859 uint32_t glyphIDsCount) const {
michael@0 1860 LOGFONT lf = fLogFont;
michael@0 1861 SkAdvancedTypefaceMetrics* info = NULL;
michael@0 1862
michael@0 1863 HDC hdc = CreateCompatibleDC(NULL);
michael@0 1864 HFONT font = CreateFontIndirect(&lf);
michael@0 1865 HFONT savefont = (HFONT)SelectObject(hdc, font);
michael@0 1866 HFONT designFont = NULL;
michael@0 1867
michael@0 1868 const char stem_chars[] = {'i', 'I', '!', '1'};
michael@0 1869 int16_t min_width;
michael@0 1870 unsigned glyphCount;
michael@0 1871
michael@0 1872 // To request design units, create a logical font whose height is specified
michael@0 1873 // as unitsPerEm.
michael@0 1874 OUTLINETEXTMETRIC otm;
michael@0 1875 unsigned int otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
michael@0 1876 if (0 == otmRet) {
michael@0 1877 call_ensure_accessible(lf);
michael@0 1878 otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
michael@0 1879 }
michael@0 1880 if (!otmRet || !GetTextFace(hdc, LF_FACESIZE, lf.lfFaceName)) {
michael@0 1881 goto Error;
michael@0 1882 }
michael@0 1883 lf.lfHeight = -SkToS32(otm.otmEMSquare);
michael@0 1884 designFont = CreateFontIndirect(&lf);
michael@0 1885 SelectObject(hdc, designFont);
michael@0 1886 if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) {
michael@0 1887 goto Error;
michael@0 1888 }
michael@0 1889 glyphCount = calculateGlyphCount(hdc, fLogFont);
michael@0 1890
michael@0 1891 info = new SkAdvancedTypefaceMetrics;
michael@0 1892 info->fEmSize = otm.otmEMSquare;
michael@0 1893 info->fMultiMaster = false;
michael@0 1894 info->fLastGlyphID = SkToU16(glyphCount - 1);
michael@0 1895 info->fStyle = 0;
michael@0 1896 tchar_to_skstring(lf.lfFaceName, &info->fFontName);
michael@0 1897
michael@0 1898 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
michael@0 1899 populate_glyph_to_unicode(hdc, glyphCount, &(info->fGlyphToUnicode));
michael@0 1900 }
michael@0 1901
michael@0 1902 if (glyphCount > 0 &&
michael@0 1903 (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE)) {
michael@0 1904 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
michael@0 1905 } else {
michael@0 1906 info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
michael@0 1907 info->fItalicAngle = 0;
michael@0 1908 info->fAscent = 0;
michael@0 1909 info->fDescent = 0;
michael@0 1910 info->fStemV = 0;
michael@0 1911 info->fCapHeight = 0;
michael@0 1912 info->fBBox = SkIRect::MakeEmpty();
michael@0 1913 goto ReturnInfo;
michael@0 1914 }
michael@0 1915
michael@0 1916 // If this bit is clear the font is a fixed pitch font.
michael@0 1917 if (!(otm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
michael@0 1918 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
michael@0 1919 }
michael@0 1920 if (otm.otmTextMetrics.tmItalic) {
michael@0 1921 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
michael@0 1922 }
michael@0 1923 if (otm.otmTextMetrics.tmPitchAndFamily & FF_ROMAN) {
michael@0 1924 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
michael@0 1925 } else if (otm.otmTextMetrics.tmPitchAndFamily & FF_SCRIPT) {
michael@0 1926 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
michael@0 1927 }
michael@0 1928
michael@0 1929 // The main italic angle of the font, in tenths of a degree counterclockwise
michael@0 1930 // from vertical.
michael@0 1931 info->fItalicAngle = otm.otmItalicAngle / 10;
michael@0 1932 info->fAscent = SkToS16(otm.otmTextMetrics.tmAscent);
michael@0 1933 info->fDescent = SkToS16(-otm.otmTextMetrics.tmDescent);
michael@0 1934 // TODO(ctguil): Use alternate cap height calculation.
michael@0 1935 // MSDN says otmsCapEmHeight is not support but it is returning a value on
michael@0 1936 // my Win7 box.
michael@0 1937 info->fCapHeight = otm.otmsCapEmHeight;
michael@0 1938 info->fBBox =
michael@0 1939 SkIRect::MakeLTRB(otm.otmrcFontBox.left, otm.otmrcFontBox.top,
michael@0 1940 otm.otmrcFontBox.right, otm.otmrcFontBox.bottom);
michael@0 1941
michael@0 1942 // Figure out a good guess for StemV - Min width of i, I, !, 1.
michael@0 1943 // This probably isn't very good with an italic font.
michael@0 1944 min_width = SHRT_MAX;
michael@0 1945 info->fStemV = 0;
michael@0 1946 for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {
michael@0 1947 ABC abcWidths;
michael@0 1948 if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) {
michael@0 1949 int16_t width = abcWidths.abcB;
michael@0 1950 if (width > 0 && width < min_width) {
michael@0 1951 min_width = width;
michael@0 1952 info->fStemV = min_width;
michael@0 1953 }
michael@0 1954 }
michael@0 1955 }
michael@0 1956
michael@0 1957 // If bit 1 is set, the font may not be embedded in a document.
michael@0 1958 // If bit 1 is clear, the font can be embedded.
michael@0 1959 // If bit 2 is set, the embedding is read-only.
michael@0 1960 if (otm.otmfsType & 0x1) {
michael@0 1961 info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
michael@0 1962 } else if (perGlyphInfo &
michael@0 1963 SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
michael@0 1964 if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
michael@0 1965 appendRange(&info->fGlyphWidths, 0);
michael@0 1966 info->fGlyphWidths->fAdvance.append(1, &min_width);
michael@0 1967 finishRange(info->fGlyphWidths.get(), 0,
michael@0 1968 SkAdvancedTypefaceMetrics::WidthRange::kDefault);
michael@0 1969 } else {
michael@0 1970 info->fGlyphWidths.reset(
michael@0 1971 getAdvanceData(hdc,
michael@0 1972 glyphCount,
michael@0 1973 glyphIDs,
michael@0 1974 glyphIDsCount,
michael@0 1975 &getWidthAdvance));
michael@0 1976 }
michael@0 1977 }
michael@0 1978
michael@0 1979 Error:
michael@0 1980 ReturnInfo:
michael@0 1981 SelectObject(hdc, savefont);
michael@0 1982 DeleteObject(designFont);
michael@0 1983 DeleteObject(font);
michael@0 1984 DeleteDC(hdc);
michael@0 1985
michael@0 1986 return info;
michael@0 1987 }
michael@0 1988
michael@0 1989 //Dummy representation of a Base64 encoded GUID from create_unique_font_name.
michael@0 1990 #define BASE64_GUID_ID "XXXXXXXXXXXXXXXXXXXXXXXX"
michael@0 1991 //Length of GUID representation from create_id, including NULL terminator.
michael@0 1992 #define BASE64_GUID_ID_LEN SK_ARRAY_COUNT(BASE64_GUID_ID)
michael@0 1993
michael@0 1994 SK_COMPILE_ASSERT(BASE64_GUID_ID_LEN < LF_FACESIZE, GUID_longer_than_facesize);
michael@0 1995
michael@0 1996 /**
michael@0 1997 NameID 6 Postscript names cannot have the character '/'.
michael@0 1998 It would be easier to hex encode the GUID, but that is 32 bytes,
michael@0 1999 and many systems have issues with names longer than 28 bytes.
michael@0 2000 The following need not be any standard base64 encoding.
michael@0 2001 The encoded value is never decoded.
michael@0 2002 */
michael@0 2003 static const char postscript_safe_base64_encode[] =
michael@0 2004 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
michael@0 2005 "abcdefghijklmnopqrstuvwxyz"
michael@0 2006 "0123456789-_=";
michael@0 2007
michael@0 2008 /**
michael@0 2009 Formats a GUID into Base64 and places it into buffer.
michael@0 2010 buffer should have space for at least BASE64_GUID_ID_LEN characters.
michael@0 2011 The string will always be null terminated.
michael@0 2012 XXXXXXXXXXXXXXXXXXXXXXXX0
michael@0 2013 */
michael@0 2014 static void format_guid_b64(const GUID& guid, char* buffer, size_t bufferSize) {
michael@0 2015 SkASSERT(bufferSize >= BASE64_GUID_ID_LEN);
michael@0 2016 size_t written = SkBase64::Encode(&guid, sizeof(guid), buffer, postscript_safe_base64_encode);
michael@0 2017 SkASSERT(written < LF_FACESIZE);
michael@0 2018 buffer[written] = '\0';
michael@0 2019 }
michael@0 2020
michael@0 2021 /**
michael@0 2022 Creates a Base64 encoded GUID and places it into buffer.
michael@0 2023 buffer should have space for at least BASE64_GUID_ID_LEN characters.
michael@0 2024 The string will always be null terminated.
michael@0 2025 XXXXXXXXXXXXXXXXXXXXXXXX0
michael@0 2026 */
michael@0 2027 static HRESULT create_unique_font_name(char* buffer, size_t bufferSize) {
michael@0 2028 GUID guid = {};
michael@0 2029 if (FAILED(CoCreateGuid(&guid))) {
michael@0 2030 return E_UNEXPECTED;
michael@0 2031 }
michael@0 2032 format_guid_b64(guid, buffer, bufferSize);
michael@0 2033
michael@0 2034 return S_OK;
michael@0 2035 }
michael@0 2036
michael@0 2037 /**
michael@0 2038 Introduces a font to GDI. On failure will return NULL. The returned handle
michael@0 2039 should eventually be passed to RemoveFontMemResourceEx.
michael@0 2040 */
michael@0 2041 static HANDLE activate_font(SkData* fontData) {
michael@0 2042 DWORD numFonts = 0;
michael@0 2043 //AddFontMemResourceEx just copies the data, but does not specify const.
michael@0 2044 HANDLE fontHandle = AddFontMemResourceEx(const_cast<void*>(fontData->data()),
michael@0 2045 static_cast<DWORD>(fontData->size()),
michael@0 2046 0,
michael@0 2047 &numFonts);
michael@0 2048
michael@0 2049 if (fontHandle != NULL && numFonts < 1) {
michael@0 2050 RemoveFontMemResourceEx(fontHandle);
michael@0 2051 return NULL;
michael@0 2052 }
michael@0 2053
michael@0 2054 return fontHandle;
michael@0 2055 }
michael@0 2056
michael@0 2057 static SkTypeface* create_from_stream(SkStream* stream) {
michael@0 2058 // Create a unique and unpredictable font name.
michael@0 2059 // Avoids collisions and access from CSS.
michael@0 2060 char familyName[BASE64_GUID_ID_LEN];
michael@0 2061 const int familyNameSize = SK_ARRAY_COUNT(familyName);
michael@0 2062 if (FAILED(create_unique_font_name(familyName, familyNameSize))) {
michael@0 2063 return NULL;
michael@0 2064 }
michael@0 2065
michael@0 2066 // Change the name of the font.
michael@0 2067 SkAutoTUnref<SkData> rewrittenFontData(SkOTUtils::RenameFont(stream, familyName, familyNameSize-1));
michael@0 2068 if (NULL == rewrittenFontData.get()) {
michael@0 2069 return NULL;
michael@0 2070 }
michael@0 2071
michael@0 2072 // Register the font with GDI.
michael@0 2073 HANDLE fontReference = activate_font(rewrittenFontData.get());
michael@0 2074 if (NULL == fontReference) {
michael@0 2075 return NULL;
michael@0 2076 }
michael@0 2077
michael@0 2078 // Create the typeface.
michael@0 2079 LOGFONT lf;
michael@0 2080 logfont_for_name(familyName, &lf);
michael@0 2081
michael@0 2082 return SkCreateFontMemResourceTypefaceFromLOGFONT(lf, fontReference);
michael@0 2083 }
michael@0 2084
michael@0 2085 SkStream* LogFontTypeface::onOpenStream(int* ttcIndex) const {
michael@0 2086 *ttcIndex = 0;
michael@0 2087
michael@0 2088 const DWORD kTTCTag =
michael@0 2089 SkEndian_SwapBE32(SkSetFourByteTag('t', 't', 'c', 'f'));
michael@0 2090 LOGFONT lf = fLogFont;
michael@0 2091
michael@0 2092 HDC hdc = ::CreateCompatibleDC(NULL);
michael@0 2093 HFONT font = CreateFontIndirect(&lf);
michael@0 2094 HFONT savefont = (HFONT)SelectObject(hdc, font);
michael@0 2095
michael@0 2096 SkMemoryStream* stream = NULL;
michael@0 2097 DWORD tables[2] = {kTTCTag, 0};
michael@0 2098 for (int i = 0; i < SK_ARRAY_COUNT(tables); i++) {
michael@0 2099 DWORD bufferSize = GetFontData(hdc, tables[i], 0, NULL, 0);
michael@0 2100 if (bufferSize == GDI_ERROR) {
michael@0 2101 call_ensure_accessible(lf);
michael@0 2102 bufferSize = GetFontData(hdc, tables[i], 0, NULL, 0);
michael@0 2103 }
michael@0 2104 if (bufferSize != GDI_ERROR) {
michael@0 2105 stream = new SkMemoryStream(bufferSize);
michael@0 2106 if (GetFontData(hdc, tables[i], 0, (void*)stream->getMemoryBase(), bufferSize)) {
michael@0 2107 break;
michael@0 2108 } else {
michael@0 2109 delete stream;
michael@0 2110 stream = NULL;
michael@0 2111 }
michael@0 2112 }
michael@0 2113 }
michael@0 2114
michael@0 2115 SelectObject(hdc, savefont);
michael@0 2116 DeleteObject(font);
michael@0 2117 DeleteDC(hdc);
michael@0 2118
michael@0 2119 return stream;
michael@0 2120 }
michael@0 2121
michael@0 2122 static void bmpCharsToGlyphs(HDC hdc, const WCHAR* bmpChars, int count, uint16_t* glyphs,
michael@0 2123 bool Ox1FHack)
michael@0 2124 {
michael@0 2125 DWORD result = GetGlyphIndicesW(hdc, bmpChars, count, glyphs, GGI_MARK_NONEXISTING_GLYPHS);
michael@0 2126 if (GDI_ERROR == result) {
michael@0 2127 for (int i = 0; i < count; ++i) {
michael@0 2128 glyphs[i] = 0;
michael@0 2129 }
michael@0 2130 return;
michael@0 2131 }
michael@0 2132
michael@0 2133 if (Ox1FHack) {
michael@0 2134 for (int i = 0; i < count; ++i) {
michael@0 2135 if (0xFFFF == glyphs[i] || 0x1F == glyphs[i]) {
michael@0 2136 glyphs[i] = 0;
michael@0 2137 }
michael@0 2138 }
michael@0 2139 } else {
michael@0 2140 for (int i = 0; i < count; ++i) {
michael@0 2141 if (0xFFFF == glyphs[i]){
michael@0 2142 glyphs[i] = 0;
michael@0 2143 }
michael@0 2144 }
michael@0 2145 }
michael@0 2146 }
michael@0 2147
michael@0 2148 static uint16_t nonBmpCharToGlyph(HDC hdc, SCRIPT_CACHE* scriptCache, const WCHAR utf16[2]) {
michael@0 2149 uint16_t index = 0;
michael@0 2150 // Use uniscribe to detemine glyph index for non-BMP characters.
michael@0 2151 static const int numWCHAR = 2;
michael@0 2152 static const int maxItems = 2;
michael@0 2153 // MSDN states that this can be NULL, but some things don't work then.
michael@0 2154 SCRIPT_CONTROL scriptControl = { 0 };
michael@0 2155 // Add extra item to SCRIPT_ITEM to work around a bug (now documented).
michael@0 2156 // https://bugzilla.mozilla.org/show_bug.cgi?id=366643
michael@0 2157 SCRIPT_ITEM si[maxItems + 1];
michael@0 2158 int numItems;
michael@0 2159 HRZM(ScriptItemize(utf16, numWCHAR, maxItems, &scriptControl, NULL, si, &numItems),
michael@0 2160 "Could not itemize character.");
michael@0 2161
michael@0 2162 // Sometimes ScriptShape cannot find a glyph for a non-BMP and returns 2 space glyphs.
michael@0 2163 static const int maxGlyphs = 2;
michael@0 2164 SCRIPT_VISATTR vsa[maxGlyphs];
michael@0 2165 WORD outGlyphs[maxGlyphs];
michael@0 2166 WORD logClust[numWCHAR];
michael@0 2167 int numGlyphs;
michael@0 2168 HRZM(ScriptShape(hdc, scriptCache, utf16, numWCHAR, maxGlyphs, &si[0].a,
michael@0 2169 outGlyphs, logClust, vsa, &numGlyphs),
michael@0 2170 "Could not shape character.");
michael@0 2171 if (1 == numGlyphs) {
michael@0 2172 index = outGlyphs[0];
michael@0 2173 }
michael@0 2174 return index;
michael@0 2175 }
michael@0 2176
michael@0 2177 class SkAutoHDC {
michael@0 2178 public:
michael@0 2179 SkAutoHDC(const LOGFONT& lf)
michael@0 2180 : fHdc(::CreateCompatibleDC(NULL))
michael@0 2181 , fFont(::CreateFontIndirect(&lf))
michael@0 2182 , fSavefont((HFONT)SelectObject(fHdc, fFont))
michael@0 2183 { }
michael@0 2184 ~SkAutoHDC() {
michael@0 2185 SelectObject(fHdc, fSavefont);
michael@0 2186 DeleteObject(fFont);
michael@0 2187 DeleteDC(fHdc);
michael@0 2188 }
michael@0 2189 operator HDC() { return fHdc; }
michael@0 2190 private:
michael@0 2191 HDC fHdc;
michael@0 2192 HFONT fFont;
michael@0 2193 HFONT fSavefont;
michael@0 2194 };
michael@0 2195 #define SkAutoHDC(...) SK_REQUIRE_LOCAL_VAR(SkAutoHDC)
michael@0 2196
michael@0 2197 int LogFontTypeface::onCharsToGlyphs(const void* chars, Encoding encoding,
michael@0 2198 uint16_t userGlyphs[], int glyphCount) const
michael@0 2199 {
michael@0 2200 SkAutoHDC hdc(fLogFont);
michael@0 2201
michael@0 2202 TEXTMETRIC tm;
michael@0 2203 if (0 == GetTextMetrics(hdc, &tm)) {
michael@0 2204 call_ensure_accessible(fLogFont);
michael@0 2205 if (0 == GetTextMetrics(hdc, &tm)) {
michael@0 2206 tm.tmPitchAndFamily = TMPF_TRUETYPE;
michael@0 2207 }
michael@0 2208 }
michael@0 2209 bool Ox1FHack = !(tm.tmPitchAndFamily & TMPF_VECTOR) /*&& winVer < Vista */;
michael@0 2210
michael@0 2211 SkAutoSTMalloc<256, uint16_t> scratchGlyphs;
michael@0 2212 uint16_t* glyphs;
michael@0 2213 if (userGlyphs != NULL) {
michael@0 2214 glyphs = userGlyphs;
michael@0 2215 } else {
michael@0 2216 glyphs = scratchGlyphs.reset(glyphCount);
michael@0 2217 }
michael@0 2218
michael@0 2219 SCRIPT_CACHE sc = 0;
michael@0 2220 switch (encoding) {
michael@0 2221 case SkTypeface::kUTF8_Encoding: {
michael@0 2222 static const int scratchCount = 256;
michael@0 2223 WCHAR scratch[scratchCount];
michael@0 2224 int glyphIndex = 0;
michael@0 2225 const char* currentUtf8 = reinterpret_cast<const char*>(chars);
michael@0 2226 SkUnichar currentChar;
michael@0 2227 if (glyphCount) {
michael@0 2228 currentChar = SkUTF8_NextUnichar(&currentUtf8);
michael@0 2229 }
michael@0 2230 while (glyphIndex < glyphCount) {
michael@0 2231 // Try a run of bmp.
michael@0 2232 int glyphsLeft = SkTMin(glyphCount - glyphIndex, scratchCount);
michael@0 2233 int runLength = 0;
michael@0 2234 while (runLength < glyphsLeft && currentChar <= 0xFFFF) {
michael@0 2235 scratch[runLength] = static_cast<WCHAR>(currentChar);
michael@0 2236 ++runLength;
michael@0 2237 if (runLength < glyphsLeft) {
michael@0 2238 currentChar = SkUTF8_NextUnichar(&currentUtf8);
michael@0 2239 }
michael@0 2240 }
michael@0 2241 if (runLength) {
michael@0 2242 bmpCharsToGlyphs(hdc, scratch, runLength, &glyphs[glyphIndex], Ox1FHack);
michael@0 2243 glyphIndex += runLength;
michael@0 2244 }
michael@0 2245
michael@0 2246 // Try a run of non-bmp.
michael@0 2247 while (glyphIndex < glyphCount && currentChar > 0xFFFF) {
michael@0 2248 SkUTF16_FromUnichar(currentChar, reinterpret_cast<uint16_t*>(scratch));
michael@0 2249 glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, scratch);
michael@0 2250 ++glyphIndex;
michael@0 2251 if (glyphIndex < glyphCount) {
michael@0 2252 currentChar = SkUTF8_NextUnichar(&currentUtf8);
michael@0 2253 }
michael@0 2254 }
michael@0 2255 }
michael@0 2256 break;
michael@0 2257 }
michael@0 2258 case SkTypeface::kUTF16_Encoding: {
michael@0 2259 int glyphIndex = 0;
michael@0 2260 const WCHAR* currentUtf16 = reinterpret_cast<const WCHAR*>(chars);
michael@0 2261 while (glyphIndex < glyphCount) {
michael@0 2262 // Try a run of bmp.
michael@0 2263 int glyphsLeft = glyphCount - glyphIndex;
michael@0 2264 int runLength = 0;
michael@0 2265 while (runLength < glyphsLeft && !SkUTF16_IsHighSurrogate(currentUtf16[runLength])) {
michael@0 2266 ++runLength;
michael@0 2267 }
michael@0 2268 if (runLength) {
michael@0 2269 bmpCharsToGlyphs(hdc, currentUtf16, runLength, &glyphs[glyphIndex], Ox1FHack);
michael@0 2270 glyphIndex += runLength;
michael@0 2271 currentUtf16 += runLength;
michael@0 2272 }
michael@0 2273
michael@0 2274 // Try a run of non-bmp.
michael@0 2275 while (glyphIndex < glyphCount && SkUTF16_IsHighSurrogate(*currentUtf16)) {
michael@0 2276 glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, currentUtf16);
michael@0 2277 ++glyphIndex;
michael@0 2278 currentUtf16 += 2;
michael@0 2279 }
michael@0 2280 }
michael@0 2281 break;
michael@0 2282 }
michael@0 2283 case SkTypeface::kUTF32_Encoding: {
michael@0 2284 static const int scratchCount = 256;
michael@0 2285 WCHAR scratch[scratchCount];
michael@0 2286 int glyphIndex = 0;
michael@0 2287 const uint32_t* utf32 = reinterpret_cast<const uint32_t*>(chars);
michael@0 2288 while (glyphIndex < glyphCount) {
michael@0 2289 // Try a run of bmp.
michael@0 2290 int glyphsLeft = SkTMin(glyphCount - glyphIndex, scratchCount);
michael@0 2291 int runLength = 0;
michael@0 2292 while (runLength < glyphsLeft && utf32[glyphIndex + runLength] <= 0xFFFF) {
michael@0 2293 scratch[runLength] = static_cast<WCHAR>(utf32[glyphIndex + runLength]);
michael@0 2294 ++runLength;
michael@0 2295 }
michael@0 2296 if (runLength) {
michael@0 2297 bmpCharsToGlyphs(hdc, scratch, runLength, &glyphs[glyphIndex], Ox1FHack);
michael@0 2298 glyphIndex += runLength;
michael@0 2299 }
michael@0 2300
michael@0 2301 // Try a run of non-bmp.
michael@0 2302 while (glyphIndex < glyphCount && utf32[glyphIndex] > 0xFFFF) {
michael@0 2303 SkUTF16_FromUnichar(utf32[glyphIndex], reinterpret_cast<uint16_t*>(scratch));
michael@0 2304 glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, scratch);
michael@0 2305 ++glyphIndex;
michael@0 2306 }
michael@0 2307 }
michael@0 2308 break;
michael@0 2309 }
michael@0 2310 default:
michael@0 2311 SK_CRASH();
michael@0 2312 }
michael@0 2313
michael@0 2314 if (sc) {
michael@0 2315 ::ScriptFreeCache(&sc);
michael@0 2316 }
michael@0 2317
michael@0 2318 for (int i = 0; i < glyphCount; ++i) {
michael@0 2319 if (0 == glyphs[i]) {
michael@0 2320 return i;
michael@0 2321 }
michael@0 2322 }
michael@0 2323 return glyphCount;
michael@0 2324 }
michael@0 2325
michael@0 2326 int LogFontTypeface::onCountGlyphs() const {
michael@0 2327 HDC hdc = ::CreateCompatibleDC(NULL);
michael@0 2328 HFONT font = CreateFontIndirect(&fLogFont);
michael@0 2329 HFONT savefont = (HFONT)SelectObject(hdc, font);
michael@0 2330
michael@0 2331 unsigned int glyphCount = calculateGlyphCount(hdc, fLogFont);
michael@0 2332
michael@0 2333 SelectObject(hdc, savefont);
michael@0 2334 DeleteObject(font);
michael@0 2335 DeleteDC(hdc);
michael@0 2336
michael@0 2337 return glyphCount;
michael@0 2338 }
michael@0 2339
michael@0 2340 int LogFontTypeface::onGetUPEM() const {
michael@0 2341 HDC hdc = ::CreateCompatibleDC(NULL);
michael@0 2342 HFONT font = CreateFontIndirect(&fLogFont);
michael@0 2343 HFONT savefont = (HFONT)SelectObject(hdc, font);
michael@0 2344
michael@0 2345 unsigned int upem = calculateUPEM(hdc, fLogFont);
michael@0 2346
michael@0 2347 SelectObject(hdc, savefont);
michael@0 2348 DeleteObject(font);
michael@0 2349 DeleteDC(hdc);
michael@0 2350
michael@0 2351 return upem;
michael@0 2352 }
michael@0 2353
michael@0 2354 SkTypeface::LocalizedStrings* LogFontTypeface::onCreateFamilyNameIterator() const {
michael@0 2355 SkTypeface::LocalizedStrings* nameIter =
michael@0 2356 SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*this);
michael@0 2357 if (NULL == nameIter) {
michael@0 2358 SkString familyName;
michael@0 2359 this->getFamilyName(&familyName);
michael@0 2360 SkString language("und"); //undetermined
michael@0 2361 nameIter = new SkOTUtils::LocalizedStrings_SingleName(familyName, language);
michael@0 2362 }
michael@0 2363 return nameIter;
michael@0 2364 }
michael@0 2365
michael@0 2366 int LogFontTypeface::onGetTableTags(SkFontTableTag tags[]) const {
michael@0 2367 SkSFNTHeader header;
michael@0 2368 if (sizeof(header) != this->onGetTableData(0, 0, sizeof(header), &header)) {
michael@0 2369 return 0;
michael@0 2370 }
michael@0 2371
michael@0 2372 int numTables = SkEndian_SwapBE16(header.numTables);
michael@0 2373
michael@0 2374 if (tags) {
michael@0 2375 size_t size = numTables * sizeof(SkSFNTHeader::TableDirectoryEntry);
michael@0 2376 SkAutoSTMalloc<0x20, SkSFNTHeader::TableDirectoryEntry> dir(numTables);
michael@0 2377 if (size != this->onGetTableData(0, sizeof(header), size, dir.get())) {
michael@0 2378 return 0;
michael@0 2379 }
michael@0 2380
michael@0 2381 for (int i = 0; i < numTables; ++i) {
michael@0 2382 tags[i] = SkEndian_SwapBE32(dir[i].tag);
michael@0 2383 }
michael@0 2384 }
michael@0 2385 return numTables;
michael@0 2386 }
michael@0 2387
michael@0 2388 size_t LogFontTypeface::onGetTableData(SkFontTableTag tag, size_t offset,
michael@0 2389 size_t length, void* data) const
michael@0 2390 {
michael@0 2391 LOGFONT lf = fLogFont;
michael@0 2392
michael@0 2393 HDC hdc = ::CreateCompatibleDC(NULL);
michael@0 2394 HFONT font = CreateFontIndirect(&lf);
michael@0 2395 HFONT savefont = (HFONT)SelectObject(hdc, font);
michael@0 2396
michael@0 2397 tag = SkEndian_SwapBE32(tag);
michael@0 2398 if (NULL == data) {
michael@0 2399 length = 0;
michael@0 2400 }
michael@0 2401 DWORD bufferSize = GetFontData(hdc, tag, (DWORD) offset, data, (DWORD) length);
michael@0 2402 if (bufferSize == GDI_ERROR) {
michael@0 2403 call_ensure_accessible(lf);
michael@0 2404 bufferSize = GetFontData(hdc, tag, (DWORD) offset, data, (DWORD) length);
michael@0 2405 }
michael@0 2406
michael@0 2407 SelectObject(hdc, savefont);
michael@0 2408 DeleteObject(font);
michael@0 2409 DeleteDC(hdc);
michael@0 2410
michael@0 2411 return bufferSize == GDI_ERROR ? 0 : bufferSize;
michael@0 2412 }
michael@0 2413
michael@0 2414 SkScalerContext* LogFontTypeface::onCreateScalerContext(const SkDescriptor* desc) const {
michael@0 2415 SkScalerContext_GDI* ctx = SkNEW_ARGS(SkScalerContext_GDI,
michael@0 2416 (const_cast<LogFontTypeface*>(this), desc));
michael@0 2417 if (!ctx->isValid()) {
michael@0 2418 SkDELETE(ctx);
michael@0 2419 ctx = NULL;
michael@0 2420 }
michael@0 2421 return ctx;
michael@0 2422 }
michael@0 2423
michael@0 2424 void LogFontTypeface::onFilterRec(SkScalerContextRec* rec) const {
michael@0 2425 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
michael@0 2426 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
michael@0 2427 {
michael@0 2428 rec->fMaskFormat = SkMask::kA8_Format;
michael@0 2429 rec->fFlags |= SkScalerContext::kGenA8FromLCD_Flag;
michael@0 2430 }
michael@0 2431
michael@0 2432 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
michael@0 2433 SkScalerContext::kForceAutohinting_Flag |
michael@0 2434 SkScalerContext::kEmbeddedBitmapText_Flag |
michael@0 2435 SkScalerContext::kEmbolden_Flag |
michael@0 2436 SkScalerContext::kLCD_BGROrder_Flag |
michael@0 2437 SkScalerContext::kLCD_Vertical_Flag;
michael@0 2438 rec->fFlags &= ~flagsWeDontSupport;
michael@0 2439
michael@0 2440 SkPaint::Hinting h = rec->getHinting();
michael@0 2441 switch (h) {
michael@0 2442 case SkPaint::kNo_Hinting:
michael@0 2443 break;
michael@0 2444 case SkPaint::kSlight_Hinting:
michael@0 2445 // Only do slight hinting when axis aligned.
michael@0 2446 // TODO: re-enable slight hinting when FontHostTest can pass.
michael@0 2447 //if (!isAxisAligned(*rec)) {
michael@0 2448 h = SkPaint::kNo_Hinting;
michael@0 2449 //}
michael@0 2450 break;
michael@0 2451 case SkPaint::kNormal_Hinting:
michael@0 2452 case SkPaint::kFull_Hinting:
michael@0 2453 // TODO: need to be able to distinguish subpixel positioned glyphs
michael@0 2454 // and linear metrics.
michael@0 2455 //rec->fFlags &= ~SkScalerContext::kSubpixelPositioning_Flag;
michael@0 2456 h = SkPaint::kNormal_Hinting;
michael@0 2457 break;
michael@0 2458 default:
michael@0 2459 SkDEBUGFAIL("unknown hinting");
michael@0 2460 }
michael@0 2461 //TODO: if this is a bitmap font, squash hinting and subpixel.
michael@0 2462 rec->setHinting(h);
michael@0 2463
michael@0 2464 // turn this off since GDI might turn A8 into BW! Need a bigger fix.
michael@0 2465 #if 0
michael@0 2466 // Disable LCD when rotated, since GDI's output is ugly
michael@0 2467 if (isLCD(*rec) && !isAxisAligned(*rec)) {
michael@0 2468 rec->fMaskFormat = SkMask::kA8_Format;
michael@0 2469 }
michael@0 2470 #endif
michael@0 2471
michael@0 2472 if (!fCanBeLCD && isLCD(*rec)) {
michael@0 2473 rec->fMaskFormat = SkMask::kA8_Format;
michael@0 2474 rec->fFlags &= ~SkScalerContext::kGenA8FromLCD_Flag;
michael@0 2475 }
michael@0 2476 }
michael@0 2477
michael@0 2478 ///////////////////////////////////////////////////////////////////////////////
michael@0 2479
michael@0 2480 #include "SkFontMgr.h"
michael@0 2481 #include "SkDataTable.h"
michael@0 2482
michael@0 2483 static bool valid_logfont_for_enum(const LOGFONT& lf) {
michael@0 2484 // TODO: Vector FON is unsupported and should not be listed.
michael@0 2485 return
michael@0 2486 // Ignore implicit vertical variants.
michael@0 2487 lf.lfFaceName[0] && lf.lfFaceName[0] != '@'
michael@0 2488
michael@0 2489 // DEFAULT_CHARSET is used to get all fonts, but also implies all
michael@0 2490 // character sets. Filter assuming all fonts support ANSI_CHARSET.
michael@0 2491 && ANSI_CHARSET == lf.lfCharSet
michael@0 2492 ;
michael@0 2493 }
michael@0 2494
michael@0 2495 /** An EnumFontFamExProc implementation which interprets builderParam as
michael@0 2496 * an SkTDArray<ENUMLOGFONTEX>* and appends logfonts which
michael@0 2497 * pass the valid_logfont_for_enum predicate.
michael@0 2498 */
michael@0 2499 static int CALLBACK enum_family_proc(const LOGFONT* lf, const TEXTMETRIC*,
michael@0 2500 DWORD fontType, LPARAM builderParam) {
michael@0 2501 if (valid_logfont_for_enum(*lf)) {
michael@0 2502 SkTDArray<ENUMLOGFONTEX>* array = (SkTDArray<ENUMLOGFONTEX>*)builderParam;
michael@0 2503 *array->append() = *(ENUMLOGFONTEX*)lf;
michael@0 2504 }
michael@0 2505 return 1; // non-zero means continue
michael@0 2506 }
michael@0 2507
michael@0 2508 static SkFontStyle compute_fontstyle(const LOGFONT& lf) {
michael@0 2509 return SkFontStyle(lf.lfWeight, SkFontStyle::kNormal_Width,
michael@0 2510 lf.lfItalic ? SkFontStyle::kItalic_Slant
michael@0 2511 : SkFontStyle::kUpright_Slant);
michael@0 2512 }
michael@0 2513
michael@0 2514 class SkFontStyleSetGDI : public SkFontStyleSet {
michael@0 2515 public:
michael@0 2516 SkFontStyleSetGDI(const TCHAR familyName[]) {
michael@0 2517 LOGFONT lf;
michael@0 2518 sk_bzero(&lf, sizeof(lf));
michael@0 2519 lf.lfCharSet = DEFAULT_CHARSET;
michael@0 2520 _tcscpy_s(lf.lfFaceName, familyName);
michael@0 2521
michael@0 2522 HDC hdc = ::CreateCompatibleDC(NULL);
michael@0 2523 ::EnumFontFamiliesEx(hdc, &lf, enum_family_proc, (LPARAM)&fArray, 0);
michael@0 2524 ::DeleteDC(hdc);
michael@0 2525 }
michael@0 2526
michael@0 2527 virtual int count() SK_OVERRIDE {
michael@0 2528 return fArray.count();
michael@0 2529 }
michael@0 2530
michael@0 2531 virtual void getStyle(int index, SkFontStyle* fs, SkString* styleName) SK_OVERRIDE {
michael@0 2532 if (fs) {
michael@0 2533 *fs = compute_fontstyle(fArray[index].elfLogFont);
michael@0 2534 }
michael@0 2535 if (styleName) {
michael@0 2536 const ENUMLOGFONTEX& ref = fArray[index];
michael@0 2537 // For some reason, ENUMLOGFONTEX and LOGFONT disagree on their type in the
michael@0 2538 // non-unicode version.
michael@0 2539 // ENUMLOGFONTEX uses BYTE
michael@0 2540 // LOGFONT uses CHAR
michael@0 2541 // Here we assert they that the style name is logically the same (size) as
michael@0 2542 // a TCHAR, so we can use the same converter function.
michael@0 2543 SkASSERT(sizeof(TCHAR) == sizeof(ref.elfStyle[0]));
michael@0 2544 tchar_to_skstring((const TCHAR*)ref.elfStyle, styleName);
michael@0 2545 }
michael@0 2546 }
michael@0 2547
michael@0 2548 virtual SkTypeface* createTypeface(int index) SK_OVERRIDE {
michael@0 2549 return SkCreateTypefaceFromLOGFONT(fArray[index].elfLogFont);
michael@0 2550 }
michael@0 2551
michael@0 2552 virtual SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE {
michael@0 2553 // todo:
michael@0 2554 return SkCreateTypefaceFromLOGFONT(fArray[0].elfLogFont);
michael@0 2555 }
michael@0 2556
michael@0 2557 private:
michael@0 2558 SkTDArray<ENUMLOGFONTEX> fArray;
michael@0 2559 };
michael@0 2560
michael@0 2561 class SkFontMgrGDI : public SkFontMgr {
michael@0 2562 public:
michael@0 2563 SkFontMgrGDI() {
michael@0 2564 LOGFONT lf;
michael@0 2565 sk_bzero(&lf, sizeof(lf));
michael@0 2566 lf.lfCharSet = DEFAULT_CHARSET;
michael@0 2567
michael@0 2568 HDC hdc = ::CreateCompatibleDC(NULL);
michael@0 2569 ::EnumFontFamiliesEx(hdc, &lf, enum_family_proc, (LPARAM)&fLogFontArray, 0);
michael@0 2570 ::DeleteDC(hdc);
michael@0 2571 }
michael@0 2572
michael@0 2573 protected:
michael@0 2574 virtual int onCountFamilies() const SK_OVERRIDE {
michael@0 2575 return fLogFontArray.count();
michael@0 2576 }
michael@0 2577
michael@0 2578 virtual void onGetFamilyName(int index, SkString* familyName) const SK_OVERRIDE {
michael@0 2579 SkASSERT((unsigned)index < (unsigned)fLogFontArray.count());
michael@0 2580 tchar_to_skstring(fLogFontArray[index].elfLogFont.lfFaceName, familyName);
michael@0 2581 }
michael@0 2582
michael@0 2583 virtual SkFontStyleSet* onCreateStyleSet(int index) const SK_OVERRIDE {
michael@0 2584 SkASSERT((unsigned)index < (unsigned)fLogFontArray.count());
michael@0 2585 return SkNEW_ARGS(SkFontStyleSetGDI, (fLogFontArray[index].elfLogFont.lfFaceName));
michael@0 2586 }
michael@0 2587
michael@0 2588 virtual SkFontStyleSet* onMatchFamily(const char familyName[]) const SK_OVERRIDE {
michael@0 2589 if (NULL == familyName) {
michael@0 2590 familyName = ""; // do we need this check???
michael@0 2591 }
michael@0 2592 LOGFONT lf;
michael@0 2593 logfont_for_name(familyName, &lf);
michael@0 2594 return SkNEW_ARGS(SkFontStyleSetGDI, (lf.lfFaceName));
michael@0 2595 }
michael@0 2596
michael@0 2597 virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
michael@0 2598 const SkFontStyle& fontstyle) const SK_OVERRIDE {
michael@0 2599 // could be in base impl
michael@0 2600 SkAutoTUnref<SkFontStyleSet> sset(this->matchFamily(familyName));
michael@0 2601 return sset->matchStyle(fontstyle);
michael@0 2602 }
michael@0 2603
michael@0 2604 virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
michael@0 2605 const SkFontStyle& fontstyle) const SK_OVERRIDE {
michael@0 2606 // could be in base impl
michael@0 2607 SkString familyName;
michael@0 2608 ((LogFontTypeface*)familyMember)->getFamilyName(&familyName);
michael@0 2609 return this->matchFamilyStyle(familyName.c_str(), fontstyle);
michael@0 2610 }
michael@0 2611
michael@0 2612 virtual SkTypeface* onCreateFromStream(SkStream* stream, int ttcIndex) const SK_OVERRIDE {
michael@0 2613 return create_from_stream(stream);
michael@0 2614 }
michael@0 2615
michael@0 2616 virtual SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const SK_OVERRIDE {
michael@0 2617 // could be in base impl
michael@0 2618 SkAutoTUnref<SkStream> stream(SkNEW_ARGS(SkMemoryStream, (data)));
michael@0 2619 return this->createFromStream(stream);
michael@0 2620 }
michael@0 2621
michael@0 2622 virtual SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const SK_OVERRIDE {
michael@0 2623 // could be in base impl
michael@0 2624 SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(path));
michael@0 2625 return this->createFromStream(stream);
michael@0 2626 }
michael@0 2627
michael@0 2628 virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
michael@0 2629 unsigned styleBits) const SK_OVERRIDE {
michael@0 2630 LOGFONT lf;
michael@0 2631 if (NULL == familyName) {
michael@0 2632 lf = get_default_font();
michael@0 2633 } else {
michael@0 2634 logfont_for_name(familyName, &lf);
michael@0 2635 }
michael@0 2636 setStyle(&lf, (SkTypeface::Style)styleBits);
michael@0 2637 return SkCreateTypefaceFromLOGFONT(lf);
michael@0 2638 }
michael@0 2639
michael@0 2640 private:
michael@0 2641 SkTDArray<ENUMLOGFONTEX> fLogFontArray;
michael@0 2642 };
michael@0 2643
michael@0 2644 ///////////////////////////////////////////////////////////////////////////////
michael@0 2645
michael@0 2646 SkFontMgr* SkFontMgr_New_GDI() {
michael@0 2647 return SkNEW(SkFontMgrGDI);
michael@0 2648 }

mercurial