CGContextDrawImage zieht Bild auf den Kopf, wenn UIImage.CGImage vergangen

stimmen
169

Weiß jemand , warum CGContextDrawImageKopf mein Bild nach unten würde zu zeichnen? Ich lade Sie ein Bild in meiner Anwendung:

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

Und dann fragt einfach Core-Grafik es meinen Kontext zu ziehen:

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

Es macht in der richtigen Stelle, und Dimensionen, aber das Bild auf dem Kopf steht. Ich muss etwas wirklich offensichtlich hier fehlt?

Veröffentlicht am 03/02/2009 um 11:23
quelle vom benutzer
In anderen Sprachen...                            


18 antworten

stimmen
218

Anstatt von

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

Benutzen

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

In der Mitte der Beginn / Ende CGcontextMethoden.

Dadurch wird das Bild mit der richtigen Orientierung in Ihren aktuelles Bild Kontext ziehen - ich bin mir ziemlich sicher , dass dies etwas mit dem zu tun , UIImageHalt auf Kenntnis der Orientierung während des CGContextDrawImageVerfahren der zugrunde liegenden Rohbilddaten ohne Verständnis der Orientierung bekommt.

Beachten Sie, dass Sie in dieses Problem in vielen Bereichen führen wird, aber ein spezielles Beispiel wird mit Adressbuch Benutzer Bilder handelt.

Beantwortet am 06/02/2009 um 21:46
quelle vom benutzer

stimmen
211

Auch nach allem, was der Anwendung ich erwähnt habe, habe ich hatte noch Dramen mit den Bildern. Am Ende habe ich Gimp nur verwendet, um eine ‚blätterte vertikale‘ Version all meine Bilder zu erstellen. Jetzt brauche ich keine Transformationen zu verwenden. Hoffentlich wird dies nicht dazu führen, weitere Probleme auf der Spur.

Weiß jemand, warum würde CGContextDrawImage mein Bild zeichnen den Kopf? Ich lade Sie ein Bild in meiner Anwendung:

Quartz2d verwendet ein anderes Koordinatensystem, wobei der Ursprung in der linken unteren Ecke befindet. Wenn also Quartz zeichnet Pixel x [5], y [10] von einem 100 * 100 Bild, wird das Pixel in der linken unteren Ecke gezogen wird anstelle der oben links. So verursacht das ‚gekippt‘ Bild.

Das x-Koordinatensystem übereinstimmt, so dass Sie die y-Koordinaten drehen müssen.

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

Dies bedeutet, dass wir das Bild von 0 Einheiten auf der x-Achse und durch die Bilder Höhe auf der y-Achse übersetzt haben. Dies wird jedoch allein unser Bild bedeutet noch den Kopf, gerade gezogen wird „image.size.height“ weiter unten, wo wir es wollen gezogen werden.

Die Quartz2D Programmieranleitung empfiehlt die Verwendung von ScaleCTM und Leiten negative Werte um das Bild zu drehen. Sie können den folgenden Code verwenden, dies zu tun -

CGContextScaleCTM(context, 1.0, -1.0);

Kombinieren Sie die beiden kurz vor Ihrem CGContextDrawImageAnruf , und Sie sollten das Bild korrekt gezeichnet haben.

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

Nur vorsichtig sein, wenn Ihr imageRect Koordinaten nicht mit denen dem Bild entspricht, wie Sie unbeabsichtigte Ergebnisse bekommen.

Zur Umrechnung der Koordinaten zurück:

CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0, -imageRect.size.height);
Beantwortet am 04/02/2009 um 13:49
quelle vom benutzer

stimmen
19

Das Beste aus beiden Welten, verwenden Sie UIImage die drawAtPoint:oder drawInRect:während noch Ihren benutzerdefinierten Kontext festgelegt wird :

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

Vermeiden Sie auch Sie Ihren Zusammenhang mit Modifikation CGContextTranslateCTModer CGContextScaleCTMdem die zweite Antwort der Fall ist.

Beantwortet am 07/08/2013 um 06:47
quelle vom benutzer

stimmen
8

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

Das Umlegen der Standard-Koordinatensystem

Umlegen in UIKit Zeichnung modifiziert die Träger CALayer eine Zeichnung Umgebung mit einem LLO-Koordinatensystem mit dem Standardkoordinatensystem des UIKit auszurichten. Wenn Sie nur UIKit Methoden und Funktion zum Zeichnen verwenden, sollten Sie nicht die CTM kippen müssen. Wenn Sie jedoch Core Graphics oder Bild mischen I / O-Funktion ruft mit UIKit Anrufe, könnte notwendig sein, die CTM Spiegeln.

Insbesondere wenn Sie Core Graphics Funktionen ein Bild oder PDF-Dokument ziehen direkt durch den Aufruf, wird das Objekt Kopf nach unten in der Kontext der Ansicht gerendert. Sie müssen die CTM-Flip das Bild und die Seiten korrekt anzuzeigen.

So spiegelt ein Objekt in einen Kontext Core Graphics gezogen, so dass es richtig angezeigt wird, wenn in einer UIKit Ansicht angezeigt wird, müssen Sie die CTM in zwei Schritten ändern. Sie übersetzen den Ursprung an der oberen linken Ecke der Zeichenfläche, und Sie dann eine Skala Übersetzung anwenden, ändern die mit -1 y-Koordinate. Der Code hierfür sieht ähnlich wie die folgenden:

CGContextSaveGState(graphicsContext);
CGContextTranslateCTM(graphicsContext, 0.0, imageHeight);
CGContextScaleCTM(graphicsContext, 1.0, -1.0);
CGContextDrawImage(graphicsContext, image, CGRectMake(0, 0, imageWidth, imageHeight));
CGContextRestoreGState(graphicsContext);
Beantwortet am 08/04/2016 um 06:11
quelle vom benutzer

stimmen
7

Ich bin nicht sicher , für UIImage, aber diese Art von Verhalten in der Regel tritt auf, wenn Koordinaten gespiegelt werden. Die meisten von OS X - Koordinatensysteme haben ihren Ursprung in der linken unteren Ecke, wie in Postscript und PDF. Aber CGImageKoordinatensystem hat seinen Ursprung in der oberen linken Ecke.

Mögliche Lösungen können eine beinhalten isFlippedEigenschaft oder eine scaleYBy:-1affine Transformation.

Beantwortet am 03/02/2009 um 11:34
quelle vom benutzer

stimmen
6

Wenn jemand interessiert sich für eine no-brainer Lösung für ein Bild in einem benutzerdefinierten rect in einem Kontext Zeichnung:

 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)

}

Das Bild wird skaliert, um die rect zu füllen.

Beantwortet am 18/02/2016 um 14:20
quelle vom benutzer

stimmen
3

Swift 3.0 & 4.0

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

Keine Änderung erforderlich

Beantwortet am 04/08/2017 um 10:41
quelle vom benutzer

stimmen
3

Wir können dieses Problem mit der gleichen Funktion lösen:

UIGraphicsBeginImageContext(image.size);

UIGraphicsPushContext(context);

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

UIGraphicsPopContext();

UIGraphicsEndImageContext();
Beantwortet am 25/08/2011 um 07:27
quelle vom benutzer

stimmen
3

UIImagea enthält CGImageals Hauptinhaltselement sowie Skalierung und Orientierungsfaktoren. Da CGImageund seine verschiedenen Funktionen aus OSX abgeleitet sind, erwartet , dass es ein Koordinatensystem , das den Kopf ist auf die iPhone verglichen. Wenn Sie erstellen ein UIImage, wird standardmäßig ein umgedrehtes Orientierung zu kompensieren (Sie können dies ändern!). Verwenden Sie die. CGImageEigenschaft , die sehr mächtig für den Zugriff auf CGImageFunktionen, aber auf den Bildschirm usw. ist am besten mit den fertig iPhone Zeichnung UIImageMethoden.

Beantwortet am 01/08/2010 um 00:26
quelle vom benutzer

stimmen
2

Ergänzende Antwort mit Swift-Code

Quartz 2D - Grafik verwenden , um ein Koordinatensystem mit dem Ursprung in der linken unteren während UIKit in iOS ein Koordinaten verwendet System mit dem Ursprung in der oberen linken. Alles funktioniert in der Regel gut , aber wenn einige Grafikoperationen zu tun, müssen Sie das System selbst koordinieren ändern. Die Dokumentation heißt es :

Einige Technologien setzen ihre Graphikkontexte up mit einem anderen Standard-Koordinatensystem als die von Quarz verwendet. Relativ zu Quarz, ein solches Koordinatensystem ist ein modifiziertes Koordinatensystem und müssen kompensiert werden, wenn einige Quartz Zeichenoperationen durchführt. Die häufigste System modifiziert Koordinate stellt den Ursprung in der oberen linken Ecke des Kontextes und ändert die y-Achse in Richtung der Unterseite der Seite zu zeigen.

Dieses Phänomen kann in den folgenden zwei Instanzen von benutzerdefinierten Ansichten , die in ihren ein Bild zeichnen zu sehen drawRectMethoden.

Geben Sie hier image description

Auf der linken Seite ist das Bild auf dem Kopf nach unten und auf der rechten Seite des Koordinatensystem übersetzt und skaliert wurde, so dass der Ursprung in der oberen linken Seite.

Upside-down-Bild

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 Koordinatensystem

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)
}
Beantwortet am 22/12/2015 um 03:25
quelle vom benutzer

stimmen
1

drawInRectzu gehen , ist sicherlich der richtige Weg. Hier ist eine andere Kleinigkeit, die in kommen Art und Weise nützlich , wenn dies zu tun. In der Regel das Bild und das Rechteck , in die es gehen wird nicht entsprechen. In diesem Fall drawInRectwird das Bild strecken. Hier ist eine schnelle und coole Art , um sicherzustellen , dass die Ration Bild nicht geändert wird, durch die Transformation Umkehr (was die ganze Sache passen in sein):

//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];
Beantwortet am 22/02/2010 um 06:57
quelle vom benutzer

stimmen
0

Es geschieht, weil Quartz das Koordinatensystem „unten links“ hat, während UIKit - „oben links“.

Im Fall können Sie erweitern 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)
Beantwortet am 30/09/2019 um 11:28
quelle vom benutzer

stimmen
0

Swift 5 Antwort basierend auf @ ZpaceZombor ist eine ausgezeichnete Antwort

Wenn Sie eine UIImage haben, verwenden Sie einfach

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

Wenn Sie eine CGImage haben meine Kategorie unten verwenden

Hinweis: Im Gegensatz zu einigen anderen Antworten, dies man berücksichtigt, dass die rect Sie wollen y könnte ziehen in hat = 0. Diejenigen Antworten, die die Rechnung nicht nehmen falsch sind und nicht im allgemeinen Fall arbeiten!.

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)

    }
} 

Verwenden Sie wie folgt aus:

let imageFrame: CGRect = ...
let context: CGContext = ....
let img: CGImage = ..... 
context.drawImage(image: img, inRect: imageFrame)
Beantwortet am 20/09/2019 um 10:24
quelle vom benutzer

stimmen
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()
    }
}
Beantwortet am 03/05/2018 um 12:06
quelle vom benutzer

stimmen
0

Swift 3 Core Graphics-Lösung

Wenn Sie CG verwenden möchten für was auch immer Ihr Grund sein könnte, statt UIImage, diese Konstruktion Swift 3 auf früheren Antworten basierend hat das Problem für mich lösen:

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()
}
Beantwortet am 31/08/2017 um 20:05
quelle vom benutzer

stimmen
0

Im Laufe meines Projektes sprang ich von Kendall Antwort auf Cliff Antwort dieses Problem für Bilder zu lösen , die vom Telefon selbst geladen werden.

Am Ende landete ich mit nach oben CGImageCreateWithPNGDataProviderstatt:

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

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

Diese leidet nicht an den Orientierungs Fragen , die Sie davon ab, die erhalten würden CGImagevon einem UIImageund es kann als der Inhalt eines verwendet werden , CALayerohne Probleme.

Beantwortet am 15/03/2014 um 17:13
quelle vom benutzer

Beantwortet am 26/11/2009 um 13:24
quelle vom benutzer

stimmen
-5

Sie können auch dieses Problem dadurch lösen:

//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.
Beantwortet am 04/04/2012 um 02:55
quelle vom benutzer

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