Started clamping scaled antialias lines size (#166149)
fixes https://github.com/flutter/flutter/issues/165994 Now vertices and e value calculations are driven by the clamping that is happening in ComputeCorners. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
@@ -47,10 +47,11 @@ std::shared_ptr<Texture> CreateCurveTexture(
|
||||
return CreateTexture(texture_descriptor, curve_data, context, "LineCurve");
|
||||
}
|
||||
|
||||
GeometryResult CreateGeometry(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass,
|
||||
const Geometry* geometry) {
|
||||
std::pair<LineContents::EffectiveLineParameters, GeometryResult> CreateGeometry(
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass,
|
||||
const Geometry* geometry) {
|
||||
using PerVertexData = LineVertexShader::PerVertexData;
|
||||
const LineGeometry* line_geometry =
|
||||
static_cast<const LineGeometry*>(geometry);
|
||||
@@ -59,7 +60,8 @@ GeometryResult CreateGeometry(const ContentContext& renderer,
|
||||
auto& host_buffer = renderer.GetTransientsBuffer();
|
||||
|
||||
size_t count = 4;
|
||||
fml::Status calculate_status;
|
||||
fml::StatusOr<LineContents::EffectiveLineParameters> calculate_status =
|
||||
LineContents::EffectiveLineParameters{.width = 0, .radius = 0};
|
||||
BufferView vertex_buffer = host_buffer.Emplace(
|
||||
count * sizeof(PerVertexData), alignof(PerVertexData),
|
||||
[line_geometry, &transform, &calculate_status](uint8_t* buffer) {
|
||||
@@ -68,19 +70,24 @@ GeometryResult CreateGeometry(const ContentContext& renderer,
|
||||
vertices, line_geometry, transform);
|
||||
});
|
||||
if (!calculate_status.ok()) {
|
||||
return kEmptyResult;
|
||||
return std::make_pair(
|
||||
LineContents::EffectiveLineParameters{
|
||||
.width = line_geometry->GetWidth(),
|
||||
.radius = LineContents::kSampleRadius},
|
||||
kEmptyResult);
|
||||
}
|
||||
|
||||
return GeometryResult{
|
||||
.type = PrimitiveType::kTriangleStrip,
|
||||
.vertex_buffer =
|
||||
{
|
||||
.vertex_buffer = vertex_buffer,
|
||||
.vertex_count = count,
|
||||
.index_type = IndexType::kNone,
|
||||
},
|
||||
.transform = entity.GetShaderTransform(pass),
|
||||
};
|
||||
return std::make_pair(calculate_status.value(),
|
||||
GeometryResult{
|
||||
.type = PrimitiveType::kTriangleStrip,
|
||||
.vertex_buffer =
|
||||
{
|
||||
.vertex_buffer = vertex_buffer,
|
||||
.vertex_count = count,
|
||||
.index_type = IndexType::kNone,
|
||||
},
|
||||
.transform = entity.GetShaderTransform(pass),
|
||||
});
|
||||
}
|
||||
|
||||
struct LineInfo {
|
||||
@@ -111,7 +118,6 @@ LineInfo CalculateLineInfo(Point p0, Point p1, Scalar width, Scalar radius) {
|
||||
1.0 + k * (p1.x * p1.x + p1.y * p1.y - p0.x * p1.x - p0.y * p1.y)),
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const Scalar LineContents::kSampleRadius = 1.f;
|
||||
@@ -136,6 +142,10 @@ bool LineContents::Render(const ContentContext& renderer,
|
||||
frag_info.color = color_;
|
||||
|
||||
Scalar scale = entity.GetTransform().GetMaxBasisLengthXY();
|
||||
|
||||
auto geometry_result =
|
||||
CreateGeometry(renderer, entity, pass, geometry_.get());
|
||||
|
||||
std::shared_ptr<Texture> curve_texture = CreateCurveTexture(
|
||||
geometry_->GetWidth(), kSampleRadius, scale, renderer.GetContext());
|
||||
|
||||
@@ -161,7 +171,11 @@ bool LineContents::Render(const ContentContext& renderer,
|
||||
return true;
|
||||
},
|
||||
/*force_stencil=*/false,
|
||||
/*create_geom_callback=*/CreateGeometry);
|
||||
/*create_geom_callback=*/
|
||||
[geometry_result = std::move(geometry_result)](
|
||||
const ContentContext& renderer, const Entity& entity,
|
||||
RenderPass& pass,
|
||||
const Geometry* geometry) { return geometry_result.second; });
|
||||
}
|
||||
|
||||
std::optional<Rect> LineContents::GetCoverage(const Entity& entity) const {
|
||||
@@ -185,20 +199,39 @@ std::vector<uint8_t> LineContents::CreateCurveData(Scalar width,
|
||||
return curve_data;
|
||||
}
|
||||
|
||||
fml::Status LineContents::CalculatePerVertex(
|
||||
LineVertexShader::PerVertexData* per_vertex,
|
||||
const LineGeometry* geometry,
|
||||
const Matrix& entity_transform) {
|
||||
Point corners[4];
|
||||
namespace {
|
||||
void ExpandLine(std::array<Point, 4>& corners, Point expansion) {
|
||||
Point along = (corners[1] - corners[0]).Normalize();
|
||||
Point across = (corners[2] - corners[0]).Normalize();
|
||||
corners[0] += -1 * (across * expansion.x) + -1 * (along * expansion.y);
|
||||
corners[1] += -1 * (across * expansion.x) + (along * expansion.y);
|
||||
corners[2] += (across * expansion.x) + -1 * (along * expansion.y);
|
||||
corners[3] += (across * expansion.x) + (along * expansion.y);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
fml::StatusOr<LineContents::EffectiveLineParameters>
|
||||
LineContents::CalculatePerVertex(LineVertexShader::PerVertexData* per_vertex,
|
||||
const LineGeometry* geometry,
|
||||
const Matrix& entity_transform) {
|
||||
Scalar scale = entity_transform.GetMaxBasisLengthXY();
|
||||
std::array<Point, 4> corners;
|
||||
// Make sure we get kSampleRadius pixels to sample from.
|
||||
Scalar expand_size = std::max(kSampleRadius / scale, kSampleRadius);
|
||||
if (!LineGeometry::ComputeCorners(
|
||||
corners, entity_transform,
|
||||
corners.data(), entity_transform,
|
||||
/*extend_endpoints=*/geometry->GetCap() != Cap::kButt,
|
||||
geometry->GetP0(), geometry->GetP1(),
|
||||
geometry->GetWidth() + kSampleRadius * 2.0)) {
|
||||
geometry->GetP0(), geometry->GetP1(), geometry->GetWidth())) {
|
||||
return fml::Status(fml::StatusCode::kAborted, "No valid corners");
|
||||
}
|
||||
LineInfo line_info = CalculateLineInfo(geometry->GetP0(), geometry->GetP1(),
|
||||
geometry->GetWidth(), kSampleRadius);
|
||||
Scalar effective_line_width = std::fabsf((corners[2] - corners[0]).y);
|
||||
ExpandLine(corners, Point(expand_size, expand_size));
|
||||
Scalar padded_line_width = std::fabsf((corners[2] - corners[0]).y);
|
||||
Scalar effective_sample_radius =
|
||||
(padded_line_width - effective_line_width) / 2.f;
|
||||
LineInfo line_info =
|
||||
CalculateLineInfo(geometry->GetP0(), geometry->GetP1(),
|
||||
effective_line_width, effective_sample_radius);
|
||||
for (auto& corner : corners) {
|
||||
*per_vertex++ = {
|
||||
.position = corner,
|
||||
@@ -209,6 +242,7 @@ fml::Status LineContents::CalculatePerVertex(
|
||||
};
|
||||
}
|
||||
|
||||
return {};
|
||||
return EffectiveLineParameters{.width = effective_line_width,
|
||||
.radius = effective_sample_radius};
|
||||
}
|
||||
} // namespace impeller
|
||||
|
||||
@@ -19,7 +19,15 @@ class LineContents : public Contents {
|
||||
Scalar radius,
|
||||
Scalar scale);
|
||||
|
||||
static fml::Status CalculatePerVertex(
|
||||
struct EffectiveLineParameters {
|
||||
Scalar width;
|
||||
Scalar radius;
|
||||
};
|
||||
|
||||
/// Calculates the values needed for the vertex shader, per vertex.
|
||||
/// Returns the effective line parameters that are used. These differ from the
|
||||
/// ones provided by `geometry` when the line gets clamped for being too thin.
|
||||
static fml::StatusOr<EffectiveLineParameters> CalculatePerVertex(
|
||||
LineVertexShader::PerVertexData* per_vertex,
|
||||
const LineGeometry* geometry,
|
||||
const Matrix& entity_transform);
|
||||
|
||||
@@ -64,15 +64,21 @@ TEST(LineContents, CalculatePerVertex) {
|
||||
/*cap=*/Cap::kButt);
|
||||
Matrix transform;
|
||||
|
||||
fml::Status status =
|
||||
fml::StatusOr<LineContents::EffectiveLineParameters> status =
|
||||
LineContents::CalculatePerVertex(per_vertex, geometry.get(), transform);
|
||||
Scalar offset =
|
||||
(LineContents::kSampleRadius * 2.0 + geometry->GetWidth()) / 2.f;
|
||||
ASSERT_TRUE(status.ok());
|
||||
EXPECT_POINT_NEAR(per_vertex[0].position, Point(100, 100 + offset));
|
||||
EXPECT_POINT_NEAR(per_vertex[1].position, Point(200, 100 + offset));
|
||||
EXPECT_POINT_NEAR(per_vertex[2].position, Point(100, 100 - offset));
|
||||
EXPECT_POINT_NEAR(per_vertex[3].position, Point(200, 100 - offset));
|
||||
EXPECT_EQ(status.value().width, 5.f);
|
||||
EXPECT_EQ(status.value().radius, LineContents::kSampleRadius);
|
||||
EXPECT_POINT_NEAR(per_vertex[0].position,
|
||||
Point(100 - LineContents::kSampleRadius, 100 + offset));
|
||||
EXPECT_POINT_NEAR(per_vertex[1].position,
|
||||
Point(200 + LineContents::kSampleRadius, 100 + offset));
|
||||
EXPECT_POINT_NEAR(per_vertex[2].position,
|
||||
Point(100 - LineContents::kSampleRadius, 100 - offset));
|
||||
EXPECT_POINT_NEAR(per_vertex[3].position,
|
||||
Point(200 + LineContents::kSampleRadius, 100 - offset));
|
||||
|
||||
for (int i = 1; i < 4; ++i) {
|
||||
EXPECT_VECTOR3_NEAR(per_vertex[0].e0, per_vertex[i].e0) << i;
|
||||
@@ -112,5 +118,45 @@ TEST(LineContents, CreateCurveDataScaled) {
|
||||
EXPECT_NEAR(data[3] / 255.f, 1.f, kEhCloseEnough);
|
||||
}
|
||||
|
||||
// This scales the line to be less than 1 pixel.
|
||||
TEST(LineContents, CalculatePerVertexLimit) {
|
||||
LineVertexShader::PerVertexData per_vertex[4];
|
||||
Scalar scale = 0.05;
|
||||
auto geometry = std::make_unique<LineGeometry>(
|
||||
/*p0=*/Point{100, 100}, //
|
||||
/*p1=*/Point{200, 100}, //
|
||||
/*width=*/10.f, //
|
||||
/*cap=*/Cap::kButt);
|
||||
Matrix transform = Matrix::MakeTranslation({100, 100, 1.0}) *
|
||||
Matrix::MakeScale({scale, scale, 1.0}) *
|
||||
Matrix::MakeTranslation({-100, -100, 1.0});
|
||||
|
||||
fml::StatusOr<LineContents::EffectiveLineParameters> status =
|
||||
LineContents::CalculatePerVertex(per_vertex, geometry.get(), transform);
|
||||
|
||||
Scalar one_radius_size = std::max(LineContents::kSampleRadius / scale,
|
||||
LineContents::kSampleRadius);
|
||||
Scalar one_px_size = 1.f / scale;
|
||||
Scalar offset = one_px_size / 2.f + one_radius_size;
|
||||
ASSERT_TRUE(status.ok());
|
||||
EXPECT_NEAR(status.value().width, 20.f, kEhCloseEnough);
|
||||
EXPECT_NEAR(status.value().radius, one_px_size * LineContents::kSampleRadius,
|
||||
kEhCloseEnough);
|
||||
EXPECT_POINT_NEAR(per_vertex[0].position,
|
||||
Point(100 - one_radius_size, 100 + offset));
|
||||
EXPECT_POINT_NEAR(per_vertex[1].position,
|
||||
Point(200 + one_radius_size, 100 + offset));
|
||||
EXPECT_POINT_NEAR(per_vertex[2].position,
|
||||
Point(100 - one_radius_size, 100 - offset));
|
||||
EXPECT_POINT_NEAR(per_vertex[3].position,
|
||||
Point(200 + one_radius_size, 100 - offset));
|
||||
|
||||
EXPECT_NEAR(CalculateLine(per_vertex[0], Point(150, 100)), 1.f,
|
||||
kEhCloseEnough);
|
||||
// EXPECT_NEAR(CalculateLine(per_vertex[0], Point(150, 100 +
|
||||
// one_px_size)), 1.f,
|
||||
// kEhCloseEnough);
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace impeller
|
||||
|
||||
@@ -42,5 +42,10 @@ float CalculateLine() {
|
||||
void main() {
|
||||
float line = CalculateLine();
|
||||
frag_color = vec4(frag_info.color.xyz, line);
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// This is a nice way to visually debug this shader:
|
||||
// frag_color =
|
||||
// vec4(mix(vec3(1, 0,0), frag_info.color.xyz, line), 1.0);
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
frag_color = IPPremultiply(frag_color);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user