CGContextDrawImage tire image à l'envers lorsqu'il est passé UIImage.CGImage

voix
169

Est -ce que quelqu'un sait pourquoi CGContextDrawImageserait attiré mon image à l' envers? Je suis le chargement d' une image de ma demande:

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

Et puis demander simplement graphiques de base pour dessiner à mon contexte:

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

Il rend au bon endroit, et les dimensions, mais l'image est à l'envers. Je dois manquer ici quelque chose de vraiment évident?

Créé 03/02/2009 à 11:23
source utilisateur
Dans d'autres langues...                            


18 réponses

voix
218

Au lieu de

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

Utilisation

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

Au milieu de votre début / fin CGcontextméthodes.

Cela attirera l'image avec l'orientation correcte dans le contexte de votre image actuelle - je suis sûr que cela a quelque chose à voir avec la UIImagetenue sur la connaissance de l'orientation alors que la CGContextDrawImageméthode obtient les données d'images brutes sous - jacentes sans la compréhension de l' orientation.

Notez que vous rencontrez ce problème dans de nombreux domaines, mais un exemple spécifique traite avec des images de l'utilisateur du carnet d'adresses.

Créé 06/02/2009 à 21:46
source utilisateur

voix
211

Même après l'application de tout ce que je l'ai mentionné, je l'ai toujours eu des drames avec les images. En fin de compte, je l'ai juste utilisé Gimp pour créer une version « verticale basculée » de toutes mes images. Maintenant, je ne ai pas besoin d'utiliser des Transforms. Espérons que cela ne causera pas d'autres problèmes sur la piste.

Est-ce que quelqu'un sait pourquoi CGContextDrawImage établiraient mon image à l'envers? Je suis le chargement d'une image de ma demande:

Quartz2d utilise un système de coordonnées différent, où l'origine est dans le coin inférieur gauche. Ainsi, lorsque des jeux Quartz pixels x [5], y [10] d'un 100 * 100 image dans ce pixel est en cours d'élaboration dans le coin inférieur gauche au lieu de la partie supérieure gauche. Provoquant ainsi l'image « basculée ».

Le système de coordonnées x correspond, de sorte que vous devrez retourner les y coordonnées.

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

Cela signifie que nous avons traduit l'image par 0 unités sur l'axe x et par la hauteur des images sur l'axe y. Cependant, cela ne signifie notre image est toujours à l'envers, vient d'être tirée « image.size.height » ci-dessous où l'on veut à tirer.

Le guide de programmation Quartz2D recommande d'utiliser ScaleCTM et passer des valeurs négatives pour retourner l'image. Vous pouvez utiliser le code suivant pour le faire -

CGContextScaleCTM(context, 1.0, -1.0);

Combiner les deux juste avant votre CGContextDrawImageappel et vous devriez avoir l'image dessiné correctement.

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

Il suffit de faire attention si vos coordonnées imageRect ne correspondent pas à ceux de l'image, que vous pouvez obtenir des résultats inattendus.

Pour reconvertir les coordonnées:

CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0, -imageRect.size.height);
Créé 04/02/2009 à 13:49
source utilisateur

voix
19

Le meilleur des deux mondes, utilisez de UIImage drawAtPoint:ou drawInRect:tout en précisant encore votre contexte personnalisé:

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

Aussi , vous éviter de modifier votre contexte avec CGContextTranslateCTMou CGContextScaleCTMdont la deuxième réponse ne.

Créé 07/08/2013 à 06:47
source utilisateur

voix
8

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

En feuilletant le système de coordonnées par défaut

Retournement dans le dessin UIKit modifie le CALayer de support pour aligner un environnement de dessin ayant un système de coordonnées LLO avec le système de coordonnées par défaut de UIKit. Si vous utilisez uniquement des méthodes UIKit et la fonction pour le dessin, vous ne devriez pas avoir besoin de retourner la CTM. Cependant, si vous mélangez Core Graphics ou une image fonction I / O Les appels avec UIKit, retournement de la marque communautaire pourrait être nécessaire.

Plus précisément, si vous dessinez une image ou d'un document PDF en appelant des fonctions graphiques de base directement, l'objet est rendu à l'envers dans le contexte de la vue. Vous devez retourner la CTM pour afficher l'image et les pages correctement.

Pour retourner un objet dessiné dans un contexte graphique de base pour qu'il apparaisse correctement lorsqu'elle est affichée dans une vue UIKit, vous devez modifier la CTM en deux étapes. Vous traduisez l'origine dans le coin supérieur gauche de la zone de dessin, puis vous appliquez une traduction à l'échelle, la modification de la coordonnée y par -1. Le code pour faire cela ressemble à ce qui suit:

CGContextSaveGState(graphicsContext);
CGContextTranslateCTM(graphicsContext, 0.0, imageHeight);
CGContextScaleCTM(graphicsContext, 1.0, -1.0);
CGContextDrawImage(graphicsContext, image, CGRectMake(0, 0, imageWidth, imageHeight));
CGContextRestoreGState(graphicsContext);
Créé 08/04/2016 à 06:11
source utilisateur

voix
7

Je ne sais pas pour UIImage, mais ce genre de comportement se produit généralement lorsque les coordonnées sont retournées. La plupart des systèmes de coordonnées OS X ont leur origine dans le coin en bas à gauche, comme dans Postscript et PDF. Mais le CGImagesystème de coordonnées a son origine dans le coin supérieur gauche.

Les solutions possibles peuvent impliquer une isFlippedpropriété ou une scaleYBy:-1transformation affine.

Créé 03/02/2009 à 11:34
source utilisateur

voix
6

Si quelqu'un est intéressé par une solution non-tête pour dessiner une image dans un rectangle personnalisé dans un contexte:

 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'image sera mise à l'échelle pour remplir le rect.

Créé 18/02/2016 à 14:20
source utilisateur

voix
3

Swift 3.0 et 4.0

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

Aucune modification nécessaire

Créé 04/08/2017 à 10:41
source utilisateur

voix
3

Nous pouvons résoudre ce problème en utilisant la même fonction:

UIGraphicsBeginImageContext(image.size);

UIGraphicsPushContext(context);

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

UIGraphicsPopContext();

UIGraphicsEndImageContext();
Créé 25/08/2011 à 07:27
source utilisateur

voix
3

UIImagecontient un CGImagecomme membre de contenu principal, ainsi que des facteurs d'échelle et d' orientation. Depuis CGImageet ses différentes fonctions sont dérivées de Mac OS X, il attend un système de coordonnées qui est à l' envers par rapport à l'iPhone. Lorsque vous créez un UIImage, il est par défaut à une orientation à l' envers pour compenser (vous pouvez changer cela!). Utilisez le . CGImagepropriété pour accéder aux puissantes CGImagefonctions, mais le dessin sur l'écran de l' iPhone , etc. est mieux fait avec les UIImageméthodes.

Créé 01/08/2010 à 00:26
source utilisateur

voix
2

Réponse supplémentaire avec le code Swift

Quartz graphiques 2D utilisent un système de coordonnées dont l'origine est en bas à gauche tandis que UIKit dans iOS utilise un système de coordonnées à l'origine en haut à gauche. Tout fonctionne généralement bien , mais lorsque vous faites des opérations graphiques, vous devez modifier vous - même système de coordonnées. La documentation stipule que :

Certaines technologies mis en place leurs contextes graphiques en utilisant un système de coordonnées par défaut différent de celui utilisé par Quartz. Par rapport à quartz, un tel système de coordonnées est un système de coordonnées modifié et doit être compensée lors de l'exécution des opérations de dessin de quartz. Le système de coordonnées modifié le plus courant met l'origine dans le coin supérieur gauche du contexte et change l'axe des y pointer vers le bas de la page.

Ce phénomène peut être vu dans les deux cas de vues personnalisées suivantes dessiner une image dans leurs drawRectméthodes.

entrez la description d'image ici

Sur le côté gauche, l'image est à l'envers et sur le côté droit du système de coordonnées a été traduit et mis à l'échelle pour que l'origine est dans le coin supérieur gauche.

l'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)

}

MODIFIÉ système de coordonnées

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)
}
Créé 22/12/2015 à 03:25
source utilisateur

voix
1

drawInRectest certainement le chemin à parcourir. Voici une autre petite chose qui va venir de manière utile pour le faire. Habituellement , l'image et le rectangle dans lequel il va aller ne sont pas conformes. Dans ce cas drawInRectétirera l'image. Voici un moyen rapide et cool de faire en sorte que ne change pas de rapport d'aspect de l'image, en inversant la transformation (qui sera à adapter le tout dans):

//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];
Créé 22/02/2010 à 06:57
source utilisateur

voix
0

Cela arrive parce que QuartzCore a le « bas-gauche » système de coordonnées, tandis que UIKit - « en haut à gauche ».

Dans le cas , vous pouvez étendre 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)
Créé 30/09/2019 à 11:28
source utilisateur

voix
0

Swift 5 réponse basée sur @ ZpaceZombor est excellente réponse

Si vous avez un UIImage, il suffit d'utiliser

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

Si vous avez un CGImage utiliser ma catégorie ci-dessous

Remarque: Contrairement à d'autres réponses, celle-ci prend en compte que le rect que vous voulez dessiner pourrait avoir y = 0. Les réponses qui ne prennent pas en compte sont incorrectes et ne fonctionnera pas dans le cas général!.

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)

    }
} 

Utilisez comme ceci:

let imageFrame: CGRect = ...
let context: CGContext = ....
let img: CGImage = ..... 
context.drawImage(image: img, inRect: imageFrame)
Créé 20/09/2019 à 10:24
source utilisateur

voix
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()
    }
}
Créé 03/05/2018 à 12:06
source utilisateur

voix
0

Swift 3 CoreGraphics Solution

Si vous voulez utiliser CG pour quel que soit votre raison pourrait être, au lieu de UIImage, cette construction Swift 3 sur la base des réponses précédentes a résolu le problème pour moi:

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()
}
Créé 31/08/2017 à 20:05
source utilisateur

voix
0

Au cours de mon projet , je sautais de la réponse de Kendall à la réponse de Cliff pour résoudre ce problème pour les images qui sont chargées à partir du téléphone lui - même.

En fin de compte, je fini par utiliser à la CGImageCreateWithPNGDataProviderplace:

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

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

Cela ne souffre pas des problèmes d'orientation que vous obtiendriez d'obtenir le CGImaged'un UIImageet il peut être utilisé comme le contenu d'un CALayersans accroc.

Créé 15/03/2014 à 17:13
source utilisateur

Créé 26/11/2009 à 13:24
source utilisateur

voix
-5

Vous pouvez également résoudre ce problème en faisant ceci:

//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.
Créé 04/04/2012 à 02:55
source utilisateur

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