私は組み込みデバイスのファームウェア (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 など) を介して別のデバイスにデータを送信する関数である可能性があるため、この関数はタスク コンテキストからのみ呼び出す必要があります。その場合、このアプローチは機能しません。オブジェクト指向スタイルで作成する必要があり、その使用法は次のようになります。
/*
* 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;
}
とても巨大なコードで、理解するのはとても難しいです! しかし、バイト単位で情報を取得できるようにするために、別の方法でそれを作成する方法が本当にわかりません。
これを達成し、コードの可読性を維持する方法を知っている人はいますか?
どんな助けでも大歓迎です。