| Index: third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBloberizer.cpp
|
| diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBloberizer.cpp b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBloberizer.cpp
|
| index f04be8e616aa1197b0f8fbfb90b5e51654e4a262..ad1a6b7ebac3fafde8e619a96584abdffa9caa9a 100644
|
| --- a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBloberizer.cpp
|
| +++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBloberizer.cpp
|
| @@ -4,8 +4,12 @@
|
|
|
| #include "platform/fonts/shaping/ShapeResultBloberizer.h"
|
|
|
| +#include <hb.h>
|
| #include "platform/fonts/Font.h"
|
| #include "platform/fonts/shaping/CachingWordShaper.h"
|
| +#include "platform/fonts/shaping/ShapeResult.h"
|
| +#include "platform/fonts/shaping/ShapeResultInlineHeaders.h"
|
| +#include "platform/text/TextBreakIterator.h"
|
| #include "platform/text/TextRun.h"
|
|
|
| namespace blink {
|
| @@ -81,4 +85,285 @@ ShapeResultBloberizer::BlobRotation ShapeResultBloberizer::GetBlobRotation(
|
| : BlobRotation::kNoRotation;
|
| }
|
|
|
| +float ShapeResultBloberizer::FillGlyphs(
|
| + const TextRunPaintInfo& run_info,
|
| + const ShapeResultBuffer& result_buffer) {
|
| + // Fast path: full run with no vertical offsets, no text intercepts.
|
| + if (!run_info.from && run_info.to == run_info.run.length() &&
|
| + !result_buffer.HasVerticalOffsets() &&
|
| + GetType() != ShapeResultBloberizer::Type::kTextIntercepts) {
|
| + return FillFastHorizontalGlyphs(result_buffer, run_info.run);
|
| + }
|
| +
|
| + float advance = 0;
|
| + auto results = result_buffer.results_;
|
| +
|
| + if (run_info.run.Rtl()) {
|
| + unsigned word_offset = run_info.run.length();
|
| + for (unsigned j = 0; j < results.size(); j++) {
|
| + unsigned resolved_index = results.size() - 1 - j;
|
| + const RefPtr<const ShapeResult>& word_result = results[resolved_index];
|
| + word_offset -= word_result->NumCharacters();
|
| + advance =
|
| + FillGlyphsForResult(*word_result, run_info, advance, word_offset);
|
| + }
|
| + } else {
|
| + unsigned word_offset = 0;
|
| + for (const auto& word_result : results) {
|
| + advance =
|
| + FillGlyphsForResult(*word_result, run_info, advance, word_offset);
|
| + word_offset += word_result->NumCharacters();
|
| + }
|
| + }
|
| +
|
| + return advance;
|
| +}
|
| +
|
| +void ShapeResultBloberizer::FillTextEmphasisGlyphs(
|
| + const TextRunPaintInfo& run_info,
|
| + const GlyphData& emphasis_data,
|
| + const ShapeResultBuffer& result_buffer) {
|
| + float advance = 0;
|
| + unsigned word_offset = run_info.run.Rtl() ? run_info.run.length() : 0;
|
| + auto results = result_buffer.results_;
|
| +
|
| + for (unsigned j = 0; j < results.size(); j++) {
|
| + unsigned resolved_index = run_info.run.Rtl() ? results.size() - 1 - j : j;
|
| + const RefPtr<const ShapeResult>& word_result = results[resolved_index];
|
| + for (unsigned i = 0; i < word_result->runs_.size(); i++) {
|
| + unsigned resolved_offset =
|
| + word_offset - (run_info.run.Rtl() ? word_result->NumCharacters() : 0);
|
| + advance +=
|
| + FillTextEmphasisGlyphsForRun(word_result->runs_[i].get(), run_info,
|
| + emphasis_data, advance, resolved_offset);
|
| + }
|
| + word_offset += word_result->NumCharacters() * (run_info.run.Rtl() ? -1 : 1);
|
| + }
|
| +}
|
| +
|
| +namespace {
|
| +
|
| +inline bool IsSkipInkException(const ShapeResultBloberizer& bloberizer,
|
| + const TextRun& run,
|
| + unsigned character_index) {
|
| + // We want to skip descenders in general, but it is undesirable renderings for
|
| + // CJK characters.
|
| + return bloberizer.GetType() == ShapeResultBloberizer::Type::kTextIntercepts &&
|
| + !run.Is8Bit() &&
|
| + Character::IsCJKIdeographOrSymbol(run.CodepointAt(character_index));
|
| +}
|
| +
|
| +inline void AddGlyphToBloberizer(ShapeResultBloberizer& bloberizer,
|
| + float advance,
|
| + hb_direction_t direction,
|
| + const SimpleFontData* font_data,
|
| + const HarfBuzzRunGlyphData& glyph_data,
|
| + const TextRun& run,
|
| + unsigned character_index) {
|
| + FloatPoint start_offset = HB_DIRECTION_IS_HORIZONTAL(direction)
|
| + ? FloatPoint(advance, 0)
|
| + : FloatPoint(0, advance);
|
| + if (!IsSkipInkException(bloberizer, run, character_index)) {
|
| + bloberizer.Add(glyph_data.glyph, font_data,
|
| + start_offset + glyph_data.offset);
|
| + }
|
| +}
|
| +
|
| +inline void AddEmphasisMark(ShapeResultBloberizer& bloberizer,
|
| + const GlyphData& emphasis_data,
|
| + FloatPoint glyph_center,
|
| + float mid_glyph_offset) {
|
| + const SimpleFontData* emphasis_font_data = emphasis_data.font_data;
|
| + DCHECK(emphasis_font_data);
|
| +
|
| + bool is_vertical =
|
| + emphasis_font_data->PlatformData().IsVerticalAnyUpright() &&
|
| + emphasis_font_data->VerticalData();
|
| +
|
| + if (!is_vertical) {
|
| + bloberizer.Add(emphasis_data.glyph, emphasis_font_data,
|
| + mid_glyph_offset - glyph_center.X());
|
| + } else {
|
| + bloberizer.Add(
|
| + emphasis_data.glyph, emphasis_font_data,
|
| + FloatPoint(-glyph_center.X(), mid_glyph_offset - glyph_center.Y()));
|
| + }
|
| +}
|
| +
|
| +inline unsigned CountGraphemesInCluster(const UChar* str,
|
| + unsigned str_length,
|
| + uint16_t start_index,
|
| + uint16_t end_index) {
|
| + if (start_index > end_index) {
|
| + uint16_t temp_index = start_index;
|
| + start_index = end_index;
|
| + end_index = temp_index;
|
| + }
|
| + uint16_t length = end_index - start_index;
|
| + DCHECK_LE(static_cast<unsigned>(start_index + length), str_length);
|
| + TextBreakIterator* cursor_pos_iterator =
|
| + CursorMovementIterator(&str[start_index], length);
|
| +
|
| + int cursor_pos = cursor_pos_iterator->current();
|
| + int num_graphemes = -1;
|
| + while (0 <= cursor_pos) {
|
| + cursor_pos = cursor_pos_iterator->next();
|
| + num_graphemes++;
|
| + }
|
| + return std::max(0, num_graphemes);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +float ShapeResultBloberizer::FillGlyphsForResult(
|
| + const ShapeResult& result,
|
| + const TextRunPaintInfo& run_info,
|
| + float initial_advance,
|
| + unsigned run_offset) {
|
| + auto total_advance = initial_advance;
|
| +
|
| + for (const auto& run : result.runs_) {
|
| + total_advance = run->ForEachGlyphInRange(
|
| + total_advance, run_info.from, run_info.to, run_offset,
|
| + [&](const HarfBuzzRunGlyphData& glyph_data, float total_advance,
|
| + uint16_t character_index) -> bool {
|
| +
|
| + AddGlyphToBloberizer(*this, total_advance, run->direction_,
|
| + run->font_data_.Get(), glyph_data, run_info.run,
|
| + character_index);
|
| + return true;
|
| + });
|
| + }
|
| +
|
| + return total_advance;
|
| +}
|
| +
|
| +float ShapeResultBloberizer::FillFastHorizontalGlyphs(
|
| + const ShapeResultBuffer& result_buffer,
|
| + const TextRun& text_run) {
|
| + DCHECK(!result_buffer.HasVerticalOffsets());
|
| + DCHECK_NE(GetType(), ShapeResultBloberizer::Type::kTextIntercepts);
|
| +
|
| + float advance = 0;
|
| + auto results = result_buffer.results_;
|
| +
|
| + for (unsigned i = 0; i < results.size(); ++i) {
|
| + const auto& word_result = IsLeftToRightDirection(text_run.Direction())
|
| + ? results[i]
|
| + : results[results.size() - 1 - i];
|
| + DCHECK(!word_result->HasVerticalOffsets());
|
| +
|
| + for (const auto& run : word_result->runs_) {
|
| + DCHECK(run);
|
| + DCHECK(HB_DIRECTION_IS_HORIZONTAL(run->direction_));
|
| +
|
| + advance =
|
| + run->ForEachGlyph(advance,
|
| + [&](const HarfBuzzRunGlyphData& glyph_data,
|
| + float total_advance) -> bool {
|
| + DCHECK(!glyph_data.offset.Height());
|
| + Add(glyph_data.glyph, run->font_data_.Get(),
|
| + total_advance + glyph_data.offset.Width());
|
| + return true;
|
| + });
|
| + }
|
| + }
|
| +
|
| + return advance;
|
| +}
|
| +
|
| +float ShapeResultBloberizer::FillTextEmphasisGlyphsForRun(
|
| + const ShapeResult::RunInfo* run,
|
| + const TextRunPaintInfo& run_info,
|
| + const GlyphData& emphasis_data,
|
| + float initial_advance,
|
| + unsigned run_offset) {
|
| + if (!run)
|
| + return 0;
|
| +
|
| + unsigned graphemes_in_cluster = 1;
|
| + float cluster_advance = 0;
|
| +
|
| + FloatPoint glyph_center =
|
| + emphasis_data.font_data->BoundsForGlyph(emphasis_data.glyph).Center();
|
| +
|
| + const auto& text_run = run_info.run;
|
| + const auto from = run_info.from;
|
| + const auto to = run_info.to;
|
| +
|
| + TextDirection direction = text_run.Direction();
|
| +
|
| + // A "cluster" in this context means a cluster as it is used by HarfBuzz:
|
| + // The minimal group of characters and corresponding glyphs, that cannot be
|
| + // broken down further from a text shaping point of view. A cluster can
|
| + // contain multiple glyphs and grapheme clusters, with mutually overlapping
|
| + // boundaries. Below we count grapheme clusters per HarfBuzz clusters, then
|
| + // linearly split the sum of corresponding glyph advances by the number of
|
| + // grapheme clusters in order to find positions for emphasis mark drawing.
|
| + uint16_t cluster_start = static_cast<uint16_t>(
|
| + direction == TextDirection::kRtl
|
| + ? run->start_index_ + run->num_characters_ + run_offset
|
| + : run->GlyphToCharacterIndex(0) + run_offset);
|
| +
|
| + float advance_so_far = initial_advance;
|
| + const unsigned num_glyphs = run->glyph_data_.size();
|
| + for (unsigned i = 0; i < num_glyphs; ++i) {
|
| + const HarfBuzzRunGlyphData& glyph_data = run->glyph_data_[i];
|
| + uint16_t current_character_index =
|
| + run->start_index_ + glyph_data.character_index + run_offset;
|
| + bool is_run_end = (i + 1 == num_glyphs);
|
| + bool is_cluster_end =
|
| + is_run_end || (run->GlyphToCharacterIndex(i + 1) + run_offset !=
|
| + current_character_index);
|
| +
|
| + if ((direction == TextDirection::kRtl && current_character_index >= to) ||
|
| + (direction != TextDirection::kRtl && current_character_index < from)) {
|
| + advance_so_far += glyph_data.advance;
|
| + direction == TextDirection::kRtl ? --cluster_start : ++cluster_start;
|
| + continue;
|
| + }
|
| +
|
| + cluster_advance += glyph_data.advance;
|
| +
|
| + if (text_run.Is8Bit()) {
|
| + float glyph_advance_x = glyph_data.advance;
|
| + if (Character::CanReceiveTextEmphasis(
|
| + text_run[current_character_index])) {
|
| + AddEmphasisMark(*this, emphasis_data, glyph_center,
|
| + advance_so_far + glyph_advance_x / 2);
|
| + }
|
| + advance_so_far += glyph_advance_x;
|
| + } else if (is_cluster_end) {
|
| + uint16_t cluster_end;
|
| + if (direction == TextDirection::kRtl) {
|
| + cluster_end = current_character_index;
|
| + } else {
|
| + cluster_end = static_cast<uint16_t>(
|
| + is_run_end ? run->start_index_ + run->num_characters_ + run_offset
|
| + : run->GlyphToCharacterIndex(i + 1) + run_offset);
|
| + }
|
| + graphemes_in_cluster = CountGraphemesInCluster(
|
| + text_run.Characters16(), text_run.CharactersLength(), cluster_start,
|
| + cluster_end);
|
| + if (!graphemes_in_cluster || !cluster_advance)
|
| + continue;
|
| +
|
| + float glyph_advance_x = cluster_advance / graphemes_in_cluster;
|
| + for (unsigned j = 0; j < graphemes_in_cluster; ++j) {
|
| + // Do not put emphasis marks on space, separator, and control
|
| + // characters.
|
| + if (Character::CanReceiveTextEmphasis(
|
| + text_run[current_character_index])) {
|
| + AddEmphasisMark(*this, emphasis_data, glyph_center,
|
| + advance_so_far + glyph_advance_x / 2);
|
| + }
|
| + advance_so_far += glyph_advance_x;
|
| + }
|
| + cluster_start = cluster_end;
|
| + cluster_advance = 0;
|
| + }
|
| + }
|
| + return advance_so_far - initial_advance;
|
| +}
|
| +
|
| } // namespace blink
|
|
|