Vertical flip of CGContext

29,214

Solution 1

The CTM affects future drawing; you're capturing what you have already drawn. You need to concat that transformation before you draw, not after.

Solution 2

Here is some code that I wrote based on this answer, in case it is helpful to anyone:

#import <QuartzCore/QuartzCore.h>
...
+ (UIImage *) flipImageVertically:(UIImage *)originalImage {
    UIImageView *tempImageView = [[UIImageView alloc] initWithImage:originalImage];

    UIGraphicsBeginImageContext(tempImageView.frame.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGAffineTransform flipVertical = CGAffineTransformMake(
            1, 0, 0, -1, 0, tempImageView.frame.size.height
    );
    CGContextConcatCTM(context, flipVertical);  

    [tempImageView.layer renderInContext:context];

    UIImage *flipedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    [tempImageView release];

    return flipedImage;
}   

Solution 3

Here is the same code as above (benvolioT's answer) but in SWIFT 4.0

import QuartzCore
...
func flipImageVertically(originalImage:UIImage) -> UIImage{

    let tempImageView:UIImageView = UIImageView(image: originalImage)
    UIGraphicsBeginImageContext(tempImageView.frame.size)
    let context:CGContext = UIGraphicsGetCurrentContext()!
    let flipVertical: CGAffineTransform = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: tempImageView.frame.size.height)

    context.concatenate(flipVertical)
    tempImageView.layer.render(in: context)

    let flippedImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()

    return flippedImage
}

And here is an even better way in SWIFT:

func flipImageVertically(originalImage:UIImage) -> UIImage {
    let image:UIImage = UIImage(CGImage: originalImage.CGImage, scale: 1.0, orientation: UIImageOrientation.RightMirrored)!
    return image
}

Solution 4

Swift 4:

let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: originalImage.size.height)
ctx.concatenate(flipVertical)

Solution 5

Use this function in Swift 4:

func drawImage(image: UIImage) -> UIImage {
    // Setup our context
    let opaque = false
    let scale: CGFloat = 0 // 0 means we use scale factor of the device’s main screen.
    UIGraphicsBeginImageContextWithOptions(image.size, opaque, scale)
    let context = UIGraphicsGetCurrentContext()!
    let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: image.size.height)
    context.concatenate(flipVertical) // flip by y

    // draw your another figures
    //...
    // Drawing complete, set context to image and finish
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image!
}
Share:
29,214
teabot
Author by

teabot

Big Data Developer at Expedia Tinkerer and Maker at the London Hackspace (my personal page)

Updated on March 07, 2021

Comments

  • teabot
    teabot about 3 years

    I have a UIView that I am trying to render into a UIImage using [CALayer renderInContext:]. However, I find that the resultant image is flipped vertically. I kind of expect this due to the different coordinate systems. However, I then try and flip the context back to normal with an affine transform - but it doesn't have any effect:

    CGAffineTransform flipVertical = CGAffineTransformMake(
        1, 0, 0, -1, 0, imageContextHeight
    );
    CGContextConcatCTM(imageContext, flipVertical);
    CGImageRef cgImage = CGBitmapContextCreateImage(imageContext);
    UIImage* uiImage = [[UIImage imageWithCGImage:cgImage] retain];
    CGImageRelease(cgImage);
    

    What am I doing wrong?

  • MindBlower3
    MindBlower3 over 5 years
    Second method doesn't fix the issue reported in Question, but the first does
  • agurtovoy
    agurtovoy over 2 years
    Note that CGContext.height works only for bitmap contexts and returns 0 in other cases, so you might want to pass the height explicitly here.