| /* |
| * Copyright (c) 2012 Google Inc. All rights reserved. |
| * Copyright (C) 2013 BlackBerry Limited. 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. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "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. |
| */ |
| |
| #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_INLINE_HEADERS_H_ |
| #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_INLINE_HEADERS_H_ |
| |
| #include <hb.h> |
| #include <memory> |
| #include "third_party/blink/renderer/platform/fonts/shaping/shape_result.h" |
| #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" |
| #include "third_party/blink/renderer/platform/wtf/vector.h" |
| |
| namespace blink { |
| |
| class SimpleFontData; |
| |
| // This struct should be TriviallyCopyable so that std::copy() is equivalent to |
| // memcpy. |
| // Because glyph offsets are often zero, particularly for Latin runs, we hold it |
| // in |ShapeResult::GlyphData::_offsets_| for reducing memory usage. |
| struct HarfBuzzRunGlyphData { |
| DISALLOW_NEW(); |
| |
| static constexpr unsigned kCharacterIndexBits = 15; |
| static constexpr unsigned kMaxCharacterIndex = (1 << kCharacterIndexBits) - 1; |
| static constexpr unsigned kMaxGlyphs = 1 << kCharacterIndexBits; |
| |
| unsigned glyph : 16; |
| unsigned character_index : kCharacterIndexBits; |
| unsigned safe_to_break_before : 1; |
| |
| float advance; |
| }; |
| |
| // |GlyphOffset| is a simple wrapper of |FloatSize| to allocate |GlyphOffset| |
| // with |new GlyphOffset[size]| because of |FloatSize| is declared with |
| // |DISALLOW_NEW()|. |
| class ShapeResult::GlyphOffset final : public FloatSize { |
| USING_FAST_MALLOC(GlyphOffset); |
| |
| public: |
| using FloatSize::FloatSize; |
| |
| explicit GlyphOffset(const FloatSize& other) : FloatSize(other) {} |
| }; |
| |
| struct ShapeResult::RunInfo : public RefCounted<ShapeResult::RunInfo> { |
| USING_FAST_MALLOC(RunInfo); |
| |
| public: |
| static scoped_refptr<RunInfo> Create(const SimpleFontData* font, |
| hb_direction_t dir, |
| CanvasRotationInVertical canvas_rotation, |
| hb_script_t script, |
| unsigned start_index, |
| unsigned num_glyphs, |
| unsigned num_characters) { |
| return base::AdoptRef(new RunInfo(font, dir, canvas_rotation, script, |
| start_index, num_glyphs, num_characters)); |
| } |
| |
| static scoped_refptr<RunInfo> Create(const RunInfo& other) { |
| return base::AdoptRef(new RunInfo(other)); |
| } |
| |
| RunInfo(const SimpleFontData* font, |
| hb_direction_t dir, |
| CanvasRotationInVertical canvas_rotation, |
| hb_script_t script, |
| unsigned start_index, |
| unsigned num_glyphs, |
| unsigned num_characters) |
| : glyph_data_( |
| std::min(num_glyphs, HarfBuzzRunGlyphData::kMaxCharacterIndex + 1)), |
| font_data_(const_cast<SimpleFontData*>(font)), |
| start_index_(start_index), |
| num_characters_(num_characters), |
| width_(0.0f), |
| script_(script), |
| direction_(dir), |
| canvas_rotation_(canvas_rotation) {} |
| |
| RunInfo(const RunInfo& other) |
| : glyph_data_(other.glyph_data_), |
| font_data_(other.font_data_), |
| graphemes_(other.graphemes_), |
| start_index_(other.start_index_), |
| num_characters_(other.num_characters_), |
| width_(other.width_), |
| script_(other.script_), |
| direction_(other.direction_), |
| canvas_rotation_(other.canvas_rotation_) {} |
| |
| unsigned NumGlyphs() const { return glyph_data_.size(); } |
| bool IsLtr() const { return HB_DIRECTION_IS_FORWARD(direction_); } |
| bool IsRtl() const { return HB_DIRECTION_IS_BACKWARD(direction_); } |
| bool IsHorizontal() const { return HB_DIRECTION_IS_HORIZONTAL(direction_); } |
| CanvasRotationInVertical CanvasRotation() const { return canvas_rotation_; } |
| unsigned NextSafeToBreakOffset(unsigned) const; |
| unsigned PreviousSafeToBreakOffset(unsigned) const; |
| float XPositionForVisualOffset(unsigned, AdjustMidCluster) const; |
| float XPositionForOffset(unsigned, AdjustMidCluster) const; |
| void CharacterIndexForXPosition(float, |
| BreakGlyphsOption, |
| GlyphIndexResult*) const; |
| unsigned LimitNumGlyphs(unsigned start_glyph, |
| unsigned* num_glyphs_in_out, |
| const bool is_ltr, |
| const hb_glyph_info_t* glyph_infos); |
| |
| unsigned GlyphToCharacterIndex(unsigned i) const { |
| return start_index_ + glyph_data_[i].character_index; |
| } |
| |
| unsigned NumGraphemes(unsigned start, unsigned end) const; |
| |
| // For memory reporting. |
| size_t ByteSize() const { return sizeof(*this) + glyph_data_.ByteSize(); } |
| |
| // Represents a range of HarfBuzzRunGlyphData. |begin| and |end| follow the |
| // iterator pattern; i.e., |begin| is lower or equal to |end| in the address |
| // space regardless of LTR/RTL. |begin| is inclusive, |end| is exclusive. |
| struct GlyphDataRange { |
| GlyphDataRange FindGlyphDataRange(bool is_rtl, |
| unsigned start_character_index, |
| unsigned end_character_index) const; |
| unsigned size() const { return static_cast<unsigned>(end - begin); } |
| |
| const HarfBuzzRunGlyphData* begin = nullptr; |
| const HarfBuzzRunGlyphData* end = nullptr; |
| const GlyphOffset* offsets = nullptr; |
| }; |
| |
| // Find the range of HarfBuzzRunGlyphData for the specified character index |
| // range. This function uses binary search twice, hence O(2 log n). |
| GlyphDataRange FindGlyphDataRange(unsigned start_character_index, |
| unsigned end_character_index) const { |
| GlyphDataRange range = GetGlyphDataRange().FindGlyphDataRange( |
| IsRtl(), start_character_index, end_character_index); |
| return range; |
| } |
| |
| // Creates a new RunInfo instance representing a subset of the current run. |
| // Returns |nullptr| if there are no glyphs in the specified range. |
| scoped_refptr<RunInfo> CreateSubRun(unsigned start, unsigned end) { |
| DCHECK(end > start); |
| unsigned number_of_characters = std::min(end - start, num_characters_); |
| auto glyphs = FindGlyphDataRange(start, end); |
| unsigned number_of_glyphs = |
| static_cast<unsigned>(std::distance(glyphs.begin, glyphs.end)); |
| if (UNLIKELY(!number_of_glyphs)) |
| return nullptr; |
| |
| auto run = |
| Create(font_data_.get(), direction_, canvas_rotation_, script_, |
| start_index_ + start, number_of_glyphs, number_of_characters); |
| |
| run->glyph_data_.CopyFromRange(glyphs); |
| |
| float total_advance = 0; |
| for (HarfBuzzRunGlyphData& glyph_data : run->glyph_data_) { |
| glyph_data.character_index -= start; |
| total_advance += glyph_data.advance; |
| } |
| |
| run->width_ = total_advance; |
| run->num_characters_ = number_of_characters; |
| |
| return run; |
| } |
| |
| // Returns new |RunInfo| if |this| and |other| are merged. Otherwise returns |
| // null. |
| scoped_refptr<RunInfo> MergeIfPossible(const RunInfo& other) const { |
| if (!CanMerge(other)) |
| return nullptr; |
| DCHECK_LT(start_index_, other.start_index_); |
| auto run = |
| Create(font_data_.get(), direction_, canvas_rotation_, script_, |
| start_index_, glyph_data_.size() + other.glyph_data_.size(), |
| num_characters_ + other.num_characters_); |
| // Note: We populate |graphemes_| on demand, e.g. hit testing. |
| const int index_adjust = other.start_index_ - start_index_; |
| if (UNLIKELY(IsRtl())) { |
| run->glyph_data_.CopyFrom(other.glyph_data_, glyph_data_); |
| auto* const end = run->glyph_data_.begin() + other.glyph_data_.size(); |
| for (auto* it = run->glyph_data_.begin(); it < end; ++it) |
| it->character_index += index_adjust; |
| } else { |
| run->glyph_data_.CopyFrom(glyph_data_, other.glyph_data_); |
| auto* const end = run->glyph_data_.end(); |
| for (auto* it = run->glyph_data_.begin() + glyph_data_.size(); it < end; |
| ++it) |
| it->character_index += index_adjust; |
| } |
| run->width_ = width_ + other.width_; |
| return run; |
| } |
| |
| // Returns true if |other| can be merged at end of |this|. |
| bool CanMerge(const RunInfo& other) const { |
| return start_index_ + num_characters_ == other.start_index_ && |
| canvas_rotation_ == other.canvas_rotation_ && |
| font_data_ == other.font_data_ && direction_ == other.direction_ && |
| script_ == other.script_ && |
| glyph_data_.size() + other.glyph_data_.size() < |
| HarfBuzzRunGlyphData::kMaxCharacterIndex + 1; |
| } |
| |
| void ExpandRangeToIncludePartialGlyphs(int offset, int* from, int* to) const { |
| int start = IsLtr() ? offset : (offset + num_characters_); |
| int end = offset + num_characters_; |
| |
| for (unsigned i = 0; i < glyph_data_.size(); ++i) { |
| int index = offset + glyph_data_[i].character_index; |
| if (start == index) |
| continue; |
| |
| if (IsLtr()) |
| end = index; |
| |
| if (end > *from && start < *to) { |
| *from = std::min(*from, start); |
| *to = std::max(*to, end); |
| } |
| |
| if (IsLtr()) |
| end = offset + num_characters_; |
| else |
| end = start; |
| start = index; |
| } |
| |
| if (end > *from && start < *to) { |
| *from = std::min(*from, start); |
| *to = std::max(*to, end); |
| } |
| } |
| |
| // Common signatures with RunInfoPart, to templatize algorithms. |
| const RunInfo* GetRunInfo() const { return this; } |
| const GlyphDataRange GetGlyphDataRange() const { |
| return {glyph_data_.begin(), glyph_data_.end(), |
| glyph_data_.GetMayBeOffsets()}; |
| } |
| unsigned OffsetToRunStartIndex() const { return 0; } |
| |
| class GlyphDataCollection; |
| |
| // A array of glyph offsets. If all offsets are zero, we don't allocate |
| // storage for reducing memory usage. |
| class GlyphOffsetArray final { |
| public: |
| explicit GlyphOffsetArray(unsigned size) : size_(size) {} |
| |
| GlyphOffsetArray(const GlyphOffsetArray& other) : size_(other.size_) { |
| if (!other.storage_) |
| return; |
| std::copy(other.storage_.get(), other.storage_.get() + other.size(), |
| AllocateStorage()); |
| } |
| |
| // A return value of |GetOffsets()| to represent optional |GlyphOffset| |
| // array. |
| template <bool has_non_zero_glyph_offsets> |
| struct iterator final {}; |
| |
| template <bool has_non_zero_glyph_offsets> |
| iterator<has_non_zero_glyph_offsets> GetIterator() const { |
| return iterator<has_non_zero_glyph_offsets>(*this); |
| } |
| |
| template <bool has_non_zero_glyph_offsets> |
| iterator<has_non_zero_glyph_offsets> GetIteratorForRange( |
| const GlyphDataRange& range) const { |
| return iterator<has_non_zero_glyph_offsets>(range); |
| } |
| |
| unsigned size() const { return size_; } |
| bool IsEmpty() const { return size() == 0; } |
| |
| size_t ByteSize() const { |
| return storage_ ? size() * sizeof(GlyphOffset) : 0; |
| } |
| |
| void CopyFrom(const GlyphOffsetArray& other1, |
| const GlyphOffsetArray& other2) { |
| SECURITY_CHECK(size() == other1.size() + other2.size()); |
| DCHECK(!other1.IsEmpty()); |
| DCHECK(!other2.IsEmpty()); |
| if (other1.storage_) { |
| if (!storage_) |
| AllocateStorage(); |
| std::copy(other1.storage_.get(), other1.storage_.get() + other1.size(), |
| storage_.get()); |
| } |
| if (other2.storage_) { |
| if (!storage_) |
| AllocateStorage(); |
| std::copy(other2.storage_.get(), other2.storage_.get() + other2.size(), |
| storage_.get() + other1.size()); |
| } |
| } |
| |
| void CopyFromRange(const GlyphDataRange& range) { |
| DCHECK_EQ(range.size(), size()); |
| if (!range.offsets || range.size() == 0) { |
| storage_.reset(); |
| return; |
| } |
| std::copy(range.offsets, range.offsets + range.size(), AllocateStorage()); |
| } |
| |
| GlyphOffset* GetStorage() const { return storage_.get(); } |
| bool HasStorage() const { return storage_.get(); } |
| |
| void Reverse() { |
| if (!storage_) |
| return; |
| std::reverse(storage_.get(), storage_.get() + size()); |
| } |
| |
| void Shrink(unsigned new_size) { |
| DCHECK_GE(new_size, 1u); |
| // Note: To follow Vector<T>::Shrink(), we accept |new_size == size()| |
| if (new_size == size()) |
| return; |
| DCHECK_LT(new_size, size()); |
| size_ = new_size; |
| if (!storage_) |
| return; |
| GlyphOffset* new_offsets = new GlyphOffset[new_size]; |
| std::copy(storage_.get(), storage_.get() + new_size, new_offsets); |
| storage_.reset(new_offsets); |
| } |
| |
| // Functions to change one element. |
| void AddHeightAt(unsigned index, float delta) { |
| DCHECK_LT(index, size()); |
| DCHECK_NE(delta, 0.0f); |
| if (!storage_) |
| AllocateStorage(); |
| storage_[index].SetHeight(storage_[index].Height() + delta); |
| } |
| |
| void AddWidthAt(unsigned index, float delta) { |
| DCHECK_LT(index, size()); |
| DCHECK_NE(delta, 0.0f); |
| if (!storage_) |
| AllocateStorage(); |
| storage_[index].SetWidth(storage_[index].Width() + delta); |
| } |
| |
| void SetAt(unsigned index, GlyphOffset offset) { |
| DCHECK_LT(index, size()); |
| if (!storage_) { |
| if (offset.Width() == 0 && offset.Height() == 0) |
| return; |
| AllocateStorage(); |
| } |
| storage_[index] = offset; |
| } |
| |
| private: |
| // Note: HarfBuzzShaperTest.ShapeVerticalUpright uses non-zero glyph offset. |
| GlyphOffset* AllocateStorage() { |
| DCHECK_GE(size(), 1u); |
| DCHECK(!storage_); |
| storage_.reset(new GlyphOffset[size()]); |
| return storage_.get(); |
| } |
| |
| std::unique_ptr<GlyphOffset[]> storage_; |
| unsigned size_; |
| }; |
| |
| // Collection of |HarfBuzzRunGlyphData| with optional glyph offset |
| class GlyphDataCollection final { |
| public: |
| explicit GlyphDataCollection(unsigned num_glyphs) |
| : data_(new HarfBuzzRunGlyphData[num_glyphs]), offsets_(num_glyphs) {} |
| |
| GlyphDataCollection(const GlyphDataCollection& other) |
| : data_(new HarfBuzzRunGlyphData[other.size()]), |
| offsets_(other.offsets_) { |
| static_assert(base::is_trivially_copyable<HarfBuzzRunGlyphData>::value, |
| "HarfBuzzRunGlyphData should be trivially copyable"); |
| std::copy(other.data_.get(), other.data_.get() + other.size(), |
| data_.get()); |
| } |
| |
| HarfBuzzRunGlyphData& operator[](unsigned index) { |
| DCHECK_LT(index, size()); |
| return data_[index]; |
| } |
| |
| const HarfBuzzRunGlyphData& operator[](unsigned index) const { |
| DCHECK_LT(index, size()); |
| return data_[index]; |
| } |
| |
| bool HasNonZeroOffsets() const { return offsets_.HasStorage(); } |
| |
| size_t ByteSize() const { |
| return sizeof(*this) + size() * sizeof(HarfBuzzRunGlyphData) + |
| offsets_.ByteSize(); |
| } |
| |
| template <bool has_non_zero_glyph_offsets> |
| GlyphOffsetArray::iterator<has_non_zero_glyph_offsets> GetOffsets() const { |
| return offsets_.GetIterator<has_non_zero_glyph_offsets>(); |
| } |
| |
| GlyphOffset* GetMayBeOffsets() const { return offsets_.GetStorage(); } |
| |
| // Note: Caller should be adjust |HarfBuzzRunGlyphData.character_index|. |
| void CopyFrom(const GlyphDataCollection& other1, |
| const GlyphDataCollection& other2) { |
| SECURITY_CHECK(size() == other1.size() + other2.size()); |
| DCHECK(!other1.IsEmpty()); |
| DCHECK(!other2.IsEmpty()); |
| std::copy(other1.data_.get(), other1.data_.get() + other1.size(), |
| data_.get()); |
| std::copy(other2.data_.get(), other2.data_.get() + other2.size(), |
| data_.get() + other1.size()); |
| offsets_.CopyFrom(other1.offsets_, other2.offsets_); |
| } |
| |
| // Note: Caller should be adjust |HarfBuzzRunGlyphData.character_index|. |
| void CopyFromRange(const GlyphDataRange& range) { |
| DCHECK_EQ(static_cast<size_t>(range.end - range.begin), size()); |
| static_assert(base::is_trivially_copyable<HarfBuzzRunGlyphData>::value, |
| "HarfBuzzRunGlyphData should be trivially copyable"); |
| std::copy(range.begin, range.end, data_.get()); |
| offsets_.CopyFromRange(range); |
| } |
| |
| void AddOffsetHeightAt(unsigned index, float delta) { |
| offsets_.AddHeightAt(index, delta); |
| } |
| |
| void AddOffsetWidthAt(unsigned index, float delta) { |
| offsets_.AddWidthAt(index, delta); |
| } |
| |
| void SetOffsetAt(unsigned index, GlyphOffset offset) { |
| offsets_.SetAt(index, offset); |
| } |
| |
| // Vector<HarfBuzzRunGlyphData> like functions |
| using iterator = HarfBuzzRunGlyphData*; |
| using const_iterator = const HarfBuzzRunGlyphData*; |
| iterator begin() { return data_.get(); } |
| iterator end() { return data_.get() + size(); } |
| const_iterator begin() const { return data_.get(); } |
| const_iterator end() const { return data_.get() + size(); } |
| |
| using reverse_iterator = std::reverse_iterator<iterator>; |
| using const_reverse_iterator = std::reverse_iterator<const_iterator>; |
| reverse_iterator rbegin() { return std::make_reverse_iterator(end()); } |
| reverse_iterator rend() { return std::make_reverse_iterator(begin()); } |
| const_reverse_iterator rbegin() const { |
| return std::make_reverse_iterator(end()); |
| } |
| const_reverse_iterator rend() const { |
| return std::make_reverse_iterator(begin()); |
| } |
| |
| unsigned size() const { return offsets_.size(); } |
| bool IsEmpty() const { return size() == 0; } |
| |
| const HarfBuzzRunGlyphData& front() const { |
| CHECK(!IsEmpty()); |
| return (*this)[0]; |
| } |
| const HarfBuzzRunGlyphData& back() const { |
| CHECK(!IsEmpty()); |
| return (*this)[size() - 1]; |
| } |
| |
| void Reverse() { |
| std::reverse(begin(), end()); |
| offsets_.Reverse(); |
| } |
| |
| void Shrink(unsigned new_size) { |
| DCHECK_GE(new_size, 1u); |
| // Note: To follow Vector<T>::Shrink(), we accept |new_size == size()| |
| if (new_size == size()) |
| return; |
| DCHECK_LT(new_size, size()); |
| HarfBuzzRunGlyphData* new_data = new HarfBuzzRunGlyphData[new_size]; |
| std::copy(data_.get(), data_.get() + new_size, new_data); |
| data_.reset(new_data); |
| offsets_.Shrink(new_size); |
| } |
| |
| private: |
| // Note: |offsets_| holds number of elements instead o here to reduce |
| // memory usage. |
| std::unique_ptr<HarfBuzzRunGlyphData[]> data_; |
| // |offsets_| holds collection of offset for |data_[i]|. |
| // When all offsets are zero, we don't allocate for reducing memory usage. |
| GlyphOffsetArray offsets_; |
| }; |
| |
| GlyphDataCollection glyph_data_; |
| scoped_refptr<SimpleFontData> font_data_; |
| |
| // graphemes_[i] is the number of graphemes up to (and including) the ith |
| // character in the run. |
| Vector<unsigned> graphemes_; |
| |
| unsigned start_index_; |
| unsigned num_characters_; |
| float width_; |
| |
| hb_script_t script_; |
| hb_direction_t direction_; |
| |
| // For upright-in-vertical we need to tell the ShapeResultBloberizer to rotate |
| // the canvas back 90deg for this RunInfo. |
| CanvasRotationInVertical canvas_rotation_; |
| }; |
| |
| // For non-zero glyph offset array |
| template <> |
| struct ShapeResult::RunInfo::GlyphOffsetArray::iterator<true> final { |
| // The constructor for ShapeResult |
| explicit iterator(const GlyphOffsetArray& array) |
| : pointer(array.storage_.get()) { |
| DCHECK(pointer); |
| } |
| |
| // The constructor for ShapeResultView |
| explicit iterator(const GlyphDataRange& range) : pointer(range.offsets) { |
| DCHECK(pointer); |
| } |
| |
| GlyphOffset operator*() const { return *pointer; } |
| void operator++() { ++pointer; } |
| |
| const GlyphOffset* pointer; |
| }; |
| |
| // For zero glyph offset array |
| template <> |
| struct ShapeResult::RunInfo::GlyphOffsetArray::iterator<false> final { |
| explicit iterator(const GlyphOffsetArray& array) { |
| DCHECK(!array.HasStorage()); |
| } |
| explicit iterator(const GlyphDataRange& range) { DCHECK(!range.offsets); } |
| GlyphOffset operator*() const { return GlyphOffset(); } |
| void operator++() {} |
| }; |
| |
| // Find the range of HarfBuzzRunGlyphData for the specified character index |
| // range. This function uses binary search twice, hence O(2 log n). |
| inline ShapeResult::RunInfo::GlyphDataRange |
| ShapeResult::RunInfo::GlyphDataRange::FindGlyphDataRange( |
| bool is_rtl, |
| unsigned start_character_index, |
| unsigned end_character_index) const { |
| const auto comparer = [](const HarfBuzzRunGlyphData& glyph_data, |
| unsigned index) { |
| return glyph_data.character_index < index; |
| }; |
| if (!is_rtl) { |
| const HarfBuzzRunGlyphData* start_glyph = |
| std::lower_bound(begin, end, start_character_index, comparer); |
| if (UNLIKELY(start_glyph == end)) |
| return GlyphDataRange(); |
| const HarfBuzzRunGlyphData* end_glyph = |
| std::lower_bound(start_glyph, end, end_character_index, comparer); |
| if (offsets) |
| return {start_glyph, end_glyph, offsets + (start_glyph - begin)}; |
| return {start_glyph, end_glyph, nullptr}; |
| } |
| |
| // RTL needs to use reverse iterators because there maybe multiple glyphs |
| // for a character, and we want to find the first one in the logical order. |
| const auto rbegin = std::reverse_iterator<const HarfBuzzRunGlyphData*>(end); |
| const auto rend = std::reverse_iterator<const HarfBuzzRunGlyphData*>(begin); |
| const auto start_glyph_it = |
| std::lower_bound(rbegin, rend, start_character_index, comparer); |
| if (UNLIKELY(start_glyph_it == rend)) |
| return GlyphDataRange(); |
| const auto end_glyph_it = |
| std::lower_bound(start_glyph_it, rend, end_character_index, comparer); |
| // Convert reverse iterators to pointers. Then increment to make |begin| |
| // inclusive and |end| exclusive. |
| const HarfBuzzRunGlyphData* start_glyph = &*end_glyph_it + 1; |
| const HarfBuzzRunGlyphData* end_glyph = &*start_glyph_it + 1; |
| if (offsets) |
| return {start_glyph, end_glyph, offsets + (start_glyph - begin)}; |
| return {start_glyph, end_glyph, nullptr}; |
| } |
| |
| } // namespace blink |
| |
| #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_INLINE_HEADERS_H_ |