123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251 |
- #import <Foundation/Foundation.h>
- #import <Photos/Photos.h>
- #import <MobileCoreServices/UTCoreTypes.h>
- #import <MobileCoreServices/MobileCoreServices.h>
- #import <ImageIO/ImageIO.h>
- #import <PhotosUI/PhotosUI.h>
-
- extern UIViewController* UnityGetGLViewController();
-
- #define CHECK_IOS_VERSION( version ) ([[[UIDevice currentDevice] systemVersion] compare:version options:NSNumericSearch] != NSOrderedAscending)
-
- @interface UNativeGallery:NSObject
- + (int)checkPermission:(BOOL)readPermission permissionFreeMode:(BOOL)permissionFreeMode;
- + (void)requestPermission:(BOOL)readPermission permissionFreeMode:(BOOL)permissionFreeMode;
- + (void)showLimitedLibraryPicker;
- + (void)openSettings;
- + (int)canPickMultipleMedia;
- + (void)saveMedia:(NSString *)path albumName:(NSString *)album isImage:(BOOL)isImage permissionFreeMode:(BOOL)permissionFreeMode;
- + (void)pickMedia:(int)mediaType savePath:(NSString *)mediaSavePath permissionFreeMode:(BOOL)permissionFreeMode selectionLimit:(int)selectionLimit;
- + (int)isMediaPickerBusy;
- + (int)getMediaTypeFromExtension:(NSString *)extension;
- + (char *)getImageProperties:(NSString *)path;
- + (char *)getVideoProperties:(NSString *)path;
- + (char *)getVideoThumbnail:(NSString *)path savePath:(NSString *)savePath maximumSize:(int)maximumSize captureTime:(double)captureTime;
- + (char *)loadImageAtPath:(NSString *)path tempFilePath:(NSString *)tempFilePath maximumSize:(int)maximumSize;
- @end
-
- @implementation UNativeGallery
-
- static NSString *pickedMediaSavePath;
- static UIImagePickerController *imagePicker;
- API_AVAILABLE(ios(14))
- static PHPickerViewController *imagePickerNew;
- static int imagePickerState = 0; // 0 -> none, 1 -> showing (always in this state on iPad), 2 -> finished
- static BOOL simpleMediaPickMode;
- static BOOL pickingMultipleFiles = NO;
-
- + (int)checkPermission:(BOOL)readPermission permissionFreeMode:(BOOL)permissionFreeMode
- {
- // On iOS 11 and later, permission isn't mandatory to fetch media from Photos
- if( readPermission && permissionFreeMode && CHECK_IOS_VERSION( @"11.0" ) )
- return 1;
-
- // Photos permissions has changed on iOS 14
- if( @available(iOS 14.0, *) )
- {
- // Request ReadWrite permission in 2 cases:
- // 1) When attempting to pick media from Photos with PHPhotoLibrary (readPermission=true and permissionFreeMode=false)
- // 2) When attempting to write media to a specific album in Photos using PHPhotoLibrary (readPermission=false and permissionFreeMode=false)
- PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatusForAccessLevel:( ( readPermission || !permissionFreeMode ) ? PHAccessLevelReadWrite : PHAccessLevelAddOnly )];
- if( status == PHAuthorizationStatusAuthorized )
- return 1;
- else if( status == PHAuthorizationStatusRestricted )
- return 3;
- else if( status == PHAuthorizationStatusNotDetermined )
- return 2;
- else
- return 0;
- }
- else
- {
- PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
- if( status == PHAuthorizationStatusAuthorized )
- return 1;
- else if( status == PHAuthorizationStatusNotDetermined )
- return 2;
- else
- return 0;
- }
- }
-
- + (void)requestPermission:(BOOL)readPermission permissionFreeMode:(BOOL)permissionFreeMode
- {
- // On iOS 11 and later, permission isn't mandatory to fetch media from Photos
- if( readPermission && permissionFreeMode && CHECK_IOS_VERSION( @"11.0" ) )
- UnitySendMessage( "NGPermissionCallbackiOS", "OnPermissionRequested", "1" );
- else if( @available(iOS 14.0, *) )
- {
- // Photos permissions has changed on iOS 14. There are 2 permission dialogs now:
- // - AddOnly permission dialog: has 2 options: "Allow" and "Don't Allow". This dialog grants permission for save operations only. Unfortunately,
- // saving media to a custom album isn't possible with this dialog, media can only be saved to the default Photos album
- // - ReadWrite permission dialog: has 3 options: "Allow Access to All Photos" (i.e. full permission), "Select Photos" (i.e. limited access) and
- // "Don't Allow". To be able to save media to a custom album, user must grant Full Photos permission. Thus, even when readPermission is false,
- // this dialog will be used if PermissionFreeMode is set to false. So, PermissionFreeMode determines whether or not saving to a custom album is supported
- PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatusForAccessLevel:( readPermission ? PHAccessLevelReadWrite : PHAccessLevelAddOnly )];
- if( status == PHAuthorizationStatusAuthorized )
- UnitySendMessage( "NGPermissionCallbackiOS", "OnPermissionRequested", "1" );
- else if( status == PHAuthorizationStatusRestricted )
- UnitySendMessage( "NGPermissionCallbackiOS", "OnPermissionRequested", "3" );
- else if( status == PHAuthorizationStatusNotDetermined )
- {
- [PHPhotoLibrary requestAuthorizationForAccessLevel:( readPermission ? PHAccessLevelReadWrite : PHAccessLevelAddOnly ) handler:^( PHAuthorizationStatus status )
- {
- if( status == PHAuthorizationStatusAuthorized )
- UnitySendMessage( "NGPermissionCallbackiOS", "OnPermissionRequested", "1" );
- else if( status == PHAuthorizationStatusRestricted )
- UnitySendMessage( "NGPermissionCallbackiOS", "OnPermissionRequested", "3" );
- else
- UnitySendMessage( "NGPermissionCallbackiOS", "OnPermissionRequested", "0" );
- }];
- }
- else
- UnitySendMessage( "NGPermissionCallbackiOS", "OnPermissionRequested", "0" );
- }
- else
- {
- // Request permission using Photos framework: https://stackoverflow.com/a/32989022/2373034
- PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
- if( status == PHAuthorizationStatusAuthorized )
- UnitySendMessage( "NGPermissionCallbackiOS", "OnPermissionRequested", "1" );
- else if( status == PHAuthorizationStatusNotDetermined )
- {
- [PHPhotoLibrary requestAuthorization:^( PHAuthorizationStatus status )
- {
- UnitySendMessage( "NGPermissionCallbackiOS", "OnPermissionRequested", ( status == PHAuthorizationStatusAuthorized ) ? "1" : "0" );
- }];
- }
- else
- UnitySendMessage( "NGPermissionCallbackiOS", "OnPermissionRequested", "0" );
- }
- }
-
- // When Photos permission is set to restricted, allows user to change the permission or change the list of restricted images
- // It doesn't support a deterministic callback; for example there is a photoLibraryDidChange event but it won't be invoked if
- // user doesn't change the list of restricted images
- + (void)showLimitedLibraryPicker
- {
- if( @available(iOS 14.0, *) )
- {
- PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatusForAccessLevel:PHAccessLevelReadWrite];
- if( status == PHAuthorizationStatusNotDetermined )
- [self requestPermission:YES permissionFreeMode:NO];
- else if( status == PHAuthorizationStatusRestricted )
- [[PHPhotoLibrary sharedPhotoLibrary] presentLimitedLibraryPickerFromViewController:UnityGetGLViewController()];
- }
- }
-
- // Credit: https://stackoverflow.com/a/25453667/2373034
- + (void)openSettings
- {
- [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
- }
-
- + (int)canPickMultipleMedia
- {
- if( @available(iOS 14.0, *) )
- return 1;
- else
- return 0;
- }
-
- // Credit: https://stackoverflow.com/a/39909129/2373034
- + (void)saveMedia:(NSString *)path albumName:(NSString *)album isImage:(BOOL)isImage permissionFreeMode:(BOOL)permissionFreeMode
- {
- // On iOS 14+, permission workflow has changed significantly with the addition of PHAuthorizationStatusRestricted permission. On those versions,
- // user must grant Full Photos permission to be able to save to a custom album. Hence, there are 2 workflows:
- // - If PermissionFreeMode is enabled, save the media directly to the default album (i.e. ignore 'album' parameter). This will present a simple
- // permission dialog stating "The app requires access to Photos to save media to it." and the "Selected Photos" permission won't be listed in the options
- // - Otherwise, the more complex "The app requires access to Photos to interact with it." permission dialog will be shown and if the user grants
- // Full Photos permission, only then the image will be saved to the specified album. If user selects "Selected Photos" permission, default album will be
- // used as fallback
- void (^saveToPhotosAlbum)() = ^void()
- {
- if( isImage )
- {
- // Try preserving image metadata (essential for animated gif images)
- [[PHPhotoLibrary sharedPhotoLibrary] performChanges:
- ^{
- [PHAssetChangeRequest creationRequestForAssetFromImageAtFileURL:[NSURL fileURLWithPath:path]];
- }
- completionHandler:^( BOOL success, NSError *error )
- {
- if( success )
- {
- [[NSFileManager defaultManager] removeItemAtPath:path error:nil];
- UnitySendMessage( "NGMediaSaveCallbackiOS", "OnMediaSaveCompleted", "" );
- }
- else
- {
- NSLog( @"Error creating asset in default Photos album: %@", error );
-
- UIImage *image = [UIImage imageWithContentsOfFile:path];
- if( image != nil )
- UIImageWriteToSavedPhotosAlbum( image, self, @selector(image:didFinishSavingWithError:contextInfo:), (__bridge_retained void *) path );
- else
- {
- NSLog( @"Couldn't create UIImage from file at path: %@", path );
- [[NSFileManager defaultManager] removeItemAtPath:path error:nil];
- UnitySendMessage( "NGMediaSaveCallbackiOS", "OnMediaSaveFailed", "" );
- }
- }
- }];
- }
- else
- {
- if( UIVideoAtPathIsCompatibleWithSavedPhotosAlbum( path ) )
- UISaveVideoAtPathToSavedPhotosAlbum( path, self, @selector(video:didFinishSavingWithError:contextInfo:), (__bridge_retained void *) path );
- else
- {
- NSLog( @"Video at path isn't compatible with saved photos album: %@", path );
- [[NSFileManager defaultManager] removeItemAtPath:path error:nil];
- UnitySendMessage( "NGMediaSaveCallbackiOS", "OnMediaSaveFailed", "" );
- }
- }
- };
-
- void (^saveBlock)(PHAssetCollection *assetCollection) = ^void( PHAssetCollection *assetCollection )
- {
- [[PHPhotoLibrary sharedPhotoLibrary] performChanges:
- ^{
- PHAssetChangeRequest *assetChangeRequest;
- if( isImage )
- assetChangeRequest = [PHAssetChangeRequest creationRequestForAssetFromImageAtFileURL:[NSURL fileURLWithPath:path]];
- else
- assetChangeRequest = [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:[NSURL fileURLWithPath:path]];
-
- PHAssetCollectionChangeRequest *assetCollectionChangeRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:assetCollection];
- [assetCollectionChangeRequest addAssets:@[[assetChangeRequest placeholderForCreatedAsset]]];
-
- }
- completionHandler:^( BOOL success, NSError *error )
- {
- if( success )
- {
- [[NSFileManager defaultManager] removeItemAtPath:path error:nil];
- UnitySendMessage( "NGMediaSaveCallbackiOS", "OnMediaSaveCompleted", "" );
- }
- else
- {
- NSLog( @"Error creating asset: %@", error );
- saveToPhotosAlbum();
- }
- }];
- };
-
- if( permissionFreeMode && CHECK_IOS_VERSION( @"14.0" ) )
- saveToPhotosAlbum();
- else
- {
- PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
- fetchOptions.predicate = [NSPredicate predicateWithFormat:@"localizedTitle = %@", album];
- PHFetchResult *fetchResult = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAny options:fetchOptions];
- if( fetchResult.count > 0 )
- saveBlock( fetchResult.firstObject);
- else
- {
- __block PHObjectPlaceholder *albumPlaceholder;
- [[PHPhotoLibrary sharedPhotoLibrary] performChanges:
- ^{
- PHAssetCollectionChangeRequest *changeRequest = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:album];
- albumPlaceholder = changeRequest.placeholderForCreatedAssetCollection;
- }
- completionHandler:^( BOOL success, NSError *error )
- {
- if( success )
- {
- PHFetchResult *fetchResult = [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[albumPlaceholder.localIdentifier] options:nil];
- if( fetchResult.count > 0 )
- saveBlock( fetchResult.firstObject);
- else
- {
- NSLog( @"Error creating album: Album placeholder not found" );
- saveToPhotosAlbum();
- }
- }
- else
- {
- NSLog( @"Error creating album: %@", error );
- saveToPhotosAlbum();
- }
- }];
- }
- }
- }
-
- + (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
- {
- NSString* path = (__bridge_transfer NSString *)(contextInfo);
- [[NSFileManager defaultManager] removeItemAtPath:path error:nil];
-
- if( error == nil )
- UnitySendMessage( "NGMediaSaveCallbackiOS", "OnMediaSaveCompleted", "" );
- else
- {
- NSLog( @"Error saving image with UIImageWriteToSavedPhotosAlbum: %@", error );
- UnitySendMessage( "NGMediaSaveCallbackiOS", "OnMediaSaveFailed", "" );
- }
- }
-
- + (void)video:(NSString *)videoPath didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
- {
- NSString* path = (__bridge_transfer NSString *)(contextInfo);
- [[NSFileManager defaultManager] removeItemAtPath:path error:nil];
-
- if( error == nil )
- UnitySendMessage( "NGMediaSaveCallbackiOS", "OnMediaSaveCompleted", "" );
- else
- {
- NSLog( @"Error saving video with UISaveVideoAtPathToSavedPhotosAlbum: %@", error );
- UnitySendMessage( "NGMediaSaveCallbackiOS", "OnMediaSaveFailed", "" );
- }
- }
-
- // Credit: https://stackoverflow.com/a/10531752/2373034
- + (void)pickMedia:(int)mediaType savePath:(NSString *)mediaSavePath permissionFreeMode:(BOOL)permissionFreeMode selectionLimit:(int)selectionLimit
- {
- pickedMediaSavePath = mediaSavePath;
- imagePickerState = 1;
- simpleMediaPickMode = permissionFreeMode && CHECK_IOS_VERSION( @"11.0" );
-
- if( @available(iOS 14.0, *) )
- {
- // PHPickerViewController is used on iOS 14
- PHPickerConfiguration *config = simpleMediaPickMode ? [[PHPickerConfiguration alloc] init] : [[PHPickerConfiguration alloc] initWithPhotoLibrary:[PHPhotoLibrary sharedPhotoLibrary]];
- config.preferredAssetRepresentationMode = PHPickerConfigurationAssetRepresentationModeCurrent;
- config.selectionLimit = selectionLimit;
- pickingMultipleFiles = selectionLimit != 1;
-
- // mediaType is a bitmask:
- // 1: image
- // 2: video
- // 4: audio (not supported)
- if( mediaType == 1 )
- config.filter = [PHPickerFilter anyFilterMatchingSubfilters:[NSArray arrayWithObjects:[PHPickerFilter imagesFilter], [PHPickerFilter livePhotosFilter], nil]];
- else if( mediaType == 2 )
- config.filter = [PHPickerFilter videosFilter];
- else
- config.filter = [PHPickerFilter anyFilterMatchingSubfilters:[NSArray arrayWithObjects:[PHPickerFilter imagesFilter], [PHPickerFilter livePhotosFilter], [PHPickerFilter videosFilter], nil]];
-
- imagePickerNew = [[PHPickerViewController alloc] initWithConfiguration:config];
- imagePickerNew.delegate = (id) self;
- [UnityGetGLViewController() presentViewController:imagePickerNew animated:YES completion:^{ imagePickerState = 0; }];
- }
- else
- {
- // UIImagePickerController is used on previous versions
- imagePicker = [[UIImagePickerController alloc] init];
- imagePicker.delegate = (id) self;
- imagePicker.allowsEditing = NO;
- imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
-
- // mediaType is a bitmask:
- // 1: image
- // 2: video
- // 4: audio (not supported)
- if( mediaType == 1 )
- imagePicker.mediaTypes = [NSArray arrayWithObjects:(NSString *)kUTTypeImage, (NSString *)kUTTypeLivePhoto, nil];
- else if( mediaType == 2 )
- imagePicker.mediaTypes = [NSArray arrayWithObjects:(NSString *)kUTTypeMovie, (NSString *)kUTTypeVideo, nil];
- else
- imagePicker.mediaTypes = [NSArray arrayWithObjects:(NSString *)kUTTypeImage, (NSString *)kUTTypeLivePhoto, (NSString *)kUTTypeMovie, (NSString *)kUTTypeVideo, nil];
-
- if( mediaType != 1 )
- {
- // Don't compress picked videos if possible
- imagePicker.videoExportPreset = AVAssetExportPresetPassthrough;
- }
-
- UIViewController *rootViewController = UnityGetGLViewController();
- if( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ) // iPad
- {
- imagePicker.modalPresentationStyle = UIModalPresentationPopover;
- UIPopoverPresentationController *popover = imagePicker.popoverPresentationController;
- if( popover != nil )
- {
- popover.sourceView = rootViewController.view;
- popover.sourceRect = CGRectMake( rootViewController.view.frame.size.width / 2, rootViewController.view.frame.size.height / 2, 1, 1 );
- popover.permittedArrowDirections = 0;
- }
- }
-
- [rootViewController presentViewController:imagePicker animated:YES completion:^{ imagePickerState = 0; }];
- }
- }
-
- + (int)isMediaPickerBusy
- {
- if( imagePickerState == 2 )
- return 1;
-
- if( imagePicker != nil )
- {
- if( imagePickerState == 1 || [imagePicker presentingViewController] == UnityGetGLViewController() )
- return 1;
- else
- {
- imagePicker = nil;
- return 0;
- }
- }
- else if( @available(iOS 14.0, *) )
- {
- if( imagePickerNew == nil )
- return 0;
- else if( imagePickerState == 1 || [imagePickerNew presentingViewController] == UnityGetGLViewController() )
- return 1;
- else
- {
- imagePickerNew = nil;
- return 0;
- }
- }
- else
- return 0;
- }
-
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wdeprecated-declarations"
- + (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
- {
- NSString *resultPath = nil;
-
- if( [info[UIImagePickerControllerMediaType] isEqualToString:(NSString *)kUTTypeImage] )
- {
- NSLog( @"Picked an image" );
-
- // Try to obtain the raw data of the image (which allows picking gifs properly or preserving metadata)
- PHAsset *asset = nil;
-
- // Try fetching the source image via UIImagePickerControllerImageURL
- NSURL *mediaUrl = info[UIImagePickerControllerImageURL];
- if( mediaUrl != nil )
- {
- NSString *imagePath = [mediaUrl path];
- if( imagePath != nil && [[NSFileManager defaultManager] fileExistsAtPath:imagePath] )
- {
- NSError *error;
- NSString *newPath = [pickedMediaSavePath stringByAppendingPathExtension:[imagePath pathExtension]];
-
- if( ![[NSFileManager defaultManager] fileExistsAtPath:newPath] || [[NSFileManager defaultManager] removeItemAtPath:newPath error:&error] )
- {
- if( [[NSFileManager defaultManager] copyItemAtPath:imagePath toPath:newPath error:&error] )
- {
- resultPath = newPath;
- NSLog( @"Copied source image from UIImagePickerControllerImageURL" );
- }
- else
- NSLog( @"Error copying image: %@", error );
- }
- else
- NSLog( @"Error deleting existing image: %@", error );
- }
- }
-
- if( resultPath == nil )
- asset = info[UIImagePickerControllerPHAsset];
-
- if( resultPath == nil && !simpleMediaPickMode )
- {
- if( asset == nil )
- {
- mediaUrl = info[UIImagePickerControllerReferenceURL] ?: info[UIImagePickerControllerMediaURL];
- if( mediaUrl != nil )
- asset = [[PHAsset fetchAssetsWithALAssetURLs:[NSArray arrayWithObject:mediaUrl] options:nil] firstObject];
- }
-
- resultPath = [self trySavePHAsset:asset atIndex:1];
- }
-
- if( resultPath == nil )
- {
- // Save image as PNG
- UIImage *image = info[UIImagePickerControllerOriginalImage];
- if( image != nil )
- {
- resultPath = [pickedMediaSavePath stringByAppendingPathExtension:@"png"];
- if( ![self saveImageAsPNG:image toPath:resultPath] )
- {
- NSLog( @"Error creating PNG image" );
- resultPath = nil;
- }
- }
- else
- NSLog( @"Error fetching original image from picker" );
- }
- }
- else if( [info[UIImagePickerControllerMediaType] isEqualToString:(NSString *)kUTTypeLivePhoto] )
- {
- NSLog( @"Picked a live photo" );
-
- // Save live photo as PNG
- UIImage *image = info[UIImagePickerControllerOriginalImage];
- if( image != nil )
- {
- resultPath = [pickedMediaSavePath stringByAppendingPathExtension:@"png"];
- if( ![self saveImageAsPNG:image toPath:resultPath] )
- {
- NSLog( @"Error creating PNG image" );
- resultPath = nil;
- }
- }
- else
- NSLog( @"Error fetching live photo's still image from picker" );
- }
- else
- {
- NSLog( @"Picked a video" );
-
- NSURL *mediaUrl = info[UIImagePickerControllerMediaURL] ?: info[UIImagePickerControllerReferenceURL];
- if( mediaUrl != nil )
- {
- resultPath = [mediaUrl path];
-
- // On iOS 13, picked file becomes unreachable as soon as the UIImagePickerController disappears,
- // in that case, copy the video to a temporary location
- if( @available(iOS 13.0, *) )
- {
- NSError *error;
- NSString *newPath = [pickedMediaSavePath stringByAppendingPathExtension:[resultPath pathExtension]];
-
- if( ![[NSFileManager defaultManager] fileExistsAtPath:newPath] || [[NSFileManager defaultManager] removeItemAtPath:newPath error:&error] )
- {
- if( [[NSFileManager defaultManager] copyItemAtPath:resultPath toPath:newPath error:&error] )
- resultPath = newPath;
- else
- {
- NSLog( @"Error copying video: %@", error );
- resultPath = nil;
- }
- }
- else
- {
- NSLog( @"Error deleting existing video: %@", error );
- resultPath = nil;
- }
- }
- }
- }
-
- imagePicker = nil;
- imagePickerState = 2;
- UnitySendMessage( "NGMediaReceiveCallbackiOS", "OnMediaReceived", [self getCString:resultPath] );
-
- [picker dismissViewControllerAnimated:NO completion:nil];
- }
- #pragma clang diagnostic pop
-
- // Credit: https://ikyle.me/blog/2020/phpickerviewcontroller
- +(void)picker:(PHPickerViewController *)picker didFinishPicking:(NSArray<PHPickerResult *> *)results API_AVAILABLE(ios(14))
- {
- imagePickerNew = nil;
- imagePickerState = 2;
-
- [picker dismissViewControllerAnimated:NO completion:nil];
-
- if( results != nil && [results count] > 0 )
- {
- NSMutableArray<NSString *> *resultPaths = [NSMutableArray arrayWithCapacity:[results count]];
- NSLock *arrayLock = [[NSLock alloc] init];
- dispatch_group_t group = dispatch_group_create();
-
- for( int i = 0; i < [results count]; i++ )
- {
- PHPickerResult *result = results[i];
- NSItemProvider *itemProvider = result.itemProvider;
- NSString *assetIdentifier = result.assetIdentifier;
- __block NSString *resultPath = nil;
-
- int j = i + 1;
-
- //NSLog( @"result: %@", result );
- //NSLog( @"%@", result.assetIdentifier);
- //NSLog( @"%@", result.itemProvider);
-
- if( [itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage] )
- {
- NSLog( @"Picked an image" );
-
- if( !simpleMediaPickMode && assetIdentifier != nil )
- {
- PHAsset *asset = [[PHAsset fetchAssetsWithLocalIdentifiers:[NSArray arrayWithObject:assetIdentifier] options:nil] firstObject];
- resultPath = [self trySavePHAsset:asset atIndex:j];
- }
-
- if( resultPath != nil )
- {
- [arrayLock lock];
- [resultPaths addObject:resultPath];
- [arrayLock unlock];
- }
- else
- {
- dispatch_group_enter( group );
-
- [itemProvider loadFileRepresentationForTypeIdentifier:(NSString *)kUTTypeImage completionHandler:^( NSURL *url, NSError *error )
- {
- if( url != nil )
- {
- // Copy the image to a temporary location because the returned image will be deleted by the OS after this callback is completed
- resultPath = [url path];
- NSString *newPath = [[NSString stringWithFormat:@"%@%d", pickedMediaSavePath, j] stringByAppendingPathExtension:[resultPath pathExtension]];
-
- if( ![[NSFileManager defaultManager] fileExistsAtPath:newPath] || [[NSFileManager defaultManager] removeItemAtPath:newPath error:&error] )
- {
- if( [[NSFileManager defaultManager] copyItemAtPath:resultPath toPath:newPath error:&error])
- resultPath = newPath;
- else
- {
- NSLog( @"Error copying image: %@", error );
- resultPath = nil;
- }
- }
- else
- {
- NSLog( @"Error deleting existing image: %@", error );
- resultPath = nil;
- }
- }
- else
- NSLog( @"Error getting the picked image's path: %@", error );
-
- if( resultPath != nil )
- {
- [arrayLock lock];
- [resultPaths addObject:resultPath];
- [arrayLock unlock];
- }
- else
- {
- if( [itemProvider canLoadObjectOfClass:[UIImage class]] )
- {
- dispatch_group_enter( group );
-
- [itemProvider loadObjectOfClass:[UIImage class] completionHandler:^( __kindof id<NSItemProviderReading> object, NSError *error )
- {
- if( object != nil && [object isKindOfClass:[UIImage class]] )
- {
- resultPath = [[NSString stringWithFormat:@"%@%d", pickedMediaSavePath, j] stringByAppendingPathExtension:@"png"];
- if( ![self saveImageAsPNG:(UIImage *)object toPath:resultPath] )
- {
- NSLog( @"Error creating PNG image" );
- resultPath = nil;
- }
- }
- else
- NSLog( @"Error generating UIImage from picked image: %@", error );
-
- [arrayLock lock];
- [resultPaths addObject:( resultPath != nil ? resultPath : @"" )];
- [arrayLock unlock];
-
- dispatch_group_leave( group );
- }];
- }
- else
- {
- NSLog( @"Can't generate UIImage from picked image" );
-
- [arrayLock lock];
- [resultPaths addObject:@""];
- [arrayLock unlock];
- }
- }
-
- dispatch_group_leave( group );
- }];
- }
- }
- else if( [itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeLivePhoto] )
- {
- NSLog( @"Picked a live photo" );
-
- if( [itemProvider canLoadObjectOfClass:[UIImage class]] )
- {
- dispatch_group_enter( group );
-
- [itemProvider loadObjectOfClass:[UIImage class] completionHandler:^( __kindof id<NSItemProviderReading> object, NSError *error )
- {
- if( object != nil && [object isKindOfClass:[UIImage class]] )
- {
- resultPath = [[NSString stringWithFormat:@"%@%d", pickedMediaSavePath, j] stringByAppendingPathExtension:@"png"];
- if( ![self saveImageAsPNG:(UIImage *)object toPath:resultPath] )
- {
- NSLog( @"Error creating PNG image" );
- resultPath = nil;
- }
- }
- else
- NSLog( @"Error generating UIImage from picked live photo: %@", error );
-
- [arrayLock lock];
- [resultPaths addObject:( resultPath != nil ? resultPath : @"" )];
- [arrayLock unlock];
-
- dispatch_group_leave( group );
- }];
- }
- else if( [itemProvider canLoadObjectOfClass:[PHLivePhoto class]] )
- {
- dispatch_group_enter( group );
-
- [itemProvider loadObjectOfClass:[PHLivePhoto class] completionHandler:^( __kindof id<NSItemProviderReading> object, NSError *error )
- {
- if( object != nil && [object isKindOfClass:[PHLivePhoto class]] )
- {
- // Extract image data from live photo
- // Credit: https://stackoverflow.com/a/41341675/2373034
- NSArray<PHAssetResource*>* livePhotoResources = [PHAssetResource assetResourcesForLivePhoto:(PHLivePhoto *)object];
-
- PHAssetResource *livePhotoImage = nil;
- for( int k = 0; k < [livePhotoResources count]; k++ )
- {
- if( livePhotoResources[k].type == PHAssetResourceTypePhoto )
- {
- livePhotoImage = livePhotoResources[k];
- break;
- }
- }
-
- if( livePhotoImage == nil )
- {
- NSLog( @"Error extracting image data from live photo" );
-
- [arrayLock lock];
- [resultPaths addObject:@""];
- [arrayLock unlock];
- }
- else
- {
- dispatch_group_enter( group );
-
- NSString *originalFilename = livePhotoImage.originalFilename;
- if( originalFilename == nil || [originalFilename length] == 0 )
- resultPath = [NSString stringWithFormat:@"%@%d", pickedMediaSavePath, j];
- else
- resultPath = [[NSString stringWithFormat:@"%@%d", pickedMediaSavePath, j] stringByAppendingPathExtension:[originalFilename pathExtension]];
-
- [[PHAssetResourceManager defaultManager] writeDataForAssetResource:livePhotoImage toFile:[NSURL fileURLWithPath:resultPath] options:nil completionHandler:^( NSError * _Nullable error2 )
- {
- if( error2 != nil )
- {
- NSLog( @"Error saving image data from live photo: %@", error2 );
- resultPath = nil;
- }
-
- [arrayLock lock];
- [resultPaths addObject:( resultPath != nil ? resultPath : @"" )];
- [arrayLock unlock];
-
- dispatch_group_leave( group );
- }];
- }
- }
- else
- {
- NSLog( @"Error generating PHLivePhoto from picked live photo: %@", error );
-
- [arrayLock lock];
- [resultPaths addObject:@""];
- [arrayLock unlock];
- }
-
- dispatch_group_leave( group );
- }];
- }
- else
- {
- NSLog( @"Can't convert picked live photo to still image" );
-
- [arrayLock lock];
- [resultPaths addObject:@""];
- [arrayLock unlock];
- }
- }
- else if( [itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeMovie] || [itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeVideo] )
- {
- NSLog( @"Picked a video" );
-
- // Get the video file's path
- dispatch_group_enter( group );
-
- [itemProvider loadFileRepresentationForTypeIdentifier:([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeMovie] ? (NSString *)kUTTypeMovie : (NSString *)kUTTypeVideo) completionHandler:^( NSURL *url, NSError *error )
- {
- if( url != nil )
- {
- // Copy the video to a temporary location because the returned video will be deleted by the OS after this callback is completed
- resultPath = [url path];
- NSString *newPath = [[NSString stringWithFormat:@"%@%d", pickedMediaSavePath, j] stringByAppendingPathExtension:[resultPath pathExtension]];
-
- if( ![[NSFileManager defaultManager] fileExistsAtPath:newPath] || [[NSFileManager defaultManager] removeItemAtPath:newPath error:&error] )
- {
- if( [[NSFileManager defaultManager] copyItemAtPath:resultPath toPath:newPath error:&error])
- resultPath = newPath;
- else
- {
- NSLog( @"Error copying video: %@", error );
- resultPath = nil;
- }
- }
- else
- {
- NSLog( @"Error deleting existing video: %@", error );
- resultPath = nil;
- }
- }
- else
- NSLog( @"Error getting the picked video's path: %@", error );
-
- [arrayLock lock];
- [resultPaths addObject:( resultPath != nil ? resultPath : @"" )];
- [arrayLock unlock];
-
- dispatch_group_leave( group );
- }];
- }
- else
- {
- // Unknown media type picked?
- NSLog( @"Couldn't determine type of picked media: %@", itemProvider );
-
- [arrayLock lock];
- [resultPaths addObject:@""];
- [arrayLock unlock];
- }
- }
-
- dispatch_group_notify( group, dispatch_get_main_queue(),
- ^{
- if( !pickingMultipleFiles )
- UnitySendMessage( "NGMediaReceiveCallbackiOS", "OnMediaReceived", [self getCString:resultPaths[0]] );
- else
- UnitySendMessage( "NGMediaReceiveCallbackiOS", "OnMultipleMediaReceived", [self getCString:[resultPaths componentsJoinedByString:@">"]] );
- });
- }
- else
- {
- NSLog( @"No media picked" );
-
- if( !pickingMultipleFiles )
- UnitySendMessage( "NGMediaReceiveCallbackiOS", "OnMediaReceived", "" );
- else
- UnitySendMessage( "NGMediaReceiveCallbackiOS", "OnMultipleMediaReceived", "" );
- }
- }
-
- + (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
- {
- NSLog( @"UIImagePickerController cancelled" );
-
- imagePicker = nil;
- UnitySendMessage( "NGMediaReceiveCallbackiOS", "OnMediaReceived", "" );
-
- [picker dismissViewControllerAnimated:NO completion:nil];
- }
-
- + (NSString *)trySavePHAsset:(PHAsset *)asset atIndex:(int)filenameIndex
- {
- if( asset == nil )
- return nil;
-
- __block NSString *resultPath = nil;
-
- PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
- options.synchronous = YES;
- options.version = PHImageRequestOptionsVersionCurrent;
-
- if( @available(iOS 13.0, *) )
- {
- [[PHImageManager defaultManager] requestImageDataAndOrientationForAsset:asset options:options resultHandler:^( NSData *imageData, NSString *dataUTI, CGImagePropertyOrientation orientation, NSDictionary *imageInfo )
- {
- if( imageData != nil )
- resultPath = [self trySaveSourceImage:imageData withInfo:imageInfo atIndex:filenameIndex];
- else
- NSLog( @"Couldn't fetch raw image data" );
- }];
- }
- else
- {
- [[PHImageManager defaultManager] requestImageDataForAsset:asset options:options resultHandler:^( NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *imageInfo )
- {
- if( imageData != nil )
- resultPath = [self trySaveSourceImage:imageData withInfo:imageInfo atIndex:filenameIndex];
- else
- NSLog( @"Couldn't fetch raw image data" );
- }];
- }
-
- return resultPath;
- }
-
- + (NSString *)trySaveSourceImage:(NSData *)imageData withInfo:(NSDictionary *)info atIndex:(int)filenameIndex
- {
- NSString *filePath = info[@"PHImageFileURLKey"];
- if( filePath != nil ) // filePath can actually be an NSURL, convert it to NSString
- filePath = [NSString stringWithFormat:@"%@", filePath];
-
- if( filePath == nil || [filePath length] == 0 )
- {
- filePath = info[@"PHImageFileUTIKey"];
- if( filePath != nil )
- filePath = [NSString stringWithFormat:@"%@", filePath];
- }
-
- NSString *resultPath;
- if( filePath == nil || [filePath length] == 0 )
- resultPath = [NSString stringWithFormat:@"%@%d", pickedMediaSavePath, filenameIndex];
- else
- resultPath = [[NSString stringWithFormat:@"%@%d", pickedMediaSavePath, filenameIndex] stringByAppendingPathExtension:[filePath pathExtension]];
-
- NSError *error;
- if( ![[NSFileManager defaultManager] fileExistsAtPath:resultPath] || [[NSFileManager defaultManager] removeItemAtPath:resultPath error:&error] )
- {
- if( ![imageData writeToFile:resultPath atomically:YES] )
- {
- NSLog( @"Error copying source image to file" );
- resultPath = nil;
- }
- }
- else
- {
- NSLog( @"Error deleting existing image: %@", error );
- resultPath = nil;
- }
-
- return resultPath;
- }
-
- // Credit: https://lists.apple.com/archives/cocoa-dev/2012/Jan/msg00052.html
- + (int)getMediaTypeFromExtension:(NSString *)extension
- {
- CFStringRef fileUTI = UTTypeCreatePreferredIdentifierForTag( kUTTagClassFilenameExtension, (__bridge CFStringRef) extension, NULL );
-
- // mediaType is a bitmask:
- // 1: image
- // 2: video
- // 4: audio (not supported)
- int result = 0;
- if( UTTypeConformsTo( fileUTI, kUTTypeImage ) || UTTypeConformsTo( fileUTI, kUTTypeLivePhoto ) )
- result = 1;
- else if( UTTypeConformsTo( fileUTI, kUTTypeMovie ) || UTTypeConformsTo( fileUTI, kUTTypeVideo ) )
- result = 2;
- else if( UTTypeConformsTo( fileUTI, kUTTypeAudio ) )
- result = 4;
-
- CFRelease( fileUTI );
-
- return result;
- }
-
- // Credit: https://stackoverflow.com/a/4170099/2373034
- + (NSArray *)getImageMetadata:(NSString *)path
- {
- int width = 0;
- int height = 0;
- int orientation = -1;
-
- CGImageSourceRef imageSource = CGImageSourceCreateWithURL( (__bridge CFURLRef) [NSURL fileURLWithPath:path], nil );
- if( imageSource != nil )
- {
- NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:(__bridge NSString *)kCGImageSourceShouldCache];
- CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex( imageSource, 0, (__bridge CFDictionaryRef) options );
- CFRelease( imageSource );
-
- CGFloat widthF = 0.0f, heightF = 0.0f;
- if( imageProperties != nil )
- {
- if( CFDictionaryContainsKey( imageProperties, kCGImagePropertyPixelWidth ) )
- CFNumberGetValue( (CFNumberRef) CFDictionaryGetValue( imageProperties, kCGImagePropertyPixelWidth ), kCFNumberCGFloatType, &widthF );
-
- if( CFDictionaryContainsKey( imageProperties, kCGImagePropertyPixelHeight ) )
- CFNumberGetValue( (CFNumberRef) CFDictionaryGetValue( imageProperties, kCGImagePropertyPixelHeight ), kCFNumberCGFloatType, &heightF );
-
- if( CFDictionaryContainsKey( imageProperties, kCGImagePropertyOrientation ) )
- {
- CFNumberGetValue( (CFNumberRef) CFDictionaryGetValue( imageProperties, kCGImagePropertyOrientation ), kCFNumberIntType, &orientation );
-
- if( orientation > 4 )
- {
- // Landscape image
- CGFloat temp = widthF;
- widthF = heightF;
- heightF = temp;
- }
- }
-
- CFRelease( imageProperties );
- }
-
- width = (int) roundf( widthF );
- height = (int) roundf( heightF );
- }
-
- return [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:width], [NSNumber numberWithInt:height], [NSNumber numberWithInt:orientation], nil];
- }
-
- + (char *)getImageProperties:(NSString *)path
- {
- NSArray *metadata = [self getImageMetadata:path];
-
- int orientationUnity;
- int orientation = [metadata[2] intValue];
-
- // To understand the magic numbers, see ImageOrientation enum in NativeGallery.cs
- // and http://sylvana.net/jpegcrop/exif_orientation.html
- if( orientation == 1 )
- orientationUnity = 0;
- else if( orientation == 2 )
- orientationUnity = 4;
- else if( orientation == 3 )
- orientationUnity = 2;
- else if( orientation == 4 )
- orientationUnity = 6;
- else if( orientation == 5 )
- orientationUnity = 5;
- else if( orientation == 6 )
- orientationUnity = 1;
- else if( orientation == 7 )
- orientationUnity = 7;
- else if( orientation == 8 )
- orientationUnity = 3;
- else
- orientationUnity = -1;
-
- return [self getCString:[NSString stringWithFormat:@"%d>%d> >%d", [metadata[0] intValue], [metadata[1] intValue], orientationUnity]];
- }
-
- + (char *)getVideoProperties:(NSString *)path
- {
- CGSize size = CGSizeZero;
- float rotation = 0;
- long long duration = 0;
-
- AVURLAsset *asset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:path] options:nil];
- if( asset != nil )
- {
- duration = (long long) round( CMTimeGetSeconds( [asset duration] ) * 1000 );
- CGAffineTransform transform = [asset preferredTransform];
- NSArray<AVAssetTrack *>* videoTracks = [asset tracksWithMediaType:AVMediaTypeVideo];
- if( videoTracks != nil && [videoTracks count] > 0 )
- {
- size = [[videoTracks objectAtIndex:0] naturalSize];
- transform = [[videoTracks objectAtIndex:0] preferredTransform];
- }
-
- rotation = atan2( transform.b, transform.a ) * ( 180.0 / M_PI );
- }
-
- return [self getCString:[NSString stringWithFormat:@"%d>%d>%lld>%f", (int) roundf( size.width ), (int) roundf( size.height ), duration, rotation]];
- }
-
- + (char *)getVideoThumbnail:(NSString *)path savePath:(NSString *)savePath maximumSize:(int)maximumSize captureTime:(double)captureTime
- {
- AVAssetImageGenerator *thumbnailGenerator = [[AVAssetImageGenerator alloc] initWithAsset:[[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:path] options:nil]];
- thumbnailGenerator.appliesPreferredTrackTransform = YES;
- thumbnailGenerator.maximumSize = CGSizeMake( (CGFloat) maximumSize, (CGFloat) maximumSize );
- thumbnailGenerator.requestedTimeToleranceBefore = kCMTimeZero;
- thumbnailGenerator.requestedTimeToleranceAfter = kCMTimeZero;
-
- if( captureTime < 0.0 )
- captureTime = 0.0;
- else
- {
- AVURLAsset *asset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:path] options:nil];
- if( asset != nil )
- {
- double videoDuration = CMTimeGetSeconds( [asset duration] );
- if( videoDuration > 0.0 && captureTime >= videoDuration - 0.1 )
- {
- if( captureTime > videoDuration )
- captureTime = videoDuration;
-
- thumbnailGenerator.requestedTimeToleranceBefore = CMTimeMakeWithSeconds( 1.0, 600 );
- }
- }
- }
-
- NSError *error = nil;
- CGImageRef image = [thumbnailGenerator copyCGImageAtTime:CMTimeMakeWithSeconds( captureTime, 600 ) actualTime:nil error:&error];
- if( image == nil )
- {
- if( error != nil )
- NSLog( @"Error generating video thumbnail: %@", error );
- else
- NSLog( @"Error generating video thumbnail..." );
-
- return [self getCString:@""];
- }
-
- UIImage *thumbnail = [[UIImage alloc] initWithCGImage:image];
- CGImageRelease( image );
-
- if( ![UIImagePNGRepresentation( thumbnail ) writeToFile:savePath atomically:YES] )
- {
- NSLog( @"Error saving thumbnail image" );
- return [self getCString:@""];
- }
-
- return [self getCString:savePath];
- }
-
- + (BOOL)saveImageAsPNG:(UIImage *)image toPath:(NSString *)resultPath
- {
- return [UIImagePNGRepresentation( [self scaleImage:image maxSize:16384] ) writeToFile:resultPath atomically:YES];
- }
-
- + (UIImage *)scaleImage:(UIImage *)image maxSize:(int)maxSize
- {
- CGFloat width = image.size.width;
- CGFloat height = image.size.height;
-
- UIImageOrientation orientation = image.imageOrientation;
- if( width <= maxSize && height <= maxSize && orientation != UIImageOrientationDown &&
- orientation != UIImageOrientationLeft && orientation != UIImageOrientationRight &&
- orientation != UIImageOrientationLeftMirrored && orientation != UIImageOrientationRightMirrored &&
- orientation != UIImageOrientationUpMirrored && orientation != UIImageOrientationDownMirrored )
- return image;
-
- CGFloat scaleX = 1.0f;
- CGFloat scaleY = 1.0f;
- if( width > maxSize )
- scaleX = maxSize / width;
- if( height > maxSize )
- scaleY = maxSize / height;
-
- // Credit: https://github.com/mbcharbonneau/UIImage-Categories/blob/master/UIImage%2BAlpha.m
- CGImageAlphaInfo alpha = CGImageGetAlphaInfo( image.CGImage );
- BOOL hasAlpha = alpha == kCGImageAlphaFirst || alpha == kCGImageAlphaLast || alpha == kCGImageAlphaPremultipliedFirst || alpha == kCGImageAlphaPremultipliedLast;
-
- CGFloat scaleRatio = scaleX < scaleY ? scaleX : scaleY;
- CGRect imageRect = CGRectMake( 0, 0, width * scaleRatio, height * scaleRatio );
- UIGraphicsImageRendererFormat *format = [image imageRendererFormat];
- format.opaque = !hasAlpha;
- format.scale = image.scale;
- UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:imageRect.size format:format];
- image = [renderer imageWithActions:^( UIGraphicsImageRendererContext* _Nonnull myContext )
- {
- [image drawInRect:imageRect];
- }];
-
- return image;
- }
-
- + (char *)loadImageAtPath:(NSString *)path tempFilePath:(NSString *)tempFilePath maximumSize:(int)maximumSize
- {
- // Check if the image can be loaded by Unity without requiring a conversion to PNG
- // Credit: https://stackoverflow.com/a/12048937/2373034
- NSString *extension = [path pathExtension];
- BOOL conversionNeeded = [extension caseInsensitiveCompare:@"jpg"] != NSOrderedSame && [extension caseInsensitiveCompare:@"jpeg"] != NSOrderedSame && [extension caseInsensitiveCompare:@"png"] != NSOrderedSame;
-
- if( !conversionNeeded )
- {
- // Check if the image needs to be processed at all
- NSArray *metadata = [self getImageMetadata:path];
- int orientationInt = [metadata[2] intValue]; // 1: correct orientation, [1,8]: valid orientation range
- if( orientationInt == 1 && [metadata[0] intValue] <= maximumSize && [metadata[1] intValue] <= maximumSize )
- return [self getCString:path];
- }
-
- UIImage *image = [UIImage imageWithContentsOfFile:path];
- if( image == nil )
- return [self getCString:path];
-
- UIImage *scaledImage = [self scaleImage:image maxSize:maximumSize];
- if( conversionNeeded || scaledImage != image )
- {
- if( ![UIImagePNGRepresentation( scaledImage ) writeToFile:tempFilePath atomically:YES] )
- {
- NSLog( @"Error creating scaled image" );
- return [self getCString:path];
- }
-
- return [self getCString:tempFilePath];
- }
- else
- return [self getCString:path];
- }
-
- // Credit: https://stackoverflow.com/a/37052118/2373034
- + (char *)getCString:(NSString *)source
- {
- if( source == nil )
- source = @"";
-
- const char *sourceUTF8 = [source UTF8String];
- char *result = (char*) malloc( strlen( sourceUTF8 ) + 1 );
- strcpy( result, sourceUTF8 );
-
- return result;
- }
-
- @end
-
- extern "C" int _NativeGallery_CheckPermission( int readPermission, int permissionFreeMode )
- {
- return [UNativeGallery checkPermission:( readPermission == 1 ) permissionFreeMode:( permissionFreeMode == 1 )];
- }
-
- extern "C" void _NativeGallery_RequestPermission( int readPermission, int permissionFreeMode )
- {
- [UNativeGallery requestPermission:( readPermission == 1 ) permissionFreeMode:( permissionFreeMode == 1 )];
- }
-
- extern "C" void _NativeGallery_ShowLimitedLibraryPicker()
- {
- return [UNativeGallery showLimitedLibraryPicker];
- }
-
- extern "C" void _NativeGallery_OpenSettings()
- {
- [UNativeGallery openSettings];
- }
-
- extern "C" int _NativeGallery_CanPickMultipleMedia()
- {
- return [UNativeGallery canPickMultipleMedia];
- }
-
- extern "C" void _NativeGallery_ImageWriteToAlbum( const char* path, const char* album, int permissionFreeMode )
- {
- [UNativeGallery saveMedia:[NSString stringWithUTF8String:path] albumName:[NSString stringWithUTF8String:album] isImage:YES permissionFreeMode:( permissionFreeMode == 1 )];
- }
-
- extern "C" void _NativeGallery_VideoWriteToAlbum( const char* path, const char* album, int permissionFreeMode )
- {
- [UNativeGallery saveMedia:[NSString stringWithUTF8String:path] albumName:[NSString stringWithUTF8String:album] isImage:NO permissionFreeMode:( permissionFreeMode == 1 )];
- }
-
- extern "C" void _NativeGallery_PickMedia( const char* mediaSavePath, int mediaType, int permissionFreeMode, int selectionLimit )
- {
- [UNativeGallery pickMedia:mediaType savePath:[NSString stringWithUTF8String:mediaSavePath] permissionFreeMode:( permissionFreeMode == 1 ) selectionLimit:selectionLimit];
- }
-
- extern "C" int _NativeGallery_IsMediaPickerBusy()
- {
- return [UNativeGallery isMediaPickerBusy];
- }
-
- extern "C" int _NativeGallery_GetMediaTypeFromExtension( const char* extension )
- {
- return [UNativeGallery getMediaTypeFromExtension:[NSString stringWithUTF8String:extension]];
- }
-
- extern "C" char* _NativeGallery_GetImageProperties( const char* path )
- {
- return [UNativeGallery getImageProperties:[NSString stringWithUTF8String:path]];
- }
-
- extern "C" char* _NativeGallery_GetVideoProperties( const char* path )
- {
- return [UNativeGallery getVideoProperties:[NSString stringWithUTF8String:path]];
- }
-
- extern "C" char* _NativeGallery_GetVideoThumbnail( const char* path, const char* thumbnailSavePath, int maxSize, double captureTimeInSeconds )
- {
- return [UNativeGallery getVideoThumbnail:[NSString stringWithUTF8String:path] savePath:[NSString stringWithUTF8String:thumbnailSavePath] maximumSize:maxSize captureTime:captureTimeInSeconds];
- }
-
- extern "C" char* _NativeGallery_LoadImageAtPath( const char* path, const char* temporaryFilePath, int maxSize )
- {
- return [UNativeGallery loadImageAtPath:[NSString stringWithUTF8String:path] tempFilePath:[NSString stringWithUTF8String:temporaryFilePath] maximumSize:maxSize];
- }
|