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 
6 #include "flutter/impeller/renderer/testing/mocks.h"
7 #include "flutter/testing/testing.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 
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,
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();
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 
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
GLenum type
Type
Describes how the glyphs are represented in the texture.
Definition: glyph_atlas.h:74
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
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)
static Scalar RoundScaledFontSize(Scalar scale)
Definition: text_frame.cc:41
static std::shared_ptr< TypographerContext > Make()
#define EXPECT_RECT_NEAR(a, b)
TEST_P(AiksTest, DrawAtlasNoColor)
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)
INSTANTIATE_PLAYGROUND_SUITE(AiksTest)
Point Vector2
Definition: point.h:331
float Scalar
Definition: scalar.h:18
TRect< Scalar > Rect
Definition: rect.h:792
std::shared_ptr< TextFrame > MakeTextFrameFromTextBlobSkia(const sk_sp< SkTextBlob > &blob)
ISize64 ISize
Definition: size.h:174
const Scalar scale
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:64