CGContextDrawImage kreslí obraz vzhůru nohama, když prošel UIImage.CGImage

hlasů
169

Ví někdo, proč CGContextDrawImageby se vypracování můj obraz vzhůru nohama? Jsem nahrává obraz po mé žádosti:

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

A pak jednoduše žádat základní grafiku nakreslit mé kontextu:

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

To skýtá na správném místě, a rozměry, ale obraz je vzhůru nohama. Musím být něco chybí opravdu zřejmý tady?

Položena 03/02/2009 v 11:23
zdroj uživatelem
V jiných jazycích...                            


18 odpovědí

hlasů
218

Namísto

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

Použití

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

Ve středu vašich začínají koncových / CGcontextmetod.

To bude čerpat snímek ve správné orientaci do svého aktuálního obrazu kontextu - Jsem si docela jistý, že to má co do činění s UIImagelpění na vědomí orientaci, zatímco CGContextDrawImagemetoda dostane podkladových surový obrazových dat s žádným pochopením orientace.

Všimněte si, že budete narazit na tento problém v mnoha oblastech, ale jeden konkrétní příklad se zabývá uživatelskými obrázků adresáře.

Odpovězeno 06/02/2009 v 21:46
zdroj uživatelem

hlasů
211

Dokonce i po nanesení všechno, co jsem již zmínil, jsem ještě měl dramata s obrázky. Na konci jsem právě používá Gimp vytvořit ‚převrácenou vertikální‘ verze všech svých snímků. Teď už nemusíte používat žádné transformace. Doufejme, že to nebude způsobovat další problémy dolů trati.

Ví někdo, proč CGContextDrawImage bude kresba můj obraz vzhůru nohama? Jsem nahrává obraz po mé žádosti:

Quartz2d používá jiný souřadný systém, kde je původ je v levém dolním rohu. Takže když Quartz čerpá pixel x [5], y [10] 100 * 100 obrazu, který pixel je vypracován v levém dolním rohu místo v levém horním rohu. Což způsobuje, že ‚převrácený‘ image.

X koordinovat systém zápasů, takže budete muset otočit y souřadnic.

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

To znamená, že jsme přeložil obraz 0 jednotkách na ose x a výškou obrazu na ose y. Nicméně, to samo o sobě bude znamenat náš obraz je stále vzhůru nohama, jen aby byly vtahovány „image.size.height“ níže, kde jsme ho chtěli být vypracovány.

Programový průvodce Quartz2D doporučuje používat ScaleCTM a předávání záporné hodnoty pro převrácení obrazu. Můžete použít následující kód, jak to udělat -

CGContextScaleCTM(context, 1.0, -1.0);

Kombinují dva těsně před CGContextDrawImagevolání a vy byste měli mít obraz správně vypracovány.

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

Jen si dejte pozor, pokud vaše imageRect koordinuje neodpovídají vašeho obrazu, jak se můžete dostat nezamýšlené výsledky.

Chcete-li převést zpět souřadnic:

CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0, -imageRect.size.height);
Odpovězeno 04/02/2009 v 13:49
zdroj uživatelem

hlasů
19

To nejlepší z obou světů, používat UIImage je drawAtPoint:nebo drawInRect:zároveň s uvedením svůj vlastní kontext:

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

Také se vyhnout úpravou souvislosti s CGContextTranslateCTMnebo CGContextScaleCTMkteré druhá odpověď ano.

Odpovězeno 07/08/2013 v 06:47
zdroj uživatelem

hlasů
8

Příslušná Quartz2D dokumenty: https://developer.apple.com/library/ios/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/GraphicsDrawingOverview/GraphicsDrawingOverview.html#//apple_ref/doc/uid/TP40010156-CH14-SW4

Obracející výchozí souřadnicový systém

Obracející v UIKit výkresu modifikuje doprovodnou CALayer sladit kreslicí prostředí mající LLO souřadnicovém systému s výchozím souřadný systém UIKit. Pokud používáte pouze UIKit metody a funkce pro kreslení, neměli byste muset otočit CTM. Nicméně, pokud si mix core grafiku nebo obrázek I / O funkce volá s UIKit hovory, obracející CTM může být nezbytné.

Konkrétně, pokud si nakreslit obrázek nebo PDF dokument voláním Jádro grafické funkce přímo, objekt je vykreslen vzhůru nohama v kontextu v zobrazení. Musíte otočit CTM správně zobrazit obraz a stránek.

Chcete-li otočit objekt natažený do kontextu jádra grafiky, takže se zobrazí správně při zobrazení v pohledu UIKit, musíte upravit CTM ve dvou krocích. Přeložit počátek do levého horního rohu kreslicí plochy, a potom použijete měřítko překlad, úpravu osa y od -1. Kód pro dělání to vypadá podobně jako následující:

CGContextSaveGState(graphicsContext);
CGContextTranslateCTM(graphicsContext, 0.0, imageHeight);
CGContextScaleCTM(graphicsContext, 1.0, -1.0);
CGContextDrawImage(graphicsContext, image, CGRectMake(0, 0, imageWidth, imageHeight));
CGContextRestoreGState(graphicsContext);
Odpovězeno 08/04/2016 v 06:11
zdroj uživatelem

hlasů
7

Nejsem si jistý, pro UIImage, ale tento druh chování obvykle dochází, když se poloha převrácený. Většina OS X souřadnicové systémy mají svůj počátek v levém dolním rohu, stejně jako v Postscript a PDF. Ale CGImagesouřadnicový systém má svůj počátek v levém horním rohu.

Možná řešení mohou zahrnovat isFlippedvlastnictví nebo scaleYBy:-1afinní transformace.

Odpovězeno 03/02/2009 v 11:34
zdroj uživatelem

hlasů
6

Pokud někdo má zájem v roztoku ne-nasnadě pro tvorbu obrazu ve vlastním obdélníku v kontextu:

 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)

}

Obraz bude upraven tak, aby zaplnil rect.

Odpovězeno 18/02/2016 v 14:20
zdroj uživatelem

hlasů
3

Swift 3.0 & 4.0

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

Není potřeba Změna

Odpovězeno 04/08/2017 v 10:41
zdroj uživatelem

hlasů
3

Můžeme tento problém vyřešit pomocí stejnou funkci:

UIGraphicsBeginImageContext(image.size);

UIGraphicsPushContext(context);

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

UIGraphicsPopContext();

UIGraphicsEndImageContext();
Odpovězeno 25/08/2011 v 07:27
zdroj uživatelem

hlasů
3

UIImageobsahuje CGImagejako hlavní prvek obsahu, jakož i pro změnu měřítka a orientace faktorů. Vzhledem k tomu CGImagea jeho různé funkce jsou odvozeny od OSX, že očekává, že souřadnicový systém, který je vzhůru nohama ve srovnání s iPhone. Při vytváření UIImage, standard je obrácený orientaci ke kompenzaci (můžete změnit!). Použijte. CGImagevlastnost pro přístup k velmi výkonné CGImagefunkce, ale kreslení na iPhone obrazovky apod nejlépe provést pomocí UIImagemetod.

Odpovězeno 01/08/2010 v 00:26
zdroj uživatelem

hlasů
2

Doplňková odpověď s Swift kód

Quartz 2D grafika používá souřadnicový systém s počátkem v levém dolním rohu, zatímco UIKit v iOS používá souřadnicový systém s počátkem v levém horním rohu. Všechno, co se obvykle funguje dobře, ale když dělá některé grafické operace, budete muset změnit souřadnicový systém sami. Tyto dokumentace uvádí:

Některé technologie nastavit jejich grafické kontexty použít jiný výchozí souřadnicový systém, než který používá Quartz. Ve srovnání s Quartz, jako souřadnicový systém je upraven souřadnicový systém a musí být kompenzovány při provádění některých Quartz kreslení operace. Nejběžnější modifikovaný souřadnicový systém klade původ v levém horním rohu kontextu a mění osu y bodu směrem ke spodní části stránky.

Tento jev je možno vidět v těchto dvou případech z vlastních zobrazení, které čerpají obrazu v jejich drawRectmetod.

zadejte popis obrázku zde

Na levé straně je obraz vzhůru nohama a na pravé straně byla přeložena souřadný systém a zmenšen tak, že původ je v levém horním rohu.

Převrácený obraz

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)

}

Modifikovaný systém souřadnic

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)
}
Odpovězeno 22/12/2015 v 03:25
zdroj uživatelem

hlasů
1

drawInRectje jistě způsob, jak jít. Tady je další malá věc, která přijde na způsob, jak užitečné, když dělá. Obvykle je obraz i obdélník, do něhož půjde neodpovídají. V takovém případě drawInRectbude trvat obraz. Zde je rychlý a chladný způsob, jak zajistit, že obraz má poměr stran se nemění, obrácením transformace (což bude, aby se vešly na celou věc):

//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];
Odpovězeno 22/02/2010 v 06:57
zdroj uživatelem

hlasů
0

Stává se to proto, že QuartzCore má souřadnicový systém „v levém dolním rohu“, zatímco UIKit - „vlevo nahoře“.

V případě, že může prodloužit 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)
Odpovězeno 30/09/2019 v 11:28
zdroj uživatelem

hlasů
0

Swift 5 odpověď na základě @ ZpaceZombor je výborná odpověď

Máte-li UIImage, stačí použít

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

Máte-li CGImage použít své kategorii níže

Poznámka: Na rozdíl od některých jiných odpovědí, toto vezmeme v úvahu, že rect chcete kreslit může mít y = 0. těchto odpovědí, které neberou, že v úvahu, jsou nesprávná a nebude pracovat v obecném případě!.

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)

    }
} 

Použít takto:

let imageFrame: CGRect = ...
let context: CGContext = ....
let img: CGImage = ..... 
context.drawImage(image: img, inRect: imageFrame)
Odpovězeno 20/09/2019 v 10:24
zdroj uživatelem

hlasů
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()
    }
}
Odpovězeno 03/05/2018 v 12:06
zdroj uživatelem

hlasů
0

Swift 3 CoreGraphics Řešení

Chcete-li použít CG jakéhokoliv Váš důvod by mohl být namísto UIImage tato Swift 3 Konstrukce vychází z předchozí odpovědi udělal vyřešit problém pro 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()
}
Odpovězeno 31/08/2017 v 20:05
zdroj uživatelem

hlasů
0

V průběhu mého projektu jsem skočil z Kendalla odpovědi na odpověď Cliffa vyřešení tohoto problému pro obrazy, které jsou načteny z paměti telefonu.

Nakonec jsem skončil s použitím CGImageCreateWithPNGDataProvidermísto:

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

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

To netrpí otázkách orientace, které byste si od získání co CGImageod A UIImagea může být použit jako obsah CALayerbez problémů.

Odpovězeno 15/03/2014 v 17:13
zdroj uživatelem

Odpovězeno 26/11/2009 v 13:24
zdroj uživatelem

hlasů
-5

Také můžete tento problém vyřešit tím, že dělá toto:

//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.
Odpovězeno 04/04/2012 v 02:55
zdroj uživatelem

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