diff options
author | crupest <crupest@outlook.com> | 2021-09-09 17:55:40 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2021-09-09 17:55:40 +0800 |
commit | 74aee8a6308445604b513ab37812ddc99365aa98 (patch) | |
tree | 4a0886df82af6baa248e62b97fa585e09be9470d | |
parent | 700751108257a00ab1a6134fe0ca570acb3269a8 (diff) | |
download | cru-74aee8a6308445604b513ab37812ddc99365aa98.tar.gz cru-74aee8a6308445604b513ab37812ddc99365aa98.tar.bz2 cru-74aee8a6308445604b513ab37812ddc99365aa98.zip |
...
-rw-r--r-- | include/cru/common/Exception.hpp | 5 | ||||
-rw-r--r-- | include/cru/common/Range.hpp | 43 | ||||
-rw-r--r-- | include/cru/common/String.hpp | 14 | ||||
-rw-r--r-- | include/cru/common/StringUtil.hpp | 6 | ||||
-rw-r--r-- | include/cru/osx/graphics/quartz/Convert.hpp | 5 | ||||
-rw-r--r-- | include/cru/osx/graphics/quartz/TextLayout.hpp | 4 | ||||
-rw-r--r-- | include/cru/platform/GraphBase.hpp | 40 | ||||
-rw-r--r-- | src/common/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/common/String.cpp | 32 | ||||
-rw-r--r-- | src/common/StringUtil.cpp | 2 | ||||
-rw-r--r-- | src/osx/graphics/quartz/Convert.cpp | 8 | ||||
-rw-r--r-- | src/osx/graphics/quartz/TextLayout.cpp | 66 |
12 files changed, 175 insertions, 51 deletions
diff --git a/include/cru/common/Exception.hpp b/include/cru/common/Exception.hpp index 861bf5e9..8864f4df 100644 --- a/include/cru/common/Exception.hpp +++ b/include/cru/common/Exception.hpp @@ -18,4 +18,9 @@ class CRU_BASE_API Exception { private: String message_; }; + +class CRU_BASE_API TextEncodeException : public Exception { + public: + using Exception::Exception; +}; } // namespace cru diff --git a/include/cru/common/Range.hpp b/include/cru/common/Range.hpp new file mode 100644 index 00000000..ecc61243 --- /dev/null +++ b/include/cru/common/Range.hpp @@ -0,0 +1,43 @@ +#pragma once +#include "Base.hpp" + +namespace cru { +struct Range final { + constexpr static Range FromTwoSides(gsl::index start, gsl::index end) { + return Range(start, end - start); + } + + constexpr static Range FromTwoSides(gsl::index start, gsl::index end, + gsl::index offset) { + return Range(start + offset, end - start); + } + + constexpr Range() = default; + constexpr Range(const gsl::index position, const gsl::index count = 0) + : position(position), count(count) {} + + gsl::index GetStart() const { return position; } + gsl::index GetEnd() const { return position + count; } + + void ChangeEnd(gsl::index new_end) { count = new_end - position; } + + Range Normalize() const { + auto result = *this; + if (result.count < 0) { + result.position += result.count; + result.count = -result.count; + } + return result; + } + + Range CoerceInto(gsl::index min, gsl::index max) const { + auto coerce = [min, max](gsl::index index) { + return index > max ? max : (index < min ? min : index); + }; + return Range::FromTwoSides(coerce(GetStart()), coerce(GetEnd())); + } + + gsl::index position = 0; + gsl::index count = 0; +}; +} // namespace cru diff --git a/include/cru/common/String.hpp b/include/cru/common/String.hpp index 563e1606..4211a160 100644 --- a/include/cru/common/String.hpp +++ b/include/cru/common/String.hpp @@ -1,7 +1,9 @@ #pragma once - #include "Base.hpp" +#include "StringUtil.hpp" +#include "Range.hpp" + #include <cstdint> #include <iterator> @@ -127,6 +129,16 @@ class CRU_BASE_API String { } public: + Utf16CodePointIterator CodePointIterator() const { + return Utf16CodePointIterator( + std::u16string_view(reinterpret_cast<char16_t*>(buffer_), size_)); + } + + Index IndexFromCodeUnitToCodePoint(Index code_unit_index) const; + Index IndexFromCodePointToCodeUnit(Index code_point_index) const; + Range RangeFromCodeUnitToCodePoint(Range code_unit_range) const; + Range RangeFromCodePointToCodeUnit(Range code_point_range) const; + const char16_t* Char16CStr() const { return reinterpret_cast<const char16_t*>(c_str()); } diff --git a/include/cru/common/StringUtil.hpp b/include/cru/common/StringUtil.hpp index 985f0032..5c230898 100644 --- a/include/cru/common/StringUtil.hpp +++ b/include/cru/common/StringUtil.hpp @@ -1,6 +1,5 @@ #pragma once #include "Base.hpp" -#include "Exception.hpp" #include <functional> #include <string> @@ -10,11 +9,6 @@ namespace cru { using CodePoint = std::int32_t; constexpr CodePoint k_invalid_code_point = -1; -class CRU_BASE_API TextEncodeException : public Exception { - public: - using Exception::Exception; -}; - inline bool IsUtf16SurrogatePairCodeUnit(char16_t c) { return c >= 0xD800 && c <= 0xDFFF; } diff --git a/include/cru/osx/graphics/quartz/Convert.hpp b/include/cru/osx/graphics/quartz/Convert.hpp index f217e549..f85fc20e 100644 --- a/include/cru/osx/graphics/quartz/Convert.hpp +++ b/include/cru/osx/graphics/quartz/Convert.hpp @@ -1,5 +1,7 @@ #pragma once #include "cru/platform/Matrix.hpp" +#include "cru/common/String.hpp" +#include "cru/common/Range.hpp" #include <CoreGraphics/CoreGraphics.h> @@ -12,4 +14,7 @@ Matrix Convert(const CGAffineTransform& matrix); CGRect Convert(const Rect& rect); Rect Convert(const CGRect& rect); + +CFRange Convert(const Range& range); +Range Convert(const CFRange& range); } // namespace cru::platform::graphics::osx::quartz diff --git a/include/cru/osx/graphics/quartz/TextLayout.hpp b/include/cru/osx/graphics/quartz/TextLayout.hpp index 4a3830db..36d5d25b 100644 --- a/include/cru/osx/graphics/quartz/TextLayout.hpp +++ b/include/cru/osx/graphics/quartz/TextLayout.hpp @@ -47,5 +47,9 @@ private: CTFramesetterRef ct_framesetter_; CTFrameRef ct_frame_; + int line_count_; + std::vector<CGPoint> line_origins_; + std::vector<CTLineRef> lines_; + std::vector<std::vector<float>> line_caret_offsets_; }; } // namespace cru::platform::graphics::osx::quartz diff --git a/include/cru/platform/GraphBase.hpp b/include/cru/platform/GraphBase.hpp index c4dd19f6..cc0ffcc7 100644 --- a/include/cru/platform/GraphBase.hpp +++ b/include/cru/platform/GraphBase.hpp @@ -3,6 +3,7 @@ #include "Color.hpp" #include "cru/common/Format.hpp" +#include "cru/common/Range.hpp" #include <fmt/core.h> #include <cstdint> @@ -266,42 +267,5 @@ constexpr bool operator!=(const Ellipse& left, const Ellipse& right) { return !(left == right); } -struct TextRange final { - constexpr static TextRange FromTwoSides(gsl::index start, gsl::index end) { - return TextRange(start, end - start); - } - - constexpr static TextRange FromTwoSides(gsl::index start, gsl::index end, - gsl::index offset) { - return TextRange(start + offset, end - start); - } - - constexpr TextRange() = default; - constexpr TextRange(const gsl::index position, const gsl::index count = 0) - : position(position), count(count) {} - - gsl::index GetStart() const { return position; } - gsl::index GetEnd() const { return position + count; } - - void ChangeEnd(gsl::index new_end) { count = new_end - position; } - - TextRange Normalize() const { - auto result = *this; - if (result.count < 0) { - result.position += result.count; - result.count = -result.count; - } - return result; - } - - TextRange CoerceInto(gsl::index min, gsl::index max) const { - auto coerce = [min, max](gsl::index index) { - return index > max ? max : (index < min ? min : index); - }; - return TextRange::FromTwoSides(coerce(GetStart()), coerce(GetEnd())); - } - - gsl::index position = 0; - gsl::index count = 0; -}; +using TextRange = Range; } // namespace cru::platform diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 71648589..1056a763 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -12,6 +12,7 @@ target_sources(cru_base PUBLIC ${CRU_BASE_INCLUDE_DIR}/Format.hpp ${CRU_BASE_INCLUDE_DIR}/Logger.hpp ${CRU_BASE_INCLUDE_DIR}/PreConfig.hpp + ${CRU_BASE_INCLUDE_DIR}/Range.hpp ${CRU_BASE_INCLUDE_DIR}/SelfResolvable.hpp ${CRU_BASE_INCLUDE_DIR}/String.hpp ${CRU_BASE_INCLUDE_DIR}/StringUtil.hpp diff --git a/src/common/String.cpp b/src/common/String.cpp index 83dd8aa8..078462c4 100644 --- a/src/common/String.cpp +++ b/src/common/String.cpp @@ -148,6 +148,38 @@ std::string String::ToUtf8() const { return cru::ToUtf8(std::u16string_view(Char16CStr(), size())); } +Index String::IndexFromCodeUnitToCodePoint(Index code_unit_index) const { + auto iter = CodePointIterator(); + Index result = 0; + while (iter.GetPosition() <= code_unit_index && !iter.IsPastEnd()) { + ++iter; + ++result; + } + return result - 1; +} + +Index String::IndexFromCodePointToCodeUnit(Index code_point_index) const { + auto iter = CodePointIterator(); + Index cpi = 0; + while (cpi < code_point_index && !iter.IsPastEnd()) { + ++iter; + ++cpi; + } + return iter.GetPosition(); +} + +Range String::RangeFromCodeUnitToCodePoint(Range code_unit_range) const { + return Range::FromTwoSides( + IndexFromCodeUnitToCodePoint(code_unit_range.GetStart()), + IndexFromCodeUnitToCodePoint(code_unit_range.GetEnd())); +} + +Range String::RangeFromCodePointToCodeUnit(Range code_point_range) const { + return Range::FromTwoSides( + IndexFromCodePointToCodeUnit(code_point_range.GetStart()), + IndexFromCodePointToCodeUnit(code_point_range.GetEnd())); +} + namespace { inline int Compare(std::uint16_t left, std::uint16_t right) { if (left < right) return -1; diff --git a/src/common/StringUtil.cpp b/src/common/StringUtil.cpp index 36f4df4e..b1f1ed4b 100644 --- a/src/common/StringUtil.cpp +++ b/src/common/StringUtil.cpp @@ -1,6 +1,6 @@ #include "cru/common/StringUtil.hpp" #include "cru/common/Base.hpp" -#include "gsl/gsl_util" +#include "cru/common/Exception.hpp" namespace cru { namespace { diff --git a/src/osx/graphics/quartz/Convert.cpp b/src/osx/graphics/quartz/Convert.cpp index 087de56b..9dcb29db 100644 --- a/src/osx/graphics/quartz/Convert.cpp +++ b/src/osx/graphics/quartz/Convert.cpp @@ -41,4 +41,12 @@ Rect Convert(const CGRect& rect) { static_cast<float>(rect.size.width), static_cast<float>(rect.size.height)}; } + +CFRange Convert(const Range& range) { + return CFRangeMake(range.position, range.count); +} + +Range Convert(const CFRange& range) { + return Range(range.location, range.length); +} } // namespace cru::platform::graphics::osx::quartz diff --git a/src/osx/graphics/quartz/TextLayout.cpp b/src/osx/graphics/quartz/TextLayout.cpp index 08495634..73bfb1ce 100644 --- a/src/osx/graphics/quartz/TextLayout.cpp +++ b/src/osx/graphics/quartz/TextLayout.cpp @@ -3,6 +3,9 @@ #include "cru/osx/graphics/quartz/Resource.hpp" #include "cru/platform/Check.hpp" +#include <algorithm> +#include <limits> + namespace cru::platform::graphics::osx::quartz { OsxCTTextLayout::OsxCTTextLayout(IGraphFactory* graphics_factory, std::shared_ptr<OsxCTFont> font, @@ -48,15 +51,59 @@ void OsxCTTextLayout::SetMaxHeight(float max_height) { } Rect OsxCTTextLayout::GetTextBounds(bool includingTrailingSpace) { - auto lines = CTFrameGetLines(ct_frame_); + float left = std::numeric_limits<float>::max(); + float top = std::numeric_limits<float>::max(); + float right = 0; + float bottom = 0; + + for (int i = 0; i < line_count_; i++) { + auto line = static_cast<CTLineRef>(lines_[i]); + const auto& line_origin = line_origins_[i]; - const auto line_count = CFArrayGetCount(lines); + CGRect line_rect = CTLineGetImageBounds(line, nullptr); + if (includingTrailingSpace) { + float trailingWidth = CTLineGetTrailingWhitespaceWidth(line); + line_rect.size.width += trailingWidth; + } - for (int i = 0; i < line_count; i++) { - auto line = CFArrayGetValueAtIndex(lines, i); + line_rect.origin.x += line_origin.x; + line_rect.origin.y += line_origin.y; - // TODO: To be continued! + left = std::min<float>(line_rect.origin.x, left); + top = std::min<float>(line_rect.origin.y, top); + right = std::max<float>(line_rect.origin.x + line_rect.size.width, right); + bottom = + std::max<float>(line_rect.origin.y + line_rect.size.height, bottom); } + + return Rect::FromVertices(left, top, right, bottom); +} + +std::vector<Rect> OsxCTTextLayout::TextRangeRect(const TextRange& text_range) { + const auto r = text_.RangeFromCodeUnitToCodePoint(text_range).Normalize(); + + std::vector<Rect> results; + + for (int i = 0; i < line_count_; i++) { + auto line = lines_[i]; + const auto& line_origin = line_origins_[i]; + + Range range = Convert(CTLineGetStringRange(line)); + range = range.CoerceInto(r.GetStart(), r.GetEnd()); + + if (range.count) { + auto line_rect = CTLineGetImageBounds(line, nullptr); + line_rect.origin.x += line_origin.x; + line_rect.origin.y += line_origin.y; + float start_offset = CTLineGetOffsetForStringIndex(line, range.GetStart(), nullptr); + float end_offset = CTLineGetOffsetForStringIndex(line, range.GetEnd(), nullptr); + line_rect.origin.x += start_offset; + line_rect.size.width = end_offset - start_offset; + results.push_back(Convert(line_rect)); + } + } + + return results; } void OsxCTTextLayout::RecreateFrame() { @@ -77,5 +124,14 @@ void OsxCTTextLayout::RecreateFrame() { CFRelease(attributed_string); CGPathRelease(path); + + const auto lines = CTFrameGetLines(ct_frame_); + line_count_ = CFArrayGetCount(lines); + lines_.resize(line_count_); + line_origins_.resize(line_count_); + CTFrameGetLineOrigins(ct_frame_, CFRangeMake(0, 0), line_origins_.data()); + for (int i = 0; i < line_count_; i++) { + lines_[i] = static_cast<CTLineRef>(CFArrayGetValueAtIndex(lines, i)); + } } } // namespace cru::platform::graphics::osx::quartz |