| Flutter Impeller |

text_contents_unittests.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "flutter/impeller/geometry/geometry_asserts.h"
6 #include "flutter/impeller/renderer/testing/mocks.h"
7 #include "flutter/testing/testing.h"
8 #include "impeller/entity/contents/text_contents.h"
9 #include "impeller/playground/playground_test.h"
10 #include "impeller/typographer/backends/skia/text_frame_skia.h"
11 #include "impeller/typographer/backends/skia/typographer_context_skia.h"
12 #include "third_party/googletest/googletest/include/gtest/gtest.h"
13 #include "txt/platform.h"
14
15 #pragma GCC diagnostic ignored "-Wunreachable-code"
16
17 namespace impeller {
18 namespace testing {
19
20 using TextContentsTest = PlaygroundTest;
21 INSTANTIATE_PLAYGROUND_SUITE(TextContentsTest);
22
23 using ::testing::Return;
24
25 namespace {
26 std::shared_ptr<TextFrame> MakeTextFrame(const std::string& text,
27 const std::string_view& font_fixture,
28 Scalar font_size) {
29 auto c_font_fixture = std::string(font_fixture);
30 auto mapping = flutter::testing::OpenFixtureAsSkData(c_font_fixture.c_str());
31 if (!mapping) {
32 return nullptr;
33 }
34 sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
35 SkFont sk_font(font_mgr->makeFromData(mapping), font_size);
36 auto blob = SkTextBlob::MakeFromString(text.c_str(), sk_font);
37 if (!blob) {
38 return nullptr;
39 }
40
41 return MakeTextFrameFromTextBlobSkia(blob);
42 }
43
44 std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
45 Context& context,
46 const TypographerContext* typographer_context,
47 HostBuffer& host_buffer,
48 GlyphAtlas::Type type,
49 Scalar scale,
50 const std::shared_ptr<GlyphAtlasContext>& atlas_context,
51 const std::shared_ptr<TextFrame>& frame) {
52 frame->SetPerFrameData(
53 TextFrame::RoundScaledFontSize(scale), /*offset=*/{0, 0},
54 /*transform=*/Matrix::MakeScale(Vector3{scale, scale, 1}),
55 /*properties=*/std::nullopt);
56 return typographer_context->CreateGlyphAtlas(context, type, host_buffer,
57 atlas_context, {frame});
58 }
59
60 Rect PerVertexDataPositionToRect(
61 GlyphAtlasPipeline::VertexShader::PerVertexData data[6]) {
62 Scalar right = FLT_MIN;
63 Scalar left = FLT_MAX;
64 Scalar top = FLT_MAX;
65 Scalar bottom = FLT_MIN;
66 for (int i = 0; i < 6; ++i) {
67 right = std::max(right, data[i].position.x);
68 left = std::min(left, data[i].position.x);
69 top = std::min(top, data[i].position.y);
70 bottom = std::max(bottom, data[i].position.y);
71 }
72
73 return Rect::MakeLTRB(left, top, right, bottom);
74 }
75
76 Rect PerVertexDataUVToRect(
77 GlyphAtlasPipeline::VertexShader::PerVertexData data[6],
78 ISize texture_size) {
79 Scalar right = FLT_MIN;
80 Scalar left = FLT_MAX;
81 Scalar top = FLT_MAX;
82 Scalar bottom = FLT_MIN;
83 for (int i = 0; i < 6; ++i) {
84 right = std::max(right, data[i].uv.x * texture_size.width);
85 left = std::min(left, data[i].uv.x * texture_size.width);
86 top = std::min(top, data[i].uv.y * texture_size.height);
87 bottom = std::max(bottom, data[i].uv.y * texture_size.height);
88 }
89
90 return Rect::MakeLTRB(left, top, right, bottom);
91 }
92
93 double GetAspectRatio(Rect rect) {
94 return static_cast<double>(rect.GetWidth()) / rect.GetHeight();
95 }
96 } // namespace
97
98 TEST_P(TextContentsTest, SimpleComputeVertexData) {
99 #ifndef FML_OS_MACOSX
100 GTEST_SKIP() << "Results aren't stable across linux and macos.";
101 #endif
102
103 GlyphAtlasPipeline::VertexShader::PerVertexData data[6];
104
105 std::shared_ptr<TextFrame> text_frame =
106 MakeTextFrame("1", "ahem.ttf", /*font_size=*/50);
107
108 std::shared_ptr<TypographerContext> context = TypographerContextSkia::Make();
109 std::shared_ptr<GlyphAtlasContext> atlas_context =
110 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
111 std::shared_ptr<HostBuffer> host_buffer = HostBuffer::Create(
112 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter());
113 ASSERT_TRUE(context && context->IsValid());
114 std::shared_ptr<GlyphAtlas> atlas =
115 CreateGlyphAtlas(*GetContext(), context.get(), *host_buffer,
116 GlyphAtlas::Type::kAlphaBitmap, /*scale=*/1.0f,
117 atlas_context, text_frame);
118
119 ISize texture_size = atlas->GetTexture()->GetSize();
120 TextContents::ComputeVertexData(data, text_frame, /*scale=*/1.0,
121 /*entity_transform=*/Matrix(),
122 /*offset=*/Vector2(0, 0),
123 /*glyph_properties=*/std::nullopt, atlas);
124
125 Rect position_rect = PerVertexDataPositionToRect(data);
126 Rect uv_rect = PerVertexDataUVToRect(data, texture_size);
127 // The -1 offset comes from Skia in `ComputeGlyphSize`. So since the font size
128 // is 50, the math appears to be to get back a 50x50 rect and apply 1 pixel
129 // of padding.
130 EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-1, -41, 52, 52));
131 EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 52, 52));
132 }
133
134 TEST_P(TextContentsTest, SimpleComputeVertexData2x) {
135 #ifndef FML_OS_MACOSX
136 GTEST_SKIP() << "Results aren't stable across linux and macos.";
137 #endif
138
139 GlyphAtlasPipeline::VertexShader::PerVertexData data[6];
140
141 std::shared_ptr<TextFrame> text_frame =
142 MakeTextFrame("1", "ahem.ttf", /*font_size=*/50);
143
144 std::shared_ptr<TypographerContext> context = TypographerContextSkia::Make();
145 std::shared_ptr<GlyphAtlasContext> atlas_context =
146 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
147 std::shared_ptr<HostBuffer> host_buffer = HostBuffer::Create(
148 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter());
149 ASSERT_TRUE(context && context->IsValid());
150 Scalar font_scale = 2.f;
151 std::shared_ptr<GlyphAtlas> atlas = CreateGlyphAtlas(
152 *GetContext(), context.get(), *host_buffer,
153 GlyphAtlas::Type::kAlphaBitmap, font_scale, atlas_context, text_frame);
154
155 ISize texture_size = atlas->GetTexture()->GetSize();
156 TextContents::ComputeVertexData(
157 data, text_frame, font_scale,
158 /*entity_transform=*/Matrix::MakeScale({font_scale, font_scale, 1}),
159 /*offset=*/Vector2(0, 0),
160 /*glyph_properties=*/std::nullopt, atlas);
161
162 Rect position_rect = PerVertexDataPositionToRect(data);
163 Rect uv_rect = PerVertexDataUVToRect(data, texture_size);
164 EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-1, -81, 102, 102));
165 EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 102, 102));
166 }
167
168 TEST_P(TextContentsTest, MaintainsShape) {
169 std::shared_ptr<TextFrame> text_frame =
170 MakeTextFrame("th", "ahem.ttf", /*font_size=*/50);
171
172 std::shared_ptr<TypographerContext> context = TypographerContextSkia::Make();
173 std::shared_ptr<GlyphAtlasContext> atlas_context =
174 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
175 std::shared_ptr<HostBuffer> host_buffer = HostBuffer::Create(
176 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter());
177 ASSERT_TRUE(context && context->IsValid());
178
179 for (int i = 0; i <= 1000; ++i) {
180 Scalar font_scale = 0.440 + (i / 1000.0);
181 Rect position_rect[2];
182 Rect uv_rect[2];
183
184 {
185 GlyphAtlasPipeline::VertexShader::PerVertexData data[12];
186 std::shared_ptr<GlyphAtlas> atlas =
187 CreateGlyphAtlas(*GetContext(), context.get(), *host_buffer,
188 GlyphAtlas::Type::kAlphaBitmap, font_scale,
189 atlas_context, text_frame);
190 ISize texture_size = atlas->GetTexture()->GetSize();
191
192 TextContents::ComputeVertexData(
193 data, text_frame, font_scale,
194 /*entity_transform=*/Matrix::MakeScale({font_scale, font_scale, 1}),
195 /*offset=*/Vector2(0, 0),
196 /*glyph_properties=*/std::nullopt, atlas);
197 position_rect[0] = PerVertexDataPositionToRect(data);
198 uv_rect[0] = PerVertexDataUVToRect(data, texture_size);
199 position_rect[1] = PerVertexDataPositionToRect(data + 6);
200 uv_rect[1] = PerVertexDataUVToRect(data + 6, texture_size);
201 }
202 EXPECT_NEAR(GetAspectRatio(position_rect[1]), GetAspectRatio(uv_rect[1]),
203 0.001)
204 << i;
205 }
206 }
207
208 } // namespace testing
209 } // namespace impeller
type
GLenum type
Definition: blit_command_gles.cc:151
impeller::GlyphAtlas::Type
Type
Describes how the glyphs are represented in the texture.
Definition: glyph_atlas.h:74
impeller::GlyphAtlas::Type::kAlphaBitmap
@ kAlphaBitmap
impeller::HostBuffer::Create
static std::shared_ptr< HostBuffer > Create(const std::shared_ptr< Allocator > &allocator, const std::shared_ptr< const IdleWaiter > &idle_waiter)
Definition: host_buffer.cc:21
impeller::PlaygroundTest
Definition: playground_test.h:22
impeller::TextContents::ComputeVertexData
static void ComputeVertexData(GlyphAtlasPipeline::VertexShader::PerVertexData *vtx_contents, const std::shared_ptr< TextFrame > &frame, Scalar scale, const Matrix &entity_transform, Vector2 offset, std::optional< GlyphProperties > glyph_properties, const std::shared_ptr< GlyphAtlas > &atlas)
Definition: text_contents.cc:96
impeller::TextFrame::RoundScaledFontSize
static Scalar RoundScaledFontSize(Scalar scale)
Definition: text_frame.cc:41
impeller::TypographerContextSkia::Make
static std::shared_ptr< TypographerContext > Make()
Definition: typographer_context_skia.cc:77
geometry_asserts.h
EXPECT_RECT_NEAR
#define EXPECT_RECT_NEAR(a, b)
Definition: geometry_asserts.h:203
impeller::testing::TEST_P
TEST_P(AiksTest, DrawAtlasNoColor)
Definition: aiks_dl_atlas_unittests.cc:57
impeller::testing::CreateGlyphAtlas
static std::shared_ptr< GlyphAtlas > CreateGlyphAtlas(Context &context, const TypographerContext *typographer_context, HostBuffer &host_buffer, GlyphAtlas::Type type, Scalar scale, const std::shared_ptr< GlyphAtlasContext > &atlas_context, const std::shared_ptr< TextFrame > &frame)
Definition: typographer_unittests.cc:32
impeller::testing::INSTANTIATE_PLAYGROUND_SUITE
INSTANTIATE_PLAYGROUND_SUITE(AiksTest)
impeller
Definition: allocation.cc:12
impeller::Vector2
Point Vector2
Definition: point.h:331
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:792
impeller::MakeTextFrameFromTextBlobSkia
std::shared_ptr< TextFrame > MakeTextFrameFromTextBlobSkia(const sk_sp< SkTextBlob > &blob)
Definition: text_frame_skia.cc:42
impeller::ISize
ISize64 ISize
Definition: size.h:174
playground_test.h
scale
const Scalar scale
Definition: stroke_path_geometry.cc:316
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
impeller::TRect< Scalar >
impeller::TRect< Scalar >::MakeXYWH
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
impeller::TSize
Definition: size.h:25
text_contents.h
text_frame_skia.h
data
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:64
typographer_context_skia.h