在我的算法中,我需要创建一个信息输出。我需要将布尔矩阵写入bmp文件中。必须是单色图像,如果该元素上的矩阵为真,则像素为白色。主要问题是bmp头以及如何编写此内容。
在我的算法中,我需要创建一个信息输出。我需要将布尔矩阵写入bmp文件中。必须是单色图像,如果该元素上的矩阵为真,则像素为白色。主要问题是bmp头以及如何编写此内容。
imgFile.open(filename, std::ios_base::out | std::ios_base::binary);
#include <vector>
#include <fstream>
struct image
{
image(int width, int height)
: w(width), h(height), rgb(w * h * 3)
{}
uint8_t & r(int x, int y) { return rgb[(x + y*w)*3 + 2]; }
uint8_t & g(int x, int y) { return rgb[(x + y*w)*3 + 1]; }
uint8_t & b(int x, int y) { return rgb[(x + y*w)*3 + 0]; }
int w, h;
std::vector<uint8_t> rgb;
};
template<class Stream>
Stream & operator<<(Stream & out, image const& img)
{
uint32_t w = img.w, h = img.h;
uint32_t pad = w * -3 & 3;
uint32_t total = 54 + 3*w*h + pad*h;
uint32_t head[13] = {total, 0, 54, 40, w, h, (24<<16)|1};
char const* rgb = (char const*)img.rgb.data();
out.write("BM", 2);
out.write((char*)head, 52);
for(uint32_t i=0 ; i<h ; i++)
{ out.write(rgb + (3 * w * i), 3 * w);
out.write((char*)&pad, pad);
}
return out;
}
int main()
{
image img(100, 100);
for(int x=0 ; x<100 ; x++)
{ for(int y=0 ; y<100 ; y++)
{ img.r(x,y) = x;
img.g(x,y) = y;
img.b(x,y) = 100-x;
}
}
std::ofstream("/tmp/out.bmp") << img;
}
这段代码使用了一些较新的C++特性。我用它创建了8位和24位bmp文件。目前只能写入bmp文件,也许有一天我们可以读取它们!
我不喜欢所有的移位和容易出错的字节序安全问题。
它可能需要更多的注释,但代码相当直观。据我测试,所谓的运行时检测字节序最终被优化掉了(一段时间以前)。
endian_type.h >> 字节序安全POD类型。
#ifndef ENDIAN_TYPE_H
#define ENDIAN_TYPE_H
#include <algorithm>
#include <type_traits>
namespace endian_type {
template <typename T, bool store_as_big_endian>
struct EndianType {
using value_type = T;
static_assert(std::is_fundamental_v<value_type>,
"EndianType works for fundamental data types");
EndianType() = default;
EndianType(const value_type& value)
: value{ convert_to(value) } {}
struct TypeAsBytes {
unsigned char value[sizeof(value_type)];
};
static constexpr bool is_big_endian() {
union { int ival; char cval; } uval;
uval.ival = 1;
return 0 == uval.cval;
}
static TypeAsBytes convert_to(const value_type& ivalue) {
TypeAsBytes ovalue;
const unsigned char* p_ivalue = (const unsigned char*)&ivalue;
if (store_as_big_endian != is_big_endian()) {
std::reverse_copy(p_ivalue, p_ivalue + sizeof(value_type), ovalue.value);
} else {
std::copy(p_ivalue, p_ivalue + sizeof(value_type), ovalue.value);
}
return ovalue;
}
static value_type convert_from(const TypeAsBytes& ivalue) {
value_type ovalue;
unsigned char* p_ovalue = (unsigned char*) &ovalue;
const unsigned char* p_ivalue = (const unsigned char*)&ivalue;
if (store_as_big_endian != is_big_endian()) {
std::reverse_copy(p_ivalue, p_ivalue + sizeof(value_type), p_ovalue);
}
else {
std::copy(p_ivalue, p_ivalue + sizeof(value_type), p_ovalue);
}
return ovalue;
}
value_type get() const {
return convert_from(value);
}
EndianType& set(const value_type& ivalue) {
value = convert_to(ivalue);
return *this;
}
operator value_type() const {
return get();
}
EndianType& operator=(const value_type& ivalue) {
set(ivalue);
return *this;
}
private:
TypeAsBytes value;
};
template <typename T>
using BigEndian = EndianType<T, true>;
template <typename T>
using LittleEndian = EndianType<T, false>;
} // namespace endian_type
#endif // ENDIAN_TYPE_H
#ifndef BMP_WRITER
#define BMP_WRITER
#include "endian_type.h"
#include <cctype>
#include <vector>
#include <fstream>
namespace bmp_writer {
template <typename T>
using LittleEndian = endian_type::LittleEndian<T>;
struct Header {
char magic[2]{ 'B', 'M' };
LittleEndian<std::uint32_t> size;
LittleEndian<std::uint16_t> app_data1;
LittleEndian<std::uint16_t> app_data2;
LittleEndian<std::uint32_t> offset;
};
struct Info {
LittleEndian<std::uint32_t> info_size{ 40 };
LittleEndian<std::uint32_t> width;
LittleEndian<std::uint32_t> height;
LittleEndian<std::uint16_t> count_colour_planes{ 1 };
LittleEndian<std::uint16_t> bits_per_pixel;
LittleEndian<std::uint32_t> compression{};
LittleEndian<std::uint32_t> image_bytes_size;
LittleEndian<std::uint32_t> resolution_horizontal{ 2835 };
LittleEndian<std::uint32_t> resolution_vertical{ 2835 };
LittleEndian<std::uint32_t> count_pallete_entries{ 0 };
LittleEndian<std::uint32_t> important_colours{ 0 };
};
template <std::size_t count>
class Palette {
public:
static constexpr std::uint32_t NUM_CHANNELS = 4;
using Entry = std::uint8_t[NUM_CHANNELS];
private:
Palette() {
for (auto i = 0; i < count; ++i) {
auto& entry = table[i];
for (auto j = 0; j < NUM_CHANNELS - 1; ++j) {
entry[j] = i;
}
}
}
Palette(const Palette&) = delete;
Palette(const Palette&&) = delete;
Palette& operator=(const Palette&) = delete;
Palette& operator=(const Palette&&) = delete;
public:
static const Palette& get() {
static const Palette palette;
return palette;
}
Entry table[count];
};
static_assert(sizeof(Info) == 40, "");
template <typename T>
void write_bmp(
std::ofstream& out,
std::uint32_t width,
std::uint32_t height,
std::uint16_t count_colour_planes,
const T* data,
std::uint32_t data_size
) {
auto& palette = Palette<256>::get();
Header header;
Info info;
info.width = width;
info.height = height;
//info.count_colour_planes = count_colour_planes;
const std::uint32_t t_per_pixel = data_size / (width * height);
info.bits_per_pixel = std::uint16_t(sizeof(T) * 8 * t_per_pixel);
const std::uint32_t row_len = width * sizeof(T) * t_per_pixel;
// Round row up to next multiple of 4.
const std::uint32_t padded_row_len = (row_len + 3) & ~3u;
const std::uint32_t data_size_bytes = padded_row_len * height;
info.image_bytes_size = data_size_bytes;
if (count_colour_planes == 1) {
header.offset = sizeof(Info) + sizeof(Header) + sizeof(palette);
} else {
header.offset = sizeof(Info) + sizeof(Header);
}
header.size = header.offset + height * padded_row_len;
out.write(reinterpret_cast<const char*>(&header), sizeof(header));
out.write(reinterpret_cast<const char*>(&info), sizeof(info));
if (count_colour_planes == 1) {
out.write(reinterpret_cast<const char*>(&palette), sizeof(palette));
}
const char padding[3] = {};
for (int i = height; i > 0;) {
--i;
const char* p_row =
reinterpret_cast<const char*>(data + i * width);
out.write(p_row, row_len);
if (padded_row_len != row_len) {
out.write(padding, padded_row_len - row_len);
}
}
};
template <typename T>
void write_bmp(
std::ofstream& out,
std::uint32_t width,
std::uint32_t height,
std::uint16_t count_colour_planes,
const std::vector<T>& data
) {
write_bmp(out, width, height, count_colour_planes,
&*data.cbegin(), data.size());
}
template <typename T>
void write_bmp(
const std::string& outfilename,
std::uint32_t width,
std::uint32_t height,
std::uint16_t count_colour_planes,
const std::vector<T>& data
) {
std::ofstream out{ outfilename, std::ios_base::binary };
if (!out) {
throw std::runtime_error("Failed to open: " + outfilename);
}
write_bmp(out, width, height, count_colour_planes,
&*data.begin(), static_cast<std::uint32_t>(data.size()));
out.close();
}
} // namespace
#endif // BMP_WRITER
还有一个使用示例:
#include "bmp_writer.h"
struct PixelType {
PixelType(std::uint8_t r, std::uint8_t g, std::uint8_t b)
: c{ b, g, r } {}
PixelType(std::uint32_t c)
: c{ (c >> 16) & 0xffu, (c >> 8) & 0xffu, c & 0xffu } {}
PixelType() = default;
std::uint8_t c[3] = {};
};
void bmp_writer_test1() {
const int size_x = 20;
const int size_y = 10;
std::vector<PixelType> data(size_x * size_y);
// Write some pixels.
data[2] = PixelType(0xff0000); // red
data[10] = PixelType(0x00ff00); // green
bmp_writer::write_bmp(
"test_bmp_writer1.bmp",
std::uint32_t(size_x),
std::uint32_t(size_y),
std::uint16_t(sizeof(PixelType)),
data
);
}
void bmp_writer_test2() {
const int size_x = 20;
const int size_y = 10;
PixelType data[size_x * size_y];
// Write some pixels.
data[15] = PixelType(0xff, 0, 0); // red
data[17] = PixelType(0, 0xff, 0); // green
std::ofstream out{ "test_bmp_writer2.bmp", std::ios_base::binary };
if (!out) {
throw std::runtime_error("Failed to open: " "test_bmp_writer2.bmp");
}
bmp_writer::write_bmp(
out,
std::uint32_t(size_x),
std::uint32_t(size_y),
std::uint16_t(sizeof(PixelType)),
data,
sizeof(data) / sizeof PixelType
);
}