OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include <windows.h> |
| 6 |
| 7 #include "base/basictypes.h" |
| 8 |
| 9 #include "WTF/ListHashSet.h" |
| 10 #include "WTF/Vector.h" |
| 11 |
| 12 #include "SkiaFontWin.h" |
| 13 |
| 14 #include "SkCanvas.h" |
| 15 #include "SkPaint.h" |
| 16 |
| 17 namespace WebCore { |
| 18 |
| 19 namespace { |
| 20 |
| 21 struct CachedOutlineKey { |
| 22 CachedOutlineKey() : font(NULL), glyph(0), path(NULL) {} |
| 23 CachedOutlineKey(HFONT f, WORD g) : font(f), glyph(g), path(NULL) {} |
| 24 |
| 25 HFONT font; |
| 26 WORD glyph; |
| 27 |
| 28 // The lifetime of this pointer is managed externally to this class. Be sure |
| 29 // to call DeleteOutline to remove items. |
| 30 SkPath* path; |
| 31 }; |
| 32 |
| 33 const bool operator==(const CachedOutlineKey& a, const CachedOutlineKey& b) |
| 34 { |
| 35 return a.font == b.font && a.glyph == b.glyph; |
| 36 } |
| 37 |
| 38 struct CachedOutlineKeyHash { |
| 39 static unsigned hash(const CachedOutlineKey& key) |
| 40 { |
| 41 return bit_cast<unsigned>(key.font) + key.glyph; |
| 42 } |
| 43 |
| 44 static unsigned equal(const CachedOutlineKey& a, |
| 45 const CachedOutlineKey& b) |
| 46 { |
| 47 return a.font == b.font && a.glyph == b.glyph; |
| 48 } |
| 49 |
| 50 static const bool safeToCompareToEmptyOrDeleted = true; |
| 51 }; |
| 52 |
| 53 typedef ListHashSet<CachedOutlineKey, CachedOutlineKeyHash> OutlineCache; |
| 54 OutlineCache outlineCache; |
| 55 |
| 56 // The global number of glyph outlines we'll cache. |
| 57 const int outlineCacheSize = 256; |
| 58 |
| 59 inline FIXED SkScalarToFIXED(SkScalar x) |
| 60 { |
| 61 return bit_cast<FIXED>(SkScalarToFixed(x)); |
| 62 } |
| 63 |
| 64 inline SkScalar FIXEDToSkScalar(FIXED fixed) |
| 65 { |
| 66 return SkFixedToScalar(bit_cast<SkFixed>(fixed)); |
| 67 } |
| 68 |
| 69 // Removes the given key from the cached outlines, also deleting the path. |
| 70 void DeleteOutline(OutlineCache::iterator deleteMe) |
| 71 { |
| 72 delete deleteMe->path; |
| 73 outlineCache.remove(deleteMe); |
| 74 } |
| 75 |
| 76 void AddPolyCurveToPath(const TTPOLYCURVE* polyCurve, SkPath* path) |
| 77 { |
| 78 switch (polyCurve->wType) { |
| 79 case TT_PRIM_LINE: |
| 80 for (WORD i = 0; i < polyCurve->cpfx; i++) { |
| 81 path->lineTo(FIXEDToSkScalar(polyCurve->apfx[i].x), |
| 82 -FIXEDToSkScalar(polyCurve->apfx[i].y)); |
| 83 } |
| 84 break; |
| 85 |
| 86 case TT_PRIM_QSPLINE: |
| 87 // FIXME(brettw) doesn't this duplicate points if we do the loop > once? |
| 88 for (WORD i = 0; i < polyCurve->cpfx - 1; i++) { |
| 89 SkScalar bx = FIXEDToSkScalar(polyCurve->apfx[i].x); |
| 90 SkScalar by = FIXEDToSkScalar(polyCurve->apfx[i].y); |
| 91 |
| 92 SkScalar cx = FIXEDToSkScalar(polyCurve->apfx[i + 1].x); |
| 93 SkScalar cy = FIXEDToSkScalar(polyCurve->apfx[i + 1].y); |
| 94 if (i < polyCurve->cpfx - 2) { |
| 95 // We're not the last point, compute C. |
| 96 cx = SkScalarAve(bx, cx); |
| 97 cy = SkScalarAve(by, cy); |
| 98 } |
| 99 |
| 100 // Need to flip the y coordinates since the font's coordinate system
is |
| 101 // flipped from ours vertically. |
| 102 path->quadTo(bx, -by, cx, -cy); |
| 103 } |
| 104 break; |
| 105 |
| 106 case TT_PRIM_CSPLINE: |
| 107 // FIXME |
| 108 break; |
| 109 } |
| 110 } |
| 111 |
| 112 // Fills the given SkPath with the outline for the given glyph index. The font |
| 113 // currently selected into the given DC is used. Returns true on success. |
| 114 bool GetPathForGlyph(HDC dc, WORD glyph, SkPath* path) |
| 115 { |
| 116 char buffer[4096]; |
| 117 GLYPHMETRICS gm; |
| 118 MAT2 mat = {{0, 1}, {0, 0}, {0, 0}, {0, 1}}; // Each one is (fract,value). |
| 119 |
| 120 DWORD totalSize = GetGlyphOutlineW(dc, glyph, GGO_GLYPH_INDEX | GGO_NATIVE, |
| 121 &gm, arraysize(buffer), buffer, &mat); |
| 122 if (totalSize == GDI_ERROR) |
| 123 return false; |
| 124 |
| 125 const char* curGlyph = buffer; |
| 126 const char* endGlyph = &buffer[totalSize]; |
| 127 while (curGlyph < endGlyph) { |
| 128 const TTPOLYGONHEADER* polyHeader = |
| 129 reinterpret_cast<const TTPOLYGONHEADER*>(curGlyph); |
| 130 path->moveTo(FIXEDToSkScalar(polyHeader->pfxStart.x), |
| 131 -FIXEDToSkScalar(polyHeader->pfxStart.y)); |
| 132 |
| 133 const char* curPoly = curGlyph + sizeof(TTPOLYGONHEADER); |
| 134 const char* endPoly = curGlyph + polyHeader->cb; |
| 135 while (curPoly < endPoly) { |
| 136 const TTPOLYCURVE* polyCurve = |
| 137 reinterpret_cast<const TTPOLYCURVE*>(curPoly); |
| 138 AddPolyCurveToPath(polyCurve, path); |
| 139 curPoly += sizeof(WORD) * 2 + sizeof(POINTFX) * polyCurve->cpfx; |
| 140 } |
| 141 curGlyph += polyHeader->cb; |
| 142 } |
| 143 |
| 144 path->close(); |
| 145 return true; |
| 146 } |
| 147 |
| 148 // Returns a SkPath corresponding to the give glyph in the given font. The font |
| 149 // should be selected into the given DC. The returned path is owned by the |
| 150 // hashtable. Returns NULL on error. |
| 151 const SkPath* GetCachedPathForGlyph(HDC hdc, HFONT font, WORD glyph) |
| 152 { |
| 153 CachedOutlineKey key(font, glyph); |
| 154 OutlineCache::iterator found = outlineCache.find(key); |
| 155 if (found != outlineCache.end()) { |
| 156 // Keep in MRU order by removing & reinserting the value. |
| 157 key = *found; |
| 158 outlineCache.remove(found); |
| 159 outlineCache.add(key); |
| 160 return key.path; |
| 161 } |
| 162 |
| 163 key.path = new SkPath; |
| 164 if (!GetPathForGlyph(hdc, glyph, key.path)) |
| 165 return NULL; |
| 166 |
| 167 if (outlineCache.size() > outlineCacheSize) { |
| 168 // The cache is too big, find the oldest value (first in the list). |
| 169 DeleteOutline(outlineCache.begin()); |
| 170 } |
| 171 |
| 172 outlineCache.add(key); |
| 173 return key.path; |
| 174 } |
| 175 |
| 176 } // namespace |
| 177 |
| 178 bool SkiaDrawText(HFONT hfont, |
| 179 SkCanvas* canvas, |
| 180 const SkPoint& point, |
| 181 SkPaint* paint, |
| 182 const WORD* glyphs, |
| 183 const int* advances, |
| 184 int num_glyphs) |
| 185 { |
| 186 HDC dc = GetDC(0); |
| 187 HGDIOBJ old_font = SelectObject(dc, hfont); |
| 188 |
| 189 canvas->save(); |
| 190 canvas->translate(point.fX, point.fY); |
| 191 |
| 192 for (int i = 0; i < num_glyphs; i++) { |
| 193 const SkPath* path = GetCachedPathForGlyph(dc, hfont, glyphs[i]); |
| 194 if (!path) |
| 195 return false; |
| 196 canvas->drawPath(*path, *paint); |
| 197 canvas->translate(advances[i], 0); |
| 198 } |
| 199 |
| 200 canvas->restore(); |
| 201 |
| 202 SelectObject(dc, old_font); |
| 203 ReleaseDC(0, dc); |
| 204 return true; |
| 205 } |
| 206 |
| 207 /* TODO(brettw) finish this implementation |
| 208 bool SkiaDrawComplexText(HFONT font, |
| 209 SkCanvas* canvas, |
| 210 const SkPoint& point, |
| 211 SkPaint* paint |
| 212 UINT fuOptions, |
| 213 const SCRIPT_ANALYSIS* psa, |
| 214 const WORD* pwGlyphs, |
| 215 int cGlyphs, |
| 216 const int* advances, |
| 217 const int* justifies, |
| 218 const GOFFSET* glyph_offsets) |
| 219 { |
| 220 HDC dc = GetDC(0); |
| 221 HGDIOBJ old_font = SelectObject(dc, hfont); |
| 222 |
| 223 canvas->save(); |
| 224 canvas->translate(point.fX, point.fY); |
| 225 |
| 226 for (int i = 0; i < cGlyphs; i++) { |
| 227 canvas->translate(glyph_offsets[i].du, glyph_offsets[i].dv); |
| 228 |
| 229 |
| 230 |
| 231 |
| 232 // Undo the offset for this glyph. |
| 233 canvas->translate(-glyph_offsets[i].du, -glyph_offsets[i].dv); |
| 234 |
| 235 // And advance to where we're drawing the next one. We use the justifies |
| 236 // run since that is the justified advances for each character, rather t
han |
| 237 // the adnvaces one. |
| 238 canvas->translate(justifies[i], 0); |
| 239 } |
| 240 |
| 241 canvas->restore(); |
| 242 |
| 243 SelectObject(dc, old_font); |
| 244 ReleaseDC(0, dc); |
| 245 }*/ |
| 246 |
| 247 void RemoveFontFromSkiaFontWinCache(HFONT hfont) |
| 248 { |
| 249 // ListHashSet isn't the greatest structure for deleting stuff out of, but |
| 250 // removing entries will be relatively rare (we don't remove fonts much, nor |
| 251 // do we draw out own glyphs using these routines much either). |
| 252 // |
| 253 // We keep a list of all glyphs we're removing which we do in a separate |
| 254 // pass. |
| 255 Vector<CachedOutlineKey> outlinesToDelete; |
| 256 for (OutlineCache::iterator i = outlineCache.begin(); |
| 257 i != outlineCache.end(); ++i) |
| 258 outlinesToDelete.append(*i); |
| 259 |
| 260 for (Vector<CachedOutlineKey>::iterator i = outlinesToDelete.begin(); |
| 261 i != outlinesToDelete.end(); ++i) |
| 262 DeleteOutline(outlineCache.find(*i)); |
| 263 } |
| 264 |
| 265 } // namespace WebCore |
OLD | NEW |