Skip to content

Commit

Permalink
Normalize the translation column of the color matrix. (#161109)
Browse files Browse the repository at this point in the history
`dart:ui` specifies that the translation column of the color matrix
should be in the range 0..255. Skia expects a normalized range between 0
and 1. Skwasm was not normalizing this before passing it to Skia, but
CanvasKit was. After writing a unit test, it appears that the HTML
renderer has the same problem, so I also fixed that as well.

This addresses flutter/flutter#159697
  • Loading branch information
eyebrowsoffire authored Jan 8, 2025
1 parent 132e298 commit 1e9202e
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,18 @@ class SvgFilter {

SvgFilter svgFilterFromColorMatrix(List<double> matrix) {
final SvgFilterBuilder builder = SvgFilterBuilder();
builder.setFeColorMatrix(matrix, result: 'comp');

/// Flutter documentation says the translation column of the color matrix
/// is specified in unnormalized 0..255 space. `feColorMatrix` expects the
/// translation values to be normalized to 0..1 space.
///
/// See [https://api.flutter.dev/flutter/dart-ui/ColorFilter/ColorFilter.matrix.html]
final normalizedMatrix = List<double>.generate(
matrix.length,
(int i) => (i % 5 == 4) ? matrix[i] / 255.0 : matrix[i],
growable: false,
);
builder.setFeColorMatrix(normalizedMatrix, result: 'comp');
return builder.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:ffi';
import 'dart:typed_data';

import 'package:ui/src/engine.dart';
Expand Down Expand Up @@ -318,7 +319,18 @@ class SkwasmMatrixColorFilter extends SkwasmColorFilter {
@override
void withRawColorFilter(ColorFilterHandleBorrow borrow) {
withStackScope((scope) {
final rawColorFilter = colorFilterCreateMatrix(scope.convertDoublesToNative(matrix));
assert(matrix.length == 20);
final Pointer<Float> rawMatrix = scope.convertDoublesToNative(matrix);

/// Flutter documentation says the translation column of the color matrix
/// is specified in unnormalized 0..255 space. Skia expects the
/// translation values to be normalized to 0..1 space.
///
/// See [https://api.flutter.dev/flutter/dart-ui/ColorFilter/ColorFilter.matrix.html].
for (final i in <int>[4, 9, 14, 19]) {
rawMatrix[i] /= 255.0;
}
final rawColorFilter = colorFilterCreateMatrix(rawMatrix);
borrow(rawColorFilter);
colorFilterDispose(rawColorFilter);
});
Expand Down
60 changes: 20 additions & 40 deletions engine/src/flutter/lib/web_ui/test/ui/filters_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -146,59 +146,39 @@ Future<void> testMain() async {

test('matrix color filter', () async {
const ui.ColorFilter sepia = ui.ColorFilter.matrix(<double>[
0.393,
0.769,
0.189,
0,
0,
0.349,
0.686,
0.168,
0,
0,
0.272,
0.534,
0.131,
0,
0,
0,
0,
0,
1,
0,
0.393, 0.769, 0.189, 0, 0, // row
0.349, 0.686, 0.168, 0, 0, // row
0.272, 0.534, 0.131, 0, 0, // row
0, 0, 0, 1, 0, // row
]);
await drawTestImageWithPaint(ui.Paint()..colorFilter = sepia);
await matchGoldenFile('ui_filter_matrix_colorfilter.png', region: region);
expect(sepia.toString(), startsWith('ColorFilter.matrix([0.393, 0.769, 0.189, '));
});

test('matrix color filter with 0..255 translation values', () async {
const ui.ColorFilter sepia = ui.ColorFilter.matrix(<double>[
0.393, 0.769, 0.189, 0, 50.0, // row
0.349, 0.686, 0.168, 0, 50.0, // row
0.272, 0.534, 0.131, 0, 50.0, // row
0, 0, 0, 1, 0, // row
]);
await drawTestImageWithPaint(ui.Paint()..colorFilter = sepia);
await matchGoldenFile('ui_filter_matrix_colorfilter_with_translation.png', region: region);
expect(sepia.toString(), startsWith('ColorFilter.matrix([0.393, 0.769, 0.189, '));
});

test('invert colors', () async {
await drawTestImageWithPaint(ui.Paint()..invertColors = true);
await matchGoldenFile('ui_filter_invert_colors.png', region: region);
});

test('invert colors with color filter', () async {
const ui.ColorFilter sepia = ui.ColorFilter.matrix(<double>[
0.393,
0.769,
0.189,
0,
0,
0.349,
0.686,
0.168,
0,
0,
0.272,
0.534,
0.131,
0,
0,
0,
0,
0,
1,
0,
0.393, 0.769, 0.189, 0, 0, // row
0.349, 0.686, 0.168, 0, 0, // row
0.272, 0.534, 0.131, 0, 0, // row
0, 0, 0, 1, 0, // row
]);

await drawTestImageWithPaint(
Expand Down

0 comments on commit 1e9202e

Please sign in to comment.