Skip to content

Commit

Permalink
Improve color filter and documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
veloce committed Dec 19, 2024
1 parent 2e4b4ba commit 5c903ae
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 108 deletions.
1 change: 1 addition & 0 deletions lib/chessground.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export 'src/images.dart';
export 'src/premove.dart';
export 'src/widgets/board.dart';
export 'src/widgets/board_border.dart';
export 'src/widgets/color_filter.dart';
export 'src/widgets/static_board.dart';
export 'src/widgets/board_editor.dart';
export 'src/widgets/highlight.dart';
Expand Down
7 changes: 6 additions & 1 deletion lib/src/board_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,14 @@ class ChessboardSettings {
final List<BoxShadow> boxShadow;

/// Brightness adjustment of the board
///
/// A value under 1.0 darkens the board, while a value over 1.0 brightens it.
/// A value of 0.0 will make it completely black. Default value is 1.0.
final double brightness;

/// Hue adjustment of the board
/// Hue rotation of the board as an angle in degree from 0.0 to 360.0.
///
/// A value of 0.0 leaves the hue unchanged. Default value is 0.0.
final double hue;

/// Whether to show board coordinates
Expand Down
4 changes: 2 additions & 2 deletions lib/src/widgets/board.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import 'package:flutter/widgets.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';

import 'board_border.dart';
import 'change_colors.dart';
import 'color_filter.dart';
import 'piece.dart';
import 'highlight.dart';
import 'positioned_square.dart';
Expand Down Expand Up @@ -413,7 +413,7 @@ class _BoardState extends State<Chessboard> {
: board;

return widget.settings.hue != 0 || widget.settings.brightness != 0
? ChangeColors(
? BrightnessHueFilter(
hue: widget.settings.hue,
brightness: widget.settings.brightness,
child: borderedChessboard,
Expand Down
6 changes: 3 additions & 3 deletions lib/src/widgets/board_editor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import '../board_settings.dart';
import '../models.dart';
import '../fen.dart';
import 'board_border.dart';
import 'change_colors.dart';
import 'color_filter.dart';
import 'highlight.dart';
import 'piece.dart';
import 'positioned_square.dart';
Expand Down Expand Up @@ -234,7 +234,7 @@ class _BoardEditorState extends State<ChessboardEditor> {

final coloredBoard =
widget.settings.hue != 0 || widget.settings.brightness != 0
? ChangeColors(
? BrightnessHueFilter(
hue: widget.settings.hue,
brightness: widget.settings.brightness,
child: board,
Expand All @@ -251,7 +251,7 @@ class _BoardEditorState extends State<ChessboardEditor> {
);
}

return ChangeColors(
return BrightnessHueFilter(
brightness: widget.settings.brightness,
hue: widget.settings.hue,
child: coloredBoard,
Expand Down
100 changes: 0 additions & 100 deletions lib/src/widgets/change_colors.dart

This file was deleted.

200 changes: 200 additions & 0 deletions lib/src/widgets/color_filter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import 'dart:math' as math;

import 'package:flutter/material.dart';

/// Change the brightness and hue of the child widget.
///
/// The brightness and hue are applied to the child widget using a [ColorFilter].
class BrightnessHueFilter extends StatelessWidget {
const BrightnessHueFilter({
this.brightness = 1.0,
this.hue = 0.0,
required this.child,
super.key,
});

/// Apply a linear multiplier to the child, making it appear brighter or darker.
///
/// A value under 1.0 darkens the Widget, while a value over 1.0 brightens it.
/// A value of 0.0 will make it completely black.
/// Default value is 1.0.
final double brightness;

/// Rotates the hue of the child by the given angle in degrees.
///
/// A positive hue rotation increases the hue value, while a negative rotation decreases the hue value.
/// Default value is 0.0.
final double hue;

final Widget child;

@override
Widget build(BuildContext context) {
if (brightness == 1.0 && hue == 0.0) {
return child;
}
List<double> matrix = _baseMatrix;
if (brightness != 1.0) {
matrix = _brightnessFilter(matrix, value: brightness);
}
if (hue != 0.0) {
matrix = _hueFilter(matrix, value: hue);
}
return ColorFiltered(
colorFilter: ColorFilter.matrix(matrix.sublist(0, 20)), child: child);
}
}

List<double> _brightnessFilter(List<double> matrix, {required double value}) {
return multiplyMatrix5(matrix, <double>[
// dart format off
value, 0, 0, 0, 0,
0, value, 0, 0, 0,
0, 0, value, 0, 0,
0, 0, 0, 1, 0,
0, 0, 0, 0, 1
// dart format on
]);
}

/// Check: https://stackoverflow.com/questions/64639589/how-to-adjust-hue-saturation-and-brightness-of-an-image-in-flutter
List<double> _hueFilter(List<double> matrix, {required double value}) {
final double v = math.pi * (value / 180.0);
final double cosVal = math.cos(v);
final double sinVal = math.sin(v);
const double lumR = 0.213;
const double lumG = 0.715;
const double lumB = 0.072;

return multiplyMatrix5(matrix, <double>[
(lumR + (cosVal * (1 - lumR))) + (sinVal * (-lumR)),
(lumG + (cosVal * (-lumG))) + (sinVal * (-lumG)),
(lumB + (cosVal * (-lumB))) + (sinVal * (1 - lumB)),
0,
0,
(lumR + (cosVal * (-lumR))) + (sinVal * 0.143),
(lumG + (cosVal * (1 - lumG))) + (sinVal * 0.14),
(lumB + (cosVal * (-lumB))) + (sinVal * (-0.283)),
0,
0,
(lumR + (cosVal * (-lumR))) + (sinVal * (-(1 - lumR))),
(lumG + (cosVal * (-lumG))) + (sinVal * lumG),
(lumB + (cosVal * (1 - lumB))) + (sinVal * lumB),
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
0,
1,
]);
}

const List<double> _baseMatrix = [
// dart format off
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0,
0, 0, 0, 0, 1
// dart format on
];

/// Check: https://github.com/openkraken/kraken/blob/main/kraken/lib/src/css/filter.dart
/// Calc 5x5 matrix multiplication.
List<double> multiplyMatrix5(List<double> a, List<double> b) {
if (a.length != b.length) {
throw ArgumentError('Matrix length should be same.');
}

if (a.length != 25) {
throw ArgumentError('Matrix5 size is not correct.');
}

final a00 = a[0];
final a01 = a[1];
final a02 = a[2];
final a03 = a[3];
final a04 = a[4];
final a10 = a[5];
final a11 = a[6];
final a12 = a[7];
final a13 = a[8];
final a14 = a[9];
final a20 = a[10];
final a21 = a[11];
final a22 = a[12];
final a23 = a[13];
final a24 = a[14];
final a30 = a[15];
final a31 = a[16];
final a32 = a[17];
final a33 = a[18];
final a34 = a[19];
final a40 = a[20];
final a41 = a[21];
final a42 = a[22];
final a43 = a[23];
final a44 = a[24];

final b00 = b[0];
final b01 = b[1];
final b02 = b[2];
final b03 = b[3];
final b04 = b[4];
final b10 = b[5];
final b11 = b[6];
final b12 = b[7];
final b13 = b[8];
final b14 = b[9];
final b20 = b[10];
final b21 = b[11];
final b22 = b[12];
final b23 = b[13];
final b24 = b[14];
final b30 = b[15];
final b31 = b[16];
final b32 = b[17];
final b33 = b[18];
final b34 = b[19];
final b40 = b[20];
final b41 = b[21];
final b42 = b[22];
final b43 = b[23];
final b44 = b[24];

return [
// dart format off
a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30 + a04 * b40,
a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31 + a04 * b41,
a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32 + a04 * b42,
a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33 + a04 * b43,
a00 * b04 + a01 * b14 + a02 * b24 + a03 * b34 + a04 * b44,
a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30 + a14 * b40,
a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41,
a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42,
a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43,
a10 * b04 + a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44,
a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30 + a24 * b40,
a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41,
a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42,
a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43,
a20 * b04 + a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44,
a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30 + a34 * b40,
a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41,
a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42,
a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43,
a30 * b04 + a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44,
a40 * b00 + a41 * b10 + a42 * b20 + a43 * b30 + a44 * b40,
a40 * b01 + a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41,
a40 * b02 + a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42,
a40 * b03 + a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43,
a40 * b04 + a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44,
// dart format on
];
}
4 changes: 2 additions & 2 deletions lib/src/widgets/static_board.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import '../board_color_scheme.dart';
import '../fen.dart';
import '../models.dart';
import '../piece_set.dart';
import 'change_colors.dart';
import 'color_filter.dart';
import 'geometry.dart';
import 'highlight.dart';
import 'piece.dart';
Expand Down Expand Up @@ -156,7 +156,7 @@ class _StaticChessboardState extends State<StaticChessboard> {
);

return widget.hue != 0 || widget.brightness != 0
? ChangeColors(
? BrightnessHueFilter(
hue: widget.hue,
brightness: widget.brightness,
child: board,
Expand Down

0 comments on commit 5c903ae

Please sign in to comment.