我正在写一个位图(bpm)解析器,以在学习时练习c ++。但是,在这些版本上有许多不同的图像标题,而我在如何实现此方法方面遇到了麻烦,这种感觉避免了很多检查并且很优雅。您能否就以下某些方法给我建议或建议其他方法?
这是我想到的方法。我遇到麻烦的主要原因是我想使用memcpy
以避免单独复制每个字段。跨结构唯一一致的是前4个字节定义标头/结构的大小。
在类中使用多个指针。在初始化时将每个设置为一个nullptr。然后,使用reinterpret_cast
确定第一次解析文件时使用的标头。从那时起,检查哪个指针不是nullptr以确定要使用哪个指针。
每当对标题中的值进行获取或设置操作时,请使用void ptr并使用reinterpret_cast
。可能不是最好的选择,但它在那里。
创建ABC(我不确定我是否完全理解)。将允许一个继承结构。不确定使用memcpy
是否会顺利进行,因为据我了解,使用虚方法会在结构的开头添加一个不可见的指针,该指针将通过将未修改的缓冲区直接处理到结构中而使用未修改的缓冲区破坏。 (请告诉我我是否对此有误)。
当前正在尝试一种方法。这是我的头文件。
#ifndef BITMAP_H_INCLUDE
#define BITMAP_H_INCLUDE
#include <fstream>
#include <iostream>
#include <vector>
#include <cstring>
enum HeaderSizes
{
File = 14,
Core = 12,
InfoV1 = 40,
OS2 = 64,
InfoV4 = 108,
InfoV5 = 124,
};
enum InfoCompressionMethod
{
// omitted for brevity
};
enum HalftoneAlgorithms
{
// omitted for brevity
};
struct FileHeader
{
// omitted for brevity, first header is always 14 bytes
};
struct CoreHeader
{
uint32_t header_size;
uint16_t width;
uint16_t height;
uint16_t planes;
uint16_t bit_depth;
};
struct InfoV1Header
{
uint32_t header_size;
int32_t width;
int32_t height;
uint16_t planes;
uint16_t bit_depth;
uint32_t compression;
uint32_t size_image;
int32_t x_resolution;
int32_t y_resolution;
uint32_t color_used;
uint32_t color_important;
};
struct OS2Header : public InfoV1Header
{
uint16_t resolution_units;
uint16_t reserved;
uint16_t fill_direction;
uint16_t halftone_algorithm;
uint32_t halftone_param_1;
uint32_t halftone_param_2;
uint32_t color_encoding;
uint32_t application_defined;
};
struct InfoV4Header : public InfoV1Header
{
uint32_t red_mask;
uint32_t green_mask;
uint32_t blue_mask;
uint32_t alpha_mask;
uint32_t cs_type;
uint64_t red_x;
uint64_t red_y;
uint64_t red_z;
uint64_t green_x;
uint64_t green_y;
uint64_t green_z;
uint64_t blue_x;
uint64_t blue_y;
uint64_t blue_z;
uint32_t gamma_red;
uint32_t gamma_green;
uint32_t gamma_blue;
};
struct InfoV5Header : public InfoV4Header
{
uint32_t intent;
uint32_t profile_data;
uint32_t profile_size;
uint32_t reserved;
};
class Bitmap
{
public:
Bitmap(std::string in_path);
~Bitmap();
void save(std::string out_path);
private:
FileHeader* file_header;
// Info Header Options
CoreHeader* core_header;
OS2Header* os2_header;
InfoV1Header* info_v1_header;
InfoV4Header* info_v4_header;
InfoV5Header* info_v5_header;
void verify_file_header() const;
};
#endif /* BITMAP_H_INCLUDE */
似乎现在是最好的选择,但我想写一堆从检查信息标题中的nullptrs开始的方法。希望我缺少一些东西,因为我不太了解c ++。
((当前可以在c ++ 14中工作,但在有意义的情况下不反对使用17。在Linux Zorin OS 15上,使用gcc 7.4和cmake 3.15.2。)
是一个随机无关的点,V4头中的red_x到blue_z字段是32位带符号整数,而不是上面的64位uint(实际上是FXPT2DOT30类型-具有2位整数部分的固定点,十进制30位)。
处理此问题的典型方法是将整个BMP文件加载到一大块内存中,然后重新解释数据的方式。如您所见,V5衍生自V4,而V4衍生自V1。鉴于确定文件头文件类型的是文件头大小,您可以进行动态转换。...
struct InfoV1Header;
struct OS2Header;
struct InfoV4Header;
struct InfoV5Header;
struct InfoV1Header
{
uint32_t header_size;
int32_t width;
int32_t height;
uint16_t planes;
uint16_t bit_depth;
uint32_t compression;
uint32_t size_image;
int32_t x_resolution;
int32_t y_resolution;
uint32_t color_used;
uint32_t color_important;
template<typename T>
inline const T* as() const;
};
struct OS2Header : public InfoV1Header
{
uint16_t resolution_units;
uint16_t reserved;
uint16_t fill_direction;
uint16_t halftone_algorithm;
uint32_t halftone_param_1;
uint32_t halftone_param_2;
uint32_t color_encoding;
uint32_t application_defined;
};
struct InfoV4Header : public InfoV1Header
{
uint32_t red_mask;
uint32_t green_mask;
uint32_t blue_mask;
uint32_t alpha_mask;
uint32_t cs_type;
int32_t red_x;
int32_t red_y;
int32_t red_z;
int32_t green_x;
int32_t green_y;
int32_t green_z;
int32_t blue_x;
int32_t blue_y;
int32_t blue_z;
uint32_t gamma_red;
uint32_t gamma_green;
uint32_t gamma_blue;
};
struct InfoV5Header : public InfoV4Header
{
uint32_t intent;
uint32_t profile_data;
uint32_t profile_size;
uint32_t reserved;
};
template<>
inline const OS2Header* InfoV1Header::as<OS2Header>() const
{
return header_size == sizeof(OS2Header) ? (const OS2Header*)this : 0;
}
template<>
inline const InfoV4Header* InfoV1Header::as<InfoV4Header>() const
{
return header_size >= sizeof(InfoV4Header) ? (const InfoV4Header*)this : 0;
}
template<>
inline const InfoV5Header* InfoV1Header::as<InfoV5Header>() const
{
return header_size >= sizeof(InfoV5Header) ? (const InfoV5Header*)this : 0;
}
然后,与维护多个指向不同结构的指针相比,[这将使您以一种更易于管理的方式处理数据。(它们都是相同的地址-因此不值得多次存储imho)。
void doStuff(const InfoV1Header* v1)
{
// do stuff with the v1 fields
if(const OS2Header* os2 = v1->as<OS2Header>())
{
// do stuff with os2 fields
}
if(const InfoV4Header* v4 = v1->as<InfoV4Header>())
{
// do stuff with v4 fields
}
if(const InfoV5Header* v5 = v1->as<InfoV5Header>())
{
// do stuff with v5 fields
}
}