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

Commit b4f7a4d

Browse files
committed
[Impeller] Add an API for sampling strictly within the bounds of the source rect in DrawImageRect
Fixes flutter/flutter#140393
1 parent cc5d481 commit b4f7a4d

16 files changed

+134
-22
lines changed

impeller/aiks/canvas.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,8 @@ void Canvas::DrawImageRect(const std::shared_ptr<Image>& image,
628628
Rect source,
629629
Rect dest,
630630
const Paint& paint,
631-
SamplerDescriptor sampler) {
631+
SamplerDescriptor sampler,
632+
SourceRectConstraint src_rect_constraint) {
632633
if (!image || source.IsEmpty() || dest.IsEmpty()) {
633634
return;
634635
}
@@ -642,6 +643,8 @@ void Canvas::DrawImageRect(const std::shared_ptr<Image>& image,
642643
auto contents = TextureContents::MakeRect(dest);
643644
contents->SetTexture(image->GetTexture());
644645
contents->SetSourceRect(source);
646+
contents->SetStrictSourceRect(src_rect_constraint ==
647+
SourceRectConstraint::kStrict);
645648
contents->SetSamplerDescriptor(std::move(sampler));
646649
contents->SetOpacity(paint.color.alpha);
647650
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 {
@@ -866,6 +875,7 @@ class ContentContext {
866875
mutable Variants<RRectBlurPipeline> rrect_blur_pipelines_;
867876
mutable Variants<BlendPipeline> texture_blend_pipelines_;
868877
mutable Variants<TexturePipeline> texture_pipelines_;
878+
mutable Variants<TextureStrictSrcPipeline> texture_strict_src_pipelines_;
869879
#ifdef IMPELLER_ENABLE_OPENGLES
870880
mutable Variants<TextureExternalPipeline> texture_external_pipelines_;
871881
mutable Variants<TiledTextureExternalPipeline>

impeller/entity/contents/texture_contents.cc

Lines changed: 34 additions & 8 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

@@ -162,13 +161,17 @@ bool TextureContents::Render(const ContentContext& renderer,
162161
#ifdef IMPELLER_ENABLE_OPENGLES
163162
if (is_external_texture) {
164163
cmd.pipeline = renderer.GetTextureExternalPipeline(pipeline_options);
165-
} else {
166-
cmd.pipeline = renderer.GetTexturePipeline(pipeline_options);
167164
}
168-
#else
169-
cmd.pipeline = renderer.GetTexturePipeline(pipeline_options);
170165
#endif // IMPELLER_ENABLE_OPENGLES
171166

167+
if (!cmd.pipeline) {
168+
if (strict_source_rect_) {
169+
cmd.pipeline = renderer.GetTextureStrictSrcPipeline(pipeline_options);
170+
} else {
171+
cmd.pipeline = renderer.GetTexturePipeline(pipeline_options);
172+
}
173+
}
174+
172175
cmd.stencil_reference = entity.GetClipDepth();
173176
cmd.BindVertices(vertex_builder.CreateVertexBuffer(host_buffer));
174177
VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info));
@@ -177,6 +180,21 @@ bool TextureContents::Render(const ContentContext& renderer,
177180
cmd, texture_,
178181
renderer.GetContext()->GetSamplerLibrary()->GetSampler(
179182
sampler_descriptor_));
183+
} else if (strict_source_rect_) {
184+
// For a strict source rect, shrink the texture coordinate range by half a
185+
// texel to ensure that linear filtering does not sample anything outside
186+
// the source rect bounds.
187+
auto strict_texture_coords =
188+
Rect::MakeSize(texture_->GetSize()).Project(source_rect.Expand(-0.5));
189+
190+
FSStrictSrc::FragInfo frag_info;
191+
frag_info.source_rect = Vector4(strict_texture_coords.GetLTRB());
192+
FSStrictSrc::BindFragInfo(
193+
cmd, pass.GetTransientsBuffer().EmplaceUniform(frag_info));
194+
FSStrictSrc::BindTextureSampler(
195+
cmd, texture_,
196+
renderer.GetContext()->GetSamplerLibrary()->GetSampler(
197+
sampler_descriptor_));
180198
} else {
181199
FS::BindTextureSampler(
182200
cmd, texture_,
@@ -196,6 +214,14 @@ const Rect& TextureContents::GetSourceRect() const {
196214
return source_rect_;
197215
}
198216

217+
void TextureContents::SetStrictSourceRect(bool strict) {
218+
strict_source_rect_ = strict;
219+
}
220+
221+
bool TextureContents::GetStrictSourceRect() const {
222+
return strict_source_rect_;
223+
}
224+
199225
void TextureContents::SetSamplerDescriptor(SamplerDescriptor desc) {
200226
sampler_descriptor_ = std::move(desc);
201227
}

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_ = 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)