Я часто рекомендую разработчикам прочитать раздел «NSBitmapImageRep: заметки о соответствии импеданса CoreGraphics и производительность» в 10.6 заметках о выпуске AppKit :
NSBitmapImageRep: Замечания по согласованию импеданса CoreGraphics и производительности
В вышеприведенных примечаниях к выпуску подробно описаны изменения ядра на уровне NSImage для SnowLeopard.Также произошли существенные изменения на уровне NSBitmapImageRep, также для повышения производительности и улучшения согласования импеданса с CoreGraphics.
NSImage - это довольно абстрактное представление изображения.Это в значительной степени просто вещь, которую можно нарисовать, хотя она и менее абстрактна, чем NSView, поскольку она не должна вести себя по-разному в зависимости от аспектов контекста, в которые она втягивается, за исключением качественных решений.Это своего рода непрозрачное утверждение, но его можно проиллюстрировать на примере: если вы рисуете кнопку в области 100x22 по сравнению с областью 22x22, вы можете ожидать, что кнопка будет растягивать свою середину, но не концевые заглавные буквы.Изображение не должно вести себя так (и если вы попробуете это, вы, вероятно, сломаетесь!).Изображение должно всегда линейно и равномерно масштабироваться, чтобы заполнить прямоугольник, в котором оно нарисовано, хотя оно может выбирать представления и тому подобное, чтобы оптимизировать качество для этой области.Аналогично, все представления изображений в NSImage должны представлять один и тот же чертеж.Не упаковывайте какое-либо совершенно другое изображение в качестве представителя.
Это отступление от нас, NSBitmapImageRep - гораздо более конкретный объект.NSImage не имеет пикселей, NSBitmapImageRep делает.NSBitmapImageRep - это фрагмент данных вместе с информацией о формате пикселей и информацией о цветовом пространстве, который позволяет нам интерпретировать данные как прямоугольный массив значений цвета.
Это то же самое, что и CGImage.В SnowLeopard NSBitmapImageRep изначально поддерживается CGImageRef, а не напрямую порцией данных.CGImageRef действительно имеет кусок данных.В то время как в Leopard NSBitmapImageRep, созданный из CGImage, распаковывает и, возможно, обрабатывает данные (что происходит при чтении из растрового файла), в SnowLeopard мы стараемся просто повиснуть на исходном CGImage.
Это имеет некоторыепоследствия производительности.Большинство хороши!Вы должны видеть меньше кодирования и декодирования растровых данных как CGImages.Если вы инициализируете NSImage из файла JPEG, а затем рисуете его в PDF, вы должны получить PDF того же размера, что и исходный JPEG.В Leopard вы увидите PDF размер распакованного изображения.В качестве другого примера, кеши CoreGraphics, включая выгрузку на графическую карту, привязаны к экземплярам CGImage, поэтому чем больше будет использоваться один и тот же экземпляр, тем лучше.
Однако: в некоторой степени быстрые операциис NSBitmapImageRep изменились.CGImages не являются изменяемыми, NSBitmapImageRep является.Если вы измените NSBitmapImageRep, ему, скорее всего, придется скопировать данные из CGImage, включить ваши изменения и упаковать его как новый CGImage.Таким образом, в основном рисование NSBitmapImageRep выполняется быстро, просмотр или изменение его данных пикселей - нет.Это было верно в Leopard, но теперь это более верно.
Вышеуказанные шаги действительно выполняются лениво: если вы делаете что-то, что заставляет NSBitmapImageRep копировать данные из его вспомогательного CGImageRef (например, вызов bitmapData), растровое изображение не будетпереупаковывать данные как CGImageRef до тех пор, пока они не будут нарисованы или пока не потребуется CGImage по какой-либо другой причине.Так что, безусловно, доступ к данным - это не конец света, и это правильная вещь в некоторых обстоятельствах, но в целом вы должны думать о рисовании.Если вы считаете, что хотите работать с пикселями, обратите внимание на CoreImage - это API в нашей системе, который действительно предназначен для обработки пикселей.
Это совпадает с безопасностью.Проблема, которую мы видели с нашими изменениями в SnowLeopard, заключается в том, что приложения довольно любят жестко кодировать растровые форматы.NSBitmapImageRep может иметь размер 8, 32 или 128 бит на пиксель, он может быть плавающей точкой или нет, может быть предварительно умножен или нет, он может иметь или не иметь альфа-канал и т. Д. Эти аспекты определяются с помощью свойств растрового изображения, например-bitmapFormat.К сожалению, если кто-то хочет извлечь bitmapData из экземпляра NSBitmapImageRep, он обычно просто вызывает bitmapData, обрабатывает данные как (скажем) с предварительно умноженным 32-битным RGBA на пиксель и, если кажется, что работает, называет его днем.
Теперь, когда NSBitmapImageRep не обрабатывает данные так часто, как раньше, случайные повторения растровых изображений, которые вы можете получить, могут иметь другие форматы, чем раньше.Некоторые из этих жестко закодированных форматов могут быть неправильными.
Решение - , а не , чтобы попытаться обработать весь диапазон форматов, в которых могут находиться данные NSBitmapImageRep, это слишком сложно.Вместо этого нарисуйте растровое изображение во что-то, формат которого вы знаете , а затем посмотрите на это.
Это выглядит так:
NSBItmapImageRep *bitmapIGotFromAPIThatDidNotSpecifyFormat;
NSBitmapImageRep *bitmapWhoseFormatIKnow = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:width pixelsHigh:height
bitsPerSample:bps samplesPerPixel:spp hasAlpha:alpha isPlanar:isPlanar
colorSpaceName:colorSpaceName bitmapFormat:bitmapFormat bytesPerRow:rowBytes
bitsPerPixel:pixelBits];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmapWhoseFormatIKnow]];
[bitmapIGotFromAPIThatDidNotSpecifyFormat draw];
[NSGraphicsContext restoreGraphicsState];
unsigned char *bitmapDataIUnderstand = [bitmapWhoseFormatIKnow bitmapData];
Это не производит больше копийданные, чем просто доступ к bitmapData объекта bitmapIGotFromAPIThatDidNotSpecifyFormat, поскольку эти данные в любом случае необходимо будет скопировать из резервного CGImage.Также обратите внимание, что это не зависит от того, является ли исходный чертеж растровым.Это способ получить пиксели в известном формате для любого рисунка или просто получить растровое изображение.Это гораздо лучший способ получить растровое изображение, чем, например, вызывать -TIFFRepresentation.Это также лучше, чем блокировка фокуса на NSImage и использование - [NSBitmapImageRep initWithFocusedViewRect:].
Итак, чтобы подвести итог: (1) Рисование выполняется быстро.Играть с пикселями нет.(2) Если вы считаете, что вам нужно поиграть с пикселями, (а) подумайте, есть ли способ сделать это с помощью рисования, или (б) загляните в CoreImage.(3) Если вы все еще хотите получить пиксели, нарисуйте растровое изображение, формат которого вам известен, и посмотрите на эти пиксели.
На самом деле, лучше начать с предыдущего раздела спохожий заголовок - «Сопоставление импеданса NSImage, CGImage и CoreGraphics» - и прочитайте следующий раздел.
Кстати, есть хороший шанс, что обмен изображениями будет работать, но вы просто несинхронизировать их правильно.Вам нужно будет показать код, в котором оба представителя использовались, чтобы мы точно знали.