Работа с заполнением в файле BMP в C

Я пытаюсь отредактировать файл BMP на языке C. Мой код работает для файлов BMP без заполнения, но у меня возникают проблемы с заполнением.

Есть несколько других вопросов о файлах BMP, которые я прочитал, но большинство из них используют другие языки, такие как C# и Java, поэтому я не нашел их очень полезными.

Вот как выглядит массив пикселей, но гораздо больше:

char bmpArray[MAX] = {B,G,R,B,G,R,B,G,R,0,0,0,
                      B,G,R,B,G,R,B,G,R,0,0,0,
                      B,G,R,B,G,R,B,G,R,0,0,0}

Нули предназначены для заполнения байтов, чтобы каждая строка делилась на 4, это зависит от ширины пикселя изображения. Я пытаюсь сделать так, чтобы эти байты заполнения оставались в том же положении и имели дело только с байтами B,G,R. Если я применю правки к значениям отступа, полученное изображение будет искажено.

Я сделал функцию, которая генерирует количество байтов заполнения на основе ширины. Использует эту формулу 4 - ((width * 3) % 4) и это работает, как я тестировал это с изображениями с различной шириной.

Я успешно извлек данные B,G,R из BMP-файла и поместил их в массив, поэтому я опубликую только ту часть кода, с которой у меня возникли проблемы.

int c = 0;
for (int a = 0; a < height; a++) {
    for (int b = 0; b < width*3; b++) {
        if (bmpArray[a*(width*3)+b] < 127) {
            bmpArray[a*(width*3)+b] = 0;
        } else {
            bmpArray[a*(width*3)+b] = 255;
        }
        c++;
    }
    for (int pad = 0; pad < padding; pad++) {
        bmpArray[c++] = 0x00;
    }
}

То, что я пытаюсь сделать, это "нарисовать" каждую строку выходного файла BMP, а затем остановиться, как только я достигну конца строки, то есть ширины *3, а затем нарисовать байты заполнения перед переходом к следующей строке пикселей.

В качестве альтернативы, есть ли способ, которым я могу идентифицировать пиксели заполнения, используя один цикл for, а затем использовать оператор if, чтобы не изменять пиксели заполнения? Например:

for (int a = 0; a < bmpArraySize; a++) {
    paddingBytes = ??? //for example for the first row
                       // paddingBytes are i + width*3 + 1
                       // and i + width*3 + 2 and i + width*3 + 3 if padding = 3
    if (a = paddingBytes) {
        bmpArray[a] = 0x00;
    }
    else if (bmpArray[a] < 127) {
        bmpArray[a] = 0;
    }
    else {
        bmpArray[a] = 255;
    }
}

3 ответа

Решение

Проблема в этой части:

int c = 0;
for (int a = 0; a < height; a++) {
    for (int b = 0; b < width*3; b++) {
        if (bmpArray[a*(width*3)+b] < 127) {
            bmpArray[a*(width*3)+b] = 0;
        } else {
            bmpArray[a*(width*3)+b] = 255;
        }
    }
    for (int pad = 0; pad < padding; pad++) {
        bmpArray[c++] = 0x00;  /* ONLY HERE is 'c' updated! */
    }
}

В конце каждой строки вы заполняете заполнение, начиная с c, который начинается в 0 и так перезаписывает первые несколько байтов первой строки. Затем каждая следующая строка копируется, но вы продолжаете перезаписывать с самого начала (где c изначально указывал на).

Заполнение должно быть добавлено в каждой строке. В циклах вы настраиваете a а также b но вы забыли настроить для заполнения.

Я предлагаю более простой код (непроверенный!):

for (int a = 0; a < height; a++) {
    for (int b = 0; b < width*3; b++) {
        if (bmpArray[a*(width*3 + padding)+b] < 127) {
            bmpArray[a*(width*3 + padding)+b] = 0;
        } else {
            bmpArray[a*(width*3 + padding)+b] = 255;
        }
    }
    for (int pad = 0; pad < padding; pad++) {
        bmpArray[a*(width*3 + padding) + 3*width + pad] = 0x00;
    }
}

Есть ли способ, которым я могу идентифицировать отступы пикселей..

Да - в моем цикле выше с настройками для заполнения, он автоматически пропускает сам отступ. Вы можете безопасно удалить явный цикл 'set padding to 0' в конце.

Что-то вроде:

...

int originalLineSize = width * 3;
int workLineSize = originalLineSize + 4 - originalLineSize % 4;

for (int a = 0; a < bmpArraySize; ++a) {
    if ((a % workLineSize) >= originalLineSize)
        bmpArray[a] = 0x00;
    }
    else if (bmpArray[a] < 127) {
        bmpArray[a] = 0;
    ...
}

"Заполняющие байты" - это байты, следующие за пикселями строки развертки. Вас интересует не столько отступ, сколько размер строки развертки и размер пикселя:

iScanlineSize  = ((width * bitsperpixel) + 31) / 32 * 4;
iBytesperPixel = bitsperpixel / 8;

Теперь вы можете зацикливаться на линиях сканирования и адресовать пиксели и их части (цвета) следующим образом:

for (int a = 0; a < height; a++) {
    for (int b = 0; b < width; b++) {
        for (int c = 0; c < iBytesperPixel; c++) {
            pixelPart= bmpArray[a*iScanlineSize + b*iBytesperPixel + c];
        }
    ]
}
Другие вопросы по тегам