Подтвердить что ты не робот

Как создать 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, но он будет тратить память, процессор и мощность GPU, поскольку это требуется только для нового 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. (или мы просто позволяем ему оставаться в нижней части стека и не беспокоиться об этом?)

4b9b3361

Ответ 1

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

Если вам нужно использовать 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 x 768 пикселей.

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


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

Ответ 2

Также попробуйте следующее:

- (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;
}

Ответ 3

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

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

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

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

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

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

Ответ 4

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

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

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