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
Binary file added assets/images/h-flip.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions lib/constants/asset_paths.dart
Original file line number Diff line number Diff line change
@@ -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';
}
File renamed without changes.
3 changes: 3 additions & 0 deletions lib/constants/string_constants.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class StringConstants {
static const String appName = 'Magic epaper';
}
11 changes: 6 additions & 5 deletions lib/pro_image_editor/features/movable_background_image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -335,7 +336,7 @@ class _MovableBackgroundImageExampleState
secondary: Color(0xFFE2E2E2),
),
child: ProImageEditor.asset(
'assets/canvas/white.png',
ImageAssets.whiteBoard,
key: editorKey,
callbacks: ProImageEditorCallbacks(
onImageEditingStarted: onImageEditingStarted,
Expand All @@ -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:
Expand Down
5 changes: 3 additions & 2 deletions lib/pro_image_editor/features/reorder_layer_example.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -40,15 +41,15 @@ class _ReorderLayerExampleState extends State<ReorderLayerExample>
@override
void initState() {
super.initState();
preCacheImage(assetPath: 'assets/canvas/white.png');
preCacheImage(assetPath: ImageAssets.whiteBoard);
}

@override
Widget build(BuildContext context) {
if (!isPreCached) return const PrepareImageWidget();

return ProImageEditor.asset(
'assets/canvas/white.png',
ImageAssets.whiteBoard,
key: editorKey,
callbacks: ProImageEditorCallbacks(
onImageEditingStarted: onImageEditingStarted,
Expand Down
3 changes: 2 additions & 1 deletion lib/util/epd/gdey037z03.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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];
Expand Down
3 changes: 2 additions & 1 deletion lib/util/epd/gdey037z03bw.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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];
Expand Down
21 changes: 21 additions & 0 deletions lib/util/image_editor_utils.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import 'package:image/image.dart' as img;
import 'package:magic_epaper_app/util/epd/epd.dart';

List<img.Image> processImages({
required img.Image originalImage,
required Epd epd,
}) {
final List<img.Image> processedImgs = [];

img.Image transformed = img.copyResize(
originalImage,
width: epd.width,
height: epd.height,
);

for (final method in epd.processingMethods) {
processedImgs.add(method(transformed));
}
Comment on lines +16 to +18
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Clone transformed image per method to avoid side effects

Passing the same transformed image to all methods may lead to unintended side effects if any method mutates its input. Pass a copy of transformed to each method instead.

Suggested change
for (final method in epd.processingMethods) {
processedImgs.add(method(transformed));
}
for (final method in epd.processingMethods) {
processedImgs.add(method(img.copyResize(transformed)));
}


return processedImgs;
}
5 changes: 3 additions & 2 deletions lib/view/display_selection_screen.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -35,7 +36,7 @@ class _DisplaySelectionScreenState extends State<DisplaySelectionScreen> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Magic ePaper',
Text(StringConstants.appName,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
Expand Down
57 changes: 43 additions & 14 deletions lib/view/image_editor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<ImageEditor> createState() => _ImageEditorState();
}

class _ImageEditorState extends State<ImageEditor> {
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<ImageLoader>();
final List<img.Image> 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<img.Image> processedImgs = orgImg != null
? processImages(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (performance): Reprocessing on every build might hurt performance

Consider memoizing processImages results when its inputs (orgImg, flipHorizontal, flipVertical, epd) are unchanged to prevent unnecessary repeated processing.

originalImage: orgImg,
epd: widget.epd,
)
: [];

final imgList = ImageList(
imgList: processedImgs,
epd: epd,
epd: widget.epd,
flipHorizontal: flipHorizontal,
flipVertical: flipVertical,
);

return Scaffold(
Expand Down Expand Up @@ -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",
Expand All @@ -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),
Expand Down
2 changes: 1 addition & 1 deletion lib/view/widget/display_card.dart
Original file line number Diff line number Diff line change
@@ -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';

Expand Down
51 changes: 51 additions & 0 deletions lib/view/widget/flip_controls.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
import 'package:magic_epaper_app/constants/asset_paths.dart';

class FlipControls extends StatelessWidget {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Add visual state feedback for flip controls

Pass the flip state to FlipControls and update the button style or icon color to reflect when a flip is active.

Suggested implementation:

class FlipControls extends StatelessWidget {
  final VoidCallback onFlipHorizontal;
  final VoidCallback onFlipVertical;
  final bool isFlippedHorizontally;
  final bool isFlippedVertically;

  const FlipControls({
    super.key,
    required this.onFlipHorizontal,
    required this.onFlipVertical,
    required this.isFlippedHorizontally,
    required this.isFlippedVertically,
  });

  @override
  Widget build(BuildContext context) {
    final Color activeColor = Theme.of(context).colorScheme.primary;
    final Color inactiveColor = Theme.of(context).iconTheme.color ?? Colors.black;

    return Padding(

You will also need to update the widget tree inside build to use the isFlippedHorizontally and isFlippedVertically booleans. For example, if you use IconButton for the flip controls, set the color property of the icon to activeColor when the corresponding flip is active, otherwise use inactiveColor.

Additionally, update all usages of FlipControls to pass the new required parameters: isFlippedHorizontally and isFlippedVertically.

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,
),
),
),
],
),
);
}
}
49 changes: 37 additions & 12 deletions lib/view/widget/image_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<img.Image> 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<StatefulWidget> createState() => _ImageList();
Expand Down Expand Up @@ -60,10 +67,16 @@ class _ImageList extends State<ImageList> {

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);

Expand Down Expand Up @@ -137,10 +150,16 @@ class _ImageList extends State<ImageList> {
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")),
),
Expand Down Expand Up @@ -190,8 +209,14 @@ class _ImageList extends State<ImageList> {
),
),
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',
Expand Down