CGContextDrawImage disegna un'immagine capovolta quando passa UIImage.CGImage

voti
169

Qualcuno sa perché CGContextDrawImagesarebbe disegnare la mia immagine a testa in giù? Sto caricando un'immagine in dalla mia applicazione:

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

E poi semplicemente chiedendo core grafico per disegnare al mio contesto:

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

Rende nel posto giusto, e le dimensioni, ma l'immagine è capovolta. Devo mancare qualcosa di davvero evidente qui?

È pubblicato 03/02/2009 alle 11:23
fonte dall'utente
In altre lingue...                            


18 risposte

voti
218

Invece di

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

Uso

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

Nel mezzo dei vostri inizio / termine CGcontextmetodi.

Questo attirerà l'immagine con l'orientamento corretto nel vostro contesto immagine corrente - Sono abbastanza sicuro che questo ha qualcosa a che fare con la UIImagedetenzione sulla conoscenza del l'orientamento, mentre il CGContextDrawImagemetodo ottiene i dati di immagine grezzi sottostanti senza alcuna comprensione di orientamento.

Si noti che si incorrerà in questo problema in molti settori, ma un esempio specifico a che fare con le immagini della rubrica degli utenti.

Risposto il 06/02/2009 a 21:46
fonte dall'utente

voti
211

Anche dopo l'applicazione di tutto quello che ho detto, ho ancora avuto drammi con le immagini. Alla fine, ho appena usato Gimp per creare una versione 'capovolto verticale' di tutte le mie immagini. Ora non ho bisogno di utilizzare tutte le trasformazioni. Speriamo che questo non causerà ulteriori problemi lungo la pista.

Qualcuno sa perché CGContextDrawImage sarebbe disegnando la mia immagine a testa in giù? Sto caricando un'immagine in dalla mia applicazione:

Quartz2d utilizza un diverso sistema di coordinate, dove l'origine è nell'angolo inferiore sinistro. Così, quando quarzo disegna pixel x [5], y [10] di un'immagine a 100 * 100, tale pixel viene disegnato nell'angolo inferiore sinistro anziché in alto a sinistra. provocando così l'immagine 'ribaltata'.

Il sistema di x coordinare corrisponde, quindi è necessario capovolgere le coordinate y.

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

Ciò significa che abbiamo tradotto l'immagine da 0 unità sull'asse x e dall'altezza immagini sull'asse y. Tuttavia, questo da solo significa la nostra immagine è ancora a testa in giù, solo in fase di elaborazione "image.size.height" sotto dove vogliamo che da trarre.

La guida di programmazione Quartz2D consiglia di utilizzare ScaleCTM e passando valori negativi per capovolgere l'immagine. È possibile utilizzare il seguente codice per fare questo -

CGContextScaleCTM(context, 1.0, -1.0);

Combinare i due poco prima CGContextDrawImagechiamata e si dovrebbe avere l'immagine disegnata in modo corretto.

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

Basta essere attenti se il vostro imageRect coordinate non corrispondono a quelle dell'immagine, come si può ottenere risultati imprevisti.

Per riconvertire le coordinate:

CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0, -imageRect.size.height);
Risposto il 04/02/2009 a 13:49
fonte dall'utente

voti
19

Il meglio di entrambi i mondi, utilizzare UIImage di drawAtPoint:o drawInRect:, pur specificando il contesto personalizzato:

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

Inoltre si evita di modificare il contesto con CGContextTranslateCTMo CGContextScaleCTMche la seconda risposta fa.

Risposto il 07/08/2013 a 06:47
fonte dall'utente

voti
8

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

Sfogliando il sistema di coordinate predefinito

Lanciando nel disegno UIKit modifica il CALayer supporto per allineare un ambiente di disegno avente sistema con il difetto coordinate LLO sistema di coordinate UIKit. Se si utilizza solo metodi e funzioni UIKit per il disegno, non dovrebbe essere necessario capovolgere il marchio comunitario. Tuttavia, se si mescolano core grafico o l'immagine funzione di I / O chiamate con chiamate UIKit, lanciando il marchio comunitario potrebbe essere necessario.

In particolare, se si disegna un'immagine o un documento PDF chiamando direttamente le funzioni di core grafico, l'oggetto è reso a testa in giù nel contesto della vista. È necessario capovolgere il marchio comunitario per visualizzare l'immagine e le pagine in modo corretto.

Per riflettere un oggetto disegnato per un contesto core grafico in modo che appaia correttamente quando visualizzato in una vista UIKit, è necessario modificare il marchio comunitario in due fasi. Tradurre l'origine all'angolo superiore sinistro dell'area di disegno, e quindi si applica traduzione scala, modificando la coordinata y per -1. Il codice per fare questo è simile al seguente:

CGContextSaveGState(graphicsContext);
CGContextTranslateCTM(graphicsContext, 0.0, imageHeight);
CGContextScaleCTM(graphicsContext, 1.0, -1.0);
CGContextDrawImage(graphicsContext, image, CGRectMake(0, 0, imageWidth, imageHeight));
CGContextRestoreGState(graphicsContext);
Risposto il 08/04/2016 a 06:11
fonte dall'utente

voti
7

Non sono sicuro per UIImage, ma questo tipo di comportamento si verifica in genere quando le coordinate sono capovolte. La maggior parte di OS X sistemi di coordinate hanno la loro origine in basso a sinistra, come in PostScript e PDF. Ma CGImagesistema di coordinate ha la sua origine in alto a sinistra.

Possibili soluzioni possono coinvolgere una isFlippedproprietà o una scaleYBy:-1trasformazione affine.

Risposto il 03/02/2009 a 11:34
fonte dall'utente

voti
6

Se qualcuno è interessato a una soluzione non-sforzo per disegnare un'immagine in un rettangolo personalizzato in un contesto:

 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)

}

L'immagine verrà ridimensionata per riempire il rettangolo.

Risposto il 18/02/2016 a 14:20
fonte dall'utente

voti
3

Swift 3.0 & 4.0

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

Nessuna alterazione necessaria

Risposto il 04/08/2017 a 10:41
fonte dall'utente

voti
3

Siamo in grado di risolvere questo problema utilizzando la stessa funzione:

UIGraphicsBeginImageContext(image.size);

UIGraphicsPushContext(context);

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

UIGraphicsPopContext();

UIGraphicsEndImageContext();
Risposto il 25/08/2011 a 07:27
fonte dall'utente

voti
3

UIImagecontiene una CGImagecome membro contenuto principale così come fattori di scala e orientamento. Poiché CGImagee le sue varie funzioni sono derivate da OSX, si prevede un sistema che è capovolto rispetto al iPhone coordinata. Quando si crea una UIImage, il valore predefinito è un orientamento capovolto per compensare (si può cambiare questo!). Usa il . CGImageProprietà per accedere alle potenti CGImagefunzioni, ma il disegno sullo schermo di iPhone, ecc è meglio farlo con i UIImagemetodi.

Risposto il 01/08/2010 a 00:26
fonte dall'utente

voti
2

risposta supplementare con il codice Swift

Grafico Quartz 2D utilizzano un sistema di coordinate con l'origine in basso a sinistra, mentre UIKit in IOS utilizza un sistema di coordinate con l'origine in alto a sinistra. Tutto di solito funziona bene, ma quando facendo alcune operazioni grafiche, è necessario modificare il sistema da soli coordinate. La documentazione afferma:

Alcune tecnologie impostare le loro contesti grafici utilizzando il sistema rispetto a quello utilizzato da Quartz coordinate predefinito diverso. Rispetto al quarzo, un tale sistema di coordinate è un sistema di coordinate modificato e deve essere compensato durante l'esecuzione di alcune operazioni di disegno quarzo. Il più comune modificato sistema di coordinate posiziona l'origine nell'angolo superiore sinistro del contesto e cambia l'asse y per puntare verso la parte inferiore della pagina.

Questo fenomeno può essere visto nei seguenti due istanze di visualizzazioni personalizzate che disegnano un'immagine nei loro drawRectmetodi.

entrare descrizione dell'immagine qui

Sul lato sinistro, l'immagine è capovolta e sul lato destro del sistema di coordinate è stata tradotta in scala e in modo che l'origine è in alto a sinistra.

immagine capovolta

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)

}

Modified sistema di coordinate

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)
}
Risposto il 22/12/2015 a 03:25
fonte dall'utente

voti
1

drawInRectè sicuramente la strada da percorrere. Ecco un'altra piccola cosa che verrà in modo utile quando si fa questo. Di solito l'immagine e il rettangolo in cui sta per andare non sono conformi. In tal caso, drawInRectsi estenderà l'immagine. Ecco un modo veloce e fresco per assicurarsi che razione aspetto del quadro non è cambiato, invertendo la trasformazione (che sarà per adattare il tutto in):

//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];
Risposto il 22/02/2010 a 06:57
fonte dall'utente

voti
0

Succede perché QuartzCore ha il sistema di coordinate "bottom-sinistra", mentre UIKit - "top-sinistra".

Nel caso è possibile estendere 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)
Risposto il 30/09/2019 a 11:28
fonte dall'utente

voti
0

Swift 5 risposta sulla base di @ ZpaceZombor è risposta eccellente

Se si dispone di un UIImage, basta usare

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

Se si dispone di un CGImage usare la mia categoria di seguito

Nota: A differenza di altre risposte, questo prende in considerazione che il rettangolo che si desidera disegnare in potrebbe avere y = 0. Quelle risposte che non tenerne conto non sono corretti e non funzionerà nel caso generale!.

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)

    }
} 

Utilizzare in questo modo:

let imageFrame: CGRect = ...
let context: CGContext = ....
let img: CGImage = ..... 
context.drawImage(image: img, inRect: imageFrame)
Risposto il 20/09/2019 a 10:24
fonte dall'utente

voti
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()
    }
}
Risposto il 03/05/2018 a 12:06
fonte dall'utente

voti
0

Swift 3 CoreGraphics Solution

Se si desidera utilizzare CG per qualunque possa essere la ragione, invece di UIImage, questa costruzione Swift 3 sulla base delle risposte precedenti ha risolto il problema per me:

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()
}
Risposto il 31/08/2017 a 20:05
fonte dall'utente

voti
0

Durante il corso del mio progetto sono saltato dalla risposta di Kendall per la risposta di Cliff per risolvere questo problema per le immagini che vengono caricati dal telefono stesso.

Alla fine ho finito per usare CGImageCreateWithPNGDataProviderinvece:

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

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

Questo non soffre di problemi di orientamento che si otterrebbe da ottenere la CGImageda una UIImagee può essere utilizzato come il contenuto di un CALayersenza intoppi.

Risposto il 15/03/2014 a 17:13
fonte dall'utente

Risposto il 26/11/2009 a 13:24
fonte dall'utente

voti
-5

È inoltre possibile risolvere questo problema in questo modo:

//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.
Risposto il 04/04/2012 a 02:55
fonte dall'utente

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more