Как создать CGBitmapContext, который работает для отображения Retina и не тратит место для обычного отображения?

Правда ли, что если это в UIKit, в том числе drawRect, аспект HD дисплея Retina обрабатывается автоматически? Так значит ли это в drawRectтекущий контекст графики для вида 1024 x 768 на самом деле является растровым контекстом размером 2048 x 1536 пикселей?

(Обновление: если я создаю изображение, используя текущий контекст в drawRect и распечатать его размер:

CGContextRef context = UIGraphicsGetCurrentContext();
CGImageRef image = CGBitmapContextCreateImage(context);
NSLog(@"width of context %i", (int) CGImageGetWidth(image));
NSLog(@"height of context %i", (int) CGImageGetHeight(image));

затем на новом iPad с отключенной строкой состояния печатаются 2048 и 1536, а на iPad 2 будут отображаться 1024 и 768)

Мы действительно наслаждаемся роскошью в 1 балл = 4 пикселя, которая автоматически обрабатывается для нас.

Тем не менее, если мы используем CGBitmapContextCreateтогда это действительно будут пиксели, а не точки? (по крайней мере, если мы предоставим буфер данных для этого растрового изображения, размер буфера (число байтов) явно не для более высокого разрешения, а для стандартного разрешения, и даже если мы передадим NULL в качестве буфера, так что CGBitmapContextCreate обрабатывает буфер для нас, размер, вероятно, такой же, как если бы мы передавали буфер данных, и это просто стандартное разрешение, а не разрешение Retina).

Мы всегда можем создать 2048 x 1536 для iPad 1 и iPad 2, а также для нового iPad, но это приведет к потере памяти, ресурсов процессора и графического процессора, так как это необходимо только для нового iPad.

Так что мы должны использовать if () { } else { } создать такой растровый контекст и как мы на самом деле это делаем? И весь наш код CGContextMoveToPoint должен быть настроен для отображения Retina для использования x * 2 а также y * 2 против не-сетчатки отображения только с помощью x, y также? Это может быть довольно грязно для кода. (или, может быть, мы можем определить локальную переменную scaleFactor и установите его [[UIScreen mainScreen] scale] так что это 1 для стандартного разрешения и 2, если это сетчатка x а также y всегда будет x * scaleFactor, y * scaleFactor вместо просто x а также y когда мы рисуем, используя CGContextMoveToPoint, так далее.)

Кажется, что UIGraphicsBeginImageContextWithOptions Я могу создать его для Retina автоматически, если передается масштаб 0.0, но я не думаю, что его можно использовать, если мне нужно создать контекст и сохранить его (и использовать ivar или свойство UIViewController для его хранения). Если я не выпущу его с помощью UIGraphicsEndImageContextзатем он остается в стеке графического контекста, поэтому мне кажется, что я должен использовать CGBitmapContextCreate вместо. (или мы просто позволяем ему оставаться на дне стека и не беспокоиться об этом?)

4 ответа

Решение

Проведя дополнительные исследования, я нашел следующее решение:

Если вы должны использовать CGBitmapContextCreateзатем есть два шага, которые могут сделать контекст с системой размеров и координат, адаптированной к стандартному дисплею или дисплею Retina:

float scaleFactor = [[UIScreen mainScreen] scale];

CGSize size = CGSizeMake(768, 768);

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

CGContextRef context = CGBitmapContextCreate(NULL, 
                           size.width * scaleFactor, size.height * scaleFactor, 
                           8, size.width * scaleFactor * 4, colorSpace, 
                           kCGImageAlphaPremultipliedFirst);

CGContextScaleCTM(context, scaleFactor, scaleFactor);

Пример - создать область точек 768 x 768, а на новом iPad она будет 1536 x 1536 пикселей. На iPad 2 это 768 х 768 пикселей.

Ключевым фактором является то, что, CGContextScaleCTM(context, scaleFactor, scaleFactor); используется для настройки системы координат, так что любой рисунок с помощью Core Graphics, например CGContextMoveToPointи т. д. будут работать автоматически, независимо от того, является ли это стандартным разрешением или разрешением Retina.


Еще одно замечание, что UIGraphicsBeginImageContext(CGSizeMake(300, 300)); создаст 300 x 300 пикселей на дисплее Retina, а UIGraphicsBeginImageContextWithOptions(CGSizeMake(300, 300), NO, 0.0); создаст 600 х 600 пикселей на дисплее Retina. 0.0 для вызова метода, чтобы автоматически дать правильный размер для стандартного дисплея или дисплея Retina.

Также попробуйте это:

- (UIImage *)maskImageWithColor:(UIColor *)color
{
    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
    UIGraphicsBeginImageContextWithOptions(rect.size, NO, self.scale);
    CGContextRef c = UIGraphicsGetCurrentContext();
    [self drawInRect:rect];
    CGContextSetFillColorWithColor(c, [color CGColor]);
    CGContextSetBlendMode(c, kCGBlendModeSourceAtop);
    CGContextFillRect(c, rect);
    UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return result;
}

Начав новый контекст изображения, вы можете получить его, используя UIGraphicsGetCurrentContext, Затем, если вы хотите повесить его и использовать повторно после этого, просто сохраните его, как любой другой объект CF (и не забывайте выпускать его, когда вы закончите с ним, в соответствии с правилами). Вам все еще нужно позвонить UIGraphicsEndImageContext чтобы вытащить его из стека контекста UIKit, но если вы сохранили контекст, то контекст будет жить дальше, и вы сможете продолжать использовать его, пока не отпустите.

Позже, если вы хотите снова использовать контекст (и еще не выпустили его), одним из способов будет вызов UIGraphicsPushContext, который вернет контекст обратно в стек контекста.

Другой способ использования контекста - предположить, что это CGBitmapContext (документы UIKit называют его "растровым контекстом", но не называют CGBitmapContext по имени) и используют CGBitmapContextCreateImage захватить новое изображение из контекста после рисования.

Основное отличие состоит в том, что, если вы создали контекст с UIGraphicsCreateImageContextWithOptions, UIGraphicsGetImageFromCurrentImageContext возвращает UIImage которого scale должно соответствовать значению, с которым вы создали контекст. (Я не знаю, будет ли сохранено это значение масштаба, если вы откроете контекст, а затем вернете его обратно.) CGBitmapContextCreateImage возвращает CGImage, а CGImage знает только пиксели.

Другое отличие состоит в том, что API рисования UIKit, такие как UIBezierPath, работают с текущим контекстом в стеке контекста UIKit. Таким образом, если вы не перемещаете контекст, вы можете использовать только API-интерфейсы Quartz для рисования в контексте.

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

Просто создайте контекст с масштабированием 0.0, чтобы получить главный экран с:

UIGraphicsBeginImageContextWithOptions(size,NO,0.0);
CGContextRef context = UIGraphicsGetCurrentContext();

нет третьего шага.

Другие вопросы по тегам