blob: 9f87fe7a58cbcdb44f948e0e4b74c697c620f39a [file]
/*
Copyright 2016-present the Material Components for iOS authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "UIImage+MaterialRTL.h"
/** Returns the horizontally flipped version of the given UIImageOrientation. */
static UIImageOrientation MDCRTLMirroredOrientation(UIImageOrientation sourceOrientation) {
switch (sourceOrientation) {
case UIImageOrientationUp:
return UIImageOrientationUpMirrored;
case UIImageOrientationDown:
return UIImageOrientationDownMirrored;
case UIImageOrientationLeft:
return UIImageOrientationLeftMirrored;
case UIImageOrientationRight:
return UIImageOrientationRightMirrored;
case UIImageOrientationUpMirrored:
return UIImageOrientationUp;
case UIImageOrientationDownMirrored:
return UIImageOrientationDown;
case UIImageOrientationLeftMirrored:
return UIImageOrientationLeft;
case UIImageOrientationRightMirrored:
return UIImageOrientationRight;
}
NSCAssert(NO, @"Invalid enumeration value %i.", (int)sourceOrientation);
return UIImageOrientationUpMirrored;
}
/**
Returns a copy of the image actually flipped. The orientation and scale are consumed, while the
rendering mode is ported to the new image.
On iOS 9 and above, use `-[UIView imageFlippedForRightToLeftLayoutDirection]` instead.
*/
static UIImage *MDCRTLFlippedImage(UIImage *image) {
NSCAssert(![image respondsToSelector:@selector(imageFlippedForRightToLeftLayoutDirection)],
@"Do not call this method on iOS 9 and above.");
CGSize size = [image size];
CGRect rect = CGRectMake(0.0f, 0.0f, size.width, size.height);
UIGraphicsBeginImageContextWithOptions(rect.size, NO, image.scale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetShouldAntialias(context, true);
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
// Note: UIKit's and CoreGraphics coordinates systems are flipped vertically (UIKit's Y axis goes
// down, while CoreGraphics' goes up).
switch (image.imageOrientation) {
case UIImageOrientationUp:
CGContextScaleCTM(context, -1, -1);
CGContextTranslateCTM(context, -rect.size.width, -rect.size.height);
break;
case UIImageOrientationDown:
// Orientation down is equivalent to a 180ยบ rotation. The difference in coordinates systems is
// thus sufficient and nothing needs to be down to flip the image.
break;
case UIImageOrientationLeft:
CGContextRotateCTM(context, -(CGFloat)M_PI / 2.f);
CGContextTranslateCTM(context, -rect.size.width, 0);
break;
case UIImageOrientationRight:
CGContextRotateCTM(context, (CGFloat)M_PI / 2);
CGContextTranslateCTM(context, 0, -rect.size.width);
break;
case UIImageOrientationUpMirrored:
CGContextScaleCTM(context, 1, -1);
CGContextTranslateCTM(context, 0, -rect.size.height);
break;
case UIImageOrientationDownMirrored:
CGContextScaleCTM(context, -1, 1);
CGContextTranslateCTM(context, -rect.size.width, 0);
break;
case UIImageOrientationLeftMirrored:
CGContextRotateCTM(context, -(CGFloat)M_PI / 2);
CGContextTranslateCTM(context, -rect.size.width, 0);
CGContextScaleCTM(context, -1, 1);
CGContextTranslateCTM(context, -rect.size.width, 0);
break;
case UIImageOrientationRightMirrored:
CGContextRotateCTM(context, (CGFloat)M_PI / 2);
CGContextTranslateCTM(context, 0, -rect.size.width);
CGContextScaleCTM(context, -1, 1);
CGContextTranslateCTM(context, -rect.size.width, 0);
break;
default:
NSCAssert(NO, @"Invalid enumeration value %i.", (int)image.imageOrientation);
}
CGContextDrawImage(context, rect, image.CGImage);
UIImage *flippedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Port the rendering mode.
flippedImage = [flippedImage imageWithRenderingMode:image.renderingMode];
return flippedImage;
}
@implementation UIImage (MaterialRTL)
- (UIImage *)mdc_imageFlippedForRightToLeftLayoutDirection {
// On iOS 9 and above, UIImage supports being prepared for flipping. Otherwise, do the flip
// manually.
if ([self respondsToSelector:@selector(imageFlippedForRightToLeftLayoutDirection)]) {
return [self imageFlippedForRightToLeftLayoutDirection];
} else {
// -[UIImage imageWithCGImage:scale:orientation:] looses the rendering mode.
// If the image has a default rendering mode (i.e. UIImageRenderingModeAutomatic), it is fine to
// use it.
//
if (self.renderingMode == UIImageRenderingModeAutomatic) {
return [[self class] imageWithCGImage:self.CGImage
scale:self.scale
orientation:MDCRTLMirroredOrientation(self.imageOrientation)];
} else { // Otherwise, flip the image manually.
return MDCRTLFlippedImage(self);
}
}
}
@end