誰がCGContextDrawImage
が私の画像を上下逆さまに描いているのか知っていますか?私のアプリケーションから画像をロードしています:
UIImage *image = [UIImage imageNamed:@"testImage.png"];
そして、単にコアグラフィックスにそれを私のコンテキストに描画するように依頼します。
CGContextDrawImage(context, CGRectMake(0, 0, 145, 15), image.CGImage);
適切な場所と寸法でレンダリングされますが、画像は上下逆になっています。ここで本当に明白な何かを見逃しているに違いありませんか?
の代わりに
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);
両方の世界のベスト、UIImageのdrawAtPoint:
またはdrawInRect:
を使用しながら、カスタムコンテキストを指定します:
UIGraphicsPushContext(context);
[image drawAtPoint:CGPointZero]; // UIImage will handle all especial cases!
UIGraphicsPopContext();
また、2番目の答えであるCGContextTranslateCTM
またはCGContextScaleCTM
を使用してコンテキストを変更することも避けます。
デフォルトの座標系の反転
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);
UIImage
についてはわかりませんが、このような動作は通常、座標が反転したときに発生します。 OS X座標系のほとんどは、PostscriptやPDFのように、左下隅に原点があります。ただし、CGImage
座標系の原点は左上隅にあります。
可能なソリューションには、isFlipped
プロパティまたはscaleYBy:-1
アフィン変換が含まれる場合があります。
コンテキスト内のカスタム四角形に画像を描画するための簡単なソリューションに興味がある人は:
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)
}
画像は四角形に合わせて拡大縮小されます。
同じ問題を使用してこの問題を解決できます。
UIGraphicsBeginImageContext(image.size);
UIGraphicsPushContext(context);
[image drawInRect:CGRectMake(gestureEndPoint.x,gestureEndPoint.y,350,92)];
UIGraphicsPopContext();
UIGraphicsEndImageContext();
Swiftコードによる補足回答
Quartz 2Dグラフィックスは、左下にOriginがある座標系を使用し、iOSのUIKitは左上にOriginを持つ座標系を使用します。通常はすべて正常に機能しますが、グラフィック操作を実行するときは、自分で座標系を変更する必要があります。 ドキュメント 状態:
一部のテクノロジーは、Quartzで使用されているものとは異なるデフォルトの座標系を使用してグラフィックコンテキストを設定します。 Quartzに対して、このような座標系は修正された座標系であり、一部のQuartz描画操作を実行するときに補正する必要があります。最も一般的な修正された座標系では、Originがコンテキストの左上隅に配置され、y軸がページの下部を指すように変更されます。
この現象は、drawRect
メソッドで画像を描画するカスタムビューの次の2つのインスタンスで見ることができます。
左側では、画像は上下逆になっており、右側では、原点が左上になるように座標系が変換およびスケーリングされています。
上下逆さまの画像
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)
}
Swift 3.0および4.0
yourImage.draw(in: CGRect, blendMode: CGBlendMode, alpha: ImageOpacity)
変更は必要ありません
UIImage
には、その主なコンテンツメンバーとしてのCGImage
と、スケーリングおよび方向係数が含まれます。 CGImage
とそのさまざまな関数はOSXから派生しているため、iPhoneと比較して逆さまの座標系が必要です。 UIImage
を作成すると、デフォルトで逆さまの向きになります(これは変更できます!)。 .CGImage
プロパティを使用して非常に強力なCGImage
関数にアクセスしますが、iPhone画面などに描画するにはUIImage
メソッドを使用するのが最適です。
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()
}
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];
私のプロジェクトの過程で、私は Kendallの答え から Cliffの答え にジャンプして、携帯電話から読み込まれた画像のこの問題を解決しました。
最終的には、代わりに CGImageCreateWithPNGDataProvider
を使用しました:
NSString* imageFileName = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"clockdial.png"];
return CGImageCreateWithPNGDataProvider(CGDataProviderCreateWithFilename([imageFileName UTF8String]), NULL, YES, kCGRenderingIntentDefault);
これは、CGImage
からUIImage
を取得することで得られるオリエンテーションの問題に悩まされることはなく、ヒッチなしでCALayer
のコンテンツとして使用できます。
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()
}
}