画像の表示

独自定義フォーマットのイメージ
今までのプログラムでは、ウィンドウの表示用として作成されたイメージに直接描画していましたが、このイメージは 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) に転送します。