diff options
author | 2011-03-11 15:42:51 +0000 | |
---|---|---|
committer | 2011-03-11 15:42:51 +0000 | |
commit | ac6b97972d3c5a8e10b79c1409818756310e0f12 (patch) | |
tree | dd5f45b32395c827c1f25caeeec6809c3d15a609 | |
parent | f77e27deb95ff6fd6e6e30dcd3f6bf1862c394e1 (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.cpp | 1698 |
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 |