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"
15 #include "impeller/entity/entity.h"
21 
22 namespace impeller {
23 
24 TextContents::TextContents() = default;
25 
26 TextContents::~TextContents() = default;
27 
28 void TextContents::SetTextFrame(const std::shared_ptr<TextFrame>& frame) {
29  frame_ = frame;
30 }
31 
33  color_ = color;
34 }
35 
37  return color_.WithAlpha(color_.alpha * inherited_opacity_);
38 }
39 
40 bool TextContents::CanInheritOpacity(const Entity& entity) const {
41  // Computing whether or not opacity can be inherited requires determining if
42  // any glyphs can overlap exactly. While this was previously implemented
43  // via TextFrame::MaybeHasOverlapping, this code relied on scaling up text
44  // bounds for a size specified at 1.0 DPR, which was not accurate at
45  // higher or lower DPRs. Rather than re-implement the checks to compute exact
46  // glyph bounds, for now this optimization has been disabled for Text.
47  return false;
48 }
49 
51  inherited_opacity_ = opacity;
52 }
53 
55  offset_ = offset;
56 }
57 
59  force_text_color_ = value;
60 }
61 
62 std::optional<Rect> TextContents::GetCoverage(const Entity& entity) const {
63  return frame_->GetBounds().TransformBounds(entity.GetTransform());
64 }
65 
67  const std::shared_ptr<LazyGlyphAtlas>& lazy_glyph_atlas,
68  Scalar scale) {
69  lazy_glyph_atlas->AddTextFrame(*frame_, scale, offset_, properties_);
70  scale_ = scale;
71 }
72 
74  bool stroke,
76  Cap stroke_cap,
77  Join stroke_join,
78  Scalar stroke_miter) {
79  if (frame_->HasColor()) {
80  // Alpha is always applied when rendering, remove it here so
81  // we do not double-apply the alpha.
82  properties_.color = color.WithAlpha(1.0);
83  }
84  if (stroke) {
85  properties_.stroke = true;
86  properties_.stroke_width = stroke_width;
87  properties_.stroke_cap = stroke_cap;
88  properties_.stroke_join = stroke_join;
89  properties_.stroke_miter = stroke_miter;
90  }
91 }
92 
93 bool TextContents::Render(const ContentContext& renderer,
94  const Entity& entity,
95  RenderPass& pass) const {
96  auto color = GetColor();
97  if (color.IsTransparent()) {
98  return true;
99  }
100 
101  auto type = frame_->GetAtlasType();
102  const std::shared_ptr<GlyphAtlas>& atlas =
103  renderer.GetLazyGlyphAtlas()->CreateOrGetGlyphAtlas(
104  *renderer.GetContext(), renderer.GetTransientsBuffer(), type);
105 
106  if (!atlas || !atlas->IsValid()) {
107  VALIDATION_LOG << "Cannot render glyphs without prepared atlas.";
108  return false;
109  }
110 
111  // Information shared by all glyph draw calls.
112  pass.SetCommandLabel("TextFrame");
113  auto opts = OptionsFromPassAndEntity(pass, entity);
114  opts.primitive_type = PrimitiveType::kTriangle;
115  pass.SetPipeline(renderer.GetGlyphAtlasPipeline(opts));
116 
119 
120  // Common vertex uniforms for all glyphs.
121  VS::FrameInfo frame_info;
122  frame_info.mvp =
124  ISize atlas_size = atlas->GetTexture()->GetSize();
125  bool is_translation_scale = entity.GetTransform().IsTranslationScaleOnly();
126  Matrix entity_transform = entity.GetTransform();
127  Matrix basis_transform = entity_transform.Basis();
128 
129  VS::BindFrameInfo(pass,
130  renderer.GetTransientsBuffer().EmplaceUniform(frame_info));
131 
132  FS::FragInfo frag_info;
133  frag_info.use_text_color = force_text_color_ ? 1.0 : 0.0;
134  frag_info.text_color = ToVector(color.Premultiply());
135  frag_info.is_color_glyph = type == GlyphAtlas::Type::kColorBitmap;
136 
137  FS::BindFragInfo(pass,
138  renderer.GetTransientsBuffer().EmplaceUniform(frag_info));
139 
140  SamplerDescriptor sampler_desc;
141  if (is_translation_scale) {
142  sampler_desc.min_filter = MinMagFilter::kNearest;
143  sampler_desc.mag_filter = MinMagFilter::kNearest;
144  } else {
145  // Currently, we only propagate the scale of the transform to the atlas
146  // renderer, so if the transform has more than just a translation, we turn
147  // on linear sampling to prevent crunchiness caused by the pixel grid not
148  // being perfectly aligned.
149  // The downside is that this slightly over-blurs rotated/skewed text.
150  sampler_desc.min_filter = MinMagFilter::kLinear;
151  sampler_desc.mag_filter = MinMagFilter::kLinear;
152  }
153 
154  // No mipmaps for glyph atlas (glyphs are generated at exact scales).
155  sampler_desc.mip_filter = MipFilter::kBase;
156 
157  FS::BindGlyphAtlasSampler(
158  pass, // command
159  atlas->GetTexture(), // texture
160  renderer.GetContext()->GetSamplerLibrary()->GetSampler(
161  sampler_desc) // sampler
162  );
163 
164  // Common vertex information for all glyphs.
165  // All glyphs are given the same vertex information in the form of a
166  // unit-sized quad. The size of the glyph is specified in per instance data
167  // and the vertex shader uses this to size the glyph correctly. The
168  // interpolated vertex information is also used in the fragment shader to
169  // sample from the glyph atlas.
170 
171  constexpr std::array<Point, 6> unit_points = {Point{0, 0}, Point{1, 0},
172  Point{0, 1}, Point{1, 0},
173  Point{0, 1}, Point{1, 1}};
174 
175  auto& host_buffer = renderer.GetTransientsBuffer();
176  size_t vertex_count = 0;
177  for (const auto& run : frame_->GetRuns()) {
178  vertex_count += run.GetGlyphPositions().size();
179  }
180  vertex_count *= 6;
181 
182  BufferView buffer_view = host_buffer.Emplace(
183  vertex_count * sizeof(VS::PerVertexData), alignof(VS::PerVertexData),
184  [&](uint8_t* contents) {
185  VS::PerVertexData vtx;
186  VS::PerVertexData* vtx_contents =
187  reinterpret_cast<VS::PerVertexData*>(contents);
188  size_t i = 0u;
189  for (const TextRun& run : frame_->GetRuns()) {
190  const Font& font = run.GetFont();
191  Scalar rounded_scale = TextFrame::RoundScaledFontSize(
192  scale_, font.GetMetrics().point_size);
193  const FontGlyphAtlas* font_atlas =
194  atlas->GetFontGlyphAtlas(font, rounded_scale);
195  if (!font_atlas) {
196  VALIDATION_LOG << "Could not find font in the atlas.";
197  continue;
198  }
199 
200  // Adjust glyph position based on the subpixel rounding
201  // used by the font.
202  Point subpixel_adjustment(0.5, 0.5);
203  switch (font.GetAxisAlignment()) {
205  break;
206  case AxisAlignment::kX:
207  subpixel_adjustment.x = 0.125;
208  break;
209  case AxisAlignment::kY:
210  subpixel_adjustment.y = 0.125;
211  break;
212  case AxisAlignment::kAll:
213  subpixel_adjustment.x = 0.125;
214  subpixel_adjustment.y = 0.125;
215  break;
216  }
217 
218  Point screen_offset = (entity_transform * Point(0, 0));
219  for (const TextRun::GlyphPosition& glyph_position :
220  run.GetGlyphPositions()) {
221  // Note: uses unrounded scale for more accurate subpixel position.
223  glyph_position, font.GetAxisAlignment(), offset_, scale_);
224  std::optional<std::pair<Rect, Rect>> maybe_atlas_glyph_bounds =
225  font_atlas->FindGlyphBounds(
226  SubpixelGlyph{glyph_position.glyph, subpixel, properties_});
227  if (!maybe_atlas_glyph_bounds.has_value()) {
228  VALIDATION_LOG << "Could not find glyph position in the atlas.";
229  continue;
230  }
231  const Rect& atlas_glyph_bounds =
232  maybe_atlas_glyph_bounds.value().first;
233  Rect glyph_bounds = maybe_atlas_glyph_bounds.value().second;
234  Rect scaled_bounds = glyph_bounds.Scale(1.0 / rounded_scale);
235  // For each glyph, we compute two rectangles. One for the vertex
236  // positions and one for the texture coordinates (UVs). The atlas
237  // glyph bounds are used to compute UVs in cases where the
238  // destination and source sizes may differ due to clamping the sizes
239  // of large glyphs.
240  Point uv_origin =
241  (atlas_glyph_bounds.GetLeftTop() - Point(0.5, 0.5)) /
242  atlas_size;
243  Point uv_size =
244  (atlas_glyph_bounds.GetSize() + Point(1, 1)) / atlas_size;
245 
246  Point unrounded_glyph_position =
247  basis_transform *
248  (glyph_position.position + scaled_bounds.GetLeftTop());
249 
250  Point screen_glyph_position =
251  (screen_offset + unrounded_glyph_position + subpixel_adjustment)
252  .Floor();
253 
254  for (const Point& point : unit_points) {
255  Point position;
256  if (is_translation_scale) {
257  position = (screen_glyph_position +
258  (basis_transform * point * scaled_bounds.GetSize()))
259  .Round();
260  } else {
261  position = entity_transform * (glyph_position.position +
262  scaled_bounds.GetLeftTop() +
263  point * scaled_bounds.GetSize());
264  }
265  vtx.uv = uv_origin + (uv_size * point);
266  vtx.position = position;
267  vtx_contents[i++] = vtx;
268  }
269  }
270  }
271  });
272 
273  pass.SetVertexBuffer({
274  .vertex_buffer = std::move(buffer_view),
275  .index_buffer = {},
276  .vertex_count = vertex_count,
277  .index_type = IndexType::kNone,
278  });
279 
280  return pass.Draw().ok();
281 }
282 
283 } // namespace impeller
text_contents.h
impeller::GlyphAtlas::Type::kColorBitmap
@ kColorBitmap
impeller::RenderPipelineHandle::FragmentShader
FragmentShader_ FragmentShader
Definition: pipeline.h:107
impeller::Entity::GetShaderTransform
Matrix GetShaderTransform(const RenderPass &pass) const
Get the vertex shader transform used for drawing this Entity.
Definition: entity.cc:50
impeller::TextContents::CanInheritOpacity
bool CanInheritOpacity(const Entity &entity) const override
Whether or not this contents can accept the opacity peephole optimization.
Definition: text_contents.cc:40
lazy_glyph_atlas.h
impeller::AxisAlignment::kAll
@ kAll
point.h
impeller::Entity::GetShaderClipDepth
float GetShaderClipDepth() const
Definition: entity.cc:106
impeller::TPoint::y
Type y
Definition: point.h:31
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::Font
Describes a typeface along with any modifications to its intrinsic properties.
Definition: font.h:35
impeller::ContentContext::GetLazyGlyphAtlas
const std::shared_ptr< LazyGlyphAtlas > & GetLazyGlyphAtlas() const
Definition: content_context.h:720
impeller::Entity::GetTransform
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition: entity.cc:46
entity.h
impeller::Color
Definition: color.h:124
vtx
SolidFillVertexShader::PerVertexData vtx
Definition: stroke_path_geometry.cc:312
impeller::FontGlyphAtlas
An object that can look up glyph locations within the GlyphAtlas for a particular typeface.
Definition: glyph_atlas.h:200
impeller::TextContents::SetForceTextColor
void SetForceTextColor(bool value)
Force the text color to apply to the rendered glyphs, even if those glyphs are bitmaps.
Definition: text_contents.cc:58
impeller::TextFrame::ComputeSubpixelPosition
static Point ComputeSubpixelPosition(const TextRun::GlyphPosition &glyph_position, AxisAlignment alignment, Point offset, Scalar scale)
Definition: text_frame.cc:68
impeller::TextContents::SetTextProperties
void SetTextProperties(Color color, bool stroke, Scalar stroke_width, Cap stroke_cap, Join stroke_join, Scalar stroke_miter)
Must be set after text frame.
Definition: text_contents.cc:73
impeller::SubpixelGlyph
A glyph and its subpixel position.
Definition: font_glyph_pair.h:40
impeller::TextContents::GetCoverage
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.
Definition: text_contents.cc:62
formats.h
impeller::TRect::GetLeftTop
constexpr TPoint< T > GetLeftTop() const
Definition: rect.h:349
impeller::TPoint::Round
static constexpr TPoint Round(const TPoint< U > &other)
Definition: point.h:49
impeller::Color::alpha
Scalar alpha
Definition: color.h:143
impeller::RenderPass::SetVertexBuffer
virtual bool SetVertexBuffer(VertexBuffer buffer)
Specify the vertex and index buffer to use for this command.
Definition: render_pass.cc:123
buffer_view
BufferView buffer_view
Definition: blit_command_gles.cc:127
impeller::GlyphProperties::stroke_miter
Scalar stroke_miter
Definition: font_glyph_pair.h:24
impeller::TextFrame::RoundScaledFontSize
static Scalar RoundScaledFontSize(Scalar scale, Scalar point_size)
Definition: text_frame.cc:40
impeller::TextRun
Represents a collection of positioned glyphs from a specific font.
Definition: text_run.h:20
impeller::RenderPass::SetCommandLabel
virtual void SetCommandLabel(std::string_view label)
The debugging label to use for the command.
Definition: render_pass.cc:97
impeller::TextContents::SetColor
void SetColor(Color color)
Definition: text_contents.cc:32
stroke_width
const Scalar stroke_width
Definition: stroke_path_geometry.cc:304
offset
SeparatedVector2 offset
Definition: stroke_path_geometry.cc:311
impeller::GlyphProperties::color
Color color
Definition: font_glyph_pair.h:20
impeller::RenderPass::Draw
virtual fml::Status Draw()
Record the currently pending command.
Definition: render_pass.cc:127
impeller::MinMagFilter::kNearest
@ kNearest
Select nearest to the sample point. Most widely supported.
impeller::SamplerDescriptor::mag_filter
MinMagFilter mag_filter
Definition: sampler_descriptor.h:17
impeller::VS
SolidFillVertexShader VS
Definition: stroke_path_geometry.cc:16
impeller::SamplerDescriptor
Definition: sampler_descriptor.h:15
impeller::PrimitiveType::kTriangle
@ kTriangle
impeller::Entity
Definition: entity.h:20
impeller::OptionsFromPassAndEntity
ContentContextOptions OptionsFromPassAndEntity(const RenderPass &pass, const Entity &entity)
Definition: contents.cc:34
impeller::Matrix::Basis
constexpr Matrix Basis() const
The Matrix without its w components (without translation).
Definition: matrix.h:229
impeller::TSize
Definition: size.h:19
impeller::SamplerDescriptor::min_filter
MinMagFilter min_filter
Definition: sampler_descriptor.h:16
impeller::Point
TPoint< Scalar > Point
Definition: point.h:322
render_pass.h
impeller::TextContents::PopulateGlyphAtlas
void PopulateGlyphAtlas(const std::shared_ptr< LazyGlyphAtlas > &lazy_glyph_atlas, Scalar scale) override
Add any text data to the specified lazy atlas. The scale parameter must be used again later when draw...
Definition: text_contents.cc:66
impeller::ContentContext::GetContext
std::shared_ptr< Context > GetContext() const
Definition: content_context.cc:553
impeller::MinMagFilter::kLinear
@ kLinear
impeller::Color::WithAlpha
constexpr Color WithAlpha(Scalar new_alpha) const
Definition: color.h:280
impeller::Matrix::IsTranslationScaleOnly
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:392
impeller::RenderPipelineHandle::VertexShader
VertexShader_ VertexShader
Definition: pipeline.h:106
type
GLenum type
Definition: blit_command_gles.cc:126
impeller::IndexType::kNone
@ kNone
Does not use the index buffer.
impeller::TRect::Scale
constexpr TRect Scale(Type scale) const
Definition: rect.h:192
impeller::SubpixelGlyph::glyph
Glyph glyph
Definition: font_glyph_pair.h:41
impeller::Font::Metrics::point_size
Scalar point_size
Definition: font.h:48
impeller::GlyphProperties::stroke_join
Join stroke_join
Definition: font_glyph_pair.h:23
impeller::GlyphProperties::stroke
bool stroke
Definition: font_glyph_pair.h:25
impeller::AxisAlignment::kX
@ kX
impeller::TextContents::~TextContents
~TextContents()
impeller::Font::GetMetrics
const Metrics & GetMetrics() const
Definition: font.cc:45
impeller::TextContents::SetOffset
void SetOffset(Vector2 offset)
Definition: text_contents.cc:54
impeller::SamplerDescriptor::mip_filter
MipFilter mip_filter
Definition: sampler_descriptor.h:18
impeller::TPoint::x
Type x
Definition: point.h:30
impeller::GlyphProperties::stroke_width
Scalar stroke_width
Definition: font_glyph_pair.h:21
impeller::RenderPass
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:33
sampler_descriptor.h
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:73
content_context.h
impeller::BufferView
Definition: buffer_view.h:15
impeller::TRect::GetSize
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:317
impeller::FontGlyphAtlas::FindGlyphBounds
std::optional< std::pair< Rect, Rect > > FindGlyphBounds(const SubpixelGlyph &glyph) const
Find the location of a glyph in the atlas.
Definition: glyph_atlas.cc:119
impeller::GlyphProperties::stroke_cap
Cap stroke_cap
Definition: font_glyph_pair.h:22
impeller::Join
Join
Definition: path.h:24
impeller::MipFilter::kBase
@ kBase
The texture is sampled as if it only had a single mipmap level.
impeller::TextContents::TextContents
TextContents()
impeller::TextContents::SetInheritedOpacity
void SetInheritedOpacity(Scalar opacity) override
Inherit the provided opacity.
Definition: text_contents.cc:50
impeller::ToVector
constexpr Vector4 ToVector(Color color)
Definition: shader_types.h:185
buffer_view.h
impeller::TPoint< Scalar >
impeller::TextRun::GlyphPosition
Definition: text_run.h:22
impeller::HostBuffer::EmplaceUniform
BufferView EmplaceUniform(const UniformType &uniform)
Emplace uniform data onto the host buffer. Ensure that backend specific uniform alignment requirement...
Definition: host_buffer.h:50
impeller::TextContents::Render
bool Render(const ContentContext &renderer, const Entity &entity, RenderPass &pass) const override
Definition: text_contents.cc:93
scale
const Scalar scale
Definition: stroke_path_geometry.cc:308
impeller::TextContents::GetColor
Color GetColor() const
Definition: text_contents.cc:36
color.h
color
DlColor color
Definition: dl_golden_blur_unittests.cc:23
impeller::RenderPass::SetPipeline
virtual void SetPipeline(const std::shared_ptr< Pipeline< PipelineDescriptor >> &pipeline)
The pipeline to use for this command.
Definition: render_pass.cc:92
impeller::TextContents::SetTextFrame
void SetTextFrame(const std::shared_ptr< TextFrame > &frame)
Definition: text_contents.cc:28
glyph_atlas.h
impeller::AxisAlignment::kNone
@ kNone
impeller::ContentContext::GetGlyphAtlasPipeline
std::shared_ptr< Pipeline< PipelineDescriptor > > GetGlyphAtlasPipeline(ContentContextOptions opts) const
Definition: content_context.h:501
impeller
Definition: aiks_blend_unittests.cc:18
stroke
bool stroke
Definition: dl_golden_blur_unittests.cc:21
impeller::ContentContext
Definition: content_context.h:366
impeller::TRect< Scalar >
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::ContentContext::GetTransientsBuffer
HostBuffer & GetTransientsBuffer() const
Retrieve the currnent host buffer for transient storage.
Definition: content_context.h:752
impeller::Cap
Cap
Definition: path.h:18
impeller::Font::GetAxisAlignment
AxisAlignment GetAxisAlignment() const
Definition: font.cc:41
impeller::AxisAlignment::kY
@ kY