| 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
5 #include "impeller/entity/contents/line_contents.h"
6 #include "impeller/entity/contents/clip_contents.h"
7 #include "impeller/entity/contents/color_source_contents.h"
8 #include "impeller/entity/geometry/rect_geometry.h"
9 #include "impeller/renderer/texture_util.h"
10
11 namespace impeller {
12
13 using VS = LinePipeline::VertexShader;
14 using FS = LinePipeline::FragmentShader;
15
16 namespace {
17 using BindFragmentCallback = std::function<bool(RenderPass& pass)>;
18 using PipelineBuilderCallback =
19 std::function<PipelineRef(ContentContextOptions)>;
20 using CreateGeometryCallback =
21 std::function<GeometryResult(const ContentContext& renderer,
22 const Entity& entity,
23 RenderPass& pass,
24 const Geometry* geometry)>;
25
26 const int32_t kCurveResolution = 32;
27
28 uint8_t DoubleToUint8(double x) {
29 return static_cast<uint8_t>(std::clamp(std::round(x * 255.0), 0.0, 255.0));
30 }
31
32 /// See also: CreateGradientTexture
33 std::shared_ptr<Texture> CreateCurveTexture(
34 Scalar width,
35 Scalar radius,
36 Scalar scale,
37 const std::shared_ptr<impeller::Context>& context) {
38 //
39 impeller::TextureDescriptor texture_descriptor;
40 texture_descriptor.storage_mode = impeller::StorageMode::kHostVisible;
41 texture_descriptor.format = PixelFormat::kR8UNormInt;
42 texture_descriptor.size = {kCurveResolution, 1};
43
44 std::vector<uint8_t> curve_data =
45 LineContents::CreateCurveData(width, radius, scale);
46
47 return CreateTexture(texture_descriptor, curve_data, context, "LineCurve");
48 }
49
50 std::pair<LineContents::EffectiveLineParameters, GeometryResult> CreateGeometry(
51 const ContentContext& renderer,
52 const Entity& entity,
53 RenderPass& pass,
54 const Geometry* geometry) {
55 using PerVertexData = LineVertexShader::PerVertexData;
56 const LineGeometry* line_geometry =
57 static_cast<const LineGeometry*>(geometry);
58
59 auto& transform = entity.GetTransform();
60 auto& host_buffer = renderer.GetTransientsBuffer();
61
62 size_t count = 4;
63 fml::StatusOr<LineContents::EffectiveLineParameters> calculate_status =
64 LineContents::EffectiveLineParameters{.width = 0, .radius = 0};
65 BufferView vertex_buffer = host_buffer.Emplace(
66 count * sizeof(PerVertexData), alignof(PerVertexData),
67 [line_geometry, &transform, &calculate_status](uint8_t* buffer) {
68 auto vertices = reinterpret_cast<PerVertexData*>(buffer);
69 calculate_status = LineContents::CalculatePerVertex(
70 vertices, line_geometry, transform);
71 });
72 if (!calculate_status.ok()) {
73 return std::make_pair(
74 LineContents::EffectiveLineParameters{
75 .width = line_geometry->GetWidth(),
76 .radius = LineContents::kSampleRadius},
77 kEmptyResult);
78 }
79
80 // We do the math in CalculatePerVertex in unrotated space. This then applies
81 // the rotation to the line.
82 Point diff = line_geometry->GetP1() - line_geometry->GetP0();
83 Scalar angle = std::atan2(diff.y, diff.x);
84 Entity rotated_entity = entity.Clone();
85 Matrix matrix = entity.GetTransform();
86 matrix = matrix * Matrix::MakeTranslation(line_geometry->GetP0()) *
87 Matrix::MakeRotationZ(Radians(angle)) *
88 Matrix::MakeTranslation(-1 * line_geometry->GetP0());
89 rotated_entity.SetTransform(matrix);
90
91 return std::make_pair(
92 calculate_status.value(),
93 GeometryResult{
94 .type = PrimitiveType::kTriangleStrip,
95 .vertex_buffer =
96 {
97 .vertex_buffer = vertex_buffer,
98 .vertex_count = count,
99 .index_type = IndexType::kNone,
100 },
101 .transform = rotated_entity.GetShaderTransform(pass),
102 });
103 }
104
105 struct LineInfo {
106 Vector3 e0;
107 Vector3 e1;
108 Vector3 e2;
109 Vector3 e3;
110 };
111
112 LineInfo CalculateLineInfo(Point p0, Point p1, Scalar width, Scalar radius) {
113 Vector2 diff = p0 - p1;
114 float k = 2.0 / ((2.0 * radius + width) * sqrt(diff.Dot(diff)));
115
116 return LineInfo{
117 .e0 = Vector3(k * (p0.y - p1.y), //
118 k * (p1.x - p0.x), //
119 1.0 + k * (p0.x * p1.y - p1.x * p0.y)),
120 .e1 = Vector3(
121 k * (p1.x - p0.x), //
122 k * (p1.y - p0.y), //
123 1.0 + k * (p0.x * p0.x + p0.y * p0.y - p0.x * p1.x - p0.y * p1.y)),
124 .e2 = Vector3(k * (p1.y - p0.y), //
125 k * (p0.x - p1.x), //
126 1.0 + k * (p1.x * p0.y - p0.x * p1.y)),
127 .e3 = Vector3(
128 k * (p0.x - p1.x), //
129 k * (p0.y - p1.y), //
130 1.0 + k * (p1.x * p1.x + p1.y * p1.y - p0.x * p1.x - p0.y * p1.y)),
131 };
132 }
133 } // namespace
134
135 const Scalar LineContents::kSampleRadius = 1.f;
136
137 std::unique_ptr<LineContents> LineContents::Make(
138 std::unique_ptr<LineGeometry> geometry,
139 Color color) {
140 return std::unique_ptr<LineContents>(
141 new LineContents(std::move(geometry), color));
142 }
143
144 LineContents::LineContents(std::unique_ptr<LineGeometry> geometry, Color color)
145 : geometry_(std::move(geometry)), color_(color) {}
146
147 bool LineContents::Render(const ContentContext& renderer,
148 const Entity& entity,
149 RenderPass& pass) const {
150 auto& host_buffer = renderer.GetTransientsBuffer();
151
152 VS::FrameInfo frame_info;
153 FS::FragInfo frag_info;
154 frag_info.color = color_;
155
156 Scalar scale = entity.GetTransform().GetMaxBasisLengthXY();
157
158 auto geometry_result =
159 CreateGeometry(renderer, entity, pass, geometry_.get());
160
161 std::shared_ptr<Texture> curve_texture = CreateCurveTexture(
162 geometry_->GetWidth(), kSampleRadius, scale, renderer.GetContext());
163
164 SamplerDescriptor sampler_desc;
165 sampler_desc.min_filter = MinMagFilter::kLinear;
166 sampler_desc.mag_filter = MinMagFilter::kLinear;
167
168 FS::BindCurve(
169 pass, curve_texture,
170 renderer.GetContext()->GetSamplerLibrary()->GetSampler(sampler_desc));
171
172 PipelineBuilderCallback pipeline_callback =
173 [&renderer](ContentContextOptions options) {
174 return renderer.GetLinePipeline(options);
175 };
176
177 return ColorSourceContents::DrawGeometry<VS>(
178 this, geometry_.get(), renderer, entity, pass, pipeline_callback,
179 frame_info,
180 /*bind_fragment_callback=*/
181 [&frag_info, &host_buffer](RenderPass& pass) {
182 FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
183 pass.SetCommandLabel("Line");
184 return true;
185 },
186 /*force_stencil=*/false,
187 /*create_geom_callback=*/
188 [geometry_result = std::move(geometry_result)](
189 const ContentContext& renderer, const Entity& entity,
190 RenderPass& pass,
191 const Geometry* geometry) { return geometry_result.second; });
192 }
193
194 std::optional<Rect> LineContents::GetCoverage(const Entity& entity) const {
195 return geometry_->GetCoverage(entity.GetTransform());
196 }
197
198 std::vector<uint8_t> LineContents::CreateCurveData(Scalar width,
199 Scalar radius,
200 Scalar scale) {
201 std::vector<uint8_t> curve_data;
202 curve_data.reserve(kCurveResolution);
203 // More simply written as rise / run:
204 // double slope = 1.0 / ((radius * 2) / (scale * width + radius));
205 double slope = (scale * width + radius) / (radius * 2);
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);
242 if (!LineGeometry::ComputeCorners(
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
impeller::ContentContext
Definition: content_context.h:138
impeller::ContentContext::GetTransientsBuffer
HostBuffer & GetTransientsBuffer() const
Retrieve the currnent host buffer for transient storage.
Definition: content_context.h:297
impeller::ContentContext::GetLinePipeline
PipelineRef GetLinePipeline(ContentContextOptions opts) const
Definition: content_context.cc:1477
impeller::ContentContext::GetContext
std::shared_ptr< Context > GetContext() const
Definition: content_context.cc:939
impeller::Entity
Definition: entity.h:26
impeller::Entity::SetTransform
void SetTransform(const Matrix &transform)
Set the global transform matrix for this Entity.
Definition: entity.cc:60
impeller::Entity::GetShaderTransform
Matrix GetShaderTransform(const RenderPass &pass) const
Definition: entity.cc:48
impeller::Entity::Clone
Entity Clone() const
Definition: entity.cc:158
impeller::Entity::GetTransform
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition: entity.cc:44
impeller::Geometry
Definition: geometry.h:50
impeller::LineContents
Definition: line_contents.h:15
impeller::LineContents::CreateCurveData
static std::vector< uint8_t > CreateCurveData(Scalar width, Scalar radius, Scalar scale)
Definition: line_contents.cc:198
impeller::LineContents::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: line_contents.cc:194
impeller::LineContents::kSampleRadius
static const Scalar kSampleRadius
Definition: line_contents.h:17
impeller::LineContents::CalculatePerVertex
static fml::StatusOr< EffectiveLineParameters > CalculatePerVertex(LineVertexShader::PerVertexData *per_vertex, const LineGeometry *geometry, const Matrix &entity_transform)
Definition: line_contents.cc:227
impeller::LineContents::Render
bool Render(const ContentContext &renderer, const Entity &entity, RenderPass &pass) const override
Definition: line_contents.cc:147
impeller::LineGeometry
Definition: line_geometry.h:12
impeller::LineGeometry::GetP1
Point GetP1() const
Definition: line_geometry.h:32
impeller::LineGeometry::GetWidth
Scalar GetWidth() const
Definition: line_geometry.h:33
impeller::LineGeometry::GetCap
Cap GetCap() const
Definition: line_geometry.h:34
impeller::LineGeometry::ComputeCorners
static bool ComputeCorners(Point corners[4], const Matrix &transform, bool extend_endpoints, Point p0, Point p1, Scalar width)
Definition: line_geometry.cc:52
impeller::LineGeometry::GetP0
Point GetP0() const
Definition: line_geometry.h:31
impeller::RenderPass
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:30
impeller::RenderPipelineHandle::VertexShader
VertexShader_ VertexShader
Definition: pipeline.h:163
impeller::RenderPipelineHandle::FragmentShader
FragmentShader_ FragmentShader
Definition: pipeline.h:164
clip_contents.h
color_source_contents.h
x
int32_t x
Definition: dl_golden_unittests.cc:458
transform
Matrix transform
Definition: gaussian_blur_filter_contents.cc:214
e0
Vector3 e0
Definition: line_contents.cc:106
e3
Vector3 e3
Definition: line_contents.cc:109
e2
Vector3 e2
Definition: line_contents.cc:108
e1
Vector3 e1
Definition: line_contents.cc:107
line_contents.h
impeller
Definition: allocation.cc:12
impeller::Vector2
Point Vector2
Definition: point.h:331
impeller::Scalar
float Scalar
Definition: scalar.h:19
impeller::Point
TPoint< Scalar > Point
Definition: point.h:327
impeller::StorageMode::kHostVisible
@ kHostVisible
impeller::PipelineRef
raw_ptr< Pipeline< PipelineDescriptor > > PipelineRef
A raw ptr to a pipeline object.
Definition: pipeline.h:88
impeller::Cap::kButt
@ kButt
impeller::FS
LinePipeline::FragmentShader FS
Definition: line_contents.cc:14
impeller::PixelFormat::kR8UNormInt
@ kR8UNormInt
impeller::kEmptyResult
static const GeometryResult kEmptyResult
Definition: geometry.h:43
impeller::VS
LinePipeline::VertexShader VS
Definition: line_contents.cc:13
impeller::MinMagFilter::kLinear
@ kLinear
impeller::CreateTexture
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
std
Definition: comparable.h:95
rect_geometry.h
impeller::BufferView
Definition: buffer_view.h:19
impeller::Color
Definition: color.h:124
impeller::ContentContextOptions
Definition: content_context.h:40
impeller::GeometryResult
Definition: geometry.h:21
impeller::GeometryResult::transform
Matrix transform
Definition: geometry.h:39
impeller::LineContents::EffectiveLineParameters
Definition: line_contents.h:22
impeller::LineContents::EffectiveLineParameters::width
Scalar width
Definition: line_contents.h:23
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
impeller::Matrix::MakeRotationZ
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:223
impeller::Matrix::GetMaxBasisLengthXY
Scalar GetMaxBasisLengthXY() const
Definition: matrix.h:323
impeller::Radians
Definition: scalar.h:44
impeller::SamplerDescriptor
Definition: sampler_descriptor.h:14
impeller::SamplerDescriptor::mag_filter
MinMagFilter mag_filter
Definition: sampler_descriptor.h:16
impeller::SamplerDescriptor::min_filter
MinMagFilter min_filter
Definition: sampler_descriptor.h:15
impeller::TPoint< Scalar >
impeller::TPoint::GetLength
constexpr Type GetLength() const
Definition: point.h:206
impeller::TPoint::y
Type y
Definition: point.h:31
impeller::TPoint::x
Type x
Definition: point.h:30
impeller::TextureDescriptor
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
Definition: texture_descriptor.h:38
impeller::TextureDescriptor::size
ISize size
Definition: texture_descriptor.h:42
impeller::TextureDescriptor::storage_mode
StorageMode storage_mode
Definition: texture_descriptor.h:39
impeller::TextureDescriptor::format
PixelFormat format
Definition: texture_descriptor.h:41
texture_util.h