Flutter Impeller
line_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 
8 
9 namespace impeller {
10 
13 
14 namespace {
15 using BindFragmentCallback = std::function<bool(RenderPass& pass)>;
16 using PipelineBuilderCallback =
17  std::function<PipelineRef(ContentContextOptions)>;
18 using CreateGeometryCallback =
19  std::function<GeometryResult(const ContentContext& renderer,
20  const Entity& entity,
21  RenderPass& pass,
22  const Geometry* geometry)>;
23 
24 const int32_t kCurveResolution = 32;
25 
26 uint8_t DoubleToUint8(double x) {
27  return static_cast<uint8_t>(std::clamp(std::round(x * 255.0), 0.0, 255.0));
28 }
29 
30 /// See also: CreateGradientTexture
31 std::shared_ptr<Texture> CreateCurveTexture(
32  Scalar width,
33  Scalar radius,
34  Scalar scale,
35  const std::shared_ptr<impeller::Context>& context) {
36  //
37  impeller::TextureDescriptor texture_descriptor;
39  texture_descriptor.format = PixelFormat::kR8UNormInt;
40  texture_descriptor.size = {kCurveResolution, 1};
41 
42  std::vector<uint8_t> curve_data =
43  LineContents::CreateCurveData(width, radius, scale);
44 
45  return CreateTexture(texture_descriptor, curve_data, context, "LineCurve");
46 }
47 
48 std::pair<LineContents::EffectiveLineParameters, GeometryResult> CreateGeometry(
49  const ContentContext& renderer,
50  const Entity& entity,
51  RenderPass& pass,
52  const Geometry* geometry) {
53  using PerVertexData = LineVertexShader::PerVertexData;
54  const LineGeometry* line_geometry =
55  static_cast<const LineGeometry*>(geometry);
56 
57  auto& transform = entity.GetTransform();
58  auto& data_host_buffer = renderer.GetTransientsDataBuffer();
59 
60  size_t count = 4;
61  fml::StatusOr<LineContents::EffectiveLineParameters> calculate_status =
63  BufferView vertex_buffer = data_host_buffer.Emplace(
64  count * sizeof(PerVertexData), alignof(PerVertexData),
65  [line_geometry, &transform, &calculate_status](uint8_t* buffer) {
66  auto vertices = reinterpret_cast<PerVertexData*>(buffer);
67  calculate_status = LineContents::CalculatePerVertex(
68  vertices, line_geometry, transform);
69  });
70  if (!calculate_status.ok()) {
71  return std::make_pair(
73  .width = line_geometry->GetWidth(),
74  .radius = LineContents::kSampleRadius},
75  kEmptyResult);
76  }
77 
78  // We do the math in CalculatePerVertex in unrotated space. This then applies
79  // the rotation to the line.
80  Point diff = line_geometry->GetP1() - line_geometry->GetP0();
81  Scalar angle = std::atan2(diff.y, diff.x);
82  Entity rotated_entity = entity.Clone();
83  Matrix matrix = entity.GetTransform();
84  matrix = matrix * Matrix::MakeTranslation(line_geometry->GetP0()) *
86  Matrix::MakeTranslation(-1 * line_geometry->GetP0());
87  rotated_entity.SetTransform(matrix);
88 
89  return std::make_pair(
90  calculate_status.value(),
92  .type = PrimitiveType::kTriangleStrip,
93  .vertex_buffer =
94  {
95  .vertex_buffer = vertex_buffer,
96  .vertex_count = count,
97  .index_type = IndexType::kNone,
98  },
99  .transform = rotated_entity.GetShaderTransform(pass),
100  });
101 }
102 
103 struct LineInfo {
104  Vector3 e0;
105  Vector3 e1;
106  Vector3 e2;
107  Vector3 e3;
108 };
109 
110 LineInfo CalculateLineInfo(Point p0, Point p1, Scalar width, Scalar radius) {
111  Vector2 diff = p0 - p1;
112  float k = 2.0 / ((2.0 * radius + width) * sqrt(diff.Dot(diff)));
113 
114  return LineInfo{
115  .e0 = Vector3(k * (p0.y - p1.y), //
116  k * (p1.x - p0.x), //
117  1.0 + k * (p0.x * p1.y - p1.x * p0.y)),
118  .e1 = Vector3(
119  k * (p1.x - p0.x), //
120  k * (p1.y - p0.y), //
121  1.0 + k * (p0.x * p0.x + p0.y * p0.y - p0.x * p1.x - p0.y * p1.y)),
122  .e2 = Vector3(k * (p1.y - p0.y), //
123  k * (p0.x - p1.x), //
124  1.0 + k * (p1.x * p0.y - p0.x * p1.y)),
125  .e3 = Vector3(
126  k * (p0.x - p1.x), //
127  k * (p0.y - p1.y), //
128  1.0 + k * (p1.x * p1.x + p1.y * p1.y - p0.x * p1.x - p0.y * p1.y)),
129  };
130 }
131 } // namespace
132 
133 const Scalar LineContents::kSampleRadius = 1.f;
134 
135 std::unique_ptr<LineContents> LineContents::Make(
136  std::unique_ptr<LineGeometry> geometry,
137  Color color) {
138  return std::unique_ptr<LineContents>(
139  new LineContents(std::move(geometry), color));
140 }
141 
142 LineContents::LineContents(std::unique_ptr<LineGeometry> geometry, Color color)
143  : geometry_(std::move(geometry)), color_(color) {}
144 
146  const Entity& entity,
147  RenderPass& pass) const {
148  auto& data_host_buffer = renderer.GetTransientsDataBuffer();
149 
150  VS::FrameInfo frame_info;
151  FS::FragInfo frag_info;
152  frag_info.color = color_;
153  frag_info.cap_type = (geometry_->GetCap() == Cap::kRound) ? 1.0f : 0.0f;
154 
155  Scalar scale = entity.GetTransform().GetMaxBasisLengthXY();
156 
157  auto geometry_result =
158  CreateGeometry(renderer, entity, pass, geometry_.get());
159 
160  std::shared_ptr<Texture> curve_texture = CreateCurveTexture(
161  geometry_->GetWidth(), kSampleRadius, scale, renderer.GetContext());
162 
163  SamplerDescriptor sampler_desc;
164  sampler_desc.min_filter = MinMagFilter::kLinear;
165  sampler_desc.mag_filter = MinMagFilter::kLinear;
166 
167  FS::BindCurve(
168  pass, curve_texture,
169  renderer.GetContext()->GetSamplerLibrary()->GetSampler(sampler_desc));
170 
171  PipelineBuilderCallback pipeline_callback =
172  [&renderer](ContentContextOptions options) {
173  return renderer.GetLinePipeline(options);
174  };
175 
176  return ColorSourceContents::DrawGeometry<VS>(
177  this, geometry_.get(), renderer, entity, pass, pipeline_callback,
178  frame_info,
179  /*bind_fragment_callback=*/
180  [&frag_info, &data_host_buffer](RenderPass& pass) {
181  FS::BindFragInfo(pass, data_host_buffer.EmplaceUniform(frag_info));
182  pass.SetCommandLabel("Line");
183  return true;
184  },
185  /*force_stencil=*/false,
186  /*create_geom_callback=*/
187  [geometry_result = std::move(geometry_result)](
188  const ContentContext& renderer, const Entity& entity,
189  RenderPass& pass,
190  const Geometry* geometry) { return geometry_result.second; });
191 }
192 
193 std::optional<Rect> LineContents::GetCoverage(const Entity& entity) const {
194  return geometry_->GetCoverage(entity.GetTransform());
195 }
196 
197 std::vector<uint8_t> LineContents::CreateCurveData(Scalar width,
198  Scalar radius,
199  Scalar scale) {
200  std::vector<uint8_t> curve_data;
201  curve_data.reserve(kCurveResolution);
202  // More simply written as rise / run:
203  // double slope = 1.0 / ((radius * 2) / (scale * width + radius));
204  double slope = (scale * width + radius) / (radius * 2);
205  slope = std::max(slope, 1.0);
206  for (int i = 0; i < kCurveResolution; ++i) {
207  double norm =
208  (static_cast<double>(i)) / static_cast<double>(kCurveResolution - 1);
209  double scaled = slope * norm;
210  curve_data.push_back(DoubleToUint8(scaled));
211  }
212  return curve_data;
213 }
214 
215 namespace {
216 void ExpandLine(std::array<Point, 4>& corners, Point expansion) {
217  Point along = (corners[1] - corners[0]).Normalize();
218  Point across = (corners[2] - corners[0]).Normalize();
219  corners[0] += -1 * (across * expansion.x) + -1 * (along * expansion.y);
220  corners[1] += -1 * (across * expansion.x) + (along * expansion.y);
221  corners[2] += (across * expansion.x) + -1 * (along * expansion.y);
222  corners[3] += (across * expansion.x) + (along * expansion.y);
223 }
224 } // namespace
225 
226 fml::StatusOr<LineContents::EffectiveLineParameters>
227 LineContents::CalculatePerVertex(LineVertexShader::PerVertexData* per_vertex,
228  const LineGeometry* geometry,
229  const Matrix& entity_transform) {
230  Scalar scale = entity_transform.GetMaxBasisLengthXY();
231 
232  // Transform the line into unrotated space by rotating p1 to be horizontal
233  // with p0. We do this because there seems to be a flaw in the eN calculations
234  // where they create thinner lines for diagonal lines.
235  Point diff = geometry->GetP1() - geometry->GetP0();
236  Scalar magnitude = diff.GetLength();
237  Point p1_prime = Point(geometry->GetP0().x + magnitude, geometry->GetP0().y);
238 
239  std::array<Point, 4> corners;
240  // Make sure we get kSampleRadius pixels to sample from.
241  Scalar expand_size = std::max(kSampleRadius / scale, kSampleRadius);
243  corners.data(), entity_transform,
244  /*extend_endpoints=*/geometry->GetCap() != Cap::kButt,
245  geometry->GetP0(), p1_prime, geometry->GetWidth())) {
246  return fml::Status(fml::StatusCode::kAborted, "No valid corners");
247  }
248  Scalar effective_line_width = std::fabsf((corners[2] - corners[0]).y);
249  ExpandLine(corners, Point(expand_size, expand_size));
250  Scalar padded_line_width = std::fabsf((corners[2] - corners[0]).y);
251  Scalar effective_sample_radius =
252  (padded_line_width - effective_line_width) / 2.f;
253  LineInfo line_info =
254  CalculateLineInfo(geometry->GetP0(), p1_prime, effective_line_width,
255  effective_sample_radius);
256  for (auto& corner : corners) {
257  *per_vertex++ = {
258  .position = corner,
259  .e0 = line_info.e0,
260  .e1 = line_info.e1,
261  .e2 = line_info.e2,
262  .e3 = line_info.e3,
263  };
264  }
265 
266  return EffectiveLineParameters{.width = effective_line_width,
267  .radius = effective_sample_radius};
268 }
269 } // namespace impeller
PipelineRef GetLinePipeline(ContentContextOptions opts) const
HostBuffer & GetTransientsDataBuffer() const
Retrieve the current host buffer for transient storage of other non-index data.
std::shared_ptr< Context > GetContext() const
void SetTransform(const Matrix &transform)
Set the global transform matrix for this Entity.
Definition: entity.cc:62
Matrix GetShaderTransform(const RenderPass &pass) const
Definition: entity.cc:50
Entity Clone() const
Definition: entity.cc:159
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition: entity.cc:46
static std::vector< uint8_t > CreateCurveData(Scalar width, Scalar radius, Scalar scale)
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.
static const Scalar kSampleRadius
Definition: line_contents.h:17
static fml::StatusOr< EffectiveLineParameters > CalculatePerVertex(LineVertexShader::PerVertexData *per_vertex, const LineGeometry *geometry, const Matrix &entity_transform)
bool Render(const ContentContext &renderer, const Entity &entity, RenderPass &pass) const override
Scalar GetWidth() const
Definition: line_geometry.h:33
static bool ComputeCorners(Point corners[4], const Matrix &transform, bool extend_endpoints, Point p0, Point p1, Scalar width)
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:30
FragmentShader_ FragmentShader
Definition: pipeline.h:169
int32_t x
Vector3 e0
Vector3 e3
Vector3 e2
Vector3 e1
Point Vector2
Definition: point.h:430
float Scalar
Definition: scalar.h:19
TPoint< Scalar > Point
Definition: point.h:426
raw_ptr< Pipeline< PipelineDescriptor > > PipelineRef
A raw ptr to a pipeline object.
Definition: pipeline.h:89
LinePipeline::FragmentShader FS
static const GeometryResult kEmptyResult
Definition: geometry.h:43
LinePipeline::VertexShader VS
std::shared_ptr< Texture > CreateTexture(const TextureDescriptor &texture_descriptor, const std::vector< uint8_t > &data, const std::shared_ptr< impeller::Context > &context, std::string_view debug_label)
Definition: texture_util.cc:11
Definition: comparable.h:93
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:223
Scalar GetMaxBasisLengthXY() const
Return the maximum scale applied specifically to either the X axis or Y axis unit vectors (the bases)...
Definition: matrix.h:328
constexpr Scalar GetLength() const
Definition: point.h:210
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...