Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ void native_document::append_scene(
};
const auto brightness_filter = resolve_single_filter("brightness", 10.0F);
const auto grayscale_filter = resolve_single_filter("grayscale", 1.0F);
const auto contrast_filter = resolve_single_filter("contrast", 10.0F);
const auto has_opacity_group = opacity < 0.999F;
if (has_opacity_group) {
commands.push_back(webscene_scene_command{
Expand Down Expand Up @@ -422,6 +423,22 @@ void native_document::append_scene(
0,
*grayscale_filter});
}
if (contrast_filter.has_value()) {
commands.push_back(webscene_scene_command{
30U,
1U << 29U,
node.layout.x,
node.layout.y,
node.layout.width,
node.layout.height,
255U,
node.id,
0,
0,
0,
0,
*contrast_filter});
}
const auto resolve_transform_origin = [](css_length value, float available, float fallback) {
if (value.unit == length_unit::pixels) return value.value;
if (value.unit == length_unit::percent) {
Expand Down Expand Up @@ -1982,6 +1999,11 @@ void native_document::append_scene(
if (has_scale_transform) {
commands.push_back(webscene_scene_command{16U, 0U, 0, 0, 0, 0, 0U, node.id});
}
if (contrast_filter.has_value()) {
commands.push_back(webscene_scene_command{
31U, 0U, node.layout.x, node.layout.y,
node.layout.width, node.layout.height, 0U, node.id});
}
if (grayscale_filter.has_value()) {
commands.push_back(webscene_scene_command{
31U, 0U, node.layout.x, node.layout.y,
Expand Down Expand Up @@ -2344,6 +2366,11 @@ void native_document::append_scene(
if (has_scale_transform) {
commands.push_back(webscene_scene_command{16U, 0U, 0, 0, 0, 0, 0U, node.id});
}
if (contrast_filter.has_value()) {
commands.push_back(webscene_scene_command{
31U, 0U, node.layout.x, node.layout.y,
node.layout.width, node.layout.height, 0U, node.id});
}
if (grayscale_filter.has_value()) {
commands.push_back(webscene_scene_command{
31U, 0U, node.layout.x, node.layout.y,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,9 @@ typedef struct webscene_scene_header {
// Clip kind 12 uses flags bit 31 to select an SVG path stored in the indexed
// scene string; the remaining bits are its string index. A zero flag retains
// the rounded-rectangle fields used by existing producers and presenters.
// Group kind 30 uses flags bit 31 for brightness and bit 30 for grayscale;
// stroke_width carries the bounded non-negative amount. A zero flag retains
// the opacity-group alpha stored in the low byte of rgba.
// Group kind 30 uses flags bit 31 for brightness, bit 30 for grayscale, and
// bit 29 for contrast; stroke_width carries the bounded non-negative amount.
// A zero flag retains the opacity-group alpha stored in the low byte of rgba.
typedef struct webscene_scene_command {
uint32_t kind;
uint32_t flags;
Expand Down
30 changes: 24 additions & 6 deletions src/WebScene.Backend.Avalonia/NativeCanvasSceneRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ internal sealed unsafe partial class NativeCanvasSceneRenderer
private const uint DomPolygonClipIndexMask = ~DomPolygonClipResource;
private const uint DomBrightnessFilter = 1u << 31;
private const uint DomGrayscaleFilter = 1u << 30;
private const uint DomColorFilterMask = DomBrightnessFilter | DomGrayscaleFilter;
private const uint DomContrastFilter = 1u << 29;
private const uint DomColorFilterMask =
DomBrightnessFilter | DomGrayscaleFilter | DomContrastFilter;

private readonly Dictionary<uint, RetainedLayer> s_layers = new();
private readonly List<RetainedLayer> s_orderedLayers = [];
Expand Down Expand Up @@ -851,19 +853,35 @@ private static void SaveDomGroup(
return;
}
var amount = Math.Max(0, command.StrokeWidth);
var matrix = (command.Flags & DomBrightnessFilter) != 0
? new float[] {
float[] matrix;
if ((command.Flags & DomBrightnessFilter) != 0)
{
matrix = [
amount, 0, 0, 0, 0,
0, amount, 0, 0, 0,
0, 0, amount, 0, 0,
0, 0, 0, 1, 0
}
: new float[] {
];
}
else if ((command.Flags & DomGrayscaleFilter) != 0)
{
matrix = [
1 - 0.7874f * amount, 0.7152f * amount, 0.0722f * amount, 0, 0,
0.2126f * amount, 1 - 0.2848f * amount, 0.0722f * amount, 0, 0,
0.2126f * amount, 0.7152f * amount, 1 - 0.9278f * amount, 0, 0,
0, 0, 0, 1, 0
};
];
}
else
{
var intercept = 127.5f * (1 - amount);
matrix = [
amount, 0, 0, 0, intercept,
0, amount, 0, 0, intercept,
0, 0, amount, 0, intercept,
0, 0, 0, 1, 0
];
}
using var filter = SKColorFilter.CreateColorMatrix(matrix);
paint.ColorFilter = filter;
canvas.SaveLayer(paint);
Expand Down
41 changes: 27 additions & 14 deletions src/WebScene.Backend.Flutter/lib/src/scene_projector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ const int _domPolygonClipResource = 1 << 31;
const int _domPolygonClipIndexMask = _domPolygonClipResource - 1;
const int _domBrightnessFilter = 1 << 31;
const int _domGrayscaleFilter = 1 << 30;
const int _domColorFilterMask = _domBrightnessFilter | _domGrayscaleFilter;
const int _domContrastFilter = 1 << 29;
const int _domColorFilterMask =
_domBrightnessFilter | _domGrayscaleFilter | _domContrastFilter;

final class SceneApplyResult {
const SceneApplyResult({
Expand Down Expand Up @@ -966,19 +968,30 @@ final class WebSceneSceneProjector extends ChangeNotifier {
..color = ui.Color.fromARGB(command.rgba & 0xff, 255, 255, 255);
}
final amount = command.strokeWidth.clamp(0.0, double.infinity).toDouble();
final matrix = command.flags & _domBrightnessFilter != 0
? <double>[
amount, 0, 0, 0, 0,
0, amount, 0, 0, 0,
0, 0, amount, 0, 0,
0, 0, 0, 1, 0,
]
: <double>[
1 - 0.7874 * amount, 0.7152 * amount, 0.0722 * amount, 0, 0,
0.2126 * amount, 1 - 0.2848 * amount, 0.0722 * amount, 0, 0,
0.2126 * amount, 0.7152 * amount, 1 - 0.9278 * amount, 0, 0,
0, 0, 0, 1, 0,
];
final List<double> matrix;
if (command.flags & _domBrightnessFilter != 0) {
matrix = <double>[
amount, 0, 0, 0, 0,
0, amount, 0, 0, 0,
0, 0, amount, 0, 0,
0, 0, 0, 1, 0,
];
} else if (command.flags & _domGrayscaleFilter != 0) {
matrix = <double>[
1 - 0.7874 * amount, 0.7152 * amount, 0.0722 * amount, 0, 0,
0.2126 * amount, 1 - 0.2848 * amount, 0.0722 * amount, 0, 0,
0.2126 * amount, 0.7152 * amount, 1 - 0.9278 * amount, 0, 0,
0, 0, 0, 1, 0,
];
} else {
final intercept = 127.5 * (1 - amount);
matrix = <double>[
amount, 0, 0, 0, intercept,
0, amount, 0, 0, intercept,
0, 0, amount, 0, intercept,
0, 0, 0, 1, 0,
];
}
return ui.Paint()
..colorFilter = ui.ColorFilter.matrix(matrix);
}
Expand Down
Loading