| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "platform/fonts/shaping/ShapeResultBuffer.h" | 5 #include "platform/fonts/shaping/ShapeResultBuffer.h" |
| 6 | 6 |
| 7 #include "platform/fonts/CharacterRange.h" | 7 #include "platform/fonts/CharacterRange.h" |
| 8 #include "platform/fonts/SimpleFontData.h" | 8 #include "platform/fonts/SimpleFontData.h" |
| 9 #include "platform/fonts/shaping/ShapeResultBloberizer.h" | |
| 10 #include "platform/fonts/shaping/ShapeResultInlineHeaders.h" | 9 #include "platform/fonts/shaping/ShapeResultInlineHeaders.h" |
| 11 #include "platform/geometry/FloatPoint.h" | 10 #include "platform/geometry/FloatPoint.h" |
| 12 #include "platform/text/Character.h" | |
| 13 #include "platform/text/TextBreakIterator.h" | |
| 14 #include "platform/text/TextDirection.h" | 11 #include "platform/text/TextDirection.h" |
| 15 | 12 |
| 16 namespace blink { | 13 namespace blink { |
| 17 | 14 |
| 18 namespace { | |
| 19 | |
| 20 inline bool IsSkipInkException(const ShapeResultBloberizer& bloberizer, | |
| 21 const TextRun& run, | |
| 22 unsigned character_index) { | |
| 23 // We want to skip descenders in general, but it is undesirable renderings for | |
| 24 // CJK characters. | |
| 25 return bloberizer.GetType() == ShapeResultBloberizer::Type::kTextIntercepts && | |
| 26 !run.Is8Bit() && | |
| 27 Character::IsCJKIdeographOrSymbol(run.CodepointAt(character_index)); | |
| 28 } | |
| 29 | |
| 30 inline void AddGlyphToBloberizer(ShapeResultBloberizer& bloberizer, | |
| 31 float advance, | |
| 32 hb_direction_t direction, | |
| 33 const SimpleFontData* font_data, | |
| 34 const HarfBuzzRunGlyphData& glyph_data, | |
| 35 const TextRun& run, | |
| 36 unsigned character_index) { | |
| 37 FloatPoint start_offset = HB_DIRECTION_IS_HORIZONTAL(direction) | |
| 38 ? FloatPoint(advance, 0) | |
| 39 : FloatPoint(0, advance); | |
| 40 if (!IsSkipInkException(bloberizer, run, character_index)) | |
| 41 bloberizer.Add(glyph_data.glyph, font_data, | |
| 42 start_offset + glyph_data.offset); | |
| 43 } | |
| 44 | |
| 45 inline void AddEmphasisMark(ShapeResultBloberizer& bloberizer, | |
| 46 const GlyphData& emphasis_data, | |
| 47 FloatPoint glyph_center, | |
| 48 float mid_glyph_offset) { | |
| 49 const SimpleFontData* emphasis_font_data = emphasis_data.font_data; | |
| 50 DCHECK(emphasis_font_data); | |
| 51 | |
| 52 bool is_vertical = | |
| 53 emphasis_font_data->PlatformData().IsVerticalAnyUpright() && | |
| 54 emphasis_font_data->VerticalData(); | |
| 55 | |
| 56 if (!is_vertical) { | |
| 57 bloberizer.Add(emphasis_data.glyph, emphasis_font_data, | |
| 58 mid_glyph_offset - glyph_center.X()); | |
| 59 } else { | |
| 60 bloberizer.Add( | |
| 61 emphasis_data.glyph, emphasis_font_data, | |
| 62 FloatPoint(-glyph_center.X(), mid_glyph_offset - glyph_center.Y())); | |
| 63 } | |
| 64 } | |
| 65 | |
| 66 inline unsigned CountGraphemesInCluster(const UChar* str, | |
| 67 unsigned str_length, | |
| 68 uint16_t start_index, | |
| 69 uint16_t end_index) { | |
| 70 if (start_index > end_index) { | |
| 71 uint16_t temp_index = start_index; | |
| 72 start_index = end_index; | |
| 73 end_index = temp_index; | |
| 74 } | |
| 75 uint16_t length = end_index - start_index; | |
| 76 DCHECK_LE(static_cast<unsigned>(start_index + length), str_length); | |
| 77 TextBreakIterator* cursor_pos_iterator = | |
| 78 CursorMovementIterator(&str[start_index], length); | |
| 79 | |
| 80 int cursor_pos = cursor_pos_iterator->current(); | |
| 81 int num_graphemes = -1; | |
| 82 while (0 <= cursor_pos) { | |
| 83 cursor_pos = cursor_pos_iterator->next(); | |
| 84 num_graphemes++; | |
| 85 } | |
| 86 return std::max(0, num_graphemes); | |
| 87 } | |
| 88 | |
| 89 } // anonymous namespace | |
| 90 | |
| 91 float ShapeResultBuffer::FillGlyphsForResult(ShapeResultBloberizer& bloberizer, | |
| 92 const ShapeResult& result, | |
| 93 const TextRunPaintInfo& run_info, | |
| 94 float initial_advance, | |
| 95 unsigned run_offset) { | |
| 96 auto total_advance = initial_advance; | |
| 97 | |
| 98 for (const auto& run : result.runs_) { | |
| 99 total_advance = run->ForEachGlyphInRange( | |
| 100 total_advance, run_info.from, run_info.to, run_offset, | |
| 101 [&](const HarfBuzzRunGlyphData& glyph_data, float total_advance, | |
| 102 uint16_t character_index) -> bool { | |
| 103 | |
| 104 AddGlyphToBloberizer(bloberizer, total_advance, run->direction_, | |
| 105 run->font_data_.Get(), glyph_data, run_info.run, | |
| 106 character_index); | |
| 107 return true; | |
| 108 }); | |
| 109 } | |
| 110 | |
| 111 return total_advance; | |
| 112 } | |
| 113 | |
| 114 float ShapeResultBuffer::FillTextEmphasisGlyphsForRun( | |
| 115 ShapeResultBloberizer& bloberizer, | |
| 116 const ShapeResult::RunInfo* run, | |
| 117 const TextRunPaintInfo& run_info, | |
| 118 const GlyphData& emphasis_data, | |
| 119 float initial_advance, | |
| 120 unsigned run_offset) { | |
| 121 if (!run) | |
| 122 return 0; | |
| 123 | |
| 124 unsigned graphemes_in_cluster = 1; | |
| 125 float cluster_advance = 0; | |
| 126 | |
| 127 FloatPoint glyph_center = | |
| 128 emphasis_data.font_data->BoundsForGlyph(emphasis_data.glyph).Center(); | |
| 129 | |
| 130 const auto& text_run = run_info.run; | |
| 131 const auto from = run_info.from; | |
| 132 const auto to = run_info.to; | |
| 133 | |
| 134 TextDirection direction = text_run.Direction(); | |
| 135 | |
| 136 // A "cluster" in this context means a cluster as it is used by HarfBuzz: | |
| 137 // The minimal group of characters and corresponding glyphs, that cannot be | |
| 138 // broken down further from a text shaping point of view. A cluster can | |
| 139 // contain multiple glyphs and grapheme clusters, with mutually overlapping | |
| 140 // boundaries. Below we count grapheme clusters per HarfBuzz clusters, then | |
| 141 // linearly split the sum of corresponding glyph advances by the number of | |
| 142 // grapheme clusters in order to find positions for emphasis mark drawing. | |
| 143 uint16_t cluster_start = static_cast<uint16_t>( | |
| 144 direction == TextDirection::kRtl | |
| 145 ? run->start_index_ + run->num_characters_ + run_offset | |
| 146 : run->GlyphToCharacterIndex(0) + run_offset); | |
| 147 | |
| 148 float advance_so_far = initial_advance; | |
| 149 const unsigned num_glyphs = run->glyph_data_.size(); | |
| 150 for (unsigned i = 0; i < num_glyphs; ++i) { | |
| 151 const HarfBuzzRunGlyphData& glyph_data = run->glyph_data_[i]; | |
| 152 uint16_t current_character_index = | |
| 153 run->start_index_ + glyph_data.character_index + run_offset; | |
| 154 bool is_run_end = (i + 1 == num_glyphs); | |
| 155 bool is_cluster_end = | |
| 156 is_run_end || (run->GlyphToCharacterIndex(i + 1) + run_offset != | |
| 157 current_character_index); | |
| 158 | |
| 159 if ((direction == TextDirection::kRtl && current_character_index >= to) || | |
| 160 (direction != TextDirection::kRtl && current_character_index < from)) { | |
| 161 advance_so_far += glyph_data.advance; | |
| 162 direction == TextDirection::kRtl ? --cluster_start : ++cluster_start; | |
| 163 continue; | |
| 164 } | |
| 165 | |
| 166 cluster_advance += glyph_data.advance; | |
| 167 | |
| 168 if (text_run.Is8Bit()) { | |
| 169 float glyph_advance_x = glyph_data.advance; | |
| 170 if (Character::CanReceiveTextEmphasis( | |
| 171 text_run[current_character_index])) { | |
| 172 AddEmphasisMark(bloberizer, emphasis_data, glyph_center, | |
| 173 advance_so_far + glyph_advance_x / 2); | |
| 174 } | |
| 175 advance_so_far += glyph_advance_x; | |
| 176 } else if (is_cluster_end) { | |
| 177 uint16_t cluster_end; | |
| 178 if (direction == TextDirection::kRtl) | |
| 179 cluster_end = current_character_index; | |
| 180 else | |
| 181 cluster_end = static_cast<uint16_t>( | |
| 182 is_run_end ? run->start_index_ + run->num_characters_ + run_offset | |
| 183 : run->GlyphToCharacterIndex(i + 1) + run_offset); | |
| 184 | |
| 185 graphemes_in_cluster = CountGraphemesInCluster( | |
| 186 text_run.Characters16(), text_run.CharactersLength(), cluster_start, | |
| 187 cluster_end); | |
| 188 if (!graphemes_in_cluster || !cluster_advance) | |
| 189 continue; | |
| 190 | |
| 191 float glyph_advance_x = cluster_advance / graphemes_in_cluster; | |
| 192 for (unsigned j = 0; j < graphemes_in_cluster; ++j) { | |
| 193 // Do not put emphasis marks on space, separator, and control | |
| 194 // characters. | |
| 195 if (Character::CanReceiveTextEmphasis( | |
| 196 text_run[current_character_index])) | |
| 197 AddEmphasisMark(bloberizer, emphasis_data, glyph_center, | |
| 198 advance_so_far + glyph_advance_x / 2); | |
| 199 advance_so_far += glyph_advance_x; | |
| 200 } | |
| 201 cluster_start = cluster_end; | |
| 202 cluster_advance = 0; | |
| 203 } | |
| 204 } | |
| 205 return advance_so_far - initial_advance; | |
| 206 } | |
| 207 | |
| 208 float ShapeResultBuffer::FillFastHorizontalGlyphs( | |
| 209 const TextRun& text_run, | |
| 210 ShapeResultBloberizer& bloberizer) const { | |
| 211 DCHECK(!HasVerticalOffsets()); | |
| 212 DCHECK_NE(bloberizer.GetType(), ShapeResultBloberizer::Type::kTextIntercepts); | |
| 213 | |
| 214 float advance = 0; | |
| 215 | |
| 216 for (unsigned i = 0; i < results_.size(); ++i) { | |
| 217 const auto& word_result = IsLeftToRightDirection(text_run.Direction()) | |
| 218 ? results_[i] | |
| 219 : results_[results_.size() - 1 - i]; | |
| 220 DCHECK(!word_result->HasVerticalOffsets()); | |
| 221 | |
| 222 for (const auto& run : word_result->runs_) { | |
| 223 DCHECK(run); | |
| 224 DCHECK(HB_DIRECTION_IS_HORIZONTAL(run->direction_)); | |
| 225 | |
| 226 advance = run->ForEachGlyph( | |
| 227 advance, | |
| 228 [&](const HarfBuzzRunGlyphData& glyph_data, | |
| 229 float total_advance) -> bool { | |
| 230 DCHECK(!glyph_data.offset.Height()); | |
| 231 bloberizer.Add(glyph_data.glyph, run->font_data_.Get(), | |
| 232 total_advance + glyph_data.offset.Width()); | |
| 233 return true; | |
| 234 }); | |
| 235 } | |
| 236 } | |
| 237 | |
| 238 return advance; | |
| 239 } | |
| 240 | |
| 241 float ShapeResultBuffer::FillGlyphs(const TextRunPaintInfo& run_info, | |
| 242 ShapeResultBloberizer& bloberizer) const { | |
| 243 // Fast path: full run with no vertical offsets, no text intercepts. | |
| 244 if (!run_info.from && run_info.to == run_info.run.length() && | |
| 245 !HasVerticalOffsets() && | |
| 246 bloberizer.GetType() != ShapeResultBloberizer::Type::kTextIntercepts) { | |
| 247 return FillFastHorizontalGlyphs(run_info.run, bloberizer); | |
| 248 } | |
| 249 | |
| 250 float advance = 0; | |
| 251 | |
| 252 if (run_info.run.Rtl()) { | |
| 253 unsigned word_offset = run_info.run.length(); | |
| 254 for (unsigned j = 0; j < results_.size(); j++) { | |
| 255 unsigned resolved_index = results_.size() - 1 - j; | |
| 256 const RefPtr<const ShapeResult>& word_result = results_[resolved_index]; | |
| 257 word_offset -= word_result->NumCharacters(); | |
| 258 advance = FillGlyphsForResult(bloberizer, *word_result, run_info, advance, | |
| 259 word_offset); | |
| 260 } | |
| 261 } else { | |
| 262 unsigned word_offset = 0; | |
| 263 for (const auto& word_result : results_) { | |
| 264 advance = FillGlyphsForResult(bloberizer, *word_result, run_info, advance, | |
| 265 word_offset); | |
| 266 word_offset += word_result->NumCharacters(); | |
| 267 } | |
| 268 } | |
| 269 | |
| 270 return advance; | |
| 271 } | |
| 272 | |
| 273 void ShapeResultBuffer::FillTextEmphasisGlyphs( | |
| 274 const TextRunPaintInfo& run_info, | |
| 275 const GlyphData& emphasis_data, | |
| 276 ShapeResultBloberizer& bloberizer) const { | |
| 277 float advance = 0; | |
| 278 unsigned word_offset = run_info.run.Rtl() ? run_info.run.length() : 0; | |
| 279 | |
| 280 for (unsigned j = 0; j < results_.size(); j++) { | |
| 281 unsigned resolved_index = run_info.run.Rtl() ? results_.size() - 1 - j : j; | |
| 282 const RefPtr<const ShapeResult>& word_result = results_[resolved_index]; | |
| 283 for (unsigned i = 0; i < word_result->runs_.size(); i++) { | |
| 284 unsigned resolved_offset = | |
| 285 word_offset - (run_info.run.Rtl() ? word_result->NumCharacters() : 0); | |
| 286 advance += FillTextEmphasisGlyphsForRun( | |
| 287 bloberizer, word_result->runs_[i].get(), run_info, emphasis_data, | |
| 288 advance, resolved_offset); | |
| 289 } | |
| 290 word_offset += word_result->NumCharacters() * (run_info.run.Rtl() ? -1 : 1); | |
| 291 } | |
| 292 } | |
| 293 | |
| 294 // TODO(eae): This is a bit of a hack to allow reuse of the implementation | 15 // TODO(eae): This is a bit of a hack to allow reuse of the implementation |
| 295 // for both ShapeResultBuffer and single ShapeResult use cases. Ideally the | 16 // for both ShapeResultBuffer and single ShapeResult use cases. Ideally the |
| 296 // logic should move into ShapeResult itself and then the ShapeResultBuffer | 17 // logic should move into ShapeResult itself and then the ShapeResultBuffer |
| 297 // implementation may wrap that. | 18 // implementation may wrap that. |
| 298 CharacterRange ShapeResultBuffer::GetCharacterRange( | 19 CharacterRange ShapeResultBuffer::GetCharacterRange( |
| 299 RefPtr<const ShapeResult> result, | 20 RefPtr<const ShapeResult> result, |
| 300 TextDirection direction, | 21 TextDirection direction, |
| 301 float total_width, | 22 float total_width, |
| 302 unsigned from, | 23 unsigned from, |
| 303 unsigned to) { | 24 unsigned to) { |
| (...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 502 return GlyphData( | 223 return GlyphData( |
| 503 run->glyph_data_[0].glyph, | 224 run->glyph_data_[0].glyph, |
| 504 run->font_data_->EmphasisMarkFontData(font_description).Get()); | 225 run->font_data_->EmphasisMarkFontData(font_description).Get()); |
| 505 } | 226 } |
| 506 } | 227 } |
| 507 | 228 |
| 508 return GlyphData(); | 229 return GlyphData(); |
| 509 } | 230 } |
| 510 | 231 |
| 511 } // namespace blink | 232 } // namespace blink |
| OLD | NEW |