CGContextDrawImage, UIImage.CGImage aktarıldığında görüntüyü baş aşağı çeker


179

Resmimi neden CGContextDrawImagebaş aşağı çekeceğini bilen var mı ? Uygulamamdan bir resim yüklüyorum:

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

Ve sonra temel grafiklerden onu bağlamıma çekmesini isteyin:

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

Doğru yerde ve boyutlarda oluşturuluyor, ancak görüntü baş aşağı. Burada gerçekten bariz bir şeyi mi kaçırmalıyım?

Yanıtlar:


238

Onun yerine

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

kullanım

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

Başlangıç ​​/ bitiş CGcontextyöntemlerinizin ortasında .

Bu, görüntüyü doğru yönlendirme ile mevcut görüntü içeriğinize çekecektir - yöntem UIImage, yönlendirme bilgisi CGContextDrawImageolmadan temel ham görüntü verilerini alırken, yönelim bilgisini tutmakla ilgili bir şey olduğuna eminim .


19
Bu çözüm, resminizde CGContextSetAlpha () gibi CG işlevlerini kullanmanıza izin vermezken 2. cevap da kullanmanıza izin vermez.
samvermette

2
İyi bir nokta, ancak çoğunlukla bir resim çizmek için özel alfa seviyelerine ihtiyacınız yoktur (genellikle alfa ihtiyacı olan şeyler için önceden görüntülere dönüştürülür). Temel olarak benim sloganım, olabildiğince küçük bir kod kullanmak çünkü daha fazla kod hata için daha fazla şans anlamına gelir.
Kendall Helmstetter Gelner

Merhaba, Başlangıç ​​/ bitiş CGContext yöntemlerinin ortasında ne demek istiyorsun, bana daha fazla örnek kod verebilir misin? Bağlamı bir yerde depolamaya çalışıyorum ve görüntüyü çizmek için bağlamı kullanıyorum
vodkhang

2
Rusty için çalışabilirken, - [UIImage drawInRect:], iş parçacığı için güvenli olmadığı sorununa sahiptir. Benim özel uygulama için, bu yüzden ilk etapta CGContextDrawImage () kullanıyorum.
Olie

2
Açıklığa kavuşturmak gerekirse: Core Graphics, iOS ile karşılaştırıldığında koordinat sisteminin baş aşağı olduğu OS X üzerine kurulmuştur (y OS X'in sol alt köşesinden başlayarak). Bu yüzden Core Graphics görüntüyü baş aşağı
çekerken

212

Bahsettiğim her şeyi uyguladıktan sonra bile, hala görüntülerle dramlarım oldu. Sonunda, tüm görüntülerimin 'çevrilmiş dikey' bir versiyonunu oluşturmak için Gimp'i kullandım. Şimdi herhangi bir Dönüşüm kullanmama gerek yok. Umarım bu pistte başka sorunlara neden olmaz.

CGContextDrawImage resmimi neden baş aşağı çekeceğini bilen var mı? Uygulamamdan bir resim yüklüyorum:

Quartz2d, ​​kaynağın sol alt köşede olduğu farklı bir koordinat sistemi kullanır. Kuvars 100 * 100 görüntünün piksel x [5], y [10] 'u çizdiğinde, bu piksel sol üst köşeye değil sol alt köşeye çizilir. Böylece 'çevrilmiş' görüntüye neden olur.

X koordinatı sistemi eşleşir, bu nedenle y koordinatlarını çevirmeniz gerekir.

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

Bu, görüntüyü x ekseninde 0 birim ve y eksenindeki görüntü yüksekliği ile çevirdiğimiz anlamına gelir. Bununla birlikte, bu tek başına imajımızın hala baş aşağı olduğu, sadece çizilmesini istediğimiz yere "image.size.height" çizildiği anlamına gelecektir.

Quartz2D programlama kılavuzu, ScaleCTM kullanılmasını ve görüntüyü döndürmek için negatif değerler aktarılmasını önerir. Bunu yapmak için aşağıdaki kodu kullanabilirsiniz -

CGContextScaleCTM(context, 1.0, -1.0);

CGContextDrawImageAramanızdan hemen önce ikisini birleştirin ve görüntüyü doğru şekilde çizmelisiniz.

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);

İstenmeyen sonuçlar alabileceğiniz için imageRect koordinatlarınız görüntünüzdekilerle eşleşmiyorsa dikkatli olun.

Koordinatları geri dönüştürmek için:

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

Bu iyi ama ben 0,0 ve 1.0,1,0 geri ayarlamaya çalıştığımda bile diğer şeyler tercüme ve ölçek etkilendiklerini buldum sonunda Kendall'ın çözümü ile gitti ama bunun yerine UIImage kullanan fark Alt seviye
CGİnsan

3
DrawLayer kullanıyorsanız ve bağlama bir CGImageRef çizmek istiyorsanız, buradaki teknik sadece doktorun sipariş etmesini ister! Görüntü için rekt varsa, CGContextTranslateCTM (bağlam, 0, image.size.height + image.origin.y) varsa, CGContextDrawImage () 'dan önce rect.origin.y değerini 0 olarak ayarlayın. Teşekkürler!
David H

İPhone kamerasının yanlış yönelimle sonuçlandığı ImageMagick ile bir kez sorun yaşadım. Görünüşe göre görüntü dosyasındaki yönlendirme bayrağını yapıyor, bu yüzden portre görüntüleri, örneğin "sol" olarak ayarlanmış 960px x 640px idi - sol tarafta üstte olduğu gibi (veya buna yakın bir şey). Bu konu size yardımcı olabilir: stackoverflow.com/questions/1260249/…
Hari Karam Singh

8
Neden kullanmayın CGContextSaveGState()ve CGContextRestoreGState()kaydetmek ve dönüşüm matrisi geri?
Ja͢ck

20

Her iki dünyanın drawAtPoint:da en iyisi, UIImage'ları kullanın veya drawInRect:özel bağlamınızı belirlerken:

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

Ayrıca, bağlamınızı değiştirmekten CGContextTranslateCTMveya CGContextScaleCTMikinci cevabın yapmaktan kaçınabilirsiniz .


Bu harika bir fikir, ama benim için hem baş aşağı hem de soldan sağa bir görüntü ile sonuçlandı. Sonuçların görüntüden görüntüye değişeceğini tahmin ediyorum.
Luke Rogers

Belki bu daha sonraki iOS sürümleriyle çalışmayı durdurdu? O zaman benim için her şeyle çalıştı.
Rivera

Aslında bağlamı nasıl oluşturduğumu düşünüyordum. Yeni bir şey başlatmak UIGraphicsBeginImageContextWithOptionsyerine kullanmaya başladıktan sonra işe yaramış CGContextgibi görünüyordu.
Luke Rogers

12

İlgili Quartz2D belgeleri: https://developer.apple.com/library/ios/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/GraphicsDrawingOverview/GraphicsDrawingOverview.html#//apple_ref/doc/uid/TP40010156-CH14-W14SW

Varsayılan Koordinat Sisteminin Çevrilmesi

UIKit çiziminde çevirme, LLO koordinat sistemine sahip bir çizim ortamını varsayılan UIKit koordinat sistemiyle hizalamak için destek CALayer'ını değiştirir. Çizim için yalnızca UIKit yöntemlerini ve işlevini kullanırsanız, CTM'yi çevirmeniz gerekmez. Ancak, Core Graphics veya Image I / O işlev çağrılarını UIKit çağrılarıyla karıştırırsanız, CTM'yi ters çevirmek gerekebilir.

Özellikle, Temel Grafik işlevlerini doğrudan çağırarak bir görüntü veya PDF belgesi çizerseniz, nesne görünüm bağlamında baş aşağı oluşturulur. Resmi ve sayfaları doğru görüntülemek için CTM'yi çevirmeniz gerekir.

Çekirdek Grafikler bağlamına çizilen bir nesneyi UIKit görünümünde görüntülendiğinde düzgün görünmesi için CTM'yi iki adımda değiştirmeniz gerekir. Başlangıç ​​noktasını çizim alanının sol üst köşesine çevirirsiniz ve ardından ölçeklendirmeyi çevirir ve y koordinatını -1 değiştirirsiniz. Bunu yapmak için kod aşağıdakine benzer:

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

10

Herkes bir bağlamda özel bir rektöre görüntü çizmek için beyinsiz bir çözümle ilgileniyorsa:

 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)

}

Görüntü rektifi dolduracak şekilde ölçeklendirilir.


7

Emin değilim UIImage, ancak bu tür davranışlar genellikle koordinatlar çevrildiğinde ortaya çıkar. OS X koordinat sistemlerinin çoğunun kökenleri, Postscript ve PDF'de olduğu gibi sol alt köşede bulunur. Ancak CGImagekoordinat sisteminin kökeni sol üst köşede bulunur.

Olası çözümler bir isFlippedözellik veya scaleYBy:-1afin dönüşümü içerebilir .


3

UIImageihtiva eden bir CGImageana içerik üyesi olarak ölçekleme ve yönlendirme faktörler gibi. Bu yana CGImageçeşitli fonksiyonları OS X elde edilir ve bu bir ters iPhone karşılaştırılır koordinat sistemi bekler. Bir oluşturduğunuzda UIImage, telafi etmek için varsayılan olarak baş aşağı bir yöne ayarlanır (bunu değiştirebilirsiniz!). Kullan . CGImageözelliği çok güçlü CGImagefonksiyonlara erişmek için , ancak iPhone ekran vb çizim üzerine en iyi UIImageyöntemleri ile yapılır .


1
UIImage'ın varsayılan baş aşağı yönünü nasıl değiştirirsiniz?
MegaManX

3

Swift kodu ile tamamlayıcı cevap

Quartz 2B grafikler, orijin sol altta bir koordinat sistemi kullanırken iOS'ta UIKit, orijin sol üstte bir koordinat sistemi kullanır. Her şey genellikle iyi çalışır, ancak bazı grafik işlemleri yaparken, koordinat sistemini kendiniz değiştirmeniz gerekir. Dokümantasyon durumları:

Bazı teknolojiler, grafik bağlamlarını Quartz tarafından kullanılandan farklı bir varsayılan koordinat sistemi kullanarak kurarlar. Kuvars'a göre, böyle bir koordinat sistemi değiştirilmiş bir koordinat sistemidir ve bazı Kuvars çizim işlemlerini gerçekleştirirken telafi edilmelidir. En yaygın değiştirilmiş koordinat sistemi başlangıç ​​noktasını içeriğin sol üst köşesine yerleştirir ve y eksenini sayfanın altına doğru olacak şekilde değiştirir.

Bu fenomen, drawRectyöntemlerinde bir resim çizen aşağıdaki iki özel görünüm örneğinde görülebilir .

resim açıklamasını buraya girin

Sol tarafta görüntü baş aşağı ve sağ tarafta başlangıç ​​noktası sol üstte olacak şekilde koordinat sistemi çevrildi ve ölçeklendi.

Baş aşağı görüntü

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)

}

Değiştirilmiş koordinat sistemi

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

Swift 3.0 ve 4.0

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

Değişiklik gerekmez


2

Bu sorunu aynı işlevi kullanarak çözebiliriz:

UIGraphicsBeginImageContext(image.size);

UIGraphicsPushContext(context);

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

UIGraphicsPopContext();

UIGraphicsEndImageContext();

1

drawInRectkesinlikle gidilecek yoldur. İşte bunu yaparken faydalı olacak başka bir küçük şey. Genellikle içine gireceği resim ve dikdörtgen uymuyor. Bu durumda drawInRectresmi uzatır. İşte dönüşümü tersine çevirerek (her şeye sığacak) resmin en boy oranının değişmediğinden emin olmanın hızlı ve havalı bir yolu:

//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

Swift 3 CoreGraphics Çözümü

CG'yi nedeniniz ne olursa olsun kullanmak istiyorsanız, UIImage yerine, önceki cevaplara dayanan bu Swift 3 yapısı sorunu benim için çözdü:

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

Bunun nedeni, QuartzCore'un "sol-alt" koordinat sistemine sahip olması, UIKit ise - "üst-sol".

Bu durumda CGContext'i genişletebilirsiniz :

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

Görüntü rektiflerinde sıfır olmayan orijinleri doğru şekilde işleyen bu Swift 5 , saf Çekirdek Grafik uzantısını kullanıyorum:

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()
    }
}

Tam olarak CGContextnormal draw(: in:)yöntem gibi kullanabilirsiniz :

ctx.drawFlipped(myImage, in: myRect)

0

Projemin boyunca buradaki atlayan Kendall cevap için Cliff'in cevap telefondan kendisinden yüklenen görüntüler için bu sorunu çözmek için.

Sonunda CGImageCreateWithPNGDataProviderbunun yerine kullanarak sona erdi :

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

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

Bu girmesini alacağı oryantasyon sorunları muzdarip değil CGImagebir den UIImageve bir içeriklerinden olarak kullanılabilecek CALayerbir aksamadan.


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

Swift 5 cevap@ ZpaceZombor'un mükemmel cevabına dayanan cevabı

Bir UIImage'ınız varsa,

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

CGImage'ınız varsa aşağıdaki kategorimi kullanın

Not: Diğer bazı cevapların aksine, bu, çizmek istediğiniz rektörün y! = 0 olabileceğini dikkate alır. Bunu dikkate almayan cevaplar yanlıştır ve genel durumda çalışmaz.

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)

    }
} 

Bunun gibi kullanın:

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

Bu neredeyse mükemmel bir çözümdür, ancak yöntem içindeki çizim (uIImage, inRect rect: CGRect) ve uiImage.cgimage'i işlemek için işlevi değiştirmeyi düşünün
Mars

-5

Bunu yaparak da bu sorunu çözebilirsiniz:

//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.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.