Skip to content

Commit

Permalink
[Impeller] Redo simplify invert colors. (flutter#46416)
Browse files Browse the repository at this point in the history
Now with fewer state bugs.

Fixes flutter/flutter#135699
  • Loading branch information
jonahwilliams authored Sep 29, 2023
1 parent 58fd524 commit f7f4b51
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 39 deletions.
30 changes: 29 additions & 1 deletion impeller/aiks/aiks_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "flutter/testing/testing.h"
#include "impeller/aiks/aiks_playground.h"
#include "impeller/aiks/canvas.h"
#include "impeller/aiks/color_filter.h"
#include "impeller/aiks/image.h"
#include "impeller/aiks/image_filter.h"
#include "impeller/aiks/paint_pass_delegate.h"
Expand Down Expand Up @@ -123,16 +124,43 @@ TEST_P(AiksTest, CanRenderImage) {
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

TEST_P(AiksTest, CanRenderInvertedImage) {
TEST_P(AiksTest, CanRenderInvertedImageWithColorFilter) {
Canvas canvas;
Paint paint;
auto image = std::make_shared<Image>(CreateTextureForFixture("kalimba.jpg"));
paint.color = Color::Red();
paint.color_filter =
ColorFilter::MakeBlend(BlendMode::kSourceOver, Color::Yellow());
paint.invert_colors = true;

canvas.DrawImage(image, Point::MakeXY(100.0, 100.0), paint);
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

TEST_P(AiksTest, CanRenderColorFilterWithInvertColors) {
Canvas canvas;
Paint paint;
paint.color = Color::Red();
paint.color_filter =
ColorFilter::MakeBlend(BlendMode::kSourceOver, Color::Yellow());
paint.invert_colors = true;

canvas.DrawRect(Rect::MakeLTRB(0, 0, 100, 100), paint);
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

TEST_P(AiksTest, CanRenderColorFilterWithInvertColorsDrawPaint) {
Canvas canvas;
Paint paint;
paint.color = Color::Red();
paint.color_filter =
ColorFilter::MakeBlend(BlendMode::kSourceOver, Color::Yellow());
paint.invert_colors = true;

canvas.DrawPaint(paint);
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

namespace {
bool GenerateMipmap(const std::shared_ptr<Context>& context,
std::shared_ptr<Texture> texture,
Expand Down
1 change: 0 additions & 1 deletion impeller/aiks/canvas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

#include "impeller/aiks/canvas.h"

#include <algorithm>
#include <optional>
#include <utility>

Expand Down
45 changes: 45 additions & 0 deletions impeller/aiks/color_filter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
// found in the LICENSE file.

#include "impeller/aiks/color_filter.h"

#include <utility>
#include "impeller/entity/contents/filters/color_filter_contents.h"
#include "impeller/entity/contents/filters/filter_contents.h"
#include "impeller/entity/contents/filters/inputs/filter_input.h"
#include "impeller/geometry/color.h"

Expand Down Expand Up @@ -34,6 +37,12 @@ std::shared_ptr<ColorFilter> ColorFilter::MakeLinearToSrgb() {
return std::make_shared<LinearToSrgbColorFilter>();
}

std::shared_ptr<ColorFilter> ColorFilter::MakeComposed(
const std::shared_ptr<ColorFilter>& outer,
const std::shared_ptr<ColorFilter>& inner) {
return std::make_shared<ComposedColorFilter>(outer, inner);
}

/*******************************************************************************
******* BlendColorFilter
******************************************************************************/
Expand Down Expand Up @@ -142,4 +151,40 @@ std::shared_ptr<ColorFilter> LinearToSrgbColorFilter::Clone() const {
return std::make_shared<LinearToSrgbColorFilter>(*this);
}

/*******************************************************************************
******* ComposedColorFilter
******************************************************************************/

ComposedColorFilter::ComposedColorFilter(
const std::shared_ptr<ColorFilter>& outer,
const std::shared_ptr<ColorFilter>& inner)
: outer_(outer), inner_(inner) {}

ComposedColorFilter::~ComposedColorFilter() = default;

std::shared_ptr<ColorFilterContents>
ComposedColorFilter::WrapWithGPUColorFilter(
std::shared_ptr<FilterInput> input,
ColorFilterContents::AbsorbOpacity absorb_opacity) const {
std::shared_ptr<FilterContents> inner = inner_->WrapWithGPUColorFilter(
input, ColorFilterContents::AbsorbOpacity::kNo);
return outer_->WrapWithGPUColorFilter(FilterInput::Make(inner),
absorb_opacity);
}

// |ColorFilter|
ColorFilter::ColorFilterProc ComposedColorFilter::GetCPUColorFilterProc()
const {
return [inner = inner_, outer = outer_](Color color) {
auto inner_proc = inner->GetCPUColorFilterProc();
auto outer_proc = outer->GetCPUColorFilterProc();
return outer_proc(inner_proc(color));
};
}

// |ColorFilter|
std::shared_ptr<ColorFilter> ComposedColorFilter::Clone() const {
return std::make_shared<ComposedColorFilter>(outer_, inner_);
}

} // namespace impeller
28 changes: 28 additions & 0 deletions impeller/aiks/color_filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ class ColorFilter {

static std::shared_ptr<ColorFilter> MakeLinearToSrgb();

static std::shared_ptr<ColorFilter> MakeComposed(
const std::shared_ptr<ColorFilter>& outer,
const std::shared_ptr<ColorFilter>& inner);

/// @brief Wraps the given filter input with a GPU-based filter that will
/// perform the color operation. The given input will first be
/// rendered to a texture and then filtered.
Expand Down Expand Up @@ -147,4 +151,28 @@ class LinearToSrgbColorFilter final : public ColorFilter {
std::shared_ptr<ColorFilter> Clone() const override;
};

/// @brief Applies color filters as f(g(x)), where x is the input color.
class ComposedColorFilter final : public ColorFilter {
public:
ComposedColorFilter(const std::shared_ptr<ColorFilter>& outer,
const std::shared_ptr<ColorFilter>& inner);

~ComposedColorFilter() override;

// |ColorFilter|
std::shared_ptr<ColorFilterContents> WrapWithGPUColorFilter(
std::shared_ptr<FilterInput> input,
ColorFilterContents::AbsorbOpacity absorb_opacity) const override;

// |ColorFilter|
ColorFilterProc GetCPUColorFilterProc() const override;

// |ColorFilter|
std::shared_ptr<ColorFilter> Clone() const override;

private:
std::shared_ptr<ColorFilter> outer_;
std::shared_ptr<ColorFilter> inner_;
};

} // namespace impeller
54 changes: 29 additions & 25 deletions impeller/aiks/paint.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@

namespace impeller {

/// A color matrix which inverts colors.
// clang-format off
constexpr ColorMatrix kColorInversion = {
.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 //
}
};
// clang-format on

std::shared_ptr<Contents> Paint::CreateContentsForEntity(const Path& path,
bool cover) const {
std::unique_ptr<Geometry> geometry;
Expand All @@ -38,6 +50,7 @@ std::shared_ptr<Contents> Paint::CreateContentsForGeometry(
// Attempt to apply the color filter on the CPU first.
// Note: This is not just an optimization; some color sources rely on
// CPU-applied color filters to behave properly.
auto color_filter = GetColorFilter();
bool needs_color_filter = !!color_filter;
if (color_filter &&
contents->ApplyColorFilter(color_filter->GetCPUColorFilterProc())) {
Expand All @@ -59,7 +72,6 @@ std::shared_ptr<Contents> Paint::CreateContentsForGeometry(
std::shared_ptr<Contents> Paint::WithFilters(
std::shared_ptr<Contents> input) const {
input = WithColorFilter(input, ColorFilterContents::AbsorbOpacity::kYes);
input = WithInvertFilter(input);
auto image_filter =
WithImageFilter(input, Matrix(), Entity::RenderingMode::kDirect);
if (image_filter) {
Expand Down Expand Up @@ -111,6 +123,7 @@ std::shared_ptr<Contents> Paint::WithColorFilter(
return input;
}

auto color_filter = GetColorFilter();
if (!color_filter) {
return input;
}
Expand All @@ -121,33 +134,10 @@ std::shared_ptr<Contents> Paint::WithColorFilter(
if (input->ApplyColorFilter(color_filter->GetCPUColorFilterProc())) {
return input;
}

return color_filter->WrapWithGPUColorFilter(FilterInput::Make(input),
absorb_opacity);
}

/// A color matrix which inverts colors.
// clang-format off
constexpr ColorMatrix kColorInversion = {
.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 //
}
};
// clang-format on

std::shared_ptr<Contents> Paint::WithInvertFilter(
std::shared_ptr<Contents> input) const {
if (!invert_colors) {
return input;
}

return ColorFilterContents::MakeColorMatrix(
{FilterInput::Make(std::move(input))}, kColorInversion);
}

std::shared_ptr<FilterContents> Paint::MaskBlurDescriptor::CreateMaskBlur(
std::shared_ptr<ColorSourceContents> color_source_contents,
const std::shared_ptr<ColorFilter>& color_filter) const {
Expand Down Expand Up @@ -208,8 +198,22 @@ std::shared_ptr<FilterContents> Paint::MaskBlurDescriptor::CreateMaskBlur(
return FilterContents::MakeBorderMaskBlur(input, sigma, sigma, style);
}

std::shared_ptr<ColorFilter> Paint::GetColorFilter() const {
if (invert_colors && color_filter) {
auto filter = ColorFilter::MakeMatrix(kColorInversion);
return ColorFilter::MakeComposed(filter, color_filter);
}
if (invert_colors) {
return ColorFilter::MakeMatrix(kColorInversion);
}
if (color_filter) {
return color_filter;
}
return nullptr;
}

bool Paint::HasColorFilter() const {
return !!color_filter;
return !!color_filter || invert_colors;
}

} // namespace impeller
5 changes: 2 additions & 3 deletions impeller/aiks/paint.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ struct Paint {
std::shared_ptr<ColorFilter> color_filter;
std::optional<MaskBlurDescriptor> mask_blur_descriptor;

std::shared_ptr<ColorFilter> GetColorFilter() const;

/// @brief Wrap this paint's configured filters to the given contents.
/// @param[in] input The contents to wrap with paint's filters.
/// @return The filter-wrapped contents. If there are no filters that need
Expand Down Expand Up @@ -108,9 +110,6 @@ struct Paint {
std::shared_ptr<Contents> input,
ColorFilterContents::AbsorbOpacity absorb_opacity =
ColorFilterContents::AbsorbOpacity::kNo) const;

std::shared_ptr<Contents> WithInvertFilter(
std::shared_ptr<Contents> input) const;
};

} // namespace impeller
9 changes: 0 additions & 9 deletions impeller/display_list/dl_dispatcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,20 @@
#include <cstring>
#include <memory>
#include <optional>
#include <unordered_map>
#include <utility>
#include <vector>

#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"
#include "impeller/display_list/nine_patch_converter.h"
#include "impeller/display_list/skia_conversions.h"
#include "impeller/entity/contents/conical_gradient_contents.h"
#include "impeller/entity/contents/filters/filter_contents.h"
#include "impeller/entity/contents/filters/inputs/filter_input.h"
#include "impeller/entity/contents/linear_gradient_contents.h"
#include "impeller/entity/contents/radial_gradient_contents.h"
#include "impeller/entity/contents/runtime_effect_contents.h"
#include "impeller/entity/contents/sweep_gradient_contents.h"
#include "impeller/entity/contents/tiled_texture_contents.h"
#include "impeller/entity/entity.h"
#include "impeller/entity/geometry/geometry.h"
#include "impeller/geometry/path.h"
#include "impeller/geometry/path_builder.h"
#include "impeller/geometry/scalar.h"
Expand Down Expand Up @@ -483,7 +475,6 @@ static std::shared_ptr<ColorFilter> ToColorFilter(

// |flutter::DlOpReceiver|
void DlDispatcher::setColorFilter(const flutter::DlColorFilter* filter) {
// Needs https://github.com/flutter/flutter/issues/95434
paint_.color_filter = ToColorFilter(filter);
}

Expand Down

0 comments on commit f7f4b51

Please sign in to comment.