Chromium Code Reviews| Index: webkit/port/platform/graphics/skia/SkiaFontWin.cpp |
| =================================================================== |
| --- webkit/port/platform/graphics/skia/SkiaFontWin.cpp (revision 0) |
| +++ webkit/port/platform/graphics/skia/SkiaFontWin.cpp (revision 0) |
| @@ -0,0 +1,265 @@ |
| +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include <windows.h> |
| + |
| +#include "base/basictypes.h" |
| + |
| +#include "WTF/ListHashSet.h" |
| +#include "WTF/Vector.h" |
| + |
| +#include "SkiaFontWin.h" |
| + |
| +#include "SkCanvas.h" |
| +#include "SkPaint.h" |
| + |
| +namespace WebCore { |
| + |
| +namespace { |
| + |
| +struct CachedOutlineKey { |
| + CachedOutlineKey() : font(NULL), glyph(0), path(NULL) {} |
| + CachedOutlineKey(HFONT f, WORD g) : font(f), glyph(g), path(NULL) {} |
| + |
| + HFONT font; |
| + WORD glyph; |
| + |
| + // The lifetime of this pointer is managed externally to this class. Be sure |
| + // to call DeleteOutline to remove items. |
| + SkPath* path; |
| +}; |
| + |
| +const bool operator==(const CachedOutlineKey& a, const CachedOutlineKey& b) |
| +{ |
| + return a.font == b.font && a.glyph == b.glyph; |
| +} |
| + |
| +struct CachedOutlineKeyHash { |
| + static unsigned hash(const CachedOutlineKey& key) |
| + { |
| + return bit_cast<unsigned>(key.font) + key.glyph; |
| + } |
| + |
| + static unsigned equal(const CachedOutlineKey& a, |
| + const CachedOutlineKey& b) |
| + { |
| + return a.font == b.font && a.glyph == b.glyph; |
| + } |
| + |
| + static const bool safeToCompareToEmptyOrDeleted = true; |
| +}; |
| + |
| +typedef ListHashSet<CachedOutlineKey, CachedOutlineKeyHash> OutlineCache; |
| +OutlineCache outlineCache; |
| + |
| +// The global number of glyph outlines we'll cache. |
| +const int outlineCacheSize = 256; |
| + |
| +inline FIXED SkScalarToFIXED(SkScalar x) |
| +{ |
| + return bit_cast<FIXED>(SkScalarToFixed(x)); |
| +} |
| + |
| +inline SkScalar FIXEDToSkScalar(FIXED fixed) |
| +{ |
| + return SkFixedToScalar(bit_cast<SkFixed>(fixed)); |
| +} |
| + |
| +// Removes the given key from the cached outlines, also deleting the path. |
| +void DeleteOutline(OutlineCache::iterator deleteMe) |
| +{ |
| + delete deleteMe->path; |
| + outlineCache.remove(deleteMe); |
| +} |
| + |
| +void AddPolyCurveToPath(const TTPOLYCURVE* polyCurve, SkPath* path) |
| +{ |
| + switch (polyCurve->wType) { |
| + case TT_PRIM_LINE: |
| + for (WORD i = 0; i < polyCurve->cpfx; i++) { |
| + path->lineTo(FIXEDToSkScalar(polyCurve->apfx[i].x), |
| + -FIXEDToSkScalar(polyCurve->apfx[i].y)); |
| + } |
| + break; |
| + |
| + case TT_PRIM_QSPLINE: |
| + // FIXME(brettw) doesn't this duplicate points if we do the loop > once? |
| + for (WORD i = 0; i < polyCurve->cpfx - 1; i++) { |
| + SkScalar bx = FIXEDToSkScalar(polyCurve->apfx[i].x); |
| + SkScalar by = FIXEDToSkScalar(polyCurve->apfx[i].y); |
| + |
| + SkScalar cx = FIXEDToSkScalar(polyCurve->apfx[i + 1].x); |
| + SkScalar cy = FIXEDToSkScalar(polyCurve->apfx[i + 1].y); |
| + if (i < polyCurve->cpfx - 2) { |
| + // We're not the last point, compute C. |
| + cx = SkScalarAve(bx, cx); |
| + cy = SkScalarAve(by, cy); |
| + } |
| + |
| + // Need to flip the y coordinates since the font's coordinate system is |
|
M-A Ruel
2008/12/15 19:54:51
80 cols, if you care.
|
| + // flipped from ours vertically. |
| + path->quadTo(bx, -by, cx, -cy); |
| + } |
| + break; |
| + |
| + case TT_PRIM_CSPLINE: |
| + // FIXME |
| + break; |
| + } |
| +} |
| + |
| +// Fills the given SkPath with the outline for the given glyph index. The font |
| +// currently selected into the given DC is used. Returns true on success. |
| +bool GetPathForGlyph(HDC dc, WORD glyph, SkPath* path) |
| +{ |
| + char buffer[4096]; |
| + GLYPHMETRICS gm; |
| + MAT2 mat = {{0, 1}, {0, 0}, {0, 0}, {0, 1}}; // Each one is (fract,value). |
| + |
| + DWORD totalSize = GetGlyphOutlineW(dc, glyph, GGO_GLYPH_INDEX | GGO_NATIVE, |
| + &gm, arraysize(buffer), buffer, &mat); |
| + if (totalSize == GDI_ERROR) |
| + return false; |
| + |
| + const char* curGlyph = buffer; |
| + const char* endGlyph = &buffer[totalSize]; |
| + while (curGlyph < endGlyph) { |
| + const TTPOLYGONHEADER* polyHeader = |
| + reinterpret_cast<const TTPOLYGONHEADER*>(curGlyph); |
| + path->moveTo(FIXEDToSkScalar(polyHeader->pfxStart.x), |
| + -FIXEDToSkScalar(polyHeader->pfxStart.y)); |
| + |
| + const char* curPoly = curGlyph + sizeof(TTPOLYGONHEADER); |
| + const char* endPoly = curGlyph + polyHeader->cb; |
| + while (curPoly < endPoly) { |
| + const TTPOLYCURVE* polyCurve = |
| + reinterpret_cast<const TTPOLYCURVE*>(curPoly); |
| + AddPolyCurveToPath(polyCurve, path); |
| + curPoly += sizeof(WORD) * 2 + sizeof(POINTFX) * polyCurve->cpfx; |
| + } |
| + curGlyph += polyHeader->cb; |
| + } |
| + |
| + path->close(); |
| + return true; |
| +} |
| + |
| +// Returns a SkPath corresponding to the give glyph in the given font. The font |
| +// should be selected into the given DC. The returned path is owned by the |
| +// hashtable. Returns NULL on error. |
| +const SkPath* GetCachedPathForGlyph(HDC hdc, HFONT font, WORD glyph) |
| +{ |
| + CachedOutlineKey key(font, glyph); |
| + OutlineCache::iterator found = outlineCache.find(key); |
| + if (found != outlineCache.end()) { |
| + // Keep in MRU order by removing & reinserting the value. |
| + key = *found; |
| + outlineCache.remove(found); |
| + outlineCache.add(key); |
| + return key.path; |
| + } |
| + |
| + key.path = new SkPath; |
| + if (!GetPathForGlyph(hdc, glyph, key.path)) |
| + return NULL; |
| + |
| + if (outlineCache.size() > outlineCacheSize) { |
| + // The cache is too big, find the oldest value (first in the list). |
| + DeleteOutline(outlineCache.begin()); |
| + } |
| + |
| + outlineCache.add(key); |
| + return key.path; |
| +} |
| + |
| +} // namespace |
| + |
| +bool SkiaDrawText(HFONT hfont, |
| + SkCanvas* canvas, |
| + const SkPoint& point, |
| + SkPaint* paint, |
| + const WORD* glyphs, |
| + const int* advances, |
| + int num_glyphs) |
| +{ |
| + HDC dc = GetDC(0); |
| + HGDIOBJ old_font = SelectObject(dc, hfont); |
| + |
| + canvas->save(); |
| + canvas->translate(point.fX, point.fY); |
| + |
| + for (int i = 0; i < num_glyphs; i++) { |
| + const SkPath* path = GetCachedPathForGlyph(dc, hfont, glyphs[i]); |
| + if (!path) |
| + return false; |
| + canvas->drawPath(*path, *paint); |
| + canvas->translate(advances[i], 0); |
| + } |
| + |
| + canvas->restore(); |
| + |
| + SelectObject(dc, old_font); |
| + ReleaseDC(0, dc); |
| + return true; |
| +} |
| + |
| +/* TODO(brettw) finish this implementation |
| +bool SkiaDrawComplexText(HFONT font, |
| + SkCanvas* canvas, |
| + const SkPoint& point, |
| + SkPaint* paint |
| + UINT fuOptions, |
| + const SCRIPT_ANALYSIS* psa, |
| + const WORD* pwGlyphs, |
| + int cGlyphs, |
| + const int* advances, |
| + const int* justifies, |
| + const GOFFSET* glyph_offsets) |
| +{ |
| + HDC dc = GetDC(0); |
| + HGDIOBJ old_font = SelectObject(dc, hfont); |
| + |
| + canvas->save(); |
| + canvas->translate(point.fX, point.fY); |
| + |
| + for (int i = 0; i < cGlyphs; i++) { |
| + canvas->translate(glyph_offsets[i].du, glyph_offsets[i].dv); |
| + |
| + |
| + |
| + |
| + // Undo the offset for this glyph. |
| + canvas->translate(-glyph_offsets[i].du, -glyph_offsets[i].dv); |
| + |
| + // And advance to where we're drawing the next one. We use the justifies |
| + // run since that is the justified advances for each character, rather than |
| + // the adnvaces one. |
| + canvas->translate(justifies[i], 0); |
| + } |
| + |
| + canvas->restore(); |
| + |
| + SelectObject(dc, old_font); |
| + ReleaseDC(0, dc); |
| +}*/ |
| + |
| +void RemoveFontFromSkiaFontWinCache(HFONT hfont) |
| +{ |
| + // ListHashSet isn't the greatest structure for deleting stuff out of, but |
| + // removing entries will be relatively rare (we don't remove fonts much, nor |
| + // do we draw out own glyphs using these routines much either). |
| + // |
| + // We keep a list of all glyphs we're removing which we do in a separate |
| + // pass. |
| + Vector<CachedOutlineKey> outlinesToDelete; |
|
M-A Ruel
2008/12/15 19:54:51
I guess Vector<OutlineCache::iterator> would break
|
| + for (OutlineCache::iterator i = outlineCache.begin(); |
| + i != outlineCache.end(); ++i) |
| + outlinesToDelete.append(*i); |
| + |
| + for (Vector<CachedOutlineKey>::iterator i = outlinesToDelete.begin(); |
| + i != outlinesToDelete.end(); ++i) |
| + DeleteOutline(outlineCache.find(*i)); |
| +} |
| + |
| +} // namespace WebCore |
| Property changes on: webkit\port\platform\graphics\skia\SkiaFontWin.cpp |
| ___________________________________________________________________ |
| Added: svn:mergeinfo |