From a46d190f02f9cb7a1ef79aec3f55b55669ed1a2c Mon Sep 17 00:00:00 2001 From: Ryo Suzuki Date: Tue, 2 Jul 2024 23:53:30 +0900 Subject: [PATCH] =?UTF-8?q?[=E5=85=B1=E9=80=9A]=20DrawableText::fits()=20#?= =?UTF-8?q?1202?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Siv3D/include/Siv3D/DrawableText.hpp | 4 + .../Siv3D/DrawableText/SivDrawableText.cpp | 10 ++ Siv3D/src/Siv3D/Font/CFont.cpp | 7 ++ Siv3D/src/Siv3D/Font/CFont.hpp | 2 + Siv3D/src/Siv3D/Font/CFont_Headless.cpp | 6 + Siv3D/src/Siv3D/Font/CFont_Headless.hpp | 2 + .../Font/GlyphCache/BitmapGlyphCache.cpp | 114 ++++++++++++++++++ .../Font/GlyphCache/BitmapGlyphCache.hpp | 2 + .../src/Siv3D/Font/GlyphCache/IGlyphCache.hpp | 2 + .../Siv3D/Font/GlyphCache/MSDFGlyphCache.cpp | 114 ++++++++++++++++++ .../Siv3D/Font/GlyphCache/MSDFGlyphCache.hpp | 2 + .../Siv3D/Font/GlyphCache/SDFGlyphCache.cpp | 114 ++++++++++++++++++ .../Siv3D/Font/GlyphCache/SDFGlyphCache.hpp | 2 + Siv3D/src/Siv3D/Font/IFont.hpp | 2 + 14 files changed, 383 insertions(+) diff --git a/Siv3D/include/Siv3D/DrawableText.hpp b/Siv3D/include/Siv3D/DrawableText.hpp index 77e047543..144c5ae30 100644 --- a/Siv3D/include/Siv3D/DrawableText.hpp +++ b/Siv3D/include/Siv3D/DrawableText.hpp @@ -168,6 +168,10 @@ namespace s3d [[nodiscard]] RectF regionBaseAt(double size, Vec2 pos = Vec2{ 0, 0 }) const; + bool fits(const RectF& area) const; + + bool fits(double size, const RectF& area) const; + RectF draw(double x, double y, const ColorF& color = Palette::White) const; RectF draw(const Vec2& pos = Vec2{ 0, 0 }, const ColorF& color = Palette::White) const; diff --git a/Siv3D/src/Siv3D/DrawableText/SivDrawableText.cpp b/Siv3D/src/Siv3D/DrawableText/SivDrawableText.cpp index b938c5a16..9ad0907e8 100644 --- a/Siv3D/src/Siv3D/DrawableText/SivDrawableText.cpp +++ b/Siv3D/src/Siv3D/DrawableText/SivDrawableText.cpp @@ -330,6 +330,16 @@ namespace s3d return rect.movedBy(pos.x - (rect.w * 0.5), pos.y); } + bool DrawableText::fits(const RectF& area) const + { + return fits(font.fontSize(), area); + } + + bool DrawableText::fits(const double size, const RectF& area) const + { + return SIV3D_ENGINE(Font)->fits(font.id(), text, clusters, area, size, 1.0); + } + RectF DrawableText::draw(const double x, const double y, const ColorF& color) const { return draw(TextStyle::Default(), font.fontSize(), Vec2{x, y}, color); diff --git a/Siv3D/src/Siv3D/Font/CFont.cpp b/Siv3D/src/Siv3D/Font/CFont.cpp index 9797b6448..ff8226a78 100644 --- a/Siv3D/src/Siv3D/Font/CFont.cpp +++ b/Siv3D/src/Siv3D/Font/CFont.cpp @@ -441,6 +441,13 @@ namespace s3d } } + bool CFont::fits(const Font::IDType handleID, const StringView s, const Array& clusters, const RectF& area, double fontSize, const double lineHeightScale) + { + const auto& font = m_fonts[handleID]; + + return m_fonts[handleID]->getGlyphCache().fits(*font, s, clusters, area, fontSize, lineHeightScale); + } + bool CFont::draw(const Font::IDType handleID, const StringView s, const Array& clusters, const RectF& area, double fontSize, const TextStyle& textStyle, const ColorF& color, const double lineHeightScale) { const auto& font = m_fonts[handleID]; diff --git a/Siv3D/src/Siv3D/Font/CFont.hpp b/Siv3D/src/Siv3D/Font/CFont.hpp index 2c3568790..2382eb5ec 100644 --- a/Siv3D/src/Siv3D/Font/CFont.hpp +++ b/Siv3D/src/Siv3D/Font/CFont.hpp @@ -121,6 +121,8 @@ namespace s3d RectF draw(Font::IDType handleID, StringView s, const Array& clusters, const Vec2& pos, double fontSize, const TextStyle& textStyle, const ColorF& color, double lineHeightScale) override; + bool fits(Font::IDType handleID, StringView s, const Array& clusters, const RectF& area, double fontSize, double lineHeightScale); + bool draw(Font::IDType handleID, StringView s, const Array& clusters, const RectF& area, double fontSize, const TextStyle& textStyle, const ColorF& color, double lineHeightScale) override; RectF drawBase(Font::IDType handleID, StringView s, const Array& clusters, const Vec2& pos, double fontSize, const TextStyle& textStyle, const ColorF& color, double lineHeightScale) override; diff --git a/Siv3D/src/Siv3D/Font/CFont_Headless.cpp b/Siv3D/src/Siv3D/Font/CFont_Headless.cpp index 1b0903aca..6f326ada9 100644 --- a/Siv3D/src/Siv3D/Font/CFont_Headless.cpp +++ b/Siv3D/src/Siv3D/Font/CFont_Headless.cpp @@ -334,6 +334,12 @@ namespace s3d } } + bool CFont_Headless::fits(const Font::IDType, const StringView, const Array&, const RectF&, const double, const double) + { + // [Siv3D ToDo] + return false; + } + bool CFont_Headless::draw(const Font::IDType, const StringView, const Array&, const RectF&, const double, const TextStyle&, const ColorF&, const double) { // [Siv3D ToDo] diff --git a/Siv3D/src/Siv3D/Font/CFont_Headless.hpp b/Siv3D/src/Siv3D/Font/CFont_Headless.hpp index 490a38b5f..49b638df1 100644 --- a/Siv3D/src/Siv3D/Font/CFont_Headless.hpp +++ b/Siv3D/src/Siv3D/Font/CFont_Headless.hpp @@ -101,6 +101,8 @@ namespace s3d RectF draw(Font::IDType handleID, StringView s, const Array& clusters, const Vec2& pos, double fontSize, const TextStyle& textStyle, const ColorF& color, double lineHeightScale) override; + bool fits(Font::IDType handleID, StringView s, const Array& clusters, const RectF& area, double fontSize, double lineHeightScale); + bool draw(Font::IDType handleID, StringView s, const Array& clusters, const RectF& area, double fontSize, const TextStyle& textStyle, const ColorF& color, double lineHeightScale) override; RectF drawBase(Font::IDType handleID, StringView s, const Array& clusters, const Vec2& pos, double fontSize, const TextStyle& textStyle, const ColorF& color, double lineHeightScale) override; diff --git a/Siv3D/src/Siv3D/Font/GlyphCache/BitmapGlyphCache.cpp b/Siv3D/src/Siv3D/Font/GlyphCache/BitmapGlyphCache.cpp index a14b2ef91..095bba3a0 100644 --- a/Siv3D/src/Siv3D/Font/GlyphCache/BitmapGlyphCache.cpp +++ b/Siv3D/src/Siv3D/Font/GlyphCache/BitmapGlyphCache.cpp @@ -91,6 +91,120 @@ namespace s3d return{ topLeft, (xMax - basePos.x), (lineCount * prop.height() * scale * lineHeightScale) }; } + bool BitmapGlyphCache::fits(const FontData& font, const StringView s, const Array& clusters, const RectF& area, const double size, const double lineHeightScale) + { + if (not prerender(font, clusters, true)) + { + return false; + } + + // 「.」のグリフ + const Array dotGlyphCluster = font.getGlyphClusters(U".", false, Ligature::Yes); + if (not prerender(font, dotGlyphCluster, true)) + { + // do tnohing + } + updateTexture(); + + const double dotXAdvance = m_glyphTable.find(dotGlyphCluster[0].glyphIndex)->second.info.xAdvance; + const Vec2 areaBottomRight = area.br(); + + const auto& prop = font.getProperty(); + const double scale = (size / prop.fontPixelSize); + const double lineHeight = (prop.height() * scale * lineHeightScale); + + if ((area.w < (dotXAdvance * 3)) || (area.h < lineHeight)) + { + return false; + } + + const int32 maxLines = Max(static_cast(area.h / (lineHeight ? lineHeight : 1)), 1); + const Vec2 basePos{ area.pos }; + + Array penPositions; + size_t clusterIndex = 0; + { + Array xAdvances(Arg::reserve = clusters.size()); + Vec2 penPos{ basePos }; + int32 lineIndex = 0; + double currentLineWidth = 0.0; + double previousLineWidth = 0.0; + + for (; clusterIndex < clusters.size(); ++clusterIndex) + { + const auto& cluster = clusters[clusterIndex]; + double xAdvance = 0.0; + bool nextLine = false; + + if (const char32 ch = s[cluster.pos]; + IsControl(ch)) + { + if (ch == U'\t') + { + xAdvance = GetTabAdvance(prop.spaceWidth, scale, basePos.x, penPos.x, prop.indentSize); + } + else + { + xAdvance = 0.0; + nextLine = (ch == U'\n'); + } + } + else if (cluster.fontIndex != 0) + { + const size_t fallbackIndex = (cluster.fontIndex - 1); + xAdvance = SIV3D_ENGINE(Font)->regionBaseFallback(font.getFallbackFont(fallbackIndex).lock()->id(), + cluster, penPos.movedBy(0, prop.ascender * scale), size, lineHeightScale).w; + } + else + { + const auto& cache = m_glyphTable.find(cluster.glyphIndex)->second; + xAdvance = (cache.info.xAdvance * scale); + } + + if (areaBottomRight.x < (penPos.x + xAdvance)) + { + nextLine = true; + } + + if (nextLine) + { + ++lineIndex; + previousLineWidth = currentLineWidth; + + penPos.x = basePos.x; + penPos.y = basePos.y + (lineIndex * lineHeight); + } + + penPositions << penPos; + xAdvances << xAdvance; + penPos.x += xAdvance; + currentLineWidth = (penPos.x - basePos.x); + + // エリア外 + if (maxLines <= lineIndex) + { + const double dotsWidth = (dotXAdvance * 3); + double xEliminatedWidth = (area.w - previousLineWidth); + + while (clusterIndex > 0) + { + xEliminatedWidth += xAdvances[clusterIndex]; + --clusterIndex; + + if (dotsWidth <= xEliminatedWidth) + { + break; + } + } + + break; + } + } + } + + return (clusterIndex == clusters.size()); + } + bool BitmapGlyphCache::draw(const FontData& font, const StringView s, const Array& clusters, const RectF& area, const double size, const TextStyle& textStyle, const ColorF& color, const double lineHeightScale) { if (not prerender(font, clusters, true)) diff --git a/Siv3D/src/Siv3D/Font/GlyphCache/BitmapGlyphCache.hpp b/Siv3D/src/Siv3D/Font/GlyphCache/BitmapGlyphCache.hpp index 88490d8f0..d3e7df840 100644 --- a/Siv3D/src/Siv3D/Font/GlyphCache/BitmapGlyphCache.hpp +++ b/Siv3D/src/Siv3D/Font/GlyphCache/BitmapGlyphCache.hpp @@ -26,6 +26,8 @@ namespace s3d RectF draw(const FontData & font, StringView s, const Array&clusters, bool usebasePos, const Vec2 & pos, double size, const TextStyle & textStyle, const ColorF & color, double lineHeightScale) override; + bool fits(const FontData& font, StringView s, const Array& clusters, const RectF& area, double size, double lineHeightScale); + bool draw(const FontData & font, StringView s, const Array&clusters, const RectF & area, double size, const TextStyle & textStyle, const ColorF & color, double lineHeightScale) override; [[nodiscard]] diff --git a/Siv3D/src/Siv3D/Font/GlyphCache/IGlyphCache.hpp b/Siv3D/src/Siv3D/Font/GlyphCache/IGlyphCache.hpp index bcc74b721..12ae57f63 100644 --- a/Siv3D/src/Siv3D/Font/GlyphCache/IGlyphCache.hpp +++ b/Siv3D/src/Siv3D/Font/GlyphCache/IGlyphCache.hpp @@ -26,6 +26,8 @@ namespace s3d virtual RectF draw(const FontData& font, StringView s, const Array& clusters, bool useBasePos, const Vec2& pos, double size, const TextStyle& textStyle, const ColorF& color, double lineHeightScale) = 0; + virtual bool fits(const FontData& font, StringView s, const Array& clusters, const RectF& area, double size, double lineHeightScale) = 0; + virtual bool draw(const FontData& font, StringView s, const Array& clusters, const RectF& area, double size, const TextStyle& textStyle, const ColorF& color, double lineHeightScale) = 0; virtual RectF drawFallback(const FontData& font, const GlyphCluster& cluster, bool useBasePos, const Vec2& pos, const double size, const ColorF& color, double lineHeightScale) = 0; diff --git a/Siv3D/src/Siv3D/Font/GlyphCache/MSDFGlyphCache.cpp b/Siv3D/src/Siv3D/Font/GlyphCache/MSDFGlyphCache.cpp index 39ff28ddf..98d878164 100644 --- a/Siv3D/src/Siv3D/Font/GlyphCache/MSDFGlyphCache.cpp +++ b/Siv3D/src/Siv3D/Font/GlyphCache/MSDFGlyphCache.cpp @@ -90,6 +90,120 @@ namespace s3d return{ topLeft, (xMax - basePos.x), (lineCount * prop.height() * scale * lineHeightScale) }; } + bool MSDFGlyphCache::fits(const FontData& font, const StringView s, const Array& clusters, const RectF& area, const double size, const double lineHeightScale) + { + if (not prerender(font, clusters, true)) + { + return false; + } + + // 「.」のグリフ + const Array dotGlyphCluster = font.getGlyphClusters(U".", false, Ligature::Yes); + if (not prerender(font, dotGlyphCluster, true)) + { + // do tnohing + } + updateTexture(); + + const double dotXAdvance = m_glyphTable.find(dotGlyphCluster[0].glyphIndex)->second.info.xAdvance; + const Vec2 areaBottomRight = area.br(); + + const auto& prop = font.getProperty(); + const double scale = (size / prop.fontPixelSize); + const double lineHeight = (prop.height() * scale * lineHeightScale); + + if ((area.w < (dotXAdvance * 3)) || (area.h < lineHeight)) + { + return false; + } + + const int32 maxLines = Max(static_cast(area.h / (lineHeight ? lineHeight : 1)), 1); + const Vec2 basePos{ area.pos }; + + Array penPositions; + size_t clusterIndex = 0; + { + Array xAdvances(Arg::reserve = clusters.size()); + Vec2 penPos{ basePos }; + int32 lineIndex = 0; + double currentLineWidth = 0.0; + double previousLineWidth = 0.0; + + for (; clusterIndex < clusters.size(); ++clusterIndex) + { + const auto& cluster = clusters[clusterIndex]; + double xAdvance = 0.0; + bool nextLine = false; + + if (const char32 ch = s[cluster.pos]; + IsControl(ch)) + { + if (ch == U'\t') + { + xAdvance = GetTabAdvance(prop.spaceWidth, scale, basePos.x, penPos.x, prop.indentSize); + } + else + { + xAdvance = 0.0; + nextLine = (ch == U'\n'); + } + } + else if (cluster.fontIndex != 0) + { + const size_t fallbackIndex = (cluster.fontIndex - 1); + xAdvance = SIV3D_ENGINE(Font)->regionBaseFallback(font.getFallbackFont(fallbackIndex).lock()->id(), + cluster, penPos.movedBy(0, prop.ascender * scale), size, lineHeightScale).w; + } + else + { + const auto& cache = m_glyphTable.find(cluster.glyphIndex)->second; + xAdvance = (cache.info.xAdvance * scale); + } + + if (areaBottomRight.x < (penPos.x + xAdvance)) + { + nextLine = true; + } + + if (nextLine) + { + ++lineIndex; + previousLineWidth = currentLineWidth; + + penPos.x = basePos.x; + penPos.y = basePos.y + (lineIndex * lineHeight); + } + + penPositions << penPos; + xAdvances << xAdvance; + penPos.x += xAdvance; + currentLineWidth = (penPos.x - basePos.x); + + // エリア外 + if (maxLines <= lineIndex) + { + const double dotsWidth = (dotXAdvance * 3); + double xEliminatedWidth = (area.w - previousLineWidth); + + while (clusterIndex > 0) + { + xEliminatedWidth += xAdvances[clusterIndex]; + --clusterIndex; + + if (dotsWidth <= xEliminatedWidth) + { + break; + } + } + + break; + } + } + } + + return (clusterIndex == clusters.size()); + } + bool MSDFGlyphCache::draw(const FontData& font, const StringView s, const Array& clusters, const RectF& area, const double size, const TextStyle& textStyle, const ColorF& color, const double lineHeightScale) { if (not prerender(font, clusters, true)) diff --git a/Siv3D/src/Siv3D/Font/GlyphCache/MSDFGlyphCache.hpp b/Siv3D/src/Siv3D/Font/GlyphCache/MSDFGlyphCache.hpp index 238db89c4..34aea6ee8 100644 --- a/Siv3D/src/Siv3D/Font/GlyphCache/MSDFGlyphCache.hpp +++ b/Siv3D/src/Siv3D/Font/GlyphCache/MSDFGlyphCache.hpp @@ -26,6 +26,8 @@ namespace s3d RectF draw(const FontData & font, StringView s, const Array&clusters, bool usebasePos, const Vec2 & pos, double size, const TextStyle & textStyle, const ColorF & color, double lineHeightScale) override; + bool fits(const FontData& font, StringView s, const Array& clusters, const RectF& area, double size, double lineHeightScale); + bool draw(const FontData & font, StringView s, const Array&clusters, const RectF & area, double size, const TextStyle & textStyle, const ColorF & color, double lineHeightScale) override; [[nodiscard]] diff --git a/Siv3D/src/Siv3D/Font/GlyphCache/SDFGlyphCache.cpp b/Siv3D/src/Siv3D/Font/GlyphCache/SDFGlyphCache.cpp index 3fdaf01ba..791e3842c 100644 --- a/Siv3D/src/Siv3D/Font/GlyphCache/SDFGlyphCache.cpp +++ b/Siv3D/src/Siv3D/Font/GlyphCache/SDFGlyphCache.cpp @@ -90,6 +90,120 @@ namespace s3d return{ topLeft, (xMax - basePos.x), (lineCount * prop.height() * scale * lineHeightScale) }; } + bool SDFGlyphCache::fits(const FontData& font, const StringView s, const Array& clusters, const RectF& area, const double size, const double lineHeightScale) + { + if (not prerender(font, clusters, true)) + { + return false; + } + + // 「.」のグリフ + const Array dotGlyphCluster = font.getGlyphClusters(U".", false, Ligature::Yes); + if (not prerender(font, dotGlyphCluster, true)) + { + // do tnohing + } + updateTexture(); + + const double dotXAdvance = m_glyphTable.find(dotGlyphCluster[0].glyphIndex)->second.info.xAdvance; + const Vec2 areaBottomRight = area.br(); + + const auto& prop = font.getProperty(); + const double scale = (size / prop.fontPixelSize); + const double lineHeight = (prop.height() * scale * lineHeightScale); + + if ((area.w < (dotXAdvance * 3)) || (area.h < lineHeight)) + { + return false; + } + + const int32 maxLines = Max(static_cast(area.h / (lineHeight ? lineHeight : 1)), 1); + const Vec2 basePos{ area.pos }; + + Array penPositions; + size_t clusterIndex = 0; + { + Array xAdvances(Arg::reserve = clusters.size()); + Vec2 penPos{ basePos }; + int32 lineIndex = 0; + double currentLineWidth = 0.0; + double previousLineWidth = 0.0; + + for (; clusterIndex < clusters.size(); ++clusterIndex) + { + const auto& cluster = clusters[clusterIndex]; + double xAdvance = 0.0; + bool nextLine = false; + + if (const char32 ch = s[cluster.pos]; + IsControl(ch)) + { + if (ch == U'\t') + { + xAdvance = GetTabAdvance(prop.spaceWidth, scale, basePos.x, penPos.x, prop.indentSize); + } + else + { + xAdvance = 0.0; + nextLine = (ch == U'\n'); + } + } + else if (cluster.fontIndex != 0) + { + const size_t fallbackIndex = (cluster.fontIndex - 1); + xAdvance = SIV3D_ENGINE(Font)->regionBaseFallback(font.getFallbackFont(fallbackIndex).lock()->id(), + cluster, penPos.movedBy(0, prop.ascender * scale), size, lineHeightScale).w; + } + else + { + const auto& cache = m_glyphTable.find(cluster.glyphIndex)->second; + xAdvance = (cache.info.xAdvance * scale); + } + + if (areaBottomRight.x < (penPos.x + xAdvance)) + { + nextLine = true; + } + + if (nextLine) + { + ++lineIndex; + previousLineWidth = currentLineWidth; + + penPos.x = basePos.x; + penPos.y = basePos.y + (lineIndex * lineHeight); + } + + penPositions << penPos; + xAdvances << xAdvance; + penPos.x += xAdvance; + currentLineWidth = (penPos.x - basePos.x); + + // エリア外 + if (maxLines <= lineIndex) + { + const double dotsWidth = (dotXAdvance * 3); + double xEliminatedWidth = (area.w - previousLineWidth); + + while (clusterIndex > 0) + { + xEliminatedWidth += xAdvances[clusterIndex]; + --clusterIndex; + + if (dotsWidth <= xEliminatedWidth) + { + break; + } + } + + break; + } + } + } + + return (clusterIndex == clusters.size()); + } + bool SDFGlyphCache::draw(const FontData& font, const StringView s, const Array& clusters, const RectF& area, const double size, const TextStyle& textStyle, const ColorF& color, const double lineHeightScale) { if (not prerender(font, clusters, true)) diff --git a/Siv3D/src/Siv3D/Font/GlyphCache/SDFGlyphCache.hpp b/Siv3D/src/Siv3D/Font/GlyphCache/SDFGlyphCache.hpp index 4587d470d..53af09c4d 100644 --- a/Siv3D/src/Siv3D/Font/GlyphCache/SDFGlyphCache.hpp +++ b/Siv3D/src/Siv3D/Font/GlyphCache/SDFGlyphCache.hpp @@ -26,6 +26,8 @@ namespace s3d RectF draw(const FontData& font, StringView s, const Array& clusters, bool usebasePos, const Vec2& pos, double size, const TextStyle& textStyle, const ColorF& color, double lineHeightScale) override; + bool fits(const FontData& font, StringView s, const Array& clusters, const RectF& area, double size, double lineHeightScale); + bool draw(const FontData& font, StringView s, const Array& clusters, const RectF& area, double size, const TextStyle& textStyle, const ColorF& color, double lineHeightScale) override; [[nodiscard]] diff --git a/Siv3D/src/Siv3D/Font/IFont.hpp b/Siv3D/src/Siv3D/Font/IFont.hpp index f8473a7f7..3e6e6b7e1 100644 --- a/Siv3D/src/Siv3D/Font/IFont.hpp +++ b/Siv3D/src/Siv3D/Font/IFont.hpp @@ -98,6 +98,8 @@ namespace s3d virtual RectF draw(Font::IDType handleID, StringView s, const Array& clusters, const Vec2& pos, double fontSize, const TextStyle& textStyle, const ColorF& color, double lineHeightScale) = 0; + virtual bool fits(Font::IDType handleID, StringView s, const Array& clusters, const RectF& area, double fontSize, double lineHeightScale) = 0; + virtual bool draw(Font::IDType handleID, StringView s, const Array& clusters, const RectF& area, double fontSize, const TextStyle& textStyle, const ColorF& color, double lineHeightScale) = 0; virtual RectF drawBase(Font::IDType handleID, StringView s, const Array& clusters, const Vec2& pos, double fontSize, const TextStyle& textStyle, const ColorF& color, double lineHeightScale) = 0;