web-dev-qa-db-fra.com

CGContextDrawImage dessine l'image à l'envers lorsqu'elle est transmise à UIImage.CGImage

Est-ce que quelqu'un sait pourquoi CGContextDrawImage dessinerait mon image à l'envers? Je charge une image depuis mon application:

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

Et puis simplement demander aux graphiques de base de le dessiner dans 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 quelque chose de vraiment évident ici?

176
rustyshelf

Au lieu de 

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

Utilisation 

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

Au milieu de vos méthodes de début/fin CGcontext.

Cela va dessiner l’image avec l’orientation correcte dans votre contexte d’image actuel. Je suis presque certaine que cela a quelque chose à voir avec la UIImage conservant la connaissance de l’orientation tandis que la méthode CGContextDrawImage récupère les données d’image brutes sous-jacentes sans aucune compréhension de l’orientation.

Notez que vous rencontrerez ce problème dans de nombreux domaines, mais un exemple spécifique concerne les images d'utilisateur du carnet d'adresses.

227

Même après avoir appliqué tout ce que j'ai mentionné, j'ai toujours eu des drames avec les images. En fin de compte, je viens d'utiliser Gimp pour créer une version «verticale inversée» de toutes mes images. Maintenant, je n'ai pas besoin d'utiliser de Transforms. Espérons que cela ne causera pas de problèmes supplémentaires sur la piste.

Quelqu'un sait-il pourquoi CGContextDrawImage attirerait mon fichier image à l'envers? Je charge un image de mon application:

Quartz2d utilise un système de coordonnées différent, l’origine étant dans le coin inférieur gauche. Ainsi, lorsque Quartz dessine le pixel x [5], y [10] d’une image 100 * 100, ce pixel est tracé dans le coin inférieur gauche au lieu du coin supérieur gauche. Ce qui provoque l'image "retournée".

Le système de coordonnées x correspond, vous devrez donc inverser les coordonnées y.

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

Cela signifie que nous avons traduit l'image par 0 unités sur l'axe des x et par la hauteur des images sur l'axe des y. Cependant, cela seul signifiera que notre image est toujours à l'envers, simplement dessinée "image.size.height" ci-dessous où nous souhaitons qu'elle soit dessinée.

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

CGContextScaleCTM(context, 1.0, -1.0);

Combinez les deux juste avant votre appel CGContextDrawImage et vous devriez dessiner l'image 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);

Faites attention si vos coordonnées imageRect ne correspondent pas à celles de votre image, car vous pourriez obtenir des résultats inattendus.

Pour reconvertir les coordonnées:

CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0, -imageRect.size.height);
213
Cliff Viegas

Le meilleur des deux mondes, utilisez drawAtPoint: ou drawInRect: de UIImage tout en spécifiant votre contexte personnalisé:

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

De même, vous évitez de modifier votre contexte avec CGContextTranslateCTM ou CGContextScaleCTM comme le fait la deuxième réponse.

20
Rivera

Je ne suis pas sûr pour UIImage, mais ce type de comportement se produit généralement lorsque les coordonnées sont inversées. La plupart des systèmes de coordonnées OS X ont leur origine dans le coin inférieur gauche, comme dans PostScript et PDF. Mais le système de coordonnées CGImage a son origine dans le coin supérieur gauche.

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

7
mouviciel

Si quelqu'un est intéressé par une solution simple 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.

5
ZpaceZombor

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();
3
Priyanka V

Swift 3.0 & 4.0

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

Aucune modification nécessaire

3
Abdul Waheed

UIImage contient CGImage comme principal membre du contenu, ainsi que des facteurs de mise à l'échelle et d'orientation. Étant donné que CGImage et ses diverses fonctions sont dérivés d’OSX, il s’attend à un système de coordonnées inversé par rapport à l’iPhone. Lorsque vous créez une UIImage, elle prend par défaut une orientation à l'envers pour compenser (vous pouvez changer cela!). Utilisez la propriété .CGImage pour accéder aux très puissantes fonctions CGImage, mais dessiner sur l'écran de l'iPhone, etc. est mieux réalisé avec les méthodes UIImage.

3
James L

Réponse supplémentaire avec code Swift

Les graphiques Quartz 2D utilisent un système de coordonnées avec l'origine en bas à gauche, tandis qu'UIKit sous iOS utilise un système de coordonnées avec l'origine en haut à gauche. Tout fonctionne normalement mais lors de certaines opérations graphiques, vous devez modifier vous-même le système de coordonnées. La documentation indique:

Certaines technologies configurent leurs contextes graphiques en utilisant un fichier .__ différent. système de coordonnées par défaut que celui utilisé par Quartz. Relatif à Quartz, un tel système de coordonnées est un système de coordonnées modifié et doit être compensé lors de l'exécution d'un dessin au quartz opérations. Le système de coordonnées modifié le plus commun place le Origine dans le coin supérieur gauche du contexte et modifie l'axe des ordonnées pointer vers le bas de la page.

Ce phénomène peut être observé dans les deux instances suivantes de vues personnalisées qui dessinent une image dans leurs méthodes drawRect.

 enter image description here

Sur le côté gauche, l'image est à l'envers et sur le côté droit, le système de coordonnées a été traduit et mis à l'échelle afin que l'origine soit en haut à gauche.

Image à l'envers

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)

}

Système de coordonnées modifié

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)
}
2
Suragch

Solution Swift 3 CoreGraphics

Si vous voulez utiliser CG pour quelque raison que ce soit, plutôt que UIImage, cette construction Swift 3 basée sur les 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()
}
1
biomiker

drawInRect est certainement la voie à suivre. Voici une autre petite chose qui vous sera utile. Habituellement, l'image et le rectangle dans lequel elle va aller ne sont pas conformes. Dans ce cas, drawInRect étendra l'image. Voici un moyen rapide et sympa de vous assurer que le rapport d’aspect de l’image n’est pas modifié en inversant la transformation (qui correspondra au tout):

//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];
1
marc meyer

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

Finalement, j'ai utilisé CGImageCreateWithPNGDataProvider à la place:

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 auriez à obtenir de la CGImage d'une UIImage et il peut être utilisé comme contenu d'une CALayer sans accroc.

0
Ja͢ck
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()
    }
}
0
neoneye