独自定義フォーマットのイメージ
今までのプログラムでは、ウィンドウの表示用として作成されたイメージに直接描画していましたが、このイメージは OS やディスプレイのカラーモードによってフォーマットが異なるので、直接イメージバッファを操作するのには向いていません。
実際にペイントソフトなどを作る場合には、アルファ値が必要な場合や、色を 16bit カラーで扱いたい場合などがあり、OS で用意されているイメージのフォーマットだけでは表現できないものもあります。
そういった場合には、独自定義したフォーマットのイメージを使います。
カラーモードやビット幅などは、自由に自分で定義することができますが、その分、図形などの描画関数もすべて自分で実装しなければなりません。
独自定義フォーマットのイメージに描画した内容をウィンドウに表示するためには、そのイメージを環境依存のフォーマットのイメージに変換して、それをウィンドウに表示 (転送) する必要があります。
実際にペイントソフトなどを作る場合には、アルファ値が必要な場合や、色を 16bit カラーで扱いたい場合などがあり、OS で用意されているイメージのフォーマットだけでは表現できないものもあります。
そういった場合には、独自定義したフォーマットのイメージを使います。
カラーモードやビット幅などは、自由に自分で定義することができますが、その分、図形などの描画関数もすべて自分で実装しなければなりません。
独自定義フォーマットのイメージに描画した内容をウィンドウに表示するためには、そのイメージを環境依存のフォーマットのイメージに変換して、それをウィンドウに表示 (転送) する必要があります。
SPTK_IMAGE32
ペイントソフト向けの独自定義フォーマットとしては、
8bit の R,G,B 値にアルファ値を加えた RGBA の 32bit イメージが一般的です。
これは、1px の色値が 4 Byte になるので、データとして扱いやすいという利点もあります。
イメージバッファは 幅×高さ×4byte のサイズが確保され、x は左から右、y は上から下の方向、という形で並び、1px は R-G-B-A のバイト順で並んでいます。
なお、1px は 4 Byte ですが、ここでは、バッファを扱う時に 4 バイトの整数値で扱わないでください。
SPTK_IMAGE32 では、システムのエンディアンに依存しないように、必ず R-G-B-A のバイト順で並んでいます。
Windows/Linux ではリトルエンディアンなので、4 バイト整数で 0xAARRGGBB の色をセットすると、B-G-R-A のバイト順になってしまいます。
SPTK_IMAGE32 が内部でどういう形になっているかは、SPTK のソースを見てください。
8bit の R,G,B 値にアルファ値を加えた RGBA の 32bit イメージが一般的です。
これは、1px の色値が 4 Byte になるので、データとして扱いやすいという利点もあります。
SPTK_IMAGE32
SPTK には、RGBA 32bit のイメージとして SPTK_IMAGE32 が用意されています。イメージバッファは 幅×高さ×4byte のサイズが確保され、x は左から右、y は上から下の方向、という形で並び、1px は R-G-B-A のバイト順で並んでいます。
なお、1px は 4 Byte ですが、ここでは、バッファを扱う時に 4 バイトの整数値で扱わないでください。
SPTK_IMAGE32 では、システムのエンディアンに依存しないように、必ず R-G-B-A のバイト順で並んでいます。
Windows/Linux ではリトルエンディアンなので、4 バイト整数で 0xAARRGGBB の色をセットすると、B-G-R-A のバイト順になってしまいます。
SPTK_IMAGE32 が内部でどういう形になっているかは、SPTK のソースを見てください。
エンディアンについて
「エンディアン」とは、2 バイトや 4 バイトなどの複数バイトからなる数値を、メモリ上に配置する方法です。
「リトルエンディアン」、「ビッグエンディアン」、「ミドルエンディアン」といった種類がありますが、主に使われているのは「リトルエンディアン」と「ビッグエンディアン」です。
CPU などによって、使われるエンディアンが異なります。
ソースコードの移植性を考えた場合、2 バイト以上の数値を扱う際には、エンディアンについて常に意識しておかなければなりません。
エンディアンを意識する必要があるのは、以下の場合です。
例えば、ファイル上のデータが「0x12 0x34」で、これを 2 バイト数値として読み込む場合、
リトルエンディアンの場合は、下位バイトから順に読み込まれることになるので、0x12 は下位バイト、0x34 は上位バイトとなり、値は 0x3412 になります。
ビッグエンディアンの場合は、そのまま上位バイトから読み込むので、値は 0x1234 になります。
バッファから数値を取り出す場合も同じ形になるので、イメージのバッファを扱う場合も、エンディアンには注意しなければいけません。
「リトルエンディアン」、「ビッグエンディアン」、「ミドルエンディアン」といった種類がありますが、主に使われているのは「リトルエンディアン」と「ビッグエンディアン」です。
CPU などによって、使われるエンディアンが異なります。
リトルエンディアン | Windows/Linux など、x86 系 CPU。 数値の下位バイトから順に並べる。 0x12345678 なら、0x78 0x56 0x34 0x12 の順。 |
---|---|
ビッグエンディアン | PowerPC 系 CPU。 数値の上位バイトから順に並べる。 0x12345678 なら、0x12 0x34 0x56 0x78 の順。 |
ソースコードの移植性を考えた場合、2 バイト以上の数値を扱う際には、エンディアンについて常に意識しておかなければなりません。
エンディアンを意識する必要があるのは、以下の場合です。
- バッファから複数バイトの数値を取得する場合
- 複数バイトの数値をバッファに格納する場合
- ファイルから複数バイトの数値を読み込む、または書き込む場合
例えば、ファイル上のデータが「0x12 0x34」で、これを 2 バイト数値として読み込む場合、
リトルエンディアンの場合は、下位バイトから順に読み込まれることになるので、0x12 は下位バイト、0x34 は上位バイトとなり、値は 0x3412 になります。
ビッグエンディアンの場合は、そのまま上位バイトから読み込むので、値は 0x1234 になります。
バッファから数値を取り出す場合も同じ形になるので、イメージのバッファを扱う場合も、エンディアンには注意しなければいけません。
サンプルプログラム
今回は、SPTK_IMAGE32 を使ったサンプルプログラムを作ります。
ビットマップファイル (.bmp) の画像を SPTK_IMAGE32 に読み込み、それをウィンドウに表示させます。
また、スクロールバーを付けて、画像が大きい場合はスクロールできるようにします。
ビットマップファイル (.bmp) の画像を SPTK_IMAGE32 に読み込み、それをウィンドウに表示させます。
また、スクロールバーを付けて、画像が大きい場合はスクロールできるようにします。
ソースコード
サンプル画像として bitmap1.bmp を読み込んでいます。
010_image32.c
010_image32.c
#include "sptk.h" #define WIDTH 250 #define HEIGHT 250 #define SCRW 16 SPTK_IMAGE *winimg; SPTK_IMAGE32 *srcimg; int scrx = 0,scry = 0; void draw_img() { int w,h; w = srcimg->w; h = srcimg->h; if(w > WIDTH - SCRW) w = WIDTH - SCRW; if(h > HEIGHT - SCRW) h = HEIGHT - SCRW; sptk_image32_blt_image(srcimg, scrx, scry, w, h, winimg, 0, 0); sptk_update(NULL, 0, 0, w, h, 5); } void scroll_handle(SPTK_WIDGET *wg,int type,int pos) { if(wg->id == 0) scrx = pos; else scry = pos; draw_img(); } int main() { SPTK_WIDGET *scrh,*scrv; sptk_init("test", WIDTH, HEIGHT); winimg = sptk_window_get_image(); srcimg = sptk_image32_load_bitmap("bitmap1.bmp"); if(!srcimg) sptk_errexit("cannot load bitmap file"); /* スクロールバー */ scrh = sptk_widget_scrollbar_create(0, 0, HEIGHT - SCRW, WIDTH - SCRW, SCRW, 0, scroll_handle); scrv = sptk_widget_scrollbar_create(1, WIDTH - SCRW, 0, SCRW, HEIGHT - SCRW, 1, scroll_handle); sptk_scrollbar_set_status(scrh, 0, srcimg->w, WIDTH - SCRW); sptk_scrollbar_set_status(scrv, 0, srcimg->h, HEIGHT - SCRW); draw_img(); sptk_run(); sptk_image32_free(srcimg); return 0; }
解説
ビットマップ画像の読み込み
sptk_image32_load_bitmap() は、新規作成した SPTK_IMAGE32 にビットマップ画像を読み込み、イメージのポインタを返します。
読み込みに失敗した場合は、NULL が返ります。
1/4/8/24/32bit の無圧縮ビットマップ画像が読み込めます。
32bit の場合は、アルファ値を読み込みます。
ここでは、現在の作業ディレクトリ内の "bitmap1.bmp" ファイルを読み込んでいます。
パスやファイル名は適当に変更してください。
読み込みに失敗したら、sptk_errexit() で、エラーメッセージを表示して終了します。
なお、作成された SPTK_IMAGE32 は自動では解放されませんので、メインループ終了後、sptk_image32_free() で解放しています。
読み込みに失敗した場合は、NULL が返ります。
1/4/8/24/32bit の無圧縮ビットマップ画像が読み込めます。
32bit の場合は、アルファ値を読み込みます。
ここでは、現在の作業ディレクトリ内の "bitmap1.bmp" ファイルを読み込んでいます。
パスやファイル名は適当に変更してください。
読み込みに失敗したら、sptk_errexit() で、エラーメッセージを表示して終了します。
なお、作成された SPTK_IMAGE32 は自動では解放されませんので、メインループ終了後、sptk_image32_free() で解放しています。
スクロールバーの作成
画像のスクロール用に、水平と垂直の2つのスクロールバーを作成しています。
sptk_widget_scrollbar_create() で、スクロールバーウィジェットを作成します。
vert : 水平バーの場合は 0、垂直バーの場合は 0 以外を指定します。
スクロールバーの情報は、以下の関数でセットします。
page は、1 ページの幅 (バーの表示幅) です。max - min より大きい場合はスクロールは無効となります。
sptk_widget_scrollbar_create() で、スクロールバーウィジェットを作成します。
SPTK_WIDGET *sptk_widget_scrollbar_create(int id, int x,int y,int w,int h,int vert,void (*handle)(SPTK_WIDGET *,int,int));
vert : 水平バーの場合は 0、垂直バーの場合は 0 以外を指定します。
スクロールバーの情報は、以下の関数でセットします。
void sptk_scrollbar_set_status(SPTK_WIDGET *wg,int min,int max,int page); int sptk_scrollbar_set_pos(SPTK_WIDGET *wg,int pos);
page は、1 ページの幅 (バーの表示幅) です。max - min より大きい場合はスクロールは無効となります。
スクロールバーのハンドラ
スクロールバーのハンドラ関数は、SPTK_WIDGET_BAR の時とほぼ同じです。
今回は、水平/垂直スクロールバーで同じハンドラを使い、ウィジェット ID で判別させます。
水平バーが ID=0、垂直バーが ID=1 に設定してあるので、スクロールバーの位置が変わった時、変数 scrx、scry にそれぞれ現在のスクロール位置をセットし、ウィンドウ内容を更新します。
今回は、水平/垂直スクロールバーで同じハンドラを使い、ウィジェット ID で判別させます。
水平バーが ID=0、垂直バーが ID=1 に設定してあるので、スクロールバーの位置が変わった時、変数 scrx、scry にそれぞれ現在のスクロール位置をセットし、ウィンドウ内容を更新します。
draw_img()
draw_img() は、SPTK_IMAGE32 のビットマップイメージをウィンドウのイメージに転送して、更新します。
sptk_image32_blt_image() は、SPTK_IMAGE32 のイメージの一部分を SPTK_IMAGE に変換&転送します。
※ 範囲のクリッピングは行われないので、注意してください。
今回はスクロール位置を反映させる必要があるので、scrx,scry の位置から、ウィンドウに表示される部分の範囲を、ウィンドウイメージの左上 (0,0) に転送します。
sptk_image32_blt_image() は、SPTK_IMAGE32 のイメージの一部分を SPTK_IMAGE に変換&転送します。
void sptk_image32_blt_image(SPTK_IMAGE32 *srcimg, int x,int y,int w,int h,SPTK_IMAGE *dstimg,int dx,int dy);
※ 範囲のクリッピングは行われないので、注意してください。
今回はスクロール位置を反映させる必要があるので、scrx,scry の位置から、ウィンドウに表示される部分の範囲を、ウィンドウイメージの左上 (0,0) に転送します。