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?
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.
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);
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.
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
.
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.
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();
Swift 3.0 & 4.0
yourImage.draw(in: CGRect, blendMode: CGBlendMode, alpha: ImageOpacity)
Aucune modification nécessaire
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
.
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
.
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)
}
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()
}
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];
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.
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()
}
}