mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[Impeller] Linear Gamma to sRGB filter implementation. (flutter/engine#35388)
This commit is contained in:
parent
1711a00c0d
commit
455f59ceb2
@ -582,6 +582,8 @@ FILE: ../../../flutter/impeller/entity/contents/filters/inputs/filter_input.h
|
||||
FILE: ../../../flutter/impeller/entity/contents/filters/inputs/filter_input_unittests.cc
|
||||
FILE: ../../../flutter/impeller/entity/contents/filters/inputs/texture_filter_input.cc
|
||||
FILE: ../../../flutter/impeller/entity/contents/filters/inputs/texture_filter_input.h
|
||||
FILE: ../../../flutter/impeller/entity/contents/filters/linear_to_srgb_filter_contents.cc
|
||||
FILE: ../../../flutter/impeller/entity/contents/filters/linear_to_srgb_filter_contents.h
|
||||
FILE: ../../../flutter/impeller/entity/contents/linear_gradient_contents.cc
|
||||
FILE: ../../../flutter/impeller/entity/contents/linear_gradient_contents.h
|
||||
FILE: ../../../flutter/impeller/entity/contents/radial_gradient_contents.cc
|
||||
@ -644,6 +646,8 @@ FILE: ../../../flutter/impeller/entity/shaders/glyph_atlas.frag
|
||||
FILE: ../../../flutter/impeller/entity/shaders/glyph_atlas.vert
|
||||
FILE: ../../../flutter/impeller/entity/shaders/gradient_fill.vert
|
||||
FILE: ../../../flutter/impeller/entity/shaders/linear_gradient_fill.frag
|
||||
FILE: ../../../flutter/impeller/entity/shaders/linear_to_srgb_filter.frag
|
||||
FILE: ../../../flutter/impeller/entity/shaders/linear_to_srgb_filter.vert
|
||||
FILE: ../../../flutter/impeller/entity/shaders/radial_gradient_fill.frag
|
||||
FILE: ../../../flutter/impeller/entity/shaders/rrect_blur.frag
|
||||
FILE: ../../../flutter/impeller/entity/shaders/rrect_blur.vert
|
||||
|
||||
@ -441,9 +441,10 @@ void DisplayListDispatcher::setColorFilter(
|
||||
UNIMPLEMENTED;
|
||||
break;
|
||||
case flutter::DlColorFilterType::kLinearToSrgbGamma:
|
||||
FML_LOG(ERROR) << "requested DlColorFilterType::kLinearToSrgbGamma";
|
||||
UNIMPLEMENTED;
|
||||
break;
|
||||
paint_.color_filter = [](FilterInput::Ref input) {
|
||||
return FilterContents::MakeLinearToSrgbFilter({input});
|
||||
};
|
||||
return;
|
||||
case flutter::DlColorFilterType::kUnknown:
|
||||
FML_LOG(ERROR) << "requested DlColorFilterType::kUnknown";
|
||||
UNIMPLEMENTED;
|
||||
|
||||
@ -37,6 +37,8 @@ impeller_shaders("entity_shaders") {
|
||||
"shaders/glyph_atlas.frag",
|
||||
"shaders/glyph_atlas.vert",
|
||||
"shaders/gradient_fill.vert",
|
||||
"shaders/linear_to_srgb_filter.frag",
|
||||
"shaders/linear_to_srgb_filter.vert",
|
||||
"shaders/linear_gradient_fill.frag",
|
||||
"shaders/radial_gradient_fill.frag",
|
||||
"shaders/rrect_blur.vert",
|
||||
@ -85,6 +87,8 @@ impeller_component("entity") {
|
||||
"contents/filters/inputs/filter_input.h",
|
||||
"contents/filters/inputs/texture_filter_input.cc",
|
||||
"contents/filters/inputs/texture_filter_input.h",
|
||||
"contents/filters/linear_to_srgb_filter_contents.cc",
|
||||
"contents/filters/linear_to_srgb_filter_contents.h",
|
||||
"contents/linear_gradient_contents.cc",
|
||||
"contents/linear_gradient_contents.h",
|
||||
"contents/radial_gradient_contents.cc",
|
||||
|
||||
@ -199,6 +199,8 @@ ContentContext::ContentContext(std::shared_ptr<Context> context)
|
||||
CreateDefaultPipeline<BorderMaskBlurPipeline>(*context_);
|
||||
color_matrix_color_filter_pipelines_[{}] =
|
||||
CreateDefaultPipeline<ColorMatrixColorFilterPipeline>(*context_);
|
||||
linear_to_srgb_filter_pipelines_[{}] =
|
||||
CreateDefaultPipeline<LinearToSrgbFilterPipeline>(*context_);
|
||||
solid_stroke_pipelines_[{}] =
|
||||
CreateDefaultPipeline<SolidStrokePipeline>(*context_);
|
||||
glyph_atlas_pipelines_[{}] =
|
||||
|
||||
@ -42,6 +42,8 @@
|
||||
#include "impeller/entity/glyph_atlas.vert.h"
|
||||
#include "impeller/entity/gradient_fill.vert.h"
|
||||
#include "impeller/entity/linear_gradient_fill.frag.h"
|
||||
#include "impeller/entity/linear_to_srgb_filter.frag.h"
|
||||
#include "impeller/entity/linear_to_srgb_filter.vert.h"
|
||||
#include "impeller/entity/radial_gradient_fill.frag.h"
|
||||
#include "impeller/entity/rrect_blur.frag.h"
|
||||
#include "impeller/entity/rrect_blur.vert.h"
|
||||
@ -113,6 +115,8 @@ using BorderMaskBlurPipeline =
|
||||
using ColorMatrixColorFilterPipeline =
|
||||
PipelineT<ColorMatrixColorFilterVertexShader,
|
||||
ColorMatrixColorFilterFragmentShader>;
|
||||
using LinearToSrgbFilterPipeline =
|
||||
PipelineT<LinearToSrgbFilterVertexShader, LinearToSrgbFilterFragmentShader>;
|
||||
using SolidStrokePipeline =
|
||||
PipelineT<SolidStrokeVertexShader, SolidStrokeFragmentShader>;
|
||||
using GlyphAtlasPipeline =
|
||||
@ -211,6 +215,11 @@ class ContentContext {
|
||||
return GetPipeline(color_matrix_color_filter_pipelines_, opts);
|
||||
}
|
||||
|
||||
std::shared_ptr<Pipeline> GetLinearToSrgbFilterPipeline(
|
||||
ContentContextOptions opts) const {
|
||||
return GetPipeline(linear_to_srgb_filter_pipelines_, opts);
|
||||
}
|
||||
|
||||
std::shared_ptr<Pipeline> GetSolidStrokePipeline(
|
||||
ContentContextOptions opts) const {
|
||||
return GetPipeline(solid_stroke_pipelines_, opts);
|
||||
@ -345,6 +354,7 @@ class ContentContext {
|
||||
mutable Variants<BorderMaskBlurPipeline> border_mask_blur_pipelines_;
|
||||
mutable Variants<ColorMatrixColorFilterPipeline>
|
||||
color_matrix_color_filter_pipelines_;
|
||||
mutable Variants<LinearToSrgbFilterPipeline> linear_to_srgb_filter_pipelines_;
|
||||
mutable Variants<SolidStrokePipeline> solid_stroke_pipelines_;
|
||||
mutable Variants<ClipPipeline> clip_pipelines_;
|
||||
mutable Variants<GlyphAtlasPipeline> glyph_atlas_pipelines_;
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
#include "impeller/entity/contents/filters/color_matrix_filter_contents.h"
|
||||
#include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h"
|
||||
#include "impeller/entity/contents/filters/inputs/filter_input.h"
|
||||
#include "impeller/entity/contents/filters/linear_to_srgb_filter_contents.h"
|
||||
#include "impeller/entity/contents/texture_contents.h"
|
||||
#include "impeller/entity/entity.h"
|
||||
#include "impeller/geometry/path_builder.h"
|
||||
@ -122,6 +123,13 @@ std::shared_ptr<FilterContents> FilterContents::MakeColorMatrix(
|
||||
return filter;
|
||||
}
|
||||
|
||||
std::shared_ptr<FilterContents> FilterContents::MakeLinearToSrgbFilter(
|
||||
FilterInput::Ref input) {
|
||||
auto filter = std::make_shared<LinearToSrgbFilterContents>();
|
||||
filter->SetInputs({input});
|
||||
return filter;
|
||||
}
|
||||
|
||||
FilterContents::FilterContents() = default;
|
||||
|
||||
FilterContents::~FilterContents() = default;
|
||||
|
||||
@ -66,6 +66,9 @@ class FilterContents : public Contents {
|
||||
FilterInput::Ref input,
|
||||
const ColorMatrix& matrix);
|
||||
|
||||
static std::shared_ptr<FilterContents> MakeLinearToSrgbFilter(
|
||||
FilterInput::Ref input);
|
||||
|
||||
FilterContents();
|
||||
|
||||
~FilterContents() override;
|
||||
|
||||
@ -0,0 +1,83 @@
|
||||
// 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.
|
||||
|
||||
#include "impeller/entity/contents/filters/linear_to_srgb_filter_contents.h"
|
||||
|
||||
#include "impeller/entity/contents/content_context.h"
|
||||
#include "impeller/entity/contents/contents.h"
|
||||
#include "impeller/geometry/point.h"
|
||||
#include "impeller/geometry/vector.h"
|
||||
#include "impeller/renderer/render_pass.h"
|
||||
#include "impeller/renderer/sampler_library.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
LinearToSrgbFilterContents::LinearToSrgbFilterContents() = default;
|
||||
|
||||
LinearToSrgbFilterContents::~LinearToSrgbFilterContents() = default;
|
||||
|
||||
std::optional<Snapshot> LinearToSrgbFilterContents::RenderFilter(
|
||||
const FilterInput::Vector& inputs,
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
const Rect& coverage) const {
|
||||
if (inputs.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
using VS = LinearToSrgbFilterPipeline::VertexShader;
|
||||
using FS = LinearToSrgbFilterPipeline::FragmentShader;
|
||||
|
||||
auto input_snapshot = inputs[0]->GetSnapshot(renderer, entity);
|
||||
if (!input_snapshot.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ContentContext::SubpassCallback callback = [&](const ContentContext& renderer,
|
||||
RenderPass& pass) {
|
||||
Command cmd;
|
||||
cmd.label = "Linear to sRGB Filter";
|
||||
|
||||
auto options = OptionsFromPass(pass);
|
||||
options.blend_mode = Entity::BlendMode::kSource;
|
||||
cmd.pipeline = renderer.GetLinearToSrgbFilterPipeline(options);
|
||||
|
||||
VertexBufferBuilder<VS::PerVertexData> vtx_builder;
|
||||
vtx_builder.AddVertices({
|
||||
{Point(0, 0)},
|
||||
{Point(1, 0)},
|
||||
{Point(1, 1)},
|
||||
{Point(0, 0)},
|
||||
{Point(1, 1)},
|
||||
{Point(0, 1)},
|
||||
});
|
||||
|
||||
auto& host_buffer = pass.GetTransientsBuffer();
|
||||
auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
|
||||
cmd.BindVertices(vtx_buffer);
|
||||
|
||||
VS::FrameInfo frame_info;
|
||||
frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
|
||||
|
||||
auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({});
|
||||
FS::BindInputTexture(cmd, input_snapshot->texture, sampler);
|
||||
|
||||
VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info));
|
||||
|
||||
return pass.AddCommand(std::move(cmd));
|
||||
};
|
||||
|
||||
auto out_texture =
|
||||
renderer.MakeSubpass(input_snapshot->texture->GetSize(), callback);
|
||||
if (!out_texture) {
|
||||
return std::nullopt;
|
||||
}
|
||||
out_texture->SetLabel("LinearToSrgb Texture");
|
||||
|
||||
return Snapshot{.texture = out_texture,
|
||||
.transform = input_snapshot->transform,
|
||||
.sampler_descriptor = input_snapshot->sampler_descriptor};
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
@ -0,0 +1,29 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "impeller/entity/contents/filters/filter_contents.h"
|
||||
#include "impeller/entity/contents/filters/inputs/filter_input.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
class LinearToSrgbFilterContents final : public FilterContents {
|
||||
public:
|
||||
LinearToSrgbFilterContents();
|
||||
|
||||
~LinearToSrgbFilterContents() override;
|
||||
|
||||
private:
|
||||
// |FilterContents|
|
||||
std::optional<Snapshot> RenderFilter(
|
||||
const FilterInput::Vector& input_textures,
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
const Rect& coverage) const override;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(LinearToSrgbFilterContents);
|
||||
};
|
||||
|
||||
} // namespace impeller
|
||||
@ -8,6 +8,7 @@
|
||||
#include "flutter/testing/testing.h"
|
||||
#include "impeller/entity/contents/atlas_contents.h"
|
||||
#include "impeller/entity/contents/clip_contents.h"
|
||||
#include "impeller/entity/contents/contents.h"
|
||||
#include "impeller/entity/contents/filters/blend_filter_contents.h"
|
||||
#include "impeller/entity/contents/filters/filter_contents.h"
|
||||
#include "impeller/entity/contents/filters/inputs/filter_input.h"
|
||||
@ -1542,5 +1543,56 @@ TEST_P(EntityTest, ColorMatrixFilterEditable) {
|
||||
ASSERT_TRUE(OpenPlaygroundHere(callback));
|
||||
}
|
||||
|
||||
TEST_P(EntityTest, LinearToSrgbFilterCoverageIsCorrect) {
|
||||
// Set up a simple color background.
|
||||
auto fill = std::make_shared<SolidColorContents>();
|
||||
fill->SetPath(
|
||||
PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 300, 400)).TakePath());
|
||||
fill->SetColor(Color::MintCream());
|
||||
|
||||
auto filter = FilterContents::MakeLinearToSrgbFilter(FilterInput::Make(fill));
|
||||
|
||||
Entity e;
|
||||
e.SetTransformation(Matrix());
|
||||
|
||||
// Confirm that the actual filter coverage matches the expected coverage.
|
||||
auto actual = filter->GetCoverage(e);
|
||||
auto expected = Rect::MakeXYWH(0, 0, 300, 400);
|
||||
|
||||
ASSERT_TRUE(actual.has_value());
|
||||
ASSERT_RECT_NEAR(actual.value(), expected);
|
||||
}
|
||||
|
||||
TEST_P(EntityTest, LinearToSrgbFilter) {
|
||||
auto image = CreateTextureForFixture("kalimba.jpg");
|
||||
ASSERT_TRUE(image);
|
||||
|
||||
auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
|
||||
auto filtered =
|
||||
FilterContents::MakeLinearToSrgbFilter(FilterInput::Make(image));
|
||||
|
||||
// Define the entity that will serve as the control image as a Gaussian blur
|
||||
// filter with no filter at all.
|
||||
Entity entity_left;
|
||||
entity_left.SetTransformation(Matrix::MakeScale(GetContentScale()) *
|
||||
Matrix::MakeTranslation({100, 300}) *
|
||||
Matrix::MakeScale(Vector2{0.5, 0.5}));
|
||||
auto unfiltered = FilterContents::MakeGaussianBlur(FilterInput::Make(image),
|
||||
Sigma{0}, Sigma{0});
|
||||
entity_left.SetContents(unfiltered);
|
||||
|
||||
// Define the entity that will be filtered from linear to sRGB.
|
||||
Entity entity_right;
|
||||
entity_right.SetTransformation(Matrix::MakeScale(GetContentScale()) *
|
||||
Matrix::MakeTranslation({500, 300}) *
|
||||
Matrix::MakeScale(Vector2{0.5, 0.5}));
|
||||
entity_right.SetContents(filtered);
|
||||
return entity_left.Render(context, pass) &&
|
||||
entity_right.Render(context, pass);
|
||||
};
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(callback));
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace impeller
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
// 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.
|
||||
|
||||
#include <impeller/color.glsl>
|
||||
|
||||
// A color filter that applies the sRGB gamma curve to the color.
|
||||
//
|
||||
// This filter is used so that the colors are suitable for display in monitors.
|
||||
|
||||
uniform sampler2D input_texture;
|
||||
|
||||
in vec2 v_position;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
vec4 input_color = texture(input_texture, v_position);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (input_color[i] <= 0.0031308) {
|
||||
input_color[i] = input_color[i] * 12.92;
|
||||
} else {
|
||||
input_color[i] = 1.055 * pow(input_color[i], (1.0 / 2.4)) - 0.055;
|
||||
}
|
||||
}
|
||||
|
||||
frag_color = input_color;
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
// 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.
|
||||
|
||||
uniform FrameInfo {
|
||||
mat4 mvp;
|
||||
} frame_info;
|
||||
|
||||
in vec2 position;
|
||||
out vec2 v_position;
|
||||
|
||||
void main() {
|
||||
v_position = position;
|
||||
gl_Position = frame_info.mvp * vec4(position, 0.0, 1.0);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user