forked from firka/flutter
add ColorFilter matrix support (flutter/engine#7459)
* add ColorFilter matrix support
This commit is contained in:
@@ -1082,7 +1082,8 @@ class Paint {
|
||||
// Binary format must match the deserialization code in paint.cc.
|
||||
List<dynamic> _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<dynamic>(_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<double> 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<double> _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<double>(_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.
|
||||
|
||||
@@ -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<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 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<SkFilterQuality>(filter_quality));
|
||||
|
||||
if (uint_data[kColorFilterIndex] && uint_data[kInvertColorIndex]) {
|
||||
SkColor color = uint_data[kColorFilterColorIndex];
|
||||
SkBlendMode blend_mode =
|
||||
static_cast<SkBlendMode>(uint_data[kColorFilterBlendModeIndex]);
|
||||
sk_sp<SkColorFilter> color_filter =
|
||||
SkColorFilter::MakeModeFilter(color, blend_mode);
|
||||
sk_sp<SkColorFilter> invert_filter =
|
||||
SkColorFilter::MakeMatrixFilterRowMajor255(invert_colors);
|
||||
paint_.setColorFilter(invert_filter->makeComposed(color_filter));
|
||||
sk_sp<SkColorFilter> color_filter = ExtractColorFilter(uint_data, values);
|
||||
if (color_filter) {
|
||||
sk_sp<SkColorFilter> 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<SkBlendMode>(uint_data[kColorFilterBlendModeIndex]);
|
||||
paint_.setColorFilter(SkColorFilter::MakeModeFilter(color, blend_mode));
|
||||
sk_sp<SkColorFilter> color_filter = ExtractColorFilter(uint_data, values);
|
||||
if (color_filter) {
|
||||
paint_.setColorFilter(color_filter);
|
||||
}
|
||||
}
|
||||
|
||||
switch (uint_data[kMaskFilterIndex]) {
|
||||
|
||||
Reference in New Issue
Block a user