Created
March 7, 2023 11:57
-
-
Save LokieVikky/6d271de9fd7375eda5282a468f57cbf9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:io'; | |
import 'dart:math'; | |
import 'package:flutter/material.dart'; | |
import 'dart:ui' as ui; | |
enum Sequence { rotate, crop, delete, straighten,drawImage,drawFrame } | |
enum Units { cm, inch } | |
enum ImageRotation { | |
rotate90, | |
rotate180, | |
rotate270, | |
} | |
class DeleteArea { | |
Rect rect; | |
Paint paint; | |
DeleteArea(this.rect, this.paint); | |
} | |
class FrameInsets { | |
double left = 0.0; | |
double top = 0.0; | |
double right = 0.0; | |
double bottom = 0.0; | |
FrameInsets.all(double value) | |
: left = value, | |
top = value, | |
right = value, | |
bottom = value; | |
FrameInsets.only({ | |
this.left = 0.0, | |
this.top = 0.0, | |
this.right = 0.0, | |
this.bottom = 0.0, | |
}); | |
} | |
class ImageFrame { | |
Paint paint; | |
FrameInsets frame; | |
Units unit; | |
ImageFrame(this.paint, this.frame, this.unit); | |
} | |
class ImageEditor { | |
double imageHeight = 0.0; | |
double imageWidth = 0.0; | |
Future<ui.Image> edit({ | |
required String srcPath, | |
List<DeleteArea> areasToDelete = const [], | |
ImageFrame? imageFrame, | |
Rect? areaToCrop, | |
ColorFilter? colorFilter, | |
ImageRotation? imageRotation, | |
double? straightenAngle, | |
List<Sequence> sequence = const [Sequence.rotate, Sequence.crop, Sequence.delete], | |
}) async { | |
try { | |
ui.Image image; | |
File file = File(srcPath); | |
ui.Codec codec = await ui.instantiateImageCodec(await file.readAsBytes()); | |
ui.FrameInfo frame = await codec.getNextFrame(); | |
image = frame.image; | |
imageHeight = image.height.toDouble(); | |
imageWidth = image.width.toDouble(); | |
// Start Recording | |
ui.PictureRecorder pr = ui.PictureRecorder(); | |
Canvas c = Canvas(pr); | |
for (Sequence s in sequence) { | |
switch (s) { | |
case Sequence.rotate: | |
rotate(c, imageRotation); | |
break; | |
case Sequence.crop: | |
crop(c, image, colorFilter, areaToCrop); | |
break; | |
case Sequence.delete: | |
deleteSelection(c, areasToDelete); | |
break; | |
case Sequence.straighten: | |
straighten(c, straightenAngle); | |
break; | |
case Sequence.drawImage: | |
c.drawImage(image, Offset.zero, Paint()..colorFilter = colorFilter); | |
break; | |
case Sequence.drawFrame: | |
drawFrame(c, imageFrame); | |
break; | |
} | |
} | |
if (imageRotation != null) { | |
if (imageRotation == ImageRotation.rotate90 || imageRotation == ImageRotation.rotate270) { | |
return pr.endRecording().toImage(imageHeight.toInt(), imageWidth.toInt()); | |
} | |
return pr.endRecording().toImage(imageWidth.toInt(), imageHeight.toInt()); | |
} | |
return pr.endRecording().toImage(imageWidth.toInt(), imageHeight.toInt()); | |
} catch (e) { | |
rethrow; | |
} | |
} | |
void straighten(Canvas c, double? angle) { | |
if (angle == null) { | |
return; | |
} | |
if (angle < -45 || angle > 45) { | |
throw Exception("Straighten angle can only range from -45 to +45"); | |
} | |
var radians = angle * pi / 180; | |
final double r = sqrt(imageWidth * imageWidth + imageHeight * imageHeight) / 2; | |
double alpha; | |
double beta; | |
double shiftY; | |
double shiftX; | |
double translateX; | |
double translateY; | |
alpha = atan(imageHeight / imageWidth); | |
beta = alpha + radians; | |
shiftY = r * sin(beta); | |
shiftX = r * cos(beta); | |
translateX = imageWidth / 2 - shiftX; | |
translateY = imageHeight / 2 - shiftY; | |
// if (angle == 90 || angle == 270) { | |
// translateX = imageHeight / 2 - shiftX; | |
// translateY = imageWidth / 2 - shiftY; | |
// } else { | |
// translateX = imageWidth / 2 - shiftX; | |
// translateY = imageHeight / 2 - shiftY; | |
// } | |
c.translate(translateX, translateY); | |
c.rotate(radians); | |
} | |
void deleteSelection(Canvas c, List<DeleteArea> areasToDelete) { | |
for (DeleteArea element in areasToDelete) { | |
c.drawRect( | |
element.rect, | |
element.paint, | |
); | |
} | |
} | |
void drawFrame(Canvas c, ImageFrame? imageFrame) { | |
if (imageFrame != null) { | |
double leftPixels = _unitToPixel(imageFrame.unit, imageFrame.frame.left); | |
double topPixels = _unitToPixel(imageFrame.unit, imageFrame.frame.top); | |
double rightPixels = _unitToPixel(imageFrame.unit, imageFrame.frame.right); | |
double bottomPixels = _unitToPixel(imageFrame.unit, imageFrame.frame.bottom); | |
Rect leftRect = Rect.fromLTWH(0.0, 0.0, leftPixels, imageHeight); | |
Rect topRect = Rect.fromLTWH(0.0, 0.0, imageWidth, topPixels); | |
Rect rightRect = Rect.fromLTWH(imageWidth - rightPixels, 0.0, rightPixels, imageHeight); | |
Rect bottomRect = Rect.fromLTWH(0.0, imageHeight - bottomPixels, imageWidth, bottomPixels); | |
c.drawRect(leftRect, imageFrame.paint); | |
c.drawRect(topRect, imageFrame.paint); | |
c.drawRect(rightRect, imageFrame.paint); | |
c.drawRect(bottomRect, imageFrame.paint); | |
} | |
} | |
void crop(Canvas c, ui.Image image, ColorFilter? colorFilter, Rect? areaToCrop) { | |
if (areaToCrop != null) { | |
imageHeight = areaToCrop.height; | |
imageWidth = areaToCrop.width; | |
c.drawImageRect( | |
image, | |
areaToCrop, | |
Rect.fromPoints(Offset.zero, Offset(areaToCrop.width, areaToCrop.height)), | |
Paint()..colorFilter = colorFilter); | |
} | |
} | |
void rotate(Canvas c, ImageRotation? imageRotation) { | |
// Rotate | |
if (imageRotation != null) { | |
int angle; | |
switch (imageRotation) { | |
case ImageRotation.rotate90: | |
angle = 90; | |
break; | |
case ImageRotation.rotate180: | |
angle = 180; | |
break; | |
case ImageRotation.rotate270: | |
angle = 270; | |
break; | |
} | |
var radians = angle * pi / 180; | |
final double r = sqrt(imageWidth * imageWidth + imageHeight * imageHeight) / 2; | |
double alpha; | |
double beta; | |
double shiftY; | |
double shiftX; | |
double translateX; | |
double translateY; | |
alpha = atan(imageHeight / imageWidth); | |
beta = alpha + radians; | |
shiftY = r * sin(beta); | |
shiftX = r * cos(beta); | |
if (angle == 90 || angle == 270) { | |
translateX = imageHeight / 2 - shiftX; | |
translateY = imageWidth / 2 - shiftY; | |
} else { | |
translateX = imageWidth / 2 - shiftX; | |
translateY = imageHeight / 2 - shiftY; | |
} | |
c.translate(translateX, translateY); | |
c.rotate(radians); | |
} | |
} | |
double _unitToPixel(Units unit, double value) { | |
switch (unit) { | |
case Units.cm: | |
return cmToPixel(cm: value); | |
case Units.inch: | |
return inchToPixel(inch: value); | |
} | |
} | |
double cmToPixel({required double cm, int dpi = 96}) => (dpi / 2.54) * cm; | |
double inchToPixel({required double inch, int dpi = 96}) => inch * dpi; | |
Offset findActualOffset(Offset offset, Size widgetSize, Size imageSize) { | |
return Offset(((offset.dx / widgetSize.width) * 100) * imageSize.width, | |
((offset.dy / widgetSize.height) * 100) * imageSize.height); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment