Как bytesPerRow вычисляется из NSBitmapImageRep

Я хотел бы понять, как вычисляется «bytesPerRow» при создании NSBitmapImageRep (в моем случае из сопоставления массива поплавков с растровым изображением в градациях серого).

Уточнение этой детали поможет мне понять, как память отображается из массива чисел с плавающей запятой в массив байтов (0-255, unsigned char; ни один из этих массивов не показан в приведенном ниже коде).

В документации Apple говорится, что это число рассчитывается «на основе ширины изображения, количества битов на выборку и, если данные представлены в сетке, количества выборок на пиксель».

У меня возникли проблемы с выполнением этого «расчета», поэтому я настроил простой цикл, чтобы найти результаты эмпирическим путем. Следующий код работает нормально:

int Ny = 1; // Ny is arbitrary, note that BytesPerPlane is calculated as we  would expect = Ny*BytesPerRow;
for (int Nx = 0; Nx<320; Nx+=64) {
    // greyscale image representation:
    NSBitmapImageRep *dataBitMapRep = [[NSBitmapImageRep alloc]
       initWithBitmapDataPlanes: nil // allocate the pixel buffer for us
       pixelsWide: Nx 
       pixelsHigh: Ny
       bitsPerSample: 8
       samplesPerPixel: 1  
       hasAlpha: NO
       isPlanar: NO 
       colorSpaceName: NSCalibratedWhiteColorSpace // 0 = black, 1 = white
       bytesPerRow: 0  // 0 means "you figure it out"
       bitsPerPixel: 8]; // bitsPerSample must agree with samplesPerPixel
    long rowBytes = [dataBitMapRep bytesPerRow];
    printf("Nx = %d; bytes per row = %lu \n",Nx, rowBytes);
}

и выдает результат:

Nx = 0; bytes per row = 0 
Nx = 64; bytes per row = 64 
Nx = 128; bytes per row = 128 
Nx = 192; bytes per row = 192 
Nx = 256; bytes per row = 256 

Итак, мы видим, что байты/строка прыгают с шагом 64 байта, даже когда Nx постепенно увеличивается на 1 до 320 (я не показывал все эти значения Nx). Отметим также, что Nx = 320 (макс.) является произвольным для данного обсуждения.

Итак, с точки зрения выделения и отображения памяти для массива байтов, как вычисляются «байты на строку» из первых принципов? Является ли результат выше, чтобы данные из одной строки сканирования могли быть выровнены по границе длины «слова» (64 бита на моем MacBook Pro)?

Спасибо за любые идеи, с трудом представляю, как это работает.


person Bruce Dean    schedule 05.02.2012    source источник
comment
Будьте осторожны, не перепутайте 64 байта с 64 битами.   -  person Peter Hosey    schedule 05.02.2012
comment
Да, спасибо, кажется, я часто так делаю.   -  person Bruce Dean    schedule 06.02.2012


Ответы (1)


Передача 0 для bytesPerRow: означает больше, чем вы сказали в своем комментарии. Из документации:

Если вы передадите rowBytes значение 0, выделенные растровые данные могут быть дополнены, чтобы попасть в длинное слово или большие границы для производительности. … Передача ненулевого значения позволяет указать точное продвижение строки.

Таким образом, вы видите, что он увеличивается на 64 байта за раз, потому что именно так AppKit решил округлить его.

Минимальные требования к байтам на строку намного проще. Это байты на пиксель, умноженные на пиксели в строке. Это все.

Для представления растрового изображения, поддерживаемого числами с плавающей запятой, вы должны передать sizeof(float) * 8 вместо bitsPerSample, а количество байтов на пиксель будет равно sizeof(float) * samplesPerPixel. Из этого следует количество байтов на строку; вы умножаете байты на пиксель на ширину в пикселях.

Точно так же, если он поддерживается байтами без знака, вы должны передать sizeof(unsigned char) * 8 вместо bitsPerSample, а количество байтов на пиксель будет sizeof(unsigned char) * samplesPerPixel.

person Peter Hosey    schedule 05.02.2012
comment
Привет, Питер, спасибо за ваши уточняющие комментарии. Для моего приложения я использую последний вариант (поддерживаемый байтами без знака), поэтому bitsPerSample = 1*8 = 8 и bytesPerPixel = 1*8 = 8, как в моем коде выше, и это работает нормально (изображение отображается нормально). Я просто не мог рассчитать из первых принципов, как AppKit достигает своих значений для BytesPerRow, и поэтому не мог сказать, что понимаю это. Я получаю BytesPerRow = Nx*bytesPerPixel = 320*8 = 2560, кажется огромным по сравнению с Appkit (= 320). Итак, я пытался выяснить, откуда взялась 320, и, кроме того, есть ли какие-либо преимущества в использовании 2560? Еще раз спасибо. - person Bruce Dean; 05.02.2012
comment
@BruceDean: вы умножаете в 8 раз больше, чем должны. Учитывая 8 бит (= 1 байт) на выборку и 1 выборку на пиксель, получается 1 байт на пиксель, а не 8. - person Peter Hosey; 05.02.2012