diff --git a/assets/images/h-flip.png b/assets/images/h-flip.png new file mode 100644 index 00000000..dd484305 Binary files /dev/null and b/assets/images/h-flip.png differ diff --git a/lib/constants/asset_paths.dart b/lib/constants/asset_paths.dart new file mode 100644 index 00000000..e4726284 --- /dev/null +++ b/lib/constants/asset_paths.dart @@ -0,0 +1,8 @@ +class ImageAssets { + static const String flipHorizontal = 'assets/images/h-flip.png'; + static const String whiteBoard = 'assets/canvas/white.png'; + static const String redBoard = 'assets/canvas/red.png'; + static const String blackBoard = 'assets/canvas/black.png'; + static const String epaper37Bwr = 'assets/images/displays/epaper_3.7_bwr.png'; + static const String epaper37Bw = 'assets/images/displays/epaper_3.7_bw.PNG'; +} diff --git a/lib/constants.dart b/lib/constants/color_constants.dart similarity index 100% rename from lib/constants.dart rename to lib/constants/color_constants.dart diff --git a/lib/constants/string_constants.dart b/lib/constants/string_constants.dart new file mode 100644 index 00000000..d7066264 --- /dev/null +++ b/lib/constants/string_constants.dart @@ -0,0 +1,3 @@ +class StringConstants { + static const String appName = 'Magic epaper'; +} diff --git a/lib/pro_image_editor/features/movable_background_image.dart b/lib/pro_image_editor/features/movable_background_image.dart index 97637418..bf7aa837 100644 --- a/lib/pro_image_editor/features/movable_background_image.dart +++ b/lib/pro_image_editor/features/movable_background_image.dart @@ -8,6 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:magic_epaper_app/constants/asset_paths.dart'; import 'package:magic_epaper_app/pro_image_editor/features/bottom_bar.dart'; import 'package:magic_epaper_app/pro_image_editor/features/text_bottom_bar.dart'; import 'package:magic_epaper_app/provider/color_palette_provider.dart'; @@ -56,9 +57,9 @@ class _MovableBackgroundImageExampleState @override void initState() { super.initState(); - preCacheImage(assetPath: 'assets/canvas/white.png'); - preCacheImage(assetPath: 'assets/canvas/red.png'); - preCacheImage(assetPath: 'assets/canvas/black.png'); + preCacheImage(assetPath: ImageAssets.whiteBoard); + preCacheImage(assetPath: ImageAssets.redBoard); + preCacheImage(assetPath: ImageAssets.blackBoard); //_createTransparentImage(_imgRatio); _bottomBarScrollCtrl = ScrollController(); } @@ -335,7 +336,7 @@ class _MovableBackgroundImageExampleState secondary: Color(0xFFE2E2E2), ), child: ProImageEditor.asset( - 'assets/canvas/white.png', + ImageAssets.whiteBoard, key: editorKey, callbacks: ProImageEditorCallbacks( onImageEditingStarted: onImageEditingStarted, @@ -350,7 +351,7 @@ class _MovableBackgroundImageExampleState offset: Offset.zero, scale: _initScale, widget: Image.asset( - 'assets/canvas/white.png', + ImageAssets.whiteBoard, width: 240, height: 416, frameBuilder: diff --git a/lib/pro_image_editor/features/reorder_layer_example.dart b/lib/pro_image_editor/features/reorder_layer_example.dart index 7676cd7d..75ecb7fd 100644 --- a/lib/pro_image_editor/features/reorder_layer_example.dart +++ b/lib/pro_image_editor/features/reorder_layer_example.dart @@ -1,6 +1,7 @@ // Flutter imports: import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:magic_epaper_app/constants/asset_paths.dart'; // Package imports: import 'package:pro_image_editor/pro_image_editor.dart'; @@ -40,7 +41,7 @@ class _ReorderLayerExampleState extends State @override void initState() { super.initState(); - preCacheImage(assetPath: 'assets/canvas/white.png'); + preCacheImage(assetPath: ImageAssets.whiteBoard); } @override @@ -48,7 +49,7 @@ class _ReorderLayerExampleState extends State if (!isPreCached) return const PrepareImageWidget(); return ProImageEditor.asset( - 'assets/canvas/white.png', + ImageAssets.whiteBoard, key: editorKey, callbacks: ProImageEditorCallbacks( onImageEditingStarted: onImageEditingStarted, diff --git a/lib/util/epd/gdey037z03.dart b/lib/util/epd/gdey037z03.dart index 3ee4633a..d43c5796 100644 --- a/lib/util/epd/gdey037z03.dart +++ b/lib/util/epd/gdey037z03.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:magic_epaper_app/constants/asset_paths.dart'; import 'package:magic_epaper_app/util/epd/driver/uc8253.dart'; import 'package:magic_epaper_app/util/image_processing/image_processing.dart'; import 'driver/driver.dart'; @@ -16,7 +17,7 @@ class Gdey037z03 extends Epd { @override String get modelId => 'GDEY037Z03'; @override - String get imgPath => "assets/images/displays/epaper_3.7_bwr.png"; + String get imgPath => ImageAssets.epaper37Bwr; @override get colors => [Colors.white, Colors.black, Colors.red]; diff --git a/lib/util/epd/gdey037z03bw.dart b/lib/util/epd/gdey037z03bw.dart index f1c39ca5..cb7dac14 100644 --- a/lib/util/epd/gdey037z03bw.dart +++ b/lib/util/epd/gdey037z03bw.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:magic_epaper_app/constants/asset_paths.dart'; import 'package:magic_epaper_app/util/epd/driver/uc8253.dart'; import 'package:magic_epaper_app/util/image_processing/image_processing.dart'; import 'driver/driver.dart'; @@ -16,7 +17,7 @@ class Gdey037z03BW extends Epd { @override String get modelId => 'GDEY037T03'; @override - String get imgPath => "assets/images/displays/epaper_3.7_bw.PNG"; + String get imgPath => ImageAssets.epaper37Bw; @override get colors => [Colors.white, Colors.black]; diff --git a/lib/util/image_editor_utils.dart b/lib/util/image_editor_utils.dart new file mode 100644 index 00000000..89d0ff37 --- /dev/null +++ b/lib/util/image_editor_utils.dart @@ -0,0 +1,21 @@ +import 'package:image/image.dart' as img; +import 'package:magic_epaper_app/util/epd/epd.dart'; + +List processImages({ + required img.Image originalImage, + required Epd epd, +}) { + final List processedImgs = []; + + img.Image transformed = img.copyResize( + originalImage, + width: epd.width, + height: epd.height, + ); + + for (final method in epd.processingMethods) { + processedImgs.add(method(transformed)); + } + + return processedImgs; +} diff --git a/lib/view/display_selection_screen.dart b/lib/view/display_selection_screen.dart index d8bc6885..5d203cfe 100644 --- a/lib/view/display_selection_screen.dart +++ b/lib/view/display_selection_screen.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:magic_epaper_app/constants.dart'; +import 'package:magic_epaper_app/constants/color_constants.dart'; +import 'package:magic_epaper_app/constants/string_constants.dart'; import 'package:magic_epaper_app/provider/getitlocator.dart'; import 'package:magic_epaper_app/util/epd/epd.dart'; import 'package:magic_epaper_app/util/epd/gdey037z03.dart'; @@ -35,7 +36,7 @@ class _DisplaySelectionScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Magic ePaper', + Text(StringConstants.appName, style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, diff --git a/lib/view/image_editor.dart b/lib/view/image_editor.dart index f8eb54ce..2a9d2e90 100644 --- a/lib/view/image_editor.dart +++ b/lib/view/image_editor.dart @@ -2,35 +2,57 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:magic_epaper_app/pro_image_editor/features/movable_background_image.dart'; +import 'package:magic_epaper_app/view/widget/flip_controls.dart'; +import 'package:magic_epaper_app/util/image_editor_utils.dart'; import 'package:magic_epaper_app/view/widget/image_list.dart'; import 'package:provider/provider.dart'; import 'package:image/image.dart' as img; import 'package:magic_epaper_app/provider/image_loader.dart'; import 'package:magic_epaper_app/util/epd/epd.dart'; -import 'package:magic_epaper_app/constants.dart'; +import 'package:magic_epaper_app/constants/color_constants.dart'; -class ImageEditor extends StatelessWidget { +class ImageEditor extends StatefulWidget { final Epd epd; const ImageEditor({super.key, required this.epd}); + @override + State createState() => _ImageEditorState(); +} + +class _ImageEditorState extends State { + bool flipHorizontal = false; + bool flipVertical = false; + + void toggleFlipHorizontal() { + setState(() { + flipHorizontal = !flipHorizontal; + }); + } + + void toggleFlipVertical() { + setState(() { + flipVertical = !flipVertical; + }); + } + @override Widget build(BuildContext context) { var imgLoader = context.watch(); - final List processedImgs = List.empty(growable: true); final orgImg = imgLoader.image; - if (orgImg != null) { - final image = img.copyResize(imgLoader.image!, - width: epd.width, height: epd.height); - for (final method in epd.processingMethods) { - processedImgs.add(method(image)); - } - } + final List processedImgs = orgImg != null + ? processImages( + originalImage: orgImg, + epd: widget.epd, + ) + : []; final imgList = ImageList( imgList: processedImgs, - epd: epd, + epd: widget.epd, + flipHorizontal: flipHorizontal, + flipVertical: flipVertical, ); return Scaffold( @@ -69,7 +91,8 @@ class ImageEditor extends StatelessWidget { ), ), onPressed: () { - imgLoader.pickImage(width: epd.width, height: epd.height); + imgLoader.pickImage( + width: widget.epd.width, height: widget.epd.height); }, child: const Text( "Import Image", @@ -86,14 +109,20 @@ class ImageEditor extends StatelessWidget { ); imgLoader.updateImage( bytes: canvasBytes!, - width: epd.width, - height: epd.height, + width: widget.epd.width, + height: widget.epd.height, ); }, child: const Text("Open Editor"), ), ], ), + floatingActionButton: orgImg != null + ? FlipControls( + onFlipHorizontal: toggleFlipHorizontal, + onFlipVertical: toggleFlipVertical, + ) + : null, body: SafeArea( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), diff --git a/lib/view/widget/display_card.dart b/lib/view/widget/display_card.dart index 124e756c..a8f2dce5 100644 --- a/lib/view/widget/display_card.dart +++ b/lib/view/widget/display_card.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:magic_epaper_app/constants.dart'; +import 'package:magic_epaper_app/constants/color_constants.dart'; import 'package:magic_epaper_app/util/epd/epd.dart'; import 'package:magic_epaper_app/view/widget/color_dot.dart'; diff --git a/lib/view/widget/flip_controls.dart b/lib/view/widget/flip_controls.dart new file mode 100644 index 00000000..b0acb04c --- /dev/null +++ b/lib/view/widget/flip_controls.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:magic_epaper_app/constants/asset_paths.dart'; + +class FlipControls extends StatelessWidget { + final VoidCallback onFlipHorizontal; + final VoidCallback onFlipVertical; + + const FlipControls({ + super.key, + required this.onFlipHorizontal, + required this.onFlipVertical, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(bottom: 80.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + FloatingActionButton( + backgroundColor: Colors.white, + heroTag: 'flipH', + onPressed: onFlipHorizontal, + tooltip: 'Flip Horizontally', + child: Image.asset( + ImageAssets.flipHorizontal, + height: 24, + width: 24, + ), + ), + const SizedBox(height: 10), + FloatingActionButton( + backgroundColor: Colors.white, + heroTag: 'flipV', + onPressed: onFlipVertical, + tooltip: 'Flip Vertically', + child: Transform.rotate( + angle: -1.5708, + child: Image.asset( + ImageAssets.flipHorizontal, + height: 24, + width: 24, + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/view/widget/image_list.dart b/lib/view/widget/image_list.dart index e662f220..06d5f6c0 100644 --- a/lib/view/widget/image_list.dart +++ b/lib/view/widget/image_list.dart @@ -3,14 +3,21 @@ import 'package:image/image.dart' as img; import 'package:magic_epaper_app/util/epd/epd.dart'; import 'package:magic_epaper_app/util/protocol.dart'; import 'package:magic_epaper_app/util/image_processing/image_processing.dart'; -import 'package:magic_epaper_app/constants.dart'; +import 'package:magic_epaper_app/constants/color_constants.dart'; class ImageList extends StatefulWidget { final List imgList; final Epd epd; + final bool flipHorizontal; + final bool flipVertical; @override - const ImageList({super.key, required this.imgList, required this.epd}); + const ImageList( + {super.key, + required this.imgList, + required this.epd, + required this.flipHorizontal, + required this.flipVertical}); @override State createState() => _ImageList(); @@ -60,10 +67,16 @@ class _ImageList extends State { for (var i = 0; i < widget.imgList.length; i++) { var rotatedImg = img.copyRotate(widget.imgList[i], angle: 90); - var uiImage = Image.memory( - img.encodePng(rotatedImg), - height: 100, - isAntiAlias: false, + var uiImage = Transform( + alignment: Alignment.center, + transform: Matrix4.identity() + ..scale(widget.flipHorizontal ? -1.0 : 1.0, + widget.flipVertical ? -1.0 : 1.0), + child: Image.memory( + img.encodePng(rotatedImg), + height: 100, + isAntiAlias: false, + ), ); imgWidgets.add(uiImage); @@ -137,10 +150,16 @@ class _ImageList extends State { borderRadius: BorderRadius.circular(8), ), child: imgSelection < imgWidgets.length - ? Image.memory( - img.encodePng( - img.copyRotate(widget.imgList[imgSelection], angle: 90)), - fit: BoxFit.contain, + ? Transform( + alignment: Alignment.center, + transform: Matrix4.identity() + ..scale(widget.flipHorizontal ? -1.0 : 1.0, + widget.flipVertical ? -1.0 : 1.0), + child: Image.memory( + img.encodePng(img.copyRotate(widget.imgList[imgSelection], + angle: 90)), + fit: BoxFit.contain, + ), ) : const Center(child: Text("No filter selected")), ), @@ -190,8 +209,14 @@ class _ImageList extends State { ), ), onPressed: () { - Protocol(epd: widget.epd) - .writeImages(widget.imgList[imgSelection]); + img.Image finalImg = widget.imgList[imgSelection]; + if (widget.flipHorizontal) { + finalImg = img.flipHorizontal(finalImg); + } + if (widget.flipVertical) { + finalImg = img.flipVertical(finalImg); + } + Protocol(epd: widget.epd).writeImages(finalImg); }, child: const Text( 'Start Transfer',