123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631 |
- /*
- * This file is part of the SDWebImage package.
- * (c) Olivier Poitrey <rs@dailymotion.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
- #import "SDImageCoderHelper.h"
- #import "SDImageFrame.h"
- #import "NSImage+Compatibility.h"
- #import "NSData+ImageContentType.h"
- #import "SDAnimatedImageRep.h"
- #import "UIImage+ForceDecode.h"
- #import "UIImage+Metadata.h"
- #if SD_UIKIT || SD_WATCH
- static const size_t kBytesPerPixel = 4;
- static const size_t kBitsPerComponent = 8;
- /*
- * Defines the maximum size in MB of the decoded image when the flag `SDWebImageScaleDownLargeImages` is set
- * Suggested value for iPad1 and iPhone 3GS: 60.
- * Suggested value for iPad2 and iPhone 4: 120.
- * Suggested value for iPhone 3G and iPod 2 and earlier devices: 30.
- */
- static const CGFloat kDestImageSizeMB = 60.f;
- /*
- * Defines the maximum size in MB of a tile used to decode image when the flag `SDWebImageScaleDownLargeImages` is set
- * Suggested value for iPad1 and iPhone 3GS: 20.
- * Suggested value for iPad2 and iPhone 4: 40.
- * Suggested value for iPhone 3G and iPod 2 and earlier devices: 10.
- */
- static const CGFloat kSourceImageTileSizeMB = 20.f;
- static const CGFloat kBytesPerMB = 1024.0f * 1024.0f;
- static const CGFloat kPixelsPerMB = kBytesPerMB / kBytesPerPixel;
- static const CGFloat kDestTotalPixels = kDestImageSizeMB * kPixelsPerMB;
- static const CGFloat kTileTotalPixels = kSourceImageTileSizeMB * kPixelsPerMB;
- static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to overlap the seems where tiles meet.
- #endif
- @implementation SDImageCoderHelper
- + (UIImage *)animatedImageWithFrames:(NSArray<SDImageFrame *> *)frames {
- NSUInteger frameCount = frames.count;
- if (frameCount == 0) {
- return nil;
- }
-
- UIImage *animatedImage;
-
- #if SD_UIKIT || SD_WATCH
- NSUInteger durations[frameCount];
- for (size_t i = 0; i < frameCount; i++) {
- durations[i] = frames[i].duration * 1000;
- }
- NSUInteger const gcd = gcdArray(frameCount, durations);
- __block NSUInteger totalDuration = 0;
- NSMutableArray<UIImage *> *animatedImages = [NSMutableArray arrayWithCapacity:frameCount];
- [frames enumerateObjectsUsingBlock:^(SDImageFrame * _Nonnull frame, NSUInteger idx, BOOL * _Nonnull stop) {
- UIImage *image = frame.image;
- NSUInteger duration = frame.duration * 1000;
- totalDuration += duration;
- NSUInteger repeatCount;
- if (gcd) {
- repeatCount = duration / gcd;
- } else {
- repeatCount = 1;
- }
- for (size_t i = 0; i < repeatCount; ++i) {
- [animatedImages addObject:image];
- }
- }];
-
- animatedImage = [UIImage animatedImageWithImages:animatedImages duration:totalDuration / 1000.f];
-
- #else
-
- NSMutableData *imageData = [NSMutableData data];
- CFStringRef imageUTType = [NSData sd_UTTypeFromImageFormat:SDImageFormatGIF];
- // Create an image destination. GIF does not support EXIF image orientation
- CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, imageUTType, frameCount, NULL);
- if (!imageDestination) {
- // Handle failure.
- return nil;
- }
-
- for (size_t i = 0; i < frameCount; i++) {
- @autoreleasepool {
- SDImageFrame *frame = frames[i];
- float frameDuration = frame.duration;
- CGImageRef frameImageRef = frame.image.CGImage;
- NSDictionary *frameProperties = @{(__bridge NSString *)kCGImagePropertyGIFDictionary : @{(__bridge NSString *)kCGImagePropertyGIFDelayTime : @(frameDuration)}};
- CGImageDestinationAddImage(imageDestination, frameImageRef, (__bridge CFDictionaryRef)frameProperties);
- }
- }
- // Finalize the destination.
- if (CGImageDestinationFinalize(imageDestination) == NO) {
- // Handle failure.
- CFRelease(imageDestination);
- return nil;
- }
- CFRelease(imageDestination);
- CGFloat scale = MAX(frames.firstObject.image.scale, 1);
-
- SDAnimatedImageRep *imageRep = [[SDAnimatedImageRep alloc] initWithData:imageData];
- NSSize size = NSMakeSize(imageRep.pixelsWide / scale, imageRep.pixelsHigh / scale);
- imageRep.size = size;
- animatedImage = [[NSImage alloc] initWithSize:size];
- [animatedImage addRepresentation:imageRep];
- #endif
-
- return animatedImage;
- }
- + (NSArray<SDImageFrame *> *)framesFromAnimatedImage:(UIImage *)animatedImage {
- if (!animatedImage) {
- return nil;
- }
-
- NSMutableArray<SDImageFrame *> *frames = [NSMutableArray array];
- NSUInteger frameCount = 0;
-
- #if SD_UIKIT || SD_WATCH
- NSArray<UIImage *> *animatedImages = animatedImage.images;
- frameCount = animatedImages.count;
- if (frameCount == 0) {
- return nil;
- }
-
- NSTimeInterval avgDuration = animatedImage.duration / frameCount;
- if (avgDuration == 0) {
- avgDuration = 0.1; // if it's a animated image but no duration, set it to default 100ms (this do not have that 10ms limit like GIF or WebP to allow custom coder provide the limit)
- }
-
- __block NSUInteger index = 0;
- __block NSUInteger repeatCount = 1;
- __block UIImage *previousImage = animatedImages.firstObject;
- [animatedImages enumerateObjectsUsingBlock:^(UIImage * _Nonnull image, NSUInteger idx, BOOL * _Nonnull stop) {
- // ignore first
- if (idx == 0) {
- return;
- }
- if ([image isEqual:previousImage]) {
- repeatCount++;
- } else {
- SDImageFrame *frame = [SDImageFrame frameWithImage:previousImage duration:avgDuration * repeatCount];
- [frames addObject:frame];
- repeatCount = 1;
- index++;
- }
- previousImage = image;
- // last one
- if (idx == frameCount - 1) {
- SDImageFrame *frame = [SDImageFrame frameWithImage:previousImage duration:avgDuration * repeatCount];
- [frames addObject:frame];
- }
- }];
-
- #else
-
- NSRect imageRect = NSMakeRect(0, 0, animatedImage.size.width, animatedImage.size.height);
- NSImageRep *imageRep = [animatedImage bestRepresentationForRect:imageRect context:nil hints:nil];
- NSBitmapImageRep *bitmapImageRep;
- if ([imageRep isKindOfClass:[NSBitmapImageRep class]]) {
- bitmapImageRep = (NSBitmapImageRep *)imageRep;
- }
- if (!bitmapImageRep) {
- return nil;
- }
- frameCount = [[bitmapImageRep valueForProperty:NSImageFrameCount] unsignedIntegerValue];
- if (frameCount == 0) {
- return nil;
- }
- CGFloat scale = animatedImage.scale;
-
- for (size_t i = 0; i < frameCount; i++) {
- @autoreleasepool {
- // NSBitmapImageRep need to manually change frame. "Good taste" API
- [bitmapImageRep setProperty:NSImageCurrentFrame withValue:@(i)];
- float frameDuration = [[bitmapImageRep valueForProperty:NSImageCurrentFrameDuration] floatValue];
- NSImage *frameImage = [[NSImage alloc] initWithCGImage:bitmapImageRep.CGImage scale:scale orientation:kCGImagePropertyOrientationUp];
- SDImageFrame *frame = [SDImageFrame frameWithImage:frameImage duration:frameDuration];
- [frames addObject:frame];
- }
- }
- #endif
-
- return frames;
- }
- + (CGColorSpaceRef)colorSpaceGetDeviceRGB {
- #if SD_MAC
- CGColorSpaceRef screenColorSpace = NSScreen.mainScreen.colorSpace.CGColorSpace;
- if (screenColorSpace) {
- return screenColorSpace;
- }
- #endif
- static CGColorSpaceRef colorSpace;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- #if SD_UIKIT
- if (@available(iOS 9.0, tvOS 9.0, *)) {
- colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
- } else {
- colorSpace = CGColorSpaceCreateDeviceRGB();
- }
- #else
- colorSpace = CGColorSpaceCreateDeviceRGB();
- #endif
- });
- return colorSpace;
- }
- + (BOOL)CGImageContainsAlpha:(CGImageRef)cgImage {
- if (!cgImage) {
- return NO;
- }
- CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(cgImage);
- BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone ||
- alphaInfo == kCGImageAlphaNoneSkipFirst ||
- alphaInfo == kCGImageAlphaNoneSkipLast);
- return hasAlpha;
- }
- + (CGImageRef)CGImageCreateDecoded:(CGImageRef)cgImage {
- return [self CGImageCreateDecoded:cgImage orientation:kCGImagePropertyOrientationUp];
- }
- + (CGImageRef)CGImageCreateDecoded:(CGImageRef)cgImage orientation:(CGImagePropertyOrientation)orientation {
- if (!cgImage) {
- return NULL;
- }
- size_t width = CGImageGetWidth(cgImage);
- size_t height = CGImageGetHeight(cgImage);
- if (width == 0 || height == 0) return NULL;
- size_t newWidth;
- size_t newHeight;
- switch (orientation) {
- case kCGImagePropertyOrientationLeft:
- case kCGImagePropertyOrientationLeftMirrored:
- case kCGImagePropertyOrientationRight:
- case kCGImagePropertyOrientationRightMirrored: {
- // These orientation should swap width & height
- newWidth = height;
- newHeight = width;
- }
- break;
- default: {
- newWidth = width;
- newHeight = height;
- }
- break;
- }
-
- BOOL hasAlpha = [self CGImageContainsAlpha:cgImage];
- // iOS prefer BGRA8888 (premultiplied) or BGRX8888 bitmapInfo for screen rendering, which is same as `UIGraphicsBeginImageContext()` or `- [CALayer drawInContext:]`
- // Though you can use any supported bitmapInfo (see: https://developer.apple.com/library/content/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_context/dq_context.html#//apple_ref/doc/uid/TP30001066-CH203-BCIBHHBB ) and let Core Graphics reorder it when you call `CGContextDrawImage`
- // But since our build-in coders use this bitmapInfo, this can have a little performance benefit
- CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host;
- bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
- CGContextRef context = CGBitmapContextCreate(NULL, newWidth, newHeight, 8, 0, [self colorSpaceGetDeviceRGB], bitmapInfo);
- if (!context) {
- return NULL;
- }
-
- // Apply transform
- CGAffineTransform transform = SDCGContextTransformFromOrientation(orientation, CGSizeMake(newWidth, newHeight));
- CGContextConcatCTM(context, transform);
- CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage); // The rect is bounding box of CGImage, don't swap width & height
- CGImageRef newImageRef = CGBitmapContextCreateImage(context);
- CGContextRelease(context);
-
- return newImageRef;
- }
- + (UIImage *)decodedImageWithImage:(UIImage *)image {
- #if SD_MAC
- return image;
- #else
- if (![self shouldDecodeImage:image]) {
- return image;
- }
-
- CGImageRef imageRef = [self CGImageCreateDecoded:image.CGImage];
- if (!imageRef) {
- return image;
- }
- UIImage *decodedImage = [[UIImage alloc] initWithCGImage:imageRef scale:image.scale orientation:image.imageOrientation];
- CGImageRelease(imageRef);
- decodedImage.sd_isDecoded = YES;
- decodedImage.sd_imageFormat = image.sd_imageFormat;
- return decodedImage;
- #endif
- }
- + (UIImage *)decodedAndScaledDownImageWithImage:(UIImage *)image limitBytes:(NSUInteger)bytes {
- #if SD_MAC
- return image;
- #else
- if (![self shouldDecodeImage:image]) {
- return image;
- }
-
- if (![self shouldScaleDownImage:image limitBytes:bytes]) {
- return [self decodedImageWithImage:image];
- }
-
- CGFloat destTotalPixels;
- CGFloat tileTotalPixels;
- if (bytes > 0) {
- destTotalPixels = bytes / kBytesPerPixel;
- tileTotalPixels = destTotalPixels / 3;
- } else {
- destTotalPixels = kDestTotalPixels;
- tileTotalPixels = kTileTotalPixels;
- }
- CGContextRef destContext;
-
- // autorelease the bitmap context and all vars to help system to free memory when there are memory warning.
- // on iOS7, do not forget to call [[SDImageCache sharedImageCache] clearMemory];
- @autoreleasepool {
- CGImageRef sourceImageRef = image.CGImage;
-
- CGSize sourceResolution = CGSizeZero;
- sourceResolution.width = CGImageGetWidth(sourceImageRef);
- sourceResolution.height = CGImageGetHeight(sourceImageRef);
- CGFloat sourceTotalPixels = sourceResolution.width * sourceResolution.height;
- // Determine the scale ratio to apply to the input image
- // that results in an output image of the defined size.
- // see kDestImageSizeMB, and how it relates to destTotalPixels.
- CGFloat imageScale = sqrt(destTotalPixels / sourceTotalPixels);
- CGSize destResolution = CGSizeZero;
- destResolution.width = (int)(sourceResolution.width * imageScale);
- destResolution.height = (int)(sourceResolution.height * imageScale);
-
- // device color space
- CGColorSpaceRef colorspaceRef = [self colorSpaceGetDeviceRGB];
- BOOL hasAlpha = [self CGImageContainsAlpha:sourceImageRef];
- // iOS display alpha info (BGRA8888/BGRX8888)
- CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host;
- bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
-
- // kCGImageAlphaNone is not supported in CGBitmapContextCreate.
- // Since the original image here has no alpha info, use kCGImageAlphaNoneSkipFirst
- // to create bitmap graphics contexts without alpha info.
- destContext = CGBitmapContextCreate(NULL,
- destResolution.width,
- destResolution.height,
- kBitsPerComponent,
- 0,
- colorspaceRef,
- bitmapInfo);
-
- if (destContext == NULL) {
- return image;
- }
- CGContextSetInterpolationQuality(destContext, kCGInterpolationHigh);
-
- // Now define the size of the rectangle to be used for the
- // incremental blits from the input image to the output image.
- // we use a source tile width equal to the width of the source
- // image due to the way that iOS retrieves image data from disk.
- // iOS must decode an image from disk in full width 'bands', even
- // if current graphics context is clipped to a subrect within that
- // band. Therefore we fully utilize all of the pixel data that results
- // from a decoding opertion by achnoring our tile size to the full
- // width of the input image.
- CGRect sourceTile = CGRectZero;
- sourceTile.size.width = sourceResolution.width;
- // The source tile height is dynamic. Since we specified the size
- // of the source tile in MB, see how many rows of pixels high it
- // can be given the input image width.
- sourceTile.size.height = (int)(tileTotalPixels / sourceTile.size.width );
- sourceTile.origin.x = 0.0f;
- // The output tile is the same proportions as the input tile, but
- // scaled to image scale.
- CGRect destTile;
- destTile.size.width = destResolution.width;
- destTile.size.height = sourceTile.size.height * imageScale;
- destTile.origin.x = 0.0f;
- // The source seem overlap is proportionate to the destination seem overlap.
- // this is the amount of pixels to overlap each tile as we assemble the ouput image.
- float sourceSeemOverlap = (int)((kDestSeemOverlap/destResolution.height)*sourceResolution.height);
- CGImageRef sourceTileImageRef;
- // calculate the number of read/write operations required to assemble the
- // output image.
- int iterations = (int)( sourceResolution.height / sourceTile.size.height );
- // If tile height doesn't divide the image height evenly, add another iteration
- // to account for the remaining pixels.
- int remainder = (int)sourceResolution.height % (int)sourceTile.size.height;
- if(remainder) {
- iterations++;
- }
- // Add seem overlaps to the tiles, but save the original tile height for y coordinate calculations.
- float sourceTileHeightMinusOverlap = sourceTile.size.height;
- sourceTile.size.height += sourceSeemOverlap;
- destTile.size.height += kDestSeemOverlap;
- for( int y = 0; y < iterations; ++y ) {
- @autoreleasepool {
- sourceTile.origin.y = y * sourceTileHeightMinusOverlap + sourceSeemOverlap;
- destTile.origin.y = destResolution.height - (( y + 1 ) * sourceTileHeightMinusOverlap * imageScale + kDestSeemOverlap);
- sourceTileImageRef = CGImageCreateWithImageInRect( sourceImageRef, sourceTile );
- if( y == iterations - 1 && remainder ) {
- float dify = destTile.size.height;
- destTile.size.height = CGImageGetHeight( sourceTileImageRef ) * imageScale;
- dify -= destTile.size.height;
- destTile.origin.y += dify;
- }
- CGContextDrawImage( destContext, destTile, sourceTileImageRef );
- CGImageRelease( sourceTileImageRef );
- }
- }
-
- CGImageRef destImageRef = CGBitmapContextCreateImage(destContext);
- CGContextRelease(destContext);
- if (destImageRef == NULL) {
- return image;
- }
- UIImage *destImage = [[UIImage alloc] initWithCGImage:destImageRef scale:image.scale orientation:image.imageOrientation];
- CGImageRelease(destImageRef);
- if (destImage == nil) {
- return image;
- }
- destImage.sd_isDecoded = YES;
- destImage.sd_imageFormat = image.sd_imageFormat;
- return destImage;
- }
- #endif
- }
- #if SD_UIKIT || SD_WATCH
- // Convert an EXIF image orientation to an iOS one.
- + (UIImageOrientation)imageOrientationFromEXIFOrientation:(CGImagePropertyOrientation)exifOrientation {
- UIImageOrientation imageOrientation = UIImageOrientationUp;
- switch (exifOrientation) {
- case kCGImagePropertyOrientationUp:
- imageOrientation = UIImageOrientationUp;
- break;
- case kCGImagePropertyOrientationDown:
- imageOrientation = UIImageOrientationDown;
- break;
- case kCGImagePropertyOrientationLeft:
- imageOrientation = UIImageOrientationLeft;
- break;
- case kCGImagePropertyOrientationRight:
- imageOrientation = UIImageOrientationRight;
- break;
- case kCGImagePropertyOrientationUpMirrored:
- imageOrientation = UIImageOrientationUpMirrored;
- break;
- case kCGImagePropertyOrientationDownMirrored:
- imageOrientation = UIImageOrientationDownMirrored;
- break;
- case kCGImagePropertyOrientationLeftMirrored:
- imageOrientation = UIImageOrientationLeftMirrored;
- break;
- case kCGImagePropertyOrientationRightMirrored:
- imageOrientation = UIImageOrientationRightMirrored;
- break;
- default:
- break;
- }
- return imageOrientation;
- }
- // Convert an iOS orientation to an EXIF image orientation.
- + (CGImagePropertyOrientation)exifOrientationFromImageOrientation:(UIImageOrientation)imageOrientation {
- CGImagePropertyOrientation exifOrientation = kCGImagePropertyOrientationUp;
- switch (imageOrientation) {
- case UIImageOrientationUp:
- exifOrientation = kCGImagePropertyOrientationUp;
- break;
- case UIImageOrientationDown:
- exifOrientation = kCGImagePropertyOrientationDown;
- break;
- case UIImageOrientationLeft:
- exifOrientation = kCGImagePropertyOrientationLeft;
- break;
- case UIImageOrientationRight:
- exifOrientation = kCGImagePropertyOrientationRight;
- break;
- case UIImageOrientationUpMirrored:
- exifOrientation = kCGImagePropertyOrientationUpMirrored;
- break;
- case UIImageOrientationDownMirrored:
- exifOrientation = kCGImagePropertyOrientationDownMirrored;
- break;
- case UIImageOrientationLeftMirrored:
- exifOrientation = kCGImagePropertyOrientationLeftMirrored;
- break;
- case UIImageOrientationRightMirrored:
- exifOrientation = kCGImagePropertyOrientationRightMirrored;
- break;
- default:
- break;
- }
- return exifOrientation;
- }
- #endif
- #pragma mark - Helper Fuction
- #if SD_UIKIT || SD_WATCH
- + (BOOL)shouldDecodeImage:(nullable UIImage *)image {
- // Avoid extra decode
- if (image.sd_isDecoded) {
- return NO;
- }
- // Prevent "CGBitmapContextCreateImage: invalid context 0x0" error
- if (image == nil) {
- return NO;
- }
- // do not decode animated images
- if (image.images != nil) {
- return NO;
- }
-
- return YES;
- }
- + (BOOL)shouldScaleDownImage:(nonnull UIImage *)image limitBytes:(NSUInteger)bytes {
- BOOL shouldScaleDown = YES;
-
- CGImageRef sourceImageRef = image.CGImage;
- CGSize sourceResolution = CGSizeZero;
- sourceResolution.width = CGImageGetWidth(sourceImageRef);
- sourceResolution.height = CGImageGetHeight(sourceImageRef);
- float sourceTotalPixels = sourceResolution.width * sourceResolution.height;
- if (sourceTotalPixels <= 0) {
- return NO;
- }
- CGFloat destTotalPixels;
- if (bytes > 0) {
- destTotalPixels = bytes / kBytesPerPixel;
- } else {
- destTotalPixels = kDestTotalPixels;
- }
- if (destTotalPixels <= kPixelsPerMB) {
- // Too small to scale down
- return NO;
- }
- float imageScale = destTotalPixels / sourceTotalPixels;
- if (imageScale < 1) {
- shouldScaleDown = YES;
- } else {
- shouldScaleDown = NO;
- }
-
- return shouldScaleDown;
- }
- #endif
- static inline CGAffineTransform SDCGContextTransformFromOrientation(CGImagePropertyOrientation orientation, CGSize size) {
- // Inspiration from @libfeihu
- // We need to calculate the proper transformation to make the image upright.
- // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
- CGAffineTransform transform = CGAffineTransformIdentity;
-
- switch (orientation) {
- case kCGImagePropertyOrientationDown:
- case kCGImagePropertyOrientationDownMirrored:
- transform = CGAffineTransformTranslate(transform, size.width, size.height);
- transform = CGAffineTransformRotate(transform, M_PI);
- break;
-
- case kCGImagePropertyOrientationLeft:
- case kCGImagePropertyOrientationLeftMirrored:
- transform = CGAffineTransformTranslate(transform, size.width, 0);
- transform = CGAffineTransformRotate(transform, M_PI_2);
- break;
-
- case kCGImagePropertyOrientationRight:
- case kCGImagePropertyOrientationRightMirrored:
- transform = CGAffineTransformTranslate(transform, 0, size.height);
- transform = CGAffineTransformRotate(transform, -M_PI_2);
- break;
- case kCGImagePropertyOrientationUp:
- case kCGImagePropertyOrientationUpMirrored:
- break;
- }
-
- switch (orientation) {
- case kCGImagePropertyOrientationUpMirrored:
- case kCGImagePropertyOrientationDownMirrored:
- transform = CGAffineTransformTranslate(transform, size.width, 0);
- transform = CGAffineTransformScale(transform, -1, 1);
- break;
-
- case kCGImagePropertyOrientationLeftMirrored:
- case kCGImagePropertyOrientationRightMirrored:
- transform = CGAffineTransformTranslate(transform, size.height, 0);
- transform = CGAffineTransformScale(transform, -1, 1);
- break;
- case kCGImagePropertyOrientationUp:
- case kCGImagePropertyOrientationDown:
- case kCGImagePropertyOrientationLeft:
- case kCGImagePropertyOrientationRight:
- break;
- }
-
- return transform;
- }
- #if SD_UIKIT || SD_WATCH
- static NSUInteger gcd(NSUInteger a, NSUInteger b) {
- NSUInteger c;
- while (a != 0) {
- c = a;
- a = b % a;
- b = c;
- }
- return b;
- }
- static NSUInteger gcdArray(size_t const count, NSUInteger const * const values) {
- if (count == 0) {
- return 0;
- }
- NSUInteger result = values[0];
- for (size_t i = 1; i < count; ++i) {
- result = gcd(values[i], result);
- }
- return result;
- }
- #endif
- @end
|