2014 snapchat source code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

204 lines
7.8 KiB

  1. //
  2. // SCManagedCapturePreviewViewDebugView.m
  3. // Snapchat
  4. //
  5. // Created by Jiyang Zhu on 1/19/18.
  6. // Copyright © 2018 Snapchat, Inc. All rights reserved.
  7. //
  8. #import "SCManagedCapturePreviewViewDebugView.h"
  9. #import "SCManagedCapturer.h"
  10. #import "SCManagedCapturerListener.h"
  11. #import <SCFoundation/SCAssertWrapper.h>
  12. #import <SCFoundation/SCThreadHelpers.h>
  13. #import <SCFoundation/UIFont+AvenirNext.h>
  14. @import CoreText;
  15. static CGFloat const kSCManagedCapturePreviewViewDebugViewCrossHairLineWidth = 1.0;
  16. static CGFloat const kSCManagedCapturePreviewViewDebugViewCrossHairWidth = 20.0;
  17. @interface SCManagedCapturePreviewViewDebugView () <SCManagedCapturerListener>
  18. @property (assign, nonatomic) CGPoint focusPoint;
  19. @property (assign, nonatomic) CGPoint exposurePoint;
  20. @property (strong, nonatomic) NSDictionary<NSNumber *, NSValue *> *faceBoundsByFaceID;
  21. @end
  22. @implementation SCManagedCapturePreviewViewDebugView
  23. - (instancetype)initWithFrame:(CGRect)frame
  24. {
  25. self = [super initWithFrame:frame];
  26. if (self) {
  27. self.userInteractionEnabled = NO;
  28. self.backgroundColor = [UIColor clearColor];
  29. _focusPoint = [self _convertPointOfInterest:CGPointMake(0.5, 0.5)];
  30. _exposurePoint = [self _convertPointOfInterest:CGPointMake(0.5, 0.5)];
  31. [[SCManagedCapturer sharedInstance] addListener:self];
  32. }
  33. return self;
  34. }
  35. - (void)drawRect:(CGRect)rect
  36. {
  37. CGContextRef context = UIGraphicsGetCurrentContext();
  38. if (self.focusPoint.x > 0 || self.focusPoint.y > 0) {
  39. [self _drawCrossHairAtPoint:self.focusPoint inContext:context withColor:[UIColor greenColor] isXShaped:YES];
  40. }
  41. if (self.exposurePoint.x > 0 || self.exposurePoint.y > 0) {
  42. [self _drawCrossHairAtPoint:self.exposurePoint inContext:context withColor:[UIColor yellowColor] isXShaped:NO];
  43. }
  44. if (self.faceBoundsByFaceID.count > 0) {
  45. [self.faceBoundsByFaceID
  46. enumerateKeysAndObjectsUsingBlock:^(NSNumber *_Nonnull key, NSValue *_Nonnull obj, BOOL *_Nonnull stop) {
  47. CGRect faceRect = [obj CGRectValue];
  48. NSInteger faceID = [key integerValue];
  49. [self _drawRectangle:faceRect
  50. text:[NSString sc_stringWithFormat:@"ID: %@", key]
  51. inContext:context
  52. withColor:[UIColor colorWithRed:((faceID % 3) == 0)
  53. green:((faceID % 3) == 1)
  54. blue:((faceID % 3) == 2)
  55. alpha:1.0]];
  56. }];
  57. }
  58. }
  59. - (void)dealloc
  60. {
  61. [[SCManagedCapturer sharedInstance] removeListener:self];
  62. }
  63. /**
  64. Draw a crosshair with center point, context, color and shape.
  65. @param isXShaped "X" or "+"
  66. */
  67. - (void)_drawCrossHairAtPoint:(CGPoint)center
  68. inContext:(CGContextRef)context
  69. withColor:(UIColor *)color
  70. isXShaped:(BOOL)isXShaped
  71. {
  72. CGFloat width = kSCManagedCapturePreviewViewDebugViewCrossHairWidth;
  73. CGContextSetStrokeColorWithColor(context, color.CGColor);
  74. CGContextSetLineWidth(context, kSCManagedCapturePreviewViewDebugViewCrossHairLineWidth);
  75. CGContextBeginPath(context);
  76. if (isXShaped) {
  77. CGContextMoveToPoint(context, center.x - width / 2, center.y - width / 2);
  78. CGContextAddLineToPoint(context, center.x + width / 2, center.y + width / 2);
  79. CGContextMoveToPoint(context, center.x + width / 2, center.y - width / 2);
  80. CGContextAddLineToPoint(context, center.x - width / 2, center.y + width / 2);
  81. } else {
  82. CGContextMoveToPoint(context, center.x - width / 2, center.y);
  83. CGContextAddLineToPoint(context, center.x + width / 2, center.y);
  84. CGContextMoveToPoint(context, center.x, center.y - width / 2);
  85. CGContextAddLineToPoint(context, center.x, center.y + width / 2);
  86. }
  87. CGContextStrokePath(context);
  88. }
  89. /**
  90. Draw a rectangle, with a text on the top left.
  91. */
  92. - (void)_drawRectangle:(CGRect)rect text:(NSString *)text inContext:(CGContextRef)context withColor:(UIColor *)color
  93. {
  94. CGContextSetStrokeColorWithColor(context, color.CGColor);
  95. CGContextSetLineWidth(context, kSCManagedCapturePreviewViewDebugViewCrossHairLineWidth);
  96. CGContextBeginPath(context);
  97. CGContextMoveToPoint(context, CGRectGetMinX(rect), CGRectGetMinY(rect));
  98. CGContextAddLineToPoint(context, CGRectGetMinX(rect), CGRectGetMaxY(rect));
  99. CGContextAddLineToPoint(context, CGRectGetMaxX(rect), CGRectGetMaxY(rect));
  100. CGContextAddLineToPoint(context, CGRectGetMaxX(rect), CGRectGetMinY(rect));
  101. CGContextAddLineToPoint(context, CGRectGetMinX(rect), CGRectGetMinY(rect));
  102. NSMutableParagraphStyle *textStyle = [[NSMutableParagraphStyle alloc] init];
  103. textStyle.alignment = NSTextAlignmentLeft;
  104. NSDictionary *attributes = @{
  105. NSFontAttributeName : [UIFont boldSystemFontOfSize:16],
  106. NSForegroundColorAttributeName : color,
  107. NSParagraphStyleAttributeName : textStyle
  108. };
  109. [text drawInRect:rect withAttributes:attributes];
  110. CGContextStrokePath(context);
  111. }
  112. - (CGPoint)_convertPointOfInterest:(CGPoint)point
  113. {
  114. SCAssertMainThread();
  115. CGPoint convertedPoint =
  116. CGPointMake((1 - point.y) * CGRectGetWidth(self.bounds), point.x * CGRectGetHeight(self.bounds));
  117. if ([[SCManagedCapturer sharedInstance] isVideoMirrored]) {
  118. convertedPoint.x = CGRectGetWidth(self.bounds) - convertedPoint.x;
  119. }
  120. return convertedPoint;
  121. }
  122. - (NSDictionary<NSNumber *, NSValue *> *)_convertFaceBounds:(NSDictionary<NSNumber *, NSValue *> *)faceBoundsByFaceID
  123. {
  124. SCAssertMainThread();
  125. NSMutableDictionary<NSNumber *, NSValue *> *convertedFaceBoundsByFaceID =
  126. [NSMutableDictionary dictionaryWithCapacity:faceBoundsByFaceID.count];
  127. for (NSNumber *key in faceBoundsByFaceID.allKeys) {
  128. CGRect faceBounds = [[faceBoundsByFaceID objectForKey:key] CGRectValue];
  129. CGRect convertedBounds = CGRectMake(CGRectGetMinY(faceBounds) * CGRectGetWidth(self.bounds),
  130. CGRectGetMinX(faceBounds) * CGRectGetHeight(self.bounds),
  131. CGRectGetHeight(faceBounds) * CGRectGetWidth(self.bounds),
  132. CGRectGetWidth(faceBounds) * CGRectGetHeight(self.bounds));
  133. if (![[SCManagedCapturer sharedInstance] isVideoMirrored]) {
  134. convertedBounds.origin.x = CGRectGetWidth(self.bounds) - CGRectGetMaxX(convertedBounds);
  135. }
  136. [convertedFaceBoundsByFaceID setObject:[NSValue valueWithCGRect:convertedBounds] forKey:key];
  137. }
  138. return convertedFaceBoundsByFaceID;
  139. }
  140. #pragma mark - SCManagedCapturerListener
  141. - (void)managedCapturer:(id<SCCapturer>)managedCapturer didChangeExposurePoint:(CGPoint)exposurePoint
  142. {
  143. runOnMainThreadAsynchronouslyIfNecessary(^{
  144. self.exposurePoint = [self _convertPointOfInterest:exposurePoint];
  145. [self setNeedsDisplay];
  146. });
  147. }
  148. - (void)managedCapturer:(id<SCCapturer>)managedCapturer didChangeFocusPoint:(CGPoint)focusPoint
  149. {
  150. runOnMainThreadAsynchronouslyIfNecessary(^{
  151. self.focusPoint = [self _convertPointOfInterest:focusPoint];
  152. [self setNeedsDisplay];
  153. });
  154. }
  155. - (void)managedCapturer:(id<SCCapturer>)managedCapturer
  156. didDetectFaceBounds:(NSDictionary<NSNumber *, NSValue *> *)faceBoundsByFaceID
  157. {
  158. runOnMainThreadAsynchronouslyIfNecessary(^{
  159. self.faceBoundsByFaceID = [self _convertFaceBounds:faceBoundsByFaceID];
  160. [self setNeedsDisplay];
  161. });
  162. }
  163. - (void)managedCapturer:(id<SCCapturer>)managedCapturer didChangeCaptureDevicePosition:(SCManagedCapturerState *)state
  164. {
  165. runOnMainThreadAsynchronouslyIfNecessary(^{
  166. self.faceBoundsByFaceID = nil;
  167. self.focusPoint = [self _convertPointOfInterest:CGPointMake(0.5, 0.5)];
  168. self.exposurePoint = [self _convertPointOfInterest:CGPointMake(0.5, 0.5)];
  169. [self setNeedsDisplay];
  170. });
  171. }
  172. @end