[Impeller] Add explicit color filter types to Aiks. (flutter/engine#43342)

Part of https://github.com/flutter/flutter/issues/127232.

Adds an explicit ColorFilter concept to Aiks which wraps the GPU and CPU
implementations. We're not using the CPU implementations yet, but I'll
be getting it wired up in a follow-up patch.
This commit is contained in:
Brandon DeRosier
2023-06-30 18:42:21 -07:00
committed by GitHub
parent 847fcdd753
commit c90389c9cd
9 changed files with 317 additions and 98 deletions

View File

@@ -1019,6 +1019,8 @@ ORIGIN: ../../../flutter/impeller/aiks/aiks_playground.cc + ../../../flutter/LIC
ORIGIN: ../../../flutter/impeller/aiks/aiks_playground.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/aiks/canvas.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/aiks/canvas.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/aiks/color_filter.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/aiks/color_filter.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/aiks/color_source.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/aiks/color_source.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/aiks/image.cc + ../../../flutter/LICENSE
@@ -3701,6 +3703,8 @@ FILE: ../../../flutter/impeller/aiks/aiks_playground.cc
FILE: ../../../flutter/impeller/aiks/aiks_playground.h
FILE: ../../../flutter/impeller/aiks/canvas.cc
FILE: ../../../flutter/impeller/aiks/canvas.h
FILE: ../../../flutter/impeller/aiks/color_filter.cc
FILE: ../../../flutter/impeller/aiks/color_filter.h
FILE: ../../../flutter/impeller/aiks/color_source.cc
FILE: ../../../flutter/impeller/aiks/color_source.h
FILE: ../../../flutter/impeller/aiks/image.cc

View File

@@ -10,6 +10,8 @@ impeller_component("aiks") {
"aiks_context.h",
"canvas.cc",
"canvas.h",
"color_filter.cc",
"color_filter.h",
"color_source.cc",
"color_source.h",
"image.cc",

View File

@@ -65,10 +65,7 @@ TEST_P(AiksTest, RotateColorFilteredPath) {
.stroke_join = Join::kRound,
.style = Paint::Style::kStroke,
.color_filter =
[](FilterInput::Ref input) {
return ColorFilterContents::MakeBlend(
BlendMode::kSourceIn, {std::move(input)}, Color::AliceBlue());
},
ColorFilter::MakeBlend(BlendMode::kSourceIn, Color::AliceBlue()),
};
canvas.DrawPath(arrow_stem, paint);
@@ -1924,10 +1921,8 @@ TEST_P(AiksTest, PaintWithFilters) {
ASSERT_FALSE(paint.HasColorFilter());
paint.color_filter = [](FilterInput::Ref input) {
return ColorFilterContents::MakeBlend(BlendMode::kSourceOver,
{std::move(input)}, Color::Blue());
};
paint.color_filter =
ColorFilter::MakeBlend(BlendMode::kSourceOver, Color::Blue());
ASSERT_TRUE(paint.HasColorFilter());
@@ -1954,10 +1949,8 @@ TEST_P(AiksTest, OpacityPeepHoleApplicationTest) {
auto rect = Rect::MakeLTRB(0, 0, 100, 100);
Paint paint;
paint.color = Color::White().WithAlpha(0.5);
paint.color_filter = [](FilterInput::Ref input) {
return ColorFilterContents::MakeBlend(BlendMode::kSourceOver,
{std::move(input)}, Color::Blue());
};
paint.color_filter =
ColorFilter::MakeBlend(BlendMode::kSourceOver, Color::Blue());
// Paint has color filter, can't elide.
auto delegate = std::make_shared<OpacityPeepholePassDelegate>(paint, rect);
@@ -2106,10 +2099,7 @@ TEST_P(AiksTest, ForegroundBlendSubpassCollapseOptimization) {
canvas.SaveLayer({
.color_filter =
[](FilterInput::Ref input) {
return ColorFilterContents::MakeBlend(
BlendMode::kColorDodge, {std::move(input)}, Color::Red());
},
ColorFilter::MakeBlend(BlendMode::kColorDodge, Color::Red()),
});
canvas.Translate({500, 300, 0});
@@ -2124,15 +2114,13 @@ TEST_P(AiksTest, ColorMatrixFilterSubpassCollapseOptimization) {
canvas.SaveLayer({
.color_filter =
[](FilterInput::Ref input) {
return ColorFilterContents::MakeColorMatrix(
std::move(input), {.array = {
-1.0, 0, 0, 1.0, 0, //
0, -1.0, 0, 1.0, 0, //
0, 0, -1.0, 1.0, 0, //
1.0, 1.0, 1.0, 1.0, 0 //
}});
},
ColorFilter::MakeMatrix({.array =
{
-1.0, 0, 0, 1.0, 0, //
0, -1.0, 0, 1.0, 0, //
0, 0, -1.0, 1.0, 0, //
1.0, 1.0, 1.0, 1.0, 0 //
}}),
});
canvas.Translate({500, 300, 0});
@@ -2146,11 +2134,7 @@ TEST_P(AiksTest, LinearToSrgbFilterSubpassCollapseOptimization) {
Canvas canvas;
canvas.SaveLayer({
.color_filter =
[](FilterInput::Ref input) {
return ColorFilterContents::MakeLinearToSrgbFilter(
std::move(input));
},
.color_filter = ColorFilter::MakeLinearToSrgb(),
});
canvas.Translate({500, 300, 0});
@@ -2164,11 +2148,7 @@ TEST_P(AiksTest, SrgbToLinearFilterSubpassCollapseOptimization) {
Canvas canvas;
canvas.SaveLayer({
.color_filter =
[](FilterInput::Ref input) {
return ColorFilterContents::MakeSrgbToLinearFilter(
std::move(input));
},
.color_filter = ColorFilter::MakeSrgbToLinear(),
});
canvas.Translate({500, 300, 0});
@@ -2290,10 +2270,7 @@ TEST_P(AiksTest, TranslucentSaveLayerWithBlendColorFilterDrawsCorrectly) {
canvas.SaveLayer({
.color = Color::Black().WithAlpha(0.5),
.color_filter =
[](FilterInput::Ref input) {
return ColorFilterContents::MakeBlend(
BlendMode::kDestinationOver, {std::move(input)}, Color::Red());
},
ColorFilter::MakeBlend(BlendMode::kDestinationOver, Color::Red()),
});
canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()});
canvas.Restore();
@@ -2330,10 +2307,7 @@ TEST_P(AiksTest, TranslucentSaveLayerWithColorAndImageFilterDrawsCorrectly) {
canvas.SaveLayer({
.color = Color::Black().WithAlpha(0.5),
.color_filter =
[](FilterInput::Ref input) {
return ColorFilterContents::MakeBlend(
BlendMode::kDestinationOver, {std::move(input)}, Color::Red());
},
ColorFilter::MakeBlend(BlendMode::kDestinationOver, Color::Red()),
});
canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()});
@@ -2363,16 +2337,13 @@ TEST_P(AiksTest, TranslucentSaveLayerWithColorMatrixColorFilterDrawsCorrectly) {
canvas.SaveLayer({
.color = Color::Black().WithAlpha(0.5),
.color_filter =
[](FilterInput::Ref input) {
return ColorFilterContents::MakeColorMatrix({std::move(input)},
{.array = {
1, 0, 0, 0, 0, //
0, 1, 0, 0, 0, //
0, 0, 1, 0, 0, //
0, 0, 0, 2, 0 //
}});
},
.color_filter = ColorFilter::MakeMatrix({.array =
{
1, 0, 0, 0, 0, //
0, 1, 0, 0, 0, //
0, 0, 1, 0, 0, //
0, 0, 0, 2, 0 //
}}),
});
canvas.DrawImage(image, {100, 500}, {});
canvas.Restore();
@@ -2427,10 +2398,7 @@ TEST_P(AiksTest,
}});
},
.color_filter =
[](FilterInput::Ref input) {
return ColorFilterContents::MakeBlend(
BlendMode::kModulate, {std::move(input)}, Color::Green());
},
ColorFilter::MakeBlend(BlendMode::kModulate, Color::Green()),
});
canvas.DrawImage(image, {100, 500}, {});
canvas.Restore();
@@ -2640,11 +2608,8 @@ TEST_P(AiksTest, CanRenderForegroundBlendWithMaskBlur) {
canvas.DrawCircle({400, 400}, 200,
{
.color = Color::White(),
.color_filter =
[](const FilterInput::Ref& input) {
return ColorFilterContents::MakeBlend(
BlendMode::kSource, {input}, Color::Green());
},
.color_filter = ColorFilter::MakeBlend(
BlendMode::kSource, Color::Green()),
.mask_blur_descriptor =
Paint::MaskBlurDescriptor{
.style = FilterContents::BlurStyle::kNormal,
@@ -2664,11 +2629,8 @@ TEST_P(AiksTest, CanRenderForegroundAdvancedBlendWithMaskBlur) {
canvas.DrawCircle({400, 400}, 200,
{
.color = Color::White(),
.color_filter =
[](const FilterInput::Ref& input) {
return ColorFilterContents::MakeBlend(
BlendMode::kColor, {input}, Color::Green());
},
.color_filter = ColorFilter::MakeBlend(
BlendMode::kColor, Color::Green()),
.mask_blur_descriptor =
Paint::MaskBlurDescriptor{
.style = FilterContents::BlurStyle::kNormal,

View File

@@ -0,0 +1,127 @@
// 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/aiks/color_filter.h"
#include "impeller/entity/contents/filters/color_filter_contents.h"
#include "impeller/entity/contents/filters/inputs/filter_input.h"
#include "impeller/geometry/color.h"
namespace impeller {
/*******************************************************************************
******* ColorFilter
******************************************************************************/
ColorFilter::ColorFilter() = default;
ColorFilter::~ColorFilter() = default;
std::shared_ptr<ColorFilter> ColorFilter::MakeBlend(BlendMode blend_mode,
Color color) {
return std::make_shared<BlendColorFilter>(blend_mode, color);
}
std::shared_ptr<ColorFilter> ColorFilter::MakeMatrix(ColorMatrix color_matrix) {
return std::make_shared<MatrixColorFilter>(color_matrix);
}
std::shared_ptr<ColorFilter> ColorFilter::MakeSrgbToLinear() {
return std::make_shared<SrgbToLinearColorFilter>();
}
std::shared_ptr<ColorFilter> ColorFilter::MakeLinearToSrgb() {
return std::make_shared<LinearToSrgbColorFilter>();
}
/*******************************************************************************
******* BlendColorFilter
******************************************************************************/
BlendColorFilter::BlendColorFilter(BlendMode blend_mode, Color color)
: blend_mode_(blend_mode), color_(color) {}
BlendColorFilter::~BlendColorFilter() = default;
std::shared_ptr<ColorFilterContents> BlendColorFilter::GetColorFilter(
std::shared_ptr<FilterInput> input,
bool absorb_opacity) const {
auto filter =
ColorFilterContents::MakeBlend(blend_mode_, {std::move(input)}, color_);
filter->SetAbsorbOpacity(absorb_opacity);
return filter;
}
ColorFilter::ColorFilterProc BlendColorFilter::GetCPUColorFilterProc() const {
return [filter_blend_mode = blend_mode_, filter_color = color_](Color color) {
return color.Blend(filter_color, filter_blend_mode);
};
}
/*******************************************************************************
******* MatrixColorFilter
******************************************************************************/
MatrixColorFilter::MatrixColorFilter(ColorMatrix color_matrix)
: color_matrix_(color_matrix) {}
MatrixColorFilter::~MatrixColorFilter() = default;
std::shared_ptr<ColorFilterContents> MatrixColorFilter::GetColorFilter(
std::shared_ptr<FilterInput> input,
bool absorb_opacity) const {
auto filter =
ColorFilterContents::MakeColorMatrix({std::move(input)}, color_matrix_);
filter->SetAbsorbOpacity(absorb_opacity);
return filter;
}
ColorFilter::ColorFilterProc MatrixColorFilter::GetCPUColorFilterProc() const {
return [color_matrix = color_matrix_](Color color) {
return color.ApplyColorMatrix(color_matrix);
};
}
/*******************************************************************************
******* SrgbToLinearColorFilter
******************************************************************************/
SrgbToLinearColorFilter::SrgbToLinearColorFilter() = default;
SrgbToLinearColorFilter::~SrgbToLinearColorFilter() = default;
std::shared_ptr<ColorFilterContents> SrgbToLinearColorFilter::GetColorFilter(
std::shared_ptr<FilterInput> input,
bool absorb_opacity) const {
auto filter = ColorFilterContents::MakeSrgbToLinearFilter({std::move(input)});
filter->SetAbsorbOpacity(absorb_opacity);
return filter;
}
ColorFilter::ColorFilterProc SrgbToLinearColorFilter::GetCPUColorFilterProc()
const {
return [](Color color) { return color.SRGBToLinear(); };
}
/*******************************************************************************
******* LinearToSrgbColorFilter
******************************************************************************/
LinearToSrgbColorFilter::LinearToSrgbColorFilter() = default;
LinearToSrgbColorFilter::~LinearToSrgbColorFilter() = default;
std::shared_ptr<ColorFilterContents> LinearToSrgbColorFilter::GetColorFilter(
std::shared_ptr<FilterInput> input,
bool absorb_opacity) const {
auto filter = ColorFilterContents::MakeSrgbToLinearFilter({std::move(input)});
filter->SetAbsorbOpacity(absorb_opacity);
return filter;
}
ColorFilter::ColorFilterProc LinearToSrgbColorFilter::GetCPUColorFilterProc()
const {
return [](Color color) { return color.LinearToSRGB(); };
}
} // namespace impeller

View File

@@ -0,0 +1,125 @@
// 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/color_filter_contents.h"
#include "impeller/geometry/color.h"
namespace impeller {
struct Paint;
/*******************************************************************************
******* ColorFilter
******************************************************************************/
class ColorFilter {
public:
using ColorFilterProc = std::function<Color(Color)>;
ColorFilter();
virtual ~ColorFilter();
static std::shared_ptr<ColorFilter> MakeBlend(BlendMode blend_mode,
Color color);
static std::shared_ptr<ColorFilter> MakeMatrix(ColorMatrix color_matrix);
static std::shared_ptr<ColorFilter> MakeSrgbToLinear();
static std::shared_ptr<ColorFilter> MakeLinearToSrgb();
virtual std::shared_ptr<ColorFilterContents> GetColorFilter(
std::shared_ptr<FilterInput> input,
bool absorb_opacity) const = 0;
virtual ColorFilterProc GetCPUColorFilterProc() const = 0;
};
/*******************************************************************************
******* BlendColorFilter
******************************************************************************/
class BlendColorFilter final : public ColorFilter {
public:
BlendColorFilter(BlendMode blend_mode, Color color);
~BlendColorFilter() override;
// |ColorFilter|
std::shared_ptr<ColorFilterContents> GetColorFilter(
std::shared_ptr<FilterInput> input,
bool absorb_opacity) const override;
// |ColorFilter|
ColorFilterProc GetCPUColorFilterProc() const override;
private:
BlendMode blend_mode_;
Color color_;
};
/*******************************************************************************
******* MatrixColorFilter
******************************************************************************/
class MatrixColorFilter final : public ColorFilter {
public:
explicit MatrixColorFilter(ColorMatrix color_matrix);
~MatrixColorFilter() override;
// |ColorFilter|
std::shared_ptr<ColorFilterContents> GetColorFilter(
std::shared_ptr<FilterInput> input,
bool absorb_opacity) const override;
// |ColorFilter|
ColorFilterProc GetCPUColorFilterProc() const override;
private:
ColorMatrix color_matrix_;
};
/*******************************************************************************
******* SrgbToLinearColorFilter
******************************************************************************/
class SrgbToLinearColorFilter final : public ColorFilter {
public:
explicit SrgbToLinearColorFilter();
~SrgbToLinearColorFilter() override;
// |ColorFilter|
std::shared_ptr<ColorFilterContents> GetColorFilter(
std::shared_ptr<FilterInput> input,
bool absorb_opacity) const override;
// |ColorFilter|
ColorFilterProc GetCPUColorFilterProc() const override;
};
/*******************************************************************************
******* LinearToSrgbColorFilter
******************************************************************************/
class LinearToSrgbColorFilter final : public ColorFilter {
public:
explicit LinearToSrgbColorFilter();
~LinearToSrgbColorFilter() override;
// |ColorFilter|
std::shared_ptr<ColorFilterContents> GetColorFilter(
std::shared_ptr<FilterInput> input,
bool absorb_opacity) const override;
// |ColorFilter|
ColorFilterProc GetCPUColorFilterProc() const override;
};
} // namespace impeller

View File

@@ -172,7 +172,13 @@ ColorSource ColorSource::MakeImage(std::shared_ptr<Texture> texture,
contents->SetTileModes(x_tile_mode, y_tile_mode);
contents->SetSamplerDescriptor(sampler_descriptor);
contents->SetEffectTransform(effect_transform);
contents->SetColorFilter(paint.color_filter);
if (paint.color_filter) {
TiledTextureContents::ColorFilterProc filter_proc =
[color_filter = paint.color_filter](FilterInput::Ref input) {
return color_filter->GetColorFilter(std::move(input), false);
};
contents->SetColorFilter(filter_proc);
}
contents->SetColorSourceSize(Size::Ceil(texture->GetSize()));
return contents;
};

View File

@@ -90,11 +90,8 @@ std::shared_ptr<Contents> Paint::WithColorFilter(
return input;
}
if (color_filter) {
auto color_filter_contents = color_filter(FilterInput::Make(input));
if (color_filter_contents) {
color_filter_contents->SetAbsorbOpacity(absorb_opacity);
}
input = color_filter_contents;
input =
color_filter->GetColorFilter(FilterInput::Make(input), absorb_opacity);
}
return input;
}

View File

@@ -7,6 +7,7 @@
#include <memory>
#include "flutter/fml/macros.h"
#include "impeller/aiks/color_filter.h"
#include "impeller/aiks/color_source.h"
#include "impeller/entity/contents/contents.h"
#include "impeller/entity/contents/filters/color_filter_contents.h"
@@ -25,8 +26,6 @@ struct Paint {
FilterInput::Ref,
const Matrix& effect_transform,
bool is_subpass)>;
using ColorFilterProc =
std::function<std::shared_ptr<ColorFilterContents>(FilterInput::Ref)>;
using MaskFilterProc = std::function<std::shared_ptr<FilterContents>(
FilterInput::Ref,
bool is_solid_color,
@@ -62,7 +61,7 @@ struct Paint {
bool invert_colors = false;
ImageFilterProc image_filter = nullptr;
ColorFilterProc color_filter = nullptr;
std::shared_ptr<ColorFilter> color_filter;
std::optional<MaskBlurDescriptor> mask_blur_descriptor;
/// @brief Wrap this paint's configured filters to the given contents.

View File

@@ -14,6 +14,7 @@
#include "flutter/fml/logging.h"
#include "flutter/fml/trace_event.h"
#include "impeller/aiks/color_filter.h"
#include "impeller/core/formats.h"
#include "impeller/display_list/dl_image_impeller.h"
#include "impeller/display_list/dl_vertices_geometry.h"
@@ -474,7 +475,7 @@ void DlDispatcher::setColorSource(const flutter::DlColorSource* source) {
}
}
static Paint::ColorFilterProc ToColorFilterProc(
static std::shared_ptr<ColorFilter> ToColorFilter(
const flutter::DlColorFilter* filter) {
if (filter == nullptr) {
return nullptr;
@@ -484,28 +485,18 @@ static Paint::ColorFilterProc ToColorFilterProc(
auto dl_blend = filter->asBlend();
auto blend_mode = ToBlendMode(dl_blend->mode());
auto color = skia_conversions::ToColor(dl_blend->color());
return [blend_mode, color](FilterInput::Ref input) {
return ColorFilterContents::MakeBlend(blend_mode, {std::move(input)},
color);
};
return ColorFilter::MakeBlend(blend_mode, color);
}
case flutter::DlColorFilterType::kMatrix: {
const flutter::DlMatrixColorFilter* dl_matrix = filter->asMatrix();
impeller::ColorMatrix color_matrix;
dl_matrix->get_matrix(color_matrix.array);
return [color_matrix](FilterInput::Ref input) {
return ColorFilterContents::MakeColorMatrix({std::move(input)},
color_matrix);
};
return ColorFilter::MakeMatrix(color_matrix);
}
case flutter::DlColorFilterType::kSrgbToLinearGamma:
return [](FilterInput::Ref input) {
return ColorFilterContents::MakeSrgbToLinearFilter({std::move(input)});
};
return ColorFilter::MakeSrgbToLinear();
case flutter::DlColorFilterType::kLinearToSrgbGamma:
return [](FilterInput::Ref input) {
return ColorFilterContents::MakeLinearToSrgbFilter({std::move(input)});
};
return ColorFilter::MakeLinearToSrgb();
}
return nullptr;
}
@@ -513,7 +504,7 @@ static Paint::ColorFilterProc ToColorFilterProc(
// |flutter::DlOpReceiver|
void DlDispatcher::setColorFilter(const flutter::DlColorFilter* filter) {
// Needs https://github.com/flutter/flutter/issues/95434
paint_.color_filter = ToColorFilterProc(filter);
paint_.color_filter = ToColorFilter(filter);
}
// |flutter::DlOpReceiver|
@@ -662,14 +653,20 @@ static Paint::ImageFilterProc ToImageFilterProc(
case flutter::DlImageFilterType::kColorFilter: {
auto color_filter_image_filter = filter->asColorFilter();
FML_DCHECK(color_filter_image_filter);
auto color_filter_proc =
ToColorFilterProc(color_filter_image_filter->color_filter().get());
if (!color_filter_proc) {
auto color_filter =
ToColorFilter(color_filter_image_filter->color_filter().get());
if (!color_filter) {
return nullptr;
}
return [color_filter = color_filter_proc](
FilterInput::Ref input, const Matrix& effect_transform,
bool is_subpass) { return color_filter(std::move(input)); };
return [filter = color_filter](FilterInput::Ref input,
const Matrix& effect_transform,
bool is_subpass) {
// When color filters are used as image filters, set the color filter's
// "absorb opacity" flag to false. For image filters, the snapshot
// opacity needs to be deferred until the result of the filter chain is
// being blended with the layer.
return filter->GetColorFilter(std::move(input), false);
};
break;
}
case flutter::DlImageFilterType::kLocalMatrix: {