aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2011-03-11 15:42:51 +0000
committerGravatar reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2011-03-11 15:42:51 +0000
commitac6b97972d3c5a8e10b79c1409818756310e0f12 (patch)
treedd5f45b32395c827c1f25caeeec6809c3d15a609
parentf77e27deb95ff6fd6e6e30dcd3f6bf1862c394e1 (diff)
support lcd16 mask format
convert line-endings to unix style (the default for skia) git-svn-id: http://skia.googlecode.com/svn/trunk@923 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--src/ports/SkFontHost_win.cpp1698
1 files changed, 894 insertions, 804 deletions
diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp
index a20f001efb..a4f430c9d8 100644
--- a/src/ports/SkFontHost_win.cpp
+++ b/src/ports/SkFontHost_win.cpp
@@ -1,804 +1,894 @@
-/*
- ** Copyright 2006, The Android Open Source Project
- **
- ** Licensed under the Apache License, Version 2.0 (the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
- **
- ** http://www.apache.org/licenses/LICENSE-2.0
- **
- ** Unless required by applicable law or agreed to in writing, software
- ** distributed under the License is distributed on an "AS IS" BASIS,
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ** See the License for the specific language governing permissions and
- ** limitations under the License.
- */
-
-#include "SkString.h"
-//#include "SkStream.h"
-
-#include "SkEndian.h"
-#include "SkFontHost.h"
-#include "SkDescriptor.h"
-#include "SkAdvancedTypefaceMetrics.h"
-#include "SkStream.h"
-#include "SkThread.h"
-#include "SkTypeface_win.h"
-#include "SkUtils.h"
-
-#ifdef WIN32
-#include "windows.h"
-#include "tchar.h"
-#include "Usp10.h"
-
-// client3d has to undefine this for now
-#define CAN_USE_LOGFONT_NAME
-
-using namespace skia_advanced_typeface_metrics_utils;
-
-static SkMutex gFTMutex;
-
-static const uint16_t BUFFERSIZE = (16384 - 32);
-static uint8_t glyphbuf[BUFFERSIZE];
-
-// Give 1MB font cache budget
-#define FONT_CACHE_MEMORY_BUDGET (1024 * 1024)
-
-/**
- * Since LOGFONT wants its textsize as an int, and we support fractional sizes,
- * and since we have a cache of LOGFONTs for our tyepfaces, we always set the
- * lfHeight to a canonical size, and then we use the 2x2 matrix to achieve the
- * actual requested size.
- */
-static const int gCanonicalTextSize = 64;
-
-static void make_canonical(LOGFONT* lf) {
- lf->lfHeight = -gCanonicalTextSize;
-}
-
-static inline FIXED SkFixedToFIXED(SkFixed x) {
- return *(FIXED*)(&x);
-}
-
-static inline FIXED SkScalarToFIXED(SkScalar x) {
- return SkFixedToFIXED(SkScalarToFixed(x));
-}
-
-static unsigned calculateGlyphCount(HDC hdc) {
- // The 'maxp' table stores the number of glyphs at offset 4, in 2 bytes.
- const DWORD maxpTag = *(DWORD*) "maxp";
- uint16_t glyphs;
- if (GetFontData(hdc, maxpTag, 4, &glyphs, sizeof(glyphs)) != GDI_ERROR) {
- return SkEndian_SwapBE16(glyphs);
- }
-
- // Binary search for glyph count.
- static const MAT2 mat2 = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
- int32_t max = SK_MaxU16 + 1;
- int32_t min = 0;
- GLYPHMETRICS gm;
- while (min < max) {
- int32_t mid = min + ((max - min) / 2);
- if (GetGlyphOutlineW(hdc, mid, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0,
- NULL, &mat2) == GDI_ERROR) {
- max = mid;
- } else {
- min = mid + 1;
- }
- }
- SkASSERT(min == max);
- return min;
-}
-
-static SkTypeface::Style GetFontStyle(const LOGFONT& lf) {
- int style = SkTypeface::kNormal;
- if (lf.lfWeight == FW_SEMIBOLD || lf.lfWeight == FW_DEMIBOLD || lf.lfWeight == FW_BOLD)
- style |= SkTypeface::kBold;
- if (lf.lfItalic)
- style |= SkTypeface::kItalic;
-
- return (SkTypeface::Style)style;
-}
-
-// have to do this because SkTypeface::SkTypeface() is protected
-class LogFontTypeface : public SkTypeface {
-private:
- static SkMutex gMutex;
- static LogFontTypeface* gHead;
- static int32_t gCurrId;
-
- LogFontTypeface* fNext;
- LOGFONT fLogFont;
-
-public:
-
- LogFontTypeface(Style style, const LOGFONT& logFont) :
- SkTypeface(style, sk_atomic_inc(&gCurrId)+1), // 0 id is reserved so add 1
- fLogFont(logFont)
- {
- make_canonical(&fLogFont);
-
- SkAutoMutexAcquire am(gMutex);
- fNext = gHead;
- gHead = this;
- }
-
- const LOGFONT& logFont() const { return fLogFont; }
-
- virtual ~LogFontTypeface() {
- SkAutoMutexAcquire am(gMutex);
- if (gHead == this) {
- gHead = fNext;
- return;
- }
-
- LogFontTypeface* prev = gHead;
- SkASSERT(prev);
- while (prev->fNext != this) {
- prev = prev->fNext;
- SkASSERT(prev);
- }
- prev->fNext = fNext;
- }
-
- static LogFontTypeface* FindById(uint32_t id){
- SkASSERT(gHead);
- LogFontTypeface* curr = gHead;
- while (curr->uniqueID() != id) {
- curr = curr->fNext;
- SkASSERT(curr);
- }
- return curr;
- }
-
- static LogFontTypeface* FindByLogFont(const LOGFONT& lf)
- {
- LOGFONT canonical = lf;
- make_canonical(&canonical);
-
- LogFontTypeface* curr = gHead;
- while (curr && memcmp(&curr->fLogFont, &canonical, sizeof(LOGFONT))) {
- curr = curr->fNext;
- }
- return curr;
- }
-};
-
-LogFontTypeface* LogFontTypeface::gHead;
-int32_t LogFontTypeface::gCurrId;
-SkMutex LogFontTypeface::gMutex;
-
-static const LOGFONT& get_default_font() {
- static LOGFONT gDefaultFont;
- // don't hardcode on Windows, Win2000, XP, Vista, and international all have different default
- // and the user could change too
-
-
-// lfMessageFont is garbage on my XP, so skip for now
-#if 0
- if (gDefaultFont.lfFaceName[0] != 0) {
- return gDefaultFont;
- }
-
- NONCLIENTMETRICS ncm;
- ncm.cbSize = sizeof(NONCLIENTMETRICS);
- SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
-
- //memcpy(&gDefaultFont, &(ncm.lfMessageFont), sizeof(LOGFONT));
-#endif
-
- return gDefaultFont;
-}
-
-SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& lf) {
- LogFontTypeface* ptypeface = LogFontTypeface::FindByLogFont(lf);
-
- if (NULL == ptypeface) {
- SkTypeface::Style style = GetFontStyle(lf);
- ptypeface = new LogFontTypeface(style, lf);
- } else {
- ptypeface->ref();
- }
- return ptypeface;
-}
-
-uint32_t SkFontHost::NextLogicalFont(uint32_t fontID) {
- // Zero means that we don't have any fallback fonts for this fontID.
- // This function is implemented on Android, but doesn't have much
- // meaning here.
- return 0;
-}
-
-class SkScalerContext_Windows : public SkScalerContext {
-public:
- SkScalerContext_Windows(const SkDescriptor* desc);
- virtual ~SkScalerContext_Windows();
-
-protected:
- virtual unsigned generateGlyphCount();
- virtual uint16_t generateCharToGlyph(SkUnichar uni);
- virtual void generateAdvance(SkGlyph* glyph);
- virtual void generateMetrics(SkGlyph* glyph);
- virtual void generateImage(const SkGlyph& glyph);
- virtual void generatePath(const SkGlyph& glyph, SkPath* path);
- virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
- //virtual SkDeviceContext getDC() {return ddc;}
-private:
- SkScalar fScale; // to get from canonical size to real size
- MAT2 fMat22;
- HDC fDDC;
- HFONT fSavefont;
- HFONT fFont;
- SCRIPT_CACHE fSC;
- int fGlyphCount;
-};
-
-SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc)
- : SkScalerContext(desc), fDDC(0), fFont(0), fSavefont(0), fSC(0)
- , fGlyphCount(-1) {
- SkAutoMutexAcquire ac(gFTMutex);
-
- fScale = fRec.fTextSize / gCanonicalTextSize;
- fMat22.eM11 = SkScalarToFIXED(SkScalarMul(fScale, fRec.fPost2x2[0][0]));
- fMat22.eM12 = SkScalarToFIXED(SkScalarMul(fScale, -fRec.fPost2x2[0][1]));
- fMat22.eM21 = SkScalarToFIXED(SkScalarMul(fScale, fRec.fPost2x2[1][0]));
- fMat22.eM22 = SkScalarToFIXED(SkScalarMul(fScale, -fRec.fPost2x2[1][1]));
-
- fDDC = ::CreateCompatibleDC(NULL);
- SetBkMode(fDDC, TRANSPARENT);
-
- // Scaling by the DPI is inconsistent with how Skia draws elsewhere
- //SkScalar height = -(fRec.fTextSize * GetDeviceCaps(ddc, LOGPIXELSY) / 72);
- LOGFONT lf = LogFontTypeface::FindById(fRec.fFontID)->logFont();
- lf.lfHeight = -gCanonicalTextSize;
- fFont = CreateFontIndirect(&lf);
- fSavefont = (HFONT)SelectObject(fDDC, fFont);
-}
-
-SkScalerContext_Windows::~SkScalerContext_Windows() {
- if (fDDC) {
- ::SelectObject(fDDC, fSavefont);
- ::DeleteDC(fDDC);
- }
- if (fFont) {
- ::DeleteObject(fFont);
- }
- if (fSC) {
- ::ScriptFreeCache(&fSC);
- }
-}
-
-unsigned SkScalerContext_Windows::generateGlyphCount() {
- if (fGlyphCount < 0) {
- fGlyphCount = calculateGlyphCount(fDDC);
- }
- return fGlyphCount;
-}
-
-uint16_t SkScalerContext_Windows::generateCharToGlyph(SkUnichar uni) {
- uint16_t index = 0;
- WCHAR c[2];
- // TODO(ctguil): Support characters that generate more than one glyph.
- if (SkUTF16_FromUnichar(uni, (uint16_t*)c) == 1) {
- // Type1 fonts fail with uniscribe API. Use GetGlyphIndices for plane 0.
- SkAssertResult(GetGlyphIndicesW(fDDC, c, 1, &index, 0));
- } else {
- // Use uniscribe to detemine glyph index for non-BMP characters.
- // Need to add extra item to SCRIPT_ITEM to work around a bug in older
- // windows versions. https://bugzilla.mozilla.org/show_bug.cgi?id=366643
- SCRIPT_ITEM si[2 + 1];
- int items;
- SkAssertResult(
- SUCCEEDED(ScriptItemize(c, 2, 2, NULL, NULL, si, &items)));
-
- WORD log[2];
- SCRIPT_VISATTR vsa;
- int glyphs;
- SkAssertResult(SUCCEEDED(ScriptShape(
- fDDC, &fSC, c, 2, 1, &si[0].a, &index, log, &vsa, &glyphs)));
- }
- return index;
-}
-
-void SkScalerContext_Windows::generateAdvance(SkGlyph* glyph) {
- this->generateMetrics(glyph);
-}
-
-void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) {
-
- SkASSERT(fDDC);
-
- GLYPHMETRICS gm;
- memset(&gm, 0, sizeof(gm));
-
- glyph->fRsbDelta = 0;
- glyph->fLsbDelta = 0;
-
- // Note: need to use GGO_GRAY8_BITMAP instead of GGO_METRICS because GGO_METRICS returns a smaller
- // BlackBlox; we need the bigger one in case we need the image. fAdvance is the same.
- uint32_t ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
-
- if (GDI_ERROR != ret) {
- if (ret == 0) {
- // for white space, ret is zero and gmBlackBoxX, gmBlackBoxY are 1 incorrectly!
- gm.gmBlackBoxX = gm.gmBlackBoxY = 0;
- }
- glyph->fWidth = gm.gmBlackBoxX;
- glyph->fHeight = gm.gmBlackBoxY;
- glyph->fTop = SkToS16(gm.gmptGlyphOrigin.y - gm.gmBlackBoxY);
- glyph->fLeft = SkToS16(gm.gmptGlyphOrigin.x);
- glyph->fAdvanceX = SkIntToFixed(gm.gmCellIncX);
- glyph->fAdvanceY = -SkIntToFixed(gm.gmCellIncY);
- } else {
- glyph->fWidth = 0;
- }
-
-#if 0
- char buf[1024];
- sprintf(buf, "generateMetrics: id:%d, w=%d, h=%d, font:%s, fh:%d\n", glyph->fID, glyph->fWidth, glyph->fHeight, lf.lfFaceName, lf.lfHeight);
- OutputDebugString(buf);
-#endif
-}
-
-void SkScalerContext_Windows::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) {
-// Note: This code was borrowed from generateLineHeight, which has a note
-// stating that it may be incorrect.
- if (!(mx || my))
- return;
-
- SkASSERT(fDDC);
-
- OUTLINETEXTMETRIC otm;
-
- uint32_t ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
- if (sizeof(otm) != ret) {
- return;
- }
-
- if (mx) {
- mx->fTop = -fScale * otm.otmTextMetrics.tmAscent;
- mx->fAscent = -fScale * otm.otmAscent;
- mx->fDescent = -fScale * otm.otmDescent;
- mx->fBottom = fScale * otm.otmTextMetrics.tmDescent;
- mx->fLeading = fScale * (otm.otmTextMetrics.tmInternalLeading
- + otm.otmTextMetrics.tmExternalLeading);
- }
-
- if (my) {
- my->fTop = -fScale * otm.otmTextMetrics.tmAscent;
- my->fAscent = -fScale * otm.otmAscent;
- my->fDescent = -fScale * otm.otmDescent;
- my->fBottom = fScale * otm.otmTextMetrics.tmDescent;
- my->fLeading = fScale * (otm.otmTextMetrics.tmInternalLeading
- + otm.otmTextMetrics.tmExternalLeading);
- }
-}
-
-void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
-
- SkAutoMutexAcquire ac(gFTMutex);
-
- SkASSERT(fDDC);
-
- GLYPHMETRICS gm;
- memset(&gm, 0, sizeof(gm));
-
-#if 0
- char buf[1024];
- sprintf(buf, "generateImage: id:%d, w=%d, h=%d, font:%s,fh:%d\n", glyph.fID, glyph.fWidth, glyph.fHeight, lf.lfFaceName, lf.lfHeight);
- OutputDebugString(buf);
-#endif
-
- uint32_t bytecount = 0;
- uint32_t total_size = GetGlyphOutlineW(fDDC, glyph.fID, GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
- if (GDI_ERROR != total_size && total_size > 0) {
- uint8_t *pBuff = new uint8_t[total_size];
- if (NULL != pBuff) {
- total_size = GetGlyphOutlineW(fDDC, glyph.fID, GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, total_size, pBuff, &fMat22);
-
- SkASSERT(total_size != GDI_ERROR);
-
- SkASSERT(glyph.fWidth == gm.gmBlackBoxX);
- SkASSERT(glyph.fHeight == gm.gmBlackBoxY);
-
- uint8_t* dst = (uint8_t*)glyph.fImage;
- uint32_t pitch = (gm.gmBlackBoxX + 3) & ~0x3;
- if (pitch != glyph.rowBytes()) {
- SkASSERT(false); // glyph.fImage has different rowsize!?
- }
-
- for (int32_t y = gm.gmBlackBoxY - 1; y >= 0; y--) {
- uint8_t* src = pBuff + pitch * y;
-
- for (uint32_t x = 0; x < gm.gmBlackBoxX; x++) {
- if (*src > 63) {
- *dst = 0xFF;
- }
- else {
- *dst = *src << 2; // scale to 0-255
- }
- dst++;
- src++;
- bytecount++;
- }
- memset(dst, 0, glyph.rowBytes() - glyph.fWidth);
- dst += glyph.rowBytes() - glyph.fWidth;
- }
-
- delete[] pBuff;
- }
- }
-
- SkASSERT(GDI_ERROR != total_size && total_size >= 0);
-
-}
-
-void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) {
-
- SkAutoMutexAcquire ac(gFTMutex);
-
- SkASSERT(&glyph && path);
- SkASSERT(fDDC);
-
- path->reset();
-
-#if 0
- char buf[1024];
- sprintf(buf, "generatePath: id:%d, w=%d, h=%d, font:%s,fh:%d\n", glyph.fID, glyph.fWidth, glyph.fHeight, lf.lfFaceName, lf.lfHeight);
- OutputDebugString(buf);
-#endif
-
- GLYPHMETRICS gm;
- uint32_t total_size = GetGlyphOutlineW(fDDC, glyph.fID, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, BUFFERSIZE, glyphbuf, &fMat22);
-
- if (GDI_ERROR != total_size) {
-
- const uint8_t* cur_glyph = glyphbuf;
- const uint8_t* end_glyph = glyphbuf + total_size;
-
- while(cur_glyph < end_glyph) {
- const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
-
- const uint8_t* end_poly = cur_glyph + th->cb;
- const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
-
- path->moveTo(SkFixedToScalar(*(SkFixed*)(&th->pfxStart.x)), SkFixedToScalar(*(SkFixed*)(&th->pfxStart.y)));
-
- while(cur_poly < end_poly) {
- const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
-
- if (pc->wType == TT_PRIM_LINE) {
- for (uint16_t i = 0; i < pc->cpfx; i++) {
- path->lineTo(SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].x)), SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].y)));
- }
- }
-
- if (pc->wType == TT_PRIM_QSPLINE) {
- for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
- POINTFX pnt_b = pc->apfx[u]; // B is always the current point
- POINTFX pnt_c = pc->apfx[u+1];
-
- if (u < pc->cpfx - 2) { // If not on last spline, compute C
- pnt_c.x = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.x), *(SkFixed*)(&pnt_c.x)));
- pnt_c.y = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.y), *(SkFixed*)(&pnt_c.y)));
- }
-
- path->quadTo(SkFixedToScalar(*(SkFixed*)(&pnt_b.x)), SkFixedToScalar(*(SkFixed*)(&pnt_b.y)), SkFixedToScalar(*(SkFixed*)(&pnt_c.x)), SkFixedToScalar(*(SkFixed*)(&pnt_c.y)));
- }
- }
- cur_poly += sizeof(uint16_t) * 2 + sizeof(POINTFX) * pc->cpfx;
- }
- cur_glyph += th->cb;
- path->close();
- }
- }
- else {
- SkASSERT(false);
- }
- //char buf[1024];
- //sprintf(buf, "generatePath: count:%d\n", count);
- //OutputDebugString(buf);
-}
-
-void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
- SkASSERT(!"SkFontHost::Serialize unimplemented");
-}
-
-SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
- SkASSERT(!"SkFontHost::Deserialize unimplemented");
- return NULL;
-}
-
-static bool getWidthAdvance(HDC hdc, int gId, int16_t* advance) {
- // Initialize the MAT2 structure to the identify transformation matrix.
- static const MAT2 mat2 = {SkScalarToFIXED(1), SkScalarToFIXED(0),
- SkScalarToFIXED(0), SkScalarToFIXED(1)};
- int flags = GGO_METRICS | GGO_GLYPH_INDEX;
- GLYPHMETRICS gm;
- if (GDI_ERROR == GetGlyphOutline(hdc, gId, flags, &gm, 0, NULL, &mat2)) {
- return false;
- }
- SkASSERT(advance);
- *advance = gm.gmCellIncX;
- return true;
-}
-
-// static
-SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
- uint32_t fontID, bool perGlyphInfo) {
- SkAutoMutexAcquire ac(gFTMutex);
- LogFontTypeface* rec = LogFontTypeface::FindById(fontID);
- LOGFONT lf = rec->logFont();
- SkAdvancedTypefaceMetrics* info = NULL;
-
- HDC hdc = CreateCompatibleDC(NULL);
- HFONT font = CreateFontIndirect(&lf);
- HFONT savefont = (HFONT)SelectObject(hdc, font);
- HFONT designFont = NULL;
-
- // To request design units, create a logical font whose height is specified
- // as unitsPerEm.
- OUTLINETEXTMETRIC otm;
- if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm) ||
- !GetTextFace(hdc, LF_FACESIZE, lf.lfFaceName)) {
- goto Error;
- }
- lf.lfHeight = -SkToS32(otm.otmEMSquare);
- designFont = CreateFontIndirect(&lf);
- SelectObject(hdc, designFont);
- if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) {
- goto Error;
- }
- const unsigned glyphCount = calculateGlyphCount(hdc);
-
- info = new SkAdvancedTypefaceMetrics;
- info->fEmSize = otm.otmEMSquare;
- info->fMultiMaster = false;
- info->fLastGlyphID = SkToU16(glyphCount - 1);
- info->fStyle = 0;
-#ifdef UNICODE
- // Get the buffer size needed first.
- size_t str_len = WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1, NULL,
- 0, NULL, NULL);
- // Allocate a buffer (str_len already has terminating null accounted for).
- char *familyName = new char[str_len];
- // Now actually convert the string.
- WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1, familyName, str_len,
- NULL, NULL);
- info->fFontName.set(familyName);
- delete [] familyName;
-#else
- info->fFontName.set(lf.lfFaceName);
-#endif
-
- if (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE) {
- info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
- } else {
- info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
- info->fItalicAngle = 0;
- info->fAscent = 0;
- info->fDescent = 0;
- info->fStemV = 0;
- info->fCapHeight = 0;
- info->fBBox = SkIRect::MakeEmpty();
- return info;
- }
-
- // If this bit is clear the font is a fixed pitch font.
- if (!(otm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
- info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
- }
- if (otm.otmTextMetrics.tmItalic) {
- info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
- }
- // Setting symbolic style by default for now.
- info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style;
- if (otm.otmTextMetrics.tmPitchAndFamily & FF_ROMAN) {
- info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
- } else if (otm.otmTextMetrics.tmPitchAndFamily & FF_SCRIPT) {
- info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
- }
-
- // The main italic angle of the font, in tenths of a degree counterclockwise
- // from vertical.
- info->fItalicAngle = otm.otmItalicAngle / 10;
- info->fAscent = SkToS16(otm.otmTextMetrics.tmAscent);
- info->fDescent = SkToS16(-otm.otmTextMetrics.tmDescent);
- // TODO(ctguil): Use alternate cap height calculation.
- // MSDN says otmsCapEmHeight is not support but it is returning a value on
- // my Win7 box.
- info->fCapHeight = otm.otmsCapEmHeight;
- info->fBBox =
- SkIRect::MakeLTRB(otm.otmrcFontBox.left, otm.otmrcFontBox.top,
- otm.otmrcFontBox.right, otm.otmrcFontBox.bottom);
-
- // Figure out a good guess for StemV - Min width of i, I, !, 1.
- // This probably isn't very good with an italic font.
- int16_t min_width = SHRT_MAX;
- info->fStemV = 0;
- char stem_chars[] = {'i', 'I', '!', '1'};
- for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {
- ABC abcWidths;
- if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) {
- int16_t width = abcWidths.abcB;
- if (width > 0 && width < min_width) {
- min_width = width;
- info->fStemV = min_width;
- }
- }
- }
-
- // If bit 1 is set, the font may not be embedded in a document.
- // If bit 1 is clear, the font can be embedded.
- // If bit 2 is set, the embedding is read-only.
- if (otm.otmfsType & 0x1) {
- info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
- } else if (perGlyphInfo) {
- info->fGlyphWidths.reset(
- getAdvanceData(hdc, glyphCount, &getWidthAdvance));
- }
-
-Error:
- SelectObject(hdc, savefont);
- DeleteObject(designFont);
- DeleteObject(font);
- DeleteDC(hdc);
-
- return info;
-}
-
-SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
-
- //Should not be used on Windows, keep linker happy
- SkASSERT(false);
- return SkCreateTypefaceFromLOGFONT(get_default_font());
-}
-
-SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {
- SkAutoMutexAcquire ac(gFTMutex);
- LogFontTypeface* rec = LogFontTypeface::FindById(uniqueID);
-
- HDC hdc = ::CreateCompatibleDC(NULL);
- HFONT font = CreateFontIndirect(&rec->logFont());
- HFONT savefont = (HFONT)SelectObject(hdc, font);
-
- size_t bufferSize = GetFontData(hdc, 0, 0, NULL, 0);
- SkMemoryStream* stream = new SkMemoryStream(bufferSize);
- if (!GetFontData(hdc, 0, 0, (void*)stream->getMemoryBase(), bufferSize)) {
- delete stream;
- stream = NULL;
- }
-
- SelectObject(hdc, savefont);
- DeleteObject(font);
- DeleteDC(hdc);
-
- return stream;
-}
-
-SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
- return SkNEW_ARGS(SkScalerContext_Windows, (desc));
-}
-
-/** Return the closest matching typeface given either an existing family
- (specified by a typeface in that family) or by a familyName, and a
- requested style.
- 1) If familyFace is null, use famillyName.
- 2) If famillyName is null, use familyFace.
- 3) If both are null, return the default font that best matches style
- This MUST not return NULL.
- */
-
-SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
- const char familyName[],
- const void* data, size_t bytelength,
- SkTypeface::Style style) {
-
- static SkTypeface* gDefaultTypeface;
- SkAutoMutexAcquire ac(gFTMutex);
-
-#ifndef CAN_USE_LOGFONT_NAME
- familyName = NULL;
- familyFace = NULL;
-#endif
-
- // clip to legal style bits
- style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
-
- SkTypeface* tf = NULL;
- if (NULL == familyFace && NULL == familyName) {
- LOGFONT lf = get_default_font();
- lf.lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL ;
- lf.lfItalic = ((style & SkTypeface::kItalic) != 0);
- // hack until we figure out if SkTypeface should cache this itself
- if (style == SkTypeface::kNormal) {
- if (NULL == gDefaultTypeface) {
- gDefaultTypeface = SkCreateTypefaceFromLOGFONT(lf);
- }
- tf = gDefaultTypeface;
- tf->ref();
- } else {
- tf = SkCreateTypefaceFromLOGFONT(lf);
- }
- } else {
-#ifdef CAN_USE_LOGFONT_NAME
- LOGFONT lf;
- if (NULL != familyFace) {
- uint32_t id = familyFace->uniqueID();
- LogFontTypeface* rec = LogFontTypeface::FindById(id);
- if (!rec) {
- SkASSERT(false);
- lf = get_default_font();
- }
- else {
- lf = rec->logFont();
- }
- }
- else {
- memset(&lf, 0, sizeof(LOGFONT));
-
- lf.lfHeight = -11; // default
- lf.lfQuality = PROOF_QUALITY;
- lf.lfCharSet = DEFAULT_CHARSET;
-
-#ifdef UNICODE
- // Get the buffer size needed first.
- size_t str_len = ::MultiByteToWideChar(CP_UTF8, 0, familyName,
- -1, NULL, 0);
- // Allocate a buffer (str_len already has terminating null
- // accounted for).
- wchar_t *wideFamilyName = new wchar_t[str_len];
- // Now actually convert the string.
- ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1,
- wideFamilyName, str_len);
- ::wcsncpy(lf.lfFaceName, wideFamilyName, LF_FACESIZE);
- delete [] wideFamilyName;
-#else
- ::strncpy(lf.lfFaceName, familyName, LF_FACESIZE);
-#endif
- lf.lfFaceName[LF_FACESIZE-1] = '\0';
- }
-
- // use the style desired
- lf.lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL ;
- lf.lfItalic = ((style & SkTypeface::kItalic) != 0);
- tf = SkCreateTypefaceFromLOGFONT(lf);
-#endif
- }
-
- if (NULL == tf) {
- tf = SkCreateTypefaceFromLOGFONT(get_default_font());
- }
- return tf;
-}
-
-size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) {
- if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET)
- return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET;
- else
- return 0; // nothing to do
-}
-
-int SkFontHost::ComputeGammaFlag(const SkPaint& paint) {
- return 0;
-}
-
-void SkFontHost::GetGammaTables(const uint8_t* tables[2]) {
- tables[0] = NULL; // black gamma (e.g. exp=1.4)
- tables[1] = NULL; // white gamma (e.g. exp= 1/1.4)
-}
-
-SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
- printf("SkFontHost::CreateTypefaceFromFile unimplemented");
- return NULL;
-}
-
-void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
- // We don't control the hinting nor ClearType settings here
- rec->setHinting(SkPaint::kNormal_Hinting);
- if (SkMask::FormatIsLCD((SkMask::Format)rec->fMaskFormat)) {
- rec->fMaskFormat = SkMask::kA8_Format;
- }
-}
-
-#endif // WIN32
+/*
+ ** Copyright 2006, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include "SkString.h"
+//#include "SkStream.h"
+
+#include "SkEndian.h"
+#include "SkFontHost.h"
+#include "SkDescriptor.h"
+#include "SkAdvancedTypefaceMetrics.h"
+#include "SkStream.h"
+#include "SkThread.h"
+#include "SkTypeface_win.h"
+#include "SkUtils.h"
+
+#ifdef WIN32
+#include "windows.h"
+#include "tchar.h"
+#include "Usp10.h"
+
+// client3d has to undefine this for now
+#define CAN_USE_LOGFONT_NAME
+
+using namespace skia_advanced_typeface_metrics_utils;
+
+static SkMutex gFTMutex;
+
+static const uint16_t BUFFERSIZE = (16384 - 32);
+static uint8_t glyphbuf[BUFFERSIZE];
+
+// Give 1MB font cache budget
+#define FONT_CACHE_MEMORY_BUDGET (1024 * 1024)
+
+/**
+ * Since LOGFONT wants its textsize as an int, and we support fractional sizes,
+ * and since we have a cache of LOGFONTs for our tyepfaces, we always set the
+ * lfHeight to a canonical size, and then we use the 2x2 matrix to achieve the
+ * actual requested size.
+ */
+static const int gCanonicalTextSize = 64;
+
+static void make_canonical(LOGFONT* lf) {
+ lf->lfHeight = -gCanonicalTextSize;
+}
+
+static inline FIXED SkFixedToFIXED(SkFixed x) {
+ return *(FIXED*)(&x);
+}
+
+static inline FIXED SkScalarToFIXED(SkScalar x) {
+ return SkFixedToFIXED(SkScalarToFixed(x));
+}
+
+static unsigned calculateGlyphCount(HDC hdc) {
+ // The 'maxp' table stores the number of glyphs at offset 4, in 2 bytes.
+ const DWORD maxpTag = *(DWORD*) "maxp";
+ uint16_t glyphs;
+ if (GetFontData(hdc, maxpTag, 4, &glyphs, sizeof(glyphs)) != GDI_ERROR) {
+ return SkEndian_SwapBE16(glyphs);
+ }
+
+ // Binary search for glyph count.
+ static const MAT2 mat2 = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
+ int32_t max = SK_MaxU16 + 1;
+ int32_t min = 0;
+ GLYPHMETRICS gm;
+ while (min < max) {
+ int32_t mid = min + ((max - min) / 2);
+ if (GetGlyphOutlineW(hdc, mid, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0,
+ NULL, &mat2) == GDI_ERROR) {
+ max = mid;
+ } else {
+ min = mid + 1;
+ }
+ }
+ SkASSERT(min == max);
+ return min;
+}
+
+static SkTypeface::Style GetFontStyle(const LOGFONT& lf) {
+ int style = SkTypeface::kNormal;
+ if (lf.lfWeight == FW_SEMIBOLD || lf.lfWeight == FW_DEMIBOLD || lf.lfWeight == FW_BOLD)
+ style |= SkTypeface::kBold;
+ if (lf.lfItalic)
+ style |= SkTypeface::kItalic;
+
+ return (SkTypeface::Style)style;
+}
+
+// have to do this because SkTypeface::SkTypeface() is protected
+class LogFontTypeface : public SkTypeface {
+private:
+ static SkMutex gMutex;
+ static LogFontTypeface* gHead;
+ static int32_t gCurrId;
+
+ LogFontTypeface* fNext;
+ LOGFONT fLogFont;
+
+public:
+
+ LogFontTypeface(Style style, const LOGFONT& logFont) :
+ SkTypeface(style, sk_atomic_inc(&gCurrId)+1), // 0 id is reserved so add 1
+ fLogFont(logFont)
+ {
+ make_canonical(&fLogFont);
+
+ SkAutoMutexAcquire am(gMutex);
+ fNext = gHead;
+ gHead = this;
+ }
+
+ const LOGFONT& logFont() const { return fLogFont; }
+
+ virtual ~LogFontTypeface() {
+ SkAutoMutexAcquire am(gMutex);
+ if (gHead == this) {
+ gHead = fNext;
+ return;
+ }
+
+ LogFontTypeface* prev = gHead;
+ SkASSERT(prev);
+ while (prev->fNext != this) {
+ prev = prev->fNext;
+ SkASSERT(prev);
+ }
+ prev->fNext = fNext;
+ }
+
+ static LogFontTypeface* FindById(uint32_t id){
+ SkASSERT(gHead);
+ LogFontTypeface* curr = gHead;
+ while (curr->uniqueID() != id) {
+ curr = curr->fNext;
+ SkASSERT(curr);
+ }
+ return curr;
+ }
+
+ static LogFontTypeface* FindByLogFont(const LOGFONT& lf)
+ {
+ LOGFONT canonical = lf;
+ make_canonical(&canonical);
+
+ LogFontTypeface* curr = gHead;
+ while (curr && memcmp(&curr->fLogFont, &canonical, sizeof(LOGFONT))) {
+ curr = curr->fNext;
+ }
+ return curr;
+ }
+};
+
+LogFontTypeface* LogFontTypeface::gHead;
+int32_t LogFontTypeface::gCurrId;
+SkMutex LogFontTypeface::gMutex;
+
+static const LOGFONT& get_default_font() {
+ static LOGFONT gDefaultFont;
+ // don't hardcode on Windows, Win2000, XP, Vista, and international all have different default
+ // and the user could change too
+
+
+// lfMessageFont is garbage on my XP, so skip for now
+#if 0
+ if (gDefaultFont.lfFaceName[0] != 0) {
+ return gDefaultFont;
+ }
+
+ NONCLIENTMETRICS ncm;
+ ncm.cbSize = sizeof(NONCLIENTMETRICS);
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
+
+ //memcpy(&gDefaultFont, &(ncm.lfMessageFont), sizeof(LOGFONT));
+#endif
+
+ return gDefaultFont;
+}
+
+SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& lf) {
+ LogFontTypeface* ptypeface = LogFontTypeface::FindByLogFont(lf);
+
+ if (NULL == ptypeface) {
+ SkTypeface::Style style = GetFontStyle(lf);
+ ptypeface = new LogFontTypeface(style, lf);
+ } else {
+ ptypeface->ref();
+ }
+ return ptypeface;
+}
+
+uint32_t SkFontHost::NextLogicalFont(uint32_t fontID) {
+ // Zero means that we don't have any fallback fonts for this fontID.
+ // This function is implemented on Android, but doesn't have much
+ // meaning here.
+ return 0;
+}
+
+class SkScalerContext_Windows : public SkScalerContext {
+public:
+ SkScalerContext_Windows(const SkDescriptor* desc);
+ virtual ~SkScalerContext_Windows();
+
+protected:
+ virtual unsigned generateGlyphCount();
+ virtual uint16_t generateCharToGlyph(SkUnichar uni);
+ virtual void generateAdvance(SkGlyph* glyph);
+ virtual void generateMetrics(SkGlyph* glyph);
+ virtual void generateImage(const SkGlyph& glyph);
+ virtual void generatePath(const SkGlyph& glyph, SkPath* path);
+ virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
+ //virtual SkDeviceContext getDC() {return ddc;}
+private:
+ SkScalar fScale; // to get from canonical size to real size
+ MAT2 fMat22;
+ XFORM fXform;
+ HDC fDDC;
+ HFONT fSavefont;
+ HFONT fFont;
+ SCRIPT_CACHE fSC;
+ int fGlyphCount;
+};
+
+static float mul2float(SkScalar a, SkScalar b) {
+ return SkScalarToFloat(SkScalarMul(a, b));
+}
+
+static FIXED float2FIXED(float x) {
+ return SkFixedToFIXED(SkFloatToFixed(x));
+}
+
+SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc)
+ : SkScalerContext(desc), fDDC(0), fFont(0), fSavefont(0), fSC(0)
+ , fGlyphCount(-1) {
+ SkAutoMutexAcquire ac(gFTMutex);
+
+ fScale = fRec.fTextSize / gCanonicalTextSize;
+
+ fXform.eM11 = mul2float(fScale, fRec.fPost2x2[0][0]);
+ fXform.eM12 = mul2float(fScale, fRec.fPost2x2[1][0]);
+ fXform.eM21 = mul2float(fScale, fRec.fPost2x2[0][1]);
+ fXform.eM22 = mul2float(fScale, fRec.fPost2x2[1][1]);
+ fXform.eDx = 0;
+ fXform.eDy = 0;
+
+ fMat22.eM11 = float2FIXED(fXform.eM11);
+ fMat22.eM12 = float2FIXED(fXform.eM12);
+ fMat22.eM21 = float2FIXED(-fXform.eM21);
+ fMat22.eM22 = float2FIXED(-fXform.eM22);
+
+ fDDC = ::CreateCompatibleDC(NULL);
+ SetBkMode(fDDC, TRANSPARENT);
+
+ // Scaling by the DPI is inconsistent with how Skia draws elsewhere
+ //SkScalar height = -(fRec.fTextSize * GetDeviceCaps(ddc, LOGPIXELSY) / 72);
+ LOGFONT lf = LogFontTypeface::FindById(fRec.fFontID)->logFont();
+ lf.lfHeight = -gCanonicalTextSize;
+ fFont = CreateFontIndirect(&lf);
+ fSavefont = (HFONT)SelectObject(fDDC, fFont);
+}
+
+SkScalerContext_Windows::~SkScalerContext_Windows() {
+ if (fDDC) {
+ ::SelectObject(fDDC, fSavefont);
+ ::DeleteDC(fDDC);
+ }
+ if (fFont) {
+ ::DeleteObject(fFont);
+ }
+ if (fSC) {
+ ::ScriptFreeCache(&fSC);
+ }
+}
+
+unsigned SkScalerContext_Windows::generateGlyphCount() {
+ if (fGlyphCount < 0) {
+ fGlyphCount = calculateGlyphCount(fDDC);
+ }
+ return fGlyphCount;
+}
+
+uint16_t SkScalerContext_Windows::generateCharToGlyph(SkUnichar uni) {
+ uint16_t index = 0;
+ WCHAR c[2];
+ // TODO(ctguil): Support characters that generate more than one glyph.
+ if (SkUTF16_FromUnichar(uni, (uint16_t*)c) == 1) {
+ // Type1 fonts fail with uniscribe API. Use GetGlyphIndices for plane 0.
+ SkAssertResult(GetGlyphIndicesW(fDDC, c, 1, &index, 0));
+ } else {
+ // Use uniscribe to detemine glyph index for non-BMP characters.
+ // Need to add extra item to SCRIPT_ITEM to work around a bug in older
+ // windows versions. https://bugzilla.mozilla.org/show_bug.cgi?id=366643
+ SCRIPT_ITEM si[2 + 1];
+ int items;
+ SkAssertResult(
+ SUCCEEDED(ScriptItemize(c, 2, 2, NULL, NULL, si, &items)));
+
+ WORD log[2];
+ SCRIPT_VISATTR vsa;
+ int glyphs;
+ SkAssertResult(SUCCEEDED(ScriptShape(
+ fDDC, &fSC, c, 2, 1, &si[0].a, &index, log, &vsa, &glyphs)));
+ }
+ return index;
+}
+
+void SkScalerContext_Windows::generateAdvance(SkGlyph* glyph) {
+ this->generateMetrics(glyph);
+}
+
+void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) {
+
+ SkASSERT(fDDC);
+
+ GLYPHMETRICS gm;
+ memset(&gm, 0, sizeof(gm));
+
+ glyph->fRsbDelta = 0;
+ glyph->fLsbDelta = 0;
+
+ // Note: need to use GGO_GRAY8_BITMAP instead of GGO_METRICS because GGO_METRICS returns a smaller
+ // BlackBlox; we need the bigger one in case we need the image. fAdvance is the same.
+ uint32_t ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
+
+ if (GDI_ERROR != ret) {
+ if (ret == 0) {
+ // for white space, ret is zero and gmBlackBoxX, gmBlackBoxY are 1 incorrectly!
+ gm.gmBlackBoxX = gm.gmBlackBoxY = 0;
+ }
+ glyph->fWidth = gm.gmBlackBoxX;
+ glyph->fHeight = gm.gmBlackBoxY;
+ glyph->fTop = SkToS16(gm.gmptGlyphOrigin.y - gm.gmBlackBoxY);
+ glyph->fLeft = SkToS16(gm.gmptGlyphOrigin.x);
+ glyph->fAdvanceX = SkIntToFixed(gm.gmCellIncX);
+ glyph->fAdvanceY = -SkIntToFixed(gm.gmCellIncY);
+
+ // we outset by 1 in all dimensions, since the lcd image may bleed outside
+ // of the computed bounds returned by GetGlyphOutline.
+ // This was deduced by trial and error for small text (e.g. 8pt), so there
+ // maybe a more precise way to make this adjustment...
+ if (SkMask::kLCD16_Format == fRec.fMaskFormat) {
+ glyph->fWidth += 2;
+ glyph->fHeight += 2;
+ glyph->fTop -= 1;
+ glyph->fLeft -= 1;
+ }
+ } else {
+ glyph->fWidth = 0;
+ }
+}
+
+void SkScalerContext_Windows::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) {
+// Note: This code was borrowed from generateLineHeight, which has a note
+// stating that it may be incorrect.
+ if (!(mx || my))
+ return;
+
+ SkASSERT(fDDC);
+
+ OUTLINETEXTMETRIC otm;
+
+ uint32_t ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
+ if (sizeof(otm) != ret) {
+ return;
+ }
+
+ if (mx) {
+ mx->fTop = -fScale * otm.otmTextMetrics.tmAscent;
+ mx->fAscent = -fScale * otm.otmAscent;
+ mx->fDescent = -fScale * otm.otmDescent;
+ mx->fBottom = fScale * otm.otmTextMetrics.tmDescent;
+ mx->fLeading = fScale * (otm.otmTextMetrics.tmInternalLeading
+ + otm.otmTextMetrics.tmExternalLeading);
+ }
+
+ if (my) {
+ my->fTop = -fScale * otm.otmTextMetrics.tmAscent;
+ my->fAscent = -fScale * otm.otmAscent;
+ my->fDescent = -fScale * otm.otmDescent;
+ my->fBottom = fScale * otm.otmTextMetrics.tmDescent;
+ my->fLeading = fScale * (otm.otmTextMetrics.tmInternalLeading
+ + otm.otmTextMetrics.tmExternalLeading);
+ }
+}
+
+#include "SkColorPriv.h"
+
+static inline uint16_t rgb_to_lcd16(uint32_t rgb) {
+ int r = (rgb >> 16) & 0xFF;
+ int g = (rgb >> 8) & 0xFF;
+ int b = (rgb >> 0) & 0xFF;
+
+ // invert, since we draw black-on-white, but we want the original
+ // src mask values.
+ r = 255 - r;
+ g = 255 - g;
+ b = 255 - b;
+ return SkPackRGB16(SkR32ToR16(r), SkG32ToG16(g), SkB32ToB16(b));
+}
+
+void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
+
+ SkAutoMutexAcquire ac(gFTMutex);
+
+ SkASSERT(fDDC);
+
+ if (SkMask::kLCD16_Format == fRec.fMaskFormat) {
+ HDC dc = CreateCompatibleDC(0);
+ void* bits = 0;
+ BITMAPINFO info;
+ sk_bzero(&info, sizeof(info));
+ info.bmiHeader.biSize = sizeof(info.bmiHeader);
+ info.bmiHeader.biWidth = glyph.fWidth;
+ info.bmiHeader.biHeight = glyph.fHeight;
+ info.bmiHeader.biPlanes = 1;
+ info.bmiHeader.biBitCount = 32;
+ info.bmiHeader.biCompression = BI_RGB;
+ HBITMAP bm = CreateDIBSection(dc, &info, DIB_RGB_COLORS, &bits, 0, 0);
+ SelectObject(dc, bm);
+
+ // erase to white
+ size_t srcRB = glyph.fWidth << 2;
+ size_t size = glyph.fHeight * srcRB;
+ memset(bits, 0xFF, size);
+
+ SetBkMode(dc, TRANSPARENT);
+ SetTextAlign(dc, TA_LEFT | TA_BASELINE);
+ SetGraphicsMode(dc, GM_ADVANCED);
+
+ XFORM xform = fXform;
+ xform.eDx = (float)-glyph.fLeft;
+ xform.eDy = (float)-glyph.fTop;
+ SetWorldTransform(dc, &xform);
+
+ HGDIOBJ prevFont = SelectObject(dc, fFont);
+ COLORREF color = SetTextColor(dc, 0); // black
+ SkASSERT(color != CLR_INVALID);
+ uint16_t glyphID = glyph.getGlyphID();
+ ExtTextOut(dc, 0, 0, ETO_GLYPH_INDEX, NULL, (LPCWSTR)&glyphID, 1, NULL);
+ GdiFlush();
+
+ // downsample from rgba to rgb565
+ int width = glyph.fWidth;
+ size_t dstRB = glyph.rowBytes();
+ const uint32_t* src = (const uint32_t*)bits;
+ // gdi's bitmap is upside-down, so we reverse dst walking in Y
+ uint16_t* dst = (uint16_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
+ for (int y = 0; y < glyph.fHeight; y++) {
+ for (int i = 0; i < width; i++) {
+ dst[i] = rgb_to_lcd16(src[i]);
+ }
+ src = (const uint32_t*)((const char*)src + srcRB);
+ dst = (uint16_t*)((char*)dst - dstRB);
+ }
+
+ DeleteDC(dc);
+ DeleteObject(bm);
+ return;
+ }
+
+ GLYPHMETRICS gm;
+ memset(&gm, 0, sizeof(gm));
+ uint32_t bytecount = 0;
+ uint32_t total_size = GetGlyphOutlineW(fDDC, glyph.fID, GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
+ if (GDI_ERROR != total_size && total_size > 0) {
+ uint8_t *pBuff = new uint8_t[total_size];
+ if (NULL != pBuff) {
+ total_size = GetGlyphOutlineW(fDDC, glyph.fID, GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, total_size, pBuff, &fMat22);
+
+ SkASSERT(total_size != GDI_ERROR);
+
+ SkASSERT(glyph.fWidth == gm.gmBlackBoxX);
+ SkASSERT(glyph.fHeight == gm.gmBlackBoxY);
+
+ uint8_t* dst = (uint8_t*)glyph.fImage;
+ uint32_t pitch = (gm.gmBlackBoxX + 3) & ~0x3;
+ if (pitch != glyph.rowBytes()) {
+ SkASSERT(false); // glyph.fImage has different rowsize!?
+ }
+
+ for (int32_t y = gm.gmBlackBoxY - 1; y >= 0; y--) {
+ uint8_t* src = pBuff + pitch * y;
+
+ for (uint32_t x = 0; x < gm.gmBlackBoxX; x++) {
+ if (*src > 63) {
+ *dst = 0xFF;
+ }
+ else {
+ *dst = *src << 2; // scale to 0-255
+ }
+ dst++;
+ src++;
+ bytecount++;
+ }
+ memset(dst, 0, glyph.rowBytes() - glyph.fWidth);
+ dst += glyph.rowBytes() - glyph.fWidth;
+ }
+
+ delete[] pBuff;
+ }
+ }
+
+ SkASSERT(GDI_ERROR != total_size && total_size >= 0);
+
+}
+
+void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) {
+
+ SkAutoMutexAcquire ac(gFTMutex);
+
+ SkASSERT(&glyph && path);
+ SkASSERT(fDDC);
+
+ path->reset();
+
+#if 0
+ char buf[1024];
+ sprintf(buf, "generatePath: id:%d, w=%d, h=%d, font:%s,fh:%d\n", glyph.fID, glyph.fWidth, glyph.fHeight, lf.lfFaceName, lf.lfHeight);
+ OutputDebugString(buf);
+#endif
+
+ GLYPHMETRICS gm;
+ uint32_t total_size = GetGlyphOutlineW(fDDC, glyph.fID, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, BUFFERSIZE, glyphbuf, &fMat22);
+
+ if (GDI_ERROR != total_size) {
+
+ const uint8_t* cur_glyph = glyphbuf;
+ const uint8_t* end_glyph = glyphbuf + total_size;
+
+ while(cur_glyph < end_glyph) {
+ const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
+
+ const uint8_t* end_poly = cur_glyph + th->cb;
+ const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
+
+ path->moveTo(SkFixedToScalar(*(SkFixed*)(&th->pfxStart.x)), SkFixedToScalar(*(SkFixed*)(&th->pfxStart.y)));
+
+ while(cur_poly < end_poly) {
+ const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
+
+ if (pc->wType == TT_PRIM_LINE) {
+ for (uint16_t i = 0; i < pc->cpfx; i++) {
+ path->lineTo(SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].x)), SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].y)));
+ }
+ }
+
+ if (pc->wType == TT_PRIM_QSPLINE) {
+ for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
+ POINTFX pnt_b = pc->apfx[u]; // B is always the current point
+ POINTFX pnt_c = pc->apfx[u+1];
+
+ if (u < pc->cpfx - 2) { // If not on last spline, compute C
+ pnt_c.x = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.x), *(SkFixed*)(&pnt_c.x)));
+ pnt_c.y = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.y), *(SkFixed*)(&pnt_c.y)));
+ }
+
+ path->quadTo(SkFixedToScalar(*(SkFixed*)(&pnt_b.x)), SkFixedToScalar(*(SkFixed*)(&pnt_b.y)), SkFixedToScalar(*(SkFixed*)(&pnt_c.x)), SkFixedToScalar(*(SkFixed*)(&pnt_c.y)));
+ }
+ }
+ cur_poly += sizeof(uint16_t) * 2 + sizeof(POINTFX) * pc->cpfx;
+ }
+ cur_glyph += th->cb;
+ path->close();
+ }
+ }
+ else {
+ SkASSERT(false);
+ }
+ //char buf[1024];
+ //sprintf(buf, "generatePath: count:%d\n", count);
+ //OutputDebugString(buf);
+}
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+ SkASSERT(!"SkFontHost::Serialize unimplemented");
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+ SkASSERT(!"SkFontHost::Deserialize unimplemented");
+ return NULL;
+}
+
+static bool getWidthAdvance(HDC hdc, int gId, int16_t* advance) {
+ // Initialize the MAT2 structure to the identify transformation matrix.
+ static const MAT2 mat2 = {SkScalarToFIXED(1), SkScalarToFIXED(0),
+ SkScalarToFIXED(0), SkScalarToFIXED(1)};
+ int flags = GGO_METRICS | GGO_GLYPH_INDEX;
+ GLYPHMETRICS gm;
+ if (GDI_ERROR == GetGlyphOutline(hdc, gId, flags, &gm, 0, NULL, &mat2)) {
+ return false;
+ }
+ SkASSERT(advance);
+ *advance = gm.gmCellIncX;
+ return true;
+}
+
+// static
+SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
+ uint32_t fontID, bool perGlyphInfo) {
+ SkAutoMutexAcquire ac(gFTMutex);
+ LogFontTypeface* rec = LogFontTypeface::FindById(fontID);
+ LOGFONT lf = rec->logFont();
+ SkAdvancedTypefaceMetrics* info = NULL;
+
+ HDC hdc = CreateCompatibleDC(NULL);
+ HFONT font = CreateFontIndirect(&lf);
+ HFONT savefont = (HFONT)SelectObject(hdc, font);
+ HFONT designFont = NULL;
+
+ // To request design units, create a logical font whose height is specified
+ // as unitsPerEm.
+ OUTLINETEXTMETRIC otm;
+ if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm) ||
+ !GetTextFace(hdc, LF_FACESIZE, lf.lfFaceName)) {
+ goto Error;
+ }
+ lf.lfHeight = -SkToS32(otm.otmEMSquare);
+ designFont = CreateFontIndirect(&lf);
+ SelectObject(hdc, designFont);
+ if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) {
+ goto Error;
+ }
+ const unsigned glyphCount = calculateGlyphCount(hdc);
+
+ info = new SkAdvancedTypefaceMetrics;
+ info->fEmSize = otm.otmEMSquare;
+ info->fMultiMaster = false;
+ info->fLastGlyphID = SkToU16(glyphCount - 1);
+ info->fStyle = 0;
+#ifdef UNICODE
+ // Get the buffer size needed first.
+ size_t str_len = WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1, NULL,
+ 0, NULL, NULL);
+ // Allocate a buffer (str_len already has terminating null accounted for).
+ char *familyName = new char[str_len];
+ // Now actually convert the string.
+ WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1, familyName, str_len,
+ NULL, NULL);
+ info->fFontName.set(familyName);
+ delete [] familyName;
+#else
+ info->fFontName.set(lf.lfFaceName);
+#endif
+
+ if (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE) {
+ info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
+ } else {
+ info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
+ info->fItalicAngle = 0;
+ info->fAscent = 0;
+ info->fDescent = 0;
+ info->fStemV = 0;
+ info->fCapHeight = 0;
+ info->fBBox = SkIRect::MakeEmpty();
+ return info;
+ }
+
+ // If this bit is clear the font is a fixed pitch font.
+ if (!(otm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
+ info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
+ }
+ if (otm.otmTextMetrics.tmItalic) {
+ info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
+ }
+ // Setting symbolic style by default for now.
+ info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style;
+ if (otm.otmTextMetrics.tmPitchAndFamily & FF_ROMAN) {
+ info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
+ } else if (otm.otmTextMetrics.tmPitchAndFamily & FF_SCRIPT) {
+ info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
+ }
+
+ // The main italic angle of the font, in tenths of a degree counterclockwise
+ // from vertical.
+ info->fItalicAngle = otm.otmItalicAngle / 10;
+ info->fAscent = SkToS16(otm.otmTextMetrics.tmAscent);
+ info->fDescent = SkToS16(-otm.otmTextMetrics.tmDescent);
+ // TODO(ctguil): Use alternate cap height calculation.
+ // MSDN says otmsCapEmHeight is not support but it is returning a value on
+ // my Win7 box.
+ info->fCapHeight = otm.otmsCapEmHeight;
+ info->fBBox =
+ SkIRect::MakeLTRB(otm.otmrcFontBox.left, otm.otmrcFontBox.top,
+ otm.otmrcFontBox.right, otm.otmrcFontBox.bottom);
+
+ // Figure out a good guess for StemV - Min width of i, I, !, 1.
+ // This probably isn't very good with an italic font.
+ int16_t min_width = SHRT_MAX;
+ info->fStemV = 0;
+ char stem_chars[] = {'i', 'I', '!', '1'};
+ for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {
+ ABC abcWidths;
+ if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) {
+ int16_t width = abcWidths.abcB;
+ if (width > 0 && width < min_width) {
+ min_width = width;
+ info->fStemV = min_width;
+ }
+ }
+ }
+
+ // If bit 1 is set, the font may not be embedded in a document.
+ // If bit 1 is clear, the font can be embedded.
+ // If bit 2 is set, the embedding is read-only.
+ if (otm.otmfsType & 0x1) {
+ info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
+ } else if (perGlyphInfo) {
+ info->fGlyphWidths.reset(
+ getAdvanceData(hdc, glyphCount, &getWidthAdvance));
+ }
+
+Error:
+ SelectObject(hdc, savefont);
+ DeleteObject(designFont);
+ DeleteObject(font);
+ DeleteDC(hdc);
+
+ return info;
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
+
+ //Should not be used on Windows, keep linker happy
+ SkASSERT(false);
+ return SkCreateTypefaceFromLOGFONT(get_default_font());
+}
+
+SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {
+ SkAutoMutexAcquire ac(gFTMutex);
+ LogFontTypeface* rec = LogFontTypeface::FindById(uniqueID);
+
+ HDC hdc = ::CreateCompatibleDC(NULL);
+ HFONT font = CreateFontIndirect(&rec->logFont());
+ HFONT savefont = (HFONT)SelectObject(hdc, font);
+
+ size_t bufferSize = GetFontData(hdc, 0, 0, NULL, 0);
+ SkMemoryStream* stream = new SkMemoryStream(bufferSize);
+ if (!GetFontData(hdc, 0, 0, (void*)stream->getMemoryBase(), bufferSize)) {
+ delete stream;
+ stream = NULL;
+ }
+
+ SelectObject(hdc, savefont);
+ DeleteObject(font);
+ DeleteDC(hdc);
+
+ return stream;
+}
+
+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
+ return SkNEW_ARGS(SkScalerContext_Windows, (desc));
+}
+
+/** Return the closest matching typeface given either an existing family
+ (specified by a typeface in that family) or by a familyName, and a
+ requested style.
+ 1) If familyFace is null, use famillyName.
+ 2) If famillyName is null, use familyFace.
+ 3) If both are null, return the default font that best matches style
+ This MUST not return NULL.
+ */
+
+SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
+ const char familyName[],
+ const void* data, size_t bytelength,
+ SkTypeface::Style style) {
+
+ static SkTypeface* gDefaultTypeface;
+ SkAutoMutexAcquire ac(gFTMutex);
+
+#ifndef CAN_USE_LOGFONT_NAME
+ familyName = NULL;
+ familyFace = NULL;
+#endif
+
+ // clip to legal style bits
+ style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
+
+ SkTypeface* tf = NULL;
+ if (NULL == familyFace && NULL == familyName) {
+ LOGFONT lf = get_default_font();
+ lf.lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL ;
+ lf.lfItalic = ((style & SkTypeface::kItalic) != 0);
+ // hack until we figure out if SkTypeface should cache this itself
+ if (style == SkTypeface::kNormal) {
+ if (NULL == gDefaultTypeface) {
+ gDefaultTypeface = SkCreateTypefaceFromLOGFONT(lf);
+ }
+ tf = gDefaultTypeface;
+ tf->ref();
+ } else {
+ tf = SkCreateTypefaceFromLOGFONT(lf);
+ }
+ } else {
+#ifdef CAN_USE_LOGFONT_NAME
+ LOGFONT lf;
+ if (NULL != familyFace) {
+ uint32_t id = familyFace->uniqueID();
+ LogFontTypeface* rec = LogFontTypeface::FindById(id);
+ if (!rec) {
+ SkASSERT(false);
+ lf = get_default_font();
+ }
+ else {
+ lf = rec->logFont();
+ }
+ }
+ else {
+ memset(&lf, 0, sizeof(LOGFONT));
+
+ lf.lfHeight = -11; // default
+ lf.lfQuality = PROOF_QUALITY;
+ lf.lfCharSet = DEFAULT_CHARSET;
+
+#ifdef UNICODE
+ // Get the buffer size needed first.
+ size_t str_len = ::MultiByteToWideChar(CP_UTF8, 0, familyName,
+ -1, NULL, 0);
+ // Allocate a buffer (str_len already has terminating null
+ // accounted for).
+ wchar_t *wideFamilyName = new wchar_t[str_len];
+ // Now actually convert the string.
+ ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1,
+ wideFamilyName, str_len);
+ ::wcsncpy(lf.lfFaceName, wideFamilyName, LF_FACESIZE);
+ delete [] wideFamilyName;
+#else
+ ::strncpy(lf.lfFaceName, familyName, LF_FACESIZE);
+#endif
+ lf.lfFaceName[LF_FACESIZE-1] = '\0';
+ }
+
+ // use the style desired
+ lf.lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL ;
+ lf.lfItalic = ((style & SkTypeface::kItalic) != 0);
+ tf = SkCreateTypefaceFromLOGFONT(lf);
+#endif
+ }
+
+ if (NULL == tf) {
+ tf = SkCreateTypefaceFromLOGFONT(get_default_font());
+ }
+ return tf;
+}
+
+size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) {
+ if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET)
+ return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET;
+ else
+ return 0; // nothing to do
+}
+
+int SkFontHost::ComputeGammaFlag(const SkPaint& paint) {
+ return 0;
+}
+
+void SkFontHost::GetGammaTables(const uint8_t* tables[2]) {
+ tables[0] = NULL; // black gamma (e.g. exp=1.4)
+ tables[1] = NULL; // white gamma (e.g. exp= 1/1.4)
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
+ printf("SkFontHost::CreateTypefaceFromFile unimplemented");
+ return NULL;
+}
+
+void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
+ // We don't control the hinting nor ClearType settings here
+ rec->setHinting(SkPaint::kNormal_Hinting);
+
+ // we do support LCD16
+ if (SkMask::kLCD16_Format == rec->fMaskFormat) {
+ return;
+ }
+
+ if (SkMask::FormatIsLCD((SkMask::Format)rec->fMaskFormat)) {
+ rec->fMaskFormat = SkMask::kA8_Format;
+ }
+}
+
+#endif // WIN32