CGContextDrawImage dibuja la imagen al revés cuando se pasa UIImage.CGImage

votos
169

¿Alguien sabe por qué CGContextDrawImageestaría dibujando mi imagen al revés? Estoy cargando una imagen desde mi aplicación:

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

Y luego simplemente pidiendo gráficos centrales para dibujarlo en mi contexto:

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

Se renderiza en el lugar y dimensiones correctos, pero la imagen está boca abajo. Debo estar perdiendo algo realmente obvio aquí?

Publicado el 03/02/2009 a las 11:23
fuente por usuario
En otros idiomas...                            


18 respuestas

votos
218

En lugar de

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

Utilizar

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

En el medio de sus CGcontextmétodos de inicio / finalización .

Esto dibujará la imagen con la orientación correcta en el contexto de la imagen actual. Estoy bastante seguro de que esto tiene algo que ver con el UIImageconocimiento de la orientación, mientras que el CGContextDrawImagemétodo obtiene los datos de la imagen sin procesar subyacentes sin entender la orientación.

Tenga en cuenta que se encontrará con este problema en muchas áreas, pero un ejemplo específico es el de las imágenes de usuario de la libreta de direcciones.

Respondida el 06/02/2009 a las 21:46
fuente por usuario

votos
211

Incluso después de aplicar todo lo que he mencionado, todavía tengo dramas con las imágenes. Al final, acabo de usar Gimp para crear una versión 'volteada vertical' de todas mis imágenes. Ahora no necesito usar ninguna Transformación. Con suerte, esto no causará más problemas en la pista.

¿Alguien sabe por qué CGContextDrawImage dibujaría mi imagen al revés? Estoy cargando una imagen desde mi aplicación:

Quartz2d usa un sistema de coordenadas diferente, donde el origen está en la esquina inferior izquierda. Entonces, cuando Quartz dibuja el píxel x [5], y [10] de una imagen de 100 * 100, ese píxel se dibuja en la esquina inferior izquierda en lugar de la esquina superior izquierda. Por lo tanto, causando la imagen 'volteada'.

El sistema de coordenadas x coincide, por lo que necesitarás voltear las coordenadas y.

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

Esto significa que hemos traducido la imagen por 0 unidades en el eje xy por la altura de las imágenes en el eje y. Sin embargo, esto solo significará que nuestra imagen aún está boca abajo, simplemente se dibuja "image.size.height" debajo de donde deseamos que se dibuje.

La guía de programación Quartz2D recomienda usar ScaleCTM y pasar valores negativos para voltear la imagen. Puede usar el siguiente código para hacer esto:

CGContextScaleCTM(context, 1.0, -1.0);

Combine los dos momentos antes de su CGContextDrawImagellamada y la imagen debe dibujarse correctamente.

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

Solo tenga cuidado si sus coordenadas imageRect no coinciden con las de su imagen, ya que puede obtener resultados no deseados.

Para convertir de vuelta las coordenadas:

CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0, -imageRect.size.height);
Respondida el 04/02/2009 a las 13:49
fuente por usuario

votos
19

Lo mejor de ambos mundos, el uso de UIImage drawAtPoint:o drawInRect:al mismo tiempo que especifica el contexto personalizado:

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

También a evitar la modificación de su contexto con el CGContextTranslateCTMo CGContextScaleCTMla que la segunda respuesta hace.

Respondida el 07/08/2013 a las 06:47
fuente por usuario

votos
8

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

Dar la vuelta al sistema de coordenadas predeterminado

Mover de un tirón en el dibujo UIKit modifica la CALayer respaldo para alinear un entorno de dibujo que tiene una LLO sistema con el sistema de coordenadas predeterminado de UIKit de coordenadas. Si sólo utiliza métodos y función UIKit para el dibujo, que no es necesario dar la vuelta a la CTM. Sin embargo, si se mezclan núcleo gráfico o imagen I / O función de llamadas con llamadas UIKit, volteando la CTM podría ser necesario.

En concreto, si se dibuja una imagen o documento PDF llamando directamente a las funciones de núcleo de gráficos, el objeto se vuelve boca abajo en el contexto de la vista. Debes dar la vuelta a la CTM para mostrar la imagen y las páginas correctamente.

Para voltear un objeto dibujado a un contexto núcleo de gráficos para que aparezca correctamente cuando se muestra en una vista UIKit, debe modificar la CTM en dos pasos. Traducir el origen de la esquina superior izquierda del área de dibujo, y luego se aplica una escala de traducción, modificación de la coordenada y por -1. El código para hacer esto es similar a lo siguiente:

CGContextSaveGState(graphicsContext);
CGContextTranslateCTM(graphicsContext, 0.0, imageHeight);
CGContextScaleCTM(graphicsContext, 1.0, -1.0);
CGContextDrawImage(graphicsContext, image, CGRectMake(0, 0, imageWidth, imageHeight));
CGContextRestoreGState(graphicsContext);
Respondida el 08/04/2016 a las 03:11
fuente por usuario

votos
7

No estoy seguro UIImage, pero este tipo de comportamiento generalmente ocurre cuando se cambian las coordenadas. La mayoría de los sistemas de coordenadas OS X tienen su origen en la esquina inferior izquierda, como en Postscript y PDF. Pero el CGImagesistema de coordenadas tiene su origen en la esquina superior izquierda.

Las posibles soluciones pueden implicar una isFlippedpropiedad o una scaleYBy:-1transformación afín.

Respondida el 03/02/2009 a las 11:34
fuente por usuario

votos
6

Si alguien está interesado en una solución obviedad para dibujar una imagen en un rect personalizada en un contexto:

 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)

}

La imagen será modificada para llenar el rect.

Respondida el 18/02/2016 a las 11:20
fuente por usuario

votos
3

Swift 3.0 y 4.0

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

Ninguna alteración necesaria

Respondida el 04/08/2017 a las 07:41
fuente por usuario

votos
3

Podemos resolver este problema utilizando la misma función:

UIGraphicsBeginImageContext(image.size);

UIGraphicsPushContext(context);

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

UIGraphicsPopContext();

UIGraphicsEndImageContext();
Respondida el 25/08/2011 a las 07:27
fuente por usuario

votos
3

UIImagecontiene una CGImagecomo su miembro de contenido principal, así como factores de escala y la orientación. Desde CGImagey sus diversas funciones se derivan de OSX, que espera que un sistema que está al revés en comparación con el iPhone de coordenadas. Cuando se crea una UIImage, el valor predeterminado es una orientación al revés para compensar (se puede cambiar esto!). Utilizar el . CGImagepropiedad para acceder a los muy potentes CGImagefunciones, pero que dibujar en la pantalla, etc. iPhone es mejor hacerlo con los UIImagemétodos.

Respondida el 01/08/2010 a las 00:26
fuente por usuario

votos
2

respuesta suplementario con el código Swift

Gráficos Quartz 2D utilizan un sistema de coordenadas con el origen en la parte inferior izquierda mientras UIKit en IOS utiliza un sistema de coordenadas con el origen en la esquina superior izquierda. Todo por lo general funciona bien, pero al hacer algunas operaciones de gráficos, hay que modificar el sistema de coordenadas a sí mismo. Las documentación estados:

Algunas tecnologías establecieron sus contextos gráficos usando el sistema que el utilizado por el cuarzo de coordenadas predeterminado diferente. En relación con cuarzo, tal sistema de coordenadas es un sistema modificado coordinar y debe ser compensado para cuando se realizan algunas operaciones de dibujo de cuarzo. El más común modificada sistema de coordenadas sitúa el origen en la esquina superior izquierda del contexto y cambia el eje y apuntar hacia la parte inferior de la página.

Este fenómeno se puede ver en los dos casos siguientes de vistas personalizadas que atraen a una imagen en sus drawRectmétodos.

introducir descripción de la imagen aquí

En el lado izquierdo, la imagen está al revés y en el lado derecho del sistema de coordenadas se ha traducido y escalado de manera que el origen se encuentra en la parte superior izquierda.

imagen invertida

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 sistema de coordenadas

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)
}
Respondida el 22/12/2015 a las 00:25
fuente por usuario

votos
1

drawInRectes sin duda el camino a seguir. Aquí es otra cosa que vendrá en forma útil cuando se hace esto. Por lo general, la imagen y el rectángulo en el que se va a ir no se ajustan. En ese caso drawInRectse estirará la imagen. Aquí está una manera rápida y fresca para asegurarse de que la relación de aspecto de la imagen no se cambia, mediante la inversión de la transformación (que será para adaptarse a toda la cosa en):

//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];
Respondida el 22/02/2010 a las 06:57
fuente por usuario

votos
0

Esto sucede porque QuartzCore tiene el sistema de coordenadas "de abajo a la izquierda", mientras que UIKit - "de arriba a la izquierda".

En el caso puede extender 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)
Respondida el 30/09/2019 a las 11:28
fuente por usuario

votos
0

Swift 5 respuestas basado en @ ZpaceZombor excelente respuesta

Si usted tiene un UIImage, sólo tiene que utilizar

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

Si usted tiene un CGImage usar mi categoría abajo

Nota: A diferencia de algunas otras respuestas, esto se tiene en cuenta que el rect desea dibujar podría tener en y = 0. Aquellas respuestas que no tenerlo en cuenta son incorrectos y no funcionará en el caso general!.

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)

    }
} 

Utilice la siguiente manera:

let imageFrame: CGRect = ...
let context: CGContext = ....
let img: CGImage = ..... 
context.drawImage(image: img, inRect: imageFrame)
Respondida el 20/09/2019 a las 10:24
fuente por usuario

votos
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()
    }
}
Respondida el 03/05/2018 a las 09:06
fuente por usuario

votos
0

Swift 3 CoreGraphics Solución

Si desea utilizar CG para lo que podría ser la razón, en lugar de UIImage, esta construcción Swift 3 basado en las respuestas anteriores ha solucionado el problema para mí:

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()
}
Respondida el 31/08/2017 a las 17:05
fuente por usuario

votos
0

Durante el transcurso de mi proyecto Salté de la respuesta de Kendall a la respuesta del acantilado para resolver este problema para las imágenes que se cargan desde el propio teléfono.

Al final terminé usando CGImageCreateWithPNGDataProvideren su lugar:

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

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

Esto no sufre de los problemas de orientación que se podrían obtener de conseguir el CGImagede una UIImagey puede ser utilizado como el contenido de un CALayersin problemas.

Respondida el 15/03/2014 a las 17:13
fuente por usuario

Respondida el 26/11/2009 a las 13:24
fuente por usuario

votos
-5

También puede resolver este problema al hacer esto:

//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.
Respondida el 04/04/2012 a las 02:55
fuente por usuario

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