From 2820157751e37e799a4fe6be79a3031f188b56ac Mon Sep 17 00:00:00 2001 From: Razvan Lung Date: Mon, 14 Jan 2019 20:45:06 +0200 Subject: [PATCH] add ColorFilter matrix support (#7459) * add ColorFilter matrix support --- lib/ui/painting.dart | 124 ++++++++++++++++++++++++++++++++------- lib/ui/painting/paint.cc | 73 ++++++++++++++++++----- 2 files changed, 163 insertions(+), 34 deletions(-) diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index 86a814e5bf096..c4811187cf98b 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -1082,7 +1082,8 @@ class Paint { // Binary format must match the deserialization code in paint.cc. List _objects; static const int _kShaderIndex = 0; - static const int _kObjectCount = 1; // Must be one larger than the largest index. + static const int _kColorFilterMatrixIndex = 1; + static const int _kObjectCount = 2; // Must be one larger than the largest index. /// Whether to apply anti-aliasing to lines and images drawn on the /// canvas. @@ -1336,25 +1337,49 @@ class Paint { /// /// When a shape is being drawn, [colorFilter] overrides [color] and [shader]. ColorFilter get colorFilter { - final bool isNull = _data.getInt32(_kColorFilterOffset, _kFakeHostEndian) == 0; - if (isNull) - return null; - return new ColorFilter.mode( - new Color(_data.getInt32(_kColorFilterColorOffset, _kFakeHostEndian)), - BlendMode.values[_data.getInt32(_kColorFilterBlendModeOffset, _kFakeHostEndian)] - ); + switch (_data.getInt32(_kColorFilterOffset, _kFakeHostEndian)) { + case ColorFilter._TypeNone: + return null; + case ColorFilter._TypeMode: + return new ColorFilter.mode( + new Color(_data.getInt32(_kColorFilterColorOffset, _kFakeHostEndian)), + BlendMode.values[_data.getInt32(_kColorFilterBlendModeOffset, _kFakeHostEndian)], + ); + case ColorFilter._TypeMatrix: + return new ColorFilter.matrix(_objects[_kColorFilterMatrixIndex]); + case ColorFilter._TypeLinearToSrgbGamma: + return const ColorFilter.linearToSrgbGamma(); + case ColorFilter._TypeSrgbToLinearGamma: + return const ColorFilter.srgbToLinearGamma(); + } + + return null; } + set colorFilter(ColorFilter value) { if (value == null) { - _data.setInt32(_kColorFilterOffset, 0, _kFakeHostEndian); + _data.setInt32(_kColorFilterOffset, ColorFilter._TypeNone, _kFakeHostEndian); _data.setInt32(_kColorFilterColorOffset, 0, _kFakeHostEndian); _data.setInt32(_kColorFilterBlendModeOffset, 0, _kFakeHostEndian); + + if (_objects != null) { + _objects[_kColorFilterMatrixIndex] = null; + } } else { - assert(value._color != null); - assert(value._blendMode != null); - _data.setInt32(_kColorFilterOffset, 1, _kFakeHostEndian); - _data.setInt32(_kColorFilterColorOffset, value._color.value, _kFakeHostEndian); - _data.setInt32(_kColorFilterBlendModeOffset, value._blendMode.index, _kFakeHostEndian); + _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 ??= new List(_kObjectCount); + _objects[_kColorFilterMatrixIndex] = Float32List.fromList(value._matrix); + } } } @@ -2378,25 +2403,84 @@ 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, _blendMode = blendMode; + : _color = color, + _blendMode = blendMode, + _matrix = null, + _type = _TypeMode; + + /// Construct a color filter that transforms a color by a 4x5 matrix. The + /// matrix is in row-major order and the translation column is specified in + /// unnormalized, 0...255, space. + const ColorFilter.matrix(List matrix) + : _color = null, + _blendMode = null, + _matrix = matrix, + _type = _TypeMatrix; + + /// Construct a color filter that applies the srgb gamma curve to the RGB + /// channels. + const ColorFilter.linearToSrgbGamma() + : _color = null, + _blendMode = null, + _matrix = null, + _type = _TypeLinearToSrgbGamma; + + /// Creates a color filter that applies the inverse of the srgb gamma curve + /// to the RGB channels. + const ColorFilter.srgbToLinearGamma() + : _color = null, + _blendMode = null, + _matrix = null, + _type = _TypeSrgbToLinearGamma; final Color _color; final BlendMode _blendMode; + final List _matrix; + final int _type; + + // The type of SkColorFilter class to create for Skia. + // These constants must be kept in sync with ColorFilterType in paint.cc. + static const int _TypeNone = 0; // null + static const int _TypeMode = 1; // MakeModeFilter + static const int _TypeMatrix = 2; // MakeMatrixFilterRowMajor255 + static const int _TypeLinearToSrgbGamma = 3; // MakeLinearToSRGBGamma + static const int _TypeSrgbToLinearGamma = 4; // MakeSRGBToLinearGamma @override bool operator ==(dynamic other) { - if (other is! ColorFilter) + if (other is! ColorFilter) { return false; + } final ColorFilter typedOther = other; - return _color == typedOther._color && - _blendMode == typedOther._blendMode; + + if (_type != typedOther._type) { + return false; + } + if (!_listEquals(_matrix, typedOther._matrix)) { + return false; + } + + return _color == typedOther._color && _blendMode == typedOther._blendMode; } @override - int get hashCode => hashValues(_color, _blendMode); + int get hashCode => hashValues(_color, _blendMode, hashList(_matrix), _type); @override - String toString() => 'ColorFilter($_color, $_blendMode)'; + String toString() { + switch (_type) { + case _TypeMode: + return 'ColorFilter.mode($_color, $_blendMode)'; + case _TypeMatrix: + return 'ColorFilter.matrix($_matrix)'; + case _TypeLinearToSrgbGamma: + return 'ColorFilter.linearToSrgbGamma()'; + case _TypeSrgbToLinearGamma: + return 'ColorFilter.srgbToLinearGamma()'; + default: + return 'Unknown ColorFilter type. This is an error. If you\'re seeing this, please file an issue at https://github.com/flutter/flutter/issues/new.'; + } + } } /// A filter operation to apply to a raster image. diff --git a/lib/ui/painting/paint.cc b/lib/ui/painting/paint.cc index 16e953933732b..4e67a48916eb1 100644 --- a/lib/ui/painting/paint.cc +++ b/lib/ui/painting/paint.cc @@ -11,6 +11,7 @@ #include "third_party/skia/include/core/SkShader.h" #include "third_party/skia/include/core/SkString.h" #include "third_party/tonic/typed_data/dart_byte_data.h" +#include "third_party/tonic/typed_data/float32_list.h" namespace blink { @@ -35,7 +36,8 @@ constexpr size_t kDataByteCount = 75; // 4 * (last index + 1) // Indices for objects. constexpr int kShaderIndex = 0; -constexpr int kObjectCount = 1; // One larger than largest object index. +constexpr int kColorFilterMatrixIndex = 1; +constexpr int kObjectCount = 2; // One larger than largest object index. // Must be kept in sync with the default in painting.dart. constexpr uint32_t kColorDefault = 0xFF000000; @@ -61,18 +63,63 @@ constexpr SkScalar 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 +}; + +sk_sp 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(uint_data[kColorFilterBlendModeIndex]); + + return SkColorFilter::MakeModeFilter(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 SkColorFilter::MakeMatrixFilterRowMajor255(decoded.data()); + } + return nullptr; + } + case LinearToSRGBGamma: { + return SkColorFilter::MakeLinearToSRGBGamma(); + } + case SRGBToLinearGamma: { + return SkColorFilter::MakeSRGBToLinearGamma(); + } + 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_) return; + Dart_Handle values[kObjectCount]; if (!Dart_IsNull(paint_objects)) { FML_DCHECK(Dart_IsList(paint_objects)); intptr_t length = 0; Dart_ListLength(paint_objects, &length); FML_CHECK(length == kObjectCount); - Dart_Handle values[kObjectCount]; if (Dart_IsError(Dart_ListGetRange(paint_objects, 0, kObjectCount, values))) return; @@ -128,22 +175,20 @@ Paint::Paint(Dart_Handle paint_objects, Dart_Handle paint_data) { paint_.setFilterQuality(static_cast(filter_quality)); if (uint_data[kColorFilterIndex] && uint_data[kInvertColorIndex]) { - SkColor color = uint_data[kColorFilterColorIndex]; - SkBlendMode blend_mode = - static_cast(uint_data[kColorFilterBlendModeIndex]); - sk_sp color_filter = - SkColorFilter::MakeModeFilter(color, blend_mode); - sk_sp invert_filter = - SkColorFilter::MakeMatrixFilterRowMajor255(invert_colors); - paint_.setColorFilter(invert_filter->makeComposed(color_filter)); + sk_sp color_filter = ExtractColorFilter(uint_data, values); + if (color_filter) { + sk_sp invert_filter = + SkColorFilter::MakeMatrixFilterRowMajor255(invert_colors); + paint_.setColorFilter(invert_filter->makeComposed(color_filter)); + } } else if (uint_data[kInvertColorIndex]) { paint_.setColorFilter( SkColorFilter::MakeMatrixFilterRowMajor255(invert_colors)); } else if (uint_data[kColorFilterIndex]) { - SkColor color = uint_data[kColorFilterColorIndex]; - SkBlendMode blend_mode = - static_cast(uint_data[kColorFilterBlendModeIndex]); - paint_.setColorFilter(SkColorFilter::MakeModeFilter(color, blend_mode)); + sk_sp color_filter = ExtractColorFilter(uint_data, values); + if (color_filter) { + paint_.setColorFilter(color_filter); + } } switch (uint_data[kMaskFilterIndex]) {