Changeset 69972 in webkit


Ignore:
Timestamp:
Oct 18, 2010 10:41:19 AM (14 years ago)
Author:
xji@chromium.org
Message:

2010-10-18 Xiaomei Ji <xji@chromium.org>

Reviewed by David Levin.

Code cleanup: Move most function members in TextRunWorker from inlined.
https://bugs.webkit.org/show_bug.cgi?id=47732

No functionality change, so no tests added.

  • platform/graphics/chromium/FontLinux.cpp: (WebCore::TextRunWalker::setWordSpacingAdjustment): (WebCore::TextRunWalker::setLetterSpacingAdjustment): (WebCore::TextRunWalker::setXOffsetToZero): (WebCore::TextRunWalker::rtl): (WebCore::TextRunWalker::glyphs): (WebCore::TextRunWalker::length): (WebCore::TextRunWalker::xPositions): (WebCore::TextRunWalker::advances): (WebCore::TextRunWalker::width): (WebCore::TextRunWalker::logClusters): (WebCore::TextRunWalker::numCodePoints): (WebCore::TextRunWalker::fontPlatformDataForScriptRun): (WebCore::TextRunWalker::isCodepointSpace): (WebCore::TextRunWalker::TextRunWalker): (WebCore::TextRunWalker::~TextRunWalker): (WebCore::TextRunWalker::isWordBreak): (WebCore::TextRunWalker::setPadding): (WebCore::TextRunWalker::reset): (WebCore::TextRunWalker::setBackwardsIteration): (WebCore::TextRunWalker::nextScriptRun): (WebCore::TextRunWalker::widthOfFullRun): (WebCore::TextRunWalker::getTextRun): (WebCore::TextRunWalker::getNormalizedTextRun): (WebCore::TextRunWalker::setupFontForScriptRun): (WebCore::TextRunWalker::allocHarfbuzzFont): (WebCore::TextRunWalker::deleteGlyphArrays): (WebCore::TextRunWalker::createGlyphArrays): (WebCore::TextRunWalker::resetGlyphArrays): (WebCore::TextRunWalker::shapeGlyphs): (WebCore::TextRunWalker::setGlyphXPositions): (WebCore::TextRunWalker::mirrorCharacters):
Location:
trunk/WebCore
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebCore/ChangeLog

    r69971 r69972  
     12010-10-18  Xiaomei Ji  <xji@chromium.org>
     2
     3        Reviewed by David Levin.
     4
     5        Code cleanup: Move most function members in TextRunWorker from inlined.
     6        https://bugs.webkit.org/show_bug.cgi?id=47732
     7
     8        No functionality change, so no tests added.
     9
     10        * platform/graphics/chromium/FontLinux.cpp:
     11        (WebCore::TextRunWalker::setWordSpacingAdjustment):
     12        (WebCore::TextRunWalker::setLetterSpacingAdjustment):
     13        (WebCore::TextRunWalker::setXOffsetToZero):
     14        (WebCore::TextRunWalker::rtl):
     15        (WebCore::TextRunWalker::glyphs):
     16        (WebCore::TextRunWalker::length):
     17        (WebCore::TextRunWalker::xPositions):
     18        (WebCore::TextRunWalker::advances):
     19        (WebCore::TextRunWalker::width):
     20        (WebCore::TextRunWalker::logClusters):
     21        (WebCore::TextRunWalker::numCodePoints):
     22        (WebCore::TextRunWalker::fontPlatformDataForScriptRun):
     23        (WebCore::TextRunWalker::isCodepointSpace):
     24        (WebCore::TextRunWalker::TextRunWalker):
     25        (WebCore::TextRunWalker::~TextRunWalker):
     26        (WebCore::TextRunWalker::isWordBreak):
     27        (WebCore::TextRunWalker::setPadding):
     28        (WebCore::TextRunWalker::reset):
     29        (WebCore::TextRunWalker::setBackwardsIteration):
     30        (WebCore::TextRunWalker::nextScriptRun):
     31        (WebCore::TextRunWalker::widthOfFullRun):
     32        (WebCore::TextRunWalker::getTextRun):
     33        (WebCore::TextRunWalker::getNormalizedTextRun):
     34        (WebCore::TextRunWalker::setupFontForScriptRun):
     35        (WebCore::TextRunWalker::allocHarfbuzzFont):
     36        (WebCore::TextRunWalker::deleteGlyphArrays):
     37        (WebCore::TextRunWalker::createGlyphArrays):
     38        (WebCore::TextRunWalker::resetGlyphArrays):
     39        (WebCore::TextRunWalker::shapeGlyphs):
     40        (WebCore::TextRunWalker::setGlyphXPositions):
     41        (WebCore::TextRunWalker::mirrorCharacters):
     42
    1432010-10-18  No'am Rosenthal  <noam.rosenthal@nokia.com>
    244
  • trunk/WebCore/platform/graphics/chromium/FontLinux.cpp

    r68660 r69972  
    162162class TextRunWalker {
    163163public:
    164     TextRunWalker(const TextRun& run, unsigned startingX, const Font* font)
    165         : m_font(font)
    166         , m_startingX(startingX)
    167         , m_offsetX(m_startingX)
    168         , m_run(getTextRun(run))
    169         , m_iterateBackwards(m_run.rtl())
    170         , m_wordSpacingAdjustment(0)
    171         , m_padding(0)
    172         , m_padError(0)
    173     {
    174         // Do not use |run| inside this constructor. Use |m_run| instead.
    175 
    176         memset(&m_item, 0, sizeof(m_item));
    177         // We cannot know, ahead of time, how many glyphs a given script run
    178         // will produce. We take a guess that script runs will not produce more
    179         // than twice as many glyphs as there are code points plus a bit of
    180         // padding and fallback if we find that we are wrong.
    181         createGlyphArrays((m_run.length() + 2) * 2);
    182 
    183         m_item.log_clusters = new unsigned short[m_run.length()];
    184 
    185         m_item.face = 0;
    186         m_item.font = allocHarfbuzzFont();
    187 
    188         m_item.item.bidiLevel = m_run.rtl();
    189 
    190         int length = m_run.length();
    191         m_item.stringLength = length;
    192 
    193         if (!m_item.item.bidiLevel)
    194             m_item.string = m_run.characters();
    195         else {
    196             // Assume mirrored character is in the same Unicode multilingual plane as the original one.
    197             UChar* string = new UChar[length];
    198             mirrorCharacters(string, m_run.characters(), length);
    199             m_item.string = string;
    200         }
    201 
    202         reset();
    203     }
    204 
    205     ~TextRunWalker()
    206     {
    207         fastFree(m_item.font);
    208         deleteGlyphArrays();
    209         delete[] m_item.log_clusters;
    210         if (m_item.item.bidiLevel)
    211             delete[] m_item.string;
    212     }
     164    TextRunWalker(const TextRun&, unsigned, const Font*);
     165    ~TextRunWalker();
     166
     167    bool isWordBreak(unsigned, bool);
     168    // setPadding sets a number of pixels to be distributed across the TextRun.
     169    // WebKit uses this to justify text.
     170    void setPadding(int);
     171    void reset();
     172    void setBackwardsIteration(bool);
     173    // Advance to the next script run, returning false when the end of the
     174    // TextRun has been reached.
     175    bool nextScriptRun();
     176    float widthOfFullRun();
    213177
    214178    // setWordSpacingAdjustment sets a delta (in pixels) which is applied at
    215179    // each word break in the TextRun.
    216     void setWordSpacingAdjustment(int wordSpacingAdjustment)
    217     {
    218         m_wordSpacingAdjustment = wordSpacingAdjustment;
    219     }
     180    void setWordSpacingAdjustment(int wordSpacingAdjustment) { m_wordSpacingAdjustment = wordSpacingAdjustment; }
    220181
    221182    // setLetterSpacingAdjustment sets an additional number of pixels that is
     
    225186    // (NOTE: currently does nothing because I don't know how to get the
    226187    // cluster information from Harfbuzz.)
    227     void setLetterSpacingAdjustment(int letterSpacingAdjustment)
    228     {
    229         m_letterSpacing = letterSpacingAdjustment;
    230     }
    231 
    232     bool isWordBreak(unsigned i, bool isRTL)
    233     {
    234         if (!isRTL)
    235             return i && isCodepointSpace(m_item.string[i]) && !isCodepointSpace(m_item.string[i - 1]);
    236         return i != m_item.stringLength - 1 && isCodepointSpace(m_item.string[i]) && !isCodepointSpace(m_item.string[i + 1]);
    237     }
    238 
    239     // setPadding sets a number of pixels to be distributed across the TextRun.
    240     // WebKit uses this to justify text.
    241     void setPadding(int padding)
    242     {
    243         m_padding = padding;
    244         if (!m_padding)
    245             return;
    246 
    247         // If we have padding to distribute, then we try to give an equal
    248         // amount to each space. The last space gets the smaller amount, if
    249         // any.
    250         unsigned numWordBreaks = 0;
    251         bool isRTL = m_iterateBackwards;
    252 
    253         for (unsigned i = 0; i < m_item.stringLength; i++) {
    254             if (isWordBreak(i, isRTL))
    255                 numWordBreaks++;
    256         }
    257 
    258         if (numWordBreaks)
    259             m_padPerWordBreak = m_padding / numWordBreaks;
    260         else
    261             m_padPerWordBreak = 0;
    262     }
    263 
    264     void reset()
    265     {
    266         if (m_iterateBackwards)
    267             m_indexOfNextScriptRun = m_run.length() - 1;
    268         else
    269             m_indexOfNextScriptRun = 0;
    270         m_offsetX = m_startingX;
    271     }
     188    void setLetterSpacingAdjustment(int letterSpacingAdjustment) { m_letterSpacing = letterSpacingAdjustment; }
    272189
    273190    // Set the x offset for the next script run. This affects the values in
    274191    // |xPositions|
    275     void setXOffsetToZero()
    276     {
    277         m_offsetX = 0;
    278     }
    279 
    280     bool rtl() const
    281     {
    282         return m_run.rtl();
    283     }
    284 
    285     void setBackwardsIteration(bool isBackwards)
    286     {
    287         m_iterateBackwards = isBackwards;
    288         reset();
    289     }
    290 
    291     // Advance to the next script run, returning false when the end of the
    292     // TextRun has been reached.
    293     bool nextScriptRun()
    294     {
    295         if (m_iterateBackwards) {
    296             // In right-to-left mode we need to render the shaped glyph backwards and
    297             // also render the script runs themselves backwards. So given a TextRun:
    298             //    AAAAAAACTTTTTTT   (A = Arabic, C = Common, T = Thai)
    299             // we render:
    300             //    TTTTTTCAAAAAAA
    301             // (and the glyphs in each A, C and T section are backwards too)
    302             if (!hb_utf16_script_run_prev(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun))
    303                 return false;
    304         } else {
    305             if (!hb_utf16_script_run_next(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun))
    306                 return false;
    307 
    308             // It is actually wrong to consider script runs at all in this code.
    309             // Other WebKit code (e.g. Mac) segments complex text just by finding
    310             // the longest span of text covered by a single font.
    311             // But we currently need to call hb_utf16_script_run_next anyway to fill
    312             // in the harfbuzz data structures to e.g. pick the correct script's shaper.
    313             // So we allow that to run first, then do a second pass over the range it
    314             // found and take the largest subregion that stays within a single font.
    315             const FontData* glyphData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos], false, false).fontData;
    316             unsigned endOfRun;
    317             for (endOfRun = 1; endOfRun < m_item.item.length; ++endOfRun) {
    318                 const FontData* nextGlyphData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos + endOfRun], false, false).fontData;
    319                 if (nextGlyphData != glyphData)
    320                     break;
    321             }
    322             m_item.item.length = endOfRun;
    323             m_indexOfNextScriptRun = m_item.item.pos + endOfRun;
    324         }
    325 
    326         setupFontForScriptRun();
    327         shapeGlyphs();
    328         setGlyphXPositions(rtl());
    329 
    330         return true;
    331     }
    332 
    333     const uint16_t* glyphs() const
    334     {
    335         return m_glyphs16;
    336     }
     192    void setXOffsetToZero() { m_offsetX = 0; }
     193    bool rtl() const { return m_run.rtl(); }
     194    const uint16_t* glyphs() const { return m_glyphs16; }
    337195
    338196    // Return the length of the array returned by |glyphs|
    339     const unsigned length() const
    340     {
    341         return m_item.num_glyphs;
    342     }
     197    const unsigned length() const { return m_item.num_glyphs; }
    343198
    344199    // Return the x offset for each of the glyphs. Note that this is translated
    345200    // by the current x offset and that the x offset is updated for each script
    346201    // run.
    347     const SkScalar* xPositions() const
    348     {
    349         return m_xPositions;
    350     }
     202    const SkScalar* xPositions() const { return m_xPositions; }
    351203
    352204    // Get the advances (widths) for each glyph.
    353     const HB_Fixed* advances() const
    354     {
    355         return m_item.advances;
    356     }
     205    const HB_Fixed* advances() const { return m_item.advances; }
    357206
    358207    // Return the width (in px) of the current script run.
    359     const unsigned width() const
    360     {
    361         return m_pixelWidth;
    362     }
     208    const unsigned width() const { return m_pixelWidth; }
    363209
    364210    // Return the cluster log for the current script run. For example:
     
    367213    // So, for each input code point, the log tells you which output glyph was
    368214    // generated for it.
    369     const unsigned short* logClusters() const
    370     {
    371         return m_item.log_clusters;
    372     }
     215    const unsigned short* logClusters() const { return m_item.log_clusters; }
    373216
    374217    // return the number of code points in the current script run
    375     const unsigned numCodePoints() const
    376     {
    377         return m_numCodePoints;
    378     }
    379 
    380     const FontPlatformData* fontPlatformDataForScriptRun()
    381     {
    382         return reinterpret_cast<FontPlatformData*>(m_item.font->userData);
    383     }
    384 
    385     float widthOfFullRun()
    386     {
    387         float widthSum = 0;
    388         while (nextScriptRun())
    389             widthSum += width();
    390 
    391         return widthSum;
    392     }
     218    const unsigned numCodePoints() const { return m_numCodePoints; }
     219
     220    const FontPlatformData* fontPlatformDataForScriptRun() { return reinterpret_cast<FontPlatformData*>(m_item.font->userData); }
    393221
    394222private:
    395     const TextRun& getTextRun(const TextRun& originalRun)
    396     {
    397         // Normalize the text run in two ways:
    398         // 1) Convert the |originalRun| to NFC normalized form if combining diacritical marks
    399         // (U+0300..) are used in the run. This conversion is necessary since most OpenType
    400         // fonts (e.g., Arial) don't have substitution rules for the diacritical marks in
    401         // their GSUB tables.
    402         //
    403         // Note that we don't use the icu::Normalizer::isNormalized(UNORM_NFC) API here since
    404         // the API returns FALSE (= not normalized) for complex runs that don't require NFC
    405         // normalization (e.g., Arabic text). Unless the run contains the diacritical marks,
    406         // Harfbuzz will do the same thing for us using the GSUB table.
    407         // 2) Convert spacing characters into plain spaces, as some fonts will provide glyphs
    408         // for characters like '\n' otherwise.
    409         for (int i = 0; i < originalRun.length(); ++i) {
    410             UChar ch = originalRun[i];
    411             UBlockCode block = ::ublock_getCode(ch);
    412             if (block == UBLOCK_COMBINING_DIACRITICAL_MARKS || (Font::treatAsSpace(ch) && ch != ' ')) {
    413                 return getNormalizedTextRun(originalRun);
    414             }
    415         }
    416         return originalRun;
    417     }
    418 
    419     const TextRun& getNormalizedTextRun(const TextRun& originalRun)
    420     {
    421         icu::UnicodeString normalizedString;
    422         UErrorCode error = U_ZERO_ERROR;
    423         icu::Normalizer::normalize(icu::UnicodeString(originalRun.characters(), originalRun.length()), UNORM_NFC, 0 /* no options */, normalizedString, error);
    424         if (U_FAILURE(error))
    425             return originalRun;
    426 
    427         m_normalizedBuffer.set(new UChar[normalizedString.length() + 1]);
    428         normalizedString.extract(m_normalizedBuffer.get(), normalizedString.length() + 1, error);
    429         ASSERT(U_SUCCESS(error));
    430 
    431         for (int i = 0; i < normalizedString.length(); ++i) {
    432             if (Font::treatAsSpace(m_normalizedBuffer[i]))
    433                 m_normalizedBuffer[i] = ' ';
    434         }
    435 
    436         m_normalizedRun.set(new TextRun(originalRun));
    437         m_normalizedRun->setText(m_normalizedBuffer.get(), normalizedString.length());
    438         return *m_normalizedRun;
    439     }
    440 
    441     void setupFontForScriptRun()
    442     {
    443         const FontData* fontData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos], false, false).fontData;
    444         const FontPlatformData& platformData = fontData->fontDataForCharacter(' ')->platformData();
    445         m_item.face = platformData.harfbuzzFace();
    446         void* opaquePlatformData = const_cast<FontPlatformData*>(&platformData);
    447         m_item.font->userData = opaquePlatformData;
    448     }
    449 
    450     HB_FontRec* allocHarfbuzzFont()
    451     {
    452         HB_FontRec* font = reinterpret_cast<HB_FontRec*>(fastMalloc(sizeof(HB_FontRec)));
    453         memset(font, 0, sizeof(HB_FontRec));
    454         font->klass = &harfbuzzSkiaClass;
    455         font->userData = 0;
    456         // The values which harfbuzzSkiaClass returns are already scaled to
    457         // pixel units, so we just set all these to one to disable further
    458         // scaling.
    459         font->x_ppem = 1;
    460         font->y_ppem = 1;
    461         font->x_scale = 1;
    462         font->y_scale = 1;
    463 
    464         return font;
    465     }
    466 
    467     void deleteGlyphArrays()
    468     {
    469         delete[] m_item.glyphs;
    470         delete[] m_item.attributes;
    471         delete[] m_item.advances;
    472         delete[] m_item.offsets;
    473         delete[] m_glyphs16;
    474         delete[] m_xPositions;
    475     }
    476 
    477     void createGlyphArrays(int size)
    478     {
    479         m_item.glyphs = new HB_Glyph[size];
    480         m_item.attributes = new HB_GlyphAttributes[size];
    481         m_item.advances = new HB_Fixed[size];
    482         m_item.offsets = new HB_FixedPoint[size];
    483 
    484         m_glyphs16 = new uint16_t[size];
    485         m_xPositions = new SkScalar[size];
    486 
    487         m_item.num_glyphs = size;
    488         m_glyphsArrayCapacity = size; // Save the GlyphArrays size.
    489         resetGlyphArrays();
    490     }
    491 
    492     void resetGlyphArrays()
    493     {
    494         int size = m_item.num_glyphs;
    495         // All the types here don't have pointers. It is safe to reset to
    496         // zero unless Harfbuzz breaks the compatibility in the future.
    497         memset(m_item.glyphs, 0, size * sizeof(HB_Glyph));
    498         memset(m_item.attributes, 0, size * sizeof(HB_GlyphAttributes));
    499         memset(m_item.advances, 0, size * sizeof(HB_Fixed));
    500         memset(m_item.offsets, 0, size * sizeof(HB_FixedPoint));
    501         memset(m_glyphs16, 0, size * sizeof(uint16_t));
    502         memset(m_xPositions, 0, size * sizeof(SkScalar));
    503     }
    504 
    505     void shapeGlyphs()
    506     {
    507         // HB_ShapeItem() resets m_item.num_glyphs. If the previous call to
    508         // HB_ShapeItem() used less space than was available, the capacity of
    509         // the array may be larger than the current value of m_item.num_glyphs.
    510         // So, we need to reset the num_glyphs to the capacity of the array.
    511         m_item.num_glyphs = m_glyphsArrayCapacity;
    512         resetGlyphArrays();
    513         while (!HB_ShapeItem(&m_item)) {
    514             // We overflowed our arrays. Resize and retry.
    515             // HB_ShapeItem fills in m_item.num_glyphs with the needed size.
    516             deleteGlyphArrays();
    517             // The |+ 1| here is a workaround for a bug in Harfbuzz: the Khmer
    518             // shaper (at least) can fail because of insufficient glyph buffers
    519             // and request 0 additional glyphs: throwing us into an infinite
    520             // loop.
    521             createGlyphArrays(m_item.num_glyphs + 1);
    522         }
    523     }
    524 
    525     void setGlyphXPositions(bool isRTL)
    526     {
    527         double position = 0;
    528         // logClustersIndex indexes logClusters for the first (or last when
    529         // RTL) codepoint of the current glyph.  Each time we advance a glyph,
    530         // we skip over all the codepoints that contributed to the current
    531         // glyph.
    532         unsigned logClustersIndex = isRTL ? m_item.num_glyphs - 1 : 0;
    533 
    534         for (unsigned iter = 0; iter < m_item.num_glyphs; ++iter) {
    535             // Glyphs are stored in logical order, but for layout purposes we
    536             // always go left to right.
    537             int i = isRTL ? m_item.num_glyphs - iter - 1 : iter;
    538 
    539             m_glyphs16[i] = m_item.glyphs[i];
    540             double offsetX = truncateFixedPointToInteger(m_item.offsets[i].x);
    541             m_xPositions[i] = m_offsetX + position + offsetX;
    542 
    543             double advance = truncateFixedPointToInteger(m_item.advances[i]);
    544             // The first half of the conjuction works around the case where
    545             // output glyphs aren't associated with any codepoints by the
    546             // clusters log.
    547             if (logClustersIndex < m_item.item.length
    548                 && isWordBreak(m_item.item.pos + logClustersIndex, isRTL)) {
    549                 advance += m_wordSpacingAdjustment;
    550 
    551                 if (m_padding > 0) {
    552                     unsigned toPad = roundf(m_padPerWordBreak + m_padError);
    553                     m_padError += m_padPerWordBreak - toPad;
    554 
    555                     if (m_padding < toPad)
    556                         toPad = m_padding;
    557                     m_padding -= toPad;
    558                     advance += toPad;
    559                 }
    560             }
    561 
    562             // We would like to add m_letterSpacing after each cluster, but I
    563             // don't know where the cluster information is. This is typically
    564             // fine for Roman languages, but breaks more complex languages
    565             // terribly.
    566             // advance += m_letterSpacing;
    567 
    568             if (isRTL) {
    569                 while (logClustersIndex > 0 && logClusters()[logClustersIndex] == i)
    570                     logClustersIndex--;
    571             } else {
    572                 while (logClustersIndex < m_item.item.length && logClusters()[logClustersIndex] == i)
    573                     logClustersIndex++;
    574             }
    575 
    576             position += advance;
    577         }
    578 
    579         m_pixelWidth = position;
    580         m_offsetX += m_pixelWidth;
    581     }
    582 
    583     static bool isCodepointSpace(HB_UChar16 c)
    584     {
    585         // This matches the logic in RenderBlock::findNextLineBreak
    586         return c == ' ' || c == '\t';
    587     }
    588 
    589     void mirrorCharacters(UChar* destination, const UChar* source, int length) const
    590     {
    591         int position = 0;
    592         bool error = false;
    593         // Iterate characters in source and mirror character if needed.
    594         while (position < length) {
    595             UChar32 character;
    596             int nextPosition = position;
    597             U16_NEXT(source, nextPosition, length, character);
    598             character = u_charMirror(character);
    599             U16_APPEND(destination, position, length, character, error);
    600             ASSERT(!error);
    601             position = nextPosition;
    602         }
    603     }
     223    const TextRun& getTextRun(const TextRun&);
     224    const TextRun& getNormalizedTextRun(const TextRun&);
     225    void setupFontForScriptRun();
     226    HB_FontRec* allocHarfbuzzFont();
     227    void deleteGlyphArrays();
     228    void createGlyphArrays(int);
     229    void resetGlyphArrays();
     230    void shapeGlyphs();
     231    void setGlyphXPositions(bool);
     232    void mirrorCharacters(UChar*, const UChar*, int) const;
     233
     234    // This matches the logic in RenderBlock::findNextLineBreak
     235    static bool isCodepointSpace(HB_UChar16 c) { return c == ' ' || c == '\t'; }
    604236
    605237    const Font* const m_font;
     
    627259    unsigned m_letterSpacing; // pixels to be added after each glyph.
    628260};
     261
     262
     263TextRunWalker::TextRunWalker(const TextRun& run, unsigned startingX, const Font* font)
     264    : m_font(font)
     265    , m_startingX(startingX)
     266    , m_offsetX(m_startingX)
     267    , m_run(getTextRun(run))
     268    , m_iterateBackwards(m_run.rtl())
     269    , m_wordSpacingAdjustment(0)
     270    , m_padding(0)
     271    , m_padError(0)
     272{
     273    // Do not use |run| inside this constructor. Use |m_run| instead.
     274
     275    memset(&m_item, 0, sizeof(m_item));
     276    // We cannot know, ahead of time, how many glyphs a given script run
     277    // will produce. We take a guess that script runs will not produce more
     278    // than twice as many glyphs as there are code points plus a bit of
     279    // padding and fallback if we find that we are wrong.
     280    createGlyphArrays((m_run.length() + 2) * 2);
     281
     282    m_item.log_clusters = new unsigned short[m_run.length()];
     283
     284    m_item.face = 0;
     285    m_item.font = allocHarfbuzzFont();
     286
     287    m_item.item.bidiLevel = m_run.rtl();
     288
     289    int length = m_run.length();
     290    m_item.stringLength = length;
     291
     292    if (!m_item.item.bidiLevel)
     293        m_item.string = m_run.characters();
     294    else {
     295        // Assume mirrored character is in the same Unicode multilingual plane as the original one.
     296        UChar* string = new UChar[length];
     297        mirrorCharacters(string, m_run.characters(), length);
     298        m_item.string = string;
     299    }
     300
     301    reset();
     302}
     303
     304TextRunWalker::~TextRunWalker()
     305{
     306    fastFree(m_item.font);
     307    deleteGlyphArrays();
     308    delete[] m_item.log_clusters;
     309    if (m_item.item.bidiLevel)
     310        delete[] m_item.string;
     311}
     312
     313bool TextRunWalker::isWordBreak(unsigned index, bool isRTL)
     314{
     315    if (!isRTL)
     316        return index && isCodepointSpace(m_item.string[index]) && !isCodepointSpace(m_item.string[index - 1]);
     317    return index != m_item.stringLength - 1 && isCodepointSpace(m_item.string[index]) && !isCodepointSpace(m_item.string[index + 1]);
     318}
     319
     320// setPadding sets a number of pixels to be distributed across the TextRun.
     321// WebKit uses this to justify text.
     322void TextRunWalker::setPadding(int padding)
     323{
     324    m_padding = padding;
     325    if (!m_padding)
     326        return;
     327
     328    // If we have padding to distribute, then we try to give an equal
     329    // amount to each space. The last space gets the smaller amount, if
     330    // any.
     331    unsigned numWordBreaks = 0;
     332    bool isRTL = m_iterateBackwards;
     333
     334    for (unsigned i = 0; i < m_item.stringLength; i++) {
     335        if (isWordBreak(i, isRTL))
     336            numWordBreaks++;
     337    }
     338
     339    if (numWordBreaks)
     340        m_padPerWordBreak = m_padding / numWordBreaks;
     341    else
     342        m_padPerWordBreak = 0;
     343}
     344
     345void TextRunWalker::reset()
     346{
     347    if (m_iterateBackwards)
     348        m_indexOfNextScriptRun = m_run.length() - 1;
     349    else
     350        m_indexOfNextScriptRun = 0;
     351    m_offsetX = m_startingX;
     352}
     353
     354void TextRunWalker::setBackwardsIteration(bool isBackwards)
     355{
     356    m_iterateBackwards = isBackwards;
     357    reset();
     358}
     359
     360// Advance to the next script run, returning false when the end of the
     361// TextRun has been reached.
     362bool TextRunWalker::nextScriptRun()
     363{
     364    if (m_iterateBackwards) {
     365        // In right-to-left mode we need to render the shaped glyph backwards and
     366        // also render the script runs themselves backwards. So given a TextRun:
     367        //    AAAAAAACTTTTTTT   (A = Arabic, C = Common, T = Thai)
     368        // we render:
     369        //    TTTTTTCAAAAAAA
     370        // (and the glyphs in each A, C and T section are backwards too)
     371        if (!hb_utf16_script_run_prev(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun))
     372            return false;
     373    } else {
     374        if (!hb_utf16_script_run_next(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun))
     375            return false;
     376
     377        // It is actually wrong to consider script runs at all in this code.
     378        // Other WebKit code (e.g. Mac) segments complex text just by finding
     379        // the longest span of text covered by a single font.
     380        // But we currently need to call hb_utf16_script_run_next anyway to fill
     381        // in the harfbuzz data structures to e.g. pick the correct script's shaper.
     382        // So we allow that to run first, then do a second pass over the range it
     383        // found and take the largest subregion that stays within a single font.
     384        const FontData* glyphData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos], false, false).fontData;
     385        unsigned endOfRun;
     386        for (endOfRun = 1; endOfRun < m_item.item.length; ++endOfRun) {
     387            const FontData* nextGlyphData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos + endOfRun], false, false).fontData;
     388            if (nextGlyphData != glyphData)
     389                break;
     390        }
     391        m_item.item.length = endOfRun;
     392        m_indexOfNextScriptRun = m_item.item.pos + endOfRun;
     393    }
     394
     395    setupFontForScriptRun();
     396    shapeGlyphs();
     397    setGlyphXPositions(rtl());
     398
     399    return true;
     400}
     401
     402float TextRunWalker::widthOfFullRun()
     403{
     404    float widthSum = 0;
     405    while (nextScriptRun())
     406        widthSum += width();
     407
     408    return widthSum;
     409}
     410
     411const TextRun& TextRunWalker::getTextRun(const TextRun& originalRun)
     412{
     413    // Normalize the text run in two ways:
     414    // 1) Convert the |originalRun| to NFC normalized form if combining diacritical marks
     415    // (U+0300..) are used in the run. This conversion is necessary since most OpenType
     416    // fonts (e.g., Arial) don't have substitution rules for the diacritical marks in
     417    // their GSUB tables.
     418    //
     419    // Note that we don't use the icu::Normalizer::isNormalized(UNORM_NFC) API here since
     420    // the API returns FALSE (= not normalized) for complex runs that don't require NFC
     421    // normalization (e.g., Arabic text). Unless the run contains the diacritical marks,
     422    // Harfbuzz will do the same thing for us using the GSUB table.
     423    // 2) Convert spacing characters into plain spaces, as some fonts will provide glyphs
     424    // for characters like '\n' otherwise.
     425    for (int i = 0; i < originalRun.length(); ++i) {
     426        UChar ch = originalRun[i];
     427        UBlockCode block = ::ublock_getCode(ch);
     428        if (block == UBLOCK_COMBINING_DIACRITICAL_MARKS || (Font::treatAsSpace(ch) && ch != ' '))
     429            return getNormalizedTextRun(originalRun);
     430    }
     431    return originalRun;
     432}
     433
     434const TextRun& TextRunWalker::getNormalizedTextRun(const TextRun& originalRun)
     435{
     436    icu::UnicodeString normalizedString;
     437    UErrorCode error = U_ZERO_ERROR;
     438    icu::Normalizer::normalize(icu::UnicodeString(originalRun.characters(), originalRun.length()), UNORM_NFC, 0 /* no options */, normalizedString, error);
     439    if (U_FAILURE(error))
     440        return originalRun;
     441
     442    m_normalizedBuffer.set(new UChar[normalizedString.length() + 1]);
     443    normalizedString.extract(m_normalizedBuffer.get(), normalizedString.length() + 1, error);
     444    ASSERT(U_SUCCESS(error));
     445
     446    for (int i = 0; i < normalizedString.length(); ++i) {
     447        if (Font::treatAsSpace(m_normalizedBuffer[i]))
     448            m_normalizedBuffer[i] = ' ';
     449    }
     450
     451    m_normalizedRun.set(new TextRun(originalRun));
     452    m_normalizedRun->setText(m_normalizedBuffer.get(), normalizedString.length());
     453    return *m_normalizedRun;
     454}
     455
     456void TextRunWalker::setupFontForScriptRun()
     457{
     458    const FontData* fontData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos], false, false).fontData;
     459    const FontPlatformData& platformData = fontData->fontDataForCharacter(' ')->platformData();
     460    m_item.face = platformData.harfbuzzFace();
     461    void* opaquePlatformData = const_cast<FontPlatformData*>(&platformData);
     462    m_item.font->userData = opaquePlatformData;
     463}
     464
     465HB_FontRec* TextRunWalker::allocHarfbuzzFont()
     466{
     467    HB_FontRec* font = reinterpret_cast<HB_FontRec*>(fastMalloc(sizeof(HB_FontRec)));
     468    memset(font, 0, sizeof(HB_FontRec));
     469    font->klass = &harfbuzzSkiaClass;
     470    font->userData = 0;
     471    // The values which harfbuzzSkiaClass returns are already scaled to
     472    // pixel units, so we just set all these to one to disable further
     473    // scaling.
     474    font->x_ppem = 1;
     475    font->y_ppem = 1;
     476    font->x_scale = 1;
     477    font->y_scale = 1;
     478
     479    return font;
     480}
     481
     482void TextRunWalker::deleteGlyphArrays()
     483{
     484    delete[] m_item.glyphs;
     485    delete[] m_item.attributes;
     486    delete[] m_item.advances;
     487    delete[] m_item.offsets;
     488    delete[] m_glyphs16;
     489    delete[] m_xPositions;
     490}
     491
     492void TextRunWalker::createGlyphArrays(int size)
     493{
     494    m_item.glyphs = new HB_Glyph[size];
     495    m_item.attributes = new HB_GlyphAttributes[size];
     496    m_item.advances = new HB_Fixed[size];
     497    m_item.offsets = new HB_FixedPoint[size];
     498
     499    m_glyphs16 = new uint16_t[size];
     500    m_xPositions = new SkScalar[size];
     501
     502    m_item.num_glyphs = size;
     503    m_glyphsArrayCapacity = size; // Save the GlyphArrays size.
     504    resetGlyphArrays();
     505}
     506
     507void TextRunWalker::resetGlyphArrays()
     508{
     509    int size = m_item.num_glyphs;
     510    // All the types here don't have pointers. It is safe to reset to
     511    // zero unless Harfbuzz breaks the compatibility in the future.
     512    memset(m_item.glyphs, 0, size * sizeof(HB_Glyph));
     513    memset(m_item.attributes, 0, size * sizeof(HB_GlyphAttributes));
     514    memset(m_item.advances, 0, size * sizeof(HB_Fixed));
     515    memset(m_item.offsets, 0, size * sizeof(HB_FixedPoint));
     516    memset(m_glyphs16, 0, size * sizeof(uint16_t));
     517    memset(m_xPositions, 0, size * sizeof(SkScalar));
     518}
     519
     520void TextRunWalker::shapeGlyphs()
     521{
     522    // HB_ShapeItem() resets m_item.num_glyphs. If the previous call to
     523    // HB_ShapeItem() used less space than was available, the capacity of
     524    // the array may be larger than the current value of m_item.num_glyphs.
     525    // So, we need to reset the num_glyphs to the capacity of the array.
     526    m_item.num_glyphs = m_glyphsArrayCapacity;
     527    resetGlyphArrays();
     528    while (!HB_ShapeItem(&m_item)) {
     529        // We overflowed our arrays. Resize and retry.
     530        // HB_ShapeItem fills in m_item.num_glyphs with the needed size.
     531        deleteGlyphArrays();
     532        // The |+ 1| here is a workaround for a bug in Harfbuzz: the Khmer
     533        // shaper (at least) can fail because of insufficient glyph buffers
     534        // and request 0 additional glyphs: throwing us into an infinite
     535        // loop.
     536        createGlyphArrays(m_item.num_glyphs + 1);
     537    }
     538}
     539
     540void TextRunWalker::setGlyphXPositions(bool isRTL)
     541{
     542    double position = 0;
     543    // logClustersIndex indexes logClusters for the first (or last when
     544    // RTL) codepoint of the current glyph.  Each time we advance a glyph,
     545    // we skip over all the codepoints that contributed to the current
     546    // glyph.
     547    unsigned logClustersIndex = isRTL ? m_item.num_glyphs - 1 : 0;
     548
     549    for (unsigned iter = 0; iter < m_item.num_glyphs; ++iter) {
     550        // Glyphs are stored in logical order, but for layout purposes we
     551        // always go left to right.
     552        int i = isRTL ? m_item.num_glyphs - iter - 1 : iter;
     553
     554        m_glyphs16[i] = m_item.glyphs[i];
     555        double offsetX = truncateFixedPointToInteger(m_item.offsets[i].x);
     556        m_xPositions[i] = m_offsetX + position + offsetX;
     557
     558        double advance = truncateFixedPointToInteger(m_item.advances[i]);
     559        // The first half of the conjuction works around the case where
     560        // output glyphs aren't associated with any codepoints by the
     561        // clusters log.
     562        if (logClustersIndex < m_item.item.length
     563            && isWordBreak(m_item.item.pos + logClustersIndex, isRTL)) {
     564            advance += m_wordSpacingAdjustment;
     565
     566            if (m_padding > 0) {
     567                unsigned toPad = roundf(m_padPerWordBreak + m_padError);
     568                m_padError += m_padPerWordBreak - toPad;
     569
     570                if (m_padding < toPad)
     571                    toPad = m_padding;
     572                m_padding -= toPad;
     573                advance += toPad;
     574            }
     575        }
     576
     577        // We would like to add m_letterSpacing after each cluster, but I
     578        // don't know where the cluster information is. This is typically
     579        // fine for Roman languages, but breaks more complex languages
     580        // terribly.
     581        // advance += m_letterSpacing;
     582
     583        if (isRTL) {
     584            while (logClustersIndex > 0 && logClusters()[logClustersIndex] == i)
     585                logClustersIndex--;
     586        } else {
     587            while (logClustersIndex < m_item.item.length && logClusters()[logClustersIndex] == i)
     588                logClustersIndex++;
     589        }
     590
     591        position += advance;
     592    }
     593
     594    m_pixelWidth = position;
     595    m_offsetX += m_pixelWidth;
     596}
     597
     598void TextRunWalker::mirrorCharacters(UChar* destination, const UChar* source, int length) const
     599{
     600    int position = 0;
     601    bool error = false;
     602    // Iterate characters in source and mirror character if needed.
     603    while (position < length) {
     604        UChar32 character;
     605        int nextPosition = position;
     606        U16_NEXT(source, nextPosition, length, character);
     607        character = u_charMirror(character);
     608        U16_APPEND(destination, position, length, character, error);
     609        ASSERT(!error);
     610        position = nextPosition;
     611    }
     612}
    629613
    630614static void setupForTextPainting(SkPaint* paint, SkColor color)
Note: See TracChangeset for help on using the changeset viewer.