Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 8cc6d6d

Browse files
authored
[Impeller] Correctly compute UVs in texture fill (#43028)
Resolves flutter/flutter#128353. Depends on #43026. * Maps the texel coordinate space correctly relative to the geometry pixel coordinates. * Use decal sampling for subpass textures. * Simplify the computation by using new rect utilities such as `Rect::Project`.
1 parent 703c9a1 commit 8cc6d6d

File tree

6 files changed

+64
-30
lines changed

6 files changed

+64
-30
lines changed

impeller/aiks/paint_pass_delegate.cc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
#include "impeller/aiks/paint_pass_delegate.h"
66

7+
#include "impeller/core/formats.h"
8+
#include "impeller/core/sampler_descriptor.h"
79
#include "impeller/entity/contents/contents.h"
810
#include "impeller/entity/contents/texture_contents.h"
911
#include "impeller/entity/entity_pass.h"
@@ -46,6 +48,13 @@ std::shared_ptr<Contents> PaintPassDelegate::CreateContentsForSubpassTarget(
4648
contents->SetSourceRect(Rect::MakeSize(target->GetSize()));
4749
contents->SetOpacity(paint_.color.alpha);
4850
contents->SetDeferApplyingOpacity(true);
51+
52+
SamplerDescriptor sampler_desc;
53+
sampler_desc.label = "Subpass";
54+
sampler_desc.width_address_mode = SamplerAddressMode::kDecal;
55+
sampler_desc.height_address_mode = SamplerAddressMode::kDecal;
56+
contents->SetSamplerDescriptor(sampler_desc);
57+
4958
return paint_.WithFiltersForSubpassTarget(std::move(contents),
5059
effect_transform);
5160
}
@@ -140,6 +149,13 @@ OpacityPeepholePassDelegate::CreateContentsForSubpassTarget(
140149
contents->SetSourceRect(Rect::MakeSize(target->GetSize()));
141150
contents->SetOpacity(paint_.color.alpha);
142151
contents->SetDeferApplyingOpacity(true);
152+
153+
SamplerDescriptor sampler_desc;
154+
sampler_desc.label = "Subpass";
155+
sampler_desc.width_address_mode = SamplerAddressMode::kDecal;
156+
sampler_desc.height_address_mode = SamplerAddressMode::kDecal;
157+
contents->SetSamplerDescriptor(sampler_desc);
158+
143159
return paint_.WithFiltersForSubpassTarget(std::move(contents),
144160
effect_transform);
145161
}

impeller/entity/contents/texture_contents.cc

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,16 @@ TextureContents::~TextureContents() = default;
2626

2727
std::shared_ptr<TextureContents> TextureContents::MakeRect(Rect destination) {
2828
auto contents = std::make_shared<TextureContents>();
29-
contents->rect_ = destination;
29+
contents->destination_rect_ = destination;
3030
return contents;
3131
}
3232

3333
void TextureContents::SetLabel(std::string label) {
3434
label_ = std::move(label);
3535
}
3636

37-
void TextureContents::SetRect(Rect rect) {
38-
rect_ = rect;
37+
void TextureContents::SetDestinationRect(Rect rect) {
38+
destination_rect_ = rect;
3939
}
4040

4141
void TextureContents::SetTexture(std::shared_ptr<Texture> texture) {
@@ -70,7 +70,7 @@ std::optional<Rect> TextureContents::GetCoverage(const Entity& entity) const {
7070
if (GetOpacity() == 0) {
7171
return std::nullopt;
7272
}
73-
return rect_.TransformBounds(entity.GetTransformation());
73+
return destination_rect_.TransformBounds(entity.GetTransformation());
7474
};
7575

7676
std::optional<Snapshot> TextureContents::RenderToSnapshot(
@@ -82,7 +82,7 @@ std::optional<Snapshot> TextureContents::RenderToSnapshot(
8282
const std::string& label) const {
8383
// Passthrough textures that have simple rectangle paths and complete source
8484
// rects.
85-
auto bounds = rect_;
85+
auto bounds = destination_rect_;
8686
auto opacity = GetOpacity();
8787
if (source_rect_ == Rect::MakeSize(texture_->GetSize()) &&
8888
(opacity >= 1 - kEhCloseEnough || defer_applying_opacity_)) {
@@ -104,37 +104,30 @@ std::optional<Snapshot> TextureContents::RenderToSnapshot(
104104
label); // label
105105
}
106106

107-
static TextureFillVertexShader::PerVertexData ComputeVertexData(
108-
const Point& position,
109-
const Rect& coverage_rect,
110-
const ISize& texture_size,
111-
const Rect& source_rect) {
112-
TextureFillVertexShader::PerVertexData data;
113-
data.position = position;
114-
auto coverage_coords = (position - coverage_rect.origin) / coverage_rect.size;
115-
data.texture_coords =
116-
(source_rect.origin + source_rect.size * coverage_coords) / texture_size;
117-
return data;
118-
}
119-
120107
bool TextureContents::Render(const ContentContext& renderer,
121108
const Entity& entity,
122109
RenderPass& pass) const {
123110
using VS = TextureFillVertexShader;
124111
using FS = TextureFillFragmentShader;
125112

126-
const auto coverage_rect = rect_;
127-
128-
if (coverage_rect.size.IsEmpty() || source_rect_.IsEmpty() ||
113+
if (destination_rect_.size.IsEmpty() || source_rect_.IsEmpty() ||
129114
texture_ == nullptr || texture_->GetSize().IsEmpty()) {
130-
return true;
115+
return true; // Nothing to render.
131116
}
132117

118+
// Expand the source rect by half a texel, which aligns sampled texels to the
119+
// pixel grid if the source rect is the same size as the destination rect.
120+
auto texture_coords =
121+
Rect::MakeSize(texture_->GetSize()).Project(source_rect_.Expand(0.5));
122+
133123
VertexBufferBuilder<VS::PerVertexData> vertex_builder;
134-
for (const auto position : rect_.GetPoints()) {
135-
vertex_builder.AppendVertex(ComputeVertexData(
136-
position, coverage_rect, texture_->GetSize(), source_rect_));
137-
}
124+
125+
vertex_builder.AddVertices({
126+
{destination_rect_.GetLeftTop(), texture_coords.GetLeftTop()},
127+
{destination_rect_.GetRightTop(), texture_coords.GetRightTop()},
128+
{destination_rect_.GetLeftBottom(), texture_coords.GetLeftBottom()},
129+
{destination_rect_.GetRightBottom(), texture_coords.GetRightBottom()},
130+
});
138131

139132
auto& host_buffer = pass.GetTransientsBuffer();
140133

impeller/entity/contents/texture_contents.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class TextureContents final : public Contents {
3030

3131
void SetLabel(std::string label);
3232

33-
void SetRect(Rect rect);
33+
void SetDestinationRect(Rect rect);
3434

3535
void SetTexture(std::shared_ptr<Texture> texture);
3636

@@ -78,7 +78,7 @@ class TextureContents final : public Contents {
7878
private:
7979
std::string label_;
8080

81-
Rect rect_;
81+
Rect destination_rect_;
8282
bool stencil_enabled_ = true;
8383

8484
std::shared_ptr<Texture> texture_;

impeller/entity/entity_unittests.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,7 +1069,7 @@ TEST_P(EntityTest, GaussianBlurFilter) {
10691069
if (selected_input_type == 0) {
10701070
auto texture = std::make_shared<TextureContents>();
10711071
texture->SetSourceRect(Rect::MakeSize(boston->GetSize()));
1072-
texture->SetRect(input_rect);
1072+
texture->SetDestinationRect(input_rect);
10731073
texture->SetTexture(boston);
10741074
texture->SetOpacity(input_color.alpha);
10751075

@@ -1192,7 +1192,7 @@ TEST_P(EntityTest, MorphologyFilter) {
11921192
Rect::MakeXYWH(path_rect[0], path_rect[1], path_rect[2], path_rect[3]);
11931193
auto texture = std::make_shared<TextureContents>();
11941194
texture->SetSourceRect(Rect::MakeSize(boston->GetSize()));
1195-
texture->SetRect(input_rect);
1195+
texture->SetDestinationRect(input_rect);
11961196
texture->SetTexture(boston);
11971197
texture->SetOpacity(input_color.alpha);
11981198

impeller/geometry/geometry_unittests.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
#include "gtest/gtest.h"
56
#include "impeller/geometry/geometry_asserts.h"
67

78
#include <limits>
@@ -2142,6 +2143,20 @@ TEST(GeometryTest, RectScale) {
21422143
}
21432144
}
21442145

2146+
TEST(GeometryTest, RectDirections) {
2147+
auto r = Rect::MakeLTRB(1, 2, 3, 4);
2148+
2149+
ASSERT_EQ(r.GetLeft(), 1);
2150+
ASSERT_EQ(r.GetTop(), 2);
2151+
ASSERT_EQ(r.GetRight(), 3);
2152+
ASSERT_EQ(r.GetBottom(), 4);
2153+
2154+
ASSERT_POINT_NEAR(r.GetLeftTop(), Point(1, 2));
2155+
ASSERT_POINT_NEAR(r.GetRightTop(), Point(3, 2));
2156+
ASSERT_POINT_NEAR(r.GetLeftBottom(), Point(1, 4));
2157+
ASSERT_POINT_NEAR(r.GetRightBottom(), Point(3, 4));
2158+
}
2159+
21452160
TEST(GeometryTest, RectProject) {
21462161
{
21472162
auto r = Rect::MakeLTRB(-100, -100, 100, 100);

impeller/geometry/rect.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,16 @@ struct TRect {
162162
return std::max(origin.y, origin.y + size.height);
163163
}
164164

165+
constexpr TPoint<T> GetLeftTop() const { return {GetLeft(), GetTop()}; }
166+
167+
constexpr TPoint<T> GetRightTop() const { return {GetRight(), GetTop()}; }
168+
169+
constexpr TPoint<T> GetLeftBottom() const { return {GetLeft(), GetBottom()}; }
170+
171+
constexpr TPoint<T> GetRightBottom() const {
172+
return {GetRight(), GetBottom()};
173+
}
174+
165175
constexpr std::array<T, 4> GetLTRB() const {
166176
return {GetLeft(), GetTop(), GetRight(), GetBottom()};
167177
}

0 commit comments

Comments
 (0)