Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -333,6 +333,8 @@ FILE: ../../../flutter/lib/ui/painting/canvas.cc
FILE: ../../../flutter/lib/ui/painting/canvas.h
FILE: ../../../flutter/lib/ui/painting/codec.cc
FILE: ../../../flutter/lib/ui/painting/codec.h
FILE: ../../../flutter/lib/ui/painting/color_filter.cc
FILE: ../../../flutter/lib/ui/painting/color_filter.h
FILE: ../../../flutter/lib/ui/painting/engine_layer.cc
FILE: ../../../flutter/lib/ui/painting/engine_layer.h
FILE: ../../../flutter/lib/ui/painting/frame_info.cc
Expand Down
2 changes: 2 additions & 0 deletions lib/ui/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ source_set("ui") {
"painting/canvas.h",
"painting/codec.cc",
"painting/codec.h",
"painting/color_filter.cc",
"painting/color_filter.h",
"painting/engine_layer.cc",
"painting/engine_layer.h",
"painting/frame_info.cc",
Expand Down
2 changes: 2 additions & 0 deletions lib/ui/dart_ui.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "flutter/lib/ui/isolate_name_server/isolate_name_server_natives.h"
#include "flutter/lib/ui/painting/canvas.h"
#include "flutter/lib/ui/painting/codec.h"
#include "flutter/lib/ui/painting/color_filter.h"
#include "flutter/lib/ui/painting/engine_layer.h"
#include "flutter/lib/ui/painting/frame_info.h"
#include "flutter/lib/ui/painting/gradient.h"
Expand Down Expand Up @@ -75,6 +76,7 @@ void DartUI::InitForGlobal() {
CanvasPath::RegisterNatives(g_natives);
CanvasPathMeasure::RegisterNatives(g_natives);
Codec::RegisterNatives(g_natives);
ColorFilter::RegisterNatives(g_natives);
DartRuntimeHooks::RegisterNatives(g_natives);
EngineLayer::RegisterNatives(g_natives);
FontCollection::RegisterNatives(g_natives);
Expand Down
129 changes: 81 additions & 48 deletions lib/ui/painting.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1056,13 +1056,10 @@ class Paint {
static const int _kStrokeJoinIndex = 6;
static const int _kStrokeMiterLimitIndex = 7;
static const int _kFilterQualityIndex = 8;
static const int _kColorFilterIndex = 9;
static const int _kColorFilterColorIndex = 10;
static const int _kColorFilterBlendModeIndex = 11;
static const int _kMaskFilterIndex = 12;
static const int _kMaskFilterBlurStyleIndex = 13;
static const int _kMaskFilterSigmaIndex = 14;
static const int _kInvertColorIndex = 15;
static const int _kMaskFilterIndex = 9;
static const int _kMaskFilterBlurStyleIndex = 10;
static const int _kMaskFilterSigmaIndex = 11;
static const int _kInvertColorIndex = 12;

static const int _kIsAntiAliasOffset = _kIsAntiAliasIndex << 2;
static const int _kColorOffset = _kColorIndex << 2;
Expand All @@ -1073,20 +1070,17 @@ class Paint {
static const int _kStrokeJoinOffset = _kStrokeJoinIndex << 2;
static const int _kStrokeMiterLimitOffset = _kStrokeMiterLimitIndex << 2;
static const int _kFilterQualityOffset = _kFilterQualityIndex << 2;
static const int _kColorFilterOffset = _kColorFilterIndex << 2;
static const int _kColorFilterColorOffset = _kColorFilterColorIndex << 2;
static const int _kColorFilterBlendModeOffset = _kColorFilterBlendModeIndex << 2;
static const int _kMaskFilterOffset = _kMaskFilterIndex << 2;
static const int _kMaskFilterBlurStyleOffset = _kMaskFilterBlurStyleIndex << 2;
static const int _kMaskFilterSigmaOffset = _kMaskFilterSigmaIndex << 2;
static const int _kInvertColorOffset = _kInvertColorIndex << 2;
// If you add more fields, remember to update _kDataByteCount.
static const int _kDataByteCount = 75;
static const int _kDataByteCount = 52;

// Binary format must match the deserialization code in paint.cc.
List<dynamic> _objects;
static const int _kShaderIndex = 0;
static const int _kColorFilterMatrixIndex = 1;
static const int _kColorFilterIndex = 1;
static const int _kImageFilterIndex = 2;
static const int _kObjectCount = 3; // Must be one larger than the largest index.

Expand Down Expand Up @@ -1342,48 +1336,23 @@ class Paint {
///
/// When a shape is being drawn, [colorFilter] overrides [color] and [shader].
ColorFilter get colorFilter {
switch (_data.getInt32(_kColorFilterOffset, _kFakeHostEndian)) {
case ColorFilter._TypeNone:
return null;
case ColorFilter._TypeMode:
return ColorFilter.mode(
Color(_data.getInt32(_kColorFilterColorOffset, _kFakeHostEndian)),
BlendMode.values[_data.getInt32(_kColorFilterBlendModeOffset, _kFakeHostEndian)],
);
case ColorFilter._TypeMatrix:
return ColorFilter.matrix(_objects[_kColorFilterMatrixIndex]);
case ColorFilter._TypeLinearToSrgbGamma:
return const ColorFilter.linearToSrgbGamma();
case ColorFilter._TypeSrgbToLinearGamma:
return const ColorFilter.srgbToLinearGamma();
if (_objects == null || _objects[_kColorFilterIndex] == null) {
return null;
}

return null;
return _objects[_kColorFilterIndex].creator;
}

set colorFilter(ColorFilter value) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the overall approach! My only concern is the test coverage. We could have a C++ unit test to cover paint.cc.

It might be a little more challenging to add a unit test for the Dart code. I wonder if we can use the fixture similar to the shell_test.dart that used in https://github.com/flutter/engine/blob/master/shell/common/shell_unittests.cc#L156. It allows the C++ unit test to specify a Dart function as the entry point, and a C++ native callback. I've used it to set a onReportTimings callback on the Dart side, and check its results on the C++ side (https://github.com/flutter/engine/blob/master/shell/common/shell_unittests.cc#L315).

I think it would be nice if we can create the ColorFilter on the Dart side, and check the SkColorFilter object is properly set on the C++ side. CC @chinmaygarde who might have more suggestions how to do this kind of Dart fixture test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added dart tests that will serve to test the C++ side, and that cover the same code. LMK if you think we need more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (value == null) {
_data.setInt32(_kColorFilterOffset, ColorFilter._TypeNone, _kFakeHostEndian);
_data.setInt32(_kColorFilterColorOffset, 0, _kFakeHostEndian);
_data.setInt32(_kColorFilterBlendModeOffset, 0, _kFakeHostEndian);

if (_objects != null) {
_objects[_kColorFilterMatrixIndex] = null;
_objects[_kColorFilterIndex] = null;
}
} else {
_data.setInt32(_kColorFilterOffset, value._type, _kFakeHostEndian);

if (value._type == ColorFilter._TypeMode) {
assert(value._color != null);
assert(value._blendMode != null);

_data.setInt32(_kColorFilterColorOffset, value._color.value, _kFakeHostEndian);
_data.setInt32(_kColorFilterBlendModeOffset, value._blendMode.index, _kFakeHostEndian);
} else if (value._type == ColorFilter._TypeMatrix) {
assert(value._matrix != null);

_objects ??= List<dynamic>(_kObjectCount);
_objects[_kColorFilterMatrixIndex] = Float32List.fromList(value._matrix);
if (_objects == null) {
_objects = List<dynamic>(_kObjectCount);
_objects[_kColorFilterIndex] = value._toNativeColorFilter();
} else if (_objects[_kColorFilterIndex]?.creator != value) {
_objects[_kColorFilterIndex] = value._toNativeColorFilter();
}
}
}
Expand Down Expand Up @@ -2520,7 +2489,9 @@ class ColorFilter {
/// to the [Paint.blendMode], using the output of this filter as the source
/// and the background as the destination.
const ColorFilter.mode(Color color, BlendMode blendMode)
: _color = color,
: assert(color != null),
assert(blendMode != null),
_color = color,
_blendMode = blendMode,
_matrix = null,
_type = _TypeMode;
Expand All @@ -2529,7 +2500,9 @@ class ColorFilter {
/// matrix is in row-major order and the translation column is specified in
/// unnormalized, 0...255, space.
const ColorFilter.matrix(List<double> matrix)
: _color = null,
: assert(matrix != null),
assert(matrix.length == 20),
_color = null,
_blendMode = null,
_matrix = matrix,
_type = _TypeMatrix;
Expand Down Expand Up @@ -2580,6 +2553,21 @@ class ColorFilter {
return _color == typedOther._color && _blendMode == typedOther._blendMode;
}

_ColorFilter _toNativeColorFilter() {
switch (_type) {
case _TypeMode:
return _ColorFilter.mode(this);
case _TypeMatrix:
return _ColorFilter.matrix(this);
case _TypeLinearToSrgbGamma:
return _ColorFilter.linearToSrgbGamma(this);
case _TypeSrgbToLinearGamma:
return _ColorFilter.srgbToLinearGamma(this);
default:
throw StateError('Unknown mode $_type for ColorFilter.');
}
}

@override
int get hashCode => hashValues(_color, _blendMode, hashList(_matrix), _type);

Expand All @@ -2600,6 +2588,51 @@ class ColorFilter {
}
}

/// A [ColorFilter] that is backed by a native SkColorFilter.
///
/// This is a private class, rather than being the implementation of the public
/// ColorFilter, because we want ColorFilter to be const constructible and
/// efficiently comparable, so that widgets can check for ColorFilter equality to
// avoid repainting.
class _ColorFilter extends NativeFieldWrapperClass2 {
_ColorFilter.mode(this.creator)
: assert(creator != null),
assert(creator._type == ColorFilter._TypeMode) {
_constructor();
_initMode(creator._color.value, creator._blendMode.index);
}

_ColorFilter.matrix(this.creator)
: assert(creator != null),
assert(creator._type == ColorFilter._TypeMatrix) {
_constructor();
_initMatrix(Float32List.fromList(creator._matrix));
}
_ColorFilter.linearToSrgbGamma(this.creator)
: assert(creator != null),
assert(creator._type == ColorFilter._TypeLinearToSrgbGamma) {
_constructor();
_initLinearToSrgbGamma();
}

_ColorFilter.srgbToLinearGamma(this.creator)
: assert(creator != null),
assert(creator._type == ColorFilter._TypeSrgbToLinearGamma) {
_constructor();
_initSrgbToLinearGamma();
}

/// The original Dart object that created the native wrapper, which retains
/// the values used for the filter.
final ColorFilter creator;

void _constructor() native 'ColorFilter_constructor';
void _initMode(int color, int blendMode) native 'ColorFilter_initMode';
void _initMatrix(Float32List matrix) native 'ColorFilter_initMatrix';
void _initLinearToSrgbGamma() native 'ColorFilter_initLinearToSrgbGamma';
void _initSrgbToLinearGamma() native 'ColorFilter_initSrgToLinearGamma';
}

/// A filter operation to apply to a raster image.
///
/// See also:
Expand Down
70 changes: 70 additions & 0 deletions lib/ui/painting/color_filter.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// 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 "flutter/lib/ui/painting/color_filter.h"

#include "third_party/tonic/converter/dart_converter.h"
#include "third_party/tonic/dart_args.h"
#include "third_party/tonic/dart_binding_macros.h"
#include "third_party/tonic/dart_library_natives.h"

namespace flutter {

static void ColorFilter_constructor(Dart_NativeArguments args) {
DartCallConstructor(&ColorFilter::Create, args);
}

IMPLEMENT_WRAPPERTYPEINFO(ui, ColorFilter);

#define FOR_EACH_BINDING(V) \
V(ColorFilter, initMode) \
V(ColorFilter, initMatrix) \
V(ColorFilter, initSrgbToLinearGamma) \
V(ColorFilter, initLinearToSrgbGamma)

FOR_EACH_BINDING(DART_NATIVE_CALLBACK)

void ColorFilter::RegisterNatives(tonic::DartLibraryNatives* natives) {
natives->Register(
{{"ColorFilter_constructor", ColorFilter_constructor, 1, true},
FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
}

fml::RefPtr<ColorFilter> ColorFilter::Create() {
return fml::MakeRefCounted<ColorFilter>();
}

void ColorFilter::initMode(int color, int blend_mode) {
filter_ = SkColorFilters::Blend(static_cast<SkColor>(color),
static_cast<SkBlendMode>(blend_mode));
}

sk_sp<SkColorFilter> ColorFilter::MakeColorMatrixFilter255(
const float array[20]) {
float tmp[20];
memcpy(tmp, array, sizeof(tmp));
tmp[4] *= 1.0f / 255;
tmp[9] *= 1.0f / 255;
tmp[14] *= 1.0f / 255;
tmp[19] *= 1.0f / 255;
return SkColorFilters::Matrix(tmp);
}

void ColorFilter::initMatrix(const tonic::Float32List& color_matrix) {
FML_CHECK(color_matrix.num_elements() == 20);

filter_ = MakeColorMatrixFilter255(color_matrix.data());
}

void ColorFilter::initLinearToSrgbGamma() {
filter_ = SkColorFilters::LinearToSRGBGamma();
}

void ColorFilter::initSrgbToLinearGamma() {
filter_ = SkColorFilters::SRGBToLinearGamma();
}

ColorFilter::~ColorFilter() = default;

} // namespace flutter
52 changes: 52 additions & 0 deletions lib/ui/painting/color_filter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// 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.

#ifndef FLUTTER_LIB_UI_COLOR_FILTER_H_
#define FLUTTER_LIB_UI_COLOR_FILTER_H_

#include "flutter/lib/ui/dart_wrapper.h"
#include "third_party/skia/include/core/SkColorFilter.h"
#include "third_party/tonic/typed_data/typed_list.h"

using tonic::DartPersistentValue;

namespace tonic {
class DartLibraryNatives;
} // namespace tonic

namespace flutter {

// A handle to an SkCodec object.

@xxrlzzz xxrlzzz Feb 20, 2021

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is right? I don't see any SkCodec usage in this class

//
// Doesn't mirror SkCodec's API but provides a simple sequential access API.
class ColorFilter : public RefCountedDartWrappable<ColorFilter> {
DEFINE_WRAPPERTYPEINFO();
FML_FRIEND_MAKE_REF_COUNTED(ColorFilter);

public:
static fml::RefPtr<ColorFilter> Create();

// Flutter still defines the matrix to be biased by 255 in the last column
// (translate). skia is normalized, treating the last column as 0...1, so we
// post-scale here before calling the skia factory.
static sk_sp<SkColorFilter> MakeColorMatrixFilter255(const float array[20]);

void initMode(int color, int blend_mode);
void initMatrix(const tonic::Float32List& color_matrix);
void initSrgbToLinearGamma();
void initLinearToSrgbGamma();

~ColorFilter() override;

sk_sp<SkColorFilter> filter() const { return filter_; }

static void RegisterNatives(tonic::DartLibraryNatives* natives);

private:
sk_sp<SkColorFilter> filter_;
};

} // namespace flutter

#endif // FLUTTER_LIB_UI_COLOR_FILTER_H_
Loading