|
|
// // SCManagedCapturePreviewViewDebugView.m // Snapchat // // Created by Jiyang Zhu on 1/19/18. // Copyright © 2018 Snapchat, Inc. All rights reserved. //
#import "SCManagedCapturePreviewViewDebugView.h"
#import "SCManagedCapturer.h" #import "SCManagedCapturerListener.h"
#import <SCFoundation/SCAssertWrapper.h> #import <SCFoundation/SCThreadHelpers.h> #import <SCFoundation/UIFont+AvenirNext.h>
@import CoreText;
static CGFloat const kSCManagedCapturePreviewViewDebugViewCrossHairLineWidth = 1.0; static CGFloat const kSCManagedCapturePreviewViewDebugViewCrossHairWidth = 20.0;
@interface SCManagedCapturePreviewViewDebugView () <SCManagedCapturerListener>
@property (assign, nonatomic) CGPoint focusPoint; @property (assign, nonatomic) CGPoint exposurePoint; @property (strong, nonatomic) NSDictionary<NSNumber *, NSValue *> *faceBoundsByFaceID;
@end
@implementation SCManagedCapturePreviewViewDebugView
- (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.userInteractionEnabled = NO; self.backgroundColor = [UIColor clearColor]; _focusPoint = [self _convertPointOfInterest:CGPointMake(0.5, 0.5)]; _exposurePoint = [self _convertPointOfInterest:CGPointMake(0.5, 0.5)]; [[SCManagedCapturer sharedInstance] addListener:self]; } return self; }
- (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext();
if (self.focusPoint.x > 0 || self.focusPoint.y > 0) { [self _drawCrossHairAtPoint:self.focusPoint inContext:context withColor:[UIColor greenColor] isXShaped:YES]; }
if (self.exposurePoint.x > 0 || self.exposurePoint.y > 0) { [self _drawCrossHairAtPoint:self.exposurePoint inContext:context withColor:[UIColor yellowColor] isXShaped:NO]; }
if (self.faceBoundsByFaceID.count > 0) { [self.faceBoundsByFaceID enumerateKeysAndObjectsUsingBlock:^(NSNumber *_Nonnull key, NSValue *_Nonnull obj, BOOL *_Nonnull stop) { CGRect faceRect = [obj CGRectValue]; NSInteger faceID = [key integerValue]; [self _drawRectangle:faceRect text:[NSString sc_stringWithFormat:@"ID: %@", key] inContext:context withColor:[UIColor colorWithRed:((faceID % 3) == 0) green:((faceID % 3) == 1) blue:((faceID % 3) == 2) alpha:1.0]]; }]; } }
- (void)dealloc { [[SCManagedCapturer sharedInstance] removeListener:self]; }
/** Draw a crosshair with center point, context, color and shape.
@param isXShaped "X" or "+" */ - (void)_drawCrossHairAtPoint:(CGPoint)center inContext:(CGContextRef)context withColor:(UIColor *)color isXShaped:(BOOL)isXShaped { CGFloat width = kSCManagedCapturePreviewViewDebugViewCrossHairWidth;
CGContextSetStrokeColorWithColor(context, color.CGColor); CGContextSetLineWidth(context, kSCManagedCapturePreviewViewDebugViewCrossHairLineWidth); CGContextBeginPath(context);
if (isXShaped) { CGContextMoveToPoint(context, center.x - width / 2, center.y - width / 2); CGContextAddLineToPoint(context, center.x + width / 2, center.y + width / 2); CGContextMoveToPoint(context, center.x + width / 2, center.y - width / 2); CGContextAddLineToPoint(context, center.x - width / 2, center.y + width / 2); } else { CGContextMoveToPoint(context, center.x - width / 2, center.y); CGContextAddLineToPoint(context, center.x + width / 2, center.y); CGContextMoveToPoint(context, center.x, center.y - width / 2); CGContextAddLineToPoint(context, center.x, center.y + width / 2); }
CGContextStrokePath(context); }
/** Draw a rectangle, with a text on the top left. */ - (void)_drawRectangle:(CGRect)rect text:(NSString *)text inContext:(CGContextRef)context withColor:(UIColor *)color { CGContextSetStrokeColorWithColor(context, color.CGColor); CGContextSetLineWidth(context, kSCManagedCapturePreviewViewDebugViewCrossHairLineWidth); CGContextBeginPath(context);
CGContextMoveToPoint(context, CGRectGetMinX(rect), CGRectGetMinY(rect)); CGContextAddLineToPoint(context, CGRectGetMinX(rect), CGRectGetMaxY(rect)); CGContextAddLineToPoint(context, CGRectGetMaxX(rect), CGRectGetMaxY(rect)); CGContextAddLineToPoint(context, CGRectGetMaxX(rect), CGRectGetMinY(rect)); CGContextAddLineToPoint(context, CGRectGetMinX(rect), CGRectGetMinY(rect));
NSMutableParagraphStyle *textStyle = [[NSMutableParagraphStyle alloc] init]; textStyle.alignment = NSTextAlignmentLeft; NSDictionary *attributes = @{ NSFontAttributeName : [UIFont boldSystemFontOfSize:16], NSForegroundColorAttributeName : color, NSParagraphStyleAttributeName : textStyle };
[text drawInRect:rect withAttributes:attributes];
CGContextStrokePath(context); }
- (CGPoint)_convertPointOfInterest:(CGPoint)point { SCAssertMainThread(); CGPoint convertedPoint = CGPointMake((1 - point.y) * CGRectGetWidth(self.bounds), point.x * CGRectGetHeight(self.bounds)); if ([[SCManagedCapturer sharedInstance] isVideoMirrored]) { convertedPoint.x = CGRectGetWidth(self.bounds) - convertedPoint.x; } return convertedPoint; }
- (NSDictionary<NSNumber *, NSValue *> *)_convertFaceBounds:(NSDictionary<NSNumber *, NSValue *> *)faceBoundsByFaceID { SCAssertMainThread(); NSMutableDictionary<NSNumber *, NSValue *> *convertedFaceBoundsByFaceID = [NSMutableDictionary dictionaryWithCapacity:faceBoundsByFaceID.count]; for (NSNumber *key in faceBoundsByFaceID.allKeys) { CGRect faceBounds = [[faceBoundsByFaceID objectForKey:key] CGRectValue]; CGRect convertedBounds = CGRectMake(CGRectGetMinY(faceBounds) * CGRectGetWidth(self.bounds), CGRectGetMinX(faceBounds) * CGRectGetHeight(self.bounds), CGRectGetHeight(faceBounds) * CGRectGetWidth(self.bounds), CGRectGetWidth(faceBounds) * CGRectGetHeight(self.bounds)); if (![[SCManagedCapturer sharedInstance] isVideoMirrored]) { convertedBounds.origin.x = CGRectGetWidth(self.bounds) - CGRectGetMaxX(convertedBounds); } [convertedFaceBoundsByFaceID setObject:[NSValue valueWithCGRect:convertedBounds] forKey:key]; } return convertedFaceBoundsByFaceID; }
#pragma mark - SCManagedCapturerListener - (void)managedCapturer:(id<SCCapturer>)managedCapturer didChangeExposurePoint:(CGPoint)exposurePoint { runOnMainThreadAsynchronouslyIfNecessary(^{ self.exposurePoint = [self _convertPointOfInterest:exposurePoint]; [self setNeedsDisplay]; }); }
- (void)managedCapturer:(id<SCCapturer>)managedCapturer didChangeFocusPoint:(CGPoint)focusPoint { runOnMainThreadAsynchronouslyIfNecessary(^{ self.focusPoint = [self _convertPointOfInterest:focusPoint]; [self setNeedsDisplay]; }); }
- (void)managedCapturer:(id<SCCapturer>)managedCapturer didDetectFaceBounds:(NSDictionary<NSNumber *, NSValue *> *)faceBoundsByFaceID { runOnMainThreadAsynchronouslyIfNecessary(^{ self.faceBoundsByFaceID = [self _convertFaceBounds:faceBoundsByFaceID]; [self setNeedsDisplay]; }); }
- (void)managedCapturer:(id<SCCapturer>)managedCapturer didChangeCaptureDevicePosition:(SCManagedCapturerState *)state { runOnMainThreadAsynchronouslyIfNecessary(^{ self.faceBoundsByFaceID = nil; self.focusPoint = [self _convertPointOfInterest:CGPointMake(0.5, 0.5)]; self.exposurePoint = [self _convertPointOfInterest:CGPointMake(0.5, 0.5)]; [self setNeedsDisplay]; }); }
@end
|