Skip to content

Commit

Permalink
Merge pull request #94 from fluttercandies/scale
Browse files Browse the repository at this point in the history
Feat: modify the attributes of scale
  • Loading branch information
CaiJingLong committed Jul 19, 2022
2 parents 6326cda + 8fc9c4a commit 62b7025
Show file tree
Hide file tree
Showing 13 changed files with 169 additions and 37 deletions.
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,20 @@ p, q, r, s, t
#### Scale

```dart
ScaleOption(width, height, keepRatio: keepRatio);
ScaleOption(width, height, keepRatio: keepRatio, keepWidthFirst: keepWidthFirst);
```

After specifying the width and height, it is not clipped, but stretched to the specified width and height (Does maintain the aspect ratio of the image if wanted).
`keepRatio` and `keepWidthFirst` are optional parameters used to maintain the aspect ratio of the original image to prevent image deformation.

`keepWidthFirst` takes effect only when `keepRatio` is true.

The following is a brief description:

| width | height | keepRatio | keepWidthFirst | src size | dest size |
| ----- | ------ | --------- | -------------- | --------- | --------- |
| 500 | 300 | false | true/false | 1920x1920 | 500x300 |
| 500 | 300 | true | true | 1920x1920 | 500x500 |
| 500 | 300 | true | false | 1920x1920 | 300x300 |

#### AddText

Expand Down Expand Up @@ -230,8 +240,8 @@ Support next `BlendMode`, other will be ignored. You can also see [the document
| iOS/macOS | android(PorterDuff.Mode) | flutter(BlendMode) |
| --------------------------- | ------------------------ | ------------------ |
| kCGBlendModeClear | CLEAR | clear |
|   | SRC | src |
|   | DST | dst |
| | SRC | src |
| | DST | dst |
| kCGBlendModeNormal | SRC_OVER | srcOver |
| kCGBlendModeDestinationOver | DST_OVER | dstOver |
| kCGBlendModeSourceIn | SRC_IN | srcIn |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,14 @@ class ImageHandler(private var bitmap: Bitmap) {
}

private fun handleScale(option: ScaleOption): Bitmap {
val bitmapRatio = bitmap.width.toFloat() / bitmap.height.toFloat()
val optionRatio = option.width.toFloat() / option.height.toFloat()
var w = option.width
var h = option.height
if (option.keepRatio) {
if (bitmapRatio < optionRatio) {
w = (option.height * bitmapRatio).toInt()
} else if (bitmapRatio > optionRatio) {
h = (option.width / bitmapRatio).toInt()
val srcRatio = bitmap.width.toFloat() / bitmap.height.toFloat()
if (option.keepWidthFirst) {
h = (w / srcRatio).toInt()
} else {
w = (srcRatio * h).toInt()
}
}
val newBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package com.fluttercandies.image_editor.option

data class ScaleOption(val width: Int, val height: Int, val keepRatio: Boolean) : Option
data class ScaleOption(val width: Int, val height: Int, val keepRatio: Boolean, val keepWidthFirst: Boolean) : Option
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ object ConvertUtils {
val w = optionMap["width"] as Int
val h = optionMap["height"] as Int
val keepRatio = optionMap["keepRatio"] as Boolean
return ScaleOption(w, h, keepRatio)
val keepWidthFirst = optionMap["keepWidthFirst"] as Boolean
return ScaleOption(w, h, keepRatio, keepWidthFirst)
}

private fun getColorOption(optionMap: Any?): ColorOption {
Expand Down
7 changes: 7 additions & 0 deletions example/lib/home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import 'package:image_editor/image_editor.dart'
Option,
OutputFormat,
RotateOption;
import 'package:image_size_getter/image_size_getter.dart';

import 'const/resource.dart';
import 'widget/clip_widget.dart';
Expand Down Expand Up @@ -137,6 +138,8 @@ class _SimpleExamplePageState extends State<SimpleExamplePage> {

final Uint8List assetImage = await getAssetImage();

final srcSize = ImageSizeGetter.getSize(MemoryInput(assetImage));

print(const JsonEncoder.withIndent(' ').convert(option.toJson()));
final Uint8List? result = await ImageEditor.editImage(
image: assetImage,
Expand All @@ -148,6 +151,10 @@ class _SimpleExamplePageState extends State<SimpleExamplePage> {
return;
}

final resultSize = ImageSizeGetter.getSize(MemoryInput(result));

print('srcSize: $srcSize, resultSize: $resultSize');

final MemoryImage img = MemoryImage(result);
setProvider(img);
}
Expand Down
29 changes: 25 additions & 4 deletions example/lib/widget/scale_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,38 +17,59 @@ class _ScaleWidgetState extends State<ScaleWidget> {
int width = 1920;
int height = 1920;

bool keepRatio = false;
bool keepWidthFirst = true;

@override
Widget build(BuildContext context) {
return ExpandContainer(
title: 'width: $width, height: $height',
title: 'scale',
child: Column(
children: <Widget>[
Slider(
onChanged: (double v) => setState(() => width = v.toInt()),
value: width.toDouble(),
min: 1,
max: 1920,
label: 'width',
),
Slider(
onChanged: (double v) => setState(() => height = v.toInt()),
value: height.toDouble(),
min: 1,
max: 1920,
),
CheckboxListTile(
title: Text('keep ratio'),
value: keepRatio,
onChanged: (bool? v) => setState(() => keepRatio = v == true),
),
CheckboxListTile(
title: Text(
keepWidthFirst ? 'keep width first' : 'keep height first',
),
value: keepWidthFirst,
onChanged: (bool? v) => setState(() => keepWidthFirst = v == true),
),
SizedBox(
width: double.infinity,
child: TextButton(
child: const Text('scale'),
onPressed: _rotate,
onPressed: _scale,
),
),
],
),
);
}

void _rotate() {
final ScaleOption opt = ScaleOption(width, height);
void _scale() {
final ScaleOption opt = ScaleOption(
width,
height,
keepRatio: keepRatio,
keepWidthFirst: keepWidthFirst,
);
widget.onTap?.call(opt);
}
}
104 changes: 84 additions & 20 deletions ios/Classes/EditorUIImageHandler.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,68 @@
#import "EditorUIImageHandler.h"
#import <CoreImage/CIFilterBuiltins.h>


void releaseCGContext(CGContextRef ref) {
char *bitmapData = CGBitmapContextGetData(ref);
if (bitmapData) free(bitmapData);
CGContextRelease(ref);
}

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnonnull"
CGContextRef createCGContext(size_t pixelsWide, size_t pixelsHigh) {
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
void *bitmapData;
size_t bitmapByteCount;
size_t bitmapBytesPerRow;

bitmapBytesPerRow = (pixelsWide * 4);// 1
bitmapByteCount = (bitmapBytesPerRow * pixelsHigh);

colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);// 2
bitmapData = calloc(bitmapByteCount, sizeof(uint8_t));// 3
if (bitmapData == NULL) {
fprintf(stderr, "Memory not allocated!");
return NULL;
}
context = CGBitmapContextCreate(bitmapData,// 4
pixelsWide,
pixelsHigh,
8, // bits per component
bitmapBytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedLast);
if (context == NULL) {
free(bitmapData);// 5
fprintf(stderr, "Context not created!");
return NULL;
}
CGColorSpaceRelease(colorSpace);// 6

return context;// 7
}

#pragma clang diagnostic pop

CGSize CGImageGetSize(CGImageRef ref) {
size_t w = CGImageGetWidth(ref);
size_t h = CGImageGetHeight(ref);
return CGSizeMake(w, h);
}

UIImage *getImageFromCGContext(CGContextRef context) {
size_t h = CGBitmapContextGetHeight(context);
size_t w = CGBitmapContextGetWidth(context);

CGImageRef pImage = CGBitmapContextCreateImage(context);

UIImage *image = [[UIImage alloc] initWithCGImage:pImage];
CGImageRelease(pImage);
return image;
}


@implementation EditorUIImageHandler {
UIImage *outImage;
}
Expand Down Expand Up @@ -211,29 +273,31 @@ - (void)scale:(FIScaleOption *)option {
if (!outImage) {
return;
}
float imageRatio = outImage.size.width / outImage.size.height;
float optionRatio = option.width / option.height;
int w = option.width;
int h = option.height;

CGImageRef srcImage = outImage.CGImage;

double width = option.width;
double height = option.height;

if (option.keepRatio) {
if (imageRatio < optionRatio) {
w = option.height * imageRatio;
} else if (imageRatio > optionRatio) {
h = option.width / imageRatio;
CGSize srcSize = CGImageGetSize(srcImage);

double srcRatio = srcSize.width / srcSize.height;

if (option.keepWidthFirst) {
height = width / srcRatio;
} else {
width = srcRatio * height;
}
}
UIGraphicsBeginImageContext(CGSizeMake(w, h));
CGContextRef ctx = UIGraphicsGetCurrentContext();
if (!ctx) {
return;
}
[outImage drawInRect:CGRectMake(0, 0, w, h)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (!newImage) {
return;
}
outImage = newImage;

CGContextRef context = createCGContext((size_t) width, (size_t) height);

CGContextDrawImage(context, CGRectMake(0, 0, width, height), srcImage);

outImage = getImageFromCGContext(context);

releaseCGContext(context);
}

#pragma mark add text
Expand Down
1 change: 1 addition & 0 deletions ios/Classes/FIConvertUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ NS_ASSUME_NONNULL_BEGIN
@property(assign, nonatomic) int width;
@property(assign, nonatomic) int height;
@property(assign, nonatomic) BOOL keepRatio;
@property(assign, nonatomic) BOOL keepWidthFirst;

@end

Expand Down
1 change: 1 addition & 0 deletions ios/Classes/FIConvertUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ + (id)createFromDict:(NSDictionary *)dict {
option.width = [dict[@"width"] intValue];
option.height = [dict[@"height"] intValue];
option.keepRatio = [dict[@"keepRatio"] boolValue];
option.keepWidthFirst = [dict[@"keepWidthFirst"] boolValue];
return option;
}

Expand Down
3 changes: 3 additions & 0 deletions lib/src/option/scale.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ class ScaleOption implements Option {
this.width,
this.height, {
this.keepRatio = false,
this.keepWidthFirst = true,
}) : assert(width > 0 && height > 0);

final int width;
final int height;
final bool keepRatio;
final bool keepWidthFirst;

@override
bool get canIgnore => false;
Expand All @@ -23,6 +25,7 @@ class ScaleOption implements Option {
'width': width,
'height': height,
'keepRatio': keepRatio,
'keepWidthFirst': keepWidthFirst,
};
}
}
2 changes: 2 additions & 0 deletions macos/Classes/FIConvertUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ NS_ASSUME_NONNULL_BEGIN
@interface FIScaleOption : NSObject <FIOption>
@property(assign, nonatomic) int width;
@property(assign, nonatomic) int height;
@property(assign, nonatomic) BOOL keepRatio;
@property(assign, nonatomic) BOOL keepWidthFirst;
@end

@interface FIAddText : NSObject <FIOption>
Expand Down
2 changes: 2 additions & 0 deletions macos/Classes/FIConvertUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ + (id)createFromDict:(NSDictionary *)dict {
FIScaleOption *option = [FIScaleOption new];
option.width = [dict[@"width"] intValue];
option.height = [dict[@"height"] intValue];
option.keepRatio = [dict[@"keepRatio"] boolValue];
option.keepWidthFirst = [dict[@"keepWidthFirst"] boolValue];
return option;
}

Expand Down
23 changes: 22 additions & 1 deletion macos/Classes/FIUIImageHandler.m
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,15 @@ CGContextRef createCGContext(size_t pixelsWide, size_t pixelsHigh) {

return context;// 7
}

#pragma clang diagnostic pop

CGSize CGImageGetSize(CGImageRef ref) {
size_t w = CGImageGetWidth(ref);
size_t h = CGImageGetHeight(ref);
return CGSizeMake(w, h);
}

FIImage *getImageFromCGContext(CGContextRef context) {
size_t h = CGBitmapContextGetHeight(context);
size_t w = CGBitmapContextGetWidth(context);
Expand Down Expand Up @@ -345,12 +352,26 @@ - (void)scale:(FIScaleOption *)option {
return;
}

CGImageRef srcImage = outImage.CGImage;

double width = option.width;
double height = option.height;

if (option.keepRatio) {
CGSize srcSize = CGImageGetSize(srcImage);

double srcRatio = srcSize.width / srcSize.height;

if (option.keepWidthFirst) {
height = width / srcRatio;
} else {
width = srcRatio * height;
}
}

CGContextRef context = createCGContext((size_t) width, (size_t) height);

CGContextDrawImage(context, CGRectMake(0, 0, width, height), [outImage CGImage]);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), srcImage);

outImage = [self getImageWith:context];

Expand Down

0 comments on commit 62b7025

Please sign in to comment.