CGContextDrawImage rysuje obraz do góry nogami, gdy przeszedł UIImage.CGImage

głosy
169

Czy ktoś wie dlaczego CGContextDrawImagebyłyby rysunek mój obraz do góry nogami? Wczytuję obrazu w mojej aplikacji:

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

A potem po prostu pytając rdzeń graficzny zwrócić go do mojego kontekstu:

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

To sprawia, we właściwym miejscu i wymiary, ale obraz jest do góry nogami. I musi być brakuje czegoś naprawdę oczywiste tutaj?

Utwórz 03/02/2009 o 11:23
źródło użytkownik
W innych językach...                            


18 odpowiedzi

głosy
218

Zamiast

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

Posługiwać się

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

W środku swoimi rozpoczęcia / zakończenia CGcontextmetod.

Będzie to narysować obraz z zachowaniem prawidłowej orientacji w aktualnym kontekście obrazu - Jestem prawie pewien, że to ma coś wspólnego z UIImagegospodarstwa na wiedzy o orientacji podczas gdy CGContextDrawImagemetoda pobiera dane bazowe surowy obraz bez zrozumienia orientacji.

Pamiętaj, że napotkasz ten problem w wielu dziedzinach, ale jeden konkretny przykład do czynienia z obrazami użytkowników z książki adresowej.

Odpowiedział 06/02/2009 o 21:46
źródło użytkownik

głosy
211

Nawet po zastosowaniu wszystko już wspomniałem, mam jeszcze dramaty z obrazami. W końcu, ja tylko używane Gimp aby stworzyć „pionową odbitym” wersję wszystkich moich obrazów. Teraz nie trzeba używać żadnych transformacje. Mam nadzieję, że nie będzie to powodować dalsze problemy w dół toru.

Czy ktoś wie dlaczego CGContextDrawImage byłby rysunek mój obraz do góry nogami? Wczytuję obrazu w mojej aplikacji:

Quartz2d używa innego układu współrzędnych, gdzie pochodzenie jest w lewym dolnym rogu. Tak więc, gdy kwarcowy zwraca pikseli x [5], R [10] 100 * 100 obrazu, że piksel jest przedstawione w lewym dolnym rogu zamiast w lewym górnym rogu. Powodując „odbitym” obraz.

System x koordynować mecze, więc trzeba będzie przerzucić y współrzędne.

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

Oznacza to, tłumaczyliśmy obraz przez 0 jednostek na osi X i przez wysokość obrazów na osi y. Jednak ta sama oznaczać będzie nasz wizerunek jest jeszcze do góry nogami, tylko wciągnięcia „image.size.height” poniżej, gdzie go chcą być rysowane.

Przewodnik programowania Quartz2D zaleca stosowanie ScaleCTM i przekazywanie wartości ujemne, aby odwrócić obraz. Można użyć następującego kodu, aby to zrobić -

CGContextScaleCTM(context, 1.0, -1.0);

Połączyć dwa tuż przed CGContextDrawImagerozmowy i trzeba mieć obraz sporządzony prawidłowo.

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

Wystarczy być ostrożnym, jeśli imageRect koordynuje nie zgadzać się z obrazu, jak można dostać niezamierzonych rezultatów.

Aby przekonwertować z powrotem współrzędne:

CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0, -imageRect.size.height);
Odpowiedział 04/02/2009 o 13:49
źródło użytkownik

głosy
19

Najlepsze z obu światów, należy użyć UIImage użytkownika drawAtPoint:lub drawInRect:jednocześnie podając swój własny kontekst:

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

Również uniknąć modyfikując swój kontekst z CGContextTranslateCTMlub CGContextScaleCTMktórych druga odpowiedź jest.

Odpowiedział 07/08/2013 o 06:47
źródło użytkownik

głosy
8

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

Odbijanie domyślnego układu współrzędnych

Odbijanie w UIKit rysunku modyfikuje CALayer podkładową w celu dostosowania środowiska rysunkowego posiadające LLO systemu z domyślnego systemu współrzędnych UIKit współrzędnych. Jeśli używasz tylko metod i funkcji UIKit do rysowania, nie ma potrzeby, aby odwrócić CTM. Jeśli jednak mieszać rdzeń graficzny lub obraz I / O funkcja połączeń z połączeń UIKit, przerzucanie CTM może być konieczne.

W szczególności, jeśli narysować obraz lub dokument PDF poprzez wywołanie funkcji rdzenia graficznego bezpośrednio, obiekt staje do góry nogami w kontekście widoku za. Musisz odwrócić CTM do wyświetlania obrazu i stron prawidłowo.

Aby obrócić obiekt narysowany w kontekście rdzeń graficzny tak, że wydaje się prawidłowo, gdy wyświetlane w widoku UIKit, należy zmodyfikować CTM w dwóch etapach. Tłumaczyć pochodzenie do lewego górnego rogu obszaru rysunku, a następnie zastosować tłumaczenie skalę, modyfikując współrzędną y przez -1. Kod dla tej operacji wygląda podobnie do następującego:

CGContextSaveGState(graphicsContext);
CGContextTranslateCTM(graphicsContext, 0.0, imageHeight);
CGContextScaleCTM(graphicsContext, 1.0, -1.0);
CGContextDrawImage(graphicsContext, image, CGRectMake(0, 0, imageWidth, imageHeight));
CGContextRestoreGState(graphicsContext);
Odpowiedział 08/04/2016 o 06:11
źródło użytkownik

głosy
7

Nie jestem pewien na UIImage, ale tego rodzaju zachowanie zazwyczaj pojawia się, gdy współrzędne są odwrócone. Większość OS X koordynować systemy mają swoje źródło w lewym dolnym rogu, tak jak w Postscript i PDF. Ale CGImageukład współrzędnych ma swoje źródło w lewym górnym rogu.

Możliwe rozwiązania mogą obejmować isFlippedwłasność lub scaleYBy:-1affine przekształcać.

Odpowiedział 03/02/2009 o 11:34
źródło użytkownik

głosy
6

Jeśli ktoś jest zainteresowany w roztworze nie myślenia do rysowania obrazu w niestandardowym rect w kontekście:

 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 zostanie przeskalowany, aby wypełnić rect.

Odpowiedział 18/02/2016 o 14:20
źródło użytkownik

głosy
3

Swift 3.0 i 4.0

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

Zmiana bez bólu

Odpowiedział 04/08/2017 o 10:41
źródło użytkownik

głosy
3

Możemy rozwiązać ten problem stosując tę ​​samą funkcję:

UIGraphicsBeginImageContext(image.size);

UIGraphicsPushContext(context);

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

UIGraphicsPopContext();

UIGraphicsEndImageContext();
Odpowiedział 25/08/2011 o 07:27
źródło użytkownik

głosy
3

UIImagezawiera CGImagejako główny członka treści, jak również skalowania i orientacji czynników. Ponieważ CGImagei jego różne funkcje pochodzą z OSX, to spodziewa się, że system, który jest do góry nogami w porównaniu do iPhone współrzędnych. Po utworzeniu UIImage, to domyślnie orientacji góry nogami, aby zrekompensować (można to zmienić!). Użyj . CGImageNieruchomość aby uzyskać dostęp do bardzo zaawansowanych CGImagefunkcji, ale rysunek na iPhone ekranu itp najlepiej zrobić z UIImagemetod.

Odpowiedział 01/08/2010 o 00:26
źródło użytkownik

głosy
2

Odpowiedź miejski z kodem Swift

Grafika 2D kwarcowy użyć układu współrzędnych z pochodzenia w lewym dolnym rogu podczas UIKit w iOS wykorzystuje układ współrzędnych o początku w lewym górnym rogu. Wszystko zazwyczaj działa dobrze, ale gdy robi pewne operacje graficzne, trzeba zmodyfikować system sam współrzędnych. W dokumentacji czytamy:

Niektóre technologie skonfigurować ich kontekstów graficznych przy użyciu innego systemu niż domyślny jeden używany przez Quartz współrzędnych. Względem kwarc, taki układ jest zmodyfikowany układ współrzędnych i muszą zostać zrekompensowane podczas wykonywania pewnych operacji rysowania kwarcu. Najczęstszym zmodyfikowany układ współrzędnych miejsca początku w górnym lewym rogu kontekstu i zmienia oś y, punkt w kierunku dolnej strony.

Zjawisko to można zaobserwować w dwóch następujących przypadkach widoków niestandardowych narysować obraz w swoich drawRectmetod.

wprowadzić opis obrazu tutaj

Z lewej strony, obraz jest do góry nogami, a po prawej stronie układ współrzędnych został przetłumaczony i skalowane tak, że pochodzenie jest w lewym górnym rogu.

obraz do góry nogami

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)

}

Zmodyfikowane układu współrzędnych

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)
}
Odpowiedział 22/12/2015 o 03:25
źródło użytkownik

głosy
1

drawInRectjest z pewnością droga. Oto kolejna mała rzecz, która przyjdzie w sposób użyteczny, gdy to robi. Zwykle obraz i prostokąt, w którym jest ona pójdzie nie są zgodne. W tym przypadku drawInRectbędzie rozciągnąć obraz. Oto szybki i fajny sposób, aby upewnić się, że obraz jest aspekt racja nie ulega zmianie, przez odwrócenie transformacji (który będzie pasować w całość):

//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];
Odpowiedział 22/02/2010 o 06:57
źródło użytkownik

głosy
0

Zdarza się, ponieważ QuartzCore ma układ współrzędnych "bottom-left", a UIKit - "top-left".

W przypadku można rozszerzyć 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)
Odpowiedział 30/09/2019 o 11:28
źródło użytkownik

głosy
0

Swift 5 odpowiedzią na podstawie @ ZpaceZombor doskonała odpowiedź

Jeśli masz UIImage, wystarczy użyć

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

Jeśli masz CGImage używać poniżej mojej kategorii

Uwaga: W przeciwieństwie do niektórych innych odpowiedzi, to weźmie się pod uwagę fakt, że rect chcesz przyciągnąć może mieć y = 0. te odpowiedzi, które nie biorą pod uwagę, że są błędne i nie będzie działać w ogólnym przypadku!.

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)

    }
} 

Używać tak:

let imageFrame: CGRect = ...
let context: CGContext = ....
let img: CGImage = ..... 
context.drawImage(image: img, inRect: imageFrame)
Odpowiedział 20/09/2019 o 10:24
źródło użytkownik

głosy
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()
    }
}
Odpowiedział 03/05/2018 o 12:06
źródło użytkownik

głosy
0

Swift 3 CoreGraphics Rozwiązanie

Jeśli chcesz korzystać z CG dla bez względu na powód może być, zamiast UIImage, to Swift 3 konstrukcja oparta na poprzednich odpowiedzi nie rozwiązuje problemu dla mnie:

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()
}
Odpowiedział 31/08/2017 o 20:05
źródło użytkownik

głosy
0

W trakcie mojego projektu Skoczyłem z odpowiedzią Kendalla do odpowiedzi Cliffa aby rozwiązać ten problem w przypadku obrazów, które są ładowane z samego telefonu.

W końcu skończyło się używając CGImageCreateWithPNGDataProviderzamiast:

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

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

To nie cierpi z powodu kwestii orientacji, które można uzyskać z uzyskiwanie CGImageod A UIImagei może być używany jako zawartość CALayerbez żadnych przeszkód.

Odpowiedział 15/03/2014 o 17:13
źródło użytkownik

Odpowiedział 26/11/2009 o 13:24
źródło użytkownik

głosy
-5

Można również rozwiązać ten problem w ten sposób:

//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.
Odpowiedział 04/04/2012 o 02:55
źródło użytkownik

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