CGContextDrawImage trekt beeld ondersteboven toen doorgegeven UIImage.CGImage

stemmen
169

Weet iemand waarom CGContextDrawImagezou mijn beeld ondersteboven te tekenen? Ik ben het laden van een afbeelding uit mijn toepassing:

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

En dan gewoon vragen core graphics te vestigen op mijn context:

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

Het maakt de juiste plaats en afmetingen, maar het beeld is omgekeerd. Ik moet missen iets heel voor de hand hier?

De vraag is gesteld op 03/02/2009 om 11:23
bron van user
In andere talen...                            


18 antwoorden

stemmen
218

In plaats van

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

Gebruik

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

In het midden van het begin / einde CGcontextmethoden.

Hierdoor wordt het beeld met de juiste oriëntatie te trekken in uw huidige afbeelding context - ik ben er vrij zeker van dat dit iets te maken heeft met het UIImagevasthouden aan kennis van de oriëntatie, terwijl de CGContextDrawImagemethode krijgt de onderliggende afbeelding ruwe data zonder begrip van oriëntatie.

Merk op dat u zult tegenkomen dit probleem in veel gebieden, maar een specifiek voorbeeld is het omgaan met adresboek gebruiker beelden.

antwoordde op 06/02/2009 om 21:46
bron van user

stemmen
211

Zelfs na het aanbrengen van alles wat ik heb gezegd, heb ik nog had drama's met de beelden. Op het einde, ik heb net gebruikt Gimp om een ​​'omgedraaid verticale' versie van al mijn beelden te creëren. Nu heb ik geen behoefte aan een Transformaties gebruiken. Hopelijk zal dit niet verder problemen op het spoor veroorzaken.

Weet iemand waarom CGContextDrawImage mijn beeld ondersteboven zou tekenen? Ik ben het laden van een afbeelding uit mijn toepassing:

Quartz2d gebruikt een andere coördinatenstelsel, waarbij de oorsprong van de linker benedenhoek. Toen Quartz trekt beeldpunt x [5], y [10] van het 100 * 100, is dat beeldpunt in de linkerbenedenhoek plaats van linksboven opgesteld. Waardoor voor de 'omgedraaid' imago.

De x-coördinaat systeem past, dus je moet aan de y-coördinaten te spiegelen.

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

Dit betekent dat wij het beeld vertaald door 0 eenheden op de x-as en de afbeeldingen hoogte op de y-as. Echter, dit alleen zal betekenen ons imago is nog steeds ondersteboven, gewoon "image.size.height" hieronder waar we willen dat het getrokken te worden getrokken.

De Quartz2D programmagids raadt aan ScaleCTM en doorgeven negatieve waarden naar afbeelding spiegelen. U kunt de volgende code gebruiken om dit te doen -

CGContextScaleCTM(context, 1.0, -1.0);

Combineer de twee vlak voor je CGContextDrawImagebellen en je moet hebben het beeld correct opgesteld.

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

Wees voorzichtig als je imageRect coördinaten niet overeenkomen met die van uw afbeelding, zoals u onbedoelde resultaten kunnen krijgen.

Om terug te zetten de coördinaten:

CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0, -imageRect.size.height);
antwoordde op 04/02/2009 om 13:49
bron van user

stemmen
19

Het beste van twee werelden, gebruiken UIImage's drawAtPoint:of drawInRect:terwijl nog steeds met vermelding van uw aangepaste context:

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

Ook u voorkomen dat het aanpassen van uw verband met CGContextTranslateCTMof CGContextScaleCTMdie het tweede antwoord doet.

antwoordde op 07/08/2013 om 06:47
bron van user

stemmen
8

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

Flipping de Default Coordinate System

Flipping in UIKit tekening wijzigt de onderkant CALayer een tekenomgeving heeft LLO coördinatensysteem met de standaard coördinatensysteem van UIKit lijnen. Als je alleen UIKit methoden en functie te gebruiken om te tekenen, moet je niet nodig om de CTM spiegelen. Echter, als je mix Core Graphics of Image I / O-functie gesprekken met UIKit gesprekken, het wegknippen van de CTM kan het nodig zijn.

In het bijzonder, als je een afbeelding of PDF-document tekenen door te bellen naar Core Graphics functies, het wordt gerenderd ondersteboven in context van de weergave. U moet de CTM spiegelen aan het beeld en pagina's correct weergegeven.

Om een ​​object getrokken door een Core Graphics context, zodat het correct wordt weergegeven wanneer deze wordt weergegeven in een UIKit uitzicht, moet u de CTM in twee stappen te wijzigen omdraaien. Je vertaalt de oorsprong naar de linkerbovenhoek van de tekening gebied, en vervolgens een schaal vertaling past u, het wijzigen van de y-coördinaat met -1. De code om dit te doen ziet er ongeveer als volgt:

CGContextSaveGState(graphicsContext);
CGContextTranslateCTM(graphicsContext, 0.0, imageHeight);
CGContextScaleCTM(graphicsContext, 1.0, -1.0);
CGContextDrawImage(graphicsContext, image, CGRectMake(0, 0, imageWidth, imageHeight));
CGContextRestoreGState(graphicsContext);
antwoordde op 08/04/2016 om 06:11
bron van user

stemmen
7

Ik ben niet zeker van UIImage, maar dit soort gedrag gebeurt meestal wanneer coördinaten worden omgedraaid. Het grootste deel van OS X-coördinaat systemen hebben hun oorsprong in de linker benedenhoek, net als in Postscript en PDF. Maar CGImagecoördinatensysteem heeft zijn oorsprong in de linkerbovenhoek.

Mogelijke oplossingen kan een te betrekken isFlippedpand of een scaleYBy:-1verwante transformatie.

antwoordde op 03/02/2009 om 11:34
bron van user

stemmen
6

Als iemand geïnteresseerd is in een no-brainer oplossing voor het tekenen van een beeld in een aangepaste rect in een context:

 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)

}

Het beeld wordt geschaald naar de rect te vullen.

antwoordde op 18/02/2016 om 14:20
bron van user

stemmen
3

Swift 3.0 & 4.0

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

Geen wijziging nodig

antwoordde op 04/08/2017 om 10:41
bron van user

stemmen
3

We kunnen dit probleem op te lossen met behulp van dezelfde functie:

UIGraphicsBeginImageContext(image.size);

UIGraphicsPushContext(context);

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

UIGraphicsPopContext();

UIGraphicsEndImageContext();
antwoordde op 25/08/2011 om 07:27
bron van user

stemmen
3

UIImagebevat CGImageals hoofdinhoud lid en schaal en oriëntatie factoren. Sinds CGImageen de verschillende functies zijn afgeleid van OSX, verwacht een coördinatensysteem dat omgekeerd is in vergelijking met de iPhone. Wanneer u een te creëren UIImage, wordt standaard een omgekeerde oriëntatie te compenseren (u kunt dit veranderen!). Gebruik de . CGImageeigenschap voor toegang tot de zeer krachtige CGImagefuncties, maar het tekenen op het iPhone-scherm enz. kan het beste gebeuren met de UIImagemethoden.

antwoordde op 01/08/2010 om 00:26
bron van user

stemmen
2

Aanvullend antwoord met Swift code

Quartz 2D grafieken gebruiken een coördinatensysteem met de oorsprong linksonder terwijl UIKit in IOS gebruikt een coördinatenstelsel met oorsprong in de linkerbovenhoek. Alles werkt meestal prima, maar bij het doen van sommige grafische bewerkingen, moet je wijzigen van de coördinatensysteem jezelf. De documentatie luidt als volgt:

Sommige technologieën opzetten van hun graphics context met behulp van een andere standaard assenstelsel dan degene die gebruikt wordt door Quartz. Ten opzichte van Quartz dergelijk coördinatenstelsel is een gewijzigde coördinatensysteem en moet worden gecompenseerd bij het uitvoeren van bepaalde bewerkingen Quartz tekening. De meest voorkomende gemodificeerde coördinatensysteem plaatst de oorsprong in de linkerbovenhoek van het kader en verandert de y-as te wijzen naar de onderkant van de pagina.

Dit verschijnsel is te zien in de volgende twee gevallen aangepaste weergaven die een beeld trekt in hun drawRectwerkwijzen.

voer image beschrijving hier

Aan de linkerkant, het beeld is omgekeerd en aan de rechterzijde het coördinatensysteem is vertaald en geschaald zodat de oorsprong in de linkerbovenhoek.

image 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 coördinatensysteem

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)
}
antwoordde op 22/12/2015 om 03:25
bron van user

stemmen
1

drawInRectis zeker de weg te gaan. Hier is een ander klein ding dat in de weg handig zal komen wanneer je dit doet. Meestal zijn de foto en de rechthoek waarin het gaat om te gaan niet voldoen. In dat geval drawInRectwordt het beeld uitstrekken. Hier is een snelle en leuke manier om ervoor te zorgen dat de foto's aspect ratio niet wordt veranderd, door de transformatie (die zal zijn om de hele zaak te passen in) omkeren:

//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];
antwoordde op 22/02/2010 om 06:57
bron van user

stemmen
0

Het gebeurt omdat QuartzCore heeft het coördinatensysteem "linksonder", terwijl UIKit - "linksboven".

In het geval dat u kunt uitbreiden 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)
antwoordde op 30/09/2019 om 11:28
bron van user

stemmen
0

Swift 5 antwoord gebaseerd op @ ZpaceZombor is uitstekend antwoord

Als u een UIImage, gewoon gebruik maken van

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

Als u een CGImage gebruiken onder mijn categorie

Let op: In tegenstelling tot sommige andere antwoorden, deze rekening mee houdt dat de rect u wilt tekenen in zou Y = 0. Deze antwoorden die geen rekening mee te houden onjuist zijn en zal niet werken in het algemene geval!.

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)

    }
} 

Gebruik als volgt uit:

let imageFrame: CGRect = ...
let context: CGContext = ....
let img: CGImage = ..... 
context.drawImage(image: img, inRect: imageFrame)
antwoordde op 20/09/2019 om 10:24
bron van user

stemmen
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()
    }
}
antwoordde op 03/05/2018 om 12:06
bron van user

stemmen
0

Swift 3 CoreGraphics Solution

Als u wilt CG om wat de reden zou kunnen zijn, in plaats van UIImage gebruiken, moet deze Swift 3 bouw op basis van eerdere antwoorden deed het probleem op te lossen voor mij:

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()
}
antwoordde op 31/08/2017 om 20:05
bron van user

stemmen
0

In de loop van mijn project sprong ik uit het antwoord Kendall's op antwoord van Cliff's om dit probleem op te lossen voor beelden die worden geladen vanuit de telefoon zelf.

Uiteindelijk belandde ik met behulp van CGImageCreateWithPNGDataProviderplaats:

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

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

Dit heeft geen last van de oriëntatie zaken die je zou krijgen van het krijgen van de CGImageuit een UIImageen het kan worden gebruikt als de inhoud van een CALayervan een leien dakje.

antwoordde op 15/03/2014 om 17:13
bron van user

antwoordde op 26/11/2009 om 13:24
bron van user

stemmen
-5

U kunt dit probleem oplossen door dit te doen:

//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.
antwoordde op 04/04/2012 om 02:55
bron van user

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