2009-06-30 Adam Langley <agl@google.com>
[WebKit-https.git] / WebCore / platform / graphics / chromium / FontLinux.cpp
1 /*
2  * Copyright (c) 2007, 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 "Font.h"
33
34 #include "FloatRect.h"
35 #include "GlyphBuffer.h"
36 #include "GraphicsContext.h"
37 #include "NotImplemented.h"
38 #include "PlatformContextSkia.h"
39 #include "SimpleFontData.h"
40
41 #include "SkCanvas.h"
42 #include "SkPaint.h"
43 #include "SkTemplates.h"
44 #include "SkTypeface.h"
45 #include "SkUtils.h"
46
47 extern "C" {
48 #include "harfbuzz-shaper.h"
49 #include "harfbuzz-unicode.h"
50 }
51
52 namespace WebCore {
53
54 bool Font::canReturnFallbackFontsForComplexText()
55 {
56     return false;
57 }
58
59 void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font,
60                       const GlyphBuffer& glyphBuffer,  int from, int numGlyphs,
61                       const FloatPoint& point) const {
62     SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t)); // compile-time assert
63
64     const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from);
65     SkScalar x = SkFloatToScalar(point.x());
66     SkScalar y = SkFloatToScalar(point.y());
67
68     // FIXME: text rendering speed:
69     // Android has code in their WebCore fork to special case when the
70     // GlyphBuffer has no advances other than the defaults. In that case the
71     // text drawing can proceed faster. However, it's unclear when those
72     // patches may be upstreamed to WebKit so we always use the slower path
73     // here.
74     const GlyphBufferAdvance* adv = glyphBuffer.advances(from);
75     SkAutoSTMalloc<32, SkPoint> storage(numGlyphs);
76     SkPoint* pos = storage.get();
77
78     for (int i = 0; i < numGlyphs; i++) {
79         pos[i].set(x, y);
80         x += SkFloatToScalar(adv[i].width());
81         y += SkFloatToScalar(adv[i].height());
82     }
83
84     SkCanvas* canvas = gc->platformContext()->canvas();
85     int textMode = gc->platformContext()->getTextDrawingMode();
86
87     // We draw text up to two times (once for fill, once for stroke).
88     if (textMode & cTextFill) {
89         SkPaint paint;
90         gc->platformContext()->setupPaintForFilling(&paint);
91         font->platformData().setupPaint(&paint);
92         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
93         paint.setColor(gc->fillColor().rgb());
94         canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint);
95     }
96
97     if ((textMode & cTextStroke)
98         && gc->platformContext()->getStrokeStyle() != NoStroke
99         && gc->platformContext()->getStrokeThickness() > 0) {
100
101         SkPaint paint;
102         gc->platformContext()->setupPaintForStroking(&paint, 0, 0);
103         font->platformData().setupPaint(&paint);
104         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
105         paint.setColor(gc->strokeColor().rgb());
106
107         if (textMode & cTextFill) {
108             // If we also filled, we don't want to draw shadows twice.
109             // See comment in FontChromiumWin.cpp::paintSkiaText() for more details.
110             paint.setLooper(0)->safeUnref();
111         }
112
113         canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint);
114     }
115 }
116
117 extern const HB_FontClass harfbuzzSkiaClass;
118 extern HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag, HB_Byte* buffer, HB_UInt* len);
119
120 // Harfbuzz uses 26.6 fixed point values for pixel offsets. However, we don't
121 // handle subpixel positioning so this function is used to truncate Harfbuzz
122 // values to a number of pixels.
123 static int truncateFixedPointToInteger(HB_Fixed value)
124 {
125     return value >> 6;
126 }
127
128 // TextRunWalker walks a TextRun and presents each script run in sequence. A
129 // TextRun is a sequence of code-points with the same embedding level (i.e. they
130 // are all left-to-right or right-to-left). A script run is a subsequence where
131 // all the characters have the same script (e.g. Arabic, Thai etc). Shaping is
132 // only ever done with script runs since the shapers only know how to deal with
133 // a single script.
134 //
135 // After creating it, the script runs are either iterated backwards or forwards.
136 // It defaults to backwards for RTL and forwards otherwise (which matches the
137 // presentation order), however you can set it with |setBackwardsIteration|.
138 //
139 // Once you have setup the object, call |nextScriptRun| to get the first script
140 // run. This will return false when the iteration is complete. At any time you
141 // can call |reset| to start over again.
142 class TextRunWalker {
143 public:
144     TextRunWalker(const TextRun& run, unsigned startingX, const Font* font)
145         : m_font(font)
146         , m_run(run)
147         , m_startingX(startingX)
148         , m_offsetX(m_startingX)
149         , m_iterateBackwards(run.rtl())
150     {
151         memset(&m_item, 0, sizeof(m_item));
152         // We cannot know, ahead of time, how many glyphs a given script run
153         // will produce. We take a guess that script runs will not produce more
154         // than twice as many glyphs as there are code points and fallback if
155         // we find that we are wrong.
156         m_maxGlyphs = run.length() * 2;
157         createGlyphArrays();
158
159         m_item.log_clusters = new unsigned short[run.length()];
160
161         m_item.face = 0;
162         m_item.font = allocHarfbuzzFont();
163
164         m_item.string = run.characters();
165         m_item.stringLength = run.length();
166         m_item.item.bidiLevel = run.rtl();
167
168         reset();
169     }
170
171     ~TextRunWalker()
172     {
173         fastFree(m_item.font);
174         deleteGlyphArrays();
175         delete[] m_item.log_clusters;
176         if (m_item.face)
177             HB_FreeFace(m_item.face);
178     }
179
180     void reset()
181     {
182         if (m_iterateBackwards)
183             m_indexOfNextScriptRun = m_run.length() - 1;
184         else
185             m_indexOfNextScriptRun = 0;
186         m_offsetX = m_startingX;
187     }
188
189     // Set the x offset for the next script run. This affects the values in
190     // |xPositions|
191     void setXOffsetToZero()
192     {
193         m_offsetX = 0;
194     }
195
196     bool rtl() const
197     {
198         return m_run.rtl();
199     }
200
201     void setBackwardsIteration(bool isBackwards)
202     {
203         m_iterateBackwards = isBackwards;
204         reset();
205     }
206
207     // Advance to the next script run, returning false when the end of the
208     // TextRun has been reached.
209     bool nextScriptRun()
210     {
211         if (m_iterateBackwards) {
212             // In right-to-left mode we need to render the shaped glyph backwards and
213             // also render the script runs themselves backwards. So given a TextRun:
214             //    AAAAAAACTTTTTTT   (A = Arabic, C = Common, T = Thai)
215             // we render:
216             //    TTTTTTCAAAAAAA
217             // (and the glyphs in each A, C and T section are backwards too)
218             if (!hb_utf16_script_run_prev(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun))
219                 return false;
220         } else {
221             if (!hb_utf16_script_run_next(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun))
222                 return false;
223         }
224
225         setupFontForScriptRun();
226
227         if (!shapeGlyphs())
228             return false;
229         setGlyphXPositions(rtl());
230         return true;
231     }
232
233     const uint16_t* glyphs() const
234     {
235         return m_glyphs16;
236     }
237
238     // Return the length of the array returned by |glyphs|
239     const unsigned length() const
240     {
241         return m_item.num_glyphs;
242     }
243
244     // Return the x offset for each of the glyphs. Note that this is translated
245     // by the current x offset and that the x offset is updated for each script
246     // run.
247     const SkScalar* xPositions() const
248     {
249         return m_xPositions;
250     }
251
252     // Get the advances (widths) for each glyph.
253     const HB_Fixed* advances() const
254     {
255         return m_item.advances;
256     }
257
258     // Return the width (in px) of the current script run.
259     const unsigned width() const
260     {
261         return m_pixelWidth;
262     }
263
264     // Return the cluster log for the current script run. For example:
265     //   script run: f i a n c é  (fi gets ligatured)
266     //   log clutrs: 0 0 1 2 3 4
267     // So, for each input code point, the log tells you which output glyph was
268     // generated for it.
269     const unsigned short* logClusters() const
270     {
271         return m_item.log_clusters;
272     }
273
274     // return the number of code points in the current script run
275     const unsigned numCodePoints() const
276     {
277         return m_numCodePoints;
278     }
279
280     const FontPlatformData* fontPlatformDataForScriptRun()
281     {
282         return reinterpret_cast<FontPlatformData*>(m_item.font->userData);
283     }
284
285     float widthOfFullRun()
286     {
287         float widthSum = 0;
288         while (nextScriptRun())
289             widthSum += width();
290
291         return widthSum;
292     }
293
294 private:
295     void setupFontForScriptRun()
296     {
297         const FontData* fontData = m_font->fontDataAt(0);
298         if (!fontData->containsCharacters(m_item.string + m_item.item.pos, m_item.item.length))
299             fontData = m_font->fontDataForCharacters(m_item.string + m_item.item.pos, m_item.item.length);
300         const FontPlatformData& platformData = fontData->fontDataForCharacter(' ')->platformData();
301         void* opaquePlatformData = const_cast<FontPlatformData*>(&platformData);
302         m_item.font->userData = opaquePlatformData;
303         if (m_item.face)
304             HB_FreeFace(m_item.face);
305         m_item.face = HB_NewFace(opaquePlatformData, harfbuzzSkiaGetTable);
306     }
307
308     HB_FontRec* allocHarfbuzzFont()
309     {
310         HB_FontRec* font = reinterpret_cast<HB_FontRec*>(fastMalloc(sizeof(HB_FontRec)));
311         memset(font, 0, sizeof(HB_FontRec));
312         font->klass = &harfbuzzSkiaClass;
313         font->userData = 0;
314         // The values which harfbuzzSkiaClass returns are already scaled to
315         // pixel units, so we just set all these to one to disable further
316         // scaling.
317         font->x_ppem = 1;
318         font->y_ppem = 1;
319         font->x_scale = 1;
320         font->y_scale = 1;
321
322         return font;
323     }
324
325     void deleteGlyphArrays()
326     {
327         delete[] m_item.glyphs;
328         delete[] m_item.attributes;
329         delete[] m_item.advances;
330         delete[] m_item.offsets;
331         delete[] m_glyphs16;
332         delete[] m_xPositions;
333     }
334
335     bool createGlyphArrays()
336     {
337         m_item.glyphs = new HB_Glyph[m_maxGlyphs];
338         m_item.attributes = new HB_GlyphAttributes[m_maxGlyphs];
339         m_item.advances = new HB_Fixed[m_maxGlyphs];
340         m_item.offsets = new HB_FixedPoint[m_maxGlyphs];
341         m_glyphs16 = new uint16_t[m_maxGlyphs];
342         m_xPositions = new SkScalar[m_maxGlyphs];
343
344         return m_item.glyphs
345             && m_item.attributes
346             && m_item.advances
347             && m_item.offsets
348             && m_glyphs16
349             && m_xPositions;
350     }
351
352     bool expandGlyphArrays()
353     {
354         deleteGlyphArrays();
355         m_maxGlyphs <<= 1;
356         return createGlyphArrays();
357     }
358
359     bool shapeGlyphs()
360     {
361         for (;;) {
362             m_item.num_glyphs = m_maxGlyphs;
363             HB_ShapeItem(&m_item);
364             if (m_item.num_glyphs < m_maxGlyphs)
365                 break;
366
367             // We overflowed our arrays. Resize and retry.
368             if (!expandGlyphArrays())
369                 return false;
370         }
371
372         return true;
373     }
374
375     void setGlyphXPositions(bool isRTL)
376     {
377         m_pixelWidth = 0;
378         for (unsigned i = 0; i < m_item.num_glyphs; ++i) {
379             int index;
380             if (isRTL)
381                 index = m_item.num_glyphs - (i + 1);
382             else
383                 index = i;
384
385             m_glyphs16[i] = m_item.glyphs[i];
386             m_xPositions[index] = m_offsetX + m_pixelWidth;
387             m_pixelWidth += truncateFixedPointToInteger(m_item.advances[index]);
388         }
389         m_offsetX += m_pixelWidth;
390     }
391
392     const Font* const m_font;
393     const TextRun& m_run;
394     HB_ShaperItem m_item;
395     uint16_t* m_glyphs16; // A vector of 16-bit glyph ids.
396     SkScalar* m_xPositions; // A vector of x positions for each glyph.
397     ssize_t m_indexOfNextScriptRun; // Indexes the script run in |m_run|.
398     const unsigned m_startingX; // Offset in pixels of the first script run.
399     unsigned m_offsetX; // Offset in pixels to the start of the next script run.
400     unsigned m_pixelWidth; // Width (in px) of the current script run.
401     unsigned m_numCodePoints; // Code points in current script run.
402     unsigned m_maxGlyphs; // Current size of all the Harfbuzz arrays.
403     bool m_iterateBackwards;
404 };
405
406 static void setupForTextPainting(SkPaint* paint, SkColor color)
407 {
408     paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
409     paint->setColor(color);
410 }
411
412 void Font::drawComplexText(GraphicsContext* gc, const TextRun& run,
413                            const FloatPoint& point, int from, int to) const
414 {
415     if (!run.length())
416         return;
417
418     SkCanvas* canvas = gc->platformContext()->canvas();
419     int textMode = gc->platformContext()->getTextDrawingMode();
420     bool fill = textMode & cTextFill;
421     bool stroke = (textMode & cTextStroke)
422                && gc->platformContext()->getStrokeStyle() != NoStroke
423                && gc->platformContext()->getStrokeThickness() > 0;
424
425     if (!fill && !stroke)
426         return;
427
428     SkPaint strokePaint, fillPaint;
429     if (fill) {
430         gc->platformContext()->setupPaintForFilling(&fillPaint);
431         setupForTextPainting(&fillPaint, gc->fillColor().rgb());
432     }
433     if (stroke) {
434         gc->platformContext()->setupPaintForStroking(&strokePaint, 0, 0);
435         setupForTextPainting(&strokePaint, gc->strokeColor().rgb());
436     }
437
438     TextRunWalker walker(run, point.x(), this);
439
440     while (walker.nextScriptRun()) {
441         if (fill) {
442             walker.fontPlatformDataForScriptRun()->setupPaint(&fillPaint);
443             canvas->drawPosTextH(walker.glyphs(), walker.length() << 1, walker.xPositions(), point.y(), fillPaint);
444         }
445
446         if (stroke) {
447             walker.fontPlatformDataForScriptRun()->setupPaint(&strokePaint);
448             canvas->drawPosTextH(walker.glyphs(), walker.length() << 1, walker.xPositions(), point.y(), strokePaint);
449         }
450     }
451 }
452
453 float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* /* fallbackFonts */) const
454 {
455     TextRunWalker walker(run, 0, this);
456     return walker.widthOfFullRun();
457 }
458
459 static int glyphIndexForXPositionInScriptRun(const TextRunWalker& walker, int x)
460 {
461     const HB_Fixed* advances = walker.advances();
462     int glyphIndex;
463     if (walker.rtl()) {
464         for (glyphIndex = walker.length() - 1; glyphIndex >= 0; --glyphIndex) {
465             if (x < truncateFixedPointToInteger(advances[glyphIndex]))
466                 break;
467             x -= truncateFixedPointToInteger(advances[glyphIndex]);
468         }
469     } else {
470         for (glyphIndex = 0; glyphIndex < walker.length(); ++glyphIndex) {
471             if (x < truncateFixedPointToInteger(advances[glyphIndex]))
472                 break;
473             x -= truncateFixedPointToInteger(advances[glyphIndex]);
474         }
475     }
476
477     return glyphIndex;
478 }
479
480 // Return the code point index for the given |x| offset into the text run.
481 int Font::offsetForPositionForComplexText(const TextRun& run, int x,
482                                           bool includePartialGlyphs) const
483 {
484     // (Mac code ignores includePartialGlyphs, and they don't know what it's
485     // supposed to do, so we just ignore it as well.)
486     TextRunWalker walker(run, 0, this);
487
488     // If this is RTL text, the first glyph from the left is actually the last
489     // code point. So we need to know how many code points there are total in
490     // order to subtract. This is different from the length of the TextRun
491     // because UTF-16 surrogate pairs are a single code point, but 32-bits long.
492     // In LTR we leave this as 0 so that we get the correct value for
493     // |basePosition|, below.
494     unsigned totalCodePoints = 0;
495     if (walker.rtl()) {
496         ssize_t offset = 0;
497         while (offset < run.length()) {
498             utf16_to_code_point(run.characters(), run.length(), &offset);
499             totalCodePoints++;
500         }
501     }
502
503     unsigned basePosition = totalCodePoints;
504
505     // For RTL:
506     //   code-point order:  abcd efg hijkl
507     //   on screen:         lkjih gfe dcba
508     //                                ^   ^
509     //                                |   |
510     //                  basePosition--|   |
511     //                 totalCodePoints----|
512     // Since basePosition is currently the total number of code-points, the
513     // first thing we do is decrement it so that it's pointing to the start of
514     // the current script-run.
515     //
516     // For LTR, basePosition is zero so it already points to the start of the
517     // first script run.
518     while (walker.nextScriptRun()) {
519         if (walker.rtl())
520             basePosition -= walker.numCodePoints();
521
522         if (x < walker.width()) {
523             // The x value in question is within this script run. We consider
524             // each glyph in presentation order and stop when we find the one
525             // covering this position.
526             const int glyphIndex = glyphIndexForXPositionInScriptRun(walker, x);
527
528             // Now that we have a glyph index, we have to turn that into a
529             // code-point index. Because of ligatures, several code-points may
530             // have gone into a single glyph. We iterate over the clusters log
531             // and find the first code-point which contributed to the glyph.
532             const unsigned short* log = walker.logClusters();
533             for (unsigned j = 0; j < walker.numCodePoints(); ++j) {
534                 if (log[j] == glyphIndex)
535                     return basePosition + j;
536             }
537
538             ASSERT_NOT_REACHED();
539         }
540
541         x -= walker.width();
542
543         if (!walker.rtl())
544             basePosition += walker.numCodePoints();
545     }
546
547     return basePosition;
548 }
549
550 // Return the rectangle for selecting the given range of code-points in the TextRun.
551 FloatRect Font::selectionRectForComplexText(const TextRun& run,
552                                             const IntPoint& point, int height,
553                                             int from, int to) const
554 {
555     int fromX = -1, toX = -1, fromAdvance = -1, toAdvance = -1;
556     TextRunWalker walker(run, 0, this);
557
558     // Base will point to the x offset for the current script run. Note that, in
559     // the LTR case, width will be 0.
560     int base = walker.rtl() ? walker.widthOfFullRun() : 0;
561     const int leftEdge = base;
562
563     // We want to enumerate the script runs in code point order in the following
564     // code. This call also resets |walker|.
565     walker.setBackwardsIteration(false);
566
567     while (walker.nextScriptRun() && (fromX == -1 || toX == -1)) {
568         // TextRunWalker will helpfully accululate the x offsets for different
569         // script runs for us. For this code, however, we always want the x offsets
570         // to start from zero so we call this before each script run.
571         walker.setXOffsetToZero();
572
573         if (walker.rtl())
574             base -= walker.width();
575
576         if (fromX == -1 && from < walker.numCodePoints()) {
577             // |from| is within this script run. So we index the clusters log to
578             // find which glyph this code-point contributed to and find its x
579             // position.
580             int glyph = walker.logClusters()[from];
581             fromX = base + walker.xPositions()[glyph];
582             fromAdvance = walker.advances()[glyph];
583         } else
584             from -= walker.numCodePoints();
585
586         if (toX == -1 && to < walker.numCodePoints()) {
587             int glyph = walker.logClusters()[to];
588             toX = base + walker.xPositions()[glyph];
589             toAdvance = walker.advances()[glyph];
590         } else
591             to -= walker.numCodePoints();
592
593         if (!walker.rtl())
594             base += walker.width();
595     }
596
597     // The position in question might be just after the text.
598     const int rightEdge = base;
599     if (fromX == -1 && !from)
600         fromX = leftEdge;
601     else if (walker.rtl())
602        fromX += truncateFixedPointToInteger(fromAdvance);
603
604     if (toX == -1 && !to)
605         toX = rightEdge;
606     else if (!walker.rtl())
607         toX += truncateFixedPointToInteger(toAdvance);
608
609     ASSERT(fromX != -1 && toX != -1);
610
611     if (fromX < toX)
612         return FloatRect(point.x() + fromX, point.y(), toX - fromX, height);
613
614     return FloatRect(point.x() + toX, point.y(), fromX - toX, height);
615 }
616
617 } // namespace WebCore