Cory Imdieke
Cory Imdieke

Reputation: 14808

Flip UIImage Along Either Axis

I'm trying to create a method which flips a UIImage along the X axis, Y axis, or both. I keep getting close but my transform knowledge isn't good enough to get all the way there. Here is the code I have so far:

- (UIImage *)flippedImageByAxis:(MVImageFlip)axis{
     UIGraphicsBeginImageContext(self.size);
     CGContextRef context = UIGraphicsGetCurrentContext();
     CGAffineTransform verticalFlip = CGAffineTransformMake(1, 0, 0, -1, 0, self.size.height);
     CGAffineTransform horizFlip = CGAffineTransformMake(-1.0, 0.0, 0.0, 1.0, self.size.width, 0.0);

     if(axis == MVImageFlipXAxis || axis == MVImageFlipXAxisAndYAxis)
         CGContextConcatCTM(context, horizFlip);
     if(axis == MVImageFlipYAxis || axis == MVImageFlipXAxisAndYAxis)
         CGContextConcatCTM(context, verticalFlip);

     CGContextDrawImage(context, CGRectMake(0.0, 0.0, self.size.width, self.size.height), [self CGImage]);

     UIImage *flipedImage = UIGraphicsGetImageFromCurrentImageContext();
     UIGraphicsEndImageContext();

     return flipedImage;
 }

This flips the image but not properly. The Y-flipped image doesn't get flipped at all, the X flipped image looks like the XY image should look like, and the XY image looks like what the Y image should look like. Combining the transforms and getting them to work properly is making my head hurt.

The MVImageFlip enum is just the three you see in the code. Nothing special.

Upvotes: 4

Views: 9665

Answers (5)

ultramiraculous
ultramiraculous

Reputation: 1102

If you're intending to just display the image and not save it, you probably don't want to create a second CGContext. It's far more efficient to just use the already-loaded CGImage in the UIImage, and change the orientation like so:

- (UIImage *)flippedImageByFlippingAlongXAxis:(BOOL)flipOnX andAlongYAxis:(BOOL)flipOnY
{
    UIImageOrientation currentOrientation = self.imageOrientation;
    UIImageOrientation newOrientation = currentOrientation;

    if (flipOnX == YES) {
        switch (newOrientation) {
            case UIImageOrientationUp:
                newOrientation = UIImageOrientationUpMirrored;
                break;
            case UIImageOrientationDown:
                newOrientation = UIImageOrientationMirrored;
                break;
            case UIImageOrientationUpMirrored:
                newOrientation = UIImageOrientationUp;
                break;
            case UIImageOrientationDownMirrored:
                newOrientation = UIImageOrientationDown;
                break;
            case UIImageOrientationLeft:
                newOrientation = UIImageOrientationLeftMirrored;
                break;
            case UIImageOrientationRight:
                newOrientation = UIImageOrientationRightMirrored;
                break;
            case UIImageOrientationLeftMirrored:
                newOrientation = UIImageOrientationLeft;
                break;
            case UIImageOrientationRightMirrored:
                newOrientation = UIImageOrientationRight;
                break;
        }
    }

    if (flipOnY == YES) {
        switch (newOrientation) {
            case UIImageOrientationUp:
                newOrientation = UIImageOrientationDownMirrored;
                break;
            case UIImageOrientationDown:
                newOrientation = UIImageOrientationUpMirrored;
                break;
            case UIImageOrientationUpMirrored:
                newOrientation = UIImageOrientationDown;
                break;
            case UIImageOrientationDownMirrored:
                newOrientation = UIImageOrientationUp;
                break;
            case UIImageOrientationLeft:
                newOrientation = UIImageOrientationRightMirrored;
                break;
            case UIImageOrientationRight:
                newOrientation = UIImageOrientationLeftMirrored;
                break;
            case UIImageOrientationLeftMirrored:
                newOrientation = UIImageOrientationRight;
                break;
            case UIImageOrientationRightMirrored:
                newOrientation = UIImageOrientationLeft;
                break;
        }
    }

    return [UIImage imageWithCGImage:self.CGImage scale:self.scale orientation:newOrientation];
}

When you start a new CGContext and doing flip using CGContextDrawImage, you're allocating another block of memory to hold the same bytes in a different order. By changing the UIImage orientation, you're able to avoid another allocation. The same image data is used, just drawn in a different orientation.

Upvotes: 7

Igor Povzyk
Igor Povzyk

Reputation: 11

Swift 3 version:

func flipV(im:UIImage)->UIImage {
        var newOrient:UIImageOrientation
        switch im.imageOrientation {
        case .up:
            newOrient = .downMirrored
        case .upMirrored:
            newOrient = .down
        case .down:
            newOrient = .upMirrored
        case .downMirrored:
            newOrient = .up
        case .left:
            newOrient = .leftMirrored
        case .leftMirrored:
            newOrient = .left
        case .right:
            newOrient = .rightMirrored
        case .rightMirrored:
            newOrient = .right
        }
        return UIImage(cgImage: im.cgImage!, scale: im.scale, orientation: newOrient)
    }

    func flipH(im:UIImage)->UIImage {
        var newOrient:UIImageOrientation
        switch im.imageOrientation {
        case .up:
            newOrient = .upMirrored
        case .upMirrored:
            newOrient = .up
        case .down:
            newOrient = .downMirrored
        case .downMirrored:
            newOrient = .down
        case .left:
            newOrient = .rightMirrored
        case .leftMirrored:
            newOrient = .right
        case .right:
            newOrient = .leftMirrored
        case .rightMirrored:
            newOrient = .left
        }
        return UIImage(cgImage: im.cgImage!, scale: im.scale, orientation: newOrient)
    }

Upvotes: 0

fresidue
fresidue

Reputation: 918

This is merely a slightly fixed and 'updated to Swift' version of the old answer by @ultramiraculous. Just in case it helps someone.

Vertical Flip (reflection over x-axis):

func flipV(im:UIImage)->UIImage {
    var newOrient:UIImageOrientation
    switch im.imageOrientation {
    case .Up:
        newOrient = .DownMirrored
    case .UpMirrored:
        newOrient = .Down
    case .Down:
        newOrient = .UpMirrored
    case .DownMirrored:
        newOrient = .Up
    case .Left:
        newOrient = .LeftMirrored
    case .LeftMirrored:
        newOrient = .Left
    case .Right:
        newOrient = .RightMirrored
    case .RightMirrored:
        newOrient = .Right
    }
    return UIImage(CGImage: im.CGImage, scale: im.scale, orientation: newOrient)
}

Horizontal Flip (reflection over y-axis)

func flipH(im:UIImage)->UIImage {
    var newOrient:UIImageOrientation
    switch im.imageOrientation {
    case .Up:
        newOrient = .UpMirrored
    case .UpMirrored:
        newOrient = .Up
    case .Down:
        newOrient = .DownMirrored
    case .DownMirrored:
        newOrient = .Down
    case .Left:
        newOrient = .RightMirrored
    case .LeftMirrored:
        newOrient = .Right
    case .Right:
        newOrient = .LeftMirrored
    case .RightMirrored:
        newOrient = .Left
    }
    return UIImage(CGImage: im.CGImage, scale: im.scale, orientation: newOrient)
}

Upvotes: 2

Cory Imdieke
Cory Imdieke

Reputation: 14808

I finally was able to figure this out. Here is the code that works for anyone else who might need it.

- (UIImage *)flippedImageByAxis:(MVImageFlip)axis{
    UIGraphicsBeginImageContext(self.size);
    CGContextRef context = UIGraphicsGetCurrentContext();

    if(axis == MVImageFlipXAxis){
        // Do nothing, X is flipped normally in a Core Graphics Context
    } else if(axis == MVImageFlipYAxis){
        // fix X axis
        CGContextTranslateCTM(context, 0, self.size.height);
        CGContextScaleCTM(context, 1.0f, -1.0f);

        // then flip Y axis
        CGContextTranslateCTM(context, self.size.width, 0);
        CGContextScaleCTM(context, -1.0f, 1.0f);
    } else if(axis == MVImageFlipXAxisAndYAxis){
        // just flip Y
        CGContextTranslateCTM(context, self.size.width, 0);
        CGContextScaleCTM(context, -1.0f, 1.0f);
    }

    CGContextDrawImage(context, CGRectMake(0.0, 0.0, self.size.width, self.size.height), [self CGImage]);

    UIImage *flipedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return flipedImage;
}

Upvotes: 25

Ziminji
Ziminji

Reputation: 1306

Try the following:

UIImage* myimage = [UIImage imageNamed: @"myimage.png"];
myimage = [UIImage imageWithCGImage: myimage.CGImage scale: 1.0f orientation: UIImageOrientationUpMirrored];

Upvotes: 9

Related Questions