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

Commit 041ae35

Browse files
[Impeller] Add an API for sampling strictly within the bounds of the source rect in DrawImageRect (#49696)
Fixes flutter/flutter#140393
1 parent 602c354 commit 041ae35

18 files changed

+329
-23
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5261,6 +5261,7 @@ ORIGIN: ../../../flutter/impeller/entity/shaders/sweep_gradient_ssbo_fill.frag +
52615261
ORIGIN: ../../../flutter/impeller/entity/shaders/texture_fill.frag + ../../../flutter/LICENSE
52625262
ORIGIN: ../../../flutter/impeller/entity/shaders/texture_fill.vert + ../../../flutter/LICENSE
52635263
ORIGIN: ../../../flutter/impeller/entity/shaders/texture_fill_external.frag + ../../../flutter/LICENSE
5264+
ORIGIN: ../../../flutter/impeller/entity/shaders/texture_fill_strict_src.frag + ../../../flutter/LICENSE
52645265
ORIGIN: ../../../flutter/impeller/entity/shaders/tiled_texture_fill.frag + ../../../flutter/LICENSE
52655266
ORIGIN: ../../../flutter/impeller/entity/shaders/tiled_texture_fill_external.frag + ../../../flutter/LICENSE
52665267
ORIGIN: ../../../flutter/impeller/entity/shaders/vertices.frag + ../../../flutter/LICENSE
@@ -8090,6 +8091,7 @@ FILE: ../../../flutter/impeller/entity/shaders/sweep_gradient_ssbo_fill.frag
80908091
FILE: ../../../flutter/impeller/entity/shaders/texture_fill.frag
80918092
FILE: ../../../flutter/impeller/entity/shaders/texture_fill.vert
80928093
FILE: ../../../flutter/impeller/entity/shaders/texture_fill_external.frag
8094+
FILE: ../../../flutter/impeller/entity/shaders/texture_fill_strict_src.frag
80938095
FILE: ../../../flutter/impeller/entity/shaders/tiled_texture_fill.frag
80948096
FILE: ../../../flutter/impeller/entity/shaders/tiled_texture_fill_external.frag
80958097
FILE: ../../../flutter/impeller/entity/shaders/vertices.frag

impeller/aiks/canvas.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -662,7 +662,8 @@ void Canvas::DrawImageRect(const std::shared_ptr<Image>& image,
662662
Rect source,
663663
Rect dest,
664664
const Paint& paint,
665-
SamplerDescriptor sampler) {
665+
SamplerDescriptor sampler,
666+
SourceRectConstraint src_rect_constraint) {
666667
if (!image || source.IsEmpty() || dest.IsEmpty()) {
667668
return;
668669
}
@@ -676,6 +677,8 @@ void Canvas::DrawImageRect(const std::shared_ptr<Image>& image,
676677
auto contents = TextureContents::MakeRect(dest);
677678
contents->SetTexture(image->GetTexture());
678679
contents->SetSourceRect(source);
680+
contents->SetStrictSourceRect(src_rect_constraint ==
681+
SourceRectConstraint::kStrict);
679682
contents->SetSamplerDescriptor(std::move(sampler));
680683
contents->SetOpacity(paint.color.alpha);
681684
contents->SetDeferApplyingOpacity(paint.HasColorFilter());

impeller/aiks/canvas.h

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ enum class PointStyle {
4545
kSquare,
4646
};
4747

48+
/// Controls the behavior of the source rectangle given to DrawImageRect.
49+
enum class SourceRectConstraint {
50+
/// @brief Faster, but may sample outside the bounds of the source rectangle.
51+
kFast,
52+
53+
/// @brief Sample only within the source rectangle. May be slower.
54+
kStrict,
55+
};
56+
4857
class Canvas {
4958
public:
5059
struct DebugOptions {
@@ -123,11 +132,13 @@ class Canvas {
123132
const Paint& paint,
124133
SamplerDescriptor sampler = {});
125134

126-
void DrawImageRect(const std::shared_ptr<Image>& image,
127-
Rect source,
128-
Rect dest,
129-
const Paint& paint,
130-
SamplerDescriptor sampler = {});
135+
void DrawImageRect(
136+
const std::shared_ptr<Image>& image,
137+
Rect source,
138+
Rect dest,
139+
const Paint& paint,
140+
SamplerDescriptor sampler = {},
141+
SourceRectConstraint src_rect_constraint = SourceRectConstraint::kFast);
131142

132143
void ClipPath(
133144
Path path,

impeller/aiks/canvas_recorder.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -237,13 +237,16 @@ class CanvasRecorder {
237237
offset, paint, sampler);
238238
}
239239

240-
void DrawImageRect(const std::shared_ptr<Image>& image,
241-
Rect source,
242-
Rect dest,
243-
const Paint& paint,
244-
SamplerDescriptor sampler = {}) {
240+
void DrawImageRect(
241+
const std::shared_ptr<Image>& image,
242+
Rect source,
243+
Rect dest,
244+
const Paint& paint,
245+
SamplerDescriptor sampler = {},
246+
SourceRectConstraint src_rect_constraint = SourceRectConstraint::kFast) {
245247
return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawImageRect), image,
246-
source, dest, paint, sampler);
248+
source, dest, paint, sampler,
249+
src_rect_constraint);
247250
}
248251

249252
void ClipPath(

impeller/aiks/canvas_recorder_unittests.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ class Serializer {
5454

5555
void Write(const std::vector<Color>& matrices) {}
5656

57+
void Write(const SourceRectConstraint& src_rect_constraint) {}
58+
5759
CanvasRecorderOp last_op_;
5860
};
5961
} // namespace
@@ -196,7 +198,7 @@ TEST(CanvasRecorder, DrawImage) {
196198

197199
TEST(CanvasRecorder, DrawImageRect) {
198200
CanvasRecorder<Serializer> recorder;
199-
recorder.DrawImageRect({}, {}, {}, {}, {});
201+
recorder.DrawImageRect({}, {}, {}, {}, {}, SourceRectConstraint::kFast);
200202
ASSERT_EQ(recorder.GetSerializer().last_op_,
201203
CanvasRecorderOp::kDrawImageRect);
202204
}

impeller/aiks/trace_serializer.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,4 +254,9 @@ void TraceSerializer::Write(const std::vector<Rect>& matrices) {
254254
void TraceSerializer::Write(const std::vector<Color>& matrices) {
255255
buffer_ << "[std::vector<Color>] ";
256256
}
257+
258+
void TraceSerializer::Write(const SourceRectConstraint& src_rect_constraint) {
259+
buffer_ << "[SourceRectConstraint] ";
260+
}
261+
257262
} // namespace impeller

impeller/aiks/trace_serializer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ class TraceSerializer {
5858

5959
void Write(const std::vector<Color>& matrices);
6060

61+
void Write(const SourceRectConstraint& src_rect_constraint);
62+
6163
private:
6264
std::stringstream buffer_;
6365
};

impeller/display_list/dl_unittests.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,18 @@ TEST_P(DisplayListTest, CanDrawNinePatchImageCornersScaledDown) {
801801
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
802802
}
803803

804+
TEST_P(DisplayListTest, NinePatchImagePrecision) {
805+
// Draw a nine patch image with colored corners and verify that the corner
806+
// color does not leak outside the intended region.
807+
auto texture = CreateTextureForFixture("nine_patch_corners.png");
808+
flutter::DisplayListBuilder builder;
809+
builder.DrawImageNine(DlImageImpeller::Make(texture),
810+
SkIRect::MakeXYWH(10, 10, 1, 1),
811+
SkRect::MakeXYWH(0, 0, 200, 100),
812+
flutter::DlFilterMode::kNearest, nullptr);
813+
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
814+
}
815+
804816
TEST_P(DisplayListTest, CanDrawPoints) {
805817
flutter::DisplayListBuilder builder;
806818
SkPoint points[7] = {

impeller/display_list/nine_patch_converter.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ void NinePatchConverter::DrawNinePatch(const std::shared_ptr<Image>& image,
8686
// DrawImageAtlas.
8787
canvas->DrawImageRect(image, Rect::MakeLTRB(srcX0, srcY0, srcX1, srcY1),
8888
Rect::MakeLTRB(dstX0, dstY0, dstX1, dstY1), *paint,
89-
sampler);
89+
sampler, SourceRectConstraint::kStrict);
9090
}
9191
}
9292
}

impeller/entity/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ impeller_shaders("entity_shaders") {
5252
"shaders/texture_fill.frag",
5353
"shaders/texture_fill.vert",
5454
"shaders/texture_fill_external.frag",
55+
"shaders/texture_fill_strict_src.frag",
5556
"shaders/tiled_texture_fill.frag",
5657
"shaders/tiled_texture_fill_external.frag",
5758
"shaders/vertices.frag",

impeller/entity/contents/content_context.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ ContentContext::ContentContext(
332332
rrect_blur_pipelines_.CreateDefault(*context_, options_trianglestrip);
333333
texture_blend_pipelines_.CreateDefault(*context_, options);
334334
texture_pipelines_.CreateDefault(*context_, options);
335+
texture_strict_src_pipelines_.CreateDefault(*context_, options);
335336
position_uv_pipelines_.CreateDefault(*context_, options);
336337
tiled_texture_pipelines_.CreateDefault(*context_, options);
337338
gaussian_blur_noalpha_decal_pipelines_.CreateDefault(*context_,

impeller/entity/contents/content_context.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
#include "impeller/entity/sweep_gradient_fill.frag.h"
6060
#include "impeller/entity/texture_fill.frag.h"
6161
#include "impeller/entity/texture_fill.vert.h"
62+
#include "impeller/entity/texture_fill_strict_src.frag.h"
6263
#include "impeller/entity/tiled_texture_fill.frag.h"
6364
#include "impeller/entity/uv.comp.h"
6465
#include "impeller/entity/vertices.frag.h"
@@ -130,6 +131,9 @@ using RRectBlurPipeline =
130131
using BlendPipeline = RenderPipelineT<BlendVertexShader, BlendFragmentShader>;
131132
using TexturePipeline =
132133
RenderPipelineT<TextureFillVertexShader, TextureFillFragmentShader>;
134+
using TextureStrictSrcPipeline =
135+
RenderPipelineT<TextureFillVertexShader,
136+
TextureFillStrictSrcFragmentShader>;
133137
using PositionUVPipeline =
134138
RenderPipelineT<TextureFillVertexShader, TiledTextureFillFragmentShader>;
135139
using TiledTexturePipeline =
@@ -418,6 +422,11 @@ class ContentContext {
418422
return GetPipeline(texture_pipelines_, opts);
419423
}
420424

425+
std::shared_ptr<Pipeline<PipelineDescriptor>> GetTextureStrictSrcPipeline(
426+
ContentContextOptions opts) const {
427+
return GetPipeline(texture_strict_src_pipelines_, opts);
428+
}
429+
421430
#ifdef IMPELLER_ENABLE_OPENGLES
422431
std::shared_ptr<Pipeline<PipelineDescriptor>> GetTextureExternalPipeline(
423432
ContentContextOptions opts) const {
@@ -871,6 +880,7 @@ class ContentContext {
871880
mutable Variants<RRectBlurPipeline> rrect_blur_pipelines_;
872881
mutable Variants<BlendPipeline> texture_blend_pipelines_;
873882
mutable Variants<TexturePipeline> texture_pipelines_;
883+
mutable Variants<TextureStrictSrcPipeline> texture_strict_src_pipelines_;
874884
#ifdef IMPELLER_ENABLE_OPENGLES
875885
mutable Variants<TextureExternalPipeline> texture_external_pipelines_;
876886
mutable Variants<TiledTextureExternalPipeline>

impeller/entity/contents/texture_contents.cc

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ bool TextureContents::Render(const ContentContext& renderer,
111111

112112
using VS = TextureFillVertexShader;
113113
using FS = TextureFillFragmentShader;
114+
using FSStrictSrc = TextureFillStrictSrcFragmentShader;
114115
using FSExternal = TextureFillExternalFragmentShader;
115116

116117
if (destination_rect_.IsEmpty() || source_rect_.IsEmpty() ||
@@ -121,11 +122,9 @@ bool TextureContents::Render(const ContentContext& renderer,
121122
bool is_external_texture =
122123
texture_->GetTextureDescriptor().type == TextureType::kTextureExternalOES;
123124

124-
// Expand the source rect by half a texel, which aligns sampled texels to the
125-
// pixel grid if the source rect is the same size as the destination rect.
125+
auto source_rect = capture.AddRect("Source rect", source_rect_);
126126
auto texture_coords =
127-
Rect::MakeSize(texture_->GetSize())
128-
.Project(capture.AddRect("Source rect", source_rect_).Expand(0.5));
127+
Rect::MakeSize(texture_->GetSize()).Project(source_rect);
129128

130129
VertexBufferBuilder<VS::PerVertexData> vertex_builder;
131130

@@ -160,16 +159,22 @@ bool TextureContents::Render(const ContentContext& renderer,
160159
}
161160
pipeline_options.primitive_type = PrimitiveType::kTriangleStrip;
162161

162+
std::shared_ptr<Pipeline<PipelineDescriptor>> pipeline;
163163
#ifdef IMPELLER_ENABLE_OPENGLES
164164
if (is_external_texture) {
165-
pass.SetPipeline(renderer.GetTextureExternalPipeline(pipeline_options));
166-
} else {
167-
pass.SetPipeline(renderer.GetTexturePipeline(pipeline_options));
165+
pipeline = renderer.GetTextureExternalPipeline(pipeline_options);
168166
}
169-
#else
170-
pass.SetPipeline(renderer.GetTexturePipeline(pipeline_options));
171167
#endif // IMPELLER_ENABLE_OPENGLES
172168

169+
if (!pipeline) {
170+
if (strict_source_rect_enabled_) {
171+
pipeline = renderer.GetTextureStrictSrcPipeline(pipeline_options);
172+
} else {
173+
pipeline = renderer.GetTexturePipeline(pipeline_options);
174+
}
175+
}
176+
pass.SetPipeline(pipeline);
177+
173178
pass.SetStencilReference(entity.GetClipDepth());
174179
pass.SetVertexBuffer(vertex_builder.CreateVertexBuffer(host_buffer));
175180
VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
@@ -178,6 +183,20 @@ bool TextureContents::Render(const ContentContext& renderer,
178183
pass, texture_,
179184
renderer.GetContext()->GetSamplerLibrary()->GetSampler(
180185
sampler_descriptor_));
186+
} else if (strict_source_rect_enabled_) {
187+
// For a strict source rect, shrink the texture coordinate range by half a
188+
// texel to ensure that linear filtering does not sample anything outside
189+
// the source rect bounds.
190+
auto strict_texture_coords =
191+
Rect::MakeSize(texture_->GetSize()).Project(source_rect.Expand(-0.5));
192+
193+
FSStrictSrc::FragInfo frag_info;
194+
frag_info.source_rect = Vector4(strict_texture_coords.GetLTRB());
195+
FSStrictSrc::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
196+
FSStrictSrc::BindTextureSampler(
197+
pass, texture_,
198+
renderer.GetContext()->GetSamplerLibrary()->GetSampler(
199+
sampler_descriptor_));
181200
} else {
182201
FS::BindTextureSampler(
183202
pass, texture_,
@@ -195,6 +214,14 @@ const Rect& TextureContents::GetSourceRect() const {
195214
return source_rect_;
196215
}
197216

217+
void TextureContents::SetStrictSourceRect(bool strict) {
218+
strict_source_rect_enabled_ = strict;
219+
}
220+
221+
bool TextureContents::GetStrictSourceRect() const {
222+
return strict_source_rect_enabled_;
223+
}
224+
198225
void TextureContents::SetSamplerDescriptor(SamplerDescriptor desc) {
199226
sampler_descriptor_ = std::move(desc);
200227
}

impeller/entity/contents/texture_contents.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ class TextureContents final : public Contents {
4545

4646
const Rect& GetSourceRect() const;
4747

48+
void SetStrictSourceRect(bool strict);
49+
50+
bool GetStrictSourceRect() const;
51+
4852
void SetOpacity(Scalar opacity);
4953

5054
Scalar GetOpacity() const;
@@ -85,6 +89,7 @@ class TextureContents final : public Contents {
8589
std::shared_ptr<Texture> texture_;
8690
SamplerDescriptor sampler_descriptor_ = {};
8791
Rect source_rect_;
92+
bool strict_source_rect_enabled_ = false;
8893
Scalar opacity_ = 1.0f;
8994
Scalar inherited_opacity_ = 1.0f;
9095
bool defer_applying_opacity_ = false;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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+
precision mediump float;
6+
7+
#include <impeller/constants.glsl>
8+
#include <impeller/types.glsl>
9+
10+
uniform f16sampler2D texture_sampler;
11+
12+
uniform FragInfo {
13+
vec4 source_rect;
14+
}
15+
frag_info;
16+
17+
in highp vec2 v_texture_coords;
18+
IMPELLER_MAYBE_FLAT in float16_t v_alpha;
19+
20+
out f16vec4 frag_color;
21+
22+
void main() {
23+
vec2 texture_coords = vec2(clamp(v_texture_coords.x, frag_info.source_rect.x,
24+
frag_info.source_rect.z),
25+
clamp(v_texture_coords.y, frag_info.source_rect.y,
26+
frag_info.source_rect.w));
27+
f16vec4 sampled =
28+
texture(texture_sampler, texture_coords, kDefaultMipBiasHalf);
29+
frag_color = sampled * v_alpha;
30+
}

impeller/fixtures/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ test_fixtures("file_fixtures") {
8989
"flutter_logo_baked.glb",
9090
"kalimba.jpg",
9191
"multiple_stages.hlsl",
92+
"nine_patch_corners.png",
9293
"resources_limit.vert",
9394
"sample.comp",
9495
"sample.frag",
577 Bytes
Loading

0 commit comments

Comments
 (0)