CGContextDrawImage рисует изображение вверх ногами при передаче UIImage.CGImage - PullRequest
177 голосов
/ 03 февраля 2009

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

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

А затем просто попросите основную графику нарисовать ее в моем контексте:

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

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

Ответы [ 15 ]

232 голосов
/ 06 февраля 2009

вместо

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

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

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

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

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

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

213 голосов
/ 04 февраля 2009

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

Кто-нибудь знает почему CGContextDrawImage будет рисовать мой изображение с ног на голову? Я загружаю изображение из моего приложения:

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

Система координат x совпадает, поэтому вам нужно будет перевернуть координаты y.

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

Это означает, что мы перевели изображение на 0 единиц по оси x и по высоте изображений по оси y. Однако это само по себе будет означать, что наше изображение все еще вверх дном, просто рисуем «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);
20 голосов
/ 07 августа 2013

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

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

Также вы избегаете изменения контекста с помощью CGContextTranslateCTM или CGContextScaleCTM, что делает второй ответ.

10 голосов
/ 08 апреля 2016

Соответствующие документы 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. Однако если вы смешиваете вызовы функций Core Graphics или Image I / O с вызовами UIKit, может потребоваться переключение CTM.

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

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

CGContextSaveGState(graphicsContext);
CGContextTranslateCTM(graphicsContext, 0.0, imageHeight);
CGContextScaleCTM(graphicsContext, 1.0, -1.0);
CGContextDrawImage(graphicsContext, image, CGRectMake(0, 0, imageWidth, imageHeight));
CGContextRestoreGState(graphicsContext);
7 голосов
/ 03 февраля 2009

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

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

6 голосов
/ 18 февраля 2016

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

 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)

}

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

3 голосов
/ 04 августа 2017

Swift 3.0 и 4.0

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

Никаких изменений не требуется

3 голосов
/ 22 декабря 2015

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

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

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

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

enter image description here

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

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

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)
}
3 голосов
/ 25 августа 2011

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

UIGraphicsBeginImageContext(image.size);

UIGraphicsPushContext(context);

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

UIGraphicsPopContext();

UIGraphicsEndImageContext();
3 голосов
/ 01 августа 2010

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

...