• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2008, Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "config.h"
32 #include "SkiaFontWin.h"
33 
34 #include "AffineTransform.h"
35 #include "PlatformContextSkia.h"
36 #include "Gradient.h"
37 #include "Pattern.h"
38 #include "SkCanvas.h"
39 #include "SkPaint.h"
40 #include "SkShader.h"
41 
42 #include <wtf/ListHashSet.h>
43 #include <wtf/Vector.h>
44 
45 namespace WebCore {
46 
47 struct CachedOutlineKey {
CachedOutlineKeyWebCore::CachedOutlineKey48     CachedOutlineKey() : font(0), glyph(0), path(0) {}
CachedOutlineKeyWebCore::CachedOutlineKey49     CachedOutlineKey(HFONT f, WORD g) : font(f), glyph(g), path(0) {}
50 
51     HFONT font;
52     WORD glyph;
53 
54     // The lifetime of this pointer is managed externally to this class. Be sure
55     // to call DeleteOutline to remove items.
56     SkPath* path;
57 };
58 
operator ==(const CachedOutlineKey & a,const CachedOutlineKey & b)59 const bool operator==(const CachedOutlineKey& a, const CachedOutlineKey& b)
60 {
61     return a.font == b.font && a.glyph == b.glyph;
62 }
63 
64 struct CachedOutlineKeyHash {
hashWebCore::CachedOutlineKeyHash65     static unsigned hash(const CachedOutlineKey& key)
66     {
67         unsigned keyBytes;
68         memcpy(&keyBytes, &key.font, sizeof(unsigned));
69         return keyBytes + key.glyph;
70     }
71 
equalWebCore::CachedOutlineKeyHash72     static unsigned equal(const CachedOutlineKey& a, const CachedOutlineKey& b)
73     {
74         return a.font == b.font && a.glyph == b.glyph;
75     }
76 
77     static const bool safeToCompareToEmptyOrDeleted = true;
78 };
79 
80 typedef ListHashSet<CachedOutlineKey, CachedOutlineKeyHash> OutlineCache;
81 
82 // FIXME: Convert from static constructor to accessor function. WebCore tries to
83 // avoid global constructors to save on start-up time.
84 static OutlineCache outlineCache;
85 
86 // The global number of glyph outlines we'll cache.
87 static const int outlineCacheSize = 256;
88 
FIXEDToSkScalar(FIXED fixed)89 static SkScalar FIXEDToSkScalar(FIXED fixed)
90 {
91     SkFixed skFixed;
92     memcpy(&skFixed, &fixed, sizeof(SkFixed));
93     return SkFixedToScalar(skFixed);
94 }
95 
96 // Removes the given key from the cached outlines, also deleting the path.
deleteOutline(OutlineCache::iterator deleteMe)97 static void deleteOutline(OutlineCache::iterator deleteMe)
98 {
99     delete deleteMe->path;
100     outlineCache.remove(deleteMe);
101 }
102 
addPolyCurveToPath(const TTPOLYCURVE * polyCurve,SkPath * path)103 static void addPolyCurveToPath(const TTPOLYCURVE* polyCurve, SkPath* path)
104 {
105     switch (polyCurve->wType) {
106     case TT_PRIM_LINE:
107         for (WORD i = 0; i < polyCurve->cpfx; i++) {
108           path->lineTo(FIXEDToSkScalar(polyCurve->apfx[i].x), -FIXEDToSkScalar(polyCurve->apfx[i].y));
109         }
110         break;
111 
112     case TT_PRIM_QSPLINE:
113         // FIXME: doesn't this duplicate points if we do the loop > once?
114         for (WORD i = 0; i < polyCurve->cpfx - 1; i++) {
115             SkScalar bx = FIXEDToSkScalar(polyCurve->apfx[i].x);
116             SkScalar by = FIXEDToSkScalar(polyCurve->apfx[i].y);
117 
118             SkScalar cx = FIXEDToSkScalar(polyCurve->apfx[i + 1].x);
119             SkScalar cy = FIXEDToSkScalar(polyCurve->apfx[i + 1].y);
120             if (i < polyCurve->cpfx - 2) {
121                 // We're not the last point, compute C.
122                 cx = SkScalarAve(bx, cx);
123                 cy = SkScalarAve(by, cy);
124             }
125 
126             // Need to flip the y coordinates since the font's coordinate system is
127             // flipped from ours vertically.
128             path->quadTo(bx, -by, cx, -cy);
129         }
130         break;
131 
132     case TT_PRIM_CSPLINE:
133         // FIXME
134         break;
135     }
136 }
137 
138 // The size of the glyph path buffer.
139 static const int glyphPathBufferSize = 4096;
140 
141 // Fills the given SkPath with the outline for the given glyph index. The font
142 // currently selected into the given DC is used. Returns true on success.
getPathForGlyph(HDC dc,WORD glyph,SkPath * path)143 static bool getPathForGlyph(HDC dc, WORD glyph, SkPath* path)
144 {
145     char buffer[glyphPathBufferSize];
146     GLYPHMETRICS gm;
147     MAT2 mat = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};  // Each one is (fract,value).
148 
149     DWORD totalSize = GetGlyphOutlineW(dc, glyph, GGO_GLYPH_INDEX | GGO_NATIVE,
150                                        &gm, glyphPathBufferSize, buffer, &mat);
151     if (totalSize == GDI_ERROR)
152         return false;
153 
154     const char* curGlyph = buffer;
155     const char* endGlyph = &buffer[totalSize];
156     while (curGlyph < endGlyph) {
157         const TTPOLYGONHEADER* polyHeader =
158             reinterpret_cast<const TTPOLYGONHEADER*>(curGlyph);
159         path->moveTo(FIXEDToSkScalar(polyHeader->pfxStart.x),
160                      -FIXEDToSkScalar(polyHeader->pfxStart.y));
161 
162         const char* curPoly = curGlyph + sizeof(TTPOLYGONHEADER);
163         const char* endPoly = curGlyph + polyHeader->cb;
164         while (curPoly < endPoly) {
165             const TTPOLYCURVE* polyCurve =
166                 reinterpret_cast<const TTPOLYCURVE*>(curPoly);
167             addPolyCurveToPath(polyCurve, path);
168             curPoly += sizeof(WORD) * 2 + sizeof(POINTFX) * polyCurve->cpfx;
169         }
170         path->close();
171         curGlyph += polyHeader->cb;
172     }
173 
174     return true;
175 }
176 
177 // Returns a SkPath corresponding to the give glyph in the given font. The font
178 // should be selected into the given DC. The returned path is owned by the
179 // hashtable. Returns 0 on error.
lookupOrCreatePathForGlyph(HDC hdc,HFONT font,WORD glyph)180 const SkPath* SkiaWinOutlineCache::lookupOrCreatePathForGlyph(HDC hdc, HFONT font, WORD glyph)
181 {
182     CachedOutlineKey key(font, glyph);
183     OutlineCache::iterator found = outlineCache.find(key);
184     if (found != outlineCache.end()) {
185         // Keep in MRU order by removing & reinserting the value.
186         key = *found;
187         outlineCache.remove(found);
188         outlineCache.add(key);
189         return key.path;
190     }
191 
192     key.path = new SkPath;
193     if (!getPathForGlyph(hdc, glyph, key.path))
194       return 0;
195 
196     if (outlineCache.size() > outlineCacheSize)
197         // The cache is too big, find the oldest value (first in the list).
198         deleteOutline(outlineCache.begin());
199 
200     outlineCache.add(key);
201     return key.path;
202 }
203 
204 
removePathsForFont(HFONT hfont)205 void SkiaWinOutlineCache::removePathsForFont(HFONT hfont)
206 {
207     // ListHashSet isn't the greatest structure for deleting stuff out of, but
208     // removing entries will be relatively rare (we don't remove fonts much, nor
209     // do we draw out own glyphs using these routines much either).
210     //
211     // We keep a list of all glyphs we're removing which we do in a separate
212     // pass.
213     Vector<CachedOutlineKey> outlinesToDelete;
214     for (OutlineCache::iterator i = outlineCache.begin();
215          i != outlineCache.end(); ++i)
216         outlinesToDelete.append(*i);
217 
218     for (Vector<CachedOutlineKey>::iterator i = outlinesToDelete.begin();
219          i != outlinesToDelete.end(); ++i)
220         deleteOutline(outlineCache.find(*i));
221 }
222 
windowsCanHandleDrawTextShadow(WebCore::GraphicsContext * context)223 bool windowsCanHandleDrawTextShadow(WebCore::GraphicsContext *context)
224 {
225     IntSize shadowSize;
226     int shadowBlur;
227     Color shadowColor;
228 
229     bool hasShadow = context->getShadow(shadowSize, shadowBlur, shadowColor);
230     return (hasShadow && (shadowBlur == 0) && (shadowColor.alpha() == 255) && (context->fillColor().alpha() == 255));
231 }
232 
windowsCanHandleTextDrawing(GraphicsContext * context)233 bool windowsCanHandleTextDrawing(GraphicsContext* context)
234 {
235     // Check for non-translation transforms. Sometimes zooms will look better in
236     // Skia, and sometimes better in Windows. The main problem is that zooming
237     // in using Skia will show you the hinted outlines for the smaller size,
238     // which look weird. All else being equal, it's better to use Windows' text
239     // drawing, so we don't check for zooms.
240     const AffineTransform& matrix = context->getCTM();
241     if (matrix.b() != 0 || matrix.c() != 0)  // Check for skew.
242         return false;
243 
244     // Check for stroke effects.
245     if (context->platformContext()->getTextDrawingMode() != cTextFill)
246         return false;
247 
248     // Check for gradients.
249     if (context->fillGradient() || context->strokeGradient())
250         return false;
251 
252     // Check for patterns.
253     if (context->fillPattern() || context->strokePattern())
254         return false;
255 
256     // Check for shadow effects.
257     if (context->platformContext()->getDrawLooper() && (!windowsCanHandleDrawTextShadow(context)))
258         return false;
259 
260     return true;
261 }
262 
263 // Draws the given text string using skia.  Note that gradient or
264 // pattern may be NULL, in which case a solid colour is used.
skiaDrawText(HFONT hfont,HDC dc,SkCanvas * canvas,const SkPoint & point,SkPaint * paint,const WORD * glyphs,const int * advances,const GOFFSET * offsets,int numGlyphs)265 static bool skiaDrawText(HFONT hfont,
266                          HDC dc,
267                          SkCanvas* canvas,
268                          const SkPoint& point,
269                          SkPaint* paint,
270                          const WORD* glyphs,
271                          const int* advances,
272                          const GOFFSET* offsets,
273                          int numGlyphs)
274 {
275     float x = point.fX, y = point.fY;
276 
277     for (int i = 0; i < numGlyphs; i++) {
278         const SkPath* path = SkiaWinOutlineCache::lookupOrCreatePathForGlyph(dc, hfont, glyphs[i]);
279         if (!path)
280             return false;
281 
282         float offsetX = 0.0f, offsetY = 0.0f;
283         if (offsets && (offsets[i].du != 0 || offsets[i].dv != 0)) {
284             offsetX = offsets[i].du;
285             offsetY = offsets[i].dv;
286         }
287 
288         SkPath newPath;
289         newPath.addPath(*path, x + offsetX, y + offsetY);
290         canvas->drawPath(newPath, *paint);
291 
292         x += advances[i];
293     }
294 
295     return true;
296 }
297 
paintSkiaText(GraphicsContext * context,HFONT hfont,int numGlyphs,const WORD * glyphs,const int * advances,const GOFFSET * offsets,const SkPoint * origin)298 bool paintSkiaText(GraphicsContext* context,
299                    HFONT hfont,
300                    int numGlyphs,
301                    const WORD* glyphs,
302                    const int* advances,
303                    const GOFFSET* offsets,
304                    const SkPoint* origin)
305 {
306     HDC dc = GetDC(0);
307     HGDIOBJ oldFont = SelectObject(dc, hfont);
308 
309     PlatformContextSkia* platformContext = context->platformContext();
310     int textMode = platformContext->getTextDrawingMode();
311 
312     // Filling (if necessary). This is the common case.
313     SkPaint paint;
314     platformContext->setupPaintForFilling(&paint);
315     paint.setFlags(SkPaint::kAntiAlias_Flag);
316     bool didFill = false;
317 
318     if ((textMode & cTextFill) && SkColorGetA(paint.getColor())) {
319         if (!skiaDrawText(hfont, dc, platformContext->canvas(), *origin, &paint,
320                           &glyphs[0], &advances[0], &offsets[0], numGlyphs))
321             return false;
322         didFill = true;
323     }
324 
325     // Stroking on top (if necessary).
326     if ((textMode & WebCore::cTextStroke)
327         && platformContext->getStrokeStyle() != NoStroke
328         && platformContext->getStrokeThickness() > 0) {
329 
330         paint.reset();
331         platformContext->setupPaintForStroking(&paint, 0, 0);
332         paint.setFlags(SkPaint::kAntiAlias_Flag);
333         if (didFill) {
334             // If there is a shadow and we filled above, there will already be
335             // a shadow. We don't want to draw it again or it will be too dark
336             // and it will go on top of the fill.
337             //
338             // Note that this isn't strictly correct, since the stroke could be
339             // very thick and the shadow wouldn't account for this. The "right"
340             // thing would be to draw to a new layer and then draw that layer
341             // with a shadow. But this is a lot of extra work for something
342             // that isn't normally an issue.
343             paint.setLooper(0)->safeUnref();
344         }
345 
346         if (!skiaDrawText(hfont, dc, platformContext->canvas(), *origin, &paint,
347                           &glyphs[0], &advances[0], &offsets[0], numGlyphs))
348             return false;
349     }
350 
351     SelectObject(dc, oldFont);
352     ReleaseDC(0, dc);
353 
354     return true;
355 }
356 
357 }  // namespace WebCore
358