summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/graphics/android/FontAndroid.cpp
diff options
context:
space:
mode:
authorNicolas Roard <nicolasroard@google.com>2012-04-06 11:35:50 -0700
committerNicolas Roard <nicolasroard@google.com>2012-04-06 14:03:59 -0700
commit2e510fd5b5a30f1315c272d44ae3aa4cba355498 (patch)
treedb3af5f32855d329856f190c3509ae11ae519851 /Source/WebCore/platform/graphics/android/FontAndroid.cpp
parentc88c88907b618e468ec3928b06a3a31d4f99b9c6 (diff)
downloadexternal_webkit-2e510fd5b5a30f1315c272d44ae3aa4cba355498.zip
external_webkit-2e510fd5b5a30f1315c272d44ae3aa4cba355498.tar.gz
external_webkit-2e510fd5b5a30f1315c272d44ae3aa4cba355498.tar.bz2
Reorganize platform/graphics/android
Change-Id: Idc67155cfa99784dcd931e705336bfa063ecae46
Diffstat (limited to 'Source/WebCore/platform/graphics/android/FontAndroid.cpp')
-rw-r--r--Source/WebCore/platform/graphics/android/FontAndroid.cpp1192
1 files changed, 0 insertions, 1192 deletions
diff --git a/Source/WebCore/platform/graphics/android/FontAndroid.cpp b/Source/WebCore/platform/graphics/android/FontAndroid.cpp
deleted file mode 100644
index 3146612..0000000
--- a/Source/WebCore/platform/graphics/android/FontAndroid.cpp
+++ /dev/null
@@ -1,1192 +0,0 @@
-/*
- * Copyright 2009, The Android Open Source Project
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-
-#include "EmojiFont.h"
-#include "GraphicsOperationCollection.h"
-#include "GraphicsOperation.h"
-#include "Font.h"
-#include "FontData.h"
-#include "FontFallbackList.h"
-#include "GraphicsContext.h"
-#include "GlyphBuffer.h"
-#include "IntRect.h"
-#include "NotImplemented.h"
-#include "PlatformGraphicsContext.h"
-#include "SkCanvas.h"
-#include "SkColorFilter.h"
-#include "SkLayerDrawLooper.h"
-#include "SkPaint.h"
-#include "SkTemplates.h"
-#include "SkTypeface.h"
-#include "SkUtils.h"
-#include "TextRun.h"
-
-#ifdef SUPPORT_COMPLEX_SCRIPTS
-#include "HarfbuzzSkia.h"
-#include <unicode/normlzr.h>
-#include <unicode/uchar.h>
-#include <wtf/OwnArrayPtr.h>
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnArrayPtr.h>
-#include <wtf/PassOwnPtr.h>
-#include <wtf/unicode/CharacterNames.h>
-#include <wtf/unicode/Unicode.h>
-#endif
-
-using namespace android;
-
-namespace WebCore {
-
-typedef std::pair<int, float> FallbackFontKey;
-typedef HashMap<FallbackFontKey, FontPlatformData*> FallbackHash;
-
-static void updateForFont(SkPaint* paint, const SimpleFontData* font) {
- font->platformData().setupPaint(paint);
- paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-}
-
-static SkPaint* setupFill(SkPaint* paint, GraphicsContext* gc,
- const SimpleFontData* font) {
- gc->setupFillPaint(paint);
- updateForFont(paint, font);
- return paint;
-}
-
-static SkPaint* setupStroke(SkPaint* paint, GraphicsContext* gc,
- const SimpleFontData* font) {
- gc->setupStrokePaint(paint);
- updateForFont(paint, font);
- return paint;
-}
-
-static bool setupForText(SkPaint* paint, GraphicsContext* gc,
- const SimpleFontData* font) {
- int mode = gc->textDrawingMode() & (TextModeFill | TextModeStroke);
- if (!mode)
- return false;
-
- paint->setVerticalText(font->platformData().orientation() == Vertical);
-
- FloatSize shadowOffset;
- float shadowBlur;
- Color shadowColor;
- ColorSpace shadowColorSpace;
- bool hasShadow = gc->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace);
- bool hasBothStrokeAndFill =
- (mode & (TextModeStroke | TextModeFill)) == (TextModeStroke | TextModeFill);
- if (hasShadow || hasBothStrokeAndFill) {
- SkLayerDrawLooper* looper = new SkLayerDrawLooper;
- paint->setLooper(looper)->unref();
-
- // The layerDrawLooper uses at the root paint to determine the text
- // encoding so we need to make sure it is properly configured.
- updateForFont(paint, font);
-
- // Specify the behavior of the looper
- SkLayerDrawLooper::LayerInfo info;
- info.fPaintBits = SkLayerDrawLooper::kEntirePaint_Bits;
- info.fColorMode = SkXfermode::kSrc_Mode;
- info.fFlagsMask = SkPaint::kAllFlags;
-
- // The paint is only valid until the looper receives another call to
- // addLayer(). Therefore, we must cache certain state for later use.
- bool hasFillPaint = false;
- bool hasStrokePaint = false;
- SkScalar strokeWidth;
-
- if ((mode & TextModeStroke) && gc->willStroke()) {
- strokeWidth = setupStroke(looper->addLayer(info), gc, font)->getStrokeWidth();
- hasStrokePaint = true;
- }
- if ((mode & TextModeFill) && gc->willFill()) {
- setupFill(looper->addLayer(info), gc, font);
- hasFillPaint = true;
- }
-
- if (hasShadow) {
- SkPaint shadowPaint;
- SkPoint offset;
- if (gc->setupShadowPaint(&shadowPaint, &offset)) {
-
- // add an offset to the looper when creating a shadow layer
- info.fOffset.set(offset.fX, offset.fY);
-
- SkPaint* p = looper->addLayer(info);
- *p = shadowPaint;
-
- // Currently, only GraphicsContexts associated with the
- // HTMLCanvasElement have shadows ignore transforms set. This
- // allows us to distinguish between CSS and Canvas shadows which
- // have different rendering specifications.
- if (gc->shadowsIgnoreTransforms()) {
- SkColorFilter* cf = SkColorFilter::CreateModeFilter(p->getColor(),
- SkXfermode::kSrcIn_Mode);
- p->setColorFilter(cf)->unref();
- } else { // in CSS
- p->setShader(NULL);
- }
-
- if (hasStrokePaint && !hasFillPaint) {
- // stroke the shadow if we have stroke but no fill
- p->setStyle(SkPaint::kStroke_Style);
- p->setStrokeWidth(strokeWidth);
- }
- updateForFont(p, font);
- }
- }
- } else if (mode & TextModeFill) {
- (void)setupFill(paint, gc, font);
- } else if (mode & TextModeStroke) {
- (void)setupStroke(paint, gc, font);
- } else {
- return false;
- }
- return true;
-}
-
-bool Font::canReturnFallbackFontsForComplexText()
-{
- return false;
-}
-
-bool Font::canExpandAroundIdeographsInComplexText()
-{
- return false;
-}
-
-void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font,
- const GlyphBuffer& glyphBuffer, int from, int numGlyphs,
- const FloatPoint& point) const
-{
- // compile-time assert
- SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t));
-
- SkPaint paint;
- if (!setupForText(&paint, gc, font)) {
- return;
- }
-
- SkScalar x = SkFloatToScalar(point.x());
- SkScalar y = SkFloatToScalar(point.y());
- const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from);
- const GlyphBufferAdvance* adv = glyphBuffer.advances(from);
- SkAutoSTMalloc<32, SkPoint> storage(numGlyphs), storage2(numGlyphs), storage3(numGlyphs);
- SkPoint* pos = storage.get();
-
- SkCanvas* canvas = gc->platformContext()->recordingCanvas();
-
- /* We need an array of [x,y,x,y,x,y,...], but webkit is giving us
- point.xy + [width, height, width, height, ...], so we have to convert
- */
-
- if (font->platformData().orientation() == Vertical)
- y += SkFloatToScalar(font->fontMetrics().floatAscent(IdeographicBaseline) - font->fontMetrics().floatAscent());
-
- if (EmojiFont::IsAvailable()) {
- // set filtering, to make scaled images look nice(r)
- paint.setFilterBitmap(true);
-
- int localIndex = 0;
- int localCount = 0;
- for (int i = 0; i < numGlyphs; i++) {
- if (EmojiFont::IsEmojiGlyph(glyphs[i])) {
- if (localCount)
- canvas->drawPosText(&glyphs[localIndex],
- localCount * sizeof(uint16_t),
- &pos[localIndex], paint);
- EmojiFont::Draw(canvas, glyphs[i], x, y, paint);
- // reset local index/count track for "real" glyphs
- localCount = 0;
- localIndex = i + 1;
- } else {
- pos[i].set(x, y);
- localCount += 1;
- }
- x += SkFloatToScalar(adv[i].width());
- y += SkFloatToScalar(adv[i].height());
- }
- // draw the last run of glyphs (if any)
- if (localCount)
- canvas->drawPosText(&glyphs[localIndex],
- localCount * sizeof(uint16_t),
- &pos[localIndex], paint);
- } else {
- for (int i = 0; i < numGlyphs; i++) {
- pos[i].set(x, y);
- y += SkFloatToScalar(adv[i].height());
- x += SkFloatToScalar(adv[i].width());
- }
-
- if (font->platformData().orientation() == Vertical) {
- canvas->save();
- canvas->rotate(-90);
- SkMatrix rotator;
- rotator.reset();
- rotator.setRotate(90);
- rotator.mapPoints(pos, numGlyphs);
- }
-
- canvas->drawPosText(glyphs, numGlyphs * sizeof(uint16_t), pos, paint);
-
- if (font->platformData().orientation() == Vertical)
- canvas->restore();
- }
- gc->platformContext()->endRecording();
-}
-
-void Font::drawEmphasisMarksForComplexText(WebCore::GraphicsContext*, WebCore::TextRun const&, WTF::AtomicString const&, WebCore::FloatPoint const&, int, int) const
-{
- notImplemented();
-}
-
-#ifndef SUPPORT_COMPLEX_SCRIPTS
-
-FloatRect Font::selectionRectForComplexText(const TextRun& run,
- const FloatPoint& point, int h, int, int) const
-{
- SkPaint paint;
- SkScalar width, left;
- SkPaint::FontMetrics metrics;
-
- primaryFont()->platformData().setupPaint(&paint);
-
- width = paint.measureText(run.characters(), run.length() << 1);
- SkScalar spacing = paint.getFontMetrics(&metrics);
-
- return FloatRect(point.x(),
- point.y(),
- roundf(SkScalarToFloat(width)),
- roundf(SkScalarToFloat(spacing)));
-}
-
-void Font::drawComplexText(GraphicsContext* gc, TextRun const& run,
- FloatPoint const& point, int, int) const
-{
- SkCanvas* canvas = gc->platformContext()->mCanvas;
- SkPaint paint;
-
- if (!setupForText(&paint, gc, primaryFont())) {
- return;
- }
-
- // go to chars, instead of glyphs, which was set by setupForText()
- paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
-
- canvas->drawText(run.characters(), run.length() << 1,
- SkFloatToScalar(point.x()), SkFloatToScalar(point.y()),
- paint);
-}
-
-float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>*, GlyphOverflow*) const
-{
- SkPaint paint;
-
- primaryFont()->platformData().setupPaint(&paint);
-
-//printf("--------- complext measure %d chars\n", run.to() - run.from());
-
- SkScalar width = paint.measureText(run.characters(), run.length() << 1);
- return SkScalarToFloat(width);
-}
-
-int Font::offsetForPositionForComplexText(const TextRun& run, float x,
- bool includePartialGlyphs) const
-{
- SkPaint paint;
- int count = run.length();
- SkAutoSTMalloc<64, SkScalar> storage(count);
- SkScalar* widths = storage.get();
-
- primaryFont()->platformData().setupPaint(&paint);
-
- count = paint.getTextWidths(run.characters(), count << 1, widths);
-
- if (count > 0)
- {
- SkScalar pos = 0;
- for (int i = 0; i < count; i++)
- {
- if (x < SkScalarRound(pos + SkScalarHalf(widths[i])))
- return i;
- pos += widths[i];
- }
- }
- return count;
-}
-
-#else
-
-// TODO Should we remove the multilayer support?
-// If yes. remove isCanvasMultiLayered() and adjustTextRenderMode().
-static bool isCanvasMultiLayered(SkCanvas* canvas)
-{
- SkCanvas::LayerIter layerIterator(canvas, false);
- layerIterator.next();
- return !layerIterator.done();
-}
-
-static void adjustTextRenderMode(SkPaint* paint, bool isCanvasMultiLayered)
-{
- // Our layers only have a single alpha channel. This means that subpixel
- // rendered text cannot be compositied correctly when the layer is
- // collapsed. Therefore, subpixel text is disabled when we are drawing
- // onto a layer.
- if (isCanvasMultiLayered)
- paint->setLCDRenderText(false);
-}
-
-// Harfbuzz uses 26.6 fixed point values for pixel offsets. However, we don't
-// handle subpixel positioning so this function is used to truncate Harfbuzz
-// values to a number of pixels.
-static int truncateFixedPointToInteger(HB_Fixed value)
-{
- return value >> 6;
-}
-
-// TextRunWalker walks a TextRun and presents each script run in sequence. A
-// TextRun is a sequence of code-points with the same embedding level (i.e. they
-// are all left-to-right or right-to-left). A script run is a subsequence where
-// all the characters have the same script (e.g. Arabic, Thai etc). Shaping is
-// only ever done with script runs since the shapers only know how to deal with
-// a single script.
-//
-// After creating it, the script runs are either iterated backwards or forwards.
-// It defaults to backwards for RTL and forwards otherwise (which matches the
-// presentation order), however you can set it with |setBackwardsIteration|.
-//
-// Once you have setup the object, call |nextScriptRun| to get the first script
-// run. This will return false when the iteration is complete. At any time you
-// can call |reset| to start over again.
-class TextRunWalker {
-public:
- TextRunWalker(const TextRun&, int, int, const Font*);
- ~TextRunWalker();
-
- bool isWordBreak(unsigned, bool);
- // setPadding sets a number of pixels to be distributed across the TextRun.
- // WebKit uses this to justify text.
- void setPadding(int);
- void reset();
- void setBackwardsIteration(bool);
- // Advance to the next script run, returning false when the end of the
- // TextRun has been reached.
- bool nextScriptRun();
- float widthOfFullRun();
-
- // setWordSpacingAdjustment sets a delta (in pixels) which is applied at
- // each word break in the TextRun.
- void setWordSpacingAdjustment(int wordSpacingAdjustment)
- {
- m_wordSpacingAdjustment = wordSpacingAdjustment;
- }
-
- // setLetterSpacingAdjustment sets an additional number of pixels that is
- // added to the advance after each output cluster. This matches the behaviour
- // of WidthIterator::advance.
- //
- // (NOTE: currently does nothing because I don't know how to get the
- // cluster information from Harfbuzz.)
- void setLetterSpacingAdjustment(int letterSpacingAdjustment)
- {
- m_letterSpacing = letterSpacingAdjustment;
- }
-
- // setWordAndLetterSpacing calls setWordSpacingAdjustment() and
- // setLetterSpacingAdjustment() to override m_wordSpacingAdjustment
- // and m_letterSpacing.
- void setWordAndLetterSpacing(int wordSpacingAdjustment, int letterSpacingAdjustment);
-
- // Set the x offset for the next script run. This affects the values in
- // |xPositions|
- void setXOffsetToZero() { m_offsetX = 0; }
- bool rtl() const { return m_run.rtl(); }
- const uint16_t* glyphs() const { return m_glyphs16; }
-
- // Return the length of the array returned by |glyphs|
- unsigned length() const { return m_item.num_glyphs; }
-
- // Return the offset for each of the glyphs. Note that this is translated
- // by the current x offset and that the x offset is updated for each script
- // run.
- const SkPoint* positions() const { return m_positions; }
-
- // Get the advances (widths) for each glyph.
- const HB_Fixed* advances() const { return m_item.advances; }
-
- // Return the width (in px) of the current script run.
- unsigned width() const { return m_pixelWidth; }
-
- // Return the cluster log for the current script run. For example:
- // script run: f i a n c é (fi gets ligatured)
- // log clutrs: 0 0 1 2 3 4
- // So, for each input code point, the log tells you which output glyph was
- // generated for it.
- const unsigned short* logClusters() const { return m_item.log_clusters; }
-
- // return the number of code points in the current script run
- unsigned numCodePoints() const { return m_numCodePoints; }
-
- const FontPlatformData* fontPlatformDataForScriptRun() {
- return reinterpret_cast<FontPlatformData*>(m_item.font->userData);
- }
-
-private:
- enum CustomScript {
- Bengali,
- Devanagari,
- Hebrew,
- HebrewBold,
- Naskh,
- Tamil,
- Thai,
- NUM_SCRIPTS
- };
-
- static const char* paths[NUM_SCRIPTS];
-
- void setupFontForScriptRun();
- const FontPlatformData* setupComplexFont(CustomScript script,
- const FontPlatformData& platformData);
- HB_FontRec* allocHarfbuzzFont();
- void deleteGlyphArrays();
- void createGlyphArrays(int);
- void resetGlyphArrays();
- void shapeGlyphs();
- void setGlyphPositions(bool);
-
- static void normalizeSpacesAndMirrorChars(const UChar* source, bool rtl,
- UChar* destination, int length);
- static const TextRun& getNormalizedTextRun(const TextRun& originalRun,
- OwnPtr<TextRun>& normalizedRun, OwnArrayPtr<UChar>& normalizedBuffer);
-
- // This matches the logic in RenderBlock::findNextLineBreak
- static bool isCodepointSpace(HB_UChar16 c) { return c == ' ' || c == '\t'; }
-
- const Font* const m_font;
- HB_ShaperItem m_item;
- uint16_t* m_glyphs16; // A vector of 16-bit glyph ids.
- SkPoint* m_positions; // A vector of positions for each glyph.
- ssize_t m_indexOfNextScriptRun; // Indexes the script run in |m_run|.
- const int m_startingX; // Offset in pixels of the first script run.
- const int m_startingY; // Offset in pixels of the first script run.
- int m_offsetX; // Offset in pixels to the start of the next script run.
- unsigned m_pixelWidth; // Width (in px) of the current script run.
- unsigned m_numCodePoints; // Code points in current script run.
- unsigned m_glyphsArrayCapacity; // Current size of all the Harfbuzz arrays.
-
- OwnPtr<TextRun> m_normalizedRun;
- OwnArrayPtr<UChar> m_normalizedBuffer; // A buffer for normalized run.
- const TextRun& m_run;
- bool m_iterateBackwards;
- int m_wordSpacingAdjustment; // delta adjustment (pixels) for each word break.
- float m_padding; // pixels to be distributed over the line at word breaks.
- float m_padPerWordBreak; // pixels to be added to each word break.
- float m_padError; // |m_padPerWordBreak| might have a fractional component.
- // Since we only add a whole number of padding pixels at
- // each word break we accumulate error. This is the
- // number of pixels that we are behind so far.
- unsigned m_letterSpacing; // pixels to be added after each glyph.
-};
-
-
-// Indexed using enum CustomScript
-const char* TextRunWalker::paths[] = {
- "/system/fonts/Lohit-Bengali.ttf",
- "/system/fonts/Lohit-Devanagari.ttf",
- "/system/fonts/DroidSansHebrew-Regular.ttf",
- "/system/fonts/DroidSansHebrew-Bold.ttf",
- "/system/fonts/DroidNaskh-Regular.ttf",
- "/system/fonts/Lohit-Tamil.ttf",
- "/system/fonts/DroidSansThai.ttf"
-};
-
-TextRunWalker::TextRunWalker(const TextRun& run, int startingX, int startingY, const Font* font)
- : m_font(font)
- , m_startingX(startingX)
- , m_startingY(startingY)
- , m_offsetX(m_startingX)
- , m_run(getNormalizedTextRun(run, m_normalizedRun, m_normalizedBuffer))
- , m_iterateBackwards(m_run.rtl())
- , m_wordSpacingAdjustment(0)
- , m_padding(0)
- , m_padPerWordBreak(0)
- , m_padError(0)
- , m_letterSpacing(0)
-{
- // Do not use |run| inside this constructor. Use |m_run| instead.
-
- memset(&m_item, 0, sizeof(m_item));
- // We cannot know, ahead of time, how many glyphs a given script run
- // will produce. We take a guess that script runs will not produce more
- // than twice as many glyphs as there are code points plus a bit of
- // padding and fallback if we find that we are wrong.
- createGlyphArrays((m_run.length() + 2) * 2);
-
- m_item.log_clusters = new unsigned short[m_run.length()];
-
- m_item.face = 0;
- m_item.font = allocHarfbuzzFont();
-
- m_item.item.bidiLevel = m_run.rtl();
-
- m_item.string = m_run.characters();
- m_item.stringLength = m_run.length();
-
- reset();
-}
-
-TextRunWalker::~TextRunWalker()
-{
- fastFree(m_item.font);
- deleteGlyphArrays();
- delete[] m_item.log_clusters;
-}
-
-bool TextRunWalker::isWordBreak(unsigned index, bool isRTL)
-{
- if (!isRTL)
- return index && isCodepointSpace(m_item.string[index])
- && !isCodepointSpace(m_item.string[index - 1]);
- return index != m_item.stringLength - 1 && isCodepointSpace(m_item.string[index])
- && !isCodepointSpace(m_item.string[index + 1]);
-}
-
-// setPadding sets a number of pixels to be distributed across the TextRun.
-// WebKit uses this to justify text.
-void TextRunWalker::setPadding(int padding)
-{
- m_padding = padding;
- if (!m_padding)
- return;
-
- // If we have padding to distribute, then we try to give an equal
- // amount to each space. The last space gets the smaller amount, if
- // any.
- unsigned numWordBreaks = 0;
- bool isRTL = m_iterateBackwards;
-
- for (unsigned i = 0; i < m_item.stringLength; i++) {
- if (isWordBreak(i, isRTL))
- numWordBreaks++;
- }
-
- if (numWordBreaks)
- m_padPerWordBreak = m_padding / numWordBreaks;
- else
- m_padPerWordBreak = 0;
-}
-
-void TextRunWalker::reset()
-{
- if (m_iterateBackwards)
- m_indexOfNextScriptRun = m_run.length() - 1;
- else
- m_indexOfNextScriptRun = 0;
- m_offsetX = m_startingX;
-}
-
-void TextRunWalker::setBackwardsIteration(bool isBackwards)
-{
- m_iterateBackwards = isBackwards;
- reset();
-}
-
-// Advance to the next script run, returning false when the end of the
-// TextRun has been reached.
-bool TextRunWalker::nextScriptRun()
-{
- if (m_iterateBackwards) {
- // In right-to-left mode we need to render the shaped glyph backwards and
- // also render the script runs themselves backwards. So given a TextRun:
- // AAAAAAACTTTTTTT (A = Arabic, C = Common, T = Thai)
- // we render:
- // TTTTTTCAAAAAAA
- // (and the glyphs in each A, C and T section are backwards too)
- if (!hb_utf16_script_run_prev(&m_numCodePoints, &m_item.item, m_run.characters(),
- m_run.length(), &m_indexOfNextScriptRun))
- return false;
- } else {
- if (!hb_utf16_script_run_next(&m_numCodePoints, &m_item.item, m_run.characters(),
- m_run.length(), &m_indexOfNextScriptRun))
- return false;
-
- // It is actually wrong to consider script runs at all in this code.
- // Other WebKit code (e.g. Mac) segments complex text just by finding
- // the longest span of text covered by a single font.
- // But we currently need to call hb_utf16_script_run_next anyway to fill
- // in the harfbuzz data structures to e.g. pick the correct script's shaper.
- // So we allow that to run first, then do a second pass over the range it
- // found and take the largest subregion that stays within a single font.
- const FontData* glyphData = m_font->glyphDataForCharacter(
- m_item.string[m_item.item.pos], false).fontData;
- unsigned endOfRun;
- for (endOfRun = 1; endOfRun < m_item.item.length; ++endOfRun) {
- const FontData* nextGlyphData = m_font->glyphDataForCharacter(
- m_item.string[m_item.item.pos + endOfRun], false).fontData;
- if (nextGlyphData != glyphData)
- break;
- }
- m_item.item.length = endOfRun;
- m_indexOfNextScriptRun = m_item.item.pos + endOfRun;
- }
-
- setupFontForScriptRun();
- shapeGlyphs();
- setGlyphPositions(rtl());
-
- return true;
-}
-
-float TextRunWalker::widthOfFullRun()
-{
- float widthSum = 0;
- while (nextScriptRun())
- widthSum += width();
-
- return widthSum;
-}
-
-void TextRunWalker::setWordAndLetterSpacing(int wordSpacingAdjustment,
- int letterSpacingAdjustment)
-{
- setWordSpacingAdjustment(wordSpacingAdjustment);
- setLetterSpacingAdjustment(letterSpacingAdjustment);
-}
-
-const FontPlatformData* TextRunWalker::setupComplexFont(
- CustomScript script,
- const FontPlatformData& platformData)
-{
- static FallbackHash fallbackPlatformData;
-
- FallbackFontKey key(script, platformData.size());
- FontPlatformData* newPlatformData = 0;
-
- if (!fallbackPlatformData.contains(key)) {
- SkTypeface* typeface = SkTypeface::CreateFromFile(paths[script]);
- newPlatformData = new FontPlatformData(platformData, typeface);
- SkSafeUnref(typeface);
- fallbackPlatformData.set(key, newPlatformData);
- }
-
- if (!newPlatformData)
- newPlatformData = fallbackPlatformData.get(key);
-
- // If we couldn't allocate a new FontPlatformData, revert to the one passed
- return newPlatformData ? newPlatformData : &platformData;
-}
-
-void TextRunWalker::setupFontForScriptRun()
-{
- const FontData* fontData = m_font->glyphDataForCharacter(m_run[0], false).fontData;
- const FontPlatformData& platformData =
- fontData->fontDataForCharacter(' ')->platformData();
- const FontPlatformData* complexPlatformData = &platformData;
-
- switch (m_item.item.script) {
- case HB_Script_Bengali:
- complexPlatformData = setupComplexFont(Bengali, platformData);
- break;
- case HB_Script_Devanagari:
- complexPlatformData = setupComplexFont(Devanagari, platformData);
- break;
- case HB_Script_Hebrew:
- switch (platformData.typeface()->style()) {
- case SkTypeface::kBold:
- case SkTypeface::kBoldItalic:
- complexPlatformData = setupComplexFont(HebrewBold, platformData);
- break;
- case SkTypeface::kNormal:
- case SkTypeface::kItalic:
- default:
- complexPlatformData = setupComplexFont(Hebrew, platformData);
- break;
- }
- break;
- case HB_Script_Arabic:
- complexPlatformData = setupComplexFont(Naskh, platformData);
- break;
- case HB_Script_Tamil:
- complexPlatformData = setupComplexFont(Tamil, platformData);
- break;
- case HB_Script_Thai:
- complexPlatformData = setupComplexFont(Thai, platformData);
- break;
- default:
- // HB_Script_Common; includes Ethiopic
- complexPlatformData = &platformData;
- break;
- }
- m_item.face = complexPlatformData->harfbuzzFace();
- m_item.font->userData = const_cast<FontPlatformData*>(complexPlatformData);
-
- int size = complexPlatformData->size();
- m_item.font->x_ppem = size;
- m_item.font->y_ppem = size;
- // x_ and y_scale are the conversion factors from font design space (fEmSize) to 1/64th of device pixels in 16.16 format.
- const int devicePixelFraction = 64;
- const int multiplyFor16Dot16 = 1 << 16;
- int scale = devicePixelFraction * size * multiplyFor16Dot16 / complexPlatformData->emSizeInFontUnits();
- m_item.font->x_scale = scale;
- m_item.font->y_scale = scale;
-}
-
-HB_FontRec* TextRunWalker::allocHarfbuzzFont()
-{
- HB_FontRec* font = reinterpret_cast<HB_FontRec*>(fastMalloc(sizeof(HB_FontRec)));
- memset(font, 0, sizeof(HB_FontRec));
- font->klass = &harfbuzzSkiaClass;
- font->userData = 0;
-
- return font;
-}
-
-void TextRunWalker::deleteGlyphArrays()
-{
- delete[] m_item.glyphs;
- delete[] m_item.attributes;
- delete[] m_item.advances;
- delete[] m_item.offsets;
- delete[] m_glyphs16;
- delete[] m_positions;
-}
-
-void TextRunWalker::createGlyphArrays(int size)
-{
- m_item.glyphs = new HB_Glyph[size];
- m_item.attributes = new HB_GlyphAttributes[size];
- m_item.advances = new HB_Fixed[size];
- m_item.offsets = new HB_FixedPoint[size];
-
- m_glyphs16 = new uint16_t[size];
- m_positions = new SkPoint[size];
-
- m_item.num_glyphs = size;
- m_glyphsArrayCapacity = size; // Save the GlyphArrays size.
-}
-
-void TextRunWalker::resetGlyphArrays()
-{
- int size = m_item.num_glyphs;
- // All the types here don't have pointers. It is safe to reset to
- // zero unless Harfbuzz breaks the compatibility in the future.
- memset(m_item.glyphs, 0, size * sizeof(m_item.glyphs[0]));
- memset(m_item.attributes, 0, size * sizeof(m_item.attributes[0]));
- memset(m_item.advances, 0, size * sizeof(m_item.advances[0]));
- memset(m_item.offsets, 0, size * sizeof(m_item.offsets[0]));
- memset(m_glyphs16, 0, size * sizeof(m_glyphs16[0]));
- memset(m_positions, 0, size * sizeof(m_positions[0]));
-}
-
-void TextRunWalker::shapeGlyphs()
-{
- // HB_ShapeItem() resets m_item.num_glyphs. If the previous call to
- // HB_ShapeItem() used less space than was available, the capacity of
- // the array may be larger than the current value of m_item.num_glyphs.
- // So, we need to reset the num_glyphs to the capacity of the array.
- m_item.num_glyphs = m_glyphsArrayCapacity;
- resetGlyphArrays();
- while (!HB_ShapeItem(&m_item)) {
- // We overflowed our arrays. Resize and retry.
- // HB_ShapeItem fills in m_item.num_glyphs with the needed size.
- deleteGlyphArrays();
- createGlyphArrays(m_item.num_glyphs << 1);
- resetGlyphArrays();
- }
-}
-
-void TextRunWalker::setGlyphPositions(bool isRTL)
-{
- int position = 0;
- // logClustersIndex indexes logClusters for the first (or last when
- // RTL) codepoint of the current glyph. Each time we advance a glyph,
- // we skip over all the codepoints that contributed to the current
- // glyph.
- unsigned logClustersIndex = isRTL && m_item.num_glyphs ? m_item.num_glyphs - 1 : 0;
-
- for (unsigned iter = 0; iter < m_item.num_glyphs; ++iter) {
- // Glyphs are stored in logical order, but for layout purposes we
- // always go left to right.
- int i = isRTL ? m_item.num_glyphs - iter - 1 : iter;
-
- m_glyphs16[i] = m_item.glyphs[i];
- int offsetX = truncateFixedPointToInteger(m_item.offsets[i].x);
- int offsetY = truncateFixedPointToInteger(m_item.offsets[i].y);
- m_positions[i].set(SkIntToScalar(m_offsetX + position) + offsetX, m_startingY + offsetY);
-
- int advance = truncateFixedPointToInteger(m_item.advances[i]);
- // The first half of the conjunction works around the case where
- // output glyphs aren't associated with any codepoints by the
- // clusters log.
- if (logClustersIndex < m_item.item.length
- && isWordBreak(m_item.item.pos + logClustersIndex, isRTL)) {
- advance += m_wordSpacingAdjustment;
-
- if (m_padding > 0) {
- int toPad = roundf(m_padPerWordBreak + m_padError);
- m_padError += m_padPerWordBreak - toPad;
-
- if (m_padding < toPad)
- toPad = m_padding;
- m_padding -= toPad;
- advance += toPad;
- }
- }
-
- // ZeroWidthJoiners and ZeroWidthNonJoiners should be stripped by
- // Harfbuzz, but aren't. Check for zwj and zwnj and replace with a
- // zero width space. We get the glyph data for space instead of
- // zeroWidthSpace because the latter was seen to render with an
- // unexpected code point (the symbol for a cloud). Since the standard
- // space is in page zero and since we've also confirmed that there is
- // no advance on this glyph, that should be ok.
- if (0 == m_item.advances[i]) {
- const HB_UChar16 c = m_item.string[m_item.item.pos + logClustersIndex];
- if ((c == zeroWidthJoiner) || (c == zeroWidthNonJoiner)) {
- static Glyph spaceGlyph = m_font->glyphDataForCharacter(space, false).glyph;
- m_glyphs16[i] = spaceGlyph;
- }
- }
-
- // TODO We would like to add m_letterSpacing after each cluster, but I
- // don't know where the cluster information is. This is typically
- // fine for Roman languages, but breaks more complex languages
- // terribly.
- // advance += m_letterSpacing;
-
- if (isRTL) {
- while (logClustersIndex > 0 && logClusters()[logClustersIndex] == i)
- logClustersIndex--;
- } else {
- while (logClustersIndex < m_item.item.length
- && logClusters()[logClustersIndex] == i)
- logClustersIndex++;
- }
-
- position += advance;
- }
-
- m_pixelWidth = position;
- m_offsetX += m_pixelWidth;
-}
-
-void TextRunWalker::normalizeSpacesAndMirrorChars(const UChar* source, bool rtl,
- UChar* destination, int length)
-{
- int position = 0;
- bool error = false;
- // Iterate characters in source and mirror character if needed.
- while (position < length) {
- UChar32 character;
- int nextPosition = position;
- U16_NEXT(source, nextPosition, length, character);
-
- if (Font::treatAsSpace(character))
- character = space;
- else if (Font::treatAsZeroWidthSpaceInComplexScript(character))
- character = zeroWidthSpace;
- else if (rtl)
- character = u_charMirror(character);
-
- U16_APPEND(destination, position, length, character, error);
- ASSERT(!error);
- position = nextPosition;
- }
-}
-
-const TextRun& TextRunWalker::getNormalizedTextRun(const TextRun& originalRun,
- OwnPtr<TextRun>& normalizedRun, OwnArrayPtr<UChar>& normalizedBuffer)
-{
- // Normalize the text run in three ways:
- // 1) Convert the |originalRun| to NFC normalized form if combining diacritical marks
- // (U+0300..) are used in the run. This conversion is necessary since most OpenType
- // fonts (e.g., Arial) don't have substitution rules for the diacritical marks in
- // their GSUB tables.
- //
- // Note that we don't use the icu::Normalizer::isNormalized(UNORM_NFC) API here since
- // the API returns FALSE (= not normalized) for complex runs that don't require NFC
- // normalization (e.g., Arabic text). Unless the run contains the diacritical marks,
- // Harfbuzz will do the same thing for us using the GSUB table.
- // 2) Convert spacing characters into plain spaces, as some fonts will provide glyphs
- // for characters like '\n' otherwise.
- // 3) Convert mirrored characters such as parenthesis for rtl text.
-
- // Convert to NFC form if the text has diacritical marks.
- icu::UnicodeString normalizedString;
- UErrorCode error = U_ZERO_ERROR;
-
- for (int16_t i = 0; i < originalRun.length(); ++i) {
- UChar ch = originalRun[i];
- if (::ublock_getCode(ch) == UBLOCK_COMBINING_DIACRITICAL_MARKS) {
- icu::Normalizer::normalize(icu::UnicodeString(originalRun.characters(),
- originalRun.length()), UNORM_NFC, 0 /* no options */,
- normalizedString, error);
- if (U_FAILURE(error))
- return originalRun;
- break;
- }
- }
-
- // Normalize space and mirror parenthesis for rtl text.
- int normalizedBufferLength;
- const UChar* sourceText;
- if (normalizedString.isEmpty()) {
- normalizedBufferLength = originalRun.length();
- sourceText = originalRun.characters();
- } else {
- normalizedBufferLength = normalizedString.length();
- sourceText = normalizedString.getBuffer();
- }
-
- normalizedBuffer = adoptArrayPtr(new UChar[normalizedBufferLength + 1]);
-
- normalizeSpacesAndMirrorChars(sourceText, originalRun.rtl(), normalizedBuffer.get(),
- normalizedBufferLength);
-
- normalizedRun = adoptPtr(new TextRun(originalRun));
- normalizedRun->setText(normalizedBuffer.get(), normalizedBufferLength);
- return *normalizedRun;
-}
-
-FloatRect Font::selectionRectForComplexText(const TextRun& run,
- const FloatPoint& point, int height, int from, int to) const
-{
-
- int fromX = -1, toX = -1, fromAdvance = -1, toAdvance = -1;
- TextRunWalker walker(run, 0, 0, this);
- walker.setWordAndLetterSpacing(wordSpacing(), letterSpacing());
-
- // Base will point to the x offset for the current script run. Note that, in
- // the LTR case, width will be 0.
- int base = walker.rtl() ? walker.widthOfFullRun() : 0;
- const int leftEdge = base;
-
- // We want to enumerate the script runs in code point order in the following
- // code. This call also resets |walker|.
- walker.setBackwardsIteration(false);
-
- while (walker.nextScriptRun() && (fromX == -1 || toX == -1)) {
- // TextRunWalker will helpfully accumulate the x offsets for different
- // script runs for us. For this code, however, we always want the x
- // offsets to start from zero so we call this before each script run.
- walker.setXOffsetToZero();
-
- if (walker.rtl())
- base -= walker.width();
-
- int numCodePoints = static_cast<int>(walker.numCodePoints());
- if (fromX == -1 && from < numCodePoints) {
- // |from| is within this script run. So we index the clusters log to
- // find which glyph this code-point contributed to and find its x
- // position.
- int glyph = walker.logClusters()[from];
- fromX = base + walker.positions()[glyph].x();
- fromAdvance = walker.advances()[glyph];
- } else if (!walker.rtl())
- from -= numCodePoints;
-
- if (toX == -1 && to < numCodePoints) {
- int glyph = walker.logClusters()[to];
- toX = base + walker.positions()[glyph].x();
- toAdvance = walker.advances()[glyph];
- } else if (!walker.rtl())
- to -= numCodePoints;
-
- if (!walker.rtl())
- base += walker.width();
- }
-
- // The position in question might be just after the text.
- const int rightEdge = base;
- if (fromX == -1 && !from)
- fromX = leftEdge;
- else if (walker.rtl())
- fromX += truncateFixedPointToInteger(fromAdvance);
-
- if (toX == -1 && !to)
- toX = rightEdge;
-
- ASSERT(fromX != -1 && toX != -1);
-
- if (fromX < toX)
- return FloatRect(point.x() + fromX, point.y(), toX - fromX, height);
-
- return FloatRect(point.x() + toX, point.y(), fromX - toX, height);
-}
-
-void Font::drawComplexText(GraphicsContext* gc, TextRun const& run,
- FloatPoint const& point, int, int) const
-{
- if (!run.length())
- return;
-
- int mode = gc->textDrawingMode();
- bool fill = mode & TextModeFill;
- bool stroke = mode & TextModeStroke;
- if (!fill && !stroke)
- return;
-
- SkPaint fillPaint, strokePaint;
- if (fill)
- setupFill(&fillPaint, gc, primaryFont());
- if (stroke)
- setupStroke(&strokePaint, gc, primaryFont());
-
- SkCanvas* canvas = gc->platformContext()->recordingCanvas();
-
- bool haveMultipleLayers = isCanvasMultiLayered(canvas);
- TextRunWalker walker(run, point.x(), point.y(), this);
- walker.setWordAndLetterSpacing(wordSpacing(), letterSpacing());
- walker.setPadding(run.expansion());
-
- while (walker.nextScriptRun()) {
- if (fill) {
- walker.fontPlatformDataForScriptRun()->setupPaint(&fillPaint);
- adjustTextRenderMode(&fillPaint, haveMultipleLayers);
- canvas->drawPosText(walker.glyphs(), walker.length() << 1,
- walker.positions(), fillPaint);
- }
- if (stroke) {
- walker.fontPlatformDataForScriptRun()->setupPaint(&strokePaint);
- adjustTextRenderMode(&strokePaint, haveMultipleLayers);
- canvas->drawPosText(walker.glyphs(), walker.length() << 1,
- walker.positions(), strokePaint);
- }
- }
-
- gc->platformContext()->endRecording();
-}
-
-float Font::floatWidthForComplexText(const TextRun& run,
- HashSet<const SimpleFontData*>*, GlyphOverflow*) const
-{
- TextRunWalker walker(run, 0, 0, this);
- walker.setWordAndLetterSpacing(wordSpacing(), letterSpacing());
- return walker.widthOfFullRun();
-}
-
-static int glyphIndexForXPositionInScriptRun(const TextRunWalker& walker, int x)
-{
- const HB_Fixed* advances = walker.advances();
- int glyphIndex;
- if (walker.rtl()) {
- for (glyphIndex = walker.length() - 1; glyphIndex >= 0; --glyphIndex) {
- if (x < truncateFixedPointToInteger(advances[glyphIndex]))
- break;
- x -= truncateFixedPointToInteger(advances[glyphIndex]);
- }
- } else {
- for (glyphIndex = 0; glyphIndex < static_cast<int>(walker.length());
- ++glyphIndex) {
- if (x < truncateFixedPointToInteger(advances[glyphIndex]))
- break;
- x -= truncateFixedPointToInteger(advances[glyphIndex]);
- }
- }
-
- return glyphIndex;
-}
-
-int Font::offsetForPositionForComplexText(const TextRun& run, float x,
- bool includePartialGlyphs) const
-{
- // (Mac code ignores includePartialGlyphs, and they don't know what it's
- // supposed to do, so we just ignore it as well.)
- TextRunWalker walker(run, 0, 0, this);
- walker.setWordAndLetterSpacing(wordSpacing(), letterSpacing());
-
- // If this is RTL text, the first glyph from the left is actually the last
- // code point. So we need to know how many code points there are total in
- // order to subtract. This is different from the length of the TextRun
- // because UTF-16 surrogate pairs are a single code point, but 32-bits long.
- // In LTR we leave this as 0 so that we get the correct value for
- // |basePosition|, below.
- unsigned totalCodePoints = 0;
- if (walker.rtl()) {
- ssize_t offset = 0;
- while (offset < run.length()) {
- utf16_to_code_point(run.characters(), run.length(), &offset);
- totalCodePoints++;
- }
- }
-
- unsigned basePosition = totalCodePoints;
-
- // For RTL:
- // code-point order: abcd efg hijkl
- // on screen: lkjih gfe dcba
- // ^ ^
- // | |
- // basePosition--| |
- // totalCodePoints----|
- // Since basePosition is currently the total number of code-points, the
- // first thing we do is decrement it so that it's pointing to the start of
- // the current script-run.
- //
- // For LTR, basePosition is zero so it already points to the start of the
- // first script run.
- while (walker.nextScriptRun()) {
- if (walker.rtl())
- basePosition -= walker.numCodePoints();
-
- if (x >= 0 && x < static_cast<int>(walker.width())) {
- // The x value in question is within this script run. We consider
- // each glyph in presentation order and stop when we find the one
- // covering this position.
- const int glyphIndex = glyphIndexForXPositionInScriptRun(walker, x);
-
- // Now that we have a glyph index, we have to turn that into a
- // code-point index. Because of ligatures, several code-points may
- // have gone into a single glyph. We iterate over the clusters log
- // and find the first code-point which contributed to the glyph.
-
- // Some shapers (i.e. Khmer) will produce cluster logs which report
- // that /no/ code points contributed to certain glyphs. Because of
- // this, we take any code point which contributed to the glyph in
- // question, or any subsequent glyph. If we run off the end, then
- // we take the last code point.
- const unsigned short* log = walker.logClusters();
- for (unsigned j = 0; j < walker.numCodePoints(); ++j) {
- if (log[j] >= glyphIndex)
- return basePosition + j;
- }
-
- return basePosition + walker.numCodePoints() - 1;
- }
-
- x -= walker.width();
-
- if (!walker.rtl())
- basePosition += walker.numCodePoints();
- }
-
- return basePosition;
-}
-#endif
-
-}