Почему мой алгоритм преобразования между индексом и x, y с помощью растровых буферов приводит к вертикальному отражению изображения? - программирование

Почему мой алгоритм преобразования между индексом и x, y с помощью растровых буферов приводит к вертикальному отражению изображения?

При работе с растровыми буферами, такими как:

[50, 50, 50, 255, 50, 50, 50, 255, ...]
[r,  g,  b,  a,   r,  g,  b,  a, ...]

Я часто использую математику так:

let bufferWidth = width * 4;
buffer.forEach((channel, index) => {
    let y = Math.floor(index / bufferWidth);
    let x = Math.floor((index % bufferWidth) / 4);
    let remainder = index % 4;

для вычисления x, y или наоборот для работы с плоскими буферами растровых данных. Почти всегда я получаю перевернутые результаты и, так или иначе, переворачиваю их обратно, но в моих мыслях об этом явно что-то не так.

Что плохого в этой математике, которая может привести к переворачиванию растрового изображения?

Полный код, функция для обрезки растрового изображения:

function crop(
  buffer,
  width,
  height,
  leftLimit,
  rightLimit,
  lowerLimit,
  upperLimit
) {
  let croppedWidth = rightLimit - leftLimit;
  let croppedHeight = upperLimit - lowerLimit;
  let length = croppedHeight * croppedWidth * 4;
  let bufferWidth = width * 4;
  let croppedBuffer = new Uint8Array(length);
  buffer.forEach((channel, index) => {
    let y = Math.floor(index / bufferWidth);
    let x = Math.floor((index % bufferWidth) / 4);
    let remainder = index % 4;
    let yCropped = y - lowerLimit;
    let xCropped = x - leftLimit;
    let indexCropped = yCropped * croppedWidth * 4 + xCropped * 4 + remainder;
    if (
      xCropped >= 0 &&
      xCropped <= croppedWidth &&
      yCropped >= 0 &&
      yCropped <= croppedHeight
    ) {
      croppedBuffer[indexCropped] = buffer[index];
    }
  });
  return croppedBuffer;
}
4b9b3361

Ответ 1

Растровое изображение обычно начинается с нижнего левого угла и продолжается до правого верхнего угла. Но не всегда.

В заголовочном файле растрового изображения есть значение biHeight, если это значение отрицательное, то растровое изображение перевернуто, оно начинается снизу слева. И если это значение положительное, то растровое изображение начинается сверху слева.

Если у вас есть доступ к biHeight просто переверните его значение, чтобы отобразить растровое изображение правой стороной вверх.

Чтобы упростить вычисления, выберите любую действительную точку X/Y, затем найдите соответствующий source_index в буфере, как показано ниже. Скопируйте эту точку в ваш целевой буфер.

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

for(let i = 0; i < bytes_per_pixel; i++)
    buffer_cropped[dst_index + i] = buffer[source_index + i];

Код ниже должен работать для 32-битных изображений (4 байта на пиксель). Обратите внимание, что для 24-битных изображений потребуется заполнение.

if (height < 0) 
    height = -height;
let bytes_per_pixel = 4;
let cropped_x = 10;
let cropped_y = 10;
let cropped_width = width / 2;
let cropped_height = height / 2;

if (new_x < 0 || new_x >= new_width || 
    new_y < 0 || new_y >= new_height) { error... }
if (cropped_width < 1 || cropped_width > width || 
    cropped_height < 1 || cropped_height > height) { error... }

let dst_index = 0;
for(let y = cropped_y; y < cropped_y + cropped_height; y++)
{
    for(let x = cropped_x; x < cropped_x + cropped_width; x++)
    {
        //use for right-side up bitmap:
        //int source_index = (y * width + x) * bytes_per_pixel;
        ////

        //use for upside-down bitmap:
        let source_index = ((height - y - 1)* width + x) * bytes_per_pixel;
        ////

        for(let i = 0; i < bytes_per_pixel; i++)
            buffer_cropped[dst_index + i] = buffer[source_index + i];
        dst_index += bits_per_pixel;
    }
}

Ответ 2

Проверьте заголовок вашего растрового изображения. В случае (сверху вниз) высота должна быть отрицательной. Документация

biHeight Высота растрового изображения в пикселях. Если biHeight положителен, растровое изображение представляет собой DIB снизу вверх, а его источник - левый нижний угол. Если biHeight отрицателен, растровое изображение является DIB сверху вниз, а его источником является левый верхний угол.

Если biHeight отрицателен, указывая на нисходящий DIB, biCompression должен быть или BI_RGB или BI_BITFIELDS. Нисходящие DIB не могут быть сжаты.