Ei kuvausta
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.

NativeCamera.mm 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. #import <Foundation/Foundation.h>
  2. #import <MobileCoreServices/UTCoreTypes.h>
  3. #import <ImageIO/ImageIO.h>
  4. #import <AVFoundation/AVFoundation.h>
  5. #import <UIKit/UIKit.h>
  6. #ifdef UNITY_4_0 || UNITY_5_0
  7. #import "iPhone_View.h"
  8. #else
  9. extern UIViewController* UnityGetGLViewController();
  10. #endif
  11. #define CHECK_IOS_VERSION( version ) ([[[UIDevice currentDevice] systemVersion] compare:version options:NSNumericSearch] != NSOrderedAscending)
  12. @interface UNativeCamera:NSObject
  13. + (int)checkPermission;
  14. + (int)requestPermission:(BOOL)asyncMode;
  15. + (int)canOpenSettings;
  16. + (void)openSettings;
  17. + (int)hasCamera;
  18. + (void)openCamera:(BOOL)imageMode defaultCamera:(int)defaultCamera savePath:(NSString *)imageSavePath maxImageSize:(int)maxImageSize videoQuality:(int)videoQuality maxVideoDuration:(int)maxVideoDuration;
  19. + (int)isCameraBusy;
  20. + (char *)getImageProperties:(NSString *)path;
  21. + (char *)getVideoProperties:(NSString *)path;
  22. + (char *)getVideoThumbnail:(NSString *)path savePath:(NSString *)savePath maximumSize:(int)maximumSize captureTime:(double)captureTime;
  23. + (char *)loadImageAtPath:(NSString *)path tempFilePath:(NSString *)tempFilePath maximumSize:(int)maximumSize;
  24. @end
  25. @implementation UNativeCamera
  26. static NSString *pickedMediaSavePath;
  27. static UIImagePickerController *imagePicker;
  28. static int cameraMaxImageSize = -1;
  29. static int imagePickerState = 0; // 0 -> none, 1 -> showing, 2 -> finished
  30. static BOOL recordingVideo = NO;
  31. static AVAudioSessionCategory unityAudioSessionCategory = AVAudioSessionCategoryAmbient;
  32. static NSUInteger unityAudioSessionCategoryOptions = 1;
  33. static AVAudioSessionMode unityAudioSessionMode = AVAudioSessionModeDefault;
  34. // Credit: https://stackoverflow.com/a/20464727/2373034
  35. + (int)checkPermission
  36. {
  37. if( CHECK_IOS_VERSION( @"7.0" ) )
  38. {
  39. AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
  40. if( status == AVAuthorizationStatusAuthorized )
  41. return 1;
  42. else if( status == AVAuthorizationStatusNotDetermined )
  43. return 2;
  44. else
  45. return 0;
  46. }
  47. return 1;
  48. }
  49. // Credit: https://stackoverflow.com/a/20464727/2373034
  50. + (int)requestPermission:(BOOL)asyncMode
  51. {
  52. int result = [self requestPermissionInternal:asyncMode];
  53. if( asyncMode && result >= 0 ) // Result returned immediately, forward it
  54. UnitySendMessage( "NCPermissionCallbackiOS", "OnPermissionRequested", [self getCString:[NSString stringWithFormat:@"%d", result]] );
  55. return result;
  56. }
  57. + (int)requestPermissionInternal:(BOOL)asyncMode
  58. {
  59. if( CHECK_IOS_VERSION( @"7.0" ) )
  60. {
  61. AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
  62. if( status == AVAuthorizationStatusAuthorized )
  63. return 1;
  64. else if( status == AVAuthorizationStatusNotDetermined )
  65. {
  66. if( asyncMode )
  67. {
  68. [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^( BOOL granted )
  69. {
  70. UnitySendMessage( "NCPermissionCallbackiOS", "OnPermissionRequested", granted ? "1" : "0" );
  71. }];
  72. return -1;
  73. }
  74. else
  75. {
  76. __block BOOL authorized = NO;
  77. dispatch_semaphore_t sema = dispatch_semaphore_create( 0 );
  78. [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^( BOOL granted )
  79. {
  80. authorized = granted;
  81. dispatch_semaphore_signal( sema );
  82. }];
  83. dispatch_semaphore_wait( sema, DISPATCH_TIME_FOREVER );
  84. return authorized ? 1 : 0;
  85. }
  86. }
  87. else
  88. return 0;
  89. }
  90. return 1;
  91. }
  92. // Credit: https://stackoverflow.com/a/25453667/2373034
  93. + (int)canOpenSettings
  94. {
  95. return ( &UIApplicationOpenSettingsURLString != NULL ) ? 1 : 0;
  96. }
  97. // Credit: https://stackoverflow.com/a/25453667/2373034
  98. #pragma clang diagnostic push
  99. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  100. + (void)openSettings
  101. {
  102. if( &UIApplicationOpenSettingsURLString != NULL )
  103. {
  104. #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000
  105. if( CHECK_IOS_VERSION( @"10.0" ) )
  106. [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
  107. else
  108. #endif
  109. [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
  110. }
  111. }
  112. #pragma clang diagnostic pop
  113. + (int)hasCamera
  114. {
  115. return [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] ? 1 : 0;
  116. }
  117. // Credit: https://stackoverflow.com/a/10531752/2373034
  118. + (void)openCamera:(BOOL)imageMode defaultCamera:(int)defaultCamera savePath:(NSString *)imageSavePath maxImageSize:(int)maxImageSize videoQuality:(int)videoQuality maxVideoDuration:(int)maxVideoDuration
  119. {
  120. if( ![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] )
  121. {
  122. NSLog( @"Device has no registered cameras!" );
  123. UnitySendMessage( "NCCameraCallbackiOS", "OnMediaReceived", "" );
  124. return;
  125. }
  126. if( ( imageMode && ![[UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera] containsObject:(NSString*)kUTTypeImage] ) ||
  127. ( !imageMode && ![[UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera] containsObject:(NSString*)kUTTypeMovie] ) )
  128. {
  129. NSLog( @"Camera does not support this operation!" );
  130. UnitySendMessage( "NCCameraCallbackiOS", "OnMediaReceived", "" );
  131. return;
  132. }
  133. imagePicker = [[UIImagePickerController alloc] init];
  134. imagePicker.delegate = (id) self;
  135. imagePicker.allowsEditing = NO;
  136. imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
  137. if( imageMode )
  138. imagePicker.mediaTypes = [NSArray arrayWithObject:(NSString *)kUTTypeImage];
  139. else
  140. {
  141. imagePicker.mediaTypes = [NSArray arrayWithObject:(NSString *)kUTTypeMovie];
  142. if( maxVideoDuration > 0 )
  143. imagePicker.videoMaximumDuration = maxVideoDuration;
  144. if( videoQuality == 0 )
  145. imagePicker.videoQuality = UIImagePickerControllerQualityTypeLow;
  146. else if( videoQuality == 1 )
  147. imagePicker.videoQuality = UIImagePickerControllerQualityTypeMedium;
  148. else if( videoQuality == 2 )
  149. imagePicker.videoQuality = UIImagePickerControllerQualityTypeHigh;
  150. }
  151. if( defaultCamera == 0 && [UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear] )
  152. imagePicker.cameraDevice = UIImagePickerControllerCameraDeviceRear;
  153. else if( defaultCamera == 1 && [UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront] )
  154. imagePicker.cameraDevice = UIImagePickerControllerCameraDeviceFront;
  155. // Bugfix for https://github.com/yasirkula/UnityNativeCamera/issues/45
  156. if( !imageMode )
  157. {
  158. unityAudioSessionCategory = [[AVAudioSession sharedInstance] category];
  159. unityAudioSessionCategoryOptions = [[AVAudioSession sharedInstance] categoryOptions];
  160. unityAudioSessionMode = [[AVAudioSession sharedInstance] mode];
  161. }
  162. recordingVideo = !imageMode;
  163. pickedMediaSavePath = imageSavePath;
  164. cameraMaxImageSize = maxImageSize;
  165. imagePickerState = 1;
  166. [UnityGetGLViewController() presentViewController:imagePicker animated:YES completion:^{ imagePickerState = 0; }];
  167. }
  168. + (int)isCameraBusy
  169. {
  170. if( imagePickerState == 2 )
  171. return 1;
  172. if( imagePicker != nil )
  173. {
  174. if( imagePickerState == 1 || [imagePicker presentingViewController] == UnityGetGLViewController() )
  175. return 1;
  176. imagePicker = nil;
  177. [self restoreAudioSession];
  178. return 0;
  179. }
  180. return 0;
  181. }
  182. + (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
  183. {
  184. NSString *path = nil;
  185. if( [info[UIImagePickerControllerMediaType] isEqualToString:(NSString *)kUTTypeImage] )
  186. {
  187. NSLog( @"UIImagePickerController finished taking picture" );
  188. UIImage *image = info[UIImagePickerControllerEditedImage] ?: info[UIImagePickerControllerOriginalImage];
  189. if( image == nil )
  190. path = nil;
  191. else
  192. {
  193. NSString *extension = [pickedMediaSavePath pathExtension];
  194. BOOL saveAsJPEG = [extension caseInsensitiveCompare:@"jpg"] == NSOrderedSame || [extension caseInsensitiveCompare:@"jpeg"] == NSOrderedSame;
  195. // Try to save the image with metadata
  196. // CANCELED: a number of users reported that this method results in 90-degree rotated images, uncomment at your own risk
  197. // Credit: https://stackoverflow.com/a/15858955
  198. /*NSDictionary *metadata = [info objectForKey:UIImagePickerControllerMediaMetadata];
  199. NSMutableDictionary *mutableMetadata = nil;
  200. CFDictionaryRef metadataRef;
  201. CFStringRef imageType;
  202. if( saveAsJPEG )
  203. {
  204. mutableMetadata = [metadata mutableCopy];
  205. [mutableMetadata setObject:@(1.0) forKey:(__bridge NSString *)kCGImageDestinationLossyCompressionQuality];
  206. metadataRef = (__bridge CFDictionaryRef) mutableMetadata;
  207. imageType = kUTTypeJPEG;
  208. }
  209. else
  210. {
  211. metadataRef = (__bridge CFDictionaryRef) metadata;
  212. imageType = kUTTypePNG;
  213. }
  214. CGImageDestinationRef imageDestination = CGImageDestinationCreateWithURL( (__bridge CFURLRef) [NSURL fileURLWithPath:pickedMediaSavePath], imageType , 1, NULL );
  215. if( imageDestination == NULL )
  216. NSLog( @"Failed to create image destination" );
  217. else
  218. {
  219. CGImageDestinationAddImage( imageDestination, image.CGImage, metadataRef );
  220. if( CGImageDestinationFinalize( imageDestination ) )
  221. path = pickedMediaSavePath;
  222. else
  223. NSLog( @"Failed to finalize the image" );
  224. CFRelease( imageDestination );
  225. }*/
  226. if( path == nil )
  227. {
  228. //NSLog( @"Attempting to save the image without metadata as fallback" );
  229. if( ( saveAsJPEG && [UIImageJPEGRepresentation( [self scaleImage:image maxSize:cameraMaxImageSize], 1.0 ) writeToFile:pickedMediaSavePath atomically:YES] ) ||
  230. ( !saveAsJPEG && [UIImagePNGRepresentation( [self scaleImage:image maxSize:cameraMaxImageSize] ) writeToFile:pickedMediaSavePath atomically:YES] ) )
  231. path = pickedMediaSavePath;
  232. else
  233. {
  234. NSLog( @"Error saving image without metadata" );
  235. path = nil;
  236. }
  237. }
  238. }
  239. }
  240. else
  241. {
  242. NSLog( @"UIImagePickerController finished recording video" );
  243. NSURL *mediaUrl = info[UIImagePickerControllerMediaURL] ?: info[UIImagePickerControllerReferenceURL];
  244. if( mediaUrl == nil )
  245. path = nil;
  246. else
  247. path = [mediaUrl path];
  248. }
  249. imagePicker = nil;
  250. imagePickerState = 2;
  251. UnitySendMessage( "NCCameraCallbackiOS", "OnMediaReceived", [self getCString:path] );
  252. [picker dismissViewControllerAnimated:NO completion:nil];
  253. [self restoreAudioSession];
  254. }
  255. + (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
  256. {
  257. NSLog( @"UIImagePickerController cancelled" );
  258. imagePicker = nil;
  259. UnitySendMessage( "NCCameraCallbackiOS", "OnMediaReceived", "" );
  260. [picker dismissViewControllerAnimated:NO completion:nil];
  261. [self restoreAudioSession];
  262. }
  263. // Bugfix for https://github.com/yasirkula/UnityNativeCamera/issues/45
  264. + (void)restoreAudioSession
  265. {
  266. if( recordingVideo )
  267. {
  268. BOOL audioModeSwitchResult = YES;
  269. NSError *error = nil;
  270. #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000
  271. if( CHECK_IOS_VERSION( @"10.0" ) )
  272. audioModeSwitchResult = [[AVAudioSession sharedInstance] setCategory:unityAudioSessionCategory mode:unityAudioSessionMode options:unityAudioSessionCategoryOptions error:&error];
  273. else
  274. #endif
  275. audioModeSwitchResult = [[AVAudioSession sharedInstance] setCategory:unityAudioSessionCategory withOptions:unityAudioSessionCategoryOptions error:&error] && [[AVAudioSession sharedInstance] setMode:unityAudioSessionMode error:&error];
  276. if( !audioModeSwitchResult )
  277. {
  278. if( error != nil )
  279. NSLog( @"Error setting audio session category back to %@ with mode %@ and options %lu: %@", unityAudioSessionCategory, unityAudioSessionMode, (unsigned long) unityAudioSessionCategoryOptions, error );
  280. else
  281. NSLog( @"Error setting audio session category back to %@ with mode %@ and options %lu", unityAudioSessionCategory, unityAudioSessionMode, (unsigned long) unityAudioSessionCategoryOptions );
  282. }
  283. }
  284. }
  285. // Credit: https://stackoverflow.com/a/4170099/2373034
  286. + (NSArray *)getImageMetadata:(NSString *)path
  287. {
  288. int width = 0;
  289. int height = 0;
  290. int orientation = -1;
  291. CGImageSourceRef imageSource = CGImageSourceCreateWithURL( (__bridge CFURLRef) [NSURL fileURLWithPath:path], nil );
  292. if( imageSource != nil )
  293. {
  294. NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:(__bridge NSString *)kCGImageSourceShouldCache];
  295. CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex( imageSource, 0, (__bridge CFDictionaryRef) options );
  296. CFRelease( imageSource );
  297. CGFloat widthF = 0.0f, heightF = 0.0f;
  298. if( imageProperties != nil )
  299. {
  300. if( CFDictionaryContainsKey( imageProperties, kCGImagePropertyPixelWidth ) )
  301. CFNumberGetValue( (CFNumberRef) CFDictionaryGetValue( imageProperties, kCGImagePropertyPixelWidth ), kCFNumberCGFloatType, &widthF );
  302. if( CFDictionaryContainsKey( imageProperties, kCGImagePropertyPixelHeight ) )
  303. CFNumberGetValue( (CFNumberRef) CFDictionaryGetValue( imageProperties, kCGImagePropertyPixelHeight ), kCFNumberCGFloatType, &heightF );
  304. if( CFDictionaryContainsKey( imageProperties, kCGImagePropertyOrientation ) )
  305. {
  306. CFNumberGetValue( (CFNumberRef) CFDictionaryGetValue( imageProperties, kCGImagePropertyOrientation ), kCFNumberIntType, &orientation );
  307. if( orientation > 4 )
  308. {
  309. // Landscape image
  310. CGFloat temp = widthF;
  311. widthF = heightF;
  312. heightF = temp;
  313. }
  314. }
  315. CFRelease( imageProperties );
  316. }
  317. width = (int) roundf( widthF );
  318. height = (int) roundf( heightF );
  319. }
  320. return [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:width], [NSNumber numberWithInt:height], [NSNumber numberWithInt:orientation], nil];
  321. }
  322. + (char *)getImageProperties:(NSString *)path
  323. {
  324. NSArray *metadata = [self getImageMetadata:path];
  325. int orientationUnity;
  326. int orientation = [metadata[2] intValue];
  327. // To understand the magic numbers, see ImageOrientation enum in NativeCamera.cs
  328. // and http://sylvana.net/jpegcrop/exif_orientation.html
  329. if( orientation == 1 )
  330. orientationUnity = 0;
  331. else if( orientation == 2 )
  332. orientationUnity = 4;
  333. else if( orientation == 3 )
  334. orientationUnity = 2;
  335. else if( orientation == 4 )
  336. orientationUnity = 6;
  337. else if( orientation == 5 )
  338. orientationUnity = 5;
  339. else if( orientation == 6 )
  340. orientationUnity = 1;
  341. else if( orientation == 7 )
  342. orientationUnity = 7;
  343. else if( orientation == 8 )
  344. orientationUnity = 3;
  345. else
  346. orientationUnity = -1;
  347. return [self getCString:[NSString stringWithFormat:@"%d>%d> >%d", [metadata[0] intValue], [metadata[1] intValue], orientationUnity]];
  348. }
  349. + (char *)getVideoProperties:(NSString *)path
  350. {
  351. CGSize size = CGSizeZero;
  352. float rotation = 0;
  353. long long duration = 0;
  354. AVURLAsset *asset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:path] options:nil];
  355. if( asset != nil )
  356. {
  357. duration = (long long) round( CMTimeGetSeconds( [asset duration] ) * 1000 );
  358. CGAffineTransform transform = [asset preferredTransform];
  359. NSArray<AVAssetTrack *>* videoTracks = [asset tracksWithMediaType:AVMediaTypeVideo];
  360. if( videoTracks != nil && [videoTracks count] > 0 )
  361. {
  362. size = [[videoTracks objectAtIndex:0] naturalSize];
  363. transform = [[videoTracks objectAtIndex:0] preferredTransform];
  364. }
  365. rotation = atan2( transform.b, transform.a ) * ( 180.0 / M_PI );
  366. }
  367. return [self getCString:[NSString stringWithFormat:@"%d>%d>%lld>%f", (int) roundf( size.width ), (int) roundf( size.height ), duration, rotation]];
  368. }
  369. + (char *)getVideoThumbnail:(NSString *)path savePath:(NSString *)savePath maximumSize:(int)maximumSize captureTime:(double)captureTime
  370. {
  371. AVAssetImageGenerator *thumbnailGenerator = [[AVAssetImageGenerator alloc] initWithAsset:[[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:path] options:nil]];
  372. thumbnailGenerator.appliesPreferredTrackTransform = YES;
  373. thumbnailGenerator.maximumSize = CGSizeMake( (CGFloat) maximumSize, (CGFloat) maximumSize );
  374. thumbnailGenerator.requestedTimeToleranceBefore = kCMTimeZero;
  375. thumbnailGenerator.requestedTimeToleranceAfter = kCMTimeZero;
  376. if( captureTime < 0.0 )
  377. captureTime = 0.0;
  378. else
  379. {
  380. AVURLAsset *asset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:path] options:nil];
  381. if( asset != nil )
  382. {
  383. double videoDuration = CMTimeGetSeconds( [asset duration] );
  384. if( videoDuration > 0.0 && captureTime >= videoDuration - 0.1 )
  385. {
  386. if( captureTime > videoDuration )
  387. captureTime = videoDuration;
  388. thumbnailGenerator.requestedTimeToleranceBefore = CMTimeMakeWithSeconds( 1.0, 600 );
  389. }
  390. }
  391. }
  392. NSError *error = nil;
  393. CGImageRef image = [thumbnailGenerator copyCGImageAtTime:CMTimeMakeWithSeconds( captureTime, 600 ) actualTime:nil error:&error];
  394. if( image == nil )
  395. {
  396. if( error != nil )
  397. NSLog( @"Error generating video thumbnail: %@", error );
  398. else
  399. NSLog( @"Error generating video thumbnail..." );
  400. return [self getCString:@""];
  401. }
  402. UIImage *thumbnail = [[UIImage alloc] initWithCGImage:image];
  403. CGImageRelease( image );
  404. if( ![UIImagePNGRepresentation( thumbnail ) writeToFile:savePath atomically:YES] )
  405. {
  406. NSLog( @"Error saving thumbnail image" );
  407. return [self getCString:@""];
  408. }
  409. return [self getCString:savePath];
  410. }
  411. + (UIImage *)scaleImage:(UIImage *)image maxSize:(int)maxSize
  412. {
  413. CGFloat width = image.size.width;
  414. CGFloat height = image.size.height;
  415. UIImageOrientation orientation = image.imageOrientation;
  416. if( width <= maxSize && height <= maxSize && orientation != UIImageOrientationDown &&
  417. orientation != UIImageOrientationLeft && orientation != UIImageOrientationRight &&
  418. orientation != UIImageOrientationLeftMirrored && orientation != UIImageOrientationRightMirrored &&
  419. orientation != UIImageOrientationUpMirrored && orientation != UIImageOrientationDownMirrored )
  420. return image;
  421. CGFloat scaleX = 1.0f;
  422. CGFloat scaleY = 1.0f;
  423. if( width > maxSize )
  424. scaleX = maxSize / width;
  425. if( height > maxSize )
  426. scaleY = maxSize / height;
  427. // Credit: https://github.com/mbcharbonneau/UIImage-Categories/blob/master/UIImage%2BAlpha.m
  428. CGImageAlphaInfo alpha = CGImageGetAlphaInfo( image.CGImage );
  429. BOOL hasAlpha = alpha == kCGImageAlphaFirst || alpha == kCGImageAlphaLast || alpha == kCGImageAlphaPremultipliedFirst || alpha == kCGImageAlphaPremultipliedLast;
  430. CGFloat scaleRatio = scaleX < scaleY ? scaleX : scaleY;
  431. CGRect imageRect = CGRectMake( 0, 0, width * scaleRatio, height * scaleRatio );
  432. UIGraphicsBeginImageContextWithOptions( imageRect.size, !hasAlpha, image.scale );
  433. [image drawInRect:imageRect];
  434. image = UIGraphicsGetImageFromCurrentImageContext();
  435. UIGraphicsEndImageContext();
  436. return image;
  437. }
  438. + (char *)loadImageAtPath:(NSString *)path tempFilePath:(NSString *)tempFilePath maximumSize:(int)maximumSize
  439. {
  440. // Check if the image can be loaded by Unity without requiring a conversion to PNG
  441. // Credit: https://stackoverflow.com/a/12048937/2373034
  442. NSString *extension = [path pathExtension];
  443. BOOL conversionNeeded = [extension caseInsensitiveCompare:@"jpg"] != NSOrderedSame && [extension caseInsensitiveCompare:@"jpeg"] != NSOrderedSame && [extension caseInsensitiveCompare:@"png"] != NSOrderedSame;
  444. if( !conversionNeeded )
  445. {
  446. // Check if the image needs to be processed at all
  447. NSArray *metadata = [self getImageMetadata:path];
  448. int orientationInt = [metadata[2] intValue]; // 1: correct orientation, [1,8]: valid orientation range
  449. if( orientationInt == 1 && [metadata[0] intValue] <= maximumSize && [metadata[1] intValue] <= maximumSize )
  450. return [self getCString:path];
  451. }
  452. UIImage *image = [UIImage imageWithContentsOfFile:path];
  453. if( image == nil )
  454. return [self getCString:path];
  455. UIImage *scaledImage = [self scaleImage:image maxSize:maximumSize];
  456. if( conversionNeeded || scaledImage != image )
  457. {
  458. if( ![UIImagePNGRepresentation( scaledImage ) writeToFile:tempFilePath atomically:YES] )
  459. {
  460. NSLog( @"Error creating scaled image" );
  461. return [self getCString:path];
  462. }
  463. return [self getCString:tempFilePath];
  464. }
  465. else
  466. return [self getCString:path];
  467. }
  468. // Credit: https://stackoverflow.com/a/37052118/2373034
  469. + (char *)getCString:(NSString *)source
  470. {
  471. if( source == nil )
  472. source = @"";
  473. const char *sourceUTF8 = [source UTF8String];
  474. char *result = (char*) malloc( strlen( sourceUTF8 ) + 1 );
  475. strcpy( result, sourceUTF8 );
  476. return result;
  477. }
  478. @end
  479. extern "C" int _NativeCamera_CheckPermission()
  480. {
  481. return [UNativeCamera checkPermission];
  482. }
  483. extern "C" int _NativeCamera_RequestPermission( int asyncMode )
  484. {
  485. return [UNativeCamera requestPermission:( asyncMode == 1 )];
  486. }
  487. extern "C" int _NativeCamera_CanOpenSettings()
  488. {
  489. return [UNativeCamera canOpenSettings];
  490. }
  491. extern "C" void _NativeCamera_OpenSettings()
  492. {
  493. [UNativeCamera openSettings];
  494. }
  495. extern "C" int _NativeCamera_HasCamera()
  496. {
  497. return [UNativeCamera hasCamera];
  498. }
  499. extern "C" void _NativeCamera_TakePicture( const char* imageSavePath, int maxSize, int preferredCamera )
  500. {
  501. [UNativeCamera openCamera:YES defaultCamera:preferredCamera savePath:[NSString stringWithUTF8String:imageSavePath] maxImageSize:maxSize videoQuality:-1 maxVideoDuration:-1];
  502. }
  503. extern "C" void _NativeCamera_RecordVideo( int quality, int maxDuration, int preferredCamera )
  504. {
  505. [UNativeCamera openCamera:NO defaultCamera:preferredCamera savePath:nil maxImageSize:4096 videoQuality:quality maxVideoDuration:maxDuration];
  506. }
  507. extern "C" int _NativeCamera_IsCameraBusy()
  508. {
  509. return [UNativeCamera isCameraBusy];
  510. }
  511. extern "C" char* _NativeCamera_GetImageProperties( const char* path )
  512. {
  513. return [UNativeCamera getImageProperties:[NSString stringWithUTF8String:path]];
  514. }
  515. extern "C" char* _NativeCamera_GetVideoProperties( const char* path )
  516. {
  517. return [UNativeCamera getVideoProperties:[NSString stringWithUTF8String:path]];
  518. }
  519. extern "C" char* _NativeCamera_GetVideoThumbnail( const char* path, const char* thumbnailSavePath, int maxSize, double captureTimeInSeconds )
  520. {
  521. return [UNativeCamera getVideoThumbnail:[NSString stringWithUTF8String:path] savePath:[NSString stringWithUTF8String:thumbnailSavePath] maximumSize:maxSize captureTime:captureTimeInSeconds];
  522. }
  523. extern "C" char* _NativeCamera_LoadImageAtPath( const char* path, const char* temporaryFilePath, int maxSize )
  524. {
  525. return [UNativeCamera loadImageAtPath:[NSString stringWithUTF8String:path] tempFilePath:[NSString stringWithUTF8String:temporaryFilePath] maximumSize:maxSize];
  526. }