| OLD | NEW |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 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/ShapeResultBloberizer.h" | 5 #include "platform/fonts/shaping/ShapeResultBloberizer.h" |
| 6 | 6 |
| 7 #include <hb.h> |
| 7 #include "platform/fonts/Font.h" | 8 #include "platform/fonts/Font.h" |
| 8 #include "platform/fonts/shaping/CachingWordShaper.h" | 9 #include "platform/fonts/shaping/CachingWordShaper.h" |
| 10 #include "platform/fonts/shaping/ShapeResult.h" |
| 11 #include "platform/fonts/shaping/ShapeResultInlineHeaders.h" |
| 12 #include "platform/text/TextBreakIterator.h" |
| 9 #include "platform/text/TextRun.h" | 13 #include "platform/text/TextRun.h" |
| 10 | 14 |
| 11 namespace blink { | 15 namespace blink { |
| 12 | 16 |
| 13 ShapeResultBloberizer::ShapeResultBloberizer(const Font& font, | 17 ShapeResultBloberizer::ShapeResultBloberizer(const Font& font, |
| 14 float device_scale_factor, | 18 float device_scale_factor, |
| 15 Type type) | 19 Type type) |
| 16 : font_(font), device_scale_factor_(device_scale_factor), type_(type) {} | 20 : font_(font), device_scale_factor_(device_scale_factor), type_(type) {} |
| 17 | 21 |
| 18 bool ShapeResultBloberizer::HasPendingVerticalOffsets() const { | 22 bool ShapeResultBloberizer::HasPendingVerticalOffsets() const { |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 74 ShapeResultBloberizer::BlobRotation ShapeResultBloberizer::GetBlobRotation( | 78 ShapeResultBloberizer::BlobRotation ShapeResultBloberizer::GetBlobRotation( |
| 75 const SimpleFontData* font_data) { | 79 const SimpleFontData* font_data) { |
| 76 // For vertical upright text we need to compensate the inherited 90deg CW | 80 // For vertical upright text we need to compensate the inherited 90deg CW |
| 77 // rotation (using a 90deg CCW rotation). | 81 // rotation (using a 90deg CCW rotation). |
| 78 return (font_data->PlatformData().IsVerticalAnyUpright() && | 82 return (font_data->PlatformData().IsVerticalAnyUpright() && |
| 79 font_data->VerticalData()) | 83 font_data->VerticalData()) |
| 80 ? BlobRotation::kCCWRotation | 84 ? BlobRotation::kCCWRotation |
| 81 : BlobRotation::kNoRotation; | 85 : BlobRotation::kNoRotation; |
| 82 } | 86 } |
| 83 | 87 |
| 88 float ShapeResultBloberizer::FillGlyphs( |
| 89 const TextRunPaintInfo& run_info, |
| 90 const ShapeResultBuffer& result_buffer) { |
| 91 // Fast path: full run with no vertical offsets, no text intercepts. |
| 92 if (!run_info.from && run_info.to == run_info.run.length() && |
| 93 !result_buffer.HasVerticalOffsets() && |
| 94 GetType() != ShapeResultBloberizer::Type::kTextIntercepts) { |
| 95 return FillFastHorizontalGlyphs(result_buffer, run_info.run); |
| 96 } |
| 97 |
| 98 float advance = 0; |
| 99 auto results = result_buffer.results_; |
| 100 |
| 101 if (run_info.run.Rtl()) { |
| 102 unsigned word_offset = run_info.run.length(); |
| 103 for (unsigned j = 0; j < results.size(); j++) { |
| 104 unsigned resolved_index = results.size() - 1 - j; |
| 105 const RefPtr<const ShapeResult>& word_result = results[resolved_index]; |
| 106 word_offset -= word_result->NumCharacters(); |
| 107 advance = |
| 108 FillGlyphsForResult(*word_result, run_info, advance, word_offset); |
| 109 } |
| 110 } else { |
| 111 unsigned word_offset = 0; |
| 112 for (const auto& word_result : results) { |
| 113 advance = |
| 114 FillGlyphsForResult(*word_result, run_info, advance, word_offset); |
| 115 word_offset += word_result->NumCharacters(); |
| 116 } |
| 117 } |
| 118 |
| 119 return advance; |
| 120 } |
| 121 |
| 122 void ShapeResultBloberizer::FillTextEmphasisGlyphs( |
| 123 const TextRunPaintInfo& run_info, |
| 124 const GlyphData& emphasis_data, |
| 125 const ShapeResultBuffer& result_buffer) { |
| 126 float advance = 0; |
| 127 unsigned word_offset = run_info.run.Rtl() ? run_info.run.length() : 0; |
| 128 auto results = result_buffer.results_; |
| 129 |
| 130 for (unsigned j = 0; j < results.size(); j++) { |
| 131 unsigned resolved_index = run_info.run.Rtl() ? results.size() - 1 - j : j; |
| 132 const RefPtr<const ShapeResult>& word_result = results[resolved_index]; |
| 133 for (unsigned i = 0; i < word_result->runs_.size(); i++) { |
| 134 unsigned resolved_offset = |
| 135 word_offset - (run_info.run.Rtl() ? word_result->NumCharacters() : 0); |
| 136 advance += |
| 137 FillTextEmphasisGlyphsForRun(word_result->runs_[i].get(), run_info, |
| 138 emphasis_data, advance, resolved_offset); |
| 139 } |
| 140 word_offset += word_result->NumCharacters() * (run_info.run.Rtl() ? -1 : 1); |
| 141 } |
| 142 } |
| 143 |
| 144 namespace { |
| 145 |
| 146 inline bool IsSkipInkException(const ShapeResultBloberizer& bloberizer, |
| 147 const TextRun& run, |
| 148 unsigned character_index) { |
| 149 // We want to skip descenders in general, but it is undesirable renderings for |
| 150 // CJK characters. |
| 151 return bloberizer.GetType() == ShapeResultBloberizer::Type::kTextIntercepts && |
| 152 !run.Is8Bit() && |
| 153 Character::IsCJKIdeographOrSymbol(run.CodepointAt(character_index)); |
| 154 } |
| 155 |
| 156 inline void AddGlyphToBloberizer(ShapeResultBloberizer& bloberizer, |
| 157 float advance, |
| 158 hb_direction_t direction, |
| 159 const SimpleFontData* font_data, |
| 160 const HarfBuzzRunGlyphData& glyph_data, |
| 161 const TextRun& run, |
| 162 unsigned character_index) { |
| 163 FloatPoint start_offset = HB_DIRECTION_IS_HORIZONTAL(direction) |
| 164 ? FloatPoint(advance, 0) |
| 165 : FloatPoint(0, advance); |
| 166 if (!IsSkipInkException(bloberizer, run, character_index)) { |
| 167 bloberizer.Add(glyph_data.glyph, font_data, |
| 168 start_offset + glyph_data.offset); |
| 169 } |
| 170 } |
| 171 |
| 172 inline void AddEmphasisMark(ShapeResultBloberizer& bloberizer, |
| 173 const GlyphData& emphasis_data, |
| 174 FloatPoint glyph_center, |
| 175 float mid_glyph_offset) { |
| 176 const SimpleFontData* emphasis_font_data = emphasis_data.font_data; |
| 177 DCHECK(emphasis_font_data); |
| 178 |
| 179 bool is_vertical = |
| 180 emphasis_font_data->PlatformData().IsVerticalAnyUpright() && |
| 181 emphasis_font_data->VerticalData(); |
| 182 |
| 183 if (!is_vertical) { |
| 184 bloberizer.Add(emphasis_data.glyph, emphasis_font_data, |
| 185 mid_glyph_offset - glyph_center.X()); |
| 186 } else { |
| 187 bloberizer.Add( |
| 188 emphasis_data.glyph, emphasis_font_data, |
| 189 FloatPoint(-glyph_center.X(), mid_glyph_offset - glyph_center.Y())); |
| 190 } |
| 191 } |
| 192 |
| 193 inline unsigned CountGraphemesInCluster(const UChar* str, |
| 194 unsigned str_length, |
| 195 uint16_t start_index, |
| 196 uint16_t end_index) { |
| 197 if (start_index > end_index) { |
| 198 uint16_t temp_index = start_index; |
| 199 start_index = end_index; |
| 200 end_index = temp_index; |
| 201 } |
| 202 uint16_t length = end_index - start_index; |
| 203 DCHECK_LE(static_cast<unsigned>(start_index + length), str_length); |
| 204 TextBreakIterator* cursor_pos_iterator = |
| 205 CursorMovementIterator(&str[start_index], length); |
| 206 |
| 207 int cursor_pos = cursor_pos_iterator->current(); |
| 208 int num_graphemes = -1; |
| 209 while (0 <= cursor_pos) { |
| 210 cursor_pos = cursor_pos_iterator->next(); |
| 211 num_graphemes++; |
| 212 } |
| 213 return std::max(0, num_graphemes); |
| 214 } |
| 215 |
| 216 } // namespace |
| 217 |
| 218 float ShapeResultBloberizer::FillGlyphsForResult( |
| 219 const ShapeResult& result, |
| 220 const TextRunPaintInfo& run_info, |
| 221 float initial_advance, |
| 222 unsigned run_offset) { |
| 223 auto total_advance = initial_advance; |
| 224 |
| 225 for (const auto& run : result.runs_) { |
| 226 total_advance = run->ForEachGlyphInRange( |
| 227 total_advance, run_info.from, run_info.to, run_offset, |
| 228 [&](const HarfBuzzRunGlyphData& glyph_data, float total_advance, |
| 229 uint16_t character_index) -> bool { |
| 230 |
| 231 AddGlyphToBloberizer(*this, total_advance, run->direction_, |
| 232 run->font_data_.Get(), glyph_data, run_info.run, |
| 233 character_index); |
| 234 return true; |
| 235 }); |
| 236 } |
| 237 |
| 238 return total_advance; |
| 239 } |
| 240 |
| 241 float ShapeResultBloberizer::FillFastHorizontalGlyphs( |
| 242 const ShapeResultBuffer& result_buffer, |
| 243 const TextRun& text_run) { |
| 244 DCHECK(!result_buffer.HasVerticalOffsets()); |
| 245 DCHECK_NE(GetType(), ShapeResultBloberizer::Type::kTextIntercepts); |
| 246 |
| 247 float advance = 0; |
| 248 auto results = result_buffer.results_; |
| 249 |
| 250 for (unsigned i = 0; i < results.size(); ++i) { |
| 251 const auto& word_result = IsLeftToRightDirection(text_run.Direction()) |
| 252 ? results[i] |
| 253 : results[results.size() - 1 - i]; |
| 254 DCHECK(!word_result->HasVerticalOffsets()); |
| 255 |
| 256 for (const auto& run : word_result->runs_) { |
| 257 DCHECK(run); |
| 258 DCHECK(HB_DIRECTION_IS_HORIZONTAL(run->direction_)); |
| 259 |
| 260 advance = |
| 261 run->ForEachGlyph(advance, |
| 262 [&](const HarfBuzzRunGlyphData& glyph_data, |
| 263 float total_advance) -> bool { |
| 264 DCHECK(!glyph_data.offset.Height()); |
| 265 Add(glyph_data.glyph, run->font_data_.Get(), |
| 266 total_advance + glyph_data.offset.Width()); |
| 267 return true; |
| 268 }); |
| 269 } |
| 270 } |
| 271 |
| 272 return advance; |
| 273 } |
| 274 |
| 275 float ShapeResultBloberizer::FillTextEmphasisGlyphsForRun( |
| 276 const ShapeResult::RunInfo* run, |
| 277 const TextRunPaintInfo& run_info, |
| 278 const GlyphData& emphasis_data, |
| 279 float initial_advance, |
| 280 unsigned run_offset) { |
| 281 if (!run) |
| 282 return 0; |
| 283 |
| 284 unsigned graphemes_in_cluster = 1; |
| 285 float cluster_advance = 0; |
| 286 |
| 287 FloatPoint glyph_center = |
| 288 emphasis_data.font_data->BoundsForGlyph(emphasis_data.glyph).Center(); |
| 289 |
| 290 const auto& text_run = run_info.run; |
| 291 const auto from = run_info.from; |
| 292 const auto to = run_info.to; |
| 293 |
| 294 TextDirection direction = text_run.Direction(); |
| 295 |
| 296 // A "cluster" in this context means a cluster as it is used by HarfBuzz: |
| 297 // The minimal group of characters and corresponding glyphs, that cannot be |
| 298 // broken down further from a text shaping point of view. A cluster can |
| 299 // contain multiple glyphs and grapheme clusters, with mutually overlapping |
| 300 // boundaries. Below we count grapheme clusters per HarfBuzz clusters, then |
| 301 // linearly split the sum of corresponding glyph advances by the number of |
| 302 // grapheme clusters in order to find positions for emphasis mark drawing. |
| 303 uint16_t cluster_start = static_cast<uint16_t>( |
| 304 direction == TextDirection::kRtl |
| 305 ? run->start_index_ + run->num_characters_ + run_offset |
| 306 : run->GlyphToCharacterIndex(0) + run_offset); |
| 307 |
| 308 float advance_so_far = initial_advance; |
| 309 const unsigned num_glyphs = run->glyph_data_.size(); |
| 310 for (unsigned i = 0; i < num_glyphs; ++i) { |
| 311 const HarfBuzzRunGlyphData& glyph_data = run->glyph_data_[i]; |
| 312 uint16_t current_character_index = |
| 313 run->start_index_ + glyph_data.character_index + run_offset; |
| 314 bool is_run_end = (i + 1 == num_glyphs); |
| 315 bool is_cluster_end = |
| 316 is_run_end || (run->GlyphToCharacterIndex(i + 1) + run_offset != |
| 317 current_character_index); |
| 318 |
| 319 if ((direction == TextDirection::kRtl && current_character_index >= to) || |
| 320 (direction != TextDirection::kRtl && current_character_index < from)) { |
| 321 advance_so_far += glyph_data.advance; |
| 322 direction == TextDirection::kRtl ? --cluster_start : ++cluster_start; |
| 323 continue; |
| 324 } |
| 325 |
| 326 cluster_advance += glyph_data.advance; |
| 327 |
| 328 if (text_run.Is8Bit()) { |
| 329 float glyph_advance_x = glyph_data.advance; |
| 330 if (Character::CanReceiveTextEmphasis( |
| 331 text_run[current_character_index])) { |
| 332 AddEmphasisMark(*this, emphasis_data, glyph_center, |
| 333 advance_so_far + glyph_advance_x / 2); |
| 334 } |
| 335 advance_so_far += glyph_advance_x; |
| 336 } else if (is_cluster_end) { |
| 337 uint16_t cluster_end; |
| 338 if (direction == TextDirection::kRtl) { |
| 339 cluster_end = current_character_index; |
| 340 } else { |
| 341 cluster_end = static_cast<uint16_t>( |
| 342 is_run_end ? run->start_index_ + run->num_characters_ + run_offset |
| 343 : run->GlyphToCharacterIndex(i + 1) + run_offset); |
| 344 } |
| 345 graphemes_in_cluster = CountGraphemesInCluster( |
| 346 text_run.Characters16(), text_run.CharactersLength(), cluster_start, |
| 347 cluster_end); |
| 348 if (!graphemes_in_cluster || !cluster_advance) |
| 349 continue; |
| 350 |
| 351 float glyph_advance_x = cluster_advance / graphemes_in_cluster; |
| 352 for (unsigned j = 0; j < graphemes_in_cluster; ++j) { |
| 353 // Do not put emphasis marks on space, separator, and control |
| 354 // characters. |
| 355 if (Character::CanReceiveTextEmphasis( |
| 356 text_run[current_character_index])) { |
| 357 AddEmphasisMark(*this, emphasis_data, glyph_center, |
| 358 advance_so_far + glyph_advance_x / 2); |
| 359 } |
| 360 advance_so_far += glyph_advance_x; |
| 361 } |
| 362 cluster_start = cluster_end; |
| 363 cluster_advance = 0; |
| 364 } |
| 365 } |
| 366 return advance_so_far - initial_advance; |
| 367 } |
| 368 |
| 84 } // namespace blink | 369 } // namespace blink |
| OLD | NEW |