Я прочитал BMP файл в массив байтов. Поэтому после изучения ответов на этот вопрос: массив байтов - int c++. Является ли этот способ безопасным и правильным получать ширину и высоту из заголовка информации BMP?
long width, height;
memcpy(&width, &bmp[0x12], sizeof(long));
memcpy(&height, &bmp[0x16], sizeof(long));
И какие проблемы может возникнуть при таком подходе?
long* width = (long*)(&bmp[0x12]);
long* height= (long*)(&bmp[0x16]);
Согласно файлу формата Википедии BMP, 0x12
является смещением ширины растрового изображения в пикселях, а 0x16
- смещением высоты растрового изображения в пикселях.
Оплаченный Я нашел это решение для загрузки растрового изображения из буфера памяти, но я хочу, чтобы код был простым, потому что мне нужна только ширина, высота и необработанные данные растрового изображения, и я не знаю, является ли этот ответ безопасным.
Благодарю!
Оба подхода делают одно и то же, и обе имеют одну и ту же основную проблему: не будут работать, если хост-система имеет другой порядок байтов, чем использует файл BMP. Это всегда проблема с прямым доступом к значениям, превышающим один байт в двоичном формате.
Последний подход также имеет дополнительный недостаток возможного нарушения, если хост не может выполнять long
доступ к результирующим адресам.
Короче говоря, оба "решения" плохи. Лучше извлекать значение по байтам и повторно указывать его:
static uint32_t read_uint32_t(const uint8_t *buffer, size_t offset)
{
buffer += offset;
const uint32_t b0 = *buffer++;
const uint32_t b1 = *buffer++;
const uint32_t b2 = *buffer++;
const uint32_t b3 = *buffer++;
return (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
}
В приведенном выше примере используется smidgeon C99 для краткости, перенос его обратно на C89 тривиален.
Редактировать: вышеупомянутое работает над любой дугой, поскольку он больше не выполняет прямой доступ. Вместо этого он предполагает, что buffer
содержит байты в формате little-endian, и я считаю, что формат BMP, основанный на x86, всегда используется. Таким образом, он будет работать даже на большой машине. Он также больше не может неправильно выровнять большие обращения к памяти, так как все обращения имеют размер байта, который должен работать.
Оба подхода могут быть правильными для Win32. Но в целом оба являются небезопасными. Первый вариант зависит от sizeof(long) == 4
. Второй тоже полагается на это, но требует правильного выравнивания данных (что может быть проблемой, например, для ARM). Оба имеют общий характер, что они принимают определенную endianess и будут демонстрировать различное поведение, например, x86 и mips.
memcpy
работает нормально, пока long
32-битное значение (вместо этого я использовал uint32_t
). Приведение будет работать, если вы знаете, что архитектура, которая запускает код, всегда будет x86.
[Хорошие отзывы других о байтере].
Если вы заботитесь о байте, используйте что-то вроде:
uint32_t width = bmp[0x12] + (bmp[0x13] << 8) + (bmp[0x14] << 16) + (bmp[0x15] << 24);
и то же самое для длины. Это предполагает, что ваши значения bmp
являются unsigned char
.