CGContextDrawImage trekker bildet opp ned når bestått UIImage.CGImage

stemmer
172

Vet noen hvorfor CGContextDrawImageskulle tegne mitt bilde opp ned? Jeg legger i et bilde fra min søknad:

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

Og så bare spør core grafikk til å trekke den til min sammenheng:

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

Den gjengir på rett sted, og dimensjoner, men bildet er opp ned. Jeg må være mangler noe veldig opplagt her?

Publisert på 03/02/2009 klokken 10:23
kilden bruker
På andre språk...                            


18 svar

stemmer
221

I stedet for

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

Bruk

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

I midten av dine begin / end CGcontextmetoder.

Dette vil tegne bildet med riktig orientering i din nåværende bilde kontekst - Jeg er ganske sikker på at dette har noe å gjøre med UIImageå holde på kunnskap om orientering, mens CGContextDrawImagemetoden blir de underliggende rå bildedata med ingen forståelse av orientering.

Vær oppmerksom på at du vil støte på dette problemet i mange områder, men en konkret eksempel er å håndtere adressebok bruker bilder.

Svarte 06/02/2009 kl. 20:46
kilden bruker

stemmer
211

Selv etter bruk av alt jeg har nevnt, har jeg fortsatt hadde dramaer med bildene. Til slutt har jeg bare brukt Gimp til å lage en 'snudd vertikalt' versjon av alle mine bilder. Nå trenger jeg ikke å bruke noen Transforms. Forhåpentligvis vil dette ikke føre til ytterligere problemer nedover sporet.

Vet noen hvorfor CGContextDrawImage skulle tegne mitt bilde opp ned? Jeg legger i et bilde fra min søknad:

Quartz2d bruker et annet koordinatsystem, der opprinnelse er i nedre venstre hjørne. Da kvarts trekker piksel x [5], y [10] fra en 100 * 100 bilde, blir det bildeelement som er trukket i det nedre venstre hjørne i stedet for den øvre venstre. Dermed forårsaker 'snudd' image.

X koordinatsystem kamper, så du må snu y koordinater.

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

Dette betyr at vi har oversatt bildet av 0 enheter på x-aksen og av bildene høyde på y-aksen. Men dette alene vil bety vårt bilde er fortsatt opp ned, bare blir trukket "image.size.height" under der vi ønsker den skal trekkes.

Den Quartz2D programmeringsveiledning anbefaler bruk ScaleCTM og tilførsel av negative verdier for å vende bildet. Du kan bruke følgende kode for å gjøre dette -

CGContextScaleCTM(context, 1.0, -1.0);

Kombinere de to like før CGContextDrawImagesamtalen, og du bør ha bildet tegnet riktig.

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

Bare vær forsiktig hvis imageRect koordinatene ikke stemmer overens med i bildet, som du kan få utilsiktede resultater.

For å konvertere tilbake koordinatene:

CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0, -imageRect.size.height);
Svarte 04/02/2009 kl. 12:49
kilden bruker

stemmer
19

Det beste av to verdener, bruker UIImage oss drawAtPoint:eller drawInRect:samtidig angi egendefinerte konteksten:

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

Også du unngå Fjern sammenheng med CGContextTranslateCTMeller CGContextScaleCTMsom det andre svaret gjør.

Svarte 07/08/2013 kl. 05:47
kilden bruker

stemmer
10

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

Flipping Standard koordinatsystem

Vende i UIKit tegning modifiserer underlags CALayer å innrette en tegning miljø med en LLO koordinatsystem med standard koordinatsystem av UIKit. Hvis du bare bruker UIKit metoder og funksjon for tegning, bør du ikke trenger å snu CTM. Men hvis du blander core grafikk eller bilde I / O funksjonskall med UIKit samtaler, blar CTM kan være nødvendig.

Spesielt hvis du tegner et bilde eller PDF-dokument ved å ringe kjerners grafikkfunksjoner direkte, er objektet gjengis opp-ned i visningen kontekst. Du må snu CTM å vise bildet og sider korrekt.

Å vende en gjenstand trukket til en Core Graphics sammenheng slik at det vises riktig når de vises i en UIKit visning, må du endre CTM i to trinn. Du sette opprinnelsen til øvre venstre hjørnet av tegneområdet, og deretter bruke en skala oversettelse, modifisere y-koordinat med -1. Koden for å gjøre dette ligner på følgende:

CGContextSaveGState(graphicsContext);
CGContextTranslateCTM(graphicsContext, 0.0, imageHeight);
CGContextScaleCTM(graphicsContext, 1.0, -1.0);
CGContextDrawImage(graphicsContext, image, CGRectMake(0, 0, imageWidth, imageHeight));
CGContextRestoreGState(graphicsContext);
Svarte 08/04/2016 kl. 06:11
kilden bruker

stemmer
7

Jeg er ikke sikker på UIImage, men denne typen skjer vanligvis når koordinatene er snudd. De fleste av OS X koordinatsystemer har sin opprinnelse i nedre venstre hjørne, som i Postscript og PDF. Men CGImagekoordinatsystem har sin opprinnelse i øvre venstre hjørne.

Mulige løsninger kan innebære en isFlippedeiendom eller en scaleYBy:-1affine transformere.

Svarte 03/02/2009 kl. 10:34
kilden bruker

stemmer
6

Hvis noen er interessert i en no-brainer løsning for å tegne et bilde i en tilpasset rect i en sammenheng:

 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)

}

Bildet vil bli skalert til å fylle rect.

Svarte 18/02/2016 kl. 14:20
kilden bruker

stemmer
3

Swift 3.0 og 4.0

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

Ingen Endring nødvendig

Svarte 04/08/2017 kl. 10:41
kilden bruker

stemmer
3

Vi kan løse dette problemet ved hjelp av den samme funksjonen:

UIGraphicsBeginImageContext(image.size);

UIGraphicsPushContext(context);

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

UIGraphicsPopContext();

UIGraphicsEndImageContext();
Svarte 25/08/2011 kl. 06:27
kilden bruker

stemmer
3

UIImageinneholder CGImagesom sin hovedinnholdet medlem samt skaleringsfaktorer og orientering. Siden CGImageog dens ulike funksjoner er avledet fra OSX, forventer det et koordinatsystem som er opp ned i forhold til iPhone. Når du oppretter en UIImage, brukes til en opp-ned orientering for å kompensere (du kan endre dette!). Bruke . CGImageeiendom for å få tilgang til de svært kraftige CGImagefunksjoner, men tegning på iPhone-skjermen etc. gjøres best med UIImagemetoder.

Svarte 31/07/2010 kl. 23:26
kilden bruker

stemmer
2

Supplerende svar med Swift-kode

Quartz 2D-grafikk bruke et koordinatsystem med origo i bunnen venstre mens UIKit i iOS bruker et koordinatsystem med origo øverst til venstre. Alt fungerer som regel fint, men når du gjør noen grafiske operasjoner, må du endre koordinatsystemet selv. De dokumentasjon heter det:

Noen teknologier satt opp sine grafikk sammenhenger ved hjelp av en annen standard koordinatsystem enn den som brukes av kvarts. I forhold til kvarts, er et slikt koordinatsystem en modifisert koordinatsystem, og det må kompenseres for ved å utføre noen Kvarts tegneoperasjoner. Den vanligste modifiserte koordinatsystemet plasserer origo i øverste venstre hjørne av rammen og endrer y-aksen peke mot bunnen av siden.

Dette fenomen kan sees i de følgende to tilfeller av tilpassede visninger som trekker et bilde i deres drawRectmetoder.

skriv bildebeskrivelse her

På venstre side er bildet opp-ned og på høyre side koordinatsystemet er oversatt og skalert slik at origo er i øverst til venstre.

Opp-ned bilde

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)

}

Modifiserte koordinatsystem

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)
}
Svarte 22/12/2015 kl. 03:25
kilden bruker

stemmer
1

Swift 3 Coregraphics Solution

Hvis du ønsker å bruke CG for uansett grunn kan være, i stedet for UIImage, dette Swift tre konstruksjon basert på tidligere svar løste problemet for meg:

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()
}
Svarte 31/08/2017 kl. 20:05
kilden bruker

stemmer
1

drawInRecter absolutt veien å gå. Her er en annen liten ting som vil komme i veien nyttig når du gjør dette. Vanligvis bildet og rektangelet der det kommer til å gå ikke svarer. I så fall drawInRectvil strekke bildet. Her er en rask og kul måte å sørge for at bildet aspektet rasjon ikke er endret, ved å reversere transformasjon (som vil være å passe det hele i):

//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];
Svarte 22/02/2010 kl. 05:57
kilden bruker

stemmer
0

Det skjer fordi QuartzCore har koordinatsystemet "rste venstre", mens UIKit - "øverst til venstre".

I tilfelle kan du utvide 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)
Svarte 30/09/2019 kl. 11:28
kilden bruker

stemmer
0

Swift 5 svar basert på @ ZpaceZombor er utmerket svar

Hvis du har en UIImage, bare bruk

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

Hvis du har en CGImage bruke min kategori

Merk: I motsetning til enkelte andre svar, dette tar en hensyn til at rect du ønsker å trekke inn kan ha y = 0. De svar som ikke tar dette i betraktning er feil og vil ikke fungere i det generelle tilfellet!.

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)

    }
} 

Bruk som dette:

let imageFrame: CGRect = ...
let context: CGContext = ....
let img: CGImage = ..... 
context.drawImage(image: img, inRect: imageFrame)
Svarte 20/09/2019 kl. 10:24
kilden bruker

stemmer
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()
    }
}
Svarte 03/05/2018 kl. 12:06
kilden bruker

stemmer
0

I løpet av mitt prosjekt jeg hoppet fra Kendall svar til Cliff svar for å løse dette problemet for bilder som er lastet fra selve telefonen.

Til slutt endte jeg opp med å bruke CGImageCreateWithPNGDataProvideri stedet:

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

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

Dette betyr ikke lider av orienteringsproblemer som du ville fått fra å få CGImagefra en UIImage, og det kan brukes som innholdet i en CALayeruten vanskelighet.

Svarte 15/03/2014 kl. 16:13
kilden bruker

Svarte 26/11/2009 kl. 12:24
kilden bruker

stemmer
-5

Du kan også løse dette problemet ved å gjøre dette:

//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.
Svarte 04/04/2012 kl. 01:55
kilden bruker

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