未验证 提交 887e0523 编写于 作者: D Dan Field 提交者: GitHub

Refactor ColorFilter to have a native wrapper (#9668)

上级 75387dbc
......@@ -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
......
......@@ -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",
......
......@@ -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"
......@@ -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);
......
......@@ -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;
......@@ -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.
......@@ -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) {
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();
}
}
}
......@@ -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;
......@@ -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;
......@@ -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);
......@@ -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:
......
// 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
// 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.
//
// 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_
......@@ -5,6 +5,7 @@
#include "flutter/lib/ui/painting/paint.h"
#include "flutter/fml/logging.h"
#include "flutter/lib/ui/painting/color_filter.h"
#include "flutter/lib/ui/painting/image_filter.h"
#include "flutter/lib/ui/painting/shader.h"
#include "third_party/skia/include/core/SkColorFilter.h"
......@@ -27,18 +28,15 @@ constexpr int kStrokeCapIndex = 5;
constexpr int kStrokeJoinIndex = 6;
constexpr int kStrokeMiterLimitIndex = 7;
constexpr int kFilterQualityIndex = 8;
constexpr int kColorFilterIndex = 9;
constexpr int kColorFilterColorIndex = 10;
constexpr int kColorFilterBlendModeIndex = 11;
constexpr int kMaskFilterIndex = 12;
constexpr int kMaskFilterBlurStyleIndex = 13;
constexpr int kMaskFilterSigmaIndex = 14;
constexpr int kInvertColorIndex = 15;
constexpr size_t kDataByteCount = 75; // 4 * (last index + 1)
constexpr int kMaskFilterIndex = 9;
constexpr int kMaskFilterBlurStyleIndex = 10;
constexpr int kMaskFilterSigmaIndex = 11;
constexpr int kInvertColorIndex = 12;
constexpr size_t kDataByteCount = 52; // 4 * (last index + 1)
// Indices for objects.
constexpr int kShaderIndex = 0;
constexpr int kColorFilterMatrixIndex = 1;
constexpr int kColorFilterIndex = 1;
constexpr int kImageFilterIndex = 2;
constexpr int kObjectCount = 3; // One larger than largest object index.
......@@ -66,64 +64,6 @@ constexpr float invert_colors[20] = {
// Must be kept in sync with the MaskFilter private constants in painting.dart.
enum MaskFilterType { Null, Blur };
// Must be kept in sync with the ColorFilter private constants in painting.dart.
enum ColorFilterType {
None,
Mode,
Matrix,
LinearToSRGBGamma,
SRGBToLinearGamma
};
// 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]) {
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);
}
sk_sp<SkColorFilter> ExtractColorFilter(const uint32_t* uint_data,
Dart_Handle* values) {
switch (uint_data[kColorFilterIndex]) {
case Mode: {
SkColor color = uint_data[kColorFilterColorIndex];
SkBlendMode blend_mode =
static_cast<SkBlendMode>(uint_data[kColorFilterBlendModeIndex]);
return SkColorFilters::Blend(color, blend_mode);
}
case Matrix: {
Dart_Handle matrixHandle = values[kColorFilterMatrixIndex];
if (!Dart_IsNull(matrixHandle)) {
FML_DCHECK(Dart_IsList(matrixHandle));
intptr_t length = 0;
Dart_ListLength(matrixHandle, &length);
FML_CHECK(length == 20);
tonic::Float32List decoded(matrixHandle);
return MakeColorMatrixFilter255(decoded.data());
}
return nullptr;
}
case LinearToSRGBGamma: {
return SkColorFilters::LinearToSRGBGamma();
}
case SRGBToLinearGamma: {
return SkColorFilters::SRGBToLinearGamma();
}
default:
FML_DLOG(ERROR) << "Out of range value received for kColorFilterIndex.";
return nullptr;
}
}
Paint::Paint(Dart_Handle paint_objects, Dart_Handle paint_data) {
is_null_ = Dart_IsNull(paint_data);
if (is_null_)
......@@ -145,6 +85,13 @@ Paint::Paint(Dart_Handle paint_objects, Dart_Handle paint_data) {
paint_.setShader(decoded->shader());
}
Dart_Handle color_filter = values[kColorFilterIndex];
if (!Dart_IsNull(color_filter)) {
ColorFilter* decoded_color_filter =
tonic::DartConverter<ColorFilter*>::FromDart(color_filter);
paint_.setColorFilter(decoded_color_filter->filter());
}
Dart_Handle image_filter = values[kImageFilterIndex];
if (!Dart_IsNull(image_filter)) {
ImageFilter* decoded =
......@@ -197,20 +144,14 @@ Paint::Paint(Dart_Handle paint_objects, Dart_Handle paint_data) {
if (filter_quality)
paint_.setFilterQuality(static_cast<SkFilterQuality>(filter_quality));
if (uint_data[kColorFilterIndex] && uint_data[kInvertColorIndex]) {
sk_sp<SkColorFilter> color_filter = ExtractColorFilter(uint_data, values);
if (color_filter) {
sk_sp<SkColorFilter> invert_filter =
MakeColorMatrixFilter255(invert_colors);
paint_.setColorFilter(invert_filter->makeComposed(color_filter));
}
} else if (uint_data[kInvertColorIndex]) {
paint_.setColorFilter(MakeColorMatrixFilter255(invert_colors));
} else if (uint_data[kColorFilterIndex]) {
sk_sp<SkColorFilter> color_filter = ExtractColorFilter(uint_data, values);
if (color_filter) {
paint_.setColorFilter(color_filter);
if (uint_data[kInvertColorIndex]) {
sk_sp<SkColorFilter> invert_filter =
ColorFilter::MakeColorMatrixFilter255(invert_colors);
sk_sp<SkColorFilter> current_filter = paint_.refColorFilter();
if (current_filter) {
invert_filter = invert_filter->makeComposed(current_filter);
}
paint_.setColorFilter(invert_filter);
}
switch (uint_data[kMaskFilterIndex]) {
......
// 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.
import 'dart:typed_data';
import 'dart:ui';
import 'package:test/test.dart';
const Color red = Color(0xFFAA0000);
const Color green = Color(0xFF00AA00);
const int greenRedColorBlend = 0xFF3131DB;
const int greenRedColorBlendInverted = 0xFFCECE24;
const int greenGreyscaled = 0xFF7A7A7A;
const int greenInvertedGreyscaled = 0xFF858585;
const int greenLinearToSrgbGamma = 0xFF00D500;
const int greenLinearToSrgbGammaInverted = 0xFFFF2AFF;
const int greenSrgbToLinearGamma = 0xFF006700;
const int greenSrgbToLinearGammaInverted = 0xFFFF98FF;
const List<double> greyscaleColorMatrix = <double>[
0.2126, 0.7152, 0.0722, 0, 0, //
0.2126, 0.7152, 0.0722, 0, 0, //
0.2126, 0.7152, 0.0722, 0, 0, //
0, 0, 0, 1, 0, //
];
void main() {
Future<Uint32List> getBytesForPaint(Paint paint, {int width = 1, int height = 1}) async {
final PictureRecorder recorder = PictureRecorder();
final Canvas recorderCanvas = Canvas(recorder);
recorderCanvas.drawPaint(paint);
final Picture picture = recorder.endRecording();
final Image image = await picture.toImage(width, height);
final ByteData bytes = await image.toByteData();
expect(bytes.lengthInBytes, width * height * 4);
return bytes.buffer.asUint32List();
}
test('ColorFilter - mode', () async {
final Paint paint = Paint()
..color = green
..colorFilter = ColorFilter.mode(red, BlendMode.color);
Uint32List bytes = await getBytesForPaint(paint);
expect(bytes[0], greenRedColorBlend);
paint.invertColors = true;
bytes = await getBytesForPaint(paint);
expect(bytes[0], greenRedColorBlendInverted);
});
test('ColorFilter - matrix', () async {
final Paint paint = Paint()
..color = green
..colorFilter = ColorFilter.matrix(greyscaleColorMatrix);
Uint32List bytes = await getBytesForPaint(paint);
expect(bytes[0], greenGreyscaled);
paint.invertColors = true;
bytes = await getBytesForPaint(paint);
expect(bytes[0], greenInvertedGreyscaled);
});
test('ColorFilter - linearToSrgbGamma', () async {
final Paint paint = Paint()
..color = green
..colorFilter = ColorFilter.linearToSrgbGamma();
Uint32List bytes = await getBytesForPaint(paint);
expect(bytes[0], greenLinearToSrgbGamma);
paint.invertColors = true;
bytes = await getBytesForPaint(paint);
expect(bytes[0], greenLinearToSrgbGammaInverted);
});
test('ColorFilter - srgbToLinearGamma', () async {
final Paint paint = Paint()
..color = green
..colorFilter = ColorFilter.srgbToLinearGamma();
Uint32List bytes = await getBytesForPaint(paint);
expect(bytes[0], greenSrgbToLinearGamma);
paint.invertColors = true;
bytes = await getBytesForPaint(paint);
expect(bytes[0], greenSrgbToLinearGammaInverted);
});
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册