#include "config.h"
#include "SkiaFontWin.h"
#include "SkCanvas.h"
#include "SkPaint.h"
#include <wtf/ListHashSet.h>
#include <wtf/Vector.h>
namespace WebCore {
struct CachedOutlineKey {
CachedOutlineKey() : font(0), glyph(0), path(0) {}
CachedOutlineKey(HFONT f, WORD g) : font(f), glyph(g), path(0) {}
HFONT font;
WORD glyph;
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)
{
unsigned keyBytes;
memcpy(&keyBytes, &key.font, sizeof(unsigned));
return keyBytes + 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;
static OutlineCache outlineCache;
static const int outlineCacheSize = 256;
static SkScalar FIXEDToSkScalar(FIXED fixed)
{
SkFixed skFixed;
memcpy(&skFixed, &fixed, sizeof(SkFixed));
return SkFixedToScalar(skFixed);
}
static void deleteOutline(OutlineCache::iterator deleteMe)
{
delete deleteMe->path;
outlineCache.remove(deleteMe);
}
static 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:
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) {
cx = SkScalarAve(bx, cx);
cy = SkScalarAve(by, cy);
}
path->quadTo(bx, -by, cx, -cy);
}
break;
case TT_PRIM_CSPLINE:
break;
}
}
static const int glyphPathBufferSize = 4096;
static bool getPathForGlyph(HDC dc, WORD glyph, SkPath* path)
{
char buffer[glyphPathBufferSize];
GLYPHMETRICS gm;
MAT2 mat = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
DWORD totalSize = GetGlyphOutlineW(dc, glyph, GGO_GLYPH_INDEX | GGO_NATIVE,
&gm, glyphPathBufferSize, 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;
}
const SkPath* SkiaWinOutlineCache::lookupOrCreatePathForGlyph(HDC hdc, HFONT font, WORD glyph)
{
CachedOutlineKey key(font, glyph);
OutlineCache::iterator found = outlineCache.find(key);
if (found != outlineCache.end()) {
key = *found;
outlineCache.remove(found);
outlineCache.add(key);
return key.path;
}
key.path = new SkPath;
if (!getPathForGlyph(hdc, glyph, key.path))
return 0;
if (outlineCache.size() > outlineCacheSize)
deleteOutline(outlineCache.begin());
outlineCache.add(key);
return key.path;
}
void SkiaWinOutlineCache::removePathsForFont(HFONT hfont)
{
Vector<CachedOutlineKey> outlinesToDelete;
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));
}
}