Files
midi_ios/TUIKit/TUIMultimediaPlugin/Edit/UI/PhotoEditor/TUIMultimediaPictureEditorController.m
2025-08-14 10:07:49 +08:00

287 lines
12 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright (c) 2024 Tencent. All rights reserved.
// Author: eddardliu
#import "TUIMultimediaPictureEditorController.h"
#import <ReactiveObjC/ReactiveObjC.h>
#import <Masonry/Masonry.h>
#import <TXLiteAVSDK_Professional/TXPictureEditer.h>
#import <TXLiteAVSDK_Professional/TXVideoEditerTypeDef.h>
#import "TUIMultimediaPlugin/NSArray+Functional.h"
#import "TUIMultimediaPlugin/TUIMultimediaCommonEditorControlView.h"
#import "TUIMultimediaPlugin/TUIMultimediaConstant.h"
#import "TUIMultimediaPlugin/TUIMultimediaPasterConfig.h"
#import "TUIMultimediaPlugin/TUIMultimediaPasterSelectController.h"
#import "TUIMultimediaPlugin/TUIMultimediaSubtitleEditController.h"
#import "TUIMultimediaPlugin/TUIMultimediaCommon.h"
#import "TUIMultimediaPlugin/TUIMultimediaSticker.h"
#import "TUIMultimediaPlugin/TUIMultimediaAuthorizationPrompter.h"
@interface TUIMultimediaPictureEditorController () <TUIMultimediaCommonEditorControlViewDelegate, TUIMultimediaPasterSelectControllerDelegate> {
TXPictureEditer *_editor;
UIImageView *_imgView;
TUIMultimediaPasterSelectController *_pasterSelectController;
TUIMultimediaSubtitleEditController *_subtitleEditController;
TUIMultimediaCommonEditorControlView *_commonEditCtrlView;
BOOL _originNavgationBarHidden;
BOOL _hasCrop;
}
@end
@implementation TUIMultimediaPictureEditorController
- (instancetype)init {
self = [super init];
_hasCrop = false;
_sourceType = SOURCE_TYPE_RECORD;
return self;
}
- (void)viewDidLoad {
_editor = [[TXPictureEditer alloc] init];
_pasterSelectController = [[TUIMultimediaPasterSelectController alloc] init];
_pasterSelectController.delegate = self;
_pasterSelectController.modalPresentationStyle = UIModalPresentationOverCurrentContext;
_subtitleEditController = [[TUIMultimediaSubtitleEditController alloc] init];
_subtitleEditController.modalPresentationStyle = UIModalPresentationOverCurrentContext;
_commonEditCtrlView = [[TUIMultimediaCommonEditorControlView alloc] initWithConfig:TUIMultimediaCommonEditorConfig.configForPictureEditor];
[self.view addSubview:_commonEditCtrlView];
_commonEditCtrlView.delegate = self;
_commonEditCtrlView.clipsToBounds = true;
_commonEditCtrlView.sourceType = _sourceType;
_commonEditCtrlView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
_commonEditCtrlView.previewSize = _srcPicture.size;
_imgView = [[UIImageView alloc] init];
_imgView.image = _srcPicture;
[_commonEditCtrlView.previewView addSubview:_imgView];
[_imgView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(_commonEditCtrlView.previewView);
}];
_commonEditCtrlView.mosaciOriginalImage = _srcPicture;
UIPinchGestureRecognizer *pinchRec = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(onPinch:)];
[self.view addGestureRecognizer:pinchRec];
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(onPanGesture:)];
[self.view addGestureRecognizer:panGesture];
}
- (void)onPinch:(UIPinchGestureRecognizer *)gestureRecognizer {
switch (gestureRecognizer.state) {
case UIGestureRecognizerStateChanged: {
if (gestureRecognizer.numberOfTouches < 2) {
break;
}
CGPoint touchPoint1 = [gestureRecognizer locationOfTouch:0 inView:self.view];
CGPoint touchPoint2 = [gestureRecognizer locationOfTouch:1 inView:self.view];
CGPoint scaleCenter = CGPointMake((touchPoint1.x + touchPoint2.x) / 2, (touchPoint1.y + touchPoint2.y) / 2);
[_commonEditCtrlView previewScale:gestureRecognizer.scale center:scaleCenter];
gestureRecognizer.scale = 1.0f;
break;
}
case UIGestureRecognizerStateEnded:
[_commonEditCtrlView previewAdjustToLimitRect];
break;
default:
break;
}
}
- (void)onPanGesture:(UIPanGestureRecognizer *)gestureRecognizer {
switch (gestureRecognizer.state) {
case UIGestureRecognizerStateChanged: {
CGPoint offset = [gestureRecognizer translationInView:self.view];
[gestureRecognizer setTranslation:CGPointZero inView:self.view];
[_commonEditCtrlView previewMove:offset];
break;
}
case UIGestureRecognizerStateEnded:
[_commonEditCtrlView previewAdjustToLimitRect];
break;
default:
break;
}
}
- (void)viewWillAppear:(BOOL)animated {
if (self.navigationController != nil) {
_originNavgationBarHidden = self.navigationController.navigationBarHidden;
self.navigationController.navigationBarHidden = YES;
}
}
- (void)viewWillDisappear:(BOOL)animated {
if (self.navigationController != nil) {
self.navigationController.navigationBarHidden = _originNavgationBarHidden;
}
}
#pragma mark - Properties
- (void)setSrcPicture:(UIImage *)photo {
_srcPicture = photo;
_imgView.image = _srcPicture;
_commonEditCtrlView.previewSize = _srcPicture.size;
}
- (void)setSourceType:(int)sourceType {
NSLog(@"setSourceType sourceType = %d",sourceType);
_sourceType = sourceType;
if (_commonEditCtrlView != nil) {
_commonEditCtrlView.sourceType = sourceType;
}
}
#pragma mark - TUIMultimediaCommonEditorControlViewDelegate protocol
- (void)onCommonEditorControlViewCancel:(nonnull TUIMultimediaCommonEditorControlView *)view {
if (_completeCallback == nil) {
return;
}
_completeCallback(nil, PHOTO_EDIT_RESULT_CODE_CANCEL);
}
- (void)onCommonEditorControlViewCancelGenerate:(nonnull TUIMultimediaCommonEditorControlView *)view {
}
- (void)onCommonEditorControlViewComplete:(nonnull TUIMultimediaCommonEditorControlView *)view
stickers:(nonnull NSArray<TUIMultimediaSticker *> *)stickers {
if (![TUIMultimediaAuthorizationPrompter verifyPermissionGranted:self]) {
return;
}
if (_completeCallback == nil) {
return;
}
if (stickers.count == 0) {
NSLog(@"Return directly without edit the picture");
UIImage* outputImage = (_hasCrop || _sourceType == SOURCE_TYPE_RECORD) ? _srcPicture : nil;
_completeCallback(outputImage, _hasCrop ? VIDEO_EDIT_RESULT_CODE_GENERATE_SUCCESS : VIDEO_EDIT_RESULT_CODE_NO_EDIT);
return;
}
[_editor setPicture:_srcPicture];
[_editor setOutputRotation:0];
[_editor setOutputSize:_srcPicture.size.width height:_srcPicture.size.height];
[_editor setPasterList:[self stickercConvertToPaster:stickers rotationAngle:0]];
@weakify(self)
[_editor processPicture:^(UIImage *processedPicture) {
@strongify(self)
self.completeCallback(processedPicture, PHOTO_EDIT_RESULT_CODE_GENERATE_SUCCESS);
}];
}
- (void)onCommonEditorControlViewCrop:(NSInteger)rotationAngle normalizedCropRect:(CGRect)normalizedCropRect
stickers:(NSArray<TUIMultimediaSticker *> *)stickers {
[_editor setPicture:_srcPicture];
[_editor setPasterList:[self stickercConvertToPaster:stickers rotationAngle:rotationAngle]];
normalizedCropRect = [self adjustCropRectAccordingRotaionAngle:normalizedCropRect rotationAngle:rotationAngle];
CGRect cropRect = CGRectMake(normalizedCropRect.origin.x * _srcPicture.size.width * _srcPicture.scale,
normalizedCropRect.origin.y * _srcPicture.size.height * _srcPicture.scale,
normalizedCropRect.size.width * _srcPicture.size.width * _srcPicture.scale,
normalizedCropRect.size.height * _srcPicture.size.height * _srcPicture.scale);
[_editor setCropRect:cropRect];
CGSize outputSize = [self getOutputSize:cropRect.size rotationAngle:rotationAngle];
[_editor setOutputSize:outputSize.width height:outputSize.height];
[_editor setOutputRotation:(CGFloat)rotationAngle];
[_editor processPicture:^(UIImage *processedPicture) {
[self onProcessPictureForCrop:processedPicture];
}];
}
- (CGSize) getOutputSize:(CGSize) cropRectSize rotationAngle:(CGFloat) rotationAngle {
int outputWidth = 1080;
int outputHeight = 1920;
if(((int)rotationAngle + 180) % 180 == 0) {
outputHeight = cropRectSize.height / cropRectSize.width * outputWidth;
} else {
outputHeight = cropRectSize.width / cropRectSize.height * outputWidth;
}
return CGSizeMake(outputWidth, outputHeight);
}
- (NSArray<TXPaster *> *) stickercConvertToPaster:(nonnull NSArray<TUIMultimediaSticker *> *)stickers rotationAngle:(NSInteger)rotationAngle {
CGSize previewSize = _commonEditCtrlView.previewView.frame.size;
return [stickers tui_multimedia_map:^TXPaster *(TUIMultimediaSticker *s) {
TXPaster *p = [[TXPaster alloc] init];
p.pasterImage = s.image;
CGRect frame = CGRectMake(s.frame.origin.x / previewSize.width,
s.frame.origin.y / previewSize.height,
s.frame.size.width / previewSize.width,
s.frame.size.height / previewSize.height);
p.frame = frame;
return p;
}];
}
- (CGRect) adjustCropRectAccordingRotaionAngle:(CGRect)normalizedCropRect rotationAngle:(CGFloat)rotationAngle {
normalizedCropRect = CGRectMake(MIN(1.0f, MAX(normalizedCropRect.origin.x, 0)),
MIN(1.0f, MAX(normalizedCropRect.origin.y, 0)),
MIN(1.0f, MAX(normalizedCropRect.size.width, 0)),
MIN(1.0f, MAX(normalizedCropRect.size.height, 0)));
if (rotationAngle == 90) {
normalizedCropRect = CGRectMake(normalizedCropRect.origin.y,
1 - normalizedCropRect.size.width - normalizedCropRect.origin.x,
normalizedCropRect.size.height,
normalizedCropRect.size.width);
} else if (rotationAngle == 180) {
normalizedCropRect = CGRectMake(1 - normalizedCropRect.size.width - normalizedCropRect.origin.x,
1 - normalizedCropRect.size.height - normalizedCropRect.origin.y,
normalizedCropRect.size.width,
normalizedCropRect.size.height);
} else if (rotationAngle == 270) {
normalizedCropRect = CGRectMake(1 - normalizedCropRect.origin.y - normalizedCropRect.size.height,
normalizedCropRect.origin.x,
normalizedCropRect.size.height,
normalizedCropRect.size.width);
}
return normalizedCropRect;
}
- (void) onProcessPictureForCrop:(UIImage *) processedPicture {
_hasCrop = true;
_srcPicture = processedPicture;
_commonEditCtrlView.previewView.hidden = NO;
_commonEditCtrlView.previewSize = _srcPicture.size;
_imgView.image = _srcPicture;
_commonEditCtrlView.mosaciOriginalImage = _srcPicture;
}
- (void)onCommonEditorControlViewNeedAddPaster:(TUIMultimediaCommonEditorControlView *)view {
_commonEditCtrlView.modifyButtonsHidden = YES;
[self presentViewController:_pasterSelectController animated:NO completion:nil];
}
- (void)onCommonEditorControlViewNeedModifySubtitle:(TUIMultimediaSubtitleInfo *)info callback:(void (^)(TUIMultimediaSubtitleInfo *info, BOOL isOk))callback {
_subtitleEditController.subtitleInfo = info;
_subtitleEditController.callback = ^(TUIMultimediaSubtitleEditController *c, BOOL isOk) {
[c.presentingViewController dismissViewControllerAnimated:NO completion:nil];
if (callback != nil) {
callback(c.subtitleInfo, isOk);
}
};
[self presentViewController:_subtitleEditController animated:NO completion:nil];
}
- (void)onCommonEditorControlViewNeedEditMusic:(nonnull TUIMultimediaCommonEditorControlView *)view {
}
#pragma mark - TUIMultimediaPasterSelectControllerDelegate protocol
- (void)pasterSelectController:(TUIMultimediaPasterSelectController *)c onPasterSelected:(UIImage *)image {
[_commonEditCtrlView addPaster:image];
_commonEditCtrlView.modifyButtonsHidden = NO;
[c.presentingViewController dismissViewControllerAnimated:NO completion:nil];
}
- (void)onPasterSelectControllerExit:(TUIMultimediaPasterSelectController *)c {
_commonEditCtrlView.modifyButtonsHidden = NO;
[c.presentingViewController dismissViewControllerAnimated:NO completion:nil];
}
@end