Skip to content

Commit abcc472

Browse files
author
Yan Kochkin
committed
added new param to crop_widget
1 parent b325697 commit abcc472

File tree

4 files changed

+245
-14
lines changed

4 files changed

+245
-14
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
part of 'package:custom_image_crop/src/widgets/custom_image_crop_widget.dart';
2+
3+
/// Returns params to use for displaing crop screen.
4+
CropFitParams calculateCropParams({
5+
required double screenWidth,
6+
required double screenHeight,
7+
required double cropPercentage,
8+
required int imageWidth,
9+
required int imageHeight,
10+
required CustomImageFit imageFit,
11+
}) {
12+
/// 'cropSize' is the size of the full crop area
13+
/// 'cropSizeToPaint' is the size of the crop area highlighted in ui
14+
/// 'defaultScale' is used to adjust image scale
15+
16+
switch (imageFit) {
17+
case CustomImageFit.fitCropSpace:
18+
final cropSize = min(screenWidth, screenHeight);
19+
final cropSizeToPaint = cropSize;
20+
final defaultScale = (cropSize * cropPercentage) / max(imageWidth, imageHeight);
21+
22+
return CropFitParams(cropSize, cropSizeToPaint, defaultScale);
23+
24+
case CustomImageFit.fillCropWidth:
25+
final cropSize = screenWidth;
26+
final cropSizeToPaint = cropSize;
27+
final defaultScale = (cropSize * cropPercentage) / imageWidth;
28+
29+
return CropFitParams(cropSize, cropSizeToPaint, defaultScale);
30+
case CustomImageFit.fillCropHeight:
31+
final cropSize = screenHeight;
32+
final cropSizeToPaint = cropSize;
33+
final defaultScale = (cropSize * cropPercentage) / imageHeight;
34+
35+
return CropFitParams(cropSize, cropSizeToPaint, defaultScale);
36+
case CustomImageFit.fitVisibleSpace:
37+
late final double cropSize;
38+
late final double cropSizeToPaint;
39+
late final double defaultScale;
40+
41+
if (screenHeight < screenWidth) {
42+
cropSize = screenHeight;
43+
cropSizeToPaint = screenWidth * cropPercentage;
44+
defaultScale = screenHeight / imageHeight;
45+
} else {
46+
cropSize = screenWidth;
47+
cropSizeToPaint = cropSize * cropPercentage;
48+
defaultScale = screenWidth / imageWidth;
49+
}
50+
51+
return CropFitParams(cropSize, cropSizeToPaint, defaultScale);
52+
case CustomImageFit.fillVisibleSpace:
53+
late final double cropSize;
54+
late final double cropSizeToPaint;
55+
late final double defaultScale;
56+
57+
if (screenHeight > screenWidth) {
58+
cropSize = screenHeight;
59+
cropSizeToPaint = screenWidth * cropPercentage;
60+
defaultScale = screenHeight / imageHeight;
61+
} else {
62+
cropSize = screenWidth;
63+
cropSizeToPaint = cropSize * cropPercentage;
64+
defaultScale = screenWidth / imageWidth;
65+
}
66+
67+
return CropFitParams(cropSize, cropSizeToPaint, defaultScale);
68+
case CustomImageFit.fillVisibleHeight:
69+
final cropSize = screenHeight;
70+
final cropSizeToPaint = screenWidth * cropPercentage;
71+
final defaultScale = screenHeight / imageHeight;
72+
73+
return CropFitParams(cropSize, cropSizeToPaint, defaultScale);
74+
case CustomImageFit.fillVisiblelWidth:
75+
final cropSize = screenWidth;
76+
final cropSizeToPaint = cropSize * cropPercentage;
77+
final defaultScale = screenWidth / imageWidth;
78+
79+
return CropFitParams(cropSize, cropSizeToPaint, defaultScale);
80+
}
81+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
part of 'package:custom_image_crop/src/widgets/custom_image_crop_widget.dart';
2+
3+
/// Returns params to use for cropping image.
4+
OnCropParams caclulateOnCropParams({
5+
required double screenWidth,
6+
required double screenHeight,
7+
required double cropPercentage,
8+
required double dataScale,
9+
required int imageWidth,
10+
required int imageHeight,
11+
required CustomImageFit imageFit,
12+
}) {
13+
/// 'uiSize' is the size of the ui screen
14+
/// 'cropSize' is the size of the area to crop
15+
/// 'translateScale' is the scale used to adjust x and y coodinates of the crop area
16+
/// 'scale' is used to adjust image scale
17+
18+
switch (imageFit) {
19+
case CustomImageFit.fitCropSpace:
20+
final uiSize = min(screenWidth, screenHeight);
21+
final cropSize = max(imageWidth, imageHeight).toDouble();
22+
final translateScale = cropSize / (uiSize * cropPercentage);
23+
final scale = dataScale;
24+
25+
return OnCropParams(cropSize, translateScale, scale);
26+
27+
case CustomImageFit.fillCropWidth:
28+
final uiSize = screenWidth;
29+
final cropSize = imageWidth.toDouble();
30+
final translateScale = cropSize / (uiSize * cropPercentage);
31+
final scale = dataScale;
32+
33+
return OnCropParams(cropSize, translateScale, scale);
34+
case CustomImageFit.fillCropHeight:
35+
final uiSize = screenHeight;
36+
final cropSize = imageHeight.toDouble();
37+
final translateScale = cropSize / (uiSize * cropPercentage);
38+
final scale = dataScale;
39+
40+
return OnCropParams(cropSize, translateScale, scale);
41+
case CustomImageFit.fitVisibleSpace:
42+
late final double uiSize;
43+
late final double cropSize;
44+
late final double translateScale;
45+
late final double scale;
46+
47+
if (screenHeight < screenWidth) {
48+
uiSize = screenHeight;
49+
cropSize = imageHeight.toDouble();
50+
translateScale = cropSize / uiSize / cropPercentage * (screenHeight / screenWidth);
51+
scale = dataScale / cropPercentage * (screenHeight / screenWidth);
52+
} else {
53+
uiSize = screenWidth;
54+
cropSize = imageWidth.toDouble();
55+
translateScale = cropSize / uiSize / cropPercentage;
56+
scale = dataScale / cropPercentage;
57+
}
58+
59+
return OnCropParams(cropSize, translateScale, scale);
60+
case CustomImageFit.fillVisibleSpace:
61+
late final double uiSize;
62+
late final double cropSize;
63+
late final double translateScale;
64+
late final double scale;
65+
66+
if (screenHeight > screenWidth) {
67+
uiSize = screenHeight;
68+
cropSize = imageHeight.toDouble();
69+
translateScale = cropSize / uiSize / cropPercentage * (screenHeight / screenWidth);
70+
scale = dataScale / cropPercentage * (screenHeight / screenWidth);
71+
} else {
72+
uiSize = screenWidth;
73+
cropSize = imageWidth.toDouble();
74+
translateScale = cropSize / uiSize / cropPercentage;
75+
scale = dataScale / cropPercentage;
76+
}
77+
78+
return OnCropParams(cropSize, translateScale, scale);
79+
case CustomImageFit.fillVisibleHeight:
80+
final uiSize = screenHeight;
81+
final cropSize = imageHeight.toDouble();
82+
final translateScale = cropSize / uiSize / cropPercentage * (screenHeight / screenWidth);
83+
final scale = dataScale / cropPercentage * (screenHeight / screenWidth);
84+
85+
return OnCropParams(cropSize, translateScale, scale);
86+
case CustomImageFit.fillVisiblelWidth:
87+
final uiSize = screenWidth;
88+
final cropSize = imageWidth.toDouble();
89+
final translateScale = cropSize / uiSize / cropPercentage;
90+
final scale = dataScale / cropPercentage;
91+
92+
return OnCropParams(cropSize, translateScale, scale);
93+
}
94+
}

lib/src/models/params_model.dart

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/// Params used to display crop screen.
2+
class CropFitParams {
3+
/// The size of actual crop area.
4+
final double cropSize;
5+
6+
/// The size of crop area to show on screen.
7+
final double cropSizeToPaint;
8+
9+
/// The scale used to adjust display of the image based on 'CustomImageFit' type.
10+
final double additionalScale;
11+
12+
CropFitParams(this.cropSize, this.cropSizeToPaint, this.additionalScale);
13+
}
14+
15+
/// Params used to crop image.
16+
class OnCropParams {
17+
/// The size of actual crop area.
18+
final double cropSize;
19+
20+
/// The translate scale used to crop the image based on 'CustomImageFit' type.
21+
final double translateScale;
22+
23+
/// Is used to crop the image based on 'CustomImageFit' type.
24+
final double scale;
25+
26+
OnCropParams(this.cropSize, this.translateScale, this.scale);
27+
}

lib/src/widgets/custom_image_crop_widget.dart

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'dart:async';
22
import 'dart:math';
33
import 'dart:ui' as ui;
44

5+
import 'package:custom_image_crop/src/models/params_model.dart';
56
import 'package:flutter/material.dart';
67
import 'package:gesture_x_detector/gesture_x_detector.dart';
78
import 'package:vector_math/vector_math_64.dart' as vector_math;
@@ -10,6 +11,8 @@ import 'package:custom_image_crop/src/controllers/controller.dart';
1011
import 'package:custom_image_crop/src/painters/dotted_path_painter.dart';
1112
import 'package:custom_image_crop/src/clippers/inverted_clipper.dart';
1213
import 'package:custom_image_crop/src/models/model.dart';
14+
part 'package:custom_image_crop/src/calculators/calculate_crop_params.dart';
15+
part 'package:custom_image_crop/src/calculators/calculate_on_crop_params.dart';
1316

1417
/// An image cropper that is customizable.
1518
/// You can rotate, scale and translate either
@@ -31,6 +34,9 @@ class CustomImageCrop extends StatefulWidget {
3134
/// The shape of the cropping area
3235
final CustomCropShape shape;
3336

37+
/// How to fit image inside visible space
38+
final CustomImageFit imageFit;
39+
3440
/// The percentage of the available area that is
3541
/// reserved for the cropping area
3642
final double cropPercentage;
@@ -83,6 +89,7 @@ class CustomImageCrop extends StatefulWidget {
8389
this.overlayColor = const Color.fromRGBO(0, 0, 0, 0.5),
8490
this.backgroundColor = Colors.white,
8591
this.shape = CustomCropShape.Circle,
92+
this.imageFit = CustomImageFit.fitCropSpace,
8693
this.cropPercentage = 0.8,
8794
this.drawPath = DottedCropPathPainter.drawPath,
8895
this.canRotate = true,
@@ -159,10 +166,16 @@ class _CustomImageCropState extends State<CustomImageCrop>
159166
builder: (context, constraints) {
160167
_width = constraints.maxWidth;
161168
_height = constraints.maxHeight;
162-
final cropWidth = min(_width, _height) * widget.cropPercentage;
163-
final defaultScale = cropWidth / max(image.width, image.height);
164-
final scale = data.scale * defaultScale;
165-
_path = _getPath(cropWidth, _width, _height);
169+
final cropParams = calculateCropParams(
170+
cropPercentage: widget.cropPercentage,
171+
imageFit: widget.imageFit,
172+
imageHeight: image.height,
173+
imageWidth: image.width,
174+
screenHeight: _height,
175+
screenWidth: _width,
176+
);
177+
final scale = data.scale * cropParams.additionalScale;
178+
_path = _getPath(cropParams.cropSizeToPaint, _width, _height);
166179
return XGestureDetector(
167180
onMoveStart: onMoveStart,
168181
onMoveUpdate: onMoveUpdate,
@@ -270,20 +283,26 @@ class _CustomImageCropState extends State<CustomImageCrop>
270283
final imageHeight = _imageAsUIImage!.height;
271284
final pictureRecorder = ui.PictureRecorder();
272285
final canvas = Canvas(pictureRecorder);
273-
final uiWidth = min(_width, _height) * widget.cropPercentage;
274-
final cropWidth = max(imageWidth, imageHeight).toDouble();
275-
final translateScale = cropWidth / uiWidth;
276-
final scale = data.scale;
277-
final clipPath = Path.from(_getPath(cropWidth, cropWidth, cropWidth));
286+
final onCropParams = caclulateOnCropParams(
287+
cropPercentage: widget.cropPercentage,
288+
imageFit: widget.imageFit,
289+
imageHeight: imageHeight,
290+
imageWidth: imageWidth,
291+
screenHeight: _height,
292+
screenWidth: _width,
293+
dataScale: data.scale,
294+
);
295+
final clipPath =
296+
Path.from(_getPath(onCropParams.cropSize, onCropParams.cropSize, onCropParams.cropSize));
278297
final matrix4Image = Matrix4.diagonal3(vector_math.Vector3.all(1))
279-
..translate(translateScale * data.x + cropWidth / 2,
280-
translateScale * data.y + cropWidth / 2)
281-
..scale(scale)
298+
..translate(onCropParams.translateScale * data.x + onCropParams.cropSize / 2,
299+
onCropParams.translateScale * data.y + onCropParams.cropSize / 2)
300+
..scale(onCropParams.scale)
282301
..rotateZ(data.angle);
283302
final bgPaint = Paint()
284303
..color = widget.backgroundColor
285304
..style = PaintingStyle.fill;
286-
canvas.drawRect(Rect.fromLTWH(0, 0, cropWidth, cropWidth), bgPaint);
305+
canvas.drawRect(Rect.fromLTWH(0, 0, onCropParams.cropSize, onCropParams.cropSize), bgPaint);
287306
canvas.save();
288307
canvas.clipPath(clipPath);
289308
canvas.transform(matrix4Image.storage);
@@ -298,7 +317,7 @@ class _CustomImageCropState extends State<CustomImageCrop>
298317

299318
ui.Picture picture = pictureRecorder.endRecording();
300319
ui.Image image =
301-
await picture.toImage(cropWidth.floor(), cropWidth.floor());
320+
await picture.toImage(onCropParams.cropSize.floor(), onCropParams.cropSize.floor());
302321

303322
// Adding compute would be preferrable. Unfortunately we cannot pass an ui image to this.
304323
// A workaround would be to save the image and load it inside of the isolate
@@ -333,3 +352,13 @@ enum CustomCropShape {
333352
Circle,
334353
Square,
335354
}
355+
356+
enum CustomImageFit {
357+
fitCropSpace,
358+
fillCropWidth,
359+
fillCropHeight,
360+
fitVisibleSpace,
361+
fillVisibleSpace,
362+
fillVisibleHeight,
363+
fillVisiblelWidth,
364+
}

0 commit comments

Comments
 (0)