diff options
author | Nicolas Roard <nicolasroard@google.com> | 2012-04-06 11:35:50 -0700 |
---|---|---|
committer | Nicolas Roard <nicolasroard@google.com> | 2012-04-06 14:03:59 -0700 |
commit | 2e510fd5b5a30f1315c272d44ae3aa4cba355498 (patch) | |
tree | db3af5f32855d329856f190c3509ae11ae519851 /Source/WebCore/platform/graphics/android/FontAndroid.cpp | |
parent | c88c88907b618e468ec3928b06a3a31d4f99b9c6 (diff) | |
download | external_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.cpp | 1192 |
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 - -} |