Swiftgram/submodules/LegacyComponents/Sources/TGPhotoPaintController.m
Ilya Laktyushin 40e28d769d Various fixes
2020-11-12 20:12:00 +04:00

2602 lines
98 KiB
Objective-C

#import "TGPhotoPaintController.h"
#import "LegacyComponentsInternal.h"
#import <LegacyComponents/UIImage+TG.h>
#import <LegacyComponents/TGPaintUtils.h>
#import <LegacyComponents/TGPhotoEditorUtils.h>
#import <LegacyComponents/TGPhotoEditorAnimation.h>
#import "TGPhotoEditorInterfaceAssets.h"
#import <LegacyComponents/TGObserverProxy.h>
#import <LegacyComponents/TGMenuView.h>
#import <LegacyComponents/TGModernButton.h>
#import "TGMenuSheetController.h"
#import <LegacyComponents/TGMediaAsset.h>
#import <LegacyComponents/TGMediaAssetImageSignals.h>
#import "TGPainting.h"
#import <LegacyComponents/TGPaintingData.h>
#import "TGPaintRadialBrush.h"
#import "TGPaintEllipticalBrush.h"
#import "TGPaintNeonBrush.h"
#import "TGPaintArrowBrush.h"
#import "TGPaintCanvas.h"
#import "TGPaintingWrapperView.h"
#import "TGPaintState.h"
#import "TGPaintBrushPreview.h"
#import "TGPaintSwatch.h"
#import "TGPhotoPaintFont.h"
#import <LegacyComponents/TGPaintUndoManager.h>
#import "PGPhotoEditor.h"
#import "TGPhotoEditorPreviewView.h"
#import "TGPhotoPaintActionsView.h"
#import "TGPhotoPaintSettingsView.h"
#import "TGPhotoPaintSettingsWrapperView.h"
#import "TGPhotoBrushSettingsView.h"
#import "TGPhotoTextSettingsView.h"
#import "TGPhotoPaintSelectionContainerView.h"
#import "TGPhotoEntitiesContainerView.h"
#import "TGPhotoStickerEntityView.h"
#import "TGPhotoTextEntityView.h"
#import "TGPhotoPaintEyedropperView.h"
#import "TGPaintFaceDetector.h"
#import "TGPhotoMaskPosition.h"
const CGFloat TGPhotoPaintTopPanelSize = 44.0f;
const CGFloat TGPhotoPaintBottomPanelSize = 79.0f;
const CGSize TGPhotoPaintingLightMaxSize = { 1280.0f, 1280.0f };
const CGSize TGPhotoPaintingMaxSize = { 1920.0f, 1920.0f };
const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
@interface TGPhotoPaintController () <UIScrollViewDelegate, UIGestureRecognizerDelegate, ASWatcher>
{
TGPaintUndoManager *_undoManager;
TGObserverProxy *_keyboardWillChangeFrameProxy;
CGFloat _keyboardHeight;
TGModernGalleryZoomableScrollView *_scrollView;
UIView *_scrollContentView;
UIButton *_containerView;
TGPhotoEditorSparseView *_wrapperView;
UIView *_portraitToolsWrapperView;
UIView *_landscapeToolsWrapperView;
UIPinchGestureRecognizer *_pinchGestureRecognizer;
UIRotationGestureRecognizer *_rotationGestureRecognizer;
NSArray *_brushes;
TGPainting *_painting;
TGPaintCanvas *_canvasView;
TGPaintBrushPreview *_brushPreview;
CGSize _previousSize;
UIView *_contentView;
UIView *_contentWrapperView;
UIView *_dimView;
TGModernButton *_doneButton;
TGPhotoPaintActionsView *_landscapeActionsView;
TGPhotoPaintActionsView *_portraitActionsView;
TGPhotoPaintSettingsView *_portraitSettingsView;
TGPhotoPaintSettingsView *_landscapeSettingsView;
TGPhotoPaintSettingsWrapperView *_settingsViewWrapper;
UIView<TGPhotoPaintPanelView> *_settingsView;
id<TGPhotoPaintStickersScreen> _stickersScreen;
bool _appeared;
bool _skipEntitiesSetup;
bool _entitiesReady;
TGPhotoPaintFont *_selectedTextFont;
TGPhotoPaintTextEntityStyle _selectedTextStyle;
TGPhotoEntitiesContainerView *_entitiesContainerView;
TGPhotoPaintEntityView *_currentEntityView;
TGPhotoPaintSelectionContainerView *_selectionContainerView;
TGPhotoPaintEntitySelectionView *_entitySelectionView;
TGPhotoPaintEyedropperView *_eyedropperView;
TGPhotoTextEntityView *_editedTextView;
CGPoint _editedTextCenter;
CGAffineTransform _editedTextTransform;
UIButton *_textEditingDismissButton;
TGMenuContainerView *_menuContainerView;
TGPaintingData *_resultData;
TGPaintingWrapperView *_paintingWrapperView;
bool _enableStickers;
NSData *_eyedropperBackgroundData;
CGSize _eyedropperBackgroundSize;
NSInteger _eyedropperBackgroundBytesPerRow;
CGBitmapInfo _eyedropperBackgroundInfo;
id<LegacyComponentsContext> _context;
}
@property (nonatomic, strong) ASHandle *actionHandle;
@property (nonatomic, weak) PGPhotoEditor *photoEditor;
@property (nonatomic, weak) TGPhotoEditorPreviewView *previewView;
@end
@implementation TGPhotoPaintController
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView entitiesView:(TGPhotoEntitiesContainerView *)entitiesView
{
self = [super initWithContext:context];
if (self != nil)
{
_context = context;
_enableStickers = photoEditor.enableStickers;
_actionHandle = [[ASHandle alloc] initWithDelegate:self releaseOnMainThread:true];
self.photoEditor = photoEditor;
self.previewView = previewView;
_entitiesContainerView = entitiesView;
if (entitiesView != nil) {
_skipEntitiesSetup = true;
}
entitiesView.userInteractionEnabled = true;
_brushes = @
[
[[TGPaintRadialBrush alloc] init],
[[TGPaintEllipticalBrush alloc] init],
[[TGPaintNeonBrush alloc] init],
[[TGPaintArrowBrush alloc] init],
];
_selectedTextFont = [[TGPhotoPaintFont availableFonts] firstObject];
_selectedTextStyle = TGPhotoPaintTextEntityStyleFramed;
if (_photoEditor.paintingData.undoManager != nil)
_undoManager = [_photoEditor.paintingData.undoManager copy];
else
_undoManager = [[TGPaintUndoManager alloc] init];
CGSize size = TGScaleToSize(photoEditor.originalSize, [TGPhotoPaintController maximumPaintingSize]);
_painting = [[TGPainting alloc] initWithSize:size undoManager:_undoManager imageData:[_photoEditor.paintingData data]];
_undoManager.painting = _painting;
_keyboardWillChangeFrameProxy = [[TGObserverProxy alloc] initWithTarget:self targetSelector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification];
}
return self;
}
- (void)dealloc
{
[_actionHandle reset];
}
- (void)loadView
{
[super loadView];
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_scrollView = [[TGModernGalleryZoomableScrollView alloc] initWithFrame:self.view.bounds hasDoubleTap:false];
if (iosMajorVersion() >= 11) {
_scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
_scrollView.contentInset = UIEdgeInsetsZero;
_scrollView.delegate = self;
_scrollView.showsHorizontalScrollIndicator = false;
_scrollView.showsVerticalScrollIndicator = false;
[self.view addSubview:_scrollView];
_scrollContentView = [[UIView alloc] initWithFrame:self.view.bounds];
[_scrollView addSubview:_scrollContentView];
_containerView = [[UIButton alloc] initWithFrame:self.view.bounds];
_containerView.clipsToBounds = true;
[_containerView addTarget:self action:@selector(containerPressed) forControlEvents:UIControlEventTouchUpInside];
[_scrollContentView addSubview:_containerView];
_pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)];
_pinchGestureRecognizer.delegate = self;
[_containerView addGestureRecognizer:_pinchGestureRecognizer];
_rotationGestureRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(handleRotate:)];
_rotationGestureRecognizer.delegate = self;
[_containerView addGestureRecognizer:_rotationGestureRecognizer];
TGPhotoEditorPreviewView *previewView = _previewView;
previewView.userInteractionEnabled = false;
previewView.hidden = true;
__weak TGPhotoPaintController *weakSelf = self;
_paintingWrapperView = [[TGPaintingWrapperView alloc] init];
_paintingWrapperView.clipsToBounds = true;
_paintingWrapperView.shouldReceiveTouch = ^bool
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return false;
return (strongSelf->_editedTextView == nil);
};
[_containerView addSubview:_paintingWrapperView];
_contentView = [[UIView alloc] init];
_contentView.clipsToBounds = true;
_contentView.userInteractionEnabled = false;
[_containerView addSubview:_contentView];
_contentWrapperView = [[UIView alloc] init];
_contentWrapperView.userInteractionEnabled = false;
[_contentView addSubview:_contentWrapperView];
if (_entitiesContainerView == nil) {
_entitiesContainerView = [[TGPhotoEntitiesContainerView alloc] init];
_entitiesContainerView.clipsToBounds = true;
_entitiesContainerView.stickersContext = _stickersContext;
}
_entitiesContainerView.entitySelected = ^(TGPhotoPaintEntityView *sender)
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf selectEntityView:sender];
};
_entitiesContainerView.entityRemoved = ^(TGPhotoPaintEntityView *entity)
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
if (entity == strongSelf->_currentEntityView)
[strongSelf _clearCurrentSelection];
[strongSelf updateSettingsButton];
};
if (!_skipEntitiesSetup) {
[_contentWrapperView addSubview:_entitiesContainerView];
}
_undoManager.entitiesContainer = _entitiesContainerView;
_dimView = [[UIView alloc] init];
_dimView.alpha = 0.0f;
_dimView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_dimView.backgroundColor = UIColorRGBA(0x000000, 0.4f);
_dimView.userInteractionEnabled = false;
[_entitiesContainerView addSubview:_dimView];
_selectionContainerView = [[TGPhotoPaintSelectionContainerView alloc] init];
_selectionContainerView.clipsToBounds = false;
[_containerView addSubview:_selectionContainerView];
_eyedropperView = [[TGPhotoPaintEyedropperView alloc] init];
_eyedropperView.locationChanged = ^(CGPoint location, bool finished) {
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf != nil)
{
UIColor *color = [strongSelf colorAtPoint:location];
strongSelf->_eyedropperView.color = color;
if (finished) {
TGPaintSwatch *swatch = [TGPaintSwatch swatchWithColor:color colorLocation:0.5 brushWeight:strongSelf->_portraitSettingsView.swatch.brushWeight];
[strongSelf setCurrentSwatch:swatch sender:nil];
[strongSelf commitEyedropper:false];
}
}
};
_eyedropperView.hidden = true;
[_selectionContainerView addSubview:_eyedropperView];
_wrapperView = [[TGPhotoEditorSparseView alloc] initWithFrame:CGRectZero];
[self.view addSubview:_wrapperView];
_portraitToolsWrapperView = [[UIView alloc] initWithFrame:CGRectZero];
_portraitToolsWrapperView.alpha = 0.0f;
[_wrapperView addSubview:_portraitToolsWrapperView];
_landscapeToolsWrapperView = [[UIView alloc] initWithFrame:CGRectZero];
_landscapeToolsWrapperView.alpha = 0.0f;
[_wrapperView addSubview:_landscapeToolsWrapperView];
void (^undoPressed)(void) = ^
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf->_undoManager undo];
};
void (^clearPressed)(UIView *) = ^(UIView *sender)
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf presentClearAllAlert:sender];
};
_portraitActionsView = [[TGPhotoPaintActionsView alloc] init];
_portraitActionsView.alpha = 0.0f;
_portraitActionsView.undoPressed = undoPressed;
_portraitActionsView.clearPressed = clearPressed;
[_wrapperView addSubview:_portraitActionsView];
_landscapeActionsView = [[TGPhotoPaintActionsView alloc] init];
_landscapeActionsView.alpha = 0.0f;
_landscapeActionsView.undoPressed = undoPressed;
_landscapeActionsView.clearPressed = clearPressed;
[_wrapperView addSubview:_landscapeActionsView];
_doneButton = [[TGModernButton alloc] init];
_doneButton.alpha = 0.0f;
_doneButton.userInteractionEnabled = false;
[_doneButton setTitle:TGLocalized(@"Common.Done") forState:UIControlStateNormal];
_doneButton.titleLabel.font = TGSystemFontOfSize(17.0);
[_doneButton sizeToFit];
// [_wrapperView addSubview:_doneButton];
void (^settingsPressed)(void) = ^
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf commitEyedropper:true];
if ([strongSelf->_currentEntityView isKindOfClass:[TGPhotoTextEntityView class]])
[strongSelf presentTextSettingsView];
else if ([strongSelf->_currentEntityView isKindOfClass:[TGPhotoStickerEntityView class]])
[strongSelf mirrorSelectedStickerEntity];
else
[strongSelf presentBrushSettingsView];
};
void (^eyedropperPressed)(void) = ^
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[self enableEyedropper];
};
void (^beganColorPicking)(void) = ^
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf commitEyedropper:true];
if (![strongSelf->_currentEntityView isKindOfClass:[TGPhotoTextEntityView class]])
[strongSelf setDimHidden:false animated:true];
};
void (^changedColor)(TGPhotoPaintSettingsView *, TGPaintSwatch *) = ^(TGPhotoPaintSettingsView *sender, TGPaintSwatch *swatch)
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf setCurrentSwatch:swatch sender:sender];
};
void (^finishedColorPicking)(TGPhotoPaintSettingsView *, TGPaintSwatch *) = ^(TGPhotoPaintSettingsView *sender, TGPaintSwatch *swatch)
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf commitEyedropper:true];
[strongSelf setCurrentSwatch:swatch sender:sender];
if (![strongSelf->_currentEntityView isKindOfClass:[TGPhotoTextEntityView class]])
[strongSelf setDimHidden:true animated:true];
};
_portraitSettingsView = [[TGPhotoPaintSettingsView alloc] initWithContext:_context];
_portraitSettingsView.eyedropperPressed = eyedropperPressed;
_portraitSettingsView.beganColorPicking = beganColorPicking;
_portraitSettingsView.changedColor = changedColor;
_portraitSettingsView.finishedColorPicking = finishedColorPicking;
_portraitSettingsView.settingsPressed = settingsPressed;
_portraitSettingsView.layer.rasterizationScale = TGScreenScaling();
_portraitSettingsView.interfaceOrientation = UIInterfaceOrientationPortrait;
[_portraitToolsWrapperView addSubview:_portraitSettingsView];
_landscapeSettingsView = [[TGPhotoPaintSettingsView alloc] initWithContext:_context];
_landscapeSettingsView.eyedropperPressed = eyedropperPressed;
_landscapeSettingsView.beganColorPicking = beganColorPicking;
_landscapeSettingsView.changedColor = changedColor;
_landscapeSettingsView.finishedColorPicking = finishedColorPicking;
_landscapeSettingsView.settingsPressed = settingsPressed;
_landscapeSettingsView.layer.rasterizationScale = TGScreenScaling();
_landscapeSettingsView.interfaceOrientation = UIInterfaceOrientationLandscapeLeft;
[_landscapeToolsWrapperView addSubview:_landscapeSettingsView];
[self setCurrentSwatch:_portraitSettingsView.swatch sender:nil];
if (![self _updateControllerInset:false])
[self controllerInsetUpdated:UIEdgeInsetsZero];
}
- (void)setStickersContext:(id<TGPhotoPaintStickersContext>)stickersContext {
_stickersContext = stickersContext;
_entitiesContainerView.stickersContext = stickersContext;
}
- (void)setupCanvas
{
if (_canvasView == nil) {
__weak TGPhotoPaintController *weakSelf = self;
_canvasView = [[TGPaintCanvas alloc] initWithFrame:CGRectZero];
_canvasView.pointInsideContainer = ^bool(CGPoint point)
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return false;
return [strongSelf->_containerView pointInside:[strongSelf->_canvasView convertPoint:point toView:strongSelf->_containerView] withEvent:nil];
};
_canvasView.shouldDraw = ^bool
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return false;
return ![strongSelf->_entitiesContainerView isTrackingAnyEntityView];
};
_canvasView.shouldDrawOnSingleTap = ^bool
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return false;
bool rotating = (strongSelf->_rotationGestureRecognizer.state == UIGestureRecognizerStateBegan || strongSelf->_rotationGestureRecognizer.state == UIGestureRecognizerStateChanged);
bool pinching = (strongSelf->_pinchGestureRecognizer.state == UIGestureRecognizerStateBegan || strongSelf->_pinchGestureRecognizer.state == UIGestureRecognizerStateChanged);
if (strongSelf->_currentEntityView != nil && !rotating && !pinching)
{
[strongSelf selectEntityView:nil];
return false;
}
return true;
};
_canvasView.strokeBegan = ^
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf selectEntityView:nil];
};
_canvasView.strokeCommited = ^
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf updateActionsView];
};
_canvasView.hitTest = ^UIView *(CGPoint point, UIEvent *event)
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return nil;
return [strongSelf->_entitiesContainerView hitTest:[strongSelf->_canvasView convertPoint:point toView:strongSelf->_entitiesContainerView] withEvent:event];
};
_canvasView.cropRect = _photoEditor.cropRect;
_canvasView.cropOrientation = _photoEditor.cropOrientation;
_canvasView.originalSize = _photoEditor.originalSize;
[_canvasView setPainting:_painting];
[_canvasView setBrush:_brushes.firstObject];
[self setCurrentSwatch:_portraitSettingsView.swatch sender:nil];
[_paintingWrapperView addSubview:_canvasView];
}
_canvasView.hidden = false;
[self.view setNeedsLayout];
}
- (void)viewDidLoad
{
[super viewDidLoad];
PGPhotoEditor *photoEditor = _photoEditor;
if (!_skipEntitiesSetup) {
[_entitiesContainerView setupWithPaintingData:photoEditor.paintingData];
}
for (TGPhotoPaintEntityView *view in _entitiesContainerView.subviews)
{
if (![view isKindOfClass:[TGPhotoPaintEntityView class]])
continue;
[self _commonEntityViewSetup:view];
}
__weak TGPhotoPaintController *weakSelf = self;
_undoManager.historyChanged = ^
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf updateActionsView];
};
[self updateActionsView];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self transitionIn];
}
#pragma mark - Tab Bar
- (TGPhotoEditorTab)availableTabs
{
TGPhotoEditorTab result = TGPhotoEditorPaintTab | TGPhotoEditorEraserTab | TGPhotoEditorTextTab;
if (_enableStickers && _stickersContext != nil) {
result |= TGPhotoEditorStickerTab;
}
return result;
}
- (void)handleTabAction:(TGPhotoEditorTab)tab
{
[self commitEyedropper:true];
switch (tab)
{
case TGPhotoEditorStickerTab:
{
[self presentStickersView];
}
break;
case TGPhotoEditorTextTab:
{
[self createNewTextLabel];
}
break;
case TGPhotoEditorPaintTab:
{
[self selectEntityView:nil];
if (_canvasView.state.eraser)
[self toggleEraserMode];
}
break;
case TGPhotoEditorEraserTab:
{
[self selectEntityView:nil];
[self toggleEraserMode];
}
break;
default:
break;
}
}
- (TGPhotoEditorTab)activeTab
{
TGPhotoEditorTab tabs = TGPhotoEditorNoneTab;
if (_currentEntityView != nil)
return tabs;
if (_canvasView.state.eraser)
tabs |= TGPhotoEditorEraserTab;
else
tabs |= TGPhotoEditorPaintTab;
return tabs;
}
#pragma mark - Undo & Redo
- (void)updateActionsView
{
if (_portraitActionsView == nil || _landscapeActionsView == nil)
return;
NSArray *views = @[ _portraitActionsView, _landscapeActionsView ];
for (TGPhotoPaintActionsView *view in views)
{
[view setUndoEnabled:_undoManager.canUndo];
[view setClearEnabled:_undoManager.canUndo];
}
}
- (void)presentClearAllAlert:(UIView *)sender
{
TGMenuSheetController *controller = [[TGMenuSheetController alloc] initWithContext:_context dark:false];
controller.dismissesByOutsideTap = true;
controller.narrowInLandscape = true;
controller.permittedArrowDirections = UIPopoverArrowDirectionUp;
__weak TGMenuSheetController *weakController = controller;
__weak TGPhotoPaintController *weakSelf = self;
NSArray *items = @
[
[[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Paint.ClearConfirm") type:TGMenuSheetButtonTypeDestructive fontSize:20.0 action:^
{
__strong TGMenuSheetController *strongController = weakController;
if (strongController == nil)
return;
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf->_painting clear];
[strongSelf->_undoManager reset];
[strongSelf->_entitiesContainerView removeAll];
[strongSelf _clearCurrentSelection];
[strongSelf updateSettingsButton];
[strongController dismissAnimated:true manual:false completion:nil];
}],
[[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Common.Cancel") type:TGMenuSheetButtonTypeCancel fontSize:20.0 action:^
{
__strong TGMenuSheetController *strongController = weakController;
if (strongController != nil)
[strongController dismissAnimated:true];
}]
];
[controller setItemViews:items];
controller.sourceRect = ^
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return CGRectZero;
return [sender convertRect:sender.bounds toView:strongSelf.view];
};
[controller presentInViewController:self.parentViewController sourceView:self.view animated:true];
}
- (void)_clearCurrentSelection
{
_scrollView.pinchGestureRecognizer.enabled = true;
_currentEntityView = nil;
if (_entitySelectionView != nil)
{
[_entitySelectionView removeFromSuperview];
_entitySelectionView = nil;
}
}
#pragma mark - Data Handling
- (UIImage *)eyedropperImage
{
UIImage *backgroundImage = [self.photoEditor currentResultImage];
CGSize fittedSize = TGFitSize(_painting.size, TGPhotoEditorResultImageMaxSize);
UIImage *paintingImage = _painting.isEmpty ? nil : [_painting imageWithSize:fittedSize andData:NULL];
NSMutableArray *entities = [[NSMutableArray alloc] init];
UIImage *entitiesImage = nil;
if (paintingImage == nil && _entitiesContainerView.entitiesCount < 1)
{
return backgroundImage;
}
else if (_entitiesContainerView.entitiesCount > 0)
{
for (TGPhotoPaintEntityView *view in _entitiesContainerView.subviews)
{
if (![view isKindOfClass:[TGPhotoPaintEntityView class]])
continue;
TGPhotoPaintEntity *entity = [view entity];
if (entity != nil) {
[entities addObject:entity];
}
}
entitiesImage = [_entitiesContainerView imageInRect:_entitiesContainerView.bounds background:nil still:true];
}
if (entitiesImage == nil && paintingImage == nil) {
return backgroundImage;
} else {
UIGraphicsBeginImageContextWithOptions(fittedSize, false, 1.0);
[backgroundImage drawInRect:CGRectMake(0.0, 0.0, fittedSize.width, fittedSize.height)];
[paintingImage drawInRect:CGRectMake(0.0, 0.0, fittedSize.width, fittedSize.height)];
[entitiesImage drawInRect:CGRectMake(0.0, 0.0, fittedSize.width, fittedSize.height)];
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
}
}
- (TGPaintingData *)_prepareResultData
{
if (_resultData != nil)
return _resultData;
NSData *data = nil;
CGSize fittedSize = TGFitSize(_painting.size, TGPhotoEditorResultImageMaxSize);
UIImage *image = _painting.isEmpty ? nil : [_painting imageWithSize:fittedSize andData:&data];
NSMutableArray *entities = [[NSMutableArray alloc] init];
bool hasAnimatedEntities = false;
UIImage *stillImage = nil;
if (image == nil && _entitiesContainerView.entitiesCount < 1)
{
_resultData = nil;
return _resultData;
}
else if (_entitiesContainerView.entitiesCount > 0)
{
for (TGPhotoPaintEntityView *view in _entitiesContainerView.subviews)
{
if (![view isKindOfClass:[TGPhotoPaintEntityView class]])
continue;
TGPhotoPaintEntity *entity = [view entity];
if (entity != nil) {
if (entity.animated) {
hasAnimatedEntities = true;
}
[entities addObject:entity];
}
}
if (hasAnimatedEntities) {
for (TGPhotoPaintEntity *entity in entities) {
if ([entity isKindOfClass:[TGPhotoPaintTextEntity class]]) {
TGPhotoPaintTextEntity *textEntity = (TGPhotoPaintTextEntity *)entity;
for (TGPhotoPaintEntityView *view in _entitiesContainerView.subviews)
{
if (![view isKindOfClass:[TGPhotoPaintEntityView class]])
continue;
if (view.entityUUID == textEntity.uuid) {
textEntity.renderImage = [(TGPhotoTextEntityView *)view image];
break;
}
}
}
}
}
if (!hasAnimatedEntities) {
image = [_entitiesContainerView imageInRect:_entitiesContainerView.bounds background:image still:false];
} else {
stillImage = [_entitiesContainerView imageInRect:_entitiesContainerView.bounds background:image still:true];
}
}
_resultData = [TGPaintingData dataWithPaintingData:data image:image stillImage:stillImage entities:entities undoManager:_undoManager];
return _resultData;
}
- (UIImage *)image
{
TGPaintingData *paintingData = [self _prepareResultData];
return paintingData.image;
}
- (TGPaintingData *)paintingData
{
return [self _prepareResultData];
}
- (void)enableEyedropper {
if (!_eyedropperView.isHidden)
return;
[self selectEntityView:nil];
self.controlVideoPlayback(false);
[_entitiesContainerView updateVisibility:false];
UIImage *image = [self eyedropperImage];
CGImageRef cgImage = image.CGImage;
CFDataRef pixelData = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));
_eyedropperBackgroundData = (__bridge NSData *)pixelData;
_eyedropperBackgroundSize = image.size;
_eyedropperBackgroundBytesPerRow = CGImageGetBytesPerRow(cgImage);
_eyedropperBackgroundInfo = CGImageGetBitmapInfo(cgImage);
[_eyedropperView update];
[_eyedropperView present];
}
- (void)commitEyedropper:(bool)immediate {
self.controlVideoPlayback(true);
[_entitiesContainerView updateVisibility:true];
_eyedropperBackgroundData = nil;
_eyedropperBackgroundSize = CGSizeZero;
_eyedropperBackgroundBytesPerRow = 0;
_eyedropperBackgroundInfo = 0;
double timeout = immediate ? 0.0 : 0.2;
TGDispatchAfter(timeout, dispatch_get_main_queue(), ^{
[_eyedropperView dismiss];
});
}
- (UIColor *)colorFromData:(NSData *)data width:(NSInteger)width height:(NSInteger)height x:(NSInteger)x y:(NSInteger)y bpr:(NSInteger)bpr {
uint8_t *pixel = (uint8_t *)data.bytes + bpr * y + x * 4;
if (_eyedropperBackgroundInfo & kCGBitmapByteOrder32Little) {
return [UIColor colorWithRed:pixel[2] / 255.0 green:pixel[1] / 255.0 blue:pixel[0] / 255.0 alpha:1.0];
} else {
return [UIColor colorWithRed:pixel[0] / 255.0 green:pixel[1] / 255.0 blue:pixel[2] / 255.0 alpha:1.0];
}
}
- (UIColor *)colorAtPoint:(CGPoint)point
{
CGPoint convertedPoint = CGPointMake(point.x / _eyedropperView.bounds.size.width * _eyedropperBackgroundSize.width, point.y / _eyedropperView.bounds.size.height * _eyedropperBackgroundSize.height);
UIColor *backgroundColor = [self colorFromData:_eyedropperBackgroundData width:_eyedropperBackgroundSize.width height:_eyedropperBackgroundSize.height x:convertedPoint.x y:convertedPoint.y bpr:_eyedropperBackgroundBytesPerRow];
return backgroundColor;
}
#pragma mark - Entities
- (void)selectEntityView:(TGPhotoPaintEntityView *)view
{
if (_editedTextView != nil)
return;
if (_currentEntityView != nil)
{
if (_currentEntityView == view)
{
[self showMenuForEntityView];
return;
}
[self _clearCurrentSelection];
}
_currentEntityView = view;
[self updateSettingsButton];
_scrollView.pinchGestureRecognizer.enabled = _currentEntityView == nil;
if (view != nil)
{
[_currentEntityView.superview bringSubviewToFront:_currentEntityView];
}
else
{
[self hideMenu];
return;
}
if ([view isKindOfClass:[TGPhotoTextEntityView class]])
{
TGPaintSwatch *textSwatch = ((TGPhotoPaintTextEntity *)view.entity).swatch;
[self setCurrentSwatch:[TGPaintSwatch swatchWithColor:textSwatch.color colorLocation:textSwatch.colorLocation brushWeight:_portraitSettingsView.swatch.brushWeight] sender:nil];
}
_entitySelectionView = [view createSelectionView];
view.selectionView = _entitySelectionView;
[_selectionContainerView addSubview:_entitySelectionView];
__weak TGPhotoPaintController *weakSelf = self;
_entitySelectionView.entityResized = ^(CGFloat scale)
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf->_entitySelectionView.entityView scale:scale absolute:true];
};
_entitySelectionView.entityRotated = ^(CGFloat angle)
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf->_entitySelectionView.entityView rotate:angle absolute:true];
};
[_entitySelectionView update];
}
- (void)deleteEntityView:(TGPhotoPaintEntityView *)view
{
[_undoManager unregisterUndoWithUUID:view.entityUUID];
[view removeFromSuperview];
[self _clearCurrentSelection];
[self updateActionsView];
[self updateSettingsButton];
}
- (void)duplicateEntityView:(TGPhotoPaintEntityView *)view
{
TGPhotoPaintEntity *entity = [view.entity duplicate];
entity.position = [self startPositionRelativeToEntity:entity];
TGPhotoPaintEntityView *entityView = nil;
if ([entity isKindOfClass:[TGPhotoPaintStickerEntity class]])
{
TGPhotoStickerEntityView *stickerView = (TGPhotoStickerEntityView *)[_entitiesContainerView createEntityViewWithEntity:entity];
[self _commonEntityViewSetup:stickerView];
entityView = stickerView;
}
else
{
TGPhotoTextEntityView *textView = (TGPhotoTextEntityView *)[_entitiesContainerView createEntityViewWithEntity:entity];
[self _commonEntityViewSetup:textView];
entityView = textView;
}
[self selectEntityView:entityView];
[self _registerEntityRemovalUndo:entity];
[self updateActionsView];
}
- (void)editEntityView:(TGPhotoPaintEntityView *)view
{
if ([view isKindOfClass:[TGPhotoTextEntityView class]])
[(TGPhotoTextEntityView *)view beginEditing];
}
#pragma mark Menu
- (void)showMenuForEntityView
{
if (_menuContainerView != nil)
{
TGMenuContainerView *container = _menuContainerView;
bool isShowingMenu = container.isShowingMenu;
_menuContainerView = nil;
[container removeFromSuperview];
if (!isShowingMenu && container.menuView.userInfo[@"entity"] == _currentEntityView)
{
if ([_currentEntityView isKindOfClass:[TGPhotoTextEntityView class]])
[self editEntityView:_currentEntityView];
return;
}
}
UIView *parentView = self.view;
_menuContainerView = [[TGMenuContainerView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, parentView.frame.size.width, parentView.frame.size.height)];
[parentView addSubview:_menuContainerView];
NSArray *actions = nil;
if ([_currentEntityView isKindOfClass:[TGPhotoStickerEntityView class]])
{
actions = @
[
@{ @"title": TGLocalized(@"Paint.Delete"), @"action": @"delete" },
@{ @"title": TGLocalized(@"Paint.Duplicate"), @"action": @"duplicate" },
];
}
else
{
actions = @
[
@{ @"title": TGLocalized(@"Paint.Delete"), @"action": @"delete" },
@{ @"title": TGLocalized(@"Paint.Edit"), @"action": @"edit" },
@{ @"title": TGLocalized(@"Paint.Duplicate"), @"action": @"duplicate" },
];
}
[_menuContainerView.menuView setUserInfo:@{ @"entity": _currentEntityView }];
[_menuContainerView.menuView setButtonsAndActions:actions watcherHandle:_actionHandle];
[_menuContainerView.menuView sizeToFit];
CGRect sourceRect = CGRectOffset([_currentEntityView convertRect:_currentEntityView.bounds toView:_menuContainerView], 0, -15.0f);
[_menuContainerView showMenuFromRect:sourceRect animated:false];
}
- (void)hideMenu
{
[_menuContainerView hideMenu];
}
- (void)actionStageActionRequested:(NSString *)action options:(id)options
{
if ([action isEqualToString:@"menuAction"])
{
NSString *menuAction = options[@"action"];
TGPhotoPaintEntityView *entity = options[@"userInfo"][@"entity"];
if ([menuAction isEqualToString:@"delete"])
{
[self deleteEntityView:entity];
}
else if ([menuAction isEqualToString:@"duplicate"])
{
[self duplicateEntityView:entity];
}
else if ([menuAction isEqualToString:@"edit"])
{
[self editEntityView:entity];
}
}
else if ([action isEqualToString:@"menuWillHide"])
{
}
}
#pragma mark View
- (CGPoint)centerPointFittedCropRect
{
return [_previewView convertPoint:TGPaintCenterOfRect(_previewView.bounds) toView:_entitiesContainerView];
}
- (CGFloat)startRotation
{
return TGCounterRotationForOrientation(_photoEditor.cropOrientation) - _photoEditor.cropRotation;
}
- (CGPoint)startPositionRelativeToEntity:(TGPhotoPaintEntity *)entity
{
const CGPoint offset = CGPointMake(200.0f, 200.0f);
if (entity != nil)
{
return TGPaintAddPoints(entity.position, offset);
}
else
{
const CGFloat minimalDistance = 100.0f;
CGPoint position = [self centerPointFittedCropRect];
while (true)
{
bool occupied = false;
for (TGPhotoPaintEntityView *view in _entitiesContainerView.subviews)
{
if (![view isKindOfClass:[TGPhotoPaintEntityView class]])
continue;
CGPoint location = view.center;
CGFloat distance = sqrt(pow(location.x - position.x, 2) + pow(location.y - position.y, 2));
if (distance < minimalDistance)
occupied = true;
}
if (!occupied)
break;
else
position = TGPaintAddPoints(position, offset);
}
return position;
}
}
- (void)_commonEntityViewSetup:(TGPhotoPaintEntityView *)entityView
{
[self hideMenu];
__weak TGPhotoPaintController *weakSelf = self;
entityView.shouldTouchEntity = ^bool (__unused TGPhotoPaintEntityView *sender)
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return false;
return ![strongSelf->_canvasView isTracking] && ![strongSelf->_entitiesContainerView isTrackingAnyEntityView];
};
entityView.entityBeganDragging = ^(TGPhotoPaintEntityView *sender)
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf != nil && sender != strongSelf->_entitySelectionView.entityView)
[strongSelf selectEntityView:sender];
};
entityView.entityChanged = ^(TGPhotoPaintEntityView *sender)
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
if (sender == strongSelf->_entitySelectionView.entityView)
[strongSelf->_entitySelectionView update];
[strongSelf updateActionsView];
};
if ([entityView isKindOfClass:[TGPhotoTextEntityView class]]) {
TGPhotoTextEntityView *textView = (TGPhotoTextEntityView *)entityView;
__weak TGPhotoPaintController *weakSelf = self;
textView.beganEditing = ^(TGPhotoTextEntityView *sender)
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf bringTextEntityViewFront:sender];
};
textView.finishedEditing = ^(__unused TGPhotoTextEntityView *sender)
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf sendTextEntityViewBack];
};
}
}
- (void)_registerEntityRemovalUndo:(TGPhotoPaintEntity *)entity
{
[_undoManager registerUndoWithUUID:entity.uuid block:^(__unused TGPainting *painting, TGPhotoEntitiesContainerView *entitiesContainer, NSInteger uuid)
{
[entitiesContainer removeViewWithUUID:uuid];
}];
}
#pragma mark Stickers
- (void)presentStickersView
{
if (_stickersScreen != nil) {
[_stickersScreen restore];
return;
}
__weak TGPhotoPaintController *weakSelf = self;
_stickersScreen = _stickersContext.presentStickersController(^(id document, bool animated, UIView *view, CGRect rect) {
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf createNewStickerWithDocument:document animated:animated transitionPoint:CGPointZero snapshotView:nil];
}
});
_stickersScreen.screenDidAppear = ^{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf != nil) {
strongSelf.controlVideoPlayback(false);
[strongSelf->_entitiesContainerView updateVisibility:false];
}
};
_stickersScreen.screenWillDisappear = ^{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf != nil) {
strongSelf.controlVideoPlayback(true);
[strongSelf->_entitiesContainerView updateVisibility:true];
}
};
}
- (void)createNewStickerWithDocument:(id)document animated:(bool)animated transitionPoint:(CGPoint)transitionPoint snapshotView:(UIView *)snapshotView
{
TGPhotoPaintStickerEntity *entity = [[TGPhotoPaintStickerEntity alloc] initWithDocument:document baseSize:[self _stickerBaseSizeForCurrentPainting] animated:animated];
[self _setStickerEntityPosition:entity];
bool hasStickers = false;
for (TGPhotoPaintEntityView *view in _entitiesContainerView.subviews) {
if ([view isKindOfClass:[TGPhotoStickerEntityView class]]) {
hasStickers = true;
break;
}
}
TGPhotoStickerEntityView *stickerView = (TGPhotoStickerEntityView *)[_entitiesContainerView createEntityViewWithEntity:entity];
[self _commonEntityViewSetup:stickerView];
__weak TGPhotoPaintController *weakSelf = self;
__weak TGPhotoStickerEntityView *weakStickerView = stickerView;
stickerView.started = ^(double duration) {
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf != nil) {
TGPhotoEditorController *editorController = (TGPhotoEditorController *)self.parentViewController;
if (![editorController isKindOfClass:[TGPhotoEditorController class]])
return;
if (!hasStickers) {
[editorController setMinimalVideoDuration:duration];
}
NSTimeInterval currentTime = editorController.currentTime;
__strong TGPhotoStickerEntityView *strongStickerView = weakStickerView;
if (strongStickerView != nil) {
if (!isnan(currentTime)) {
[strongStickerView seekTo:currentTime];
[strongStickerView play];
}
}
}
};
[self selectEntityView:stickerView];
_entitySelectionView.alpha = 0.0f;
[_entitySelectionView fadeIn];
[self _registerEntityRemovalUndo:entity];
[self updateActionsView];
}
- (void)mirrorSelectedStickerEntity
{
if ([_currentEntityView isKindOfClass:[TGPhotoStickerEntityView class]])
[((TGPhotoStickerEntityView *)_currentEntityView) mirror];
}
#pragma mark Text
- (void)createNewTextLabel
{
TGPaintSwatch *currentSwatch = _portraitSettingsView.swatch;
TGPaintSwatch *whiteSwatch = [TGPaintSwatch swatchWithColor:[UIColor whiteColor] colorLocation:1.0f brushWeight:currentSwatch.brushWeight];
TGPaintSwatch *blackSwatch = [TGPaintSwatch swatchWithColor:[UIColor blackColor] colorLocation:0.85f brushWeight:currentSwatch.brushWeight];
[self setCurrentSwatch:_selectedTextStyle == TGPhotoPaintTextEntityStyleOutlined ? blackSwatch : whiteSwatch sender:nil];
CGFloat maxWidth = [self fittedContentSize].width - 26.0f;
TGPhotoPaintTextEntity *entity = [[TGPhotoPaintTextEntity alloc] initWithText:@"" font:_selectedTextFont swatch:_portraitSettingsView.swatch baseFontSize:[self _textBaseFontSizeForCurrentPainting] maxWidth:maxWidth style:_selectedTextStyle];
entity.position = [self startPositionRelativeToEntity:nil];
entity.angle = [self startRotation];
TGPhotoTextEntityView *textView = (TGPhotoTextEntityView *)[_entitiesContainerView createEntityViewWithEntity:entity];
[self _commonEntityViewSetup:textView];
[self selectEntityView:textView];
[self _registerEntityRemovalUndo:entity];
[self updateActionsView];
[textView beginEditing];
}
- (void)bringTextEntityViewFront:(TGPhotoTextEntityView *)entityView
{
_editedTextView = entityView;
entityView.inhibitGestures = true;
[_dimView.superview insertSubview:_dimView belowSubview:entityView];
_textEditingDismissButton = [[UIButton alloc] initWithFrame:_dimView.bounds];
_dimView.userInteractionEnabled = true;
[_textEditingDismissButton addTarget:self action:@selector(_dismissButtonTapped) forControlEvents:UIControlEventTouchUpInside];
[_dimView addSubview:_textEditingDismissButton];
_editedTextCenter = entityView.center;
_editedTextTransform = entityView.transform;
_entitySelectionView.alpha = 0.0f;
void (^changeBlock)(void) = ^
{
entityView.center = [self centerPointFittedCropRect];
entityView.transform = CGAffineTransformMakeRotation([self startRotation]);
_dimView.alpha = 1.0f;
};
_contentView.userInteractionEnabled = true;
_contentWrapperView.userInteractionEnabled = true;
if (iosMajorVersion() >= 7)
{
[UIView animateWithDuration:0.4 delay:0.0 usingSpringWithDamping:0.8f initialSpringVelocity:0.0f options:kNilOptions animations:changeBlock completion:nil];
}
else
{
[UIView animateWithDuration:0.35 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:changeBlock completion:nil];
}
[self setInterfaceHidden:true animated:true];
}
- (void)_dismissButtonTapped
{
TGPhotoTextEntityView *entityView = _editedTextView;
[entityView endEditing];
}
- (void)sendTextEntityViewBack
{
_contentView.userInteractionEnabled = false;
_contentWrapperView.userInteractionEnabled = false;
_dimView.userInteractionEnabled = false;
[_textEditingDismissButton removeFromSuperview];
_textEditingDismissButton = nil;
TGPhotoTextEntityView *entityView = _editedTextView;
_editedTextView = nil;
void (^changeBlock)(void) = ^
{
entityView.center = _editedTextCenter;
entityView.transform = _editedTextTransform;
_dimView.alpha = 0.0f;
};
void (^completionBlock)(BOOL) = ^(__unused BOOL finished)
{
[_dimView.superview bringSubviewToFront:_dimView];
entityView.inhibitGestures = false;
if (entityView.isEmpty)
{
[self deleteEntityView:entityView];
}
else
{
[_entitySelectionView update];
[_entitySelectionView fadeIn];
}
};
if (iosMajorVersion() >= 7)
{
[UIView animateWithDuration:0.4 delay:0.0 usingSpringWithDamping:0.8f initialSpringVelocity:0.0f options:kNilOptions animations:changeBlock completion:completionBlock];
}
else
{
[UIView animateWithDuration:0.35 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:changeBlock completion:completionBlock];
}
[self setInterfaceHidden:false animated:true];
TGMenuContainerView *container = _menuContainerView;
_menuContainerView = nil;
[container removeFromSuperview];
}
- (void)containerPressed
{
if (_currentEntityView == nil)
return;
if ([_currentEntityView isKindOfClass:[TGPhotoTextEntityView class]])
{
TGPhotoTextEntityView *textEntityView = (TGPhotoTextEntityView *)_currentEntityView;
if ([textEntityView isEditing])
{
[textEntityView endEditing];
return;
}
}
[self selectEntityView:nil];
}
#pragma mark - Relative Size Calculation
- (CGSize)_stickerBaseSizeForCurrentPainting
{
CGSize fittedSize = [self fittedContentSize];
CGFloat maxSide = MAX(fittedSize.width, fittedSize.height);
CGFloat side = ceil(maxSide * 0.3125f);
return CGSizeMake(side, side);
}
- (CGFloat)_textBaseFontSizeForCurrentPainting
{
CGSize fittedSize = [self fittedContentSize];
CGFloat maxSide = MAX(fittedSize.width, fittedSize.height);
return ceil(maxSide * 0.08f);
}
- (CGFloat)_brushBaseWeightForCurrentPainting
{
return 15.0f / TGPhotoPaintingMaxSize.width * _painting.size.width;
}
- (CGFloat)_brushWeightRangeForCurrentPainting
{
return 125.0f / TGPhotoPaintingMaxSize.width * _painting.size.width;
}
- (CGFloat)_brushWeightForSize:(CGFloat)size
{
return [self _brushBaseWeightForCurrentPainting] + [self _brushWeightRangeForCurrentPainting] * size;
}
+ (CGSize)maximumPaintingSize
{
static dispatch_once_t onceToken;
static CGSize size;
dispatch_once(&onceToken, ^
{
CGSize screenSize = TGScreenSize();
if ((NSInteger)screenSize.height == 480)
size = TGPhotoPaintingLightMaxSize;
else
size = TGPhotoPaintingMaxSize;
});
return size;
}
#pragma mark - Settings
- (void)setCurrentSwatch:(TGPaintSwatch *)swatch sender:(id)sender
{
[_canvasView setBrushColor:swatch.color];
[_canvasView setBrushWeight:[self _brushWeightForSize:swatch.brushWeight]];
if ([_currentEntityView isKindOfClass:[TGPhotoTextEntityView class]])
[(TGPhotoTextEntityView *)_currentEntityView setSwatch:swatch];
if (sender != _landscapeSettingsView)
[_landscapeSettingsView setSwatch:swatch];
if (sender != _portraitSettingsView)
[_portraitSettingsView setSwatch:swatch];
}
- (void)updateSettingsButton
{
if ([_currentEntityView isKindOfClass:[TGPhotoTextEntityView class]]) {
TGPhotoPaintSettingsViewIcon icon;
switch (((TGPhotoTextEntityView *)_currentEntityView).entity.style) {
case TGPhotoPaintTextEntityStyleRegular:
icon = TGPhotoPaintSettingsViewIconTextRegular;
break;
case TGPhotoPaintTextEntityStyleOutlined:
icon = TGPhotoPaintSettingsViewIconTextOutlined;
break;
case TGPhotoPaintTextEntityStyleFramed:
icon = TGPhotoPaintSettingsViewIconTextFramed;
break;
}
[self setSettingsButtonIcon:icon];
}
else if ([_currentEntityView isKindOfClass:[TGPhotoStickerEntityView class]]) {
[self setSettingsButtonIcon:TGPhotoPaintSettingsViewIconMirror];
}
else {
TGPhotoPaintSettingsViewIcon icon = TGPhotoPaintSettingsViewIconBrushPen;
if ([_canvasView.state.brush isKindOfClass:[TGPaintEllipticalBrush class]]) {
icon = TGPhotoPaintSettingsViewIconBrushMarker;
} else if ([_canvasView.state.brush isKindOfClass:[TGPaintNeonBrush class]]) {
icon = TGPhotoPaintSettingsViewIconBrushNeon;
} else if ([_canvasView.state.brush isKindOfClass:[TGPaintArrowBrush class]]) {
icon = TGPhotoPaintSettingsViewIconBrushArrow;
}
[self setSettingsButtonIcon:icon];
}
[self _updateTabs];
}
- (void)setSettingsButtonIcon:(TGPhotoPaintSettingsViewIcon)icon
{
[_portraitSettingsView setIcon:icon animated:true];
[_landscapeSettingsView setIcon:icon animated:true];
}
- (void)settingsWrapperPressed
{
[_settingsView dismissWithCompletion:^
{
[_settingsView removeFromSuperview];
_settingsView = nil;
[_settingsViewWrapper removeFromSuperview];
}];
}
- (UIView *)settingsViewWrapper
{
if (_settingsViewWrapper == nil)
{
_settingsViewWrapper = [[TGPhotoPaintSettingsWrapperView alloc] initWithFrame:self.parentViewController.view.bounds];
_settingsViewWrapper.exclusiveTouch = true;
__weak TGPhotoPaintController *weakSelf = self;
_settingsViewWrapper.pressed = ^(__unused CGPoint location)
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf settingsWrapperPressed];
};
_settingsViewWrapper.suppressTouchAtPoint = ^bool(CGPoint location)
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return false;
UIView *view = [strongSelf.view hitTest:[strongSelf.view convertPoint:location fromView:nil] withEvent:nil];
if ([view isKindOfClass:[TGModernButton class]])
return true;
if ([view isKindOfClass:[TGPaintCanvas class]])
return true;
if (view == strongSelf->_portraitToolsWrapperView || view == strongSelf->_landscapeToolsWrapperView)
return true;
return false;
};
}
[self.parentViewController.view addSubview:_settingsViewWrapper];
return _settingsViewWrapper;
}
- (TGPaintBrushPreview *)brushPreview
{
if ([_brushes.firstObject previewImage] != nil)
return nil;
if (_brushPreview == nil)
_brushPreview = [[TGPaintBrushPreview alloc] init];
return _brushPreview;
}
- (void)presentBrushSettingsView
{
TGPhotoBrushSettingsView *view = [[TGPhotoBrushSettingsView alloc] initWithBrushes:_brushes preview:[self brushPreview]];
[view setBrush:_painting.brush];
__weak TGPhotoPaintController *weakSelf = self;
view.brushChanged = ^(TGPaintBrush *brush)
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
if (strongSelf->_canvasView.state.eraser && (brush.lightSaber || brush.arrow))
brush = strongSelf->_brushes.firstObject;
[strongSelf->_canvasView setBrush:brush];
[strongSelf settingsWrapperPressed];
[strongSelf updateSettingsButton];
};
_settingsView = view;
[view sizeToFit];
UIView *wrapper = [self settingsViewWrapper];
wrapper.userInteractionEnabled = true;
[wrapper addSubview:view];
[self viewWillLayoutSubviews];
[view present];
}
- (void)presentTextSettingsView
{
TGPhotoTextSettingsView *view = [[TGPhotoTextSettingsView alloc] initWithFonts:[TGPhotoPaintFont availableFonts] selectedFont:_selectedTextFont selectedStyle:_selectedTextStyle];
__weak TGPhotoPaintController *weakSelf = self;
view.fontChanged = ^(TGPhotoPaintFont *font)
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
strongSelf->_selectedTextFont = font;
TGPhotoTextEntityView *textView = (TGPhotoTextEntityView *)strongSelf->_currentEntityView;
[textView setFont:font];
[strongSelf settingsWrapperPressed];
[strongSelf updateSettingsButton];
};
view.styleChanged = ^(TGPhotoPaintTextEntityStyle style)
{
__strong TGPhotoPaintController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
strongSelf->_selectedTextStyle = style;
if (style == TGPhotoPaintTextEntityStyleOutlined && [strongSelf->_portraitSettingsView.swatch.color isEqual:[UIColor whiteColor]])
{
TGPaintSwatch *currentSwatch = strongSelf->_portraitSettingsView.swatch;
TGPaintSwatch *blackSwatch = [TGPaintSwatch swatchWithColor:[UIColor blackColor] colorLocation:0.85f brushWeight:currentSwatch.brushWeight];
[strongSelf setCurrentSwatch:blackSwatch sender:nil];
}
else if (style != TGPhotoPaintTextEntityStyleOutlined && [strongSelf->_portraitSettingsView.swatch.color isEqual:UIColorRGB(0x000000)])
{
TGPaintSwatch *currentSwatch = strongSelf->_portraitSettingsView.swatch;
TGPaintSwatch *whiteSwatch = [TGPaintSwatch swatchWithColor:[UIColor whiteColor] colorLocation:1.0f brushWeight:currentSwatch.brushWeight];
[strongSelf setCurrentSwatch:whiteSwatch sender:nil];
}
TGPhotoTextEntityView *textView = (TGPhotoTextEntityView *)strongSelf->_currentEntityView;
[textView setStyle:style];
[strongSelf settingsWrapperPressed];
[strongSelf updateSettingsButton];
};
_settingsView = view;
[view sizeToFit];
UIView *wrapper = [self settingsViewWrapper];
wrapper.userInteractionEnabled = true;
[wrapper addSubview:view];
[self viewWillLayoutSubviews];
[view present];
}
- (void)toggleEraserMode
{
_canvasView.state.eraser = !_canvasView.state.isEraser;
if (_canvasView.state.eraser)
{
if (_canvasView.state.brush.lightSaber || _canvasView.state.brush.arrow)
[_canvasView setBrush:_brushes.firstObject];
}
[_portraitSettingsView setHighlighted:_canvasView.state.isEraser];
[_landscapeSettingsView setHighlighted:_canvasView.state.isEraser];
[self updateSettingsButton];
[self _updateTabs];
}
#pragma mark - Scroll View
- (CGSize)fittedContentSize
{
return [TGPhotoPaintController fittedContentSize:_photoEditor.cropRect orientation:_photoEditor.cropOrientation originalSize:_photoEditor.originalSize];
}
+ (CGSize)fittedContentSize:(CGRect)cropRect orientation:(UIImageOrientation)orientation originalSize:(CGSize)originalSize {
CGSize fittedOriginalSize = TGScaleToSize(originalSize, [TGPhotoPaintController maximumPaintingSize]);
CGFloat scale = fittedOriginalSize.width / originalSize.width;
CGSize size = CGSizeMake(cropRect.size.width * scale, cropRect.size.height * scale);
if (orientation == UIImageOrientationLeft || orientation == UIImageOrientationRight)
size = CGSizeMake(size.height, size.width);
return CGSizeMake(floor(size.width), floor(size.height));
}
- (CGRect)fittedCropRect:(bool)originalSize
{
return [TGPhotoPaintController fittedCropRect:_photoEditor.cropRect originalSize:_photoEditor.originalSize keepOriginalSize:originalSize];
}
+ (CGRect)fittedCropRect:(CGRect)cropRect originalSize:(CGSize)originalSize keepOriginalSize:(bool)keepOriginalSize {
CGSize fittedOriginalSize = TGScaleToSize(originalSize, [TGPhotoPaintController maximumPaintingSize]);
CGFloat scale = fittedOriginalSize.width / originalSize.width;
CGSize size = fittedOriginalSize;
if (!keepOriginalSize)
size = CGSizeMake(cropRect.size.width * scale, cropRect.size.height * scale);
return CGRectMake(-cropRect.origin.x * scale, -cropRect.origin.y * scale, size.width, size.height);
}
- (CGPoint)fittedCropCenterScale:(CGFloat)scale
{
return [TGPhotoPaintController fittedCropRect:_photoEditor.cropRect centerScale:scale];
}
+ (CGPoint)fittedCropRect:(CGRect)cropRect centerScale:(CGFloat)scale
{
CGSize size = CGSizeMake(cropRect.size.width * scale, cropRect.size.height * scale);
CGRect rect = CGRectMake(cropRect.origin.x * scale, cropRect.origin.y * scale, size.width, size.height);
return CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
}
- (void)resetScrollView
{
CGSize fittedContentSize = [self fittedContentSize];
CGRect fittedCropRect = [self fittedCropRect:false];
_contentWrapperView.frame = CGRectMake(0.0f, 0.0f, fittedContentSize.width, fittedContentSize.height);
CGFloat scale = _contentView.bounds.size.width / fittedCropRect.size.width;
_contentWrapperView.transform = CGAffineTransformMakeScale(scale, scale);
_contentWrapperView.frame = CGRectMake(0.0f, 0.0f, _contentView.bounds.size.width, _contentView.bounds.size.height);
CGSize contentSize = [self contentSize];
_scrollView.minimumZoomScale = 1.0f;
_scrollView.maximumZoomScale = 1.0f;
_scrollView.normalZoomScale = 1.0f;
_scrollView.zoomScale = 1.0f;
_scrollView.contentSize = contentSize;
[self contentView].frame = CGRectMake(0.0f, 0.0f, contentSize.width, contentSize.height);
[self adjustZoom];
_scrollView.zoomScale = _scrollView.normalZoomScale;
}
- (void)scrollViewWillBeginZooming:(UIScrollView *)__unused scrollView withView:(UIView *)__unused view
{
}
- (void)scrollViewDidZoom:(UIScrollView *)__unused scrollView
{
[self adjustZoom];
}
- (void)scrollViewDidEndZooming:(UIScrollView *)__unused scrollView withView:(UIView *)__unused view atScale:(CGFloat)__unused scale
{
[self adjustZoom];
if (_scrollView.zoomScale < _scrollView.normalZoomScale - FLT_EPSILON)
{
[TGHacks setAnimationDurationFactor:0.5f];
[_scrollView setZoomScale:_scrollView.normalZoomScale animated:true];
[TGHacks setAnimationDurationFactor:1.0f];
}
}
- (UIView *)contentView
{
return _scrollContentView;
}
- (CGSize)contentSize
{
return _scrollView.frame.size;
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)__unused scrollView
{
return [self contentView];
}
- (void)adjustZoom
{
CGSize contentSize = [self contentSize];
CGSize boundsSize = _scrollView.frame.size;
if (contentSize.width < FLT_EPSILON || contentSize.height < FLT_EPSILON || boundsSize.width < FLT_EPSILON || boundsSize.height < FLT_EPSILON)
return;
CGFloat scaleWidth = boundsSize.width / contentSize.width;
CGFloat scaleHeight = boundsSize.height / contentSize.height;
CGFloat minScale = MIN(scaleWidth, scaleHeight);
CGFloat maxScale = MAX(scaleWidth, scaleHeight);
maxScale = MAX(maxScale, minScale * 3.0f);
if (ABS(maxScale - minScale) < 0.01f)
maxScale = minScale;
_scrollView.contentInset = UIEdgeInsetsZero;
if (_scrollView.minimumZoomScale != 0.05f)
_scrollView.minimumZoomScale = 0.05f;
if (_scrollView.normalZoomScale != minScale)
_scrollView.normalZoomScale = minScale;
if (_scrollView.maximumZoomScale != maxScale)
_scrollView.maximumZoomScale = maxScale;
CGRect contentFrame = [self contentView].frame;
if (boundsSize.width > contentFrame.size.width)
contentFrame.origin.x = (boundsSize.width - contentFrame.size.width) / 2.0f;
else
contentFrame.origin.x = 0;
if (boundsSize.height > contentFrame.size.height)
contentFrame.origin.y = (boundsSize.height - contentFrame.size.height) / 2.0f;
else
contentFrame.origin.y = 0;
[self contentView].frame = contentFrame;
_scrollView.scrollEnabled = ABS(_scrollView.zoomScale - _scrollView.normalZoomScale) > FLT_EPSILON;
}
#pragma mark - Gestures
- (void)handlePinch:(UIPinchGestureRecognizer *)gestureRecognizer
{
[_entitiesContainerView handlePinch:gestureRecognizer];
}
- (void)handleRotate:(UIRotationGestureRecognizer *)gestureRecognizer
{
[_entitiesContainerView handleRotate:gestureRecognizer];
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)__unused gestureRecognizer
{
if (gestureRecognizer == _pinchGestureRecognizer && _currentEntityView == nil) {
return false;
}
return !_canvasView.isTracking;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)__unused gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)__unused otherGestureRecognizer
{
return true;
}
#pragma mark - Transitions
- (void)transitionIn
{
_portraitSettingsView.layer.shouldRasterize = true;
_landscapeSettingsView.layer.shouldRasterize = true;
[UIView animateWithDuration:0.3f animations:^
{
_portraitToolsWrapperView.alpha = 1.0f;
_landscapeToolsWrapperView.alpha = 1.0f;
_portraitActionsView.alpha = 1.0f;
_landscapeActionsView.alpha = 1.0f;
} completion:^(__unused BOOL finished)
{
_portraitSettingsView.layer.shouldRasterize = false;
_landscapeSettingsView.layer.shouldRasterize = false;
}];
if (self.presentedForAvatarCreation) {
_canvasView.hidden = true;
}
}
+ (CGRect)photoContainerFrameForParentViewFrame:(CGRect)parentViewFrame toolbarLandscapeSize:(CGFloat)toolbarLandscapeSize orientation:(UIInterfaceOrientation)orientation panelSize:(CGFloat)panelSize hasOnScreenNavigation:(bool)hasOnScreenNavigation
{
CGRect frame = [TGPhotoEditorTabController photoContainerFrameForParentViewFrame:parentViewFrame toolbarLandscapeSize:toolbarLandscapeSize orientation:orientation panelSize:panelSize hasOnScreenNavigation:hasOnScreenNavigation];
switch (orientation)
{
case UIInterfaceOrientationLandscapeLeft:
frame.origin.x -= TGPhotoPaintTopPanelSize;
break;
case UIInterfaceOrientationLandscapeRight:
frame.origin.x += TGPhotoPaintTopPanelSize;
break;
default:
frame.origin.y += TGPhotoPaintTopPanelSize;
break;
}
return frame;
}
- (CGRect)_targetFrameForTransitionInFromFrame:(CGRect)fromFrame
{
CGSize referenceSize = [self referenceViewSize];
CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:self.effectiveOrientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation];
CGSize fittedSize = TGScaleToSize(fromFrame.size, containerFrame.size);
CGRect toFrame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height);
return toFrame;
}
- (void)_finishedTransitionInWithView:(UIView *)transitionView
{
_appeared = true;
if ([transitionView isKindOfClass:[TGPhotoEditorPreviewView class]]) {
} else {
[transitionView removeFromSuperview];
}
[self setupCanvas];
_entitiesContainerView.hidden = false;
TGPhotoEditorPreviewView *previewView = _previewView;
[previewView setPaintingHidden:true];
previewView.hidden = false;
[_containerView insertSubview:previewView belowSubview:_paintingWrapperView];
[self updateContentViewLayout];
[previewView performTransitionInIfNeeded];
CGRect rect = [self fittedCropRect:true];
_entitiesContainerView.frame = CGRectMake(0, 0, rect.size.width, rect.size.height);
_entitiesContainerView.transform = CGAffineTransformMakeRotation(_photoEditor.cropRotation);
CGSize fittedOriginalSize = TGScaleToSize(_photoEditor.originalSize, [TGPhotoPaintController maximumPaintingSize]);
CGSize rotatedSize = TGRotatedContentSize(fittedOriginalSize, _photoEditor.cropRotation);
CGPoint centerPoint = CGPointMake(rotatedSize.width / 2.0f, rotatedSize.height / 2.0f);
CGFloat scale = fittedOriginalSize.width / _photoEditor.originalSize.width;
CGPoint offset = TGPaintSubtractPoints(centerPoint, [self fittedCropCenterScale:scale]);
CGPoint boundsCenter = TGPaintCenterOfRect(_contentWrapperView.bounds);
_entitiesContainerView.center = TGPaintAddPoints(boundsCenter, offset);
if (!_skipEntitiesSetup || _entitiesReady) {
[_contentWrapperView addSubview:_entitiesContainerView];
}
_entitiesReady = true;
[self resetScrollView];
}
- (void)prepareForCustomTransitionOut
{
_previewView.hidden = true;
_canvasView.hidden = true;
_contentView.hidden = true;
[UIView animateWithDuration:0.3f animations:^
{
_portraitToolsWrapperView.alpha = 0.0f;
_landscapeToolsWrapperView.alpha = 0.0f;
} completion:nil];
}
- (void)transitionOutSwitching:(bool)__unused switching completion:(void (^)(void))completion
{
[_stickersScreen invalidate];
TGPhotoEditorPreviewView *previewView = self.previewView;
previewView.interactionEnded = nil;
_portraitSettingsView.layer.shouldRasterize = true;
_landscapeSettingsView.layer.shouldRasterize = true;
[UIView animateWithDuration:0.3f animations:^
{
_portraitToolsWrapperView.alpha = 0.0f;
_landscapeToolsWrapperView.alpha = 0.0f;
_portraitActionsView.alpha = 0.0f;
_landscapeActionsView.alpha = 0.0f;
} completion:^(__unused BOOL finished)
{
if (completion != nil)
completion();
}];
}
- (CGRect)transitionOutSourceFrameForReferenceFrame:(CGRect)referenceFrame orientation:(UIInterfaceOrientation)orientation
{
CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:self.view.frame toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation];
CGSize fittedSize = TGScaleToSize(referenceFrame.size, containerFrame.size);
return CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height);
}
- (void)_animatePreviewViewTransitionOutToFrame:(CGRect)targetFrame saving:(bool)saving parentView:(UIView *)parentView completion:(void (^)(void))completion
{
_dismissing = true;
[_entitySelectionView removeFromSuperview];
_entitySelectionView = nil;
TGPhotoEditorPreviewView *previewView = self.previewView;
[previewView prepareForTransitionOut];
UIInterfaceOrientation orientation = self.effectiveOrientation;
CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:self.view.frame toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation];
CGRect referenceFrame = CGRectMake(0, 0, self.photoEditor.rotatedCropSize.width, self.photoEditor.rotatedCropSize.height);
CGRect rect = CGRectOffset([self transitionOutSourceFrameForReferenceFrame:referenceFrame orientation:orientation], -containerFrame.origin.x, -containerFrame.origin.y);
previewView.frame = rect;
UIView *snapshotView = nil;
POPSpringAnimation *snapshotAnimation = nil;
NSMutableArray *animations = [[NSMutableArray alloc] init];
if (saving && CGRectIsNull(targetFrame) && parentView != nil)
{
snapshotView = [previewView snapshotViewAfterScreenUpdates:false];
snapshotView.frame = [_containerView convertRect:previewView.frame toView:parentView];
UIView *canvasSnapshotView = [_paintingWrapperView resizableSnapshotViewFromRect:[_paintingWrapperView convertRect:previewView.bounds fromView:previewView] afterScreenUpdates:false withCapInsets:UIEdgeInsetsZero];
canvasSnapshotView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
canvasSnapshotView.transform = _contentView.transform;
canvasSnapshotView.frame = snapshotView.bounds;
[snapshotView addSubview:canvasSnapshotView];
UIView *entitiesSnapshotView = [_contentWrapperView resizableSnapshotViewFromRect:[_contentWrapperView convertRect:previewView.bounds fromView:previewView] afterScreenUpdates:false withCapInsets:UIEdgeInsetsZero];
entitiesSnapshotView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
entitiesSnapshotView.transform = _contentView.transform;
entitiesSnapshotView.frame = snapshotView.bounds;
[snapshotView addSubview:entitiesSnapshotView];
CGSize fittedSize = TGScaleToSize(previewView.frame.size, self.view.frame.size);
targetFrame = CGRectMake((self.view.frame.size.width - fittedSize.width) / 2, (self.view.frame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height);
[parentView addSubview:snapshotView];
snapshotAnimation = [TGPhotoEditorAnimation prepareTransitionAnimationForPropertyNamed:kPOPViewFrame];
snapshotAnimation.fromValue = [NSValue valueWithCGRect:snapshotView.frame];
snapshotAnimation.toValue = [NSValue valueWithCGRect:targetFrame];
[animations addObject:snapshotAnimation];
}
targetFrame = CGRectOffset(targetFrame, -containerFrame.origin.x, -containerFrame.origin.y);
CGPoint targetCenter = TGPaintCenterOfRect(targetFrame);
POPSpringAnimation *previewAnimation = [TGPhotoEditorAnimation prepareTransitionAnimationForPropertyNamed:kPOPViewFrame];
previewAnimation.fromValue = [NSValue valueWithCGRect:previewView.frame];
previewAnimation.toValue = [NSValue valueWithCGRect:targetFrame];
[animations addObject:previewAnimation];
POPSpringAnimation *previewAlphaAnimation = [TGPhotoEditorAnimation prepareTransitionAnimationForPropertyNamed:kPOPViewAlpha];
previewAlphaAnimation.fromValue = @(previewView.alpha);
previewAlphaAnimation.toValue = @(0.0f);
[animations addObject:previewAnimation];
POPSpringAnimation *entitiesAnimation = [TGPhotoEditorAnimation prepareTransitionAnimationForPropertyNamed:kPOPViewCenter];
entitiesAnimation.fromValue = [NSValue valueWithCGPoint:_contentView.center];
entitiesAnimation.toValue = [NSValue valueWithCGPoint:targetCenter];
[animations addObject:entitiesAnimation];
CGFloat targetEntitiesScale = targetFrame.size.width / _contentView.frame.size.width;
POPSpringAnimation *entitiesScaleAnimation = [TGPhotoEditorAnimation prepareTransitionAnimationForPropertyNamed:kPOPViewScaleXY];
entitiesScaleAnimation.fromValue = [NSValue valueWithCGSize:CGSizeMake(1.0f, 1.0f)];
entitiesScaleAnimation.toValue = [NSValue valueWithCGSize:CGSizeMake(targetEntitiesScale, targetEntitiesScale)];
[animations addObject:entitiesScaleAnimation];
POPSpringAnimation *entitiesAlphaAnimation = [TGPhotoEditorAnimation prepareTransitionAnimationForPropertyNamed:kPOPViewAlpha];
entitiesAlphaAnimation.fromValue = @(_canvasView.alpha);
entitiesAlphaAnimation.toValue = @(0.0f);
[animations addObject:entitiesAlphaAnimation];
POPSpringAnimation *paintingAnimation = [TGPhotoEditorAnimation prepareTransitionAnimationForPropertyNamed:kPOPViewCenter];
paintingAnimation.fromValue = [NSValue valueWithCGPoint:_paintingWrapperView.center];
paintingAnimation.toValue = [NSValue valueWithCGPoint:targetCenter];
[animations addObject:paintingAnimation];
CGFloat targetPaintingScale = targetFrame.size.width / _paintingWrapperView.frame.size.width;
POPSpringAnimation *paintingScaleAnimation = [TGPhotoEditorAnimation prepareTransitionAnimationForPropertyNamed:kPOPViewScaleXY];
paintingScaleAnimation.fromValue = [NSValue valueWithCGSize:CGSizeMake(1.0f, 1.0f)];
paintingScaleAnimation.toValue = [NSValue valueWithCGSize:CGSizeMake(targetPaintingScale, targetPaintingScale)];
[animations addObject:paintingScaleAnimation];
POPSpringAnimation *paintingAlphaAnimation = [TGPhotoEditorAnimation prepareTransitionAnimationForPropertyNamed:kPOPViewAlpha];
paintingAlphaAnimation.fromValue = @(_paintingWrapperView.alpha);
paintingAlphaAnimation.toValue = @(0.0f);
[animations addObject:paintingAlphaAnimation];
[TGPhotoEditorAnimation performBlock:^(__unused bool allFinished)
{
[snapshotView removeFromSuperview];
if (completion != nil)
completion();
} whenCompletedAllAnimations:animations];
if (snapshotAnimation != nil)
[snapshotView pop_addAnimation:snapshotAnimation forKey:@"frame"];
[previewView pop_addAnimation:previewAnimation forKey:@"frame"];
[previewView pop_addAnimation:previewAlphaAnimation forKey:@"alpha"];
[_contentView pop_addAnimation:entitiesAnimation forKey:@"frame"];
[_contentView pop_addAnimation:entitiesScaleAnimation forKey:@"scale"];
[_contentView pop_addAnimation:entitiesAlphaAnimation forKey:@"alpha"];
[_paintingWrapperView pop_addAnimation:paintingAnimation forKey:@"frame"];
[_paintingWrapperView pop_addAnimation:paintingScaleAnimation forKey:@"scale"];
[_paintingWrapperView pop_addAnimation:paintingAlphaAnimation forKey:@"alpha"];
if (saving)
{
_contentView.hidden = true;
_paintingWrapperView.hidden = true;
previewView.hidden = true;
}
}
- (CGRect)transitionOutReferenceFrame
{
TGPhotoEditorPreviewView *previewView = _previewView;
return [previewView convertRect:previewView.bounds toView:self.view];
}
- (UIView *)transitionOutReferenceView
{
return _previewView;
}
- (UIView *)snapshotView
{
TGPhotoEditorPreviewView *previewView = self.previewView;
return [previewView originalSnapshotView];
}
- (void)setInterfaceHidden:(bool)hidden animated:(bool)animated
{
CGFloat targetAlpha = hidden ? 0.0f : 1.0;
void (^changeBlock)(void) = ^
{
_portraitActionsView.alpha = targetAlpha;
_landscapeActionsView.alpha = targetAlpha;
_portraitSettingsView.alpha = targetAlpha;
_landscapeSettingsView.alpha = targetAlpha;
};
if (animated)
[UIView animateWithDuration:0.25 animations:changeBlock];
else
changeBlock();
TGPhotoEditorController *editorController = (TGPhotoEditorController *)self.parentViewController;
if (![editorController isKindOfClass:[TGPhotoEditorController class]])
return;
[editorController setToolbarHidden:hidden animated:animated];
}
- (void)setDimHidden:(bool)hidden animated:(bool)animated
{
if (!hidden)
{
[_entitySelectionView fadeOut];
if ([_currentEntityView isKindOfClass:[TGPhotoTextEntityView class]])
[_dimView.superview insertSubview:_dimView belowSubview:_currentEntityView];
else
[_dimView.superview bringSubviewToFront:_dimView];
[_doneButton.superview bringSubviewToFront:_doneButton];
}
else
{
[_entitySelectionView fadeIn];
[_dimView.superview bringSubviewToFront:_dimView];
[_doneButton.superview bringSubviewToFront:_doneButton];
}
void (^changeBlock)(void) = ^
{
_dimView.alpha = hidden ? 0.0f : 1.0f;
_doneButton.alpha = hidden ? 0.0f : 1.0f;
};
if (animated)
[UIView animateWithDuration:0.25 animations:changeBlock];
else
changeBlock();
}
- (id)currentResultRepresentation
{
return TGPaintCombineCroppedImages(self.photoEditor.currentResultImage, [self image], true, _photoEditor.originalSize, _photoEditor.cropRect, _photoEditor.cropOrientation, _photoEditor.cropRotation, false);
}
#pragma mark - Layout
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
[self updateLayout:[[LegacyComponentsGlobals provider] applicationStatusBarOrientation]];
[_entitySelectionView update];
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
if (_menuContainerView != nil)
{
[_menuContainerView removeFromSuperview];
_menuContainerView = nil;
}
[self updateLayout:toInterfaceOrientation];
}
- (void)updateContentViewLayout
{
CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(TGRotationForOrientation(_photoEditor.cropOrientation));
_contentView.transform = rotationTransform;
_contentView.frame = self.previewView.frame;
[self resetScrollView];
}
- (void)updateLayout:(UIInterfaceOrientation)orientation
{
if ([self inFormSheet] || [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
{
_landscapeToolsWrapperView.hidden = true;
orientation = UIInterfaceOrientationPortrait;
}
CGSize referenceSize = [self referenceViewSize];
CGFloat screenSide = MAX(referenceSize.width, referenceSize.height) + 2 * TGPhotoPaintBottomPanelSize;
bool sizeUpdated = false;
if (!CGSizeEqualToSize(referenceSize, _previousSize)) {
sizeUpdated = true;
_previousSize = referenceSize;
}
CGFloat panelToolbarPortraitSize = TGPhotoPaintBottomPanelSize + TGPhotoEditorToolbarSize;
CGFloat panelToolbarLandscapeSize = TGPhotoPaintBottomPanelSize + self.toolbarLandscapeSize;
UIEdgeInsets safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:self.hasOnScreenNavigation];
UIEdgeInsets screenEdges = UIEdgeInsetsMake((screenSide - referenceSize.height) / 2, (screenSide - referenceSize.width) / 2, (screenSide + referenceSize.height) / 2, (screenSide + referenceSize.width) / 2);
screenEdges.top += safeAreaInset.top;
screenEdges.left += safeAreaInset.left;
screenEdges.bottom -= safeAreaInset.bottom;
screenEdges.right -= safeAreaInset.right;
CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation];
_settingsViewWrapper.frame = self.parentViewController.view.bounds;
_doneButton.frame = CGRectMake(screenEdges.right - _doneButton.frame.size.width - 8.0, screenEdges.top + 2.0, _doneButton.frame.size.width, _doneButton.frame.size.height);
if (_settingsView != nil)
[_settingsView setInterfaceOrientation:orientation];
switch (orientation)
{
case UIInterfaceOrientationLandscapeLeft:
{
_landscapeSettingsView.interfaceOrientation = orientation;
[UIView performWithoutAnimation:^
{
_landscapeToolsWrapperView.frame = CGRectMake(0, screenEdges.top, panelToolbarLandscapeSize, _landscapeToolsWrapperView.frame.size.height);
_landscapeSettingsView.frame = CGRectMake(panelToolbarLandscapeSize - TGPhotoPaintBottomPanelSize, 0, TGPhotoPaintBottomPanelSize, _landscapeSettingsView.frame.size.height);
}];
_landscapeToolsWrapperView.frame = CGRectMake(screenEdges.left, screenEdges.top, panelToolbarLandscapeSize, referenceSize.height);
_landscapeSettingsView.frame = CGRectMake(_landscapeSettingsView.frame.origin.x, _landscapeSettingsView.frame.origin.y, _landscapeSettingsView.frame.size.width, _landscapeToolsWrapperView.frame.size.height);
_portraitToolsWrapperView.frame = CGRectMake(screenEdges.left, screenSide - panelToolbarPortraitSize, referenceSize.width, panelToolbarPortraitSize);
_portraitSettingsView.frame = CGRectMake(0, 0, _portraitToolsWrapperView.frame.size.width, TGPhotoPaintBottomPanelSize);
_landscapeActionsView.frame = CGRectMake(screenEdges.right - TGPhotoPaintTopPanelSize, screenEdges.top, TGPhotoPaintTopPanelSize, referenceSize.height);
_settingsView.frame = CGRectMake(self.toolbarLandscapeSize + 50.0f + safeAreaInset.left, 0.0f, _settingsView.frame.size.width, _settingsView.frame.size.height);
}
break;
case UIInterfaceOrientationLandscapeRight:
{
_landscapeSettingsView.interfaceOrientation = orientation;
[UIView performWithoutAnimation:^
{
_landscapeToolsWrapperView.frame = CGRectMake(screenSide - panelToolbarLandscapeSize, screenEdges.top, panelToolbarLandscapeSize, _landscapeToolsWrapperView.frame.size.height);
_landscapeSettingsView.frame = CGRectMake(0, 0, TGPhotoPaintBottomPanelSize, _landscapeSettingsView.frame.size.height);
}];
_landscapeToolsWrapperView.frame = CGRectMake(screenEdges.right - panelToolbarLandscapeSize, screenEdges.top, panelToolbarLandscapeSize, referenceSize.height);
_landscapeSettingsView.frame = CGRectMake(_landscapeSettingsView.frame.origin.x, _landscapeSettingsView.frame.origin.y, _landscapeSettingsView.frame.size.width, _landscapeToolsWrapperView.frame.size.height);
_portraitToolsWrapperView.frame = CGRectMake(screenEdges.top, screenSide - panelToolbarPortraitSize, referenceSize.width, panelToolbarPortraitSize);
_portraitSettingsView.frame = CGRectMake(0, 0, _portraitToolsWrapperView.frame.size.width, TGPhotoPaintBottomPanelSize);
_landscapeActionsView.frame = CGRectMake(screenEdges.left, screenEdges.top, TGPhotoPaintTopPanelSize, referenceSize.height);
_settingsView.frame = CGRectMake(_settingsViewWrapper.frame.size.width - _settingsView.frame.size.width - self.toolbarLandscapeSize - 50.0f - safeAreaInset.right, 0.0f, _settingsView.frame.size.width, _settingsView.frame.size.height);
}
break;
default:
{
CGFloat x = _landscapeToolsWrapperView.frame.origin.x;
if (x < screenSide / 2)
x = 0;
else
x = screenSide - TGPhotoEditorPanelSize;
_landscapeToolsWrapperView.frame = CGRectMake(x, screenEdges.top, panelToolbarLandscapeSize, referenceSize.height);
_portraitToolsWrapperView.frame = CGRectMake(screenEdges.left, screenEdges.bottom - panelToolbarPortraitSize, referenceSize.width, panelToolbarPortraitSize);
_portraitSettingsView.frame = CGRectMake(0, 0, referenceSize.width, TGPhotoPaintBottomPanelSize);
_portraitActionsView.frame = CGRectMake(screenEdges.left, screenEdges.top, referenceSize.width, TGPhotoPaintTopPanelSize);
if ([_context currentSizeClass] == UIUserInterfaceSizeClassRegular)
{
_settingsView.frame = CGRectMake(_settingsViewWrapper.frame.size.width / 2.0f - 10.0f, _settingsViewWrapper.frame.size.height - _settingsView.frame.size.height - TGPhotoEditorToolbarSize - 50.0f, _settingsView.frame.size.width, _settingsView.frame.size.height);
}
else
{
_settingsView.frame = CGRectMake(_settingsViewWrapper.frame.size.width - _settingsView.frame.size.width, _settingsViewWrapper.frame.size.height - _settingsView.frame.size.height - TGPhotoEditorToolbarSize - 50.0f - safeAreaInset.bottom, _settingsView.frame.size.width, _settingsView.frame.size.height);
}
}
break;
}
PGPhotoEditor *photoEditor = self.photoEditor;
TGPhotoEditorPreviewView *previewView = self.previewView;
CGSize fittedSize = TGScaleToSize(photoEditor.rotatedCropSize, containerFrame.size);
CGRect previewFrame = CGRectMake((containerFrame.size.width - fittedSize.width) / 2, (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height);
CGFloat visibleArea = self.view.frame.size.height - _keyboardHeight;
CGFloat yCenter = visibleArea / 2.0f;
CGFloat offset = yCenter - _previewView.center.y - containerFrame.origin.y;
CGFloat offsetHeight = _keyboardHeight > FLT_EPSILON ? offset : 0.0f;
_wrapperView.frame = CGRectMake((referenceSize.width - screenSide) / 2, (referenceSize.height - screenSide) / 2 + offsetHeight, screenSide, screenSide);
if (_dismissing || (previewView.superview != _containerView && previewView.superview != self.view))
return;
if (previewView.superview == self.view)
{
previewFrame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height);
}
UIImageOrientation cropOrientation = _photoEditor.cropOrientation;
CGRect cropRect = _photoEditor.cropRect;
CGSize originalSize = _photoEditor.originalSize;
CGFloat rotation = _photoEditor.cropRotation;
CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(TGRotationForOrientation(cropOrientation));
_contentView.transform = rotationTransform;
_contentView.frame = previewFrame;
_scrollView.frame = self.view.bounds;
if (sizeUpdated) {
[self resetScrollView];
}
[self adjustZoom];
_paintingWrapperView.transform = CGAffineTransformMakeRotation(TGRotationForOrientation(cropOrientation));
_paintingWrapperView.frame = previewFrame;
CGFloat originalWidth = TGOrientationIsSideward(cropOrientation, NULL) ? previewFrame.size.height : previewFrame.size.width;
CGFloat ratio = originalWidth / cropRect.size.width;
CGRect originalFrame = CGRectMake(-cropRect.origin.x * ratio, -cropRect.origin.y * ratio, originalSize.width * ratio, originalSize.height * ratio);
previewView.frame = previewFrame;
if ([self presentedForAvatarCreation]) {
CGAffineTransform transform = CGAffineTransformMakeRotation(TGRotationForOrientation(photoEditor.cropOrientation));
if (photoEditor.cropMirrored)
transform = CGAffineTransformScale(transform, -1.0f, 1.0f);
previewView.transform = transform;
}
CGSize fittedOriginalSize = CGSizeMake(originalSize.width * ratio, originalSize.height * ratio);
CGSize rotatedSize = TGRotatedContentSize(fittedOriginalSize, rotation);
CGPoint centerPoint = CGPointMake(rotatedSize.width / 2.0f, rotatedSize.height / 2.0f);
CGFloat scale = fittedOriginalSize.width / _photoEditor.originalSize.width;
CGPoint centerOffset = TGPaintSubtractPoints(centerPoint, [self fittedCropCenterScale:scale]);
_canvasView.transform = CGAffineTransformIdentity;
_canvasView.frame = originalFrame;
_canvasView.transform = CGAffineTransformMakeRotation(rotation);
_canvasView.center = TGPaintAddPoints(TGPaintCenterOfRect(_paintingWrapperView.bounds), centerOffset);
_selectionContainerView.transform = CGAffineTransformRotate(rotationTransform, rotation);
_selectionContainerView.frame = previewFrame;
_eyedropperView.frame = _selectionContainerView.bounds;
_containerView.frame = CGRectMake(containerFrame.origin.x, containerFrame.origin.y + offsetHeight, containerFrame.size.width, containerFrame.size.height);
}
#pragma mark - Keyboard Avoidance
- (void)keyboardWillChangeFrame:(NSNotification *)notification
{
UIView *parentView = self.view;
NSTimeInterval duration = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] == nil ? 0.3 : [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
int curve = [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] intValue];
CGRect screenKeyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect keyboardFrame = [parentView convertRect:screenKeyboardFrame fromView:nil];
CGFloat keyboardHeight = (keyboardFrame.size.height <= FLT_EPSILON || keyboardFrame.size.width <= FLT_EPSILON) ? 0.0f : (parentView.frame.size.height - keyboardFrame.origin.y);
keyboardHeight = MAX(keyboardHeight, 0.0f);
_keyboardHeight = keyboardHeight;
[self keyboardHeightChangedTo:keyboardHeight duration:duration curve:curve];
}
- (void)keyboardHeightChangedTo:(CGFloat)height duration:(NSTimeInterval)duration curve:(NSInteger)curve
{
CGSize referenceSize = [self referenceViewSize];
CGFloat screenSide = MAX(referenceSize.width, referenceSize.height) + 2 * TGPhotoPaintBottomPanelSize;
CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:self.effectiveOrientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation];
CGFloat visibleArea = self.view.frame.size.height - height;
CGFloat yCenter = visibleArea / 2.0f;
CGFloat offset = yCenter - _previewView.center.y - containerFrame.origin.y;
CGFloat offsetHeight = height > FLT_EPSILON ? offset : 0.0f;
[UIView animateWithDuration:duration delay:0.0 options:curve animations:^
{
_wrapperView.frame = CGRectMake((referenceSize.width - screenSide) / 2, (referenceSize.height - screenSide) / 2 + offsetHeight, _wrapperView.frame.size.width, _wrapperView.frame.size.height);
_containerView.frame = CGRectMake(containerFrame.origin.x, containerFrame.origin.y + offsetHeight, containerFrame.size.width, containerFrame.size.height);
} completion:nil];
}
- (void)_setStickerEntityPosition:(TGPhotoPaintStickerEntity *)entity
{
TGStickerMaskDescription *mask = [_stickersContext maskDescriptionForDocument:entity.document];
int64_t documentId = [_stickersContext documentIdForDocument:entity.document];
TGPhotoMaskPosition *position = [self _positionForMaskDescription:mask documentId:documentId];
if (position != nil)
{
entity.position = position.center;
entity.angle = position.angle;
entity.scale = position.scale;
}
else
{
entity.position = [self startPositionRelativeToEntity:nil];
entity.angle = [self startRotation];
}
}
- (TGPhotoMaskPosition *)_positionForMaskDescription:(TGStickerMaskDescription *)mask documentId:(int64_t)documentId
{
if (mask == nil)
return nil;
TGPhotoMaskAnchor anchor = [TGPhotoMaskPosition anchorOfMask:mask];
if (anchor == TGPhotoMaskAnchorNone)
return nil;
TGPaintFace *face = [self _randomFaceWithVacantAnchor:anchor documentId:documentId];
if (face == nil)
return nil;
CGPoint referencePoint = CGPointZero;
CGFloat referenceWidth = 0.0f;
CGFloat angle = 0.0f;
CGSize baseSize = [self _stickerBaseSizeForCurrentPainting];
CGRect faceBounds = [TGPaintFaceUtils transposeRect:face.bounds paintingSize:_painting.size originalSize:_photoEditor.originalSize];
switch (anchor)
{
case TGPhotoMaskAnchorForehead:
{
referencePoint = [TGPaintFaceUtils transposePoint:[face foreheadPoint] paintingSize:_painting.size originalSize:_photoEditor.originalSize];
referenceWidth = faceBounds.size.width;
angle = face.angle;
}
break;
case TGPhotoMaskAnchorEyes:
{
CGPoint point = [face eyesCenterPointAndDistance:&referenceWidth];
referenceWidth = [TGPaintFaceUtils transposeWidth:referenceWidth paintingSize:_painting.size originalSize:_photoEditor.originalSize];
referencePoint = [TGPaintFaceUtils transposePoint:point paintingSize:_painting.size originalSize:_photoEditor.originalSize];
angle = [face eyesAngle];
}
break;
case TGPhotoMaskAnchorMouth:
{
referencePoint = [TGPaintFaceUtils transposePoint:[face mouthPoint] paintingSize:_painting.size originalSize:_photoEditor.originalSize];
referenceWidth = faceBounds.size.width;
angle = face.angle;
}
break;
case TGPhotoMaskAnchorChin:
{
referencePoint = [TGPaintFaceUtils transposePoint:[face chinPoint] paintingSize:_painting.size originalSize:_photoEditor.originalSize];
referenceWidth = faceBounds.size.width;
angle = face.angle;
}
break;
default:
break;
}
CGFloat scale = referenceWidth / baseSize.width * mask.zoom;
CGPoint xComp = CGPointMake(sin(M_PI_2 - angle) * referenceWidth * mask.point.x,
cos(M_PI_2 - angle) * referenceWidth * mask.point.x);
CGPoint yComp = CGPointMake(cos(M_PI_2 + angle) * referenceWidth * mask.point.y,
sin(M_PI_2 + angle) * referenceWidth * mask.point.y);
CGPoint position = CGPointMake(referencePoint.x + xComp.x + yComp.x, referencePoint.y + xComp.y + yComp.y);
return [TGPhotoMaskPosition maskPositionWithCenter:position scale:scale angle:angle];
}
- (TGPaintFace *)_randomFaceWithVacantAnchor:(TGPhotoMaskAnchor)anchor documentId:(int64_t)documentId
{
NSInteger randomIndex = (NSInteger)arc4random_uniform((uint32_t)self.faces.count);
NSInteger count = self.faces.count;
NSInteger remaining = self.faces.count;
for (NSInteger i = randomIndex; remaining > 0; (i = (i + 1) % count), remaining--)
{
TGPaintFace *face = self.faces[i];
if (![self _isFaceAnchorOccupied:face anchor:anchor documentId:documentId])
return face;
}
return nil;
}
- (bool)_isFaceAnchorOccupied:(TGPaintFace *)face anchor:(TGPhotoMaskAnchor)anchor documentId:(int64_t)documentId
{
CGPoint anchorPoint = CGPointZero;
switch (anchor)
{
case TGPhotoMaskAnchorForehead:
{
anchorPoint = [TGPaintFaceUtils transposePoint:[face foreheadPoint] paintingSize:_painting.size originalSize:_photoEditor.originalSize];
}
break;
case TGPhotoMaskAnchorEyes:
{
anchorPoint = [TGPaintFaceUtils transposePoint:[face eyesCenterPointAndDistance:NULL] paintingSize:_painting.size originalSize:_photoEditor.originalSize];
}
break;
case TGPhotoMaskAnchorMouth:
{
anchorPoint = [TGPaintFaceUtils transposePoint:[face mouthPoint] paintingSize:_painting.size originalSize:_photoEditor.originalSize];
}
break;
case TGPhotoMaskAnchorChin:
{
anchorPoint = [TGPaintFaceUtils transposePoint:[face chinPoint] paintingSize:_painting.size originalSize:_photoEditor.originalSize];
}
break;
default:
{
}
break;
}
CGRect faceBounds = [TGPaintFaceUtils transposeRect:face.bounds paintingSize:_painting.size originalSize:_photoEditor.originalSize];
CGFloat minDistance = faceBounds.size.width * 1.1;
for (TGPhotoStickerEntityView *view in _entitiesContainerView.subviews)
{
if (![view isKindOfClass:[TGPhotoStickerEntityView class]])
continue;
TGPhotoPaintStickerEntity *entity = view.entity;
TGStickerMaskDescription *mask = [_stickersContext maskDescriptionForDocument:view.entity.document];
int64_t maskDocumentId = [_stickersContext documentIdForDocument:entity.document];
if ([TGPhotoMaskPosition anchorOfMask:mask] != anchor)
continue;
if ((documentId == maskDocumentId || self.faces.count > 1) && TGPaintDistance(entity.position, anchorPoint) < minDistance)
return true;
}
return false;
}
- (NSArray *)faces
{
TGPhotoEditorController *editorController = (TGPhotoEditorController *)self.parentViewController;
if ([editorController isKindOfClass:[TGPhotoEditorController class]])
return editorController.faces;
else
return @[];
}
- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures
{
return UIRectEdgeTop | UIRectEdgeBottom;
}
@end