NIGGER SLAYER 36780a0789 h5测试
2026-04-13 12:05:59 +08:00

317 lines
14 KiB
Objective-C

#import "FLTImageCropperPlugin.h"
#import <TOCropViewController/TOCropViewController.h>
#import <UIKit/UIKit.h>
#import <MobileCoreServices/MobileCoreServices.h>
#import <Photos/Photos.h>
@interface FLTImageCropperPlugin() <TOCropViewControllerDelegate>
@end
@implementation FLTImageCropperPlugin {
FlutterResult _result;
NSDictionary *_arguments;
UIViewController *_viewController;
float _compressQuality;
NSString *_compressFormat;
}
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
FlutterMethodChannel* channel = [FlutterMethodChannel
methodChannelWithName:@"plugins.hunghd.vn/image_cropper"
binaryMessenger:[registrar messenger]];
UIViewController *viewController = [UIApplication sharedApplication].delegate.window.rootViewController;
FLTImageCropperPlugin* instance = [[FLTImageCropperPlugin alloc] initWithViewController:viewController];
[registrar addMethodCallDelegate:instance channel:channel];
}
- (instancetype)initWithViewController:(UIViewController *)viewController {
self = [super init];
if (self) {
_viewController = viewController;
}
return self;
}
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
if ([@"cropImage" isEqualToString:call.method]) {
_result = result;
_arguments = call.arguments;
NSString *sourcePath = call.arguments[@"source_path"];
NSNumber *ratioX = call.arguments[@"ratio_x"];
NSNumber *ratioY = call.arguments[@"ratio_y"];
NSString *cropStyle = call.arguments[@"crop_style"];
NSArray *aspectRatioPresets = call.arguments[@"aspect_ratio_presets"];
NSNumber *compressQuality = call.arguments[@"compress_quality"];
NSString *compressFormat = call.arguments[@"compress_format"];
UIImage *image = [UIImage imageWithContentsOfFile:sourcePath];
TOCropViewController *cropViewController;
if ([@"circle" isEqualToString:cropStyle]) {
cropViewController = [[TOCropViewController alloc] initWithCroppingStyle:TOCropViewCroppingStyleCircular image:image];
} else {
cropViewController = [[TOCropViewController alloc] initWithImage:image];
}
cropViewController.delegate = self;
if (compressQuality && [compressQuality isKindOfClass:[NSNumber class]]) {
_compressQuality = compressQuality.intValue * 1.0f / 100;
} else {
_compressQuality = 0.9f;
}
if (compressFormat && [compressFormat isKindOfClass:[NSString class]]) {
_compressFormat = compressFormat;
} else {
_compressFormat = @"jpg";
}
NSMutableArray *allowedAspectRatios = [NSMutableArray new];
for (NSString *preset in aspectRatioPresets) {
if (preset) {
[allowedAspectRatios addObject:@([self parseAspectRatioPresetFromName:preset])];
}
}
cropViewController.allowedAspectRatios = allowedAspectRatios;
if (ratioX != (id)[NSNull null] && ratioY != (id)[NSNull null]) {
cropViewController.customAspectRatio = CGSizeMake([ratioX floatValue], [ratioY floatValue]);
cropViewController.resetAspectRatioEnabled = NO;
cropViewController.aspectRatioPickerButtonHidden = YES;
cropViewController.aspectRatioLockDimensionSwapEnabled = YES;
cropViewController.aspectRatioLockEnabled = YES;
}
[self setupUiCustomizedOptions:call.arguments forViewController:cropViewController];
UIWindow *window = [UIApplication sharedApplication].delegate.window;
if (!window && @available(iOS 13.0, *)) {
for (UIWindowScene* scene in [UIApplication sharedApplication].connectedScenes) {
if (scene.activationState == UISceneActivationStateForegroundActive) {
for (UIWindow *w in scene.windows) {
if (w.isKeyWindow) {
window = w;
break;
}
}
}
}
}
[window.rootViewController presentViewController:cropViewController animated:YES completion:nil];
} else {
result(FlutterMethodNotImplemented);
}
}
- (void)setupUiCustomizedOptions:(id)options forViewController:(TOCropViewController*)controller {
NSNumber *minimumAspectRatio = options[@"ios.minimum_aspect_ratio"];
NSNumber *rectX = options[@"ios.rect_x"];
NSNumber *rectY = options[@"ios.rect_y"];
NSNumber *rectWidth = options[@"ios.rect_width"];
NSNumber *rectHeight = options[@"ios.rect_height"];
NSNumber *showActivitySheetOnDone = options[@"ios.show_activity_sheet_on_done"];
NSNumber *showCancelConfirmationDialog = options[@"ios.show_cancel_confirmation_dialog"];
NSNumber *rotateClockwiseButtonHidden = options[@"ios.rotate_clockwise_button_hidden"];
NSNumber *hidesNavigationBar = options[@"ios.hides_navigation_bar"];
NSNumber *rotateButtonHidden = options[@"ios.rotate_button_hidden"];
NSNumber *resetButtonHidden = options[@"ios.reset_button_hidden"];
NSNumber *aspectRatioPickerButtonHidden = options[@"ios.aspect_ratio_picker_button_hidden"];
NSNumber *resetAspectRatioEnabled = options[@"ios.reset_aspect_ratio_enabled"];
NSNumber *aspectRatioLockDimensionSwapEnabled = options[@"ios.aspect_ratio_lock_dimension_swap_enabled"];
NSNumber *aspectRatioLockEnabled = options[@"ios.aspect_ratio_lock_enabled"];
NSString *title = options[@"ios.title"];
NSString *doneButtonTitle = options[@"ios.done_button_title"];
NSString *cancelButtonTitle = options[@"ios.cancel_button_title"];
if (minimumAspectRatio && [minimumAspectRatio isKindOfClass:[NSNumber class]]) {
controller.minimumAspectRatio = minimumAspectRatio.floatValue;
}
if (rectX && [rectX isKindOfClass:[NSNumber class]]
&& rectY && [rectY isKindOfClass:[NSNumber class]]
&& rectWidth && [rectWidth isKindOfClass:[NSNumber class]]
&& rectHeight && [rectHeight isKindOfClass:[NSNumber class]]) {
controller.imageCropFrame = CGRectMake(rectX.floatValue, rectY.floatValue, rectWidth.floatValue, rectHeight.floatValue);
}
if (showActivitySheetOnDone && [showActivitySheetOnDone isKindOfClass:[NSNumber class]]) {
controller.showActivitySheetOnDone = showActivitySheetOnDone.boolValue;
}
if (showCancelConfirmationDialog && [showCancelConfirmationDialog isKindOfClass:[NSNumber class]]) {
controller.showCancelConfirmationDialog = showCancelConfirmationDialog.boolValue;
}
if (rotateClockwiseButtonHidden && [rotateClockwiseButtonHidden isKindOfClass:[NSNumber class]]) {
controller.rotateClockwiseButtonHidden = rotateClockwiseButtonHidden.boolValue;
}
if (hidesNavigationBar && [hidesNavigationBar isKindOfClass:[NSNumber class]]) {
controller.hidesNavigationBar = hidesNavigationBar.boolValue;
}
if (rotateButtonHidden && [rotateButtonHidden isKindOfClass:[NSNumber class]]) {
controller.rotateButtonsHidden = rotateButtonHidden.boolValue;
}
if (resetButtonHidden && [resetButtonHidden isKindOfClass:[NSNumber class]]) {
controller.resetButtonHidden = resetButtonHidden.boolValue;
}
if (aspectRatioPickerButtonHidden && [aspectRatioPickerButtonHidden isKindOfClass:[NSNumber class]]) {
controller.aspectRatioPickerButtonHidden = aspectRatioPickerButtonHidden.boolValue;
}
if (resetAspectRatioEnabled && [resetAspectRatioEnabled isKindOfClass:[NSNumber class]]) {
controller.resetAspectRatioEnabled = resetAspectRatioEnabled.boolValue;
}
if (aspectRatioLockDimensionSwapEnabled && [aspectRatioLockDimensionSwapEnabled isKindOfClass:[NSNumber class]]) {
controller.aspectRatioLockDimensionSwapEnabled = aspectRatioLockDimensionSwapEnabled.boolValue;
}
if (aspectRatioLockEnabled && [aspectRatioLockEnabled isKindOfClass:[NSNumber class]]) {
controller.aspectRatioLockEnabled = aspectRatioLockEnabled.boolValue;
}
if (title && [title isKindOfClass:[NSString class]]) {
controller.title = title;
}
if (doneButtonTitle && [doneButtonTitle isKindOfClass:[NSString class]]) {
controller.doneButtonTitle = doneButtonTitle;
}
if (cancelButtonTitle && [cancelButtonTitle isKindOfClass:[NSString class]]) {
controller.cancelButtonTitle = cancelButtonTitle;
}
}
- (TOCropViewControllerAspectRatioPreset)parseAspectRatioPresetFromName:(NSString*)name {
if ([@"square" isEqualToString:name]) {
return TOCropViewControllerAspectRatioPresetSquare;
} else if ([@"original" isEqualToString:name]) {
return TOCropViewControllerAspectRatioPresetOriginal;
} else if ([@"3x2" isEqualToString:name]) {
return TOCropViewControllerAspectRatioPreset3x2;
} else if ([@"4x3" isEqualToString:name]) {
return TOCropViewControllerAspectRatioPreset4x3;
} else if ([@"5x3" isEqualToString:name]) {
return TOCropViewControllerAspectRatioPreset5x3;
} else if ([@"5x4" isEqualToString:name]) {
return TOCropViewControllerAspectRatioPreset5x4;
} else if ([@"7x5" isEqualToString:name]) {
return TOCropViewControllerAspectRatioPreset7x5;
} else if ([@"16x9" isEqualToString:name]) {
return TOCropViewControllerAspectRatioPreset16x9;
} else {
return TOCropViewControllerAspectRatioPresetOriginal;
}
}
# pragma TOCropViewControllerDelegate
- (void)cropViewController:(TOCropViewController *)cropViewController didCropToImage:(UIImage *)image withRect:(CGRect)cropRect angle:(NSInteger)angle
{
image = [self normalizedImage:image];
NSNumber *maxWidth = [_arguments objectForKey:@"max_width"];
NSNumber *maxHeight = [_arguments objectForKey:@"max_height"];
if (maxWidth != (id)[NSNull null] && maxHeight != (id)[NSNull null]) {
image = [self scaledImage:image maxWidth:maxWidth maxHeight:maxHeight];
}
NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString];
NSData *data;
NSString *tmpFile;
if ([@"png" isEqualToString:_compressFormat]) {
data = UIImagePNGRepresentation(image);
tmpFile = [NSString stringWithFormat:@"image_cropper_%@.png", guid];
} else {
data = UIImageJPEGRepresentation(image, _compressQuality);
tmpFile = [NSString stringWithFormat:@"image_cropper_%@.jpg", guid];
}
NSString *tmpDirectory = NSTemporaryDirectory();
NSString *tmpPath = [tmpDirectory stringByAppendingPathComponent:tmpFile];
if (_result) {
if ([[NSFileManager defaultManager] createFileAtPath:tmpPath contents:data attributes:nil]) {
_result(tmpPath);
} else {
_result([FlutterError errorWithCode:@"create_error"
message:@"Temporary file could not be created"
details:nil]);
}
[cropViewController dismissViewControllerAnimated:YES completion:nil];
_result = nil;
_arguments = nil;
}
}
- (void)cropViewController:(TOCropViewController *)cropViewController didFinishCancelled:(BOOL)cancelled {
[cropViewController dismissViewControllerAnimated:YES completion:nil];
_result(nil);
_result = nil;
_arguments = nil;
}
// The way we save images to the tmp dir currently throws away all EXIF data
// (including the orientation of the image). That means, pics taken in portrait
// will not be orientated correctly as is. To avoid that, we rotate the actual
// image data.
// TODO(goderbauer): investigate how to preserve EXIF data.
- (UIImage *)normalizedImage:(UIImage *)image {
if (image.imageOrientation == UIImageOrientationUp) return image;
UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);
[image drawInRect:(CGRect){0, 0, image.size}];
UIImage *normalizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return normalizedImage;
}
- (UIImage *)scaledImage:(UIImage *)image
maxWidth:(NSNumber *)maxWidth
maxHeight:(NSNumber *)maxHeight {
double originalWidth = image.size.width;
double originalHeight = image.size.height;
bool hasMaxWidth = maxWidth != (id)[NSNull null];
bool hasMaxHeight = maxHeight != (id)[NSNull null];
double width = hasMaxWidth ? MIN([maxWidth doubleValue], originalWidth) : originalWidth;
double height = hasMaxHeight ? MIN([maxHeight doubleValue], originalHeight) : originalHeight;
bool shouldDownscaleWidth = hasMaxWidth && [maxWidth doubleValue] < originalWidth;
bool shouldDownscaleHeight = hasMaxHeight && [maxHeight doubleValue] < originalHeight;
bool shouldDownscale = shouldDownscaleWidth || shouldDownscaleHeight;
if (shouldDownscale) {
double downscaledWidth = (height / originalHeight) * originalWidth;
double downscaledHeight = (width / originalWidth) * originalHeight;
if (width < height) {
if (!hasMaxWidth) {
width = downscaledWidth;
} else {
height = downscaledHeight;
}
} else if (height < width) {
if (!hasMaxHeight) {
height = downscaledHeight;
} else {
width = downscaledWidth;
}
} else {
if (originalWidth < originalHeight) {
width = downscaledWidth;
} else if (originalHeight < originalWidth) {
height = downscaledHeight;
}
}
}
UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), NO, 1.0);
[image drawInRect:CGRectMake(0, 0, width, height)];
UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return scaledImage;
}
@end