Skip to content

Commit 1e9202e

Browse files
Normalize the translation column of the color matrix. (#161109)
`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
1 parent 132e298 commit 1e9202e

File tree

3 files changed

+45
-42
lines changed

3 files changed

+45
-42
lines changed

engine/src/flutter/lib/web_ui/lib/src/engine/html/color_filter.dart

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,18 @@ class SvgFilter {
312312

313313
SvgFilter svgFilterFromColorMatrix(List<double> matrix) {
314314
final SvgFilterBuilder builder = SvgFilterBuilder();
315-
builder.setFeColorMatrix(matrix, result: 'comp');
315+
316+
/// Flutter documentation says the translation column of the color matrix
317+
/// is specified in unnormalized 0..255 space. `feColorMatrix` expects the
318+
/// translation values to be normalized to 0..1 space.
319+
///
320+
/// See [https://api.flutter.dev/flutter/dart-ui/ColorFilter/ColorFilter.matrix.html]
321+
final normalizedMatrix = List<double>.generate(
322+
matrix.length,
323+
(int i) => (i % 5 == 4) ? matrix[i] / 255.0 : matrix[i],
324+
growable: false,
325+
);
326+
builder.setFeColorMatrix(normalizedMatrix, result: 'comp');
316327
return builder.build();
317328
}
318329

engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/filters.dart

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'dart:ffi';
56
import 'dart:typed_data';
67

78
import 'package:ui/src/engine.dart';
@@ -318,7 +319,18 @@ class SkwasmMatrixColorFilter extends SkwasmColorFilter {
318319
@override
319320
void withRawColorFilter(ColorFilterHandleBorrow borrow) {
320321
withStackScope((scope) {
321-
final rawColorFilter = colorFilterCreateMatrix(scope.convertDoublesToNative(matrix));
322+
assert(matrix.length == 20);
323+
final Pointer<Float> rawMatrix = scope.convertDoublesToNative(matrix);
324+
325+
/// Flutter documentation says the translation column of the color matrix
326+
/// is specified in unnormalized 0..255 space. Skia expects the
327+
/// translation values to be normalized to 0..1 space.
328+
///
329+
/// See [https://api.flutter.dev/flutter/dart-ui/ColorFilter/ColorFilter.matrix.html].
330+
for (final i in <int>[4, 9, 14, 19]) {
331+
rawMatrix[i] /= 255.0;
332+
}
333+
final rawColorFilter = colorFilterCreateMatrix(rawMatrix);
322334
borrow(rawColorFilter);
323335
colorFilterDispose(rawColorFilter);
324336
});

engine/src/flutter/lib/web_ui/test/ui/filters_test.dart

Lines changed: 20 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -146,59 +146,39 @@ Future<void> testMain() async {
146146

147147
test('matrix color filter', () async {
148148
const ui.ColorFilter sepia = ui.ColorFilter.matrix(<double>[
149-
0.393,
150-
0.769,
151-
0.189,
152-
0,
153-
0,
154-
0.349,
155-
0.686,
156-
0.168,
157-
0,
158-
0,
159-
0.272,
160-
0.534,
161-
0.131,
162-
0,
163-
0,
164-
0,
165-
0,
166-
0,
167-
1,
168-
0,
149+
0.393, 0.769, 0.189, 0, 0, // row
150+
0.349, 0.686, 0.168, 0, 0, // row
151+
0.272, 0.534, 0.131, 0, 0, // row
152+
0, 0, 0, 1, 0, // row
169153
]);
170154
await drawTestImageWithPaint(ui.Paint()..colorFilter = sepia);
171155
await matchGoldenFile('ui_filter_matrix_colorfilter.png', region: region);
172156
expect(sepia.toString(), startsWith('ColorFilter.matrix([0.393, 0.769, 0.189, '));
173157
});
174158

159+
test('matrix color filter with 0..255 translation values', () async {
160+
const ui.ColorFilter sepia = ui.ColorFilter.matrix(<double>[
161+
0.393, 0.769, 0.189, 0, 50.0, // row
162+
0.349, 0.686, 0.168, 0, 50.0, // row
163+
0.272, 0.534, 0.131, 0, 50.0, // row
164+
0, 0, 0, 1, 0, // row
165+
]);
166+
await drawTestImageWithPaint(ui.Paint()..colorFilter = sepia);
167+
await matchGoldenFile('ui_filter_matrix_colorfilter_with_translation.png', region: region);
168+
expect(sepia.toString(), startsWith('ColorFilter.matrix([0.393, 0.769, 0.189, '));
169+
});
170+
175171
test('invert colors', () async {
176172
await drawTestImageWithPaint(ui.Paint()..invertColors = true);
177173
await matchGoldenFile('ui_filter_invert_colors.png', region: region);
178174
});
179175

180176
test('invert colors with color filter', () async {
181177
const ui.ColorFilter sepia = ui.ColorFilter.matrix(<double>[
182-
0.393,
183-
0.769,
184-
0.189,
185-
0,
186-
0,
187-
0.349,
188-
0.686,
189-
0.168,
190-
0,
191-
0,
192-
0.272,
193-
0.534,
194-
0.131,
195-
0,
196-
0,
197-
0,
198-
0,
199-
0,
200-
1,
201-
0,
178+
0.393, 0.769, 0.189, 0, 0, // row
179+
0.349, 0.686, 0.168, 0, 0, // row
180+
0.272, 0.534, 0.131, 0, 0, // row
181+
0, 0, 0, 1, 0, // row
202182
]);
203183

204184
await drawTestImageWithPaint(

0 commit comments

Comments
 (0)