2 * Copyright (c) 2007, 2008, Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
34 #include "FloatRect.h"
35 #include "GlyphBuffer.h"
36 #include "GraphicsContext.h"
37 #include "NotImplemented.h"
38 #include "PlatformContextSkia.h"
39 #include "SimpleFontData.h"
43 #include "SkTemplates.h"
44 #include "SkTypeface.h"
48 #include "harfbuzz-shaper.h"
49 #include "harfbuzz-unicode.h"
54 bool Font::canReturnFallbackFontsForComplexText()
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
64 const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from);
65 SkScalar x = SkFloatToScalar(point.x());
66 SkScalar y = SkFloatToScalar(point.y());
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
74 const GlyphBufferAdvance* adv = glyphBuffer.advances(from);
75 SkAutoSTMalloc<32, SkPoint> storage(numGlyphs);
76 SkPoint* pos = storage.get();
78 for (int i = 0; i < numGlyphs; i++) {
80 x += SkFloatToScalar(adv[i].width());
81 y += SkFloatToScalar(adv[i].height());
84 SkCanvas* canvas = gc->platformContext()->canvas();
85 int textMode = gc->platformContext()->getTextDrawingMode();
87 // We draw text up to two times (once for fill, once for stroke).
88 if (textMode & cTextFill) {
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);
97 if ((textMode & cTextStroke)
98 && gc->platformContext()->getStrokeStyle() != NoStroke
99 && gc->platformContext()->getStrokeThickness() > 0) {
102 gc->platformContext()->setupPaintForStroking(&paint, 0, 0);
103 font->platformData().setupPaint(&paint);
104 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
105 paint.setColor(gc->strokeColor().rgb());
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();
113 canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint);
117 extern const HB_FontClass harfbuzzSkiaClass;
118 extern HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag, HB_Byte* buffer, HB_UInt* len);
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)
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
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|.
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 {
144 TextRunWalker(const TextRun& run, unsigned startingX, const Font* font)
147 , m_startingX(startingX)
148 , m_offsetX(m_startingX)
149 , m_iterateBackwards(run.rtl())
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;
159 m_item.log_clusters = new unsigned short[run.length()];
162 m_item.font = allocHarfbuzzFont();
164 m_item.string = run.characters();
165 m_item.stringLength = run.length();
166 m_item.item.bidiLevel = run.rtl();
173 fastFree(m_item.font);
175 delete[] m_item.log_clusters;
177 HB_FreeFace(m_item.face);
182 if (m_iterateBackwards)
183 m_indexOfNextScriptRun = m_run.length() - 1;
185 m_indexOfNextScriptRun = 0;
186 m_offsetX = m_startingX;
189 // Set the x offset for the next script run. This affects the values in
191 void setXOffsetToZero()
201 void setBackwardsIteration(bool isBackwards)
203 m_iterateBackwards = isBackwards;
207 // Advance to the next script run, returning false when the end of the
208 // TextRun has been reached.
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)
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))
221 if (!hb_utf16_script_run_next(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun))
225 setupFontForScriptRun();
229 setGlyphXPositions(rtl());
233 const uint16_t* glyphs() const
238 // Return the length of the array returned by |glyphs|
239 const unsigned length() const
241 return m_item.num_glyphs;
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
247 const SkScalar* xPositions() const
252 // Get the advances (widths) for each glyph.
253 const HB_Fixed* advances() const
255 return m_item.advances;
258 // Return the width (in px) of the current script run.
259 const unsigned width() const
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
269 const unsigned short* logClusters() const
271 return m_item.log_clusters;
274 // return the number of code points in the current script run
275 const unsigned numCodePoints() const
277 return m_numCodePoints;
280 const FontPlatformData* fontPlatformDataForScriptRun()
282 return reinterpret_cast<FontPlatformData*>(m_item.font->userData);
285 float widthOfFullRun()
288 while (nextScriptRun())
295 void setupFontForScriptRun()
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;
304 HB_FreeFace(m_item.face);
305 m_item.face = HB_NewFace(opaquePlatformData, harfbuzzSkiaGetTable);
308 HB_FontRec* allocHarfbuzzFont()
310 HB_FontRec* font = reinterpret_cast<HB_FontRec*>(fastMalloc(sizeof(HB_FontRec)));
311 memset(font, 0, sizeof(HB_FontRec));
312 font->klass = &harfbuzzSkiaClass;
314 // The values which harfbuzzSkiaClass returns are already scaled to
315 // pixel units, so we just set all these to one to disable further
325 void deleteGlyphArrays()
327 delete[] m_item.glyphs;
328 delete[] m_item.attributes;
329 delete[] m_item.advances;
330 delete[] m_item.offsets;
332 delete[] m_xPositions;
335 bool createGlyphArrays()
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];
352 bool expandGlyphArrays()
356 return createGlyphArrays();
362 m_item.num_glyphs = m_maxGlyphs;
363 HB_ShapeItem(&m_item);
364 if (m_item.num_glyphs < m_maxGlyphs)
367 // We overflowed our arrays. Resize and retry.
368 if (!expandGlyphArrays())
375 void setGlyphXPositions(bool isRTL)
378 for (unsigned i = 0; i < m_item.num_glyphs; ++i) {
381 index = m_item.num_glyphs - (i + 1);
385 m_glyphs16[i] = m_item.glyphs[i];
386 m_xPositions[index] = m_offsetX + m_pixelWidth;
387 m_pixelWidth += truncateFixedPointToInteger(m_item.advances[index]);
389 m_offsetX += m_pixelWidth;
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;
406 static void setupForTextPainting(SkPaint* paint, SkColor color)
408 paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
409 paint->setColor(color);
412 void Font::drawComplexText(GraphicsContext* gc, const TextRun& run,
413 const FloatPoint& point, int from, int to) const
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;
425 if (!fill && !stroke)
428 SkPaint strokePaint, fillPaint;
430 gc->platformContext()->setupPaintForFilling(&fillPaint);
431 setupForTextPainting(&fillPaint, gc->fillColor().rgb());
434 gc->platformContext()->setupPaintForStroking(&strokePaint, 0, 0);
435 setupForTextPainting(&strokePaint, gc->strokeColor().rgb());
438 TextRunWalker walker(run, point.x(), this);
440 while (walker.nextScriptRun()) {
442 walker.fontPlatformDataForScriptRun()->setupPaint(&fillPaint);
443 canvas->drawPosTextH(walker.glyphs(), walker.length() << 1, walker.xPositions(), point.y(), fillPaint);
447 walker.fontPlatformDataForScriptRun()->setupPaint(&strokePaint);
448 canvas->drawPosTextH(walker.glyphs(), walker.length() << 1, walker.xPositions(), point.y(), strokePaint);
453 float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* /* fallbackFonts */) const
455 TextRunWalker walker(run, 0, this);
456 return walker.widthOfFullRun();
459 static int glyphIndexForXPositionInScriptRun(const TextRunWalker& walker, int x)
461 const HB_Fixed* advances = walker.advances();
464 for (glyphIndex = walker.length() - 1; glyphIndex >= 0; --glyphIndex) {
465 if (x < truncateFixedPointToInteger(advances[glyphIndex]))
467 x -= truncateFixedPointToInteger(advances[glyphIndex]);
470 for (glyphIndex = 0; glyphIndex < walker.length(); ++glyphIndex) {
471 if (x < truncateFixedPointToInteger(advances[glyphIndex]))
473 x -= truncateFixedPointToInteger(advances[glyphIndex]);
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
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);
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;
497 while (offset < run.length()) {
498 utf16_to_code_point(run.characters(), run.length(), &offset);
503 unsigned basePosition = totalCodePoints;
506 // code-point order: abcd efg hijkl
507 // on screen: lkjih gfe dcba
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.
516 // For LTR, basePosition is zero so it already points to the start of the
518 while (walker.nextScriptRun()) {
520 basePosition -= walker.numCodePoints();
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);
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;
538 ASSERT_NOT_REACHED();
544 basePosition += walker.numCodePoints();
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
555 int fromX = -1, toX = -1, fromAdvance = -1, toAdvance = -1;
556 TextRunWalker walker(run, 0, this);
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;
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);
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();
574 base -= walker.width();
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
580 int glyph = walker.logClusters()[from];
581 fromX = base + walker.xPositions()[glyph];
582 fromAdvance = walker.advances()[glyph];
584 from -= walker.numCodePoints();
586 if (toX == -1 && to < walker.numCodePoints()) {
587 int glyph = walker.logClusters()[to];
588 toX = base + walker.xPositions()[glyph];
589 toAdvance = walker.advances()[glyph];
591 to -= walker.numCodePoints();
594 base += walker.width();
597 // The position in question might be just after the text.
598 const int rightEdge = base;
599 if (fromX == -1 && !from)
601 else if (walker.rtl())
602 fromX += truncateFixedPointToInteger(fromAdvance);
604 if (toX == -1 && !to)
606 else if (!walker.rtl())
607 toX += truncateFixedPointToInteger(toAdvance);
609 ASSERT(fromX != -1 && toX != -1);
612 return FloatRect(point.x() + fromX, point.y(), toX - fromX, height);
614 return FloatRect(point.x() + toX, point.y(), fromX - toX, height);
617 } // namespace WebCore