CGContextDrawImage는 UIImage를 전달할 때 이미지를 거꾸로 그립니다.


179

CGContextDrawImage내 이미지가 거꾸로 그려 지는지 아는 사람이 있습니까? 내 응용 프로그램에서 이미지를로드하고 있습니다.

UIImage *image = [UIImage imageNamed:@"testImage.png"];

그런 다음 핵심 그래픽을 내 컨텍스트에 그려달라고 간단히 요청하십시오.

CGContextDrawImage(context, CGRectMake(0, 0, 145, 15), image.CGImage);

올바른 위치와 치수로 렌더링되지만 이미지는 거꾸로되어 있습니다. 나는 여기에 정말로 명백한 것을 놓치고 있어야합니까?

답변:


238

대신에

CGContextDrawImage(context, CGRectMake(0, 0, 145, 15), image.CGImage);

사용하다

[image drawInRect:CGRectMake(0, 0, 145, 15)];

시작 / 끝의 중간에 CGcontext 방법 .

이렇게하면 올바른 방향으로 이미지를 현재 이미지 컨텍스트에 그릴 것입니다.이 방법은 UIImage방향에 대한 지식을 유지하는 것과 관련이 있으며 CGContextDrawImage방법은 방향을 이해하지 않고 기본 원시 이미지 데이터를 가져옵니다.


19
이 솔루션을 사용하면 CGContextSetAlpha ()와 같은 이미지에서 CG 함수를 사용할 수 없지만 두 번째 답변은 그렇지 않습니다.
samvermette

2
좋은 점은 대부분 이미지를 그리는 데 사용자 정의 알파 수준이 필요하지는 않습니다 (일반적으로 알파가 필요한 항목에 대해서는 미리 이미지로 구워 짐). 기본적으로 내 좌우명은 코드가 많을수록 버그가 발생할 가능성이 높기 때문에 가능한 한 작은 코드를 사용하는 것입니다.
Kendall Helmstetter Gelner

안녕하세요, 시작 / 종료 CGContext 메소드 중간에 무엇을 의미합니까? 샘플 코드를 더 주시겠습니까? 어딘가에 컨텍스트를 저장하고 컨텍스트를 사용하여 이미지를 그리기 위해 노력하고 있습니다.
vodkhang

2
Rusty에서는 작동하지만-[UIImage drawInRect :]에는 스레드로부터 안전하지 않다는 문제가 있습니다. 내 특정 응용 프로그램의 경우 CGContextDrawImage ()를 처음 사용하는 이유입니다.
Olie

2
명확히하기 위해 : Core Graphics는 iOS에 비해 좌표 시스템이 거꾸로되어있는 OS X에 내장되어 있습니다 (OS X의 왼쪽 아래에서 시작). 그래서 코어 그래픽스가 이미지를 거꾸로 그리는 반면 drawInRect가이를 처리합니다
borchero

213

내가 언급 한 모든 것을 적용한 후에도 여전히 이미지와 함께 드라마를 보았습니다. 결국, 나는 Gimp를 사용하여 모든 이미지의 'flipped vertical'버전을 만들었습니다. 이제 변환을 사용할 필요가 없습니다. 잘만되면 이것이 더 이상 문제를 일으키지 않을 것입니다.

CGContextDrawImage가 왜 내 이미지를 거꾸로 그리는지 아는 사람이 있습니까? 내 응용 프로그램에서 이미지를로드하고 있습니다.

Quartz2d는 원점이 왼쪽 아래 모서리에있는 다른 좌표계를 사용합니다. 따라서 Quartz가 100 * 100 이미지의 픽셀 x [5], y [10]을 그리면 왼쪽 상단 대신 왼쪽 하단에 픽셀이 그려집니다. 따라서 '플립'이미지가 발생합니다.

x 좌표계가 일치하므로 y 좌표를 뒤집어 야합니다.

CGContextTranslateCTM(context, 0, image.size.height);

이것은 x 축에서 이미지를 0 단위로, y 축에서 이미지 높이로 이미지를 변환했음을 의미합니다. 그러나 이것만으로도 이미지가 여전히 거꾸로되어 있다는 것을 의미합니다. 아래에서 "image.size.height"를 그리면됩니다.

Quartz2D 프로그래밍 가이드는 ScaleCTM을 사용하고 음수 값을 전달하여 이미지를 뒤집는 것이 좋습니다. 다음 코드를 사용하여이를 수행 할 수 있습니다.

CGContextScaleCTM(context, 1.0, -1.0);

CGContextDrawImage통화 직전에 두 가지를 결합하면 이미지가 올바르게 그려집니다.

UIImage *image = [UIImage imageNamed:@"testImage.png"];    
CGRect imageRect = CGRectMake(0, 0, image.size.width, image.size.height);       

CGContextTranslateCTM(context, 0, image.size.height);
CGContextScaleCTM(context, 1.0, -1.0);

CGContextDrawImage(context, imageRect, image.CGImage);

의도하지 않은 결과를 얻을 수 있으므로 imageRect 좌표가 이미지의 좌표와 일치하지 않으면 조심하십시오.

좌표를 다시 변환하려면

CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0, -imageRect.size.height);

이것은 좋지만 0,0 및 1.0,1.0으로 다시 설정하려고 할 때도 다른 것들이 번역 및 스케일의 영향을 받고 있음을 알았습니다. 결국 Kendall의 솔루션과 함께 갔지만 UIImage를 사용하지 않고 UIImage를 사용하고 있음을 알았습니다. 우리가 여기에서 작업하고있는 낮은 수준의 CGImage 자료
PeanutPower

3
drawLayer를 사용 중이고 컨텍스트에 CGImageRef를 그리려는 경우이 기술은 의사가 지시하기를 원합니다! 이미지에 대한 rect가 있으면 CGContextTranslateCTM (context, 0, image.size.height + image.origin.y) 다음 CGContextDrawImage () 전에 rect.origin.y를 0으로 설정하십시오. 감사!
David H

ImageMagick에서 한 번 문제가 발생하여 iPhone 카메라의 방향이 잘못되었습니다. 이미지 파일에서 방향 플래그를 사용하는 것으로 나타났습니다. 따라서 세로 이미지는 플래그가 "왼쪽"으로 설정된 상태에서 960px x 640px입니다. 이 글타래는 다음과 같이 도움이 될 수 있습니다. stackoverflow.com/questions/1260249/…
Hari Karam Singh

8
변환 행렬 을 사용 CGContextSaveGState()하고 CGContextRestoreGState()저장 및 복원 하지 않는 이유는 무엇 입니까?
Ja͢ck

20

두 세계에서 가장 좋은 것은 UIImage를 사용 drawAtPoint:하거나 drawInRect:여전히 사용자 정의 컨텍스트를 지정하는 동안입니다.

UIGraphicsPushContext(context);
[image drawAtPoint:CGPointZero]; // UIImage will handle all especial cases!
UIGraphicsPopContext();

또한 당신은 당신의 문맥을 수정하지 않도록 CGContextTranslateCTM하거나 CGContextScaleCTM하는 두 번째 대답은 않습니다.


이것은 좋은 생각이지만 저에게는 이미지가 거꾸로되어 있고 왼쪽에서 오른쪽으로 나타납니다. 결과는 이미지마다 다를 것이라고 생각합니다.
Luke Rogers

어쩌면 이것이 이후 iOS 릴리스에서 작동을 멈췄습니까? 당시에는 모든 것이 나를 위해 일했습니다.
Rivera

실제로 컨텍스트를 만드는 방법에 달려 있다고 생각합니다. UIGraphicsBeginImageContextWithOptions새로운 CGContext것을 초기화하는 대신 사용하기 시작 하면 작동하는 것 같습니다.
Luke Rogers

12

관련 Quartz2D 문서 : https://developer.apple.com/library/ios/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/GraphicsDrawingOverview/GraphicsDrawingOverview.html#//apple_ref/doc/uid/TP40010156-CH14-SW4

기본 좌표계 뒤집기

UIKit 도면을 뒤집 으면 LLO 좌표계가있는 도면 환경을 UIKit의 기본 좌표계와 정렬하도록 보조 CALayer를 수정합니다. 그리기에 UIKit 메서드와 함수 만 사용하는 경우 CTM을 뒤집을 필요가 없습니다. 그러나 CoreKit 또는 Image I / O 함수 호출을 UIKit 호출과 혼합하면 CTM 뒤집기가 필요할 수 있습니다.

특히, Core Graphics 함수를 직접 호출하여 이미지 또는 PDF 문서를 그리면 객체가 뷰 컨텍스트에서 거꾸로 렌더링됩니다. 이미지와 페이지를 올바르게 표시하려면 CTM을 뒤집어 야합니다.

UIKit보기에 표시 될 때 올바르게 표시되도록 Core Graphics 컨텍스트에 그려진 객체를 뒤집으려면 CTM을 두 단계로 수정해야합니다. 원점을 도면 영역의 왼쪽 위 모서리로 변환 한 다음 y 좌표를 -1로 수정하여 축척 변환을 적용합니다. 이를 수행하는 코드는 다음과 유사합니다.

CGContextSaveGState(graphicsContext);
CGContextTranslateCTM(graphicsContext, 0.0, imageHeight);
CGContextScaleCTM(graphicsContext, 1.0, -1.0);
CGContextDrawImage(graphicsContext, image, CGRectMake(0, 0, imageWidth, imageHeight));
CGContextRestoreGState(graphicsContext);

10

컨텍스트에서 사용자 정의 rect로 이미지를 그리기위한 무료 솔루션에 관심이있는 경우 :

 func drawImage(image: UIImage, inRect rect: CGRect, context: CGContext!) {

    //flip coords
    let ty: CGFloat = (rect.origin.y + rect.size.height)
    CGContextTranslateCTM(context, 0, ty)
    CGContextScaleCTM(context, 1.0, -1.0)

    //draw image
    let rect__y_zero = CGRect(origin: CGPoint(x: rect.origin.x, y:0), size: rect.size)
    CGContextDrawImage(context, rect__y_zero, image.CGImage)

    //flip back
    CGContextScaleCTM(context, 1.0, -1.0)
    CGContextTranslateCTM(context, 0, -ty)

}

이미지는 사각형을 채우기 위해 크기가 조정됩니다.


7

확실하지 UIImage않지만 이러한 종류의 동작은 일반적으로 좌표가 뒤집힐 때 발생합니다. OS X 좌표계의 대부분은 Postscript 및 PDF에서와 같이 왼쪽 하단에 원점이 있습니다. 그러나 CGImage좌표계는 왼쪽 상단에 원점이 있습니다.

가능한 솔루션에는 isFlipped속성 또는 scaleYBy:-1아핀 변환 이 포함될 수 있습니다 .


3

UIImageCGImage주요 콘텐츠 멤버로 포함하고 배율 및 방향 요소를 포함합니다. CGImage다양한 기능이 OSX에서 파생 되었기 때문에 iPhone에 비해 거꾸로되는 좌표 시스템이 필요합니다. 을 만들면 UIImage기본적으로 거꾸로 된 방향으로 보정됩니다 (이를 변경할 수 있습니다!). 사용 . CGImage속성은 매우 강력한 CGImage기능 에 액세스 하지만 iPhone 화면에 그리는 등의 UIImage방법으로 가장 잘 수행됩니다 .


1
UIImage의 기본 거꾸로 방향을 어떻게 변경합니까?
MegaManX

3

스위프트 코드로 보충 답변

Quartz 2D 그래픽은 왼쪽 하단에 원점이있는 좌표계를 사용하고 iOS의 UIKit은 왼쪽 상단에 원점이있는 좌표계를 사용합니다. 모든 것이 정상적으로 작동하지만 일부 그래픽 작업을 수행 할 때는 좌표계를 직접 수정해야합니다. 설명서 에는 다음 이 명시되어 있습니다.

일부 기술은 Quartz에서 사용하는 것과 다른 기본 좌표계를 사용하여 그래픽 컨텍스트를 설정합니다. Quartz와 관련하여 이러한 좌표계는 수정 된 좌표계이며 일부 Quartz 그리기 작업을 수행 할 때 보정해야합니다. 가장 일반적으로 수정 된 좌표계는 원점을 컨텍스트의 왼쪽 상단 모서리에 배치하고 y 축을 페이지 맨 아래를 가리 키도록 변경합니다.

이 현상은 다음과 같은 두 가지 사용자 정의보기 인스턴스에서 볼 수 있습니다 drawRect.

여기에 이미지 설명을 입력하십시오

왼쪽에서 이미지는 뒤집어지고 오른쪽에서 좌표계는 원점이 왼쪽 상단에 있도록 변환되고 크기가 조정됩니다.

거꾸로 된 이미지

override func drawRect(rect: CGRect) {

    // image
    let image = UIImage(named: "rocket")!
    let imageRect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)

    // context
    let context = UIGraphicsGetCurrentContext()

    // draw image in context
    CGContextDrawImage(context, imageRect, image.CGImage)

}

수정 된 좌표계

override func drawRect(rect: CGRect) {

    // image
    let image = UIImage(named: "rocket")!
    let imageRect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)

    // context
    let context = UIGraphicsGetCurrentContext()

    // save the context so that it can be undone later
    CGContextSaveGState(context)

    // put the origin of the coordinate system at the top left
    CGContextTranslateCTM(context, 0, image.size.height)
    CGContextScaleCTM(context, 1.0, -1.0)

    // draw the image in the context
    CGContextDrawImage(context, imageRect, image.CGImage)

    // undo changes to the context
    CGContextRestoreGState(context)
}

3

스위프트 3.0 및 4.0

yourImage.draw(in: CGRect, blendMode: CGBlendMode, alpha: ImageOpacity)

변경 불필요


2

동일한 기능을 사용하여이 문제를 해결할 수 있습니다.

UIGraphicsBeginImageContext(image.size);

UIGraphicsPushContext(context);

[image drawInRect:CGRectMake(gestureEndPoint.x,gestureEndPoint.y,350,92)];

UIGraphicsPopContext();

UIGraphicsEndImageContext();

1

drawInRect확실히 갈 길입니다. 이 작업을 수행 할 때 유용하게 사용될 또 다른 작은 기능이 있습니다. 일반적으로 그림과 그림이 들어갈 사각형은 일치하지 않습니다. 이 경우 drawInRect그림이 늘어납니다. 다음은 변형을 반대로하여 사진의 가로 세로 비율이 변경되지 않도록하는 빠르고 멋진 방법입니다 (전체 내용에 맞음).

//Picture and irect don't conform, so there'll be stretching, compensate
    float xf = Picture.size.width/irect.size.width;
    float yf = Picture.size.height/irect.size.height;
    float m = MIN(xf, yf);
    xf /= m;
    yf /= m;
    CGContextScaleCTM(ctx, xf, yf);

    [Picture drawInRect: irect];

1

스위프트 3 코어 그래픽 솔루션

UIImage 대신 CG를 사용하려는 경우 이전 답변을 기반으로 한이 Swift 3 구성으로 문제가 해결되었습니다.

if let cgImage = uiImage.cgImage {
    cgContext.saveGState()
    cgContext.translateBy(x: 0.0, y: cgRect.size.height)
    cgContext.scaleBy(x: 1.0, y: -1.0)
    cgContext.draw(cgImage, in: cgRect)
    cgContext.restoreGState()
}

1

QuartzCore의 좌표계는 "왼쪽 아래"이고 UIKit – "왼쪽 위"이기 때문입니다.

이 경우 CGContext 를 확장 할 수 있습니다 .

extension CGContext {
  func changeToTopLeftCoordinateSystem() {
    translateBy(x: 0, y: boundingBoxOfClipPath.size.height)
    scaleBy(x: 1, y: -1)
  }
}

// somewhere in render 
ctx.saveGState()
ctx.changeToTopLeftCoordinateSystem()
ctx.draw(cgImage!, in: frame)

1

이미지 rect에서 0이 아닌 원점을 올바르게 처리 하는이 Swift 5 순수한 코어 그래픽 확장을 사용합니다.

extension CGContext {

    /// Draw `image` flipped vertically, positioned and scaled inside `rect`.
    public func drawFlipped(_ image: CGImage, in rect: CGRect) {
        self.saveGState()
        self.translateBy(x: 0, y: rect.origin.y + rect.height)
        self.scaleBy(x: 1.0, y: -1.0)
        self.draw(image, in: CGRect(origin: CGPoint(x: rect.origin.x, y: 0), size: rect.size))
        self.restoreGState()
    }
}

CGContext일반 draw(: in:)방법 과 똑같이 사용할 수 있습니다 .

ctx.drawFlipped(myImage, in: myRect)

0

내 프로젝트 과정에서 나는 Kendall의 답변 에서 Cliff의 답변으로 뛰어 들었다. 폰 자체에서로드 된 이미지 의이 문제를 해결하기 위해 으로 습니다.

결국 나는 CGImageCreateWithPNGDataProvider대신 대신 사용 했다 :

NSString* imageFileName = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"clockdial.png"];

return CGImageCreateWithPNGDataProvider(CGDataProviderCreateWithFilename([imageFileName UTF8String]), NULL, YES, kCGRenderingIntentDefault);

이것은 CGImagea에서 얻을 수있는 방향 문제로 고통받지 않으며 장애없이 UIImage내용으로 사용할 수 있습니다 CALayer.


0
func renderImage(size: CGSize) -> UIImage {
    return UIGraphicsImageRenderer(size: size).image { rendererContext in
        // flip y axis
        rendererContext.cgContext.translateBy(x: 0, y: size.height)
        rendererContext.cgContext.scaleBy(x: 1, y: -1)

        // draw image rotated/offsetted
        rendererContext.cgContext.saveGState()
        rendererContext.cgContext.translateBy(x: translate.x, y: translate.y)
        rendererContext.cgContext.rotate(by: rotateRadians)
        rendererContext.cgContext.draw(cgImage, in: drawRect)
        rendererContext.cgContext.restoreGState()
    }
}

0

스위프트 5 답변@ ZpaceZombor의 탁월한 답변을 기반으로 한 답변

UIImage가있는 경우

var image: UIImage = .... 
image.draw(in: CGRect)

CGImage가있는 경우 아래 카테고리를 사용하십시오.

참고 : 다른 답변과 달리이 답변은 그리려는 rect에 y! = 0이있을 수 있음을 고려합니다.이를 고려하지 않은 답변은 올바르지 않으며 일반적인 경우에는 작동하지 않습니다.

extension CGContext {
    final func drawImage(image: CGImage, inRect rect: CGRect) {

        //flip coords
        let ty: CGFloat = (rect.origin.y + rect.size.height)
        translateBy(x: 0, y: ty)
        scaleBy(x: 1.0, y: -1.0)

        //draw image
        let rect__y_zero = CGRect(x: rect.origin.x, y: 0, width: rect.width, height: rect.height)
        draw(image, in: rect__y_zero)

        //flip back
        scaleBy(x: 1.0, y: -1.0)
        translateBy(x: 0, y: -ty)

    }
} 

다음과 같이 사용하십시오.

let imageFrame: CGRect = ...
let context: CGContext = ....
let img: CGImage = ..... 
context.drawImage(image: img, inRect: imageFrame)

및 방법 내부 uiImage.cgimage 처리 (CGRect :있는 UIImage, inRect의 RECT 이미지)이 거의 완벽한 솔루션지만, 그릴 기능을 변경 고려입니다
화성

-5

다음을 수행하여이 문제를 해결할 수도 있습니다.

//Using an Image as a mask by directly inserting UIImageObject.CGImage causes
//the same inverted display problem. This is solved by saving it to a CGImageRef first.

//CGImageRef image = [UImageObject CGImage];

//CGContextDrawImage(context, boundsRect, image);

Nevermind... Stupid caching.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.