Flutter Impeller
text_contents.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 
7 #include <cstring>
8 #include <optional>
9 #include <utility>
10 
12 #include "impeller/core/formats.h"
14 #include "impeller/entity/entity.h"
19 
20 namespace impeller {
22  return Point(size.width, size.height);
23 }
24 
27 
28 TextContents::TextContents() = default;
29 
30 TextContents::~TextContents() = default;
31 
32 void TextContents::SetTextFrame(const std::shared_ptr<TextFrame>& frame) {
33  frame_ = frame;
34 }
35 
37  color_ = color;
38 }
39 
41  return color_.WithAlpha(color_.alpha * inherited_opacity_);
42 }
43 
45  inherited_opacity_ = opacity;
46 }
47 
49  offset_ = offset;
50 }
51 
53  force_text_color_ = value;
54 }
55 
56 std::optional<Rect> TextContents::GetCoverage(const Entity& entity) const {
57  return frame_->GetBounds().TransformBounds(entity.GetTransform());
58 }
59 
61  bool stroke,
63  Cap stroke_cap,
64  Join stroke_join,
65  Scalar stroke_miter) {
66  if (frame_->HasColor()) {
67  // Alpha is always applied when rendering, remove it here so
68  // we do not double-apply the alpha.
69  properties_.color = color.WithAlpha(1.0);
70  }
71  if (stroke) {
72  properties_.stroke = true;
73  properties_.stroke_width = stroke_width;
74  properties_.stroke_cap = stroke_cap;
75  properties_.stroke_join = stroke_join;
76  properties_.stroke_miter = stroke_miter;
77  }
78 }
79 
81  VS::PerVertexData* vtx_contents,
82  const std::shared_ptr<TextFrame>& frame,
83  Scalar scale,
84  const Matrix& entity_transform,
86  std::optional<GlyphProperties> glyph_properties,
87  const std::shared_ptr<GlyphAtlas>& atlas) {
88  // Common vertex information for all glyphs.
89  // All glyphs are given the same vertex information in the form of a
90  // unit-sized quad. The size of the glyph is specified in per instance data
91  // and the vertex shader uses this to size the glyph correctly. The
92  // interpolated vertex information is also used in the fragment shader to
93  // sample from the glyph atlas.
94 
95  constexpr std::array<Point, 6> unit_points = {Point{0, 0}, Point{1, 0},
96  Point{0, 1}, Point{1, 0},
97  Point{0, 1}, Point{1, 1}};
98 
99  ISize atlas_size = atlas->GetTexture()->GetSize();
100  bool is_translation_scale = entity_transform.IsTranslationScaleOnly();
101  Matrix basis_transform = entity_transform.Basis();
102 
103  VS::PerVertexData vtx;
104  size_t i = 0u;
105  size_t bounds_offset = 0u;
106  for (const TextRun& run : frame->GetRuns()) {
107  const Font& font = run.GetFont();
108  Scalar rounded_scale = frame->GetScale();
109  const Matrix transform = frame->GetOffsetTransform();
110  FontGlyphAtlas* font_atlas = nullptr;
111 
112  // Adjust glyph position based on the subpixel rounding
113  // used by the font.
114  Point subpixel_adjustment(0.5, 0.5);
115  switch (font.GetAxisAlignment()) {
117  break;
118  case AxisAlignment::kX:
119  subpixel_adjustment.x = 0.125;
120  break;
121  case AxisAlignment::kY:
122  subpixel_adjustment.y = 0.125;
123  break;
124  case AxisAlignment::kAll:
125  subpixel_adjustment.x = 0.125;
126  subpixel_adjustment.y = 0.125;
127  break;
128  }
129 
130  Point screen_offset = (entity_transform * Point(0, 0));
131  for (const TextRun::GlyphPosition& glyph_position :
132  run.GetGlyphPositions()) {
133  const FrameBounds& frame_bounds = frame->GetFrameBounds(bounds_offset);
134  bounds_offset++;
135  auto atlas_glyph_bounds = frame_bounds.atlas_bounds;
136  auto glyph_bounds = frame_bounds.glyph_bounds;
137 
138  // If frame_bounds.is_placeholder is true, this is the first frame
139  // the glyph has been rendered and so its atlas position was not
140  // known when the glyph was recorded. Perform a slow lookup into the
141  // glyph atlas hash table.
142  if (frame_bounds.is_placeholder) {
143  if (!font_atlas) {
144  font_atlas =
145  atlas->GetOrCreateFontGlyphAtlas(ScaledFont{font, rounded_scale});
146  }
147 
148  if (!font_atlas) {
149  VALIDATION_LOG << "Could not find font in the atlas.";
150  continue;
151  }
153  glyph_position, font.GetAxisAlignment(), transform);
154 
155  std::optional<FrameBounds> maybe_atlas_glyph_bounds =
156  font_atlas->FindGlyphBounds(SubpixelGlyph{
157  glyph_position.glyph, //
158  subpixel, //
159  glyph_properties //
160  });
161  if (!maybe_atlas_glyph_bounds.has_value()) {
162  VALIDATION_LOG << "Could not find glyph position in the atlas.";
163  continue;
164  }
165  atlas_glyph_bounds = maybe_atlas_glyph_bounds.value().atlas_bounds;
166  }
167 
168  Rect scaled_bounds = glyph_bounds.Scale(1.0 / rounded_scale);
169  // For each glyph, we compute two rectangles. One for the vertex
170  // positions and one for the texture coordinates (UVs). The atlas
171  // glyph bounds are used to compute UVs in cases where the
172  // destination and source sizes may differ due to clamping the sizes
173  // of large glyphs.
174  Point uv_origin = (atlas_glyph_bounds.GetLeftTop()) / atlas_size;
175  Point uv_size = SizeToPoint(atlas_glyph_bounds.GetSize()) / atlas_size;
176 
177  Matrix unscaled_basis =
178  Matrix::MakeScale({basis_transform.m[0] > 0 ? 1.f : -1.f,
179  basis_transform.m[5] > 0 ? 1.f : -1.f, 1.f});
180  Point unrounded_glyph_position =
181  // This is for RTL text.
182  unscaled_basis * glyph_bounds.GetLeftTop() +
183  (basis_transform * glyph_position.position);
184 
185  Point screen_glyph_position =
186  (screen_offset + unrounded_glyph_position + subpixel_adjustment)
187  .Floor();
188  for (const Point& point : unit_points) {
189  Point position;
190  if (is_translation_scale) {
191  position = (screen_glyph_position +
192  (unscaled_basis * point * glyph_bounds.GetSize()))
193  .Round();
194  } else {
195  position = entity_transform *
196  (glyph_position.position + scaled_bounds.GetLeftTop() +
197  point * scaled_bounds.GetSize());
198  }
199  vtx.uv = uv_origin + (uv_size * point);
200  vtx.position = position;
201  vtx_contents[i++] = vtx;
202  }
203  }
204  }
205 }
206 
208  const Entity& entity,
209  RenderPass& pass) const {
210  auto color = GetColor();
211  if (color.IsTransparent()) {
212  return true;
213  }
214 
215  auto type = frame_->GetAtlasType();
216  const std::shared_ptr<GlyphAtlas>& atlas =
217  renderer.GetLazyGlyphAtlas()->CreateOrGetGlyphAtlas(
218  *renderer.GetContext(), renderer.GetTransientsBuffer(), type);
219 
220  if (!atlas || !atlas->IsValid()) {
221  VALIDATION_LOG << "Cannot render glyphs without prepared atlas.";
222  return false;
223  }
224  if (!frame_->IsFrameComplete()) {
225  VALIDATION_LOG << "Failed to find font glyph bounds.";
226  return false;
227  }
228 
229  // Information shared by all glyph draw calls.
230  pass.SetCommandLabel("TextFrame");
231  auto opts = OptionsFromPassAndEntity(pass, entity);
232  opts.primitive_type = PrimitiveType::kTriangle;
233  pass.SetPipeline(renderer.GetGlyphAtlasPipeline(opts));
234 
235  // Common vertex uniforms for all glyphs.
236  VS::FrameInfo frame_info;
237  frame_info.mvp =
239  bool is_translation_scale = entity.GetTransform().IsTranslationScaleOnly();
240  Matrix entity_transform = entity.GetTransform();
241 
242  VS::BindFrameInfo(pass,
243  renderer.GetTransientsBuffer().EmplaceUniform(frame_info));
244 
245  FS::FragInfo frag_info;
246  frag_info.use_text_color = force_text_color_ ? 1.0 : 0.0;
247  frag_info.text_color = ToVector(color.Premultiply());
248  frag_info.is_color_glyph = type == GlyphAtlas::Type::kColorBitmap;
249 
250  FS::BindFragInfo(pass,
251  renderer.GetTransientsBuffer().EmplaceUniform(frag_info));
252 
253  SamplerDescriptor sampler_desc;
254  if (is_translation_scale) {
255  sampler_desc.min_filter = MinMagFilter::kNearest;
256  sampler_desc.mag_filter = MinMagFilter::kNearest;
257  } else {
258  // Currently, we only propagate the scale of the transform to the atlas
259  // renderer, so if the transform has more than just a translation, we turn
260  // on linear sampling to prevent crunchiness caused by the pixel grid not
261  // being perfectly aligned.
262  // The downside is that this slightly over-blurs rotated/skewed text.
263  sampler_desc.min_filter = MinMagFilter::kLinear;
264  sampler_desc.mag_filter = MinMagFilter::kLinear;
265  }
266 
267  // No mipmaps for glyph atlas (glyphs are generated at exact scales).
268  sampler_desc.mip_filter = MipFilter::kBase;
269 
270  FS::BindGlyphAtlasSampler(
271  pass, // command
272  atlas->GetTexture(), // texture
273  renderer.GetContext()->GetSamplerLibrary()->GetSampler(
274  sampler_desc) // sampler
275  );
276 
277  auto& host_buffer = renderer.GetTransientsBuffer();
278  size_t vertex_count = 0;
279  for (const auto& run : frame_->GetRuns()) {
280  vertex_count += run.GetGlyphPositions().size();
281  }
282  vertex_count *= 6;
283 
284  BufferView buffer_view = host_buffer.Emplace(
285  vertex_count * sizeof(VS::PerVertexData), alignof(VS::PerVertexData),
286  [&](uint8_t* contents) {
287  VS::PerVertexData* vtx_contents =
288  reinterpret_cast<VS::PerVertexData*>(contents);
289  ComputeVertexData(vtx_contents, frame_, scale_,
290  /*entity_transform=*/entity_transform, offset_,
291  GetGlyphProperties(), atlas);
292  });
293 
294  pass.SetVertexBuffer(std::move(buffer_view));
296  pass.SetElementCount(vertex_count);
297 
298  return pass.Draw().ok();
299 }
300 
301 std::optional<GlyphProperties> TextContents::GetGlyphProperties() const {
302  return (properties_.stroke || frame_->HasColor())
303  ? std::optional<GlyphProperties>(properties_)
304  : std::nullopt;
305 }
306 
307 } // namespace impeller
GLenum type
BufferView buffer_view
HostBuffer & GetTransientsBuffer() const
Retrieve the currnent host buffer for transient storage.
const std::shared_ptr< LazyGlyphAtlas > & GetLazyGlyphAtlas() const
PipelineRef GetGlyphAtlasPipeline(ContentContextOptions opts) const
std::shared_ptr< Context > GetContext() const
Matrix GetShaderTransform(const RenderPass &pass) const
Get the vertex shader transform used for drawing this Entity.
Definition: entity.cc:48
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition: entity.cc:44
float GetShaderClipDepth() const
Definition: entity.cc:88
An object that can look up glyph locations within the GlyphAtlas for a particular typeface.
Definition: glyph_atlas.h:269
std::optional< FrameBounds > FindGlyphBounds(const SubpixelGlyph &glyph) const
Find the location of a glyph in the atlas.
Definition: glyph_atlas.cc:131
Describes a typeface along with any modifications to its intrinsic properties.
Definition: font.h:35
AxisAlignment GetAxisAlignment() const
Definition: font.cc:41
BufferView EmplaceUniform(const UniformType &uniform)
Emplace uniform data onto the host buffer. Ensure that backend specific uniform alignment requirement...
Definition: host_buffer.h:48
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:30
virtual bool SetVertexBuffer(VertexBuffer buffer)
Specify the vertex and index buffer to use for this command.
Definition: render_pass.cc:127
virtual bool SetIndexBuffer(BufferView index_buffer, IndexType index_type)
Specify an index buffer to use for this command. To unset the index buffer, pass IndexType::kNone to ...
Definition: render_pass.cc:164
virtual void SetPipeline(PipelineRef pipeline)
The pipeline to use for this command.
Definition: render_pass.cc:86
virtual fml::Status Draw()
Record the currently pending command.
Definition: render_pass.cc:208
virtual void SetElementCount(size_t count)
Definition: render_pass.cc:119
virtual void SetCommandLabel(std::string_view label)
The debugging label to use for the command.
Definition: render_pass.cc:97
FragmentShader_ FragmentShader
Definition: pipeline.h:114
void SetOffset(Vector2 offset)
void SetTextProperties(Color color, bool stroke, Scalar stroke_width, Cap stroke_cap, Join stroke_join, Scalar stroke_miter)
Must be set after text frame.
void SetInheritedOpacity(Scalar opacity) override
Inherit the provided opacity.
void SetForceTextColor(bool value)
Force the text color to apply to the rendered glyphs, even if those glyphs are bitmaps.
bool Render(const ContentContext &renderer, const Entity &entity, RenderPass &pass) const override
std::optional< Rect > GetCoverage(const Entity &entity) const override
Get the area of the render pass that will be affected when this contents is rendered.
void SetTextFrame(const std::shared_ptr< TextFrame > &frame)
void SetColor(Color color)
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)
Color GetColor() const
static Point ComputeSubpixelPosition(const TextRun::GlyphPosition &glyph_position, AxisAlignment alignment, const Matrix &transform)
Definition: text_frame.cc:69
Represents a collection of positioned glyphs from a specific font.
Definition: text_run.h:20
int32_t value
@ kNone
Does not use the index buffer.
Join
Definition: path.h:25
float Scalar
Definition: scalar.h:18
GlyphAtlasPipeline::VertexShader VS
TPoint< Scalar > Point
Definition: point.h:327
Cap
Definition: path.h:19
@ kBase
The texture is sampled as if it only had a single mipmap level.
Point SizeToPoint(Size size)
constexpr Vector4 ToVector(Color color)
Definition: shader_types.h:190
GlyphAtlasPipeline::FragmentShader FS
ContentContextOptions OptionsFromPassAndEntity(const RenderPass &pass, const Entity &entity)
Definition: contents.cc:34
@ kNearest
Select nearest to the sample point. Most widely supported.
Definition: comparable.h:95
SolidFillVertexShader::PerVertexData vtx
const Scalar stroke_width
const Scalar scale
SeparatedVector2 offset
Scalar alpha
Definition: color.h:142
constexpr Color WithAlpha(Scalar new_alpha) const
Definition: color.h:277
Rect atlas_bounds
The bounds of the glyph within the glyph atlas.
Definition: glyph_atlas.h:57
Rect glyph_bounds
The local glyph bounds.
Definition: glyph_atlas.h:59
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
constexpr bool IsTranslationScaleOnly() const
Returns true if the matrix has a scale-only basis and is non-projective. Note that an identity matrix...
Definition: matrix.h:427
Scalar m[16]
Definition: matrix.h:39
constexpr Matrix Basis() const
The Matrix without its w components (without translation).
Definition: matrix.h:239
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
A font and a scale. Used as a key that represents a typeface within a glyph atlas.
A glyph and its subpixel position.
static constexpr TPoint Round(const TPoint< U > &other)
Definition: point.h:49
constexpr TSize< Type > GetSize() const
Returns the size of the rectangle which may be negative in either width or height and may have been c...
Definition: rect.h:331
constexpr TRect Scale(Type scale) const
Definition: rect.h:206
constexpr TPoint< T > GetLeftTop() const
Definition: rect.h:363
Type height
Definition: size.h:29
Type width
Definition: size.h:28
#define VALIDATION_LOG
Definition: validation.h:91