画像の表示

独自定義フォーマットのイメージ
今までのプログラムでは、ウィンドウの表示用として作成されたイメージに直接描画していましたが、このイメージは OS やディスプレイのカラーモードによってフォーマットが異なるので、直接イメージバッファを操作するのには向いていません。

実際にペイントソフトなどを作る場合には、アルファ値が必要な場合や、色を 16bit カラーで扱いたい場合などがあり、OS で用意されているイメージのフォーマットだけでは表現できないものもあります。

そういった場合には、独自定義したフォーマットのイメージを使います。
カラーモードやビット幅などは自由に自分で定義することができますが、図形などの描画関数もすべて自分で実装しなければなりません。

独自定義フォーマットのイメージに描画した内容をウィンドウに表示するためには、そのイメージを環境依存のフォーマットのイメージに変換して、それをウィンドウに表示 (転送) する必要があります。
SPTK_IMAGE32
ペイントソフト向けの独自定義フォーマットとしては、
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 などによって、使われるエンディアンが異なります。

リトルエンディアン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 に読み込み、それをウィンドウに表示させます。
また、スクロールバーを付けて、画像が大きい場合はスクロールできるようにします。

ソースコード
サンプル画像 >> bitmap1.bmp

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() で解放しています。
スクロールバーの作成
画像のスクロール用に、水平と垂直の2つのスクロールバーを作成しています。

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 にそれぞれ現在のスクロール位置をセットし、ウィンドウ内容を更新します。
draw_img()
draw_img() は、SPTK_IMAGE32 のビットマップイメージをウィンドウのイメージに転送して、更新します。

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) に転送します。