CGContextDrawImage рисует изображение вверх ногами, когда прошло UIImage.CGImage

голоса
169

Кто - нибудь знает , почему CGContextDrawImageбы обратил мое изображение вверх ногами? Я загрузка изображения в из моего приложения:

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

А потом просто попросив графическое ядро, чтобы привлечь его к моему контексту:

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

Он оказывает в нужном месте, и размеры, но изображение перевернуто. Должно быть, я что-то действительно очевидное здесь не хватает?

Задан 03/02/2009 в 11:23
источник пользователем
На других языках...                            


18 ответов

голоса
218

Вместо

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

использование

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

В середине вашего начала / конца CGcontextметодов.

Это позволит сделать изображение с правильной ориентацией в текущий контекст изображения - я уверен , что это что - то делать с UIImageпроведением на знание ориентации в то время как CGContextDrawImageметод получает исходные данные изображения , лежащие в основе без понимания ориентации.

Обратите внимание, что вы столкнетесь с этой проблемой во многих областях, но один конкретный пример работы с изображениями пользователей адресной книги.

Ответил 06/02/2009 в 21:46
источник пользователем

голоса
211

Даже после применения все, что я уже упоминал, я все еще имел драмы с изображениями. В конце концов, я просто использовал Gimp, чтобы создать «перевернутый вертикальный» вариант всех моих изображений. Теперь мне не нужно использовать какие-либо преображается. Надеюсь, это не будет вызывать дополнительные проблемы вниз по дорожке.

Кто-нибудь знает, почему CGContextDrawImage бы обратил мое изображение вверх ногами? Я загрузка изображения в из моего приложения:

Quartz2d использует другую систему координат, в которой начало координат находится в нижнем левом углу. Поэтому, когда Кварц рисует пиксельные х [5], у [10] 100 * 100 изображений, что пиксель втягивается в нижнем левом углу, а не в верхнем левом углу. Таким образом, в результате чего «перевернутый» изображения.

Система х координатная соответствует, так что вам нужно будет перевернуть Y координаты.

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

Это означает, что мы перевели изображение на 0 единиц по оси х и высоты изображения по оси у. Тем не менее, это само по себе будет означать наше изображение по-прежнему с ног на голову, только втягиваются «image.size.height» ниже, где мы хотим, чтобы это было обращено.

Руководство по программированию Quartz2D рекомендует использовать ScaleCTM и передавая отрицательные значения, чтобы перевернуть изображение. Вы можете использовать следующий код, чтобы сделать это -

CGContextScaleCTM(context, 1.0, -1.0);

Объединение двух непосредственно перед CGContextDrawImageвызовом , и вы должны иметь изображение обращено правильно.

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

Только будьте осторожны, если ваши imageRect координаты не совпадают с вашим изображением, как вы можете получить неожиданные результаты.

Для того, чтобы преобразовать обратно координаты:

CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0, -imageRect.size.height);
Ответил 04/02/2009 в 13:49
источник пользователем

голоса
19

Лучшее из обоих миров, использовать UIImage - х drawAtPoint:или в drawInRect:то же время указав свой собственный контекст:

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

Кроме того, вы не изменять свой контекст с CGContextTranslateCTMили CGContextScaleCTMкоторым второй ответ делает.

Ответил 07/08/2013 в 06:47
источник пользователем

голоса
8

Соответствующие Quartz2D документы: https://developer.apple.com/library/ios/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/GraphicsDrawingOverview/GraphicsDrawingOverview.html#//apple_ref/doc/uid/TP40010156-CH14-SW4

Подавать По умолчанию система координат

Подавать в UIKit чертежу изменяет подкладочный CALayer выравнивать среды рисования, имеющие LLO системы по умолчанию координат системы UIKit координат. Если вы используете только методы и функции UIKit для рисования, вам не нужно переворачивать CTM. Тем не менее, если вы смешиваете графическое ядро ​​или функция I / O звонки с UIKit вызовов, щелкая CTM может потребоваться изображение.

В частности, если вы рисуете изображение или PDF документ, вызвав Core Graphics функции непосредственно, объект оказывается в обратном порядке в контексте представления. Вы должны перевернуть CTM для отображения изображения и страницы правильно.

Для того, чтобы перевернуть объект, нарисованный в ядро ​​графического контекста так, чтобы он корректно отображается, когда отображается в представлении UIKit, вы должны изменить CTM в два этапа. Вы переводите начало координат в верхнем левом углу области рисования, а затем применить масштабный перевод, изменение координаты у на -1. Код для этого выглядит примерно следующим образом:

CGContextSaveGState(graphicsContext);
CGContextTranslateCTM(graphicsContext, 0.0, imageHeight);
CGContextScaleCTM(graphicsContext, 1.0, -1.0);
CGContextDrawImage(graphicsContext, image, CGRectMake(0, 0, imageWidth, imageHeight));
CGContextRestoreGState(graphicsContext);
Ответил 08/04/2016 в 06:11
источник пользователем

голоса
7

Я не уверен , что для UIImage, но такое поведение обычно происходит , когда координаты перевернуты. Большинство OS X системы координат имеют свое происхождение в нижнем левом углу, как в Postscript и PDF. Но CGImageсистема координат имеет свое начало в верхнем левом углу.

Возможные решения могут включать в isFlippedсобственность или scaleYBy:-1аффинное преобразование.

Ответил 03/02/2009 в 11:34
источник пользователем

голоса
6

Если кто-то заинтересованы в никакой опасности решения для нанесения изображения в пользовательском прямоугольнике в контексте:

 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)

}

Изображение будет масштабироваться, чтобы заполнить прямоугольник.

Ответил 18/02/2016 в 14:20
источник пользователем

голоса
3

Swift 3.0 & 4.0

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

Нет необходимости Переделка

Ответил 04/08/2017 в 10:41
источник пользователем

голоса
3

Мы можем решить эту проблему, используя ту же функцию:

UIGraphicsBeginImageContext(image.size);

UIGraphicsPushContext(context);

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

UIGraphicsPopContext();

UIGraphicsEndImageContext();
Ответил 25/08/2011 в 07:27
источник пользователем

голоса
3

UIImageсодержит в CGImageкачестве основного элемента контента, а также масштабирования и ориентации факторов. Так как CGImageи различные его функции являются производными от OSX, он ожидает, что система, которая с ног на голову по сравнению с iPhone координат. При создании UIImage, по умолчанию ориентации перевернутым , чтобы компенсировать (вы можете изменить это!). Использовать . CGImageсвойство для доступа к очень мощным CGImageфункциям, но рисунок на экране и т.д. iPhone лучше всего делать с UIImageметодами.

Ответил 01/08/2010 в 00:26
источник пользователем

голоса
2

Дополнительный ответ с кодом Swift

Quartz 2D - графики используют систему координат с началом в левом нижнем углу , в то время как в UIKit прошивкой использует систему координат с началом в верхнем левом углу . Все , как правило , работает нормально , но при выполнении некоторых операций с графикой, вы должны изменить систему координат самостоятельно. В документации говорится:

Некоторые технологии настраивают свои графические контексты с помощью другой системы по умолчанию, чем тот, которая используется Quartz координат. По сравнению с кварцем, такая система координат представляет собой модифицированную систему координат и должны быть компенсированы при выполнении некоторых операций рисования Quartz. Наиболее распространенные модифицированная система координат помещают начало координат в верхнем левом углу контекста и изменяет ось ординат в точку по направлению к нижней части страницы.

Это явление можно увидеть в следующих двух случаях пользовательских представлений , которые привлекают изображения в своих drawRectметодах.

введите описание изображения здесь

На левой стороне, изображение вверх-вниз и на правой стороне был переведен и масштабировать систему координат так, что начало координат находится в верхнем левом углу.

Перевернутая изображение

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)

}

Модифицированная система координат

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)
}
Ответил 22/12/2015 в 03:25
источник пользователем

голоса
1

drawInRectэто , безусловно, путь. Вот еще одна маленькая вещь , которая придет в путь полезно, делая это. Обычно картина и прямоугольник , в который он собирается пойти не соответствуют. В этом случае drawInRectбудет растягивать изображение. Вот быстрый и хороший способ , чтобы убедиться , что изображение в соотношение сторон не изменяется, при движении задним ходом преобразования (который будет соответствовать все это в):

//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];
Ответил 22/02/2010 в 06:57
источник пользователем

голоса
0

Это происходит потому, что QuartzCore имеет систему координат «нижний левый», а UIKit - «верхний левый».

В случае , если вы можете продлить 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)
Ответил 30/09/2019 в 11:28
источник пользователем

голоса
0

Swift 5 ответ основан на @ ZpaceZombor это отличный ответ

Если у вас есть UIImage, просто использовать

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

Если у вас есть CGImage использовать свою категорию ниже

Примечание: В отличие от некоторых других ответов, это один принимает во внимание, что прямоугольник вы хотите, чтобы привлечь, возможно, у = 0. Те ответы, которые не принимают во внимание, что неверны и не будут работать в общем случае!.

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)

    }
} 

Используйте как это:

let imageFrame: CGRect = ...
let context: CGContext = ....
let img: CGImage = ..... 
context.drawImage(image: img, inRect: imageFrame)
Ответил 20/09/2019 в 10:24
источник пользователем

голоса
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()
    }
}
Ответил 03/05/2018 в 12:06
источник пользователем

голоса
0

Swift 3 CoreGraphics Solution

Если вы хотите использовать CG для независимо от вашей причина может быть, вместо UIImage, эта Swift 3 конструкция на основе предыдущих ответов сделали решить вопрос для меня:

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()
}
Ответил 31/08/2017 в 20:05
источник пользователем

голоса
0

В ходе моего проекта я подпрыгнул от ответа Кендалла в ответ Клиффа , чтобы решить эту проблему для изображений, которые загружаются с самого телефона.

В конце концов , я закончил с использованием CGImageCreateWithPNGDataProviderвместо этого:

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

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

Это не страдает от проблем ориентации , которые вы получите от получения CGImageот А , UIImageи он может быть использован в качестве содержимого CALayerбез сучка и задоринки.

Ответил 15/03/2014 в 17:13
источник пользователем

Ответил 26/11/2009 в 13:24
источник пользователем

голоса
-5

Вы также можете решить эту проблему, делая это:

//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.
Ответил 04/04/2012 в 02:55
источник пользователем

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