我在嵌入式设备的固件上工作(用 C 编写),我需要从显示器上截取屏幕截图并将其保存为 bmp 文件。目前我正在研究生成 bmp 文件数据的模块。最简单的方法是编写一些带有以下参数的函数:
(为简单起见,我的示例中仅支持带有索引颜色的图像)
- 颜色深度
- 图像尺寸(宽、高)
- 指向函数的指针以获取 color_index (i) 的调色板颜色
- 指向函数的指针,以获取具有给定坐标 (x, y) 的像素的 color_index
- 指向写入图像数据的函数的指针
然后这个函数的用户应该这样调用它:
/*
* Assume we have the following functions:
* int_least32_t palette_color_get (int color_index);
* int pix_color_idx_get (int x, int y);
* void data_write (const char *p_data, size_t len);
*/
bmp_file_generate(
1, //-- color_depth
x, y, //-- size
palette_color_get,
pic_color_idx_get,
data_write
);
就是这样:这个函数完成所有工作,并且仅在工作完成时返回(即由给定的用户回调函数生成和“写入”的 bmp 文件data_write()
。
但是,我需要使bmp_writer
模块在协作 RTOS 中可用,并且data_write()
可能是一个通过某种协议(例如,UART)实际将数据传输到另一个设备的函数),因此,这个函数只需要从任务上下文中调用。这种方法不起作用,我需要以 OO 风格制作它,它的用法应该如下所示:
/*
* create instance of bmp_writer with needed params
* (we don't need "data_write" pointer anymore)
*/
T_BmpWriter *p_bmp_writer = new_bmp_writer(
1, //-- color_depth
x, y, //-- size
palette_color_get,
pic_color_idx_get
);
/*
* Now, byte-by-byte get all the data!
*/
while (bmp_writer__data_available(p_bmp_writer) > 0){
char cur_char = bmp_writer__get_next_char(p_bmp_writer);
//-- do something useful with current byte (i.e. cur_char).
// maybe transmit to another device, or save to flash, or anything.
}
/*
* Done! Free memory now.
*/
delete_bmp_writer(p_bmp_writer);
如您所见,用户可以bmp_writer__get_next_char(p_bmp_writer)
在需要时调用,并根据需要处理接收到的数据。
实际上我已经实现了这个,但是,通过这种方法,所有的算法都被彻底颠覆了,而且这段代码非常不可读。
我将向您展示生成调色板数据的旧代码的一部分(来自完成所有工作的函数,并且仅在工作完成时返回),以及新代码的适当部分(以状态机样式)。
旧代码:
void bmp_file_generate(/*....args....*/)
{
//-- ... write headers
//-- write palette (if needed)
if (palette_colors_cnt > 0){
size_t i;
int_least32_t cur_color;
for (i = 0; i < palette_colors_cnt; i++){
cur_color = callback_palette_color_get(i);
callback_data_write((const char *)&cur_color, sizeof(cur_color));
}
}
//-- ...... write image data ..........
}
如您所见,代码非常简短易读。
现在,新代码。
它看起来像状态机,因为它实际上是按阶段划分的(HEADER_WRITE、PALETTE_WRITE、IMG_DATA_WRITE),每个阶段都有自己的上下文。在旧代码中,上下文保存在局部变量中,但现在我们需要创建结构并从堆中分配它。所以:
/*
* Palette stage context
*/
typedef struct {
size_t i;
size_t cur_color_idx;
int_least32_t cur_color;
} T_StageContext_Palette;
/*
* Function that switches stage.
* T_BmpWriter is an object context, and pointer *me is analogue of "this" in OO-languages.
* bool_start is 1 if stage is just started, and 0 if it is finished.
*/
static void _stage_start_end(T_BmpWriter *me, U08 bool_start)
{
switch (me->stage){
//-- ...........other stages.........
case BMP_WR_STAGE__PALETTE:
if (bool_start){
//-- palette stage is just started. Allocate stage context and initialize it.
me->p_stage_context = malloc(sizeof(T_StageContext_Palette));
memset(me->p_stage_context, 0x00, sizeof(T_StageContext_Palette));
//-- we need to get first color, so, set index of byte in cur_color to maximum
((T_StageContext_Palette *)me->p_stage_context)->i = sizeof(int_least32_t);
} else {
free(me->p_stage_context);
me->p_stage_context = NULL;
}
break;
//-- ...........other stages.........
}
}
/*
* Function that turns to the next stage
*/
static void _next_stage(T_BmpWriter *me)
{
_stage_start_end(me, 0);
me->stage++;
_stage_start_end(me, 1);
}
/*
* Function that actually does the job and returns next byte
*/
U08 bmp_writer__get_next_char(T_BmpWriter *me)
{
U08 ret = 0; //-- resulting byte to return
U08 bool_ready = 0; //-- flag if byte is ready
while (!bool_ready){
switch (me->stage){
//-- ...........other stages.........
case BMP_WR_STAGE__PALETTE:
{
T_StageContext_Palette *p_stage_context =
(T_StageContext_Palette *)me->p_stage_context;
if (p_stage_context->i < sizeof(int_least32_t)){
//-- return byte of cur_color
ret = *( (U08 *)&p_stage_context->cur_color + p_stage_context->i );
p_stage_context->i++;
bool_ready = 1;
} else {
//-- need to get next color (or even go to next stage)
if (p_stage_context->cur_color_idx < me->bmp_details.palette_colors_cnt){
//-- next color
p_stage_context->cur_color = me->callback.p_palette_color_get(
me->callback.user_data,
p_stage_context->cur_color_idx
);
p_stage_context->cur_color_idx++;
p_stage_context->i = 0;
} else {
//-- next stage!
_next_stage(me);
}
}
}
break;
//-- ...........other stages.........
}
}
return ret;
}
如此庞大的代码,很难理解!但我真的不知道如何以某种不同的方式制作它,以便能够逐字节获取信息。
有谁知道如何实现这一点,并保持代码的可读性?
任何帮助表示赞赏。