123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287 |
- //
- // IDMPhoto.m
- // IDMPhotoBrowser
- //
- // Created by Michael Waterfall on 17/10/2010.
- // Copyright 2010 d3i. All rights reserved.
- //
- #import "IDMPhoto.h"
- #import "IDMPhotoBrowser.h"
- // Private
- @interface IDMPhoto () {
- // Image Sources
- NSString *_photoPath;
- // Image
- UIImage *_underlyingImage;
- // Other
- NSString *_caption;
- BOOL _loadingInProgress;
- }
- // Properties
- @property (nonatomic, strong) UIImage *underlyingImage;
- // Methods
- - (void)imageLoadingComplete;
- @end
- // IDMPhoto
- @implementation IDMPhoto
- // Properties
- @synthesize underlyingImage = _underlyingImage,
- photoURL = _photoURL,
- caption = _caption;
- #pragma mark Class Methods
- + (IDMPhoto *)photoWithImage:(UIImage *)image {
- return [[IDMPhoto alloc] initWithImage:image];
- }
- + (IDMPhoto *)photoWithFilePath:(NSString *)path {
- return [[IDMPhoto alloc] initWithFilePath:path];
- }
- + (IDMPhoto *)photoWithURL:(NSURL *)url {
- return [[IDMPhoto alloc] initWithURL:url];
- }
- + (NSArray *)photosWithImages:(NSArray *)imagesArray {
- NSMutableArray *photos = [NSMutableArray arrayWithCapacity:imagesArray.count];
-
- for (UIImage *image in imagesArray) {
- if ([image isKindOfClass:[UIImage class]]) {
- IDMPhoto *photo = [IDMPhoto photoWithImage:image];
- [photos addObject:photo];
- }
- }
-
- return photos;
- }
- + (NSArray *)photosWithFilePaths:(NSArray *)pathsArray {
- NSMutableArray *photos = [NSMutableArray arrayWithCapacity:pathsArray.count];
-
- for (NSString *path in pathsArray) {
- if ([path isKindOfClass:[NSString class]]) {
- IDMPhoto *photo = [IDMPhoto photoWithFilePath:path];
- [photos addObject:photo];
- }
- }
-
- return photos;
- }
- + (NSArray *)photosWithURLs:(NSArray *)urlsArray {
- NSMutableArray *photos = [NSMutableArray arrayWithCapacity:urlsArray.count];
-
- for (id url in urlsArray) {
- if ([url isKindOfClass:[NSURL class]]) {
- IDMPhoto *photo = [IDMPhoto photoWithURL:url];
- [photos addObject:photo];
- }
- else if ([url isKindOfClass:[NSString class]]) {
- IDMPhoto *photo = [IDMPhoto photoWithURL:[NSURL URLWithString:url]];
- [photos addObject:photo];
- }
- }
-
- return photos;
- }
- #pragma mark NSObject
- - (id)initWithImage:(UIImage *)image {
- if ((self = [super init])) {
- self.underlyingImage = image;
- }
- return self;
- }
- - (id)initWithFilePath:(NSString *)path {
- if ((self = [super init])) {
- _photoPath = [path copy];
- }
- return self;
- }
- - (id)initWithURL:(NSURL *)url {
- if ((self = [super init])) {
- _photoURL = [url copy];
- }
- return self;
- }
- #pragma mark IDMPhoto Protocol Methods
- - (UIImage *)underlyingImage {
- return _underlyingImage;
- }
- - (void)loadUnderlyingImageAndNotify {
- NSAssert([[NSThread currentThread] isMainThread], @"This method must be called on the main thread.");
- _loadingInProgress = YES;
- if (self.underlyingImage) {
- // Image already loaded
- [self imageLoadingComplete];
- } else {
- if (_photoPath) {
- // Load async from file
- [self performSelectorInBackground:@selector(loadImageFromFileAsync) withObject:nil];
- } else if (_photoURL) {
- // Load async from web (using SDWebImageManager)
- SDWebImageManager *manager = [SDWebImageManager sharedManager];
- [manager loadImageWithURL:_photoURL options:SDWebImageRetryFailed|SDWebImageHandleCookies progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
- CGFloat progress = ((CGFloat)receivedSize)/((CGFloat)expectedSize);
- if (self.progressUpdateBlock) {
- self.progressUpdateBlock(progress);
- }
- } completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
- if (image) {
- self.underlyingImage = image;
- [self performSelectorOnMainThread:@selector(imageLoadingComplete) withObject:nil waitUntilDone:NO];
- }
- }];
- } else {
- // Failed - no source
- self.underlyingImage = nil;
- [self imageLoadingComplete];
- }
- }
- }
- // Release if we can get it again from path or url
- - (void)unloadUnderlyingImage {
- _loadingInProgress = NO;
- if (self.underlyingImage && (_photoPath || _photoURL)) {
- self.underlyingImage = nil;
- }
- }
- #pragma mark - Async Loading
- /*- (UIImage *)decodedImageWithImage:(UIImage *)image {
- CGImageRef imageRef = image.CGImage;
- // System only supports RGB, set explicitly and prevent context error
- // if the downloaded image is not the supported format
- CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
-
- CGContextRef context = CGBitmapContextCreate(NULL,
- CGImageGetWidth(imageRef),
- CGImageGetHeight(imageRef),
- 8,
- // width * 4 will be enough because are in ARGB format, don't read from the image
- CGImageGetWidth(imageRef) * 4,
- colorSpace,
- // kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little
- // makes system don't need to do extra conversion when displayed.
- kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
- CGColorSpaceRelease(colorSpace);
-
- if ( ! context) {
- return nil;
- }
-
- CGRect rect = (CGRect){CGPointZero, CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)};
- CGContextDrawImage(context, rect, imageRef);
- CGImageRef decompressedImageRef = CGBitmapContextCreateImage(context);
- CGContextRelease(context);
-
- UIImage *decompressedImage = [[UIImage alloc] initWithCGImage:decompressedImageRef];
- CGImageRelease(decompressedImageRef);
- return decompressedImage;
- }*/
- - (UIImage *)decodedImageWithImage:(UIImage *)image {
- if (image.images) {
- // Do not decode animated images
- return image;
- }
-
- CGImageRef imageRef = image.CGImage;
- CGSize imageSize = CGSizeMake(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef));
- CGRect imageRect = (CGRect){.origin = CGPointZero, .size = imageSize};
-
- CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
- CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
-
- int infoMask = (bitmapInfo & kCGBitmapAlphaInfoMask);
- BOOL anyNonAlpha = (infoMask == kCGImageAlphaNone ||
- infoMask == kCGImageAlphaNoneSkipFirst ||
- infoMask == kCGImageAlphaNoneSkipLast);
-
- // CGBitmapContextCreate doesn't support kCGImageAlphaNone with RGB.
- // https://developer.apple.com/library/mac/#qa/qa1037/_index.html
- if (infoMask == kCGImageAlphaNone && CGColorSpaceGetNumberOfComponents(colorSpace) > 1)
- {
- // Unset the old alpha info.
- bitmapInfo &= ~kCGBitmapAlphaInfoMask;
-
- // Set noneSkipFirst.
- bitmapInfo |= kCGImageAlphaNoneSkipFirst;
- }
- // Some PNGs tell us they have alpha but only 3 components. Odd.
- else if (!anyNonAlpha && CGColorSpaceGetNumberOfComponents(colorSpace) == 3)
- {
- // Unset the old alpha info.
- bitmapInfo &= ~kCGBitmapAlphaInfoMask;
- bitmapInfo |= kCGImageAlphaPremultipliedFirst;
- }
-
- // It calculates the bytes-per-row based on the bitsPerComponent and width arguments.
- CGContextRef context = CGBitmapContextCreate(NULL,
- imageSize.width,
- imageSize.height,
- CGImageGetBitsPerComponent(imageRef),
- 0,
- colorSpace,
- bitmapInfo);
- CGColorSpaceRelease(colorSpace);
-
- // If failed, return undecompressed image
- if (!context) return image;
-
- CGContextDrawImage(context, imageRect, imageRef);
- CGImageRef decompressedImageRef = CGBitmapContextCreateImage(context);
-
- CGContextRelease(context);
-
- UIImage *decompressedImage = [UIImage imageWithCGImage:decompressedImageRef scale:image.scale orientation:image.imageOrientation];
- CGImageRelease(decompressedImageRef);
- return decompressedImage;
- }
- // Called in background
- // Load image in background from local file
- - (void)loadImageFromFileAsync {
- @autoreleasepool {
- @try {
- self.underlyingImage = [UIImage imageWithContentsOfFile:_photoPath];
- if (!_underlyingImage) {
- //IDMLog(@"Error loading photo from path: %@", _photoPath);
- }
- } @finally {
- self.underlyingImage = [self decodedImageWithImage: self.underlyingImage];
- [self performSelectorOnMainThread:@selector(imageLoadingComplete) withObject:nil waitUntilDone:NO];
- }
- }
- }
- // Called on main
- - (void)imageLoadingComplete {
- NSAssert([[NSThread currentThread] isMainThread], @"This method must be called on the main thread.");
- // Complete so notify
- _loadingInProgress = NO;
- [[NSNotificationCenter defaultCenter] postNotificationName:IDMPhoto_LOADING_DID_END_NOTIFICATION
- object:self];
- }
- @end
|