SkFontHost_win.cpp revision 99edd43813b7f1a8f02146cbd8c783d3c82be4ab
1 2/* 3 * Copyright 2006 The Android Open Source Project 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10#include "SkString.h" 11#include "SkEndian.h" 12#include "SkFontHost.h" 13#include "SkDescriptor.h" 14#include "SkAdvancedTypefaceMetrics.h" 15#include "SkStream.h" 16#include "SkThread.h" 17#include "SkTypeface_win.h" 18#include "SkTypefaceCache.h" 19#include "SkUtils.h" 20 21#ifdef WIN32 22#include "windows.h" 23#include "tchar.h" 24#include "Usp10.h" 25 26// define this in your Makefile or .gyp to enforce AA requests 27// which GDI ignores at small sizes. This flag guarantees AA 28// for rotated text, regardless of GDI's notions. 29//#define SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS 30 31// client3d has to undefine this for now 32#define CAN_USE_LOGFONT_NAME 33 34static bool isLCD(const SkScalerContext::Rec& rec) { 35 return SkMask::kLCD16_Format == rec.fMaskFormat || 36 SkMask::kLCD32_Format == rec.fMaskFormat; 37} 38 39static bool bothZero(SkScalar a, SkScalar b) { 40 return 0 == a && 0 == b; 41} 42 43// returns false if there is any non-90-rotation or skew 44static bool isAxisAligned(const SkScalerContext::Rec& rec) { 45 return 0 == rec.fPreSkewX && 46 (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) || 47 bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1])); 48} 49 50static bool needToRenderWithSkia(const SkScalerContext::Rec& rec) { 51#ifdef SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS 52 // What we really want to catch is when GDI will ignore the AA request and give 53 // us BW instead. Smallish rotated text is one heuristic, so this code is just 54 // an approximation. We shouldn't need to do this for larger sizes, but at those 55 // sizes, the quality difference gets less and less between our general 56 // scanconverter and GDI's. 57 if (SkMask::kA8_Format == rec.fMaskFormat && !isAxisAligned(rec)) { 58 return true; 59 } 60#endif 61 // false means allow GDI to generate the bits 62 return false; 63} 64 65using namespace skia_advanced_typeface_metrics_utils; 66 67static const uint16_t BUFFERSIZE = (16384 - 32); 68static uint8_t glyphbuf[BUFFERSIZE]; 69 70// Give 1MB font cache budget 71#define FONT_CACHE_MEMORY_BUDGET (1024 * 1024) 72 73/** 74 * Since LOGFONT wants its textsize as an int, and we support fractional sizes, 75 * and since we have a cache of LOGFONTs for our tyepfaces, we always set the 76 * lfHeight to a canonical size, and then we use the 2x2 matrix to achieve the 77 * actual requested size. 78 */ 79static const int gCanonicalTextSize = 64; 80 81static void make_canonical(LOGFONT* lf) { 82 lf->lfHeight = -gCanonicalTextSize; 83 lf->lfQuality = CLEARTYPE_QUALITY;//PROOF_QUALITY; 84 lf->lfCharSet = DEFAULT_CHARSET; 85// lf->lfClipPrecision = 64; 86} 87 88static SkTypeface::Style getStyle(const LOGFONT& lf) { 89 unsigned style = 0; 90 if (lf.lfWeight >= FW_BOLD) { 91 style |= SkTypeface::kBold; 92 } 93 if (lf.lfItalic) { 94 style |= SkTypeface::kItalic; 95 } 96 return (SkTypeface::Style)style; 97} 98 99static void setStyle(LOGFONT* lf, SkTypeface::Style style) { 100 lf->lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL ; 101 lf->lfItalic = ((style & SkTypeface::kItalic) != 0); 102} 103 104static inline FIXED SkFixedToFIXED(SkFixed x) { 105 return *(FIXED*)(&x); 106} 107 108static inline FIXED SkScalarToFIXED(SkScalar x) { 109 return SkFixedToFIXED(SkScalarToFixed(x)); 110} 111 112static unsigned calculateGlyphCount(HDC hdc) { 113 // The 'maxp' table stores the number of glyphs at offset 4, in 2 bytes. 114 const DWORD maxpTag = 115 SkEndian_SwapBE32(SkSetFourByteTag('m', 'a', 'x', 'p')); 116 uint16_t glyphs; 117 if (GetFontData(hdc, maxpTag, 4, &glyphs, sizeof(glyphs)) != GDI_ERROR) { 118 return SkEndian_SwapBE16(glyphs); 119 } 120 121 // Binary search for glyph count. 122 static const MAT2 mat2 = {{0, 1}, {0, 0}, {0, 0}, {0, 1}}; 123 int32_t max = SK_MaxU16 + 1; 124 int32_t min = 0; 125 GLYPHMETRICS gm; 126 while (min < max) { 127 int32_t mid = min + ((max - min) / 2); 128 if (GetGlyphOutlineW(hdc, mid, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, 129 NULL, &mat2) == GDI_ERROR) { 130 max = mid; 131 } else { 132 min = mid + 1; 133 } 134 } 135 SkASSERT(min == max); 136 return min; 137} 138 139static SkTypeface::Style GetFontStyle(const LOGFONT& lf) { 140 int style = SkTypeface::kNormal; 141 if (lf.lfWeight == FW_SEMIBOLD || lf.lfWeight == FW_DEMIBOLD || lf.lfWeight == FW_BOLD) 142 style |= SkTypeface::kBold; 143 if (lf.lfItalic) 144 style |= SkTypeface::kItalic; 145 146 return (SkTypeface::Style)style; 147} 148 149class LogFontTypeface : public SkTypeface { 150public: 151 LogFontTypeface(SkTypeface::Style style, SkFontID fontID, const LOGFONT& lf) : 152 SkTypeface(style, fontID, false), fLogFont(lf) {} 153 154 LOGFONT fLogFont; 155 156 static LogFontTypeface* Create(const LOGFONT& lf) { 157 SkTypeface::Style style = GetFontStyle(lf); 158 SkFontID fontID = SkTypefaceCache::NewFontID(); 159 return new LogFontTypeface(style, fontID, lf); 160 } 161}; 162 163static const LOGFONT& get_default_font() { 164 static LOGFONT gDefaultFont; 165 return gDefaultFont; 166} 167 168static bool FindByLogFont(SkTypeface* face, SkTypeface::Style requestedStyle, void* ctx) { 169 LogFontTypeface* lface = reinterpret_cast<LogFontTypeface*>(face); 170 const LOGFONT* lf = reinterpret_cast<const LOGFONT*>(ctx); 171 172 return getStyle(lface->fLogFont) == requestedStyle && 173 !memcmp(&lface->fLogFont, lf, sizeof(LOGFONT)); 174} 175 176/** 177 * This guy is public. It first searches the cache, and if a match is not found, 178 * it creates a new face. 179 */ 180SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& origLF) { 181 LOGFONT lf = origLF; 182 make_canonical(&lf); 183 SkTypeface* face = SkTypefaceCache::FindByProc(FindByLogFont, &lf); 184 if (face) { 185 face->ref(); 186 } else { 187 face = LogFontTypeface::Create(lf); 188 SkTypefaceCache::Add(face, getStyle(lf)); 189 } 190 return face; 191} 192 193/** 194 * This guy is public 195 */ 196void SkLOGFONTFromTypeface(const SkTypeface* face, LOGFONT* lf) { 197 if (NULL == face) { 198 *lf = get_default_font(); 199 } else { 200 *lf = ((const LogFontTypeface*)face)->fLogFont; 201 } 202} 203 204SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) { 205 // Zero means that we don't have any fallback fonts for this fontID. 206 // This function is implemented on Android, but doesn't have much 207 // meaning here. 208 return 0; 209} 210 211static void GetLogFontByID(SkFontID fontID, LOGFONT* lf) { 212 LogFontTypeface* face = (LogFontTypeface*)SkTypefaceCache::FindByID(fontID); 213 if (face) { 214 *lf = face->fLogFont; 215 } else { 216 sk_bzero(lf, sizeof(LOGFONT)); 217 } 218} 219 220// Construct Glyph to Unicode table. 221// Unicode code points that require conjugate pairs in utf16 are not 222// supported. 223// TODO(arthurhsu): Add support for conjugate pairs. It looks like that may 224// require parsing the TTF cmap table (platform 4, encoding 12) directly instead 225// of calling GetFontUnicodeRange(). 226static void populate_glyph_to_unicode(HDC fontHdc, const unsigned glyphCount, 227 SkTDArray<SkUnichar>* glyphToUnicode) { 228 DWORD glyphSetBufferSize = GetFontUnicodeRanges(fontHdc, NULL); 229 if (!glyphSetBufferSize) { 230 return; 231 } 232 233 SkAutoTDeleteArray<BYTE> glyphSetBuffer(new BYTE[glyphSetBufferSize]); 234 GLYPHSET* glyphSet = 235 reinterpret_cast<LPGLYPHSET>(glyphSetBuffer.get()); 236 if (GetFontUnicodeRanges(fontHdc, glyphSet) != glyphSetBufferSize) { 237 return; 238 } 239 240 glyphToUnicode->setCount(glyphCount); 241 memset(glyphToUnicode->begin(), 0, glyphCount * sizeof(SkUnichar)); 242 for (DWORD i = 0; i < glyphSet->cRanges; ++i) { 243 // There is no guarantee that within a Unicode range, the corresponding 244 // glyph id in a font file are continuous. So, even if we have ranges, 245 // we can't just use the first and last entry of the range to compute 246 // result. We need to enumerate them one by one. 247 int count = glyphSet->ranges[i].cGlyphs; 248 SkAutoTArray<WCHAR> chars(count + 1); 249 chars[count] = 0; // termintate string 250 SkAutoTArray<WORD> glyph(count); 251 for (USHORT j = 0; j < count; ++j) { 252 chars[j] = glyphSet->ranges[i].wcLow + j; 253 } 254 GetGlyphIndicesW(fontHdc, chars.get(), count, glyph.get(), 255 GGI_MARK_NONEXISTING_GLYPHS); 256 // If the glyph ID is valid, and the glyph is not mapped, then we will 257 // fill in the char id into the vector. If the glyph is mapped already, 258 // skip it. 259 // TODO(arthurhsu): better improve this. e.g. Get all used char ids from 260 // font cache, then generate this mapping table from there. It's 261 // unlikely to have collisions since glyph reuse happens mostly for 262 // different Unicode pages. 263 for (USHORT j = 0; j < count; ++j) { 264 if (glyph[j] != 0xffff && glyph[j] < glyphCount && 265 (*glyphToUnicode)[glyph[j]] == 0) { 266 (*glyphToUnicode)[glyph[j]] = chars[j]; 267 } 268 } 269 } 270} 271 272////////////////////////////////////////////////////////////////////////////////////// 273 274static int alignTo32(int n) { 275 return (n + 31) & ~31; 276} 277 278struct MyBitmapInfo : public BITMAPINFO { 279 RGBQUAD fMoreSpaceForColors[1]; 280}; 281 282class HDCOffscreen { 283public: 284 HDCOffscreen() { 285 fFont = 0; 286 fDC = 0; 287 fBM = 0; 288 fBits = NULL; 289 fWidth = fHeight = 0; 290 fIsBW = false; 291 } 292 293 ~HDCOffscreen() { 294 if (fDC) { 295 DeleteDC(fDC); 296 } 297 if (fBM) { 298 DeleteObject(fBM); 299 } 300 } 301 302 void init(HFONT font, const XFORM& xform) { 303 fFont = font; 304 fXform = xform; 305 } 306 307 const void* draw(const SkGlyph&, bool isBW, size_t* srcRBPtr); 308 309private: 310 HDC fDC; 311 HBITMAP fBM; 312 HFONT fFont; 313 XFORM fXform; 314 void* fBits; // points into fBM 315 int fWidth; 316 int fHeight; 317 bool fIsBW; 318}; 319 320const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW, size_t* srcRBPtr) { 321 if (0 == fDC) { 322 fDC = CreateCompatibleDC(0); 323 if (0 == fDC) { 324 return NULL; 325 } 326 SetGraphicsMode(fDC, GM_ADVANCED); 327 SetBkMode(fDC, TRANSPARENT); 328 SetTextAlign(fDC, TA_LEFT | TA_BASELINE); 329 SelectObject(fDC, fFont); 330 COLORREF color = SetTextColor(fDC, fIsBW ? 0xFFFFFF : 0); 331 SkASSERT(color != CLR_INVALID); 332 } 333 334 if (fBM && (fIsBW != isBW || fWidth < glyph.fWidth || fHeight < glyph.fHeight)) { 335 DeleteObject(fBM); 336 fBM = 0; 337 } 338 339 if (fIsBW != isBW) { 340 fIsBW = isBW; 341 COLORREF color = SetTextColor(fDC, fIsBW ? 0xFFFFFF : 0); 342 SkASSERT(color != CLR_INVALID); 343 } 344 fWidth = SkMax32(fWidth, glyph.fWidth); 345 fHeight = SkMax32(fHeight, glyph.fHeight); 346 347 int biWidth = isBW ? alignTo32(fWidth) : fWidth; 348 349 if (0 == fBM) { 350 MyBitmapInfo info; 351 sk_bzero(&info, sizeof(info)); 352 if (isBW) { 353 RGBQUAD blackQuad = { 0, 0, 0, 0 }; 354 RGBQUAD whiteQuad = { 0xFF, 0xFF, 0xFF, 0 }; 355 info.bmiColors[0] = blackQuad; 356 info.bmiColors[1] = whiteQuad; 357 } 358 info.bmiHeader.biSize = sizeof(info.bmiHeader); 359 info.bmiHeader.biWidth = biWidth; 360 info.bmiHeader.biHeight = fHeight; 361 info.bmiHeader.biPlanes = 1; 362 info.bmiHeader.biBitCount = isBW ? 1 : 32; 363 info.bmiHeader.biCompression = BI_RGB; 364 if (isBW) { 365 info.bmiHeader.biClrUsed = 2; 366 } 367 fBM = CreateDIBSection(fDC, &info, DIB_RGB_COLORS, &fBits, 0, 0); 368 if (0 == fBM) { 369 return NULL; 370 } 371 SelectObject(fDC, fBM); 372 } 373 374 // erase 375 size_t srcRB = isBW ? (biWidth >> 3) : (fWidth << 2); 376 size_t size = fHeight * srcRB; 377 memset(fBits, isBW ? 0 : 0xFF, size); 378 379 XFORM xform = fXform; 380 xform.eDx = (float)-glyph.fLeft; 381 xform.eDy = (float)-glyph.fTop; 382 SetWorldTransform(fDC, &xform); 383 384 uint16_t glyphID = glyph.getGlyphID(); 385#if defined(UNICODE) 386 ExtTextOut(fDC, 0, 0, ETO_GLYPH_INDEX, NULL, (LPCWSTR)&glyphID, 1, NULL); 387#else 388 ExtTextOut(fDC, 0, 0, ETO_GLYPH_INDEX, NULL, (LPCSTR)&glyphID, 1, NULL); 389#endif 390 GdiFlush(); 391 392 *srcRBPtr = srcRB; 393 // offset to the start of the image 394 return (const char*)fBits + (fHeight - glyph.fHeight) * srcRB; 395} 396 397////////////////////////////////////////////////////////////////////////////////////// 398 399class SkScalerContext_Windows : public SkScalerContext { 400public: 401 SkScalerContext_Windows(const SkDescriptor* desc); 402 virtual ~SkScalerContext_Windows(); 403 404protected: 405 virtual unsigned generateGlyphCount(); 406 virtual uint16_t generateCharToGlyph(SkUnichar uni); 407 virtual void generateAdvance(SkGlyph* glyph); 408 virtual void generateMetrics(SkGlyph* glyph); 409 virtual void generateImage(const SkGlyph& glyph); 410 virtual void generatePath(const SkGlyph& glyph, SkPath* path); 411 virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY); 412 413private: 414 HDCOffscreen fOffscreen; 415 SkScalar fScale; // to get from canonical size to real size 416 MAT2 fMat22; 417 XFORM fXform; 418 HDC fDDC; 419 HFONT fSavefont; 420 HFONT fFont; 421 SCRIPT_CACHE fSC; 422 int fGlyphCount; 423 424 HFONT fHiResFont; 425 MAT2 fMat22Identity; 426 SkMatrix fHiResMatrix; 427}; 428 429static float mul2float(SkScalar a, SkScalar b) { 430 return SkScalarToFloat(SkScalarMul(a, b)); 431} 432 433static FIXED float2FIXED(float x) { 434 return SkFixedToFIXED(SkFloatToFixed(x)); 435} 436 437static SkMutex gFTMutex; 438 439#define HIRES_TEXTSIZE 2048 440#define HIRES_SHIFT 11 441static inline SkFixed HiResToFixed(int value) { 442 return value << (16 - HIRES_SHIFT); 443} 444 445static bool needHiResMetrics(const SkScalar mat[2][2]) { 446 return mat[1][0] || mat[0][1]; 447} 448 449static BYTE compute_quality(const SkScalerContext::Rec& rec) { 450 switch (rec.fMaskFormat) { 451 case SkMask::kBW_Format: 452 return NONANTIALIASED_QUALITY; 453 case SkMask::kLCD16_Format: 454 case SkMask::kLCD32_Format: 455 return CLEARTYPE_QUALITY; 456 default: 457 return ANTIALIASED_QUALITY; 458 } 459} 460 461SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc) 462 : SkScalerContext(desc), fDDC(0), fFont(0), fSavefont(0), fSC(0) 463 , fGlyphCount(-1) { 464 SkAutoMutexAcquire ac(gFTMutex); 465 466 fScale = fRec.fTextSize / gCanonicalTextSize; 467 468 fXform.eM11 = mul2float(fScale, fRec.fPost2x2[0][0]); 469 fXform.eM12 = mul2float(fScale, fRec.fPost2x2[1][0]); 470 fXform.eM21 = mul2float(fScale, fRec.fPost2x2[0][1]); 471 fXform.eM22 = mul2float(fScale, fRec.fPost2x2[1][1]); 472 fXform.eDx = 0; 473 fXform.eDy = 0; 474 475 fMat22.eM11 = float2FIXED(fXform.eM11); 476 fMat22.eM12 = float2FIXED(fXform.eM12); 477 fMat22.eM21 = float2FIXED(-fXform.eM21); 478 fMat22.eM22 = float2FIXED(-fXform.eM22); 479 480 fDDC = ::CreateCompatibleDC(NULL); 481 SetGraphicsMode(fDDC, GM_ADVANCED); 482 SetBkMode(fDDC, TRANSPARENT); 483 484 // Scaling by the DPI is inconsistent with how Skia draws elsewhere 485 //SkScalar height = -(fRec.fTextSize * GetDeviceCaps(ddc, LOGPIXELSY) / 72); 486 LOGFONT lf; 487 GetLogFontByID(fRec.fFontID, &lf); 488 lf.lfHeight = -gCanonicalTextSize; 489 lf.lfQuality = compute_quality(fRec); 490 fFont = CreateFontIndirect(&lf); 491 492 // if we're rotated, or want fractional widths, create a hires font 493 fHiResFont = 0; 494 if (needHiResMetrics(fRec.fPost2x2) || (fRec.fFlags & kSubpixelPositioning_Flag)) { 495 lf.lfHeight = -HIRES_TEXTSIZE; 496 fHiResFont = CreateFontIndirect(&lf); 497 498 fMat22Identity.eM11 = fMat22Identity.eM22 = SkFixedToFIXED(SK_Fixed1); 499 fMat22Identity.eM12 = fMat22Identity.eM21 = SkFixedToFIXED(0); 500 501 // construct a matrix to go from HIRES logical units to our device units 502 fRec.getSingleMatrix(&fHiResMatrix); 503 SkScalar scale = SkScalarInvert(SkIntToScalar(HIRES_TEXTSIZE)); 504 fHiResMatrix.preScale(scale, scale); 505 } 506 fSavefont = (HFONT)SelectObject(fDDC, fFont); 507 508 if (needToRenderWithSkia(fRec)) { 509 this->forceGenerateImageFromPath(); 510 } 511 512 fOffscreen.init(fFont, fXform); 513} 514 515SkScalerContext_Windows::~SkScalerContext_Windows() { 516 if (fDDC) { 517 ::SelectObject(fDDC, fSavefont); 518 ::DeleteDC(fDDC); 519 } 520 if (fFont) { 521 ::DeleteObject(fFont); 522 } 523 if (fHiResFont) { 524 ::DeleteObject(fHiResFont); 525 } 526 if (fSC) { 527 ::ScriptFreeCache(&fSC); 528 } 529} 530 531unsigned SkScalerContext_Windows::generateGlyphCount() { 532 if (fGlyphCount < 0) { 533 fGlyphCount = calculateGlyphCount(fDDC); 534 } 535 return fGlyphCount; 536} 537 538uint16_t SkScalerContext_Windows::generateCharToGlyph(SkUnichar uni) { 539 uint16_t index = 0; 540 WCHAR c[2]; 541 // TODO(ctguil): Support characters that generate more than one glyph. 542 if (SkUTF16_FromUnichar(uni, (uint16_t*)c) == 1) { 543 // Type1 fonts fail with uniscribe API. Use GetGlyphIndices for plane 0. 544 SkAssertResult(GetGlyphIndicesW(fDDC, c, 1, &index, 0)); 545 } else { 546 // Use uniscribe to detemine glyph index for non-BMP characters. 547 // Need to add extra item to SCRIPT_ITEM to work around a bug in older 548 // windows versions. https://bugzilla.mozilla.org/show_bug.cgi?id=366643 549 SCRIPT_ITEM si[2 + 1]; 550 int items; 551 SkAssertResult( 552 SUCCEEDED(ScriptItemize(c, 2, 2, NULL, NULL, si, &items))); 553 554 WORD log[2]; 555 SCRIPT_VISATTR vsa; 556 int glyphs; 557 SkAssertResult(SUCCEEDED(ScriptShape( 558 fDDC, &fSC, c, 2, 1, &si[0].a, &index, log, &vsa, &glyphs))); 559 } 560 return index; 561} 562 563void SkScalerContext_Windows::generateAdvance(SkGlyph* glyph) { 564 this->generateMetrics(glyph); 565} 566 567void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) { 568 569 SkASSERT(fDDC); 570 571 GLYPHMETRICS gm; 572 sk_bzero(&gm, sizeof(gm)); 573 574 glyph->fRsbDelta = 0; 575 glyph->fLsbDelta = 0; 576 577 // Note: need to use GGO_GRAY8_BITMAP instead of GGO_METRICS because GGO_METRICS returns a smaller 578 // BlackBlox; we need the bigger one in case we need the image. fAdvance is the same. 579 uint32_t ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22); 580 581 if (GDI_ERROR != ret) { 582 if (ret == 0) { 583 // for white space, ret is zero and gmBlackBoxX, gmBlackBoxY are 1 incorrectly! 584 gm.gmBlackBoxX = gm.gmBlackBoxY = 0; 585 } 586 glyph->fWidth = gm.gmBlackBoxX; 587 glyph->fHeight = gm.gmBlackBoxY; 588 glyph->fTop = SkToS16(gm.gmptGlyphOrigin.y - gm.gmBlackBoxY); 589 glyph->fLeft = SkToS16(gm.gmptGlyphOrigin.x); 590 glyph->fAdvanceX = SkIntToFixed(gm.gmCellIncX); 591 glyph->fAdvanceY = -SkIntToFixed(gm.gmCellIncY); 592 593 // we outset in all dimensions, since the image may bleed outside 594 // of the computed bounds returned by GetGlyphOutline. 595 // This was deduced by trial and error for small text (e.g. 8pt), so there 596 // maybe a more precise way to make this adjustment... 597 // 598 // This test shows us clipping the tops of some of the CJK fonts unless we 599 // increase the top of the box by 2, hence the height by 4. This seems to 600 // correspond to an embedded bitmap font, but not sure. 601 // LayoutTests/fast/text/backslash-to-yen-sign-euc.html 602 // 603 glyph->fWidth += 4; 604 glyph->fHeight += 4; 605 glyph->fTop -= 2; 606 glyph->fLeft -= 2; 607 608 if (fHiResFont) { 609 SelectObject(fDDC, fHiResFont); 610 sk_bzero(&gm, sizeof(gm)); 611 ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22Identity); 612 if (GDI_ERROR != ret) { 613 SkPoint advance; 614 fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance); 615 glyph->fAdvanceX = SkScalarToFixed(advance.fX); 616 glyph->fAdvanceY = SkScalarToFixed(advance.fY); 617 } 618 SelectObject(fDDC, fFont); 619 } 620 } else { 621 glyph->fWidth = 0; 622 } 623} 624 625void SkScalerContext_Windows::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) { 626// Note: This code was borrowed from generateLineHeight, which has a note 627// stating that it may be incorrect. 628 if (!(mx || my)) 629 return; 630 631 SkASSERT(fDDC); 632 633 OUTLINETEXTMETRIC otm; 634 635 uint32_t ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm); 636 if (sizeof(otm) != ret) { 637 return; 638 } 639 640 if (mx) { 641 mx->fTop = -fScale * otm.otmTextMetrics.tmAscent; 642 mx->fAscent = -fScale * otm.otmAscent; 643 mx->fDescent = -fScale * otm.otmDescent; 644 mx->fBottom = fScale * otm.otmTextMetrics.tmDescent; 645 mx->fLeading = fScale * (otm.otmTextMetrics.tmInternalLeading 646 + otm.otmTextMetrics.tmExternalLeading); 647 } 648 649 if (my) { 650 my->fTop = -fScale * otm.otmTextMetrics.tmAscent; 651 my->fAscent = -fScale * otm.otmAscent; 652 my->fDescent = -fScale * otm.otmDescent; 653 my->fBottom = fScale * otm.otmTextMetrics.tmDescent; 654 my->fLeading = fScale * (otm.otmTextMetrics.tmInternalLeading 655 + otm.otmTextMetrics.tmExternalLeading); 656 } 657} 658 659#include "SkColorPriv.h" 660 661static inline uint8_t rgb_to_a8(uint32_t rgb) { 662 // can pick any component, since we're grayscale 663 int r = (rgb >> 16) & 0xFF; 664 // invert, since we draw black-on-white, but we want the original 665 // src mask values. 666 return 255 - r; 667} 668 669static inline uint16_t rgb_to_lcd16(uint32_t rgb) { 670 rgb = ~rgb; // 255 - each component 671 int r = (rgb >> 16) & 0xFF; 672 int g = (rgb >> 8) & 0xFF; 673 int b = (rgb >> 0) & 0xFF; 674 return SkPackRGB16(SkR32ToR16(r), SkG32ToG16(g), SkB32ToB16(b)); 675} 676 677void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) { 678 679 SkAutoMutexAcquire ac(gFTMutex); 680 681 SkASSERT(fDDC); 682 683 const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat; 684 const bool isAA = !isLCD(fRec); 685 686 size_t srcRB; 687 const void* bits = fOffscreen.draw(glyph, isBW, &srcRB); 688 if (!bits) { 689 sk_bzero(glyph.fImage, glyph.computeImageSize()); 690 return; 691 } 692 693 int width = glyph.fWidth; 694 size_t dstRB = glyph.rowBytes(); 695 if (isBW) { 696 const uint8_t* src = (const uint8_t*)bits; 697 // gdi's bitmap is upside-down, so we reverse dst walking in Y 698 uint8_t* dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB); 699 for (int y = 0; y < glyph.fHeight; y++) { 700 memcpy(dst, src, dstRB); 701 src += srcRB; 702 dst -= dstRB; 703 } 704 } else if (isAA) { 705 const uint32_t* src = (const uint32_t*)bits; 706 // gdi's bitmap is upside-down, so we reverse dst walking in Y 707 uint8_t* dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB); 708 for (int y = 0; y < glyph.fHeight; y++) { 709 for (int i = 0; i < width; i++) { 710 dst[i] = rgb_to_a8(src[i]); 711 } 712 src = (const uint32_t*)((const char*)src + srcRB); 713 dst -= dstRB; 714 } 715 } else { // LCD16 716 const uint32_t* src = (const uint32_t*)bits; 717 // gdi's bitmap is upside-down, so we reverse dst walking in Y 718 uint16_t* dst = (uint16_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB); 719 for (int y = 0; y < glyph.fHeight; y++) { 720 for (int i = 0; i < width; i++) { 721 dst[i] = rgb_to_lcd16(src[i]); 722 } 723 src = (const uint32_t*)((const char*)src + srcRB); 724 dst = (uint16_t*)((char*)dst - dstRB); 725 } 726 } 727} 728 729void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) { 730 731 SkAutoMutexAcquire ac(gFTMutex); 732 733 SkASSERT(&glyph && path); 734 SkASSERT(fDDC); 735 736 path->reset(); 737 738#if 0 739 char buf[1024]; 740 sprintf(buf, "generatePath: id:%d, w=%d, h=%d, font:%s,fh:%d\n", glyph.fID, glyph.fWidth, glyph.fHeight, lf.lfFaceName, lf.lfHeight); 741 OutputDebugString(buf); 742#endif 743 744 GLYPHMETRICS gm; 745 uint32_t total_size = GetGlyphOutlineW(fDDC, glyph.fID, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, BUFFERSIZE, glyphbuf, &fMat22); 746 747 if (GDI_ERROR != total_size) { 748 749 const uint8_t* cur_glyph = glyphbuf; 750 const uint8_t* end_glyph = glyphbuf + total_size; 751 752 while(cur_glyph < end_glyph) { 753 const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph; 754 755 const uint8_t* end_poly = cur_glyph + th->cb; 756 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER); 757 758 path->moveTo(SkFixedToScalar(*(SkFixed*)(&th->pfxStart.x)), SkFixedToScalar(*(SkFixed*)(&th->pfxStart.y))); 759 760 while(cur_poly < end_poly) { 761 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly; 762 763 if (pc->wType == TT_PRIM_LINE) { 764 for (uint16_t i = 0; i < pc->cpfx; i++) { 765 path->lineTo(SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].x)), SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].y))); 766 } 767 } 768 769 if (pc->wType == TT_PRIM_QSPLINE) { 770 for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline 771 POINTFX pnt_b = pc->apfx[u]; // B is always the current point 772 POINTFX pnt_c = pc->apfx[u+1]; 773 774 if (u < pc->cpfx - 2) { // If not on last spline, compute C 775 pnt_c.x = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.x), *(SkFixed*)(&pnt_c.x))); 776 pnt_c.y = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.y), *(SkFixed*)(&pnt_c.y))); 777 } 778 779 path->quadTo(SkFixedToScalar(*(SkFixed*)(&pnt_b.x)), SkFixedToScalar(*(SkFixed*)(&pnt_b.y)), SkFixedToScalar(*(SkFixed*)(&pnt_c.x)), SkFixedToScalar(*(SkFixed*)(&pnt_c.y))); 780 } 781 } 782 cur_poly += sizeof(uint16_t) * 2 + sizeof(POINTFX) * pc->cpfx; 783 } 784 cur_glyph += th->cb; 785 path->close(); 786 } 787 } 788 else { 789 SkASSERT(false); 790 } 791 //char buf[1024]; 792 //sprintf(buf, "generatePath: count:%d\n", count); 793 //OutputDebugString(buf); 794} 795 796void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) { 797 SkASSERT(!"SkFontHost::Serialize unimplemented"); 798} 799 800SkTypeface* SkFontHost::Deserialize(SkStream* stream) { 801 SkASSERT(!"SkFontHost::Deserialize unimplemented"); 802 return NULL; 803} 804 805static bool getWidthAdvance(HDC hdc, int gId, int16_t* advance) { 806 // Initialize the MAT2 structure to the identify transformation matrix. 807 static const MAT2 mat2 = {SkScalarToFIXED(1), SkScalarToFIXED(0), 808 SkScalarToFIXED(0), SkScalarToFIXED(1)}; 809 int flags = GGO_METRICS | GGO_GLYPH_INDEX; 810 GLYPHMETRICS gm; 811 if (GDI_ERROR == GetGlyphOutline(hdc, gId, flags, &gm, 0, NULL, &mat2)) { 812 return false; 813 } 814 SkASSERT(advance); 815 *advance = gm.gmCellIncX; 816 return true; 817} 818 819// static 820SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics( 821 uint32_t fontID, 822 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo, 823 const uint32_t* glyphIDs, 824 uint32_t glyphIDsCount) { 825 LOGFONT lf; 826 GetLogFontByID(fontID, &lf); 827 SkAdvancedTypefaceMetrics* info = NULL; 828 829 HDC hdc = CreateCompatibleDC(NULL); 830 HFONT font = CreateFontIndirect(&lf); 831 HFONT savefont = (HFONT)SelectObject(hdc, font); 832 HFONT designFont = NULL; 833 834 // To request design units, create a logical font whose height is specified 835 // as unitsPerEm. 836 OUTLINETEXTMETRIC otm; 837 if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm) || 838 !GetTextFace(hdc, LF_FACESIZE, lf.lfFaceName)) { 839 goto Error; 840 } 841 lf.lfHeight = -SkToS32(otm.otmEMSquare); 842 designFont = CreateFontIndirect(&lf); 843 SelectObject(hdc, designFont); 844 if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) { 845 goto Error; 846 } 847 const unsigned glyphCount = calculateGlyphCount(hdc); 848 849 info = new SkAdvancedTypefaceMetrics; 850 info->fEmSize = otm.otmEMSquare; 851 info->fMultiMaster = false; 852 info->fLastGlyphID = SkToU16(glyphCount - 1); 853 info->fStyle = 0; 854#ifdef UNICODE 855 // Get the buffer size needed first. 856 size_t str_len = WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1, NULL, 857 0, NULL, NULL); 858 // Allocate a buffer (str_len already has terminating null accounted for). 859 char *familyName = new char[str_len]; 860 // Now actually convert the string. 861 WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1, familyName, str_len, 862 NULL, NULL); 863 info->fFontName.set(familyName); 864 delete [] familyName; 865#else 866 info->fFontName.set(lf.lfFaceName); 867#endif 868 869 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) { 870 populate_glyph_to_unicode(hdc, glyphCount, &(info->fGlyphToUnicode)); 871 } 872 873 if (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE) { 874 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font; 875 } else { 876 info->fType = SkAdvancedTypefaceMetrics::kOther_Font; 877 info->fItalicAngle = 0; 878 info->fAscent = 0; 879 info->fDescent = 0; 880 info->fStemV = 0; 881 info->fCapHeight = 0; 882 info->fBBox = SkIRect::MakeEmpty(); 883 return info; 884 } 885 886 // If this bit is clear the font is a fixed pitch font. 887 if (!(otm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) { 888 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style; 889 } 890 if (otm.otmTextMetrics.tmItalic) { 891 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style; 892 } 893 // Setting symbolic style by default for now. 894 info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style; 895 if (otm.otmTextMetrics.tmPitchAndFamily & FF_ROMAN) { 896 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style; 897 } else if (otm.otmTextMetrics.tmPitchAndFamily & FF_SCRIPT) { 898 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style; 899 } 900 901 // The main italic angle of the font, in tenths of a degree counterclockwise 902 // from vertical. 903 info->fItalicAngle = otm.otmItalicAngle / 10; 904 info->fAscent = SkToS16(otm.otmTextMetrics.tmAscent); 905 info->fDescent = SkToS16(-otm.otmTextMetrics.tmDescent); 906 // TODO(ctguil): Use alternate cap height calculation. 907 // MSDN says otmsCapEmHeight is not support but it is returning a value on 908 // my Win7 box. 909 info->fCapHeight = otm.otmsCapEmHeight; 910 info->fBBox = 911 SkIRect::MakeLTRB(otm.otmrcFontBox.left, otm.otmrcFontBox.top, 912 otm.otmrcFontBox.right, otm.otmrcFontBox.bottom); 913 914 // Figure out a good guess for StemV - Min width of i, I, !, 1. 915 // This probably isn't very good with an italic font. 916 int16_t min_width = SHRT_MAX; 917 info->fStemV = 0; 918 char stem_chars[] = {'i', 'I', '!', '1'}; 919 for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) { 920 ABC abcWidths; 921 if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) { 922 int16_t width = abcWidths.abcB; 923 if (width > 0 && width < min_width) { 924 min_width = width; 925 info->fStemV = min_width; 926 } 927 } 928 } 929 930 // If bit 1 is set, the font may not be embedded in a document. 931 // If bit 1 is clear, the font can be embedded. 932 // If bit 2 is set, the embedding is read-only. 933 if (otm.otmfsType & 0x1) { 934 info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font; 935 } else if (perGlyphInfo & 936 SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) { 937 if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) { 938 appendRange(&info->fGlyphWidths, 0); 939 info->fGlyphWidths->fAdvance.append(1, &min_width); 940 finishRange(info->fGlyphWidths.get(), 0, 941 SkAdvancedTypefaceMetrics::WidthRange::kDefault); 942 } else { 943 info->fGlyphWidths.reset( 944 getAdvanceData(hdc, 945 glyphCount, 946 glyphIDs, 947 glyphIDsCount, 948 &getWidthAdvance)); 949 } 950 } 951 952Error: 953 SelectObject(hdc, savefont); 954 DeleteObject(designFont); 955 DeleteObject(font); 956 DeleteDC(hdc); 957 958 return info; 959} 960 961SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) { 962 963 //Should not be used on Windows, keep linker happy 964 SkASSERT(false); 965 return SkCreateTypefaceFromLOGFONT(get_default_font()); 966} 967 968SkStream* SkFontHost::OpenStream(SkFontID uniqueID) { 969 const DWORD kTTCTag = 970 SkEndian_SwapBE32(SkSetFourByteTag('t', 't', 'c', 'f')); 971 LOGFONT lf; 972 GetLogFontByID(uniqueID, &lf); 973 974 HDC hdc = ::CreateCompatibleDC(NULL); 975 HFONT font = CreateFontIndirect(&lf); 976 HFONT savefont = (HFONT)SelectObject(hdc, font); 977 978 SkMemoryStream* stream = NULL; 979 DWORD tables[2] = {kTTCTag, 0}; 980 for (int i = 0; i < SK_ARRAY_COUNT(tables); i++) { 981 size_t bufferSize = GetFontData(hdc, tables[i], 0, NULL, 0); 982 if (bufferSize != GDI_ERROR) { 983 stream = new SkMemoryStream(bufferSize); 984 if (GetFontData(hdc, tables[i], 0, (void*)stream->getMemoryBase(), 985 bufferSize)) { 986 break; 987 } else { 988 delete stream; 989 stream = NULL; 990 } 991 } 992 } 993 994 SelectObject(hdc, savefont); 995 DeleteObject(font); 996 DeleteDC(hdc); 997 998 return stream; 999} 1000 1001SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) { 1002 return SkNEW_ARGS(SkScalerContext_Windows, (desc)); 1003} 1004 1005/** Return the closest matching typeface given either an existing family 1006 (specified by a typeface in that family) or by a familyName, and a 1007 requested style. 1008 1) If familyFace is null, use famillyName. 1009 2) If famillyName is null, use familyFace. 1010 3) If both are null, return the default font that best matches style 1011 This MUST not return NULL. 1012 */ 1013 1014SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, 1015 const char familyName[], 1016 const void* data, size_t bytelength, 1017 SkTypeface::Style style) { 1018 LOGFONT lf; 1019 if (NULL == familyFace && NULL == familyName) { 1020 lf = get_default_font(); 1021 } else if (familyFace) { 1022 LogFontTypeface* face = (LogFontTypeface*)familyFace; 1023 lf = face->fLogFont; 1024 } else { 1025 memset(&lf, 0, sizeof(LOGFONT)); 1026#ifdef UNICODE 1027 // Get the buffer size needed first. 1028 size_t str_len = ::MultiByteToWideChar(CP_UTF8, 0, familyName, 1029 -1, NULL, 0); 1030 // Allocate a buffer (str_len already has terminating null 1031 // accounted for). 1032 wchar_t *wideFamilyName = new wchar_t[str_len]; 1033 // Now actually convert the string. 1034 ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1, 1035 wideFamilyName, str_len); 1036 ::wcsncpy(lf.lfFaceName, wideFamilyName, LF_FACESIZE); 1037 delete [] wideFamilyName; 1038#else 1039 ::strncpy(lf.lfFaceName, familyName, LF_FACESIZE); 1040#endif 1041 lf.lfFaceName[LF_FACESIZE-1] = '\0'; 1042 } 1043 setStyle(&lf, style); 1044 return SkCreateTypefaceFromLOGFONT(lf); 1045} 1046 1047size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) { 1048 if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET) 1049 return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET; 1050 else 1051 return 0; // nothing to do 1052} 1053 1054int SkFontHost::ComputeGammaFlag(const SkPaint& paint) { 1055 return 0; 1056} 1057 1058void SkFontHost::GetGammaTables(const uint8_t* tables[2]) { 1059 tables[0] = NULL; // black gamma (e.g. exp=1.4) 1060 tables[1] = NULL; // white gamma (e.g. exp= 1/1.4) 1061} 1062 1063SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) { 1064 printf("SkFontHost::CreateTypefaceFromFile unimplemented"); 1065 return NULL; 1066} 1067 1068void SkFontHost::FilterRec(SkScalerContext::Rec* rec) { 1069 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag | 1070 SkScalerContext::kAutohinting_Flag | 1071 SkScalerContext::kEmbeddedBitmapText_Flag | 1072 SkScalerContext::kEmbolden_Flag | 1073 SkScalerContext::kLCD_BGROrder_Flag | 1074 SkScalerContext::kLCD_Vertical_Flag; 1075 rec->fFlags &= ~flagsWeDontSupport; 1076 1077 SkPaint::Hinting h = rec->getHinting(); 1078 1079 // I think we can support no-hinting, if we get hires outlines and just 1080 // use skia to rasterize into a gray-scale mask... 1081#if 0 1082 switch (h) { 1083 case SkPaint::kNo_Hinting: 1084 case SkPaint::kSlight_Hinting: 1085 h = SkPaint::kNo_Hinting; 1086 break; 1087 case SkPaint::kNormal_Hinting: 1088 case SkPaint::kFull_Hinting: 1089 h = SkPaint::kNormal_Hinting; 1090 break; 1091 default: 1092 SkASSERT(!"unknown hinting"); 1093 } 1094#else 1095 h = SkPaint::kNormal_Hinting; 1096#endif 1097 rec->setHinting(h); 1098 1099// turn this off since GDI might turn A8 into BW! Need a bigger fix. 1100#if 0 1101 // Disable LCD when rotated, since GDI's output is ugly 1102 if (isLCD(*rec) && !isAxisAligned(*rec)) { 1103 rec->fMaskFormat = SkMask::kA8_Format; 1104 } 1105#endif 1106} 1107 1108#endif // WIN32 1109