CGContextDrawImage disegna un'immagine capovolta quando passa UIImage.CGImage
-
21-08-2019 - |
Domanda
Qualcuno sa perché CGContextDrawImage
sarebbe attirato 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?
Soluzione
Al posto di
CGContextDrawImage(context, CGRectMake(0, 0, 145, 15), image.CGImage);
Usa
[image drawInRect:CGRectMake(0, 0, 145, 15)];
Nel mezzo dei vostri inizio / fine metodi CGcontext
.
In questo modo attirare l'immagine con l'orientamento corretto nel vostro contesto immagine corrente - Sono abbastanza sicuro che questo ha qualcosa a che fare con la UIImage
trattenendo la conoscenza dell'orientamento mentre il metodo CGContextDrawImage
ottiene l'immagine raw sottostante i dati 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.
Altri suggerimenti
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 attirato la mia immagine sottosopra? Sto caricando un immagine dalla mia applicazione:
Quartz2d utilizza un diverso sistema di coordinate, dove l'origine è nell'angolo in basso a sinistra. 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. causando in tal modo l'immagine 'capovolto'.
Il sistema coordinata x corrisponde, quindi è necessario capovolgere le coordinate y.
CGContextTranslateCTM(context, 0, image.size.height);
Questo significa abbiamo tradotto l'immagine da 0 unità sull'asse x e dall'altezza immagini sull'asse y. Tuttavia, questo da solo significa che 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);
Unire i due poco prima CGContextDrawImage
chiamata 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);
Il meglio di entrambi i mondi, utilizzare UIImage del 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 CGContextTranslateCTM
o CGContextScaleCTM
che la seconda risposta fa.
rilevanti docs Quartz2D: 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
Ribaltamento 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 che la funzione O chiamate / 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);
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 CGImage
sistema di coordinate ha la sua origine in alto a sinistra.
Possibili soluzioni possono comportare un isFlipped
proprietà o di un scaleYBy:-1
trasformazione affine.
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 rect.
Possiamo risolvere questo problema utilizzando la stessa funzione:
UIGraphicsBeginImageContext(image.size);
UIGraphicsPushContext(context);
[image drawInRect:CGRectMake(gestureEndPoint.x,gestureEndPoint.y,350,92)];
UIGraphicsPopContext();
UIGraphicsEndImageContext();
UIImage
contiene un CGImage
come principale componente contenuto nonché scaling e fattori di orientamento. Poiché <=> e le sue varie funzioni sono derivate da OSX, si prevede un sistema che è capovolto rispetto al iPhone coordinata. Quando si crea un <=>, il valore predefinito è un orientamento capovolto per compensare (si può cambiare questo!). Utilizzare il. <=> Proprietà per accedere alle funzioni molto potenti <=>, ma disegnare sullo schermo di iPhone ecc è meglio farlo con i metodi <=>.
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. Il documentazione stati:
Alcune tecnologie definire i propri contesti grafiche che utilizzano un diverso sistema di coordinate predefinito di quella usata da quarzo. Relativo a Quarzo, un tale sistema di coordinate è un modificato sistema di coordinate e deve essere compensato durante l'esecuzione di un certo disegno quarzo operazioni. Il più comune modified sistema di coordinate pone il origine nell'angolo superiore sinistro del contesto e cambia l'asse y per puntare verso il fondo della pagina.
Questo fenomeno può essere visto nei seguenti due istanze di visualizzazioni personalizzate che disegnano un'immagine nei loro metodi drawRect
.
Sul lato sinistro, l'immagine è capovolta e sul lato destro del sistema di coordinate è stato tradotto in scala e in modo che l'origine è in alto a sinistra.
immagine Upside-down
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)
}
Swift 3.0 e 4.0
yourImage.draw(in: CGRect, blendMode: CGBlendMode, alpha: ImageOpacity)
Nessuna alterazione necessaria
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 <=> allungherà 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];
Swift 3 CoreGraphics Soluzione
Se si desidera utilizzare CG per qualunque possa essere la ragione, invece di UIImage, questo Swift 3 costruzione in base alle 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()
}
Nel corso del mio progetto sono saltato dalla risposta di Kendall per di Cliff risposta per risolvere questo problema per le immagini che vengono caricati dal telefono stesso.
Alla fine ho finito per usare CGImageCreateWithPNGDataProvider
invece:
NSString* imageFileName = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"clockdial.png"];
return CGImageCreateWithPNGDataProvider(CGDataProviderCreateWithFilename([imageFileName UTF8String]), NULL, YES, kCGRenderingIntentDefault);
Questa non soffre di problemi di orientamento che si otterrebbe da ottenere il CGImage
da un UIImage
e può essere utilizzato come il contenuto di un CALayer
senza intoppi.
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()
}
}
È 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.