web-dev-qa-db-ja.com

CGContextDrawImageは、UIImage.CGImageが渡されると画像を上下逆さまに描画します

誰がCGContextDrawImageが私の画像を上下逆さまに描いているのか知っていますか?私のアプリケーションから画像をロードしています:

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

そして、単にコアグラフィックスにそれを私のコンテキストに描画するように依頼します。

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

適切な場所と寸法でレンダリングされますが、画像は上下逆になっています。ここで本当に明白な何かを見逃しているに違いありませんか?

178
rustyshelf

の代わりに

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

つかいます

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

Begin/end CGcontextメソッドの途中で。

これにより、現在の画像コンテキストに正しい方向で画像が描画されます-これは、UIImageメソッドが基礎となるrawを取得する間、CGContextDrawImageが方向の知識を保持していることと関係があると確信しています向きがわからない画像データ。

多くの分野でこの問題に遭遇することに注意してください。しかし、1つの特定の例は、アドレス帳のユーザー画像を扱うことです。

私が言及したすべてを適用した後でも、私はまだ画像でドラマを持っていました。最後に、Gimpを使用して、すべての画像の「反転垂直」バージョンを作成しました。これで、変換を使用する必要がなくなりました。うまくいけば、これが今後の問題を引き起こさないことを願っています。

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呼び出しの直前に2つを結合すると、画像が正しく描画されます。

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);
213
Cliff Viegas

両方の世界のベスト、UIImageのdrawAtPoint:またはdrawInRect:を使用しながら、カスタムコンテキストを指定します:

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

また、2番目の答えであるCGContextTranslateCTMまたはCGContextScaleCTMを使用してコンテキストを変更することも避けます。

20
Rivera

関連するQuartz2Dドキュメント: https://developer.Apple.com/library/ios/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/GraphicsDrawingOverview/GraphicsDrawingOverview.html#//Apple_ref/doc/uid/TP40010156-CH14-SW4

デフォルトの座標系の反転

UIKitの描画を反転すると、バッキングCALayerが変更され、LLO座標系を持つ描画環境がUIKitのデフォルトの座標系に合わせられます。描画にUIKitのメソッドと関数のみを使用する場合、CTMを反転する必要はありません。ただし、Core GraphicsまたはImage I/O関数の呼び出しとUIKitの呼び出しを混在させる場合、CTMの反転が必要になる場合があります。

具体的には、Core Graphics関数を直接呼び出して画像またはPDFドキュメントを描画する場合、オブジェクトはビューのコンテキストで上下逆さまにレンダリングされます。画像とページを正しく表示するには、CTMを反転する必要があります。

Core Graphicsコンテキストに描画されたオブジェクトを反転して、UIKitビューに表示されたときに正しく表示されるようにするには、2つの手順で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
kcmoffat

UIImageについてはわかりませんが、このような動作は通常、座標が反転したときに発生します。 OS X座標系のほとんどは、PostscriptやPDFのように、左下隅に原点があります。ただし、CGImage座標系の原点は左上隅にあります。

可能なソリューションには、isFlippedプロパティまたはscaleYBy:-1アフィン変換が含まれる場合があります。

7
mouviciel

コンテキスト内のカスタム四角形に画像を描画するための簡単なソリューションに興味がある人は:

 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
ZpaceZombor

同じ問題を使用してこの問題を解決できます。

UIGraphicsBeginImageContext(image.size);

UIGraphicsPushContext(context);

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

UIGraphicsPopContext();

UIGraphicsEndImageContext();
3
Priyanka V

Swiftコードによる補足回答

Quartz 2Dグラフィックスは、左下にOriginがある座標系を使用し、iOSのUIKitは左上にOriginを持つ座標系を使用します。通常はすべて正常に機能しますが、グラフィック操作を実行するときは、自分で座標系を変更する必要があります。 ドキュメント 状態:

一部のテクノロジーは、Quartzで使用されているものとは異なるデフォルトの座標系を使用してグラフィックコンテキストを設定します。 Quartzに対して、このような座標系は修正された座標系であり、一部のQuartz描画操作を実行するときに補正する必要があります。最も一般的な修正された座標系では、Originがコンテキストの左上隅に配置され、y軸がページの下部を指すように変更されます。

この現象は、drawRectメソッドで画像を描画するカスタムビューの次の2つのインスタンスで見ることができます。

enter image description here

左側では、画像は上下逆になっており、右側では、原点が左上になるように座標系が変換およびスケーリングされています。

上下逆さまの画像

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
Suragch

Swift 3.0および4.0

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

変更は必要ありません

3
Abdul Waheed

UIImageには、その主なコンテンツメンバーとしてのCGImageと、スケーリングおよび方向係数が含まれます。 CGImageとそのさまざまな関数はOSXから派生しているため、iPhoneと比較して逆さまの座標系が必要です。 UIImageを作成すると、デフォルトで逆さまの向きになります(これは変更できます!)。 .CGImageプロパティを使用して非常に強力なCGImage関数にアクセスしますが、iPhone画面などに描画するにはUIImageメソッドを使用するのが最適です。

3
James L

Swift 3 CoreGraphics Solution

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
biomiker

drawInRectは確かに道です。これを行うときに便利な方法がもう1つあります。通常、画像とそれが入ろうとする長方形は適合しません。その場合、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
marc meyer

私のプロジェクトの過程で、私は Kendallの答え から Cliffの答え にジャンプして、携帯電話から読み込まれた画像のこの問題を解決しました。

最終的には、代わりに CGImageCreateWithPNGDataProvider を使用しました:

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

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

これは、CGImageからUIImageを取得することで得られるオリエンテーションの問題に悩まされることはなく、ヒッチなしでCALayerのコンテンツとして使用できます。

0
Ja͢ck
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
neoneye