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.

153 lines
6.4 KiB

  1. //
  2. // SCManagedCapturerUtils.m
  3. // Snapchat
  4. //
  5. // Created by Chao Pang on 10/4/17.
  6. //
  7. #import "SCManagedCapturerUtils.h"
  8. #import "SCCaptureCommon.h"
  9. #import <SCFoundation/SCAssertWrapper.h>
  10. #import <SCFoundation/SCCoreGraphicsUtils.h>
  11. #import <SCFoundation/SCDeviceName.h>
  12. #import <SCFoundation/UIScreen+SCSafeAreaInsets.h>
  13. // This is to calculate the crop ratio for generating the image shown in Preview page
  14. // Check https://snapchat.quip.com/lU3kAoDxaAFG for our design.
  15. const CGFloat kSCIPhoneXCapturedImageVideoCropRatio = (397.0 * 739.0) / (375.0 * 812.0);
  16. CGFloat SCManagedCapturedImageAndVideoAspectRatio(void)
  17. {
  18. static dispatch_once_t onceToken;
  19. static CGFloat aspectRatio;
  20. dispatch_once(&onceToken, ^{
  21. CGSize screenSize = [UIScreen mainScreen].fixedCoordinateSpace.bounds.size;
  22. UIEdgeInsets safeAreaInsets = [UIScreen sc_safeAreaInsets];
  23. aspectRatio = SCSizeGetAspectRatio(
  24. CGSizeMake(screenSize.width, screenSize.height - safeAreaInsets.top - safeAreaInsets.bottom));
  25. });
  26. return aspectRatio;
  27. }
  28. CGSize SCManagedCapturerAllScreenSize(void)
  29. {
  30. static CGSize size;
  31. static dispatch_once_t onceToken;
  32. dispatch_once(&onceToken, ^{
  33. CGSize screenSize = [UIScreen mainScreen].fixedCoordinateSpace.bounds.size;
  34. // This logic is complicated because we need to handle iPhone X properly.
  35. // See https://snapchat.quip.com/lU3kAoDxaAFG for our design.
  36. UIEdgeInsets safeAreaInsets = [UIScreen sc_safeAreaInsets];
  37. UIEdgeInsets visualSafeInsets = [UIScreen sc_visualSafeInsets];
  38. // This really is just some coordinate computations:
  39. // We know in preview, our size is (screenWidth, screenHeight - topInset - bottomInset)
  40. // We know that when the preview image is in the camera screen, the height is screenHeight - visualTopInset,
  41. // thus, we need to figure out in camera screen, what's the bleed-over width should be
  42. // (screenWidth * (screenHeight - visualTopInset) / (screenHeight - topInset - bottomInset)
  43. size = CGSizeMake(roundf(screenSize.width * (screenSize.height - visualSafeInsets.top) /
  44. (screenSize.height - safeAreaInsets.top - safeAreaInsets.bottom)),
  45. screenSize.height);
  46. });
  47. return size;
  48. }
  49. CGSize SCAsyncImageCapturePlaceholderViewSize(void)
  50. {
  51. static CGSize size;
  52. static dispatch_once_t onceToken;
  53. dispatch_once(&onceToken, ^{
  54. CGSize screenSize = [UIScreen mainScreen].fixedCoordinateSpace.bounds.size;
  55. UIEdgeInsets safeAreaInsets = [UIScreen sc_safeAreaInsets];
  56. UIEdgeInsets visualSafeInsets = [UIScreen sc_visualSafeInsets];
  57. size = CGSizeMake(roundf((screenSize.height - visualSafeInsets.top) * screenSize.width /
  58. (screenSize.height - safeAreaInsets.top - safeAreaInsets.bottom)),
  59. screenSize.height - visualSafeInsets.top);
  60. });
  61. return size;
  62. }
  63. CGFloat SCAdjustedAspectRatio(UIImageOrientation orientation, CGFloat aspectRatio)
  64. {
  65. SCCAssert(aspectRatio != kSCManagedCapturerAspectRatioUnspecified, @"");
  66. switch (orientation) {
  67. case UIImageOrientationLeft:
  68. case UIImageOrientationRight:
  69. case UIImageOrientationLeftMirrored:
  70. case UIImageOrientationRightMirrored:
  71. return 1.0 / aspectRatio;
  72. default:
  73. return aspectRatio;
  74. }
  75. }
  76. UIImage *SCCropImageToTargetAspectRatio(UIImage *image, CGFloat targetAspectRatio)
  77. {
  78. if (SCNeedsCropImageToAspectRatio(image.CGImage, image.imageOrientation, targetAspectRatio)) {
  79. CGImageRef croppedImageRef =
  80. SCCreateCroppedImageToAspectRatio(image.CGImage, image.imageOrientation, targetAspectRatio);
  81. UIImage *croppedImage =
  82. [UIImage imageWithCGImage:croppedImageRef scale:image.scale orientation:image.imageOrientation];
  83. CGImageRelease(croppedImageRef);
  84. return croppedImage;
  85. } else {
  86. return image;
  87. }
  88. }
  89. void SCCropImageSizeToAspectRatio(size_t inputWidth, size_t inputHeight, UIImageOrientation orientation,
  90. CGFloat aspectRatio, size_t *outputWidth, size_t *outputHeight)
  91. {
  92. SCCAssert(outputWidth != NULL && outputHeight != NULL, @"");
  93. aspectRatio = SCAdjustedAspectRatio(orientation, aspectRatio);
  94. if (inputWidth > roundf(inputHeight * aspectRatio)) {
  95. *outputHeight = inputHeight;
  96. *outputWidth = roundf(*outputHeight * aspectRatio);
  97. } else {
  98. *outputWidth = inputWidth;
  99. *outputHeight = roundf(*outputWidth / aspectRatio);
  100. }
  101. }
  102. BOOL SCNeedsCropImageToAspectRatio(CGImageRef image, UIImageOrientation orientation, CGFloat aspectRatio)
  103. {
  104. if (aspectRatio == kSCManagedCapturerAspectRatioUnspecified) {
  105. return NO;
  106. }
  107. aspectRatio = SCAdjustedAspectRatio(orientation, aspectRatio);
  108. size_t width = CGImageGetWidth(image);
  109. size_t height = CGImageGetHeight(image);
  110. return (width != roundf(height * aspectRatio));
  111. }
  112. CGRect SCCalculateRectToCrop(size_t imageWidth, size_t imageHeight, size_t croppedWidth, size_t croppedHeight)
  113. {
  114. if ([SCDeviceName isIphoneX]) {
  115. // X is pushed all the way over to crop out top section but none of bottom
  116. CGFloat x = (imageWidth - croppedWidth);
  117. // Crop y symmetrically.
  118. CGFloat y = roundf((imageHeight - croppedHeight) / 2.0);
  119. return CGRectMake(x, y, croppedWidth, croppedHeight);
  120. }
  121. return CGRectMake((imageWidth - croppedWidth) / 2, (imageHeight - croppedHeight) / 2, croppedWidth, croppedHeight);
  122. }
  123. CGImageRef SCCreateCroppedImageToAspectRatio(CGImageRef image, UIImageOrientation orientation, CGFloat aspectRatio)
  124. {
  125. SCCAssert(aspectRatio != kSCManagedCapturerAspectRatioUnspecified, @"");
  126. size_t width = CGImageGetWidth(image);
  127. size_t height = CGImageGetHeight(image);
  128. size_t croppedWidth, croppedHeight;
  129. if ([SCDeviceName isIphoneX]) {
  130. size_t adjustedWidth = (size_t)(width * kSCIPhoneXCapturedImageVideoCropRatio);
  131. size_t adjustedHeight = (size_t)(height * kSCIPhoneXCapturedImageVideoCropRatio);
  132. SCCropImageSizeToAspectRatio(adjustedWidth, adjustedHeight, orientation, aspectRatio, &croppedWidth,
  133. &croppedHeight);
  134. } else {
  135. SCCropImageSizeToAspectRatio(width, height, orientation, aspectRatio, &croppedWidth, &croppedHeight);
  136. }
  137. CGRect cropRect = SCCalculateRectToCrop(width, height, croppedWidth, croppedHeight);
  138. return CGImageCreateWithImageInRect(image, cropRect);
  139. }