From ae9bb0aa808e742d1f7200cfed104f9817854028 Mon Sep 17 00:00:00 2001 From: bwees Date: Sun, 25 Jan 2026 15:27:05 -0600 Subject: [PATCH] feat: mobile filter sending/recv --- mobile/lib/constants/filters.dart | 338 ++++++++++++++---- .../lib/domain/models/asset_edit.model.dart | 3 +- .../repositories/sync_stream.repository.dart | 1 + mobile/lib/pages/editing/filter.page.dart | 16 +- .../presentation/pages/drift_edit.page.dart | 51 +-- 5 files changed, 303 insertions(+), 106 deletions(-) diff --git a/mobile/lib/constants/filters.dart b/mobile/lib/constants/filters.dart index e77bcfbf1f..c14489fde4 100644 --- a/mobile/lib/constants/filters.dart +++ b/mobile/lib/constants/filters.dart @@ -1,52 +1,278 @@ import 'package:flutter/material.dart'; -const List filters = [ +class EditFilter { + final String name; + final double rrBias; + final double rgBias; + final double rbBias; + final double grBias; + final double ggBias; + final double gbBias; + final double brBias; + final double bgBias; + final double bbBias; + final double rOffset; + final double gOffset; + final double bOffset; + + const EditFilter({ + required this.name, + required this.rrBias, + required this.rgBias, + required this.rbBias, + required this.grBias, + required this.ggBias, + required this.gbBias, + required this.brBias, + required this.bgBias, + required this.bbBias, + required this.rOffset, + required this.gOffset, + required this.bOffset, + }); + + bool get isIdentity => + rrBias == 1 && + rgBias == 0 && + rbBias == 0 && + grBias == 0 && + ggBias == 1 && + gbBias == 0 && + brBias == 0 && + bgBias == 0 && + bbBias == 1 && + rOffset == 0 && + gOffset == 0 && + bOffset == 0; + + factory EditFilter.fromMatrix(List matrix, String name) { + if (matrix.length != 20) { + throw ArgumentError('Color filter matrix must have 20 elements'); + } + + return EditFilter( + name: name, + rrBias: matrix[0], + rgBias: matrix[1], + rbBias: matrix[2], + grBias: matrix[5], + ggBias: matrix[6], + gbBias: matrix[7], + brBias: matrix[10], + bgBias: matrix[11], + bbBias: matrix[12], + rOffset: matrix[4], + gOffset: matrix[9], + bOffset: matrix[14], + ); + } + + factory EditFilter.fromDtoParams(Map params, String name) { + print(params); + + return EditFilter( + name: name, + rrBias: (params['rrBias'] as num).toDouble(), + rgBias: (params['rgBias'] as num).toDouble(), + rbBias: (params['rbBias'] as num).toDouble(), + grBias: (params['grBias'] as num).toDouble(), + ggBias: (params['ggBias'] as num).toDouble(), + gbBias: (params['gbBias'] as num).toDouble(), + brBias: (params['brBias'] as num).toDouble(), + bgBias: (params['bgBias'] as num).toDouble(), + bbBias: (params['bbBias'] as num).toDouble(), + rOffset: (params['rOffset'] as num).toDouble(), + gOffset: (params['gOffset'] as num).toDouble(), + bOffset: (params['bOffset'] as num).toDouble(), + ); + } + + ColorFilter get colorFilter { + final colorMatrix = [ + rrBias, + rgBias, + rbBias, + 0, + rOffset, + grBias, + ggBias, + gbBias, + 0, + gOffset, + brBias, + bgBias, + bbBias, + 0, + bOffset, + 0, + 0, + 0, + 1, + 0, + ]; + + return ColorFilter.matrix(colorMatrix); + } + + Map get dtoParameters { + return { + "rrBias": rrBias, + "rgBias": rgBias, + "rbBias": rbBias, + "grBias": grBias, + "ggBias": ggBias, + "gbBias": gbBias, + "brBias": brBias, + "bgBias": bgBias, + "bbBias": bbBias, + "rOffset": rOffset, + "gOffset": gOffset, + "bOffset": bOffset, + }; + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (other is! EditFilter) return false; + + return rrBias == other.rrBias && + rgBias == other.rgBias && + rbBias == other.rbBias && + grBias == other.grBias && + ggBias == other.ggBias && + gbBias == other.gbBias && + brBias == other.brBias && + bgBias == other.bgBias && + bbBias == other.bbBias && + rOffset == other.rOffset && + gOffset == other.gOffset && + bOffset == other.bOffset; + } + + @override + int get hashCode => + name.hashCode ^ + rrBias.hashCode ^ + rgBias.hashCode ^ + rbBias.hashCode ^ + grBias.hashCode ^ + ggBias.hashCode ^ + gbBias.hashCode ^ + brBias.hashCode ^ + bgBias.hashCode ^ + bbBias.hashCode ^ + rOffset.hashCode ^ + gOffset.hashCode ^ + bOffset.hashCode; +} + +final List filters = [ //Original - ColorFilter.matrix([1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0], "Original"), //Vintage - ColorFilter.matrix([0.8, 0.1, 0.1, 0, 20, 0.1, 0.8, 0.1, 0, 20, 0.1, 0.1, 0.8, 0, 20, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([0.8, 0.1, 0.1, 0, 20, 0.1, 0.8, 0.1, 0, 20, 0.1, 0.1, 0.8, 0, 20, 0, 0, 0, 1, 0], "Vintage"), //Mood - ColorFilter.matrix([1.2, 0.1, 0.1, 0, 10, 0.1, 1, 0.1, 0, 10, 0.1, 0.1, 1, 0, 10, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([1.2, 0.1, 0.1, 0, 10, 0.1, 1, 0.1, 0, 10, 0.1, 0.1, 1, 0, 10, 0, 0, 0, 1, 0], "Mood"), //Crisp - ColorFilter.matrix([1.2, 0, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([1.2, 0, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1, 0], "Crisp"), //Cool - ColorFilter.matrix([0.9, 0, 0.2, 0, 0, 0, 1, 0.1, 0, 0, 0.1, 0, 1.2, 0, 0, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([0.9, 0, 0.2, 0, 0, 0, 1, 0.1, 0, 0, 0.1, 0, 1.2, 0, 0, 0, 0, 0, 1, 0], "Cool"), //Blush - ColorFilter.matrix([1.1, 0.1, 0.1, 0, 10, 0.1, 1, 0.1, 0, 10, 0.1, 0.1, 1, 0, 5, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([1.1, 0.1, 0.1, 0, 10, 0.1, 1, 0.1, 0, 10, 0.1, 0.1, 1, 0, 5, 0, 0, 0, 1, 0], "Blush"), //Sunkissed - ColorFilter.matrix([1.3, 0, 0.1, 0, 15, 0, 1.1, 0.1, 0, 10, 0, 0, 0.9, 0, 5, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([1.3, 0, 0.1, 0, 15, 0, 1.1, 0.1, 0, 10, 0, 0, 0.9, 0, 5, 0, 0, 0, 1, 0], "Sunkissed"), //Fresh - ColorFilter.matrix([1.2, 0, 0, 0, 20, 0, 1.2, 0, 0, 20, 0, 0, 1.1, 0, 20, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([1.2, 0, 0, 0, 20, 0, 1.2, 0, 0, 20, 0, 0, 1.1, 0, 20, 0, 0, 0, 1, 0], "Fresh"), //Classic - ColorFilter.matrix([1.1, 0, -0.1, 0, 10, -0.1, 1.1, 0.1, 0, 5, 0, -0.1, 1.1, 0, 0, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([1.1, 0, -0.1, 0, 10, -0.1, 1.1, 0.1, 0, 5, 0, -0.1, 1.1, 0, 0, 0, 0, 0, 1, 0], "Classic"), //Lomo-ish - ColorFilter.matrix([1.5, 0, 0.1, 0, 0, 0, 1.45, 0, 0, 0, 0.1, 0, 1.3, 0, 0, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([1.5, 0, 0.1, 0, 0, 0, 1.45, 0, 0, 0, 0.1, 0, 1.3, 0, 0, 0, 0, 0, 1, 0], "Lomo-ish"), //Nashville - ColorFilter.matrix([1.2, 0.15, -0.15, 0, 15, 0.1, 1.1, 0.1, 0, 10, -0.05, 0.2, 1.25, 0, 5, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([ + 1.2, + 0.15, + -0.15, + 0, + 15, + 0.1, + 1.1, + 0.1, + 0, + 10, + -0.05, + 0.2, + 1.25, + 0, + 5, + 0, + 0, + 0, + 1, + 0, + ], "Nashville"), //Valencia - ColorFilter.matrix([1.15, 0.1, 0.1, 0, 20, 0.1, 1.1, 0, 0, 10, 0.1, 0.1, 1.2, 0, 5, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([1.15, 0.1, 0.1, 0, 20, 0.1, 1.1, 0, 0, 10, 0.1, 0.1, 1.2, 0, 5, 0, 0, 0, 1, 0], "Valencia"), //Clarendon - ColorFilter.matrix([1.2, 0, 0, 0, 10, 0, 1.25, 0, 0, 10, 0, 0, 1.3, 0, 10, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([1.2, 0, 0, 0, 10, 0, 1.25, 0, 0, 10, 0, 0, 1.3, 0, 10, 0, 0, 0, 1, 0], "Clarendon"), //Moon - ColorFilter.matrix([0.33, 0.33, 0.33, 0, 0, 0.33, 0.33, 0.33, 0, 0, 0.33, 0.33, 0.33, 0, 0, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([ + 0.33, + 0.33, + 0.33, + 0, + 0, + 0.33, + 0.33, + 0.33, + 0, + 0, + 0.33, + 0.33, + 0.33, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + ], "Moon"), //Willow - ColorFilter.matrix([0.5, 0.5, 0.5, 0, 20, 0.5, 0.5, 0.5, 0, 20, 0.5, 0.5, 0.5, 0, 20, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([0.5, 0.5, 0.5, 0, 20, 0.5, 0.5, 0.5, 0, 20, 0.5, 0.5, 0.5, 0, 20, 0, 0, 0, 1, 0], "Willow"), //Kodak - ColorFilter.matrix([1.3, 0.1, -0.1, 0, 10, 0, 1.25, 0.1, 0, 10, 0, -0.1, 1.1, 0, 5, 0, 0, 0, 1, 0]), - //Frost - ColorFilter.matrix([0.8, 0.2, 0.1, 0, 0, 0.2, 1.1, 0.1, 0, 0, 0.1, 0.1, 1.2, 0, 10, 0, 0, 0, 1, 0]), - //Night Vision - ColorFilter.matrix([0.1, 0.95, 0.2, 0, 0, 0.1, 1.5, 0.1, 0, 0, 0.2, 0.7, 0, 0, 0, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([1.3, 0.1, -0.1, 0, 10, 0, 1.25, 0.1, 0, 10, 0, -0.1, 1.1, 0, 5, 0, 0, 0, 1, 0], "Kodak"), //Sunset - ColorFilter.matrix([1.5, 0.2, 0, 0, 0, 0.1, 0.9, 0.1, 0, 0, -0.1, -0.2, 1.3, 0, 0, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([1.5, 0.2, 0, 0, 0, 0.1, 0.9, 0.1, 0, 0, -0.1, -0.2, 1.3, 0, 0, 0, 0, 0, 1, 0], "Sunset"), //Noir - ColorFilter.matrix([1.3, -0.3, 0.1, 0, 0, -0.1, 1.2, -0.1, 0, 0, 0.1, -0.2, 1.3, 0, 0, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([1.3, -0.3, 0.1, 0, 0, -0.1, 1.2, -0.1, 0, 0, 0.1, -0.2, 1.3, 0, 0, 0, 0, 0, 1, 0], "Noir"), //Dreamy - ColorFilter.matrix([1.1, 0.1, 0.1, 0, 0, 0.1, 1.1, 0.1, 0, 0, 0.1, 0.1, 1.1, 0, 15, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([1.1, 0.1, 0.1, 0, 0, 0.1, 1.1, 0.1, 0, 0, 0.1, 0.1, 1.1, 0, 15, 0, 0, 0, 1, 0], "Dreamy"), //Sepia - ColorFilter.matrix([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]), + EditFilter.fromMatrix([ + 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, + ], "Sepia"), //Radium - ColorFilter.matrix([ + EditFilter.fromMatrix([ 1.438, -0.062, -0.062, @@ -67,9 +293,9 @@ const List filters = [ 0, 1, 0, - ]), + ], "Radium"), //Aqua - ColorFilter.matrix([ + EditFilter.fromMatrix([ 0.2126, 0.7152, 0.0722, @@ -90,59 +316,23 @@ const List filters = [ 0, 1, 0, - ]), + ], "Aqua"), //Purple Haze - ColorFilter.matrix([1.3, 0, 1.2, 0, 0, 0, 1.1, 0, 0, 0, 0.2, 0, 1.3, 0, 0, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([1.3, 0, 1.2, 0, 0, 0, 1.1, 0, 0, 0, 0.2, 0, 1.3, 0, 0, 0, 0, 0, 1, 0], "Purple Haze"), //Lemonade - ColorFilter.matrix([1.2, 0.1, 0, 0, 0, 0, 1.1, 0.2, 0, 0, 0.1, 0, 0.7, 0, 0, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([1.2, 0.1, 0, 0, 0, 0, 1.1, 0.2, 0, 0, 0.1, 0, 0.7, 0, 0, 0, 0, 0, 1, 0], "Lemonade"), //Caramel - ColorFilter.matrix([1.6, 0.2, 0, 0, 0, 0.1, 1.3, 0.1, 0, 0, 0, 0.1, 0.9, 0, 0, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([1.6, 0.2, 0, 0, 0, 0.1, 1.3, 0.1, 0, 0, 0, 0.1, 0.9, 0, 0, 0, 0, 0, 1, 0], "Caramel"), //Peachy - ColorFilter.matrix([1.3, 0.5, 0, 0, 0, 0.2, 1.1, 0.3, 0, 0, 0.1, 0.1, 1.2, 0, 0, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([1.3, 0.5, 0, 0, 0, 0.2, 1.1, 0.3, 0, 0, 0.1, 0.1, 1.2, 0, 0, 0, 0, 0, 1, 0], "Peachy"), //Neon - ColorFilter.matrix([1, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([1, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 1, 0], "Neon"), //Cold Morning - ColorFilter.matrix([0.9, 0.1, 0.2, 0, 0, 0, 1, 0.1, 0, 0, 0.1, 0, 1.2, 0, 0, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([0.9, 0.1, 0.2, 0, 0, 0, 1, 0.1, 0, 0, 0.1, 0, 1.2, 0, 0, 0, 0, 0, 1, 0], "Cold Morning"), //Lush - ColorFilter.matrix([0.9, 0.2, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1.1, 0, 0, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([0.9, 0.2, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1.1, 0, 0, 0, 0, 0, 1, 0], "Lush"), //Urban Neon - ColorFilter.matrix([1.1, 0, 0.3, 0, 0, 0, 0.9, 0.3, 0, 0, 0.3, 0.1, 1.2, 0, 0, 0, 0, 0, 1, 0]), + EditFilter.fromMatrix([1.1, 0, 0.3, 0, 0, 0, 0.9, 0.3, 0, 0, 0.3, 0.1, 1.2, 0, 0, 0, 0, 0, 1, 0], "Urban Neon"), //Monochrome - ColorFilter.matrix([0.6, 0.2, 0.2, 0, 0, 0.2, 0.6, 0.2, 0, 0, 0.2, 0.2, 0.7, 0, 0, 0, 0, 0, 1, 0]), -]; - -const List filterNames = [ - 'Original', - 'Vintage', - 'Mood', - 'Crisp', - 'Cool', - 'Blush', - 'Sunkissed', - 'Fresh', - 'Classic', - 'Lomo-ish', - 'Nashville', - 'Valencia', - 'Clarendon', - 'Moon', - 'Willow', - 'Kodak', - 'Frost', - 'Night Vision', - 'Sunset', - 'Noir', - 'Dreamy', - 'Sepia', - 'Radium', - 'Aqua', - 'Purple Haze', - 'Lemonade', - 'Caramel', - 'Peachy', - 'Neon', - 'Cold Morning', - 'Lush', - 'Urban Neon', - 'Monochrome', + EditFilter.fromMatrix([0.6, 0.2, 0.2, 0, 0, 0.2, 0.6, 0.2, 0, 0, 0.2, 0.2, 0.7, 0, 0, 0, 0, 0, 1, 0], "Monochrome"), ]; diff --git a/mobile/lib/domain/models/asset_edit.model.dart b/mobile/lib/domain/models/asset_edit.model.dart index b3266dba46..01bdd273a6 100644 --- a/mobile/lib/domain/models/asset_edit.model.dart +++ b/mobile/lib/domain/models/asset_edit.model.dart @@ -1,6 +1,6 @@ import "package:openapi/api.dart" as api show AssetEditAction; -enum AssetEditAction { rotate, crop, mirror, other } +enum AssetEditAction { rotate, crop, mirror, filter, other } extension AssetEditActionExtension on AssetEditAction { api.AssetEditAction? toDto() { @@ -8,6 +8,7 @@ extension AssetEditActionExtension on AssetEditAction { AssetEditAction.rotate => api.AssetEditAction.rotate, AssetEditAction.crop => api.AssetEditAction.crop, AssetEditAction.mirror => api.AssetEditAction.mirror, + AssetEditAction.filter => api.AssetEditAction.filter, AssetEditAction.other => null, }; } diff --git a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart index 09a6a8da10..60295b8f48 100644 --- a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart @@ -810,6 +810,7 @@ extension on api.AssetEditAction { api.AssetEditAction.crop => AssetEditAction.crop, api.AssetEditAction.rotate => AssetEditAction.rotate, api.AssetEditAction.mirror => AssetEditAction.mirror, + api.AssetEditAction.filter => AssetEditAction.filter, _ => AssetEditAction.other, }; } diff --git a/mobile/lib/pages/editing/filter.page.dart b/mobile/lib/pages/editing/filter.page.dart index f8b144bb96..611e6c23d0 100644 --- a/mobile/lib/pages/editing/filter.page.dart +++ b/mobile/lib/pages/editing/filter.page.dart @@ -23,7 +23,7 @@ class FilterImagePage extends HookWidget { @override Widget build(BuildContext context) { - final colorFilter = useState(filters[0]); + final colorFilter = useState(filters[0]); final selectedFilterIndex = useState(0); Future createFilteredImage(ui.Image inputImage, ColorFilter filter) { @@ -42,12 +42,12 @@ class FilterImagePage extends HookWidget { return completer.future; } - void applyFilter(ColorFilter filter, int index) { + void applyFilter(EditFilter filter, int index) { colorFilter.value = filter; selectedFilterIndex.value = index; } - Future applyFilterAndConvert(ColorFilter filter) async { + Future applyFilterAndConvert(EditFilter filter) async { final completer = Completer(); image.image .resolve(ImageConfiguration.empty) @@ -58,7 +58,7 @@ class FilterImagePage extends HookWidget { ); final uiImage = await completer.future; - final filteredUiImage = await createFilteredImage(uiImage, filter); + final filteredUiImage = await createFilteredImage(uiImage, filter.colorFilter); final byteData = await filteredUiImage.toByteData(format: ui.ImageByteFormat.png); final pngBytes = byteData!.buffer.asUint8List(); @@ -86,7 +86,7 @@ class FilterImagePage extends HookWidget { SizedBox( height: context.height * 0.7, child: Center( - child: ColorFiltered(colorFilter: colorFilter.value, child: image), + child: ColorFiltered(colorFilter: colorFilter.value.colorFilter, child: image), ), ), SizedBox( @@ -99,7 +99,7 @@ class FilterImagePage extends HookWidget { padding: const EdgeInsets.symmetric(horizontal: 8.0), child: _FilterButton( image: image, - label: filterNames[index], + label: filters[index].name, filter: filters[index], isSelected: selectedFilterIndex.value == index, onTap: () => applyFilter(filters[index], index), @@ -117,7 +117,7 @@ class FilterImagePage extends HookWidget { class _FilterButton extends StatelessWidget { final Image image; final String label; - final ColorFilter filter; + final EditFilter filter; final bool isSelected; final VoidCallback onTap; @@ -145,7 +145,7 @@ class _FilterButton extends StatelessWidget { child: ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(10)), child: ColorFiltered( - colorFilter: filter, + colorFilter: filter.colorFilter, child: FittedBox(fit: BoxFit.cover, child: image), ), ), diff --git a/mobile/lib/presentation/pages/drift_edit.page.dart b/mobile/lib/presentation/pages/drift_edit.page.dart index 1938c8903b..19d75f4ea4 100644 --- a/mobile/lib/presentation/pages/drift_edit.page.dart +++ b/mobile/lib/presentation/pages/drift_edit.page.dart @@ -49,7 +49,7 @@ class _DriftEditImagePageState extends ConsumerState with Ti int _rotationAngle = 0; bool _flipHorizontal = false; bool _flipVertical = false; - ColorFilter? _colorFilter; + EditFilter? _filter; double? _aspectRatio; late final originalWidth = widget.exifInfo.isFlipped ? widget.exifInfo.height : widget.exifInfo.width; @@ -74,6 +74,12 @@ class _DriftEditImagePageState extends ConsumerState with Ti final (rotationAngle, flipHorizontal, flipVertical) = normalizeTransformEdits(widget.edits); + final existingFilter = widget.edits.firstWhereOrNull((edit) => edit.action == AssetEditAction.filter); + if (existingFilter != null) { + final parsedFilter = EditFilter.fromDtoParams(existingFilter.parameters, 'Custom'); + _filter = filters.firstWhereOrNull((filter) => filter == parsedFilter); + } + // dont animate to initial rotation _rotationAnimationDuration = const Duration(milliseconds: 0); _rotationAngle = rotationAngle.toInt(); @@ -122,6 +128,10 @@ class _DriftEditImagePageState extends ConsumerState with Ti ); } + if (_filter != null && !_filter!.isIdentity) { + edits.add(AssetEdit(action: AssetEditAction.filter, parameters: _filter!.dtoParameters)); + } + try { final completer = ref.read(websocketProvider.notifier).waitForEvent("AssetEditReadyV1", (dynamic data) { final eventData = data as Map; @@ -208,9 +218,9 @@ class _DriftEditImagePageState extends ConsumerState with Ti }); } - void _applyFilter(ColorFilter? filter) { + void _applyFilter(EditFilter? filter) { setState(() { - _colorFilter = filter; + _filter = filter; }); } @@ -222,7 +232,7 @@ class _DriftEditImagePageState extends ConsumerState with Ti _rotationAngle = 0; _flipHorizontal = false; _flipVertical = false; - _colorFilter = null; + _filter = null; _aspectRatio = null; }); } @@ -231,7 +241,7 @@ class _DriftEditImagePageState extends ConsumerState with Ti final isCropped = cropController.crop != const Rect.fromLTRB(0, 0, 1, 1); final isRotated = (_rotationAngle % 360 + 360) % 360 != 0; final isFlipped = _flipHorizontal || _flipVertical; - final isFiltered = _colorFilter != null; + final isFiltered = _filter != null && !_filter!.isIdentity; return isCropped || isRotated || isFlipped || isFiltered; } @@ -292,7 +302,10 @@ class _DriftEditImagePageState extends ConsumerState with Ti controller: cropController, image: widget.image, gridColor: Colors.white, - overlayPainter: MatrixAdjustmentPainter(image: data.data!, filter: _colorFilter), + overlayPainter: MatrixAdjustmentPainter( + image: data.data!, + filter: _filter?.colorFilter, + ), ); }, ), @@ -334,7 +347,7 @@ class _DriftEditImagePageState extends ConsumerState with Ti aspectRatio: _aspectRatio, ), secondChild: _FilterControls( - currentFilter: _colorFilter, + currentFilter: _filter, previewImage: widget.image, onApplyFilter: _applyFilter, ), @@ -540,9 +553,9 @@ class _TransformControls extends StatelessWidget { } class _FilterControls extends StatelessWidget { - final ColorFilter? currentFilter; + final EditFilter? currentFilter; final Image previewImage; - final void Function(ColorFilter?) onApplyFilter; + final void Function(EditFilter?) onApplyFilter; const _FilterControls({required this.currentFilter, required this.previewImage, required this.onApplyFilter}); @@ -555,14 +568,13 @@ class _FilterControls extends StatelessWidget { child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( - children: filters.mapIndexed((i, filter) { + children: filters.map((filter) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), child: _FilterButton( image: previewImage, - label: filterNames[i], filter: filter, - isSelected: currentFilter == filters[i], + isSelected: currentFilter == filter, onTap: () => onApplyFilter(filter), ), ); @@ -576,18 +588,11 @@ class _FilterControls extends StatelessWidget { class _FilterButton extends StatelessWidget { final Image image; - final String label; - final ColorFilter filter; + final EditFilter filter; final bool isSelected; final VoidCallback onTap; - const _FilterButton({ - required this.image, - required this.label, - required this.filter, - required this.isSelected, - required this.onTap, - }); + const _FilterButton({required this.image, required this.filter, required this.isSelected, required this.onTap}); @override Widget build(BuildContext context) { @@ -605,14 +610,14 @@ class _FilterButton extends StatelessWidget { child: ClipRRect( borderRadius: BorderRadius.all(isSelected ? const Radius.circular(9) : const Radius.circular(12)), child: ColorFiltered( - colorFilter: filter, + colorFilter: filter.colorFilter, child: Image(image: image.image, fit: BoxFit.cover), ), ), ), ), const SizedBox(height: 10), - Text(label, style: context.themeData.textTheme.bodyMedium), + Text(filter.name, style: context.themeData.textTheme.bodyMedium), ], ); }