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 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 @@ -332,6 +332,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
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
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
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_initSrgbToLinearGamma';
}

/// 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.
Copy link
Contributor

@xxrlzzz xxrlzzz Feb 20, 2021

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