2009-01-15 Dimitri Glazkov <dglazkov@chromium.org>
[WebKit-https.git] / WebCore / platform / graphics / chromium / FontChromiumWin.cpp
1 /*
2  * Copyright (C) 2006, 2007 Apple Computer, Inc.
3  * Copyright (c) 2006, 2007, 2008, 2009, Google Inc. All rights reserved.
4  * 
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  * 
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  * 
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33 #include "Font.h"
34
35 #include "TransformationMatrix.h"
36 #include "ChromiumBridge.h"
37 #include "FontFallbackList.h"
38 #include "GlyphBuffer.h"
39 #include "PlatformContextSkia.h"
40 #include "SimpleFontData.h"
41 #include "SkiaFontWin.h"
42 #include "SkiaUtils.h"
43 #include "UniscribeHelperTextRun.h"
44
45 #include "skia/ext/platform_canvas_win.h"
46 #include "skia/ext/skia_utils_win.h"  // FIXME: remove this dependency.
47
48 #include <windows.h>
49
50 namespace WebCore {
51
52 static bool windowsCanHandleTextDrawing(GraphicsContext* context)
53 {
54     // Check for non-translation transforms. Sometimes zooms will look better in
55     // Skia, and sometimes better in Windows. The main problem is that zooming
56     // in using Skia will show you the hinted outlines for the smaller size,
57     // which look weird. All else being equal, it's better to use Windows' text
58     // drawing, so we don't check for zooms.
59     const TransformationMatrix& matrix = context->getCTM();
60     if (matrix.b() != 0 || matrix.c() != 0)  // Check for skew.
61         return false;
62
63     // Check for stroke effects.
64     if (context->platformContext()->getTextDrawingMode() != cTextFill)
65         return false;
66
67     // Check for shadow effects.
68     if (context->platformContext()->getDrawLooper())
69         return false;
70
71     return true;
72 }
73
74 // Skia equivalents to Windows text drawing functions. They
75 // will get the outlines from Windows and draw then using Skia using the given
76 // parameters in the paint arguments. This allows more complex effects and
77 // transforms to be drawn than Windows allows.
78 //
79 // These functions will be significantly slower than Windows GDI, and the text
80 // will look different (no ClearType), so use only when necessary.
81 //
82 // When you call a Skia* text drawing function, various glyph outlines will be
83 // cached. As a result, you should call SkiaWinOutlineCache::removePathsForFont 
84 // when the font is destroyed so that the cache does not outlive the font (since
85 // the HFONTs are recycled).
86
87 // Analog of the Windows GDI function DrawText, except using the given SkPaint
88 // attributes for the text. See above for more.
89 //
90 // Returns true of the text was drawn successfully. False indicates an error
91 // from Windows.
92 static bool skiaDrawText(HFONT hfont,
93                   SkCanvas* canvas,
94                   const SkPoint& point,
95                   SkPaint* paint,
96                   const WORD* glyphs,
97                   const int* advances,
98                   int numGlyphs)
99 {
100     HDC dc = GetDC(0);
101     HGDIOBJ oldFont = SelectObject(dc, hfont);
102
103     canvas->save();
104     canvas->translate(point.fX, point.fY);
105
106     for (int i = 0; i < numGlyphs; i++) {
107         const SkPath* path = SkiaWinOutlineCache::lookupOrCreatePathForGlyph(dc, hfont, glyphs[i]);
108         if (!path)
109             return false;
110         canvas->drawPath(*path, *paint);
111         canvas->translate(advances[i], 0);
112     }
113
114     canvas->restore();
115
116     SelectObject(dc, oldFont);
117     ReleaseDC(0, dc);
118     return true;
119 }
120
121 static bool paintSkiaText(PlatformContextSkia* platformContext,
122                           HFONT hfont,
123                           int numGlyphs,
124                           const WORD* glyphs,
125                           const int* advances,
126                           const SkPoint& origin)
127 {
128     int textMode = platformContext->getTextDrawingMode();
129
130     // Filling (if necessary). This is the common case.
131     SkPaint paint;
132     platformContext->setupPaintForFilling(&paint);
133     paint.setFlags(SkPaint::kAntiAlias_Flag);
134     bool didFill = false;
135     if ((textMode & cTextFill) && SkColorGetA(paint.getColor())) {
136         if (!skiaDrawText(hfont, platformContext->canvas(), origin, &paint, &glyphs[0], &advances[0], numGlyphs))
137             return false;
138         didFill = true;
139     }
140
141     // Stroking on top (if necessary).
142     if ((textMode & WebCore::cTextStroke)
143         && platformContext->getStrokeStyle() != NoStroke
144         && platformContext->getStrokeThickness() > 0) {
145
146         paint.reset();
147         platformContext->setupPaintForStroking(&paint, 0, 0);
148         paint.setFlags(SkPaint::kAntiAlias_Flag);
149         if (didFill) {
150             // If there is a shadow and we filled above, there will already be
151             // a shadow. We don't want to draw it again or it will be too dark
152             // and it will go on top of the fill.
153             //
154             // Note that this isn't strictly correct, since the stroke could be
155             // very thick and the shadow wouldn't account for this. The "right"
156             // thing would be to draw to a new layer and then draw that layer
157             // with a shadow. But this is a lot of extra work for something
158             // that isn't normally an issue.
159             paint.setLooper(0)->safeUnref();
160         }
161
162         if (!skiaDrawText(hfont, platformContext->canvas(), origin, &paint, &glyphs[0], &advances[0], numGlyphs))
163             return false;
164     }
165     return true;
166 }
167
168 void Font::drawGlyphs(GraphicsContext* graphicsContext,
169                       const SimpleFontData* font,
170                       const GlyphBuffer& glyphBuffer,
171                       int from,
172                       int numGlyphs,
173                       const FloatPoint& point) const
174 {
175     PlatformGraphicsContext* context = graphicsContext->platformContext();
176
177     // Max buffer length passed to the underlying windows API.
178     const int kMaxBufferLength = 1024;
179     // Default size for the buffer. It should be enough for most of cases.
180     const int kDefaultBufferLength = 256;
181
182     SkColor color = context->fillColor();
183     unsigned char alpha = SkColorGetA(color);
184     // Skip 100% transparent text; no need to draw anything.
185     if (!alpha && context->getStrokeStyle() == NoStroke)
186         return;
187
188     // Set up our graphics context.
189     HDC hdc = context->canvas()->beginPlatformPaint();
190     HGDIOBJ oldFont = SelectObject(hdc, font->platformData().hfont());
191
192     // TODO(maruel): http://b/700464 SetTextColor doesn't support transparency.
193     // Enforce non-transparent color.
194     color = SkColorSetRGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));
195     SetTextColor(hdc, skia::SkColorToCOLORREF(color));
196     SetBkMode(hdc, TRANSPARENT);
197
198     // Windows needs the characters and the advances in nice contiguous
199     // buffers, which we build here.
200     Vector<WORD, kDefaultBufferLength> glyphs;
201     Vector<int, kDefaultBufferLength> advances;
202
203     // Compute the coordinate. The 'origin' represents the baseline, so we need
204     // to move it up to the top of the bounding square.
205     int x = static_cast<int>(point.x());
206     int lineTop = static_cast<int>(point.y()) - font->ascent();
207
208     bool canUseGDI = windowsCanHandleTextDrawing(graphicsContext);
209
210     // We draw the glyphs in chunks to avoid having to do a heap allocation for
211     // the arrays of characters and advances. Since ExtTextOut is the
212     // lowest-level text output function on Windows, there should be little
213     // penalty for splitting up the text. On the other hand, the buffer cannot
214     // be bigger than 4094 or the function will fail.
215     int glyphIndex = 0;
216     while (glyphIndex < numGlyphs) {
217         // how many chars will be in this chunk?
218         int curLen = std::min(kMaxBufferLength, numGlyphs - glyphIndex);
219
220         glyphs.resize(curLen);
221         advances.resize(curLen);
222
223         int curWidth = 0;
224         for (int i = 0; i < curLen; ++i, ++glyphIndex) {
225             glyphs[i] = glyphBuffer.glyphAt(from + glyphIndex);
226             advances[i] = static_cast<int>(glyphBuffer.advanceAt(from + glyphIndex));
227             curWidth += advances[i];
228         }
229
230         bool success = false;
231         for (int executions = 0; executions < 2; ++executions) {
232             if (canUseGDI)
233                 success = !!ExtTextOut(hdc, x, lineTop, ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), curLen, &advances[0]);
234             else {
235                 // Skia's text draing origin is the baseline, like WebKit, not
236                 // the top, like Windows.
237                 SkPoint origin = { x, point.y() };
238                 success = paintSkiaText(context, font->platformData().hfont(), numGlyphs, reinterpret_cast<const WORD*>(&glyphs[0]), &advances[0], origin);
239             }
240
241             if (!success && executions == 0) {
242                 // Ask the browser to load the font for us and retry.
243                 ChromiumBridge::ensureFontLoaded(font->platformData().hfont());
244                 continue;
245             }
246             break;
247         }
248
249         ASSERT(success);
250
251         x += curWidth;
252     }
253
254     SelectObject(hdc, oldFont);
255     context->canvas()->endPlatformPaint();
256 }
257
258 FloatRect Font::selectionRectForComplexText(const TextRun& run,
259                                             const IntPoint& point,
260                                             int h,
261                                             int from,
262                                             int to) const
263 {
264     UniscribeHelperTextRun state(run, *this);
265     float left = static_cast<float>(point.x() + state.characterToX(from));
266     float right = static_cast<float>(point.x() + state.characterToX(to));
267
268     // If the text is RTL, left will actually be after right.
269     if (left < right)
270         return FloatRect(left, static_cast<float>(point.y()),
271                        right - left, static_cast<float>(h));
272
273     return FloatRect(right, static_cast<float>(point.y()),
274                      left - right, static_cast<float>(h));
275 }
276
277 void Font::drawComplexText(GraphicsContext* graphicsContext,
278                            const TextRun& run,
279                            const FloatPoint& point,
280                            int from,
281                            int to) const
282 {
283     PlatformGraphicsContext* context = graphicsContext->platformContext();
284     UniscribeHelperTextRun state(run, *this);
285
286     SkColor color = context->fillColor();
287     unsigned char alpha = SkColorGetA(color);
288     // Skip 100% transparent text; no need to draw anything.
289     if (!alpha)
290         return;
291
292     HDC hdc = context->canvas()->beginPlatformPaint();
293
294     // TODO(maruel): http://b/700464 SetTextColor doesn't support transparency.
295     // Enforce non-transparent color.
296     color = SkColorSetRGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));
297     SetTextColor(hdc, skia::SkColorToCOLORREF(color));
298     SetBkMode(hdc, TRANSPARENT);
299
300     // Uniscribe counts the coordinates from the upper left, while WebKit uses
301     // the baseline, so we have to subtract off the ascent.
302     state.draw(hdc, static_cast<int>(point.x()), static_cast<int>(point.y() - ascent()), from, to);
303     context->canvas()->endPlatformPaint();
304 }
305
306 float Font::floatWidthForComplexText(const TextRun& run) const
307 {
308     UniscribeHelperTextRun state(run, *this);
309     return static_cast<float>(state.width());
310 }
311
312 int Font::offsetForPositionForComplexText(const TextRun& run, int x,
313                                           bool includePartialGlyphs) const
314 {
315     // Mac code ignores includePartialGlyphs, and they don't know what it's
316     // supposed to do, so we just ignore it as well.
317     UniscribeHelperTextRun state(run, *this);
318     int charIndex = state.xToCharacter(x);
319
320     // XToCharacter will return -1 if the position is before the first
321     // character (we get called like this sometimes).
322     if (charIndex < 0)
323         charIndex = 0;
324     return charIndex;
325 }
326
327 } // namespace WebCore