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

[Impeller] Add an API for sampling strictly within the bounds of the source rect in DrawImageRect #49696

Merged
merged 4 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -5261,6 +5261,7 @@ ORIGIN: ../../../flutter/impeller/entity/shaders/sweep_gradient_ssbo_fill.frag +
ORIGIN: ../../../flutter/impeller/entity/shaders/texture_fill.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/texture_fill.vert + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/texture_fill_external.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/texture_fill_strict_src.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/tiled_texture_fill.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/tiled_texture_fill_external.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/vertices.frag + ../../../flutter/LICENSE
Expand Down Expand Up @@ -8090,6 +8091,7 @@ FILE: ../../../flutter/impeller/entity/shaders/sweep_gradient_ssbo_fill.frag
FILE: ../../../flutter/impeller/entity/shaders/texture_fill.frag
FILE: ../../../flutter/impeller/entity/shaders/texture_fill.vert
FILE: ../../../flutter/impeller/entity/shaders/texture_fill_external.frag
FILE: ../../../flutter/impeller/entity/shaders/texture_fill_strict_src.frag
FILE: ../../../flutter/impeller/entity/shaders/tiled_texture_fill.frag
FILE: ../../../flutter/impeller/entity/shaders/tiled_texture_fill_external.frag
FILE: ../../../flutter/impeller/entity/shaders/vertices.frag
Expand Down
5 changes: 4 additions & 1 deletion impeller/aiks/canvas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,8 @@ void Canvas::DrawImageRect(const std::shared_ptr<Image>& image,
Rect source,
Rect dest,
const Paint& paint,
SamplerDescriptor sampler) {
SamplerDescriptor sampler,
SourceRectConstraint src_rect_constraint) {
if (!image || source.IsEmpty() || dest.IsEmpty()) {
return;
}
Expand All @@ -676,6 +677,8 @@ void Canvas::DrawImageRect(const std::shared_ptr<Image>& image,
auto contents = TextureContents::MakeRect(dest);
contents->SetTexture(image->GetTexture());
contents->SetSourceRect(source);
contents->SetStrictSourceRect(src_rect_constraint ==
SourceRectConstraint::kStrict);
contents->SetSamplerDescriptor(std::move(sampler));
contents->SetOpacity(paint.color.alpha);
contents->SetDeferApplyingOpacity(paint.HasColorFilter());
Expand Down
21 changes: 16 additions & 5 deletions impeller/aiks/canvas.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ enum class PointStyle {
kSquare,
};

/// Controls the behavior of the source rectangle given to DrawImageRect.
enum class SourceRectConstraint {
/// @brief Faster, but may sample outside the bounds of the source rectangle.
kFast,

/// @brief Sample only within the source rectangle. May be slower.
kStrict,
};

class Canvas {
public:
struct DebugOptions {
Expand Down Expand Up @@ -123,11 +132,13 @@ class Canvas {
const Paint& paint,
SamplerDescriptor sampler = {});

void DrawImageRect(const std::shared_ptr<Image>& image,
Rect source,
Rect dest,
const Paint& paint,
SamplerDescriptor sampler = {});
void DrawImageRect(
const std::shared_ptr<Image>& image,
Rect source,
Rect dest,
const Paint& paint,
SamplerDescriptor sampler = {},
SourceRectConstraint src_rect_constraint = SourceRectConstraint::kFast);

void ClipPath(
Path path,
Expand Down
15 changes: 9 additions & 6 deletions impeller/aiks/canvas_recorder.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,13 +237,16 @@ class CanvasRecorder {
offset, paint, sampler);
}

void DrawImageRect(const std::shared_ptr<Image>& image,
Rect source,
Rect dest,
const Paint& paint,
SamplerDescriptor sampler = {}) {
void DrawImageRect(
const std::shared_ptr<Image>& image,
Rect source,
Rect dest,
const Paint& paint,
SamplerDescriptor sampler = {},
SourceRectConstraint src_rect_constraint = SourceRectConstraint::kFast) {
return ExecuteAndSerialize(FLT_CANVAS_RECORDER_OP_ARG(DrawImageRect), image,
source, dest, paint, sampler);
source, dest, paint, sampler,
src_rect_constraint);
}

void ClipPath(
Expand Down
4 changes: 3 additions & 1 deletion impeller/aiks/canvas_recorder_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ class Serializer {

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

void Write(const SourceRectConstraint& src_rect_constraint) {}

CanvasRecorderOp last_op_;
};
} // namespace
Expand Down Expand Up @@ -196,7 +198,7 @@ TEST(CanvasRecorder, DrawImage) {

TEST(CanvasRecorder, DrawImageRect) {
CanvasRecorder<Serializer> recorder;
recorder.DrawImageRect({}, {}, {}, {}, {});
recorder.DrawImageRect({}, {}, {}, {}, {}, SourceRectConstraint::kFast);
ASSERT_EQ(recorder.GetSerializer().last_op_,
CanvasRecorderOp::kDrawImageRect);
}
Expand Down
5 changes: 5 additions & 0 deletions impeller/aiks/trace_serializer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -254,4 +254,9 @@ void TraceSerializer::Write(const std::vector<Rect>& matrices) {
void TraceSerializer::Write(const std::vector<Color>& matrices) {
buffer_ << "[std::vector<Color>] ";
}

void TraceSerializer::Write(const SourceRectConstraint& src_rect_constraint) {
buffer_ << "[SourceRectConstraint] ";
}

} // namespace impeller
2 changes: 2 additions & 0 deletions impeller/aiks/trace_serializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ class TraceSerializer {

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

void Write(const SourceRectConstraint& src_rect_constraint);

private:
std::stringstream buffer_;
};
Expand Down
12 changes: 12 additions & 0 deletions impeller/display_list/dl_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,18 @@ TEST_P(DisplayListTest, CanDrawNinePatchImageCornersScaledDown) {
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(DisplayListTest, NinePatchImagePrecision) {
// Draw a nine patch image with colored corners and verify that the corner
// color does not leak outside the intended region.
auto texture = CreateTextureForFixture("nine_patch_corners.png");
flutter::DisplayListBuilder builder;
builder.DrawImageNine(DlImageImpeller::Make(texture),
SkIRect::MakeXYWH(10, 10, 1, 1),
SkRect::MakeXYWH(0, 0, 200, 100),
flutter::DlFilterMode::kNearest, nullptr);
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(DisplayListTest, CanDrawPoints) {
flutter::DisplayListBuilder builder;
SkPoint points[7] = {
Expand Down
2 changes: 1 addition & 1 deletion impeller/display_list/nine_patch_converter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ void NinePatchConverter::DrawNinePatch(const std::shared_ptr<Image>& image,
// DrawImageAtlas.
canvas->DrawImageRect(image, Rect::MakeLTRB(srcX0, srcY0, srcX1, srcY1),
Rect::MakeLTRB(dstX0, dstY0, dstX1, dstY1), *paint,
sampler);
sampler, SourceRectConstraint::kStrict);
}
}
}
Expand Down
1 change: 1 addition & 0 deletions impeller/entity/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ impeller_shaders("entity_shaders") {
"shaders/texture_fill.frag",
"shaders/texture_fill.vert",
"shaders/texture_fill_external.frag",
"shaders/texture_fill_strict_src.frag",
"shaders/tiled_texture_fill.frag",
"shaders/tiled_texture_fill_external.frag",
"shaders/vertices.frag",
Expand Down
1 change: 1 addition & 0 deletions impeller/entity/contents/content_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ ContentContext::ContentContext(
rrect_blur_pipelines_.CreateDefault(*context_, options_trianglestrip);
texture_blend_pipelines_.CreateDefault(*context_, options);
texture_pipelines_.CreateDefault(*context_, options);
texture_strict_src_pipelines_.CreateDefault(*context_, options);
position_uv_pipelines_.CreateDefault(*context_, options);
tiled_texture_pipelines_.CreateDefault(*context_, options);
gaussian_blur_noalpha_decal_pipelines_.CreateDefault(*context_,
Expand Down
10 changes: 10 additions & 0 deletions impeller/entity/contents/content_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
#include "impeller/entity/sweep_gradient_fill.frag.h"
#include "impeller/entity/texture_fill.frag.h"
#include "impeller/entity/texture_fill.vert.h"
#include "impeller/entity/texture_fill_strict_src.frag.h"
#include "impeller/entity/tiled_texture_fill.frag.h"
#include "impeller/entity/uv.comp.h"
#include "impeller/entity/vertices.frag.h"
Expand Down Expand Up @@ -130,6 +131,9 @@ using RRectBlurPipeline =
using BlendPipeline = RenderPipelineT<BlendVertexShader, BlendFragmentShader>;
using TexturePipeline =
RenderPipelineT<TextureFillVertexShader, TextureFillFragmentShader>;
using TextureStrictSrcPipeline =
RenderPipelineT<TextureFillVertexShader,
TextureFillStrictSrcFragmentShader>;
using PositionUVPipeline =
RenderPipelineT<TextureFillVertexShader, TiledTextureFillFragmentShader>;
using TiledTexturePipeline =
Expand Down Expand Up @@ -418,6 +422,11 @@ class ContentContext {
return GetPipeline(texture_pipelines_, opts);
}

std::shared_ptr<Pipeline<PipelineDescriptor>> GetTextureStrictSrcPipeline(
ContentContextOptions opts) const {
return GetPipeline(texture_strict_src_pipelines_, opts);
}

#ifdef IMPELLER_ENABLE_OPENGLES
std::shared_ptr<Pipeline<PipelineDescriptor>> GetTextureExternalPipeline(
ContentContextOptions opts) const {
Expand Down Expand Up @@ -871,6 +880,7 @@ class ContentContext {
mutable Variants<RRectBlurPipeline> rrect_blur_pipelines_;
mutable Variants<BlendPipeline> texture_blend_pipelines_;
mutable Variants<TexturePipeline> texture_pipelines_;
mutable Variants<TextureStrictSrcPipeline> texture_strict_src_pipelines_;
#ifdef IMPELLER_ENABLE_OPENGLES
mutable Variants<TextureExternalPipeline> texture_external_pipelines_;
mutable Variants<TiledTextureExternalPipeline>
Expand Down
45 changes: 36 additions & 9 deletions impeller/entity/contents/texture_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ bool TextureContents::Render(const ContentContext& renderer,

using VS = TextureFillVertexShader;
using FS = TextureFillFragmentShader;
using FSStrictSrc = TextureFillStrictSrcFragmentShader;
using FSExternal = TextureFillExternalFragmentShader;

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

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

VertexBufferBuilder<VS::PerVertexData> vertex_builder;

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

std::shared_ptr<Pipeline<PipelineDescriptor>> pipeline;
#ifdef IMPELLER_ENABLE_OPENGLES
if (is_external_texture) {
pass.SetPipeline(renderer.GetTextureExternalPipeline(pipeline_options));
} else {
pass.SetPipeline(renderer.GetTexturePipeline(pipeline_options));
pipeline = renderer.GetTextureExternalPipeline(pipeline_options);
}
#else
pass.SetPipeline(renderer.GetTexturePipeline(pipeline_options));
#endif // IMPELLER_ENABLE_OPENGLES

if (!pipeline) {
if (strict_source_rect_enabled_) {
pipeline = renderer.GetTextureStrictSrcPipeline(pipeline_options);
} else {
pipeline = renderer.GetTexturePipeline(pipeline_options);
}
}
pass.SetPipeline(pipeline);

pass.SetStencilReference(entity.GetClipDepth());
pass.SetVertexBuffer(vertex_builder.CreateVertexBuffer(host_buffer));
VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
Expand All @@ -178,6 +183,20 @@ bool TextureContents::Render(const ContentContext& renderer,
pass, texture_,
renderer.GetContext()->GetSamplerLibrary()->GetSampler(
sampler_descriptor_));
} else if (strict_source_rect_enabled_) {
// For a strict source rect, shrink the texture coordinate range by half a
// texel to ensure that linear filtering does not sample anything outside
// the source rect bounds.
auto strict_texture_coords =
Rect::MakeSize(texture_->GetSize()).Project(source_rect.Expand(-0.5));

FSStrictSrc::FragInfo frag_info;
frag_info.source_rect = Vector4(strict_texture_coords.GetLTRB());
FSStrictSrc::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
FSStrictSrc::BindTextureSampler(
pass, texture_,
renderer.GetContext()->GetSamplerLibrary()->GetSampler(
sampler_descriptor_));
} else {
FS::BindTextureSampler(
pass, texture_,
Expand All @@ -195,6 +214,14 @@ const Rect& TextureContents::GetSourceRect() const {
return source_rect_;
}

void TextureContents::SetStrictSourceRect(bool strict) {
strict_source_rect_enabled_ = strict;
}

bool TextureContents::GetStrictSourceRect() const {
return strict_source_rect_enabled_;
}

void TextureContents::SetSamplerDescriptor(SamplerDescriptor desc) {
sampler_descriptor_ = std::move(desc);
}
Expand Down
5 changes: 5 additions & 0 deletions impeller/entity/contents/texture_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ class TextureContents final : public Contents {

const Rect& GetSourceRect() const;

void SetStrictSourceRect(bool strict);

bool GetStrictSourceRect() const;

void SetOpacity(Scalar opacity);

Scalar GetOpacity() const;
Expand Down Expand Up @@ -85,6 +89,7 @@ class TextureContents final : public Contents {
std::shared_ptr<Texture> texture_;
SamplerDescriptor sampler_descriptor_ = {};
Rect source_rect_;
bool strict_source_rect_enabled_ = false;
Scalar opacity_ = 1.0f;
Scalar inherited_opacity_ = 1.0f;
bool defer_applying_opacity_ = false;
Expand Down
30 changes: 30 additions & 0 deletions impeller/entity/shaders/texture_fill_strict_src.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

precision mediump float;

#include <impeller/constants.glsl>
#include <impeller/types.glsl>

uniform f16sampler2D texture_sampler;

uniform FragInfo {
vec4 source_rect;
}
frag_info;

in highp vec2 v_texture_coords;
IMPELLER_MAYBE_FLAT in float16_t v_alpha;

out f16vec4 frag_color;

void main() {
vec2 texture_coords = vec2(clamp(v_texture_coords.x, frag_info.source_rect.x,
frag_info.source_rect.z),
clamp(v_texture_coords.y, frag_info.source_rect.y,
frag_info.source_rect.w));
f16vec4 sampled =
texture(texture_sampler, texture_coords, kDefaultMipBiasHalf);
frag_color = sampled * v_alpha;
}
1 change: 1 addition & 0 deletions impeller/fixtures/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ test_fixtures("file_fixtures") {
"flutter_logo_baked.glb",
"kalimba.jpg",
"multiple_stages.hlsl",
"nine_patch_corners.png",
"resources_limit.vert",
"sample.comp",
"sample.frag",
Expand Down
Binary file added impeller/fixtures/nine_patch_corners.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading