Swiftgram/submodules/LegacyComponents/Sources/TGPhotoEditorCurvesToolView.m
2020-02-22 15:38:54 +04:00

546 lines
18 KiB
Objective-C

#import "TGPhotoEditorCurvesToolView.h"
#import "LegacyComponentsInternal.h"
#import "TGFont.h"
#import "TGImageUtils.h"
typedef enum
{
TGCurvesSegmentNone,
TGCurvesSegmentBlacks,
TGCurvesSegmentShadows,
TGCurvesSegmentMidtones,
TGCurvesSegmentHighlights,
TGCurvesSegmentWhites
} TGCurvesSegment;
@interface TGPhotoEditorCurvesToolView () <UIGestureRecognizerDelegate>
{
TGCurvesSegment _activeSegment;
UILabel *_blacksLevelLabel;
UILabel *_shadowsLevelLabel;
UILabel *_midtonesLevelLabel;
UILabel *_highlightsLevelLabel;
UILabel *_whitesLevelLabel;
UIView *_selectionView;
UILongPressGestureRecognizer *_pressGestureRecognizer;
UIPanGestureRecognizer *_panGestureRecognizer;
UITapGestureRecognizer *_doubleTapGestureRecognizer;
NSArray *_interpolatedCurveValues;
CAShapeLayer *_curveLayer;
bool _appeared;
}
@property (nonatomic, weak) PGCurvesTool *curvesTool;
@end
@implementation TGPhotoEditorCurvesToolView
@synthesize valueChanged = _valueChanged;
@synthesize value = _value;
@synthesize interactionBegan = _interactionBegan;
@synthesize interactionEnded = _interactionEnded;
@synthesize actualAreaSize;
@synthesize isLandscape;
@synthesize toolbarLandscapeSize;
- (instancetype)initWithEditorItem:(id<PGPhotoEditorItem>)__unused editorItem
{
self = [self initWithFrame:CGRectZero];
if (self != nil)
{
self.backgroundColor = [UIColor clearColor];
self.contentMode = UIViewContentModeRedraw;
_activeSegment = TGCurvesSegmentNone;
_selectionView = [[UIView alloc] initWithFrame:CGRectZero];
_selectionView.alpha = 0.0f;
_selectionView.backgroundColor = [UIColor whiteColor];
_selectionView.userInteractionEnabled = false;
//[self addSubview:_selectionView];
_blacksLevelLabel = [self _levelLabel];
_blacksLevelLabel.text = @"0.00";
[self addSubview:_blacksLevelLabel];
_shadowsLevelLabel = [self _levelLabel];
_shadowsLevelLabel.text = @"0.00";
[self addSubview:_shadowsLevelLabel];
_midtonesLevelLabel = [self _levelLabel];
_midtonesLevelLabel.text = @"0.00";
[self addSubview:_midtonesLevelLabel];
_highlightsLevelLabel = [self _levelLabel];
_highlightsLevelLabel.text = @"0.00";
[self addSubview:_highlightsLevelLabel];
_whitesLevelLabel = [self _levelLabel];
_whitesLevelLabel.text = @"0.00";
[self addSubview:_whitesLevelLabel];
_panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
_panGestureRecognizer.delegate = self;
[self addGestureRecognizer:_panGestureRecognizer];
_pressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handlePress:)];
_pressGestureRecognizer.delegate = self;
_pressGestureRecognizer.minimumPressDuration = 0.1f;
[self addGestureRecognizer:_pressGestureRecognizer];
_doubleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];
_doubleTapGestureRecognizer.numberOfTapsRequired = 2;
[self addGestureRecognizer:_doubleTapGestureRecognizer];
if ([editorItem isKindOfClass:[PGCurvesTool class]])
{
PGCurvesTool *curvesTool = (PGCurvesTool *)editorItem;
self.curvesTool = curvesTool;
[self setValue:editorItem.value];
}
}
return self;
}
- (UILabel *)_levelLabel
{
UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
label.backgroundColor = [UIColor clearColor];
label.font = [TGFont systemFontOfSize:13];
label.textAlignment = NSTextAlignmentCenter;
label.textColor = [UIColor colorWithWhite:1.0f alpha:0.75f];
return label;
}
- (void)setValue:(id)value
{
if (![value isKindOfClass:[PGCurvesToolValue class]])
return;
_value = value;
[self updateCurve];
[self updateValueLabels];
}
- (void)updateCurve
{
if (CGRectEqualToRect([self _actualArea], CGRectZero))
return;
PGCurvesToolValue *curvesToolValue = (PGCurvesToolValue *)_value;
PGCurvesValue *curvesValue = nil;
switch (curvesToolValue.activeType)
{
case PGCurvesTypeLuminance:
curvesValue = curvesToolValue.luminanceCurve;
break;
case PGCurvesTypeRed:
curvesValue = curvesToolValue.redCurve;
break;
case PGCurvesTypeGreen:
curvesValue = curvesToolValue.greenCurve;
break;
case PGCurvesTypeBlue:
curvesValue = curvesToolValue.blueCurve;
break;
default:
break;
}
NSArray *points = [curvesValue interpolateCurve];
[self renderCurveWithPoints:points color:[TGPhotoEditorCurvesToolView colorForCurveType:curvesToolValue.activeType] lineWidth:2];
}
- (void)updateValueLabels
{
PGCurvesToolValue *curvesToolValue = (PGCurvesToolValue *)_value;
PGCurvesValue *curvesValue = nil;
switch (curvesToolValue.activeType)
{
case PGCurvesTypeLuminance:
curvesValue = curvesToolValue.luminanceCurve;
break;
case PGCurvesTypeRed:
curvesValue = curvesToolValue.redCurve;
break;
case PGCurvesTypeGreen:
curvesValue = curvesToolValue.greenCurve;
break;
case PGCurvesTypeBlue:
curvesValue = curvesToolValue.blueCurve;
break;
default:
break;
}
_blacksLevelLabel.text = [NSString stringWithFormat:@"%0.2f", curvesValue.blacksLevel / 100.0f];
_shadowsLevelLabel.text = [NSString stringWithFormat:@"%0.2f", curvesValue.shadowsLevel / 100.0f];
_midtonesLevelLabel.text = [NSString stringWithFormat:@"%0.2f", curvesValue.midtonesLevel / 100.0f];
_highlightsLevelLabel.text = [NSString stringWithFormat:@"%0.2f", curvesValue.highlightsLevel / 100.0f];
_whitesLevelLabel.text = [NSString stringWithFormat:@"%0.2f", curvesValue.whitesLevel / 100.0f];
}
+ (UIColor *)colorForCurveType:(PGCurvesType)curveType
{
switch (curveType)
{
case PGCurvesTypeLuminance:
return [UIColor whiteColor];
case PGCurvesTypeRed:
return UIColorRGB(0xed3d4c);
case PGCurvesTypeGreen:
return UIColorRGB(0x10ee9d);
case PGCurvesTypeBlue:
return UIColorRGB(0x3377fb);
default:
break;
}
}
- (bool)buttonPressed:(bool)__unused cancelButton
{
return true;
}
- (bool)isTracking
{
return false;
}
- (void)handlePress:(UILongPressGestureRecognizer *)gestureRecognizer
{
switch (gestureRecognizer.state)
{
case UIGestureRecognizerStateBegan:
[self selectSegmentWithPoint:[gestureRecognizer locationInView:gestureRecognizer.view]];
break;
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateFailed:
[self unselectSegments];
break;
default:
break;
}
}
- (void)handlePan:(UIPanGestureRecognizer *)gestureRecognizer
{
switch (gestureRecognizer.state)
{
case UIGestureRecognizerStateBegan:
[self selectSegmentWithPoint:[gestureRecognizer locationInView:gestureRecognizer.view]];
break;
case UIGestureRecognizerStateChanged:
{
PGCurvesToolValue *newValue = [(PGCurvesToolValue *)_value copy];
CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view];
CGFloat delta = MIN(2, -1 * translation.y / 8.0f);
PGCurvesValue *curveValue = nil;
switch (newValue.activeType)
{
case PGCurvesTypeLuminance:
curveValue = newValue.luminanceCurve;
break;
case PGCurvesTypeRed:
curveValue = newValue.redCurve;
break;
case PGCurvesTypeGreen:
curveValue = newValue.greenCurve;
break;
case PGCurvesTypeBlue:
curveValue = newValue.blueCurve;
break;
default:
break;
}
switch (_activeSegment)
{
case TGCurvesSegmentBlacks:
curveValue.blacksLevel = MAX(0, MIN(100, curveValue.blacksLevel + delta));
break;
case TGCurvesSegmentShadows:
curveValue.shadowsLevel = MAX(0, MIN(100, curveValue.shadowsLevel + delta));
break;
case TGCurvesSegmentMidtones:
curveValue.midtonesLevel = MAX(0, MIN(100, curveValue.midtonesLevel + delta));
break;
case TGCurvesSegmentHighlights:
curveValue.highlightsLevel = MAX(0, MIN(100, curveValue.highlightsLevel + delta));
break;
case TGCurvesSegmentWhites:
curveValue.whitesLevel = MAX(0, MIN(100, curveValue.whitesLevel + delta));
break;
default:
break;
}
_value = newValue;
[self updateCurve];
[self updateValueLabels];
if (self.valueChanged != nil)
self.valueChanged(newValue, false);
[gestureRecognizer setTranslation:CGPointZero inView:gestureRecognizer.view];
}
break;
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateFailed:
{
[self unselectSegments];
if (self.interactionEnded != nil)
self.interactionEnded();
}
break;
default:
break;
}
}
- (void)handleDoubleTap:(UITapGestureRecognizer *)__unused gestureRecognizer
{
PGCurvesToolValue *value = [_value copy];
if (value == nil)
return;
switch (value.activeType) {
case PGCurvesTypeLuminance:
value.luminanceCurve = [PGCurvesValue defaultValue];
break;
case PGCurvesTypeRed:
value.redCurve = [PGCurvesValue defaultValue];
break;
case PGCurvesTypeGreen:
value.greenCurve = [PGCurvesValue defaultValue];
break;
case PGCurvesTypeBlue:
value.blueCurve = [PGCurvesValue defaultValue];
break;
default:
break;
}
_value = value;
[self updateCurve];
[self updateValueLabels];
self.valueChanged(value, false);
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer == _pressGestureRecognizer || gestureRecognizer == _panGestureRecognizer)
{
CGPoint location = [gestureRecognizer locationInView:self];
CGRect actualArea = [self _actualArea];
return CGRectContainsPoint(actualArea, location);
}
return true;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if (gestureRecognizer == _pressGestureRecognizer || otherGestureRecognizer == _pressGestureRecognizer)
return true;
return false;
}
- (void)selectSegmentWithPoint:(CGPoint)point
{
if (_activeSegment != TGCurvesSegmentNone)
return;
CGRect actualArea = [self _actualArea];
CGFloat segmentWidth = actualArea.size.width / 5.0f;
point = CGPointMake(point.x - actualArea.origin.x, point.y - actualArea.origin.y);
_activeSegment = (TGCurvesSegment)(floor(point.x / segmentWidth) + 1);
[UIView animateWithDuration:0.2f animations:^
{
_selectionView.alpha = 0.11f;
}];
_selectionView.frame = CGRectMake(actualArea.origin.x + (_activeSegment - 1) * segmentWidth, actualArea.origin.y, segmentWidth, actualArea.size.height);
}
- (void)unselectSegments
{
if (_activeSegment == TGCurvesSegmentNone)
return;
_activeSegment = TGCurvesSegmentNone;
[UIView animateWithDuration:0.3f animations:^
{
_selectionView.alpha = 0.0f;
}];
}
- (void)drawRect:(CGRect)__unused rect
{
CGRect actualArea = [self _actualArea];
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat segmentWidth = actualArea.size.width / 5.0f;
CGContextSetStrokeColorWithColor(context, [UIColor colorWithWhite:1.0f alpha:0.6f].CGColor);
CGContextSetLineWidth(context, 1 - TGRetinaPixel);
for (NSUInteger i = 0; i < 4; i++)
{
CGContextMoveToPoint(context, actualArea.origin.x + segmentWidth + i * segmentWidth, actualArea.origin.y);
CGContextAddLineToPoint(context, actualArea.origin.x + segmentWidth + i * segmentWidth, actualArea.origin.y + actualArea.size.height);
CGContextStrokePath(context);
}
CGContextSetStrokeColorWithColor(context, [UIColor colorWithWhite:1.0f alpha:0.6f].CGColor);
CGContextSetLineWidth(context, 1.5f);
CGFloat lengths[] = { 7, 4 };
CGContextSetLineDash(context, 0.0, lengths, 2);
CGContextMoveToPoint(context, actualArea.origin.x, actualArea.origin.y + actualArea.size.height);
CGContextAddLineToPoint(context, actualArea.origin.x + actualArea.size.width, actualArea.origin.y );
CGContextStrokePath(context);
CGContextSetLineDash(context, 0.0, NULL, 0);
}
- (CGRect)_actualArea
{
return CGRectMake((self.frame.size.width - self.actualAreaSize.width) / 2, (self.frame.size.height - self.actualAreaSize.height) / 2, self.actualAreaSize.width, self.actualAreaSize.height);
}
- (CGPoint)_viewPointWithPoint:(CGPoint)point actualArea:(CGRect)actualArea
{
return CGPointMake(point.x * actualArea.size.width, (1.0 - point.y) * actualArea.size.height);
}
- (void)renderCurveWithPoints:(NSArray *)points color:(UIColor *)color lineWidth:(CGFloat)lineWidth
{
UIBezierPath *path = [UIBezierPath bezierPath];
CGRect actualArea = [self _actualArea];
[points enumerateObjectsUsingBlock:^(NSValue *value, NSUInteger index, __unused BOOL *stop)
{
CGPoint point = [self _viewPointWithPoint:value.CGPointValue actualArea:actualArea];
if (index == 0)
[path moveToPoint:point];
else
[path addLineToPoint:point];
}];
if (_curveLayer == nil)
{
_curveLayer = [[CAShapeLayer alloc] init];
_curveLayer.fillColor = [UIColor clearColor].CGColor;
_curveLayer.lineWidth = lineWidth;
[_curveLayer setLineCap:kCALineCapRound];
[self.layer addSublayer:_curveLayer];
}
[UIView performWithoutAnimation:^
{
_curveLayer.strokeColor = color.CGColor;
_curveLayer.path = [path CGPath];
_curveLayer.frame = CGRectMake(actualArea.origin.x, actualArea.origin.y, actualArea.size.width, actualArea.size.height);
}];
}
- (void)transitionIn
{
_appeared = true;
self.alpha = 0.0f;
[UIView animateWithDuration:0.2f animations:^
{
self.alpha = 1.0f;
}];
}
- (void)layoutSubviews
{
CGRect actualArea = [self _actualArea];
CGFloat segmentWidth = actualArea.size.width / 5.0f;
CGFloat bottomOffset = 4.0f;
[_blacksLevelLabel sizeToFit];
_blacksLevelLabel.frame = CGRectMake(actualArea.origin.x, actualArea.origin.y + actualArea.size.height - ceil(_blacksLevelLabel.frame.size.height) - bottomOffset, segmentWidth, ceil(_blacksLevelLabel.frame.size.height));
[_shadowsLevelLabel sizeToFit];
_shadowsLevelLabel.frame = CGRectMake(actualArea.origin.x + segmentWidth, actualArea.origin.y + actualArea.size.height - ceil(_shadowsLevelLabel.frame.size.height) - bottomOffset, segmentWidth, ceil(_shadowsLevelLabel.frame.size.height));
[_midtonesLevelLabel sizeToFit];
_midtonesLevelLabel.frame = CGRectMake(actualArea.origin.x + segmentWidth * 2, actualArea.origin.y + actualArea.size.height - ceil(_midtonesLevelLabel.frame.size.height) - bottomOffset, segmentWidth, ceil(_midtonesLevelLabel.frame.size.height));
[_highlightsLevelLabel sizeToFit];
_highlightsLevelLabel.frame = CGRectMake(actualArea.origin.x + segmentWidth * 3, actualArea.origin.y + actualArea.size.height - ceil(_highlightsLevelLabel.frame.size.height) - bottomOffset, segmentWidth, ceil(_highlightsLevelLabel.frame.size.height));
[_whitesLevelLabel sizeToFit];
_whitesLevelLabel.frame = CGRectMake(actualArea.origin.x + segmentWidth * 4, actualArea.origin.y + actualArea.size.height - ceil(_whitesLevelLabel.frame.size.height) - bottomOffset, segmentWidth, ceil(_whitesLevelLabel.frame.size.height));
_curveLayer.frame = CGRectMake(actualArea.origin.x, actualArea.origin.y, actualArea.size.width, actualArea.size.height);
[self updateCurve];
[self updateValueLabels];
if (!_appeared)
[self transitionIn];
}
@end