Как создать 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();
нет третьего шага.