画像
ウィンドウや Pixmap は、X サーバー内でイメージデータが確保されているため、イメージバッファを操作できません。
イメージバッファを直接操作して描画したい場合は、クライアント側でバッファを確保する XImage を使います。
イメージバッファを直接操作して描画したい場合は、クライアント側でバッファを確保する XImage を使います。
イメージフォーマット
XImage では、3つのフォーマットを使用できます。
イメージフォーマットに関する値は、ImageByteOrder(display) などのマクロで取得できます。
イメージデータは左上から順に、X は右方向、Y は下方向に並びます。
イメージフォーマットに関する値は、ImageByteOrder(display) などのマクロで取得できます。
イメージデータは左上から順に、X は右方向、Y は下方向に並びます。
XYBitmap
1bit のイメージ。
1px = 1bit を、bitmap_unit 単位でパックします。
bitmap_unit = 32 なら、32bit の整数に各 1bit のデータが詰められます。
bitmap_unit 単位のバイト順は byte_order で、ビット順は bitmap_bit_order になります。
1px = 1bit を、bitmap_unit 単位でパックします。
bitmap_unit = 32 なら、32bit の整数に各 1bit のデータが詰められます。
bitmap_unit 単位のバイト順は byte_order で、ビット順は bitmap_bit_order になります。
XYPixmap
色の各ビットごとに、1プレーンのブロックを作って並べます。
4bit イメージなら、1bit の全体データ x 4 ブロックとなります。
あまり使うことはないでしょう。
4bit イメージなら、1bit の全体データ x 4 ブロックとなります。
あまり使うことはないでしょう。
ZPixmap
1px ごとにピクセル値を並べます。通常はこちらを使います。
整数のバイト順は byte_order になります。
サポートされている ZPixmap のフォーマットは、XListPixmapFormats() で取得できます。
整数のバイト順は byte_order になります。
サポートされている ZPixmap のフォーマットは、XListPixmapFormats() で取得できます。
関数
XImage 構造体
typedef struct _XImage { int width, height; int xoffset; /* X方向のオフセットピクセル数 */ int format; /* XYBitmap, XYPixmap, ZPixmap */ char *data; /* イメージデータ */ int byte_order; /* バイトオーダー: LSBFirst, MSBFirst */ int bitmap_unit; /* 8, 16, 32 */ int bitmap_bit_order; /* ビットオーダー: LSBFirst, MSBFirst */ int bitmap_pad; /* スキャンラインのビット単位: 8, 16, 32 */ int depth; /* 深さ */ int bytes_per_line; /* スキャンラインのバイト数 */ int bits_per_pixel; /* ピクセルのビット数 (ZPixmap) */ unsigned long red_mask; /* R,G,B のビットマスク */ unsigned long green_mask; unsigned long blue_mask; XPointer obdata; struct funcs { struct _XImage *(*create_image)(); int (*destroy_image)(); unsigned long (*get_pixel)(); int (*put_pixel)(); struct _XImage *(*sub_image)(); int (*add_pixel)(); } f; } XImage;
XImage 自体は、ただの構造体です。
各情報をセットして、data に自分が確保したイメージバッファのポインタをセットします。
depth は、色として実際に使われるビット数です。
bits_per_pixel は、バッファ上の 1px あたりのビット数です。ZPixmap の場合、1, 4, 8, 16, 24, 32 の値になります。
作成
XImage *XCreateImage(Display *display, Visual *visual, unsigned int depth, int format, int offset, char *data, unsigned int width, unsigned int height, int bitmap_pad, int bytes_per_line);
XImage 構造体を確保して、各値を設定します。
ただし、イメージバッファは確保されないので、自分で確保する必要があります。
通常は、data に NULL を渡して XImage を作成した後、(ximage->bytes_per_line * ximage->height) のバイト数でバッファを確保し、ximage->data にそのポインタをセットしてください。
byte_order, bitmap_unit, bitmap_bit_order は、display から取得されます。
{red,green,blue}_mask は、visual から設定されます。
visual | RGB のマスク値をセットするために使われます。 NULL の場合、マスク値は 0 になります。 参考情報として設定されるだけなので、転送時にマスク値が使用されることはありません。 |
---|---|
format | XYBitmap, XYPixmap, ZPixmap |
offset | 各スキャンラインの先頭において、無視するピクセル数 |
data | イメージバッファのポインタ。 構造体にそのまま値をセットするだけなので、NULL でも構いません。 |
bitmap_pad | スキャンラインのビット単位 (8, 16, 32)。 bytes_per_line を計算する時、このビット単位のサイズになります。 |
bytes_per_line | Y1列ごとのバイト数。 0 で、余分な余白がないものとして計算されます。 |
解放
<X11/Xutil.h> void XDestroyImage(XImage *ximage);
XImage を解放します。
実際は関数ではなくマクロになっており、ximage->f.destroy_image(ximage) が実行されます。
XCreateImage、XGetImage、XSubImage 関数を使用して作成された場合は、イメージデータのメモリも解放されます。
※XImage は X リソースではないので、XCloseDisplay() 時に自動で解放されません。
描画
void XPutImage(Display *display, Drawable d, GC gc, XImage *image, int src_x, int src_y, int dest_x, int dest_y, unsigned int width, unsigned int height);
XImage の指定範囲を、ドローアブルに書き込みます。
XYPixmap or ZPixmap の場合、イメージの深さとドローアブルの深さが同じである必要があります。
byte_order や bitmap_unit がサーバーの要求と異なる場合、自動的に適切な変換が行われます。
プログラム
XImage を作成し、グラデーションを描画してウィンドウに出力します。
左クリックで、赤→緑→青のグラデーションに変化します。
それ以外のボタンを押すと、終了します。
※ウィンドウのサイズ変更時の対応はしていません。
<19-image.c>
左クリックで、赤→緑→青のグラデーションに変化します。
それ以外のボタンを押すと、終了します。
※ウィンドウのサイズ変更時の対応はしていません。
$ cc -o run 19-image.c util.c -lX11
<19-image.c>
#include <stdint.h> #include <stdlib.h> #include <stdio.h> #include <X11/Xlib.h> #include <X11/Xutil.h> #include "util.h" #define WIDTH 200 #define HEIGHT 200 int g_cur_type = 0; /* バッファにピクセル値をセット */ static void _setpixel(XImage *img,uint8_t *buf,uint32_t pixcol) { //マシンとイメージのバイトオーダーが同じであると想定 switch(img->bits_per_pixel) { case 32: *((uint32_t *)buf) = pixcol; break; case 24: if(img->byte_order == LSBFirst) { buf[0] = (uint8_t)pixcol; buf[1] = (uint8_t)(pixcol >> 8); buf[2] = (uint8_t)(pixcol >> 16); } else { buf[0] = (uint8_t)(pixcol >> 16); buf[1] = (uint8_t)(pixcol >> 8); buf[2] = (uint8_t)pixcol; } break; case 16: *((uint16_t *)buf) = pixcol; break; } } /* グラデーションを描画 */ static void _draw_image(XImage *img,int type) { uint8_t *pd,*pdY; int ix,iy,bytes,n; uint32_t pixcol; pdY = (uint8_t *)img->data; bytes = img->bits_per_pixel / 8; for(iy = 0; iy < img->height; iy++) { pd = pdY; n = iy * 255 / (img->height - 1); pixcol = rgb_to_pixel_sep( (type == 0)? n: 0, (type == 1)? n: 0, (type == 2)? n: 0); for(ix = 0; ix < img->width; ix++) { _setpixel(img, pd, pixcol); pd += bytes; } pdY += img->bytes_per_line; } } int main(int argc,char **argv) { Display *disp; XSetWindowAttributes attr; Window win; XEvent ev; XImage *ximg = NULL; disp = XOpenDisplay(NULL); if(!disp) return 1; set_display(disp); //ウィンドウ作成 attr.event_mask = ButtonPressMask | ExposureMask; win = XCreateWindow(disp, DefaultRootWindow(disp), 0, 0, WIDTH, HEIGHT, 0, CopyFromParent, CopyFromParent, CopyFromParent, CWEventMask, &attr); //XImage ximg = XCreateImage(disp, DefaultVisualOfScreen(g_disp.screen), g_disp.depth, ZPixmap, 0, NULL, WIDTH, HEIGHT, BitmapPad(disp), 0); if(!ximg) { printf("failed XCreateImage\n"); goto END; } printf("depth: %d\nbits_per_pixel: %d\nbytes_per_line: %d\n", ximg->depth, ximg->bits_per_pixel, ximg->bytes_per_line); ximg->data = (char *)malloc(ximg->bytes_per_line * HEIGHT); if(!ximg->data) goto END; _draw_image(ximg, g_cur_type); //イベント XMapWindow(disp, win); while(1) { XNextEvent(disp, &ev); switch(ev.type) { case Expose: XPutImage(disp, win, g_disp.gc, ximg, ev.xexpose.x, ev.xexpose.y, ev.xexpose.x, ev.xexpose.y, ev.xexpose.width, ev.xexpose.height); printf("[Expose] (%d,%d)-(%dx%d)\n", ev.xexpose.x, ev.xexpose.y, ev.xexpose.width, ev.xexpose.height); fflush(stdout); break; case ButtonPress: if(ev.xbutton.button == Button1) { g_cur_type++; if(g_cur_type == 3) g_cur_type = 0; _draw_image(ximg, g_cur_type); XPutImage(disp, win, g_disp.gc, ximg, 0, 0, 0, 0, WIDTH, HEIGHT); } else goto END; } } END: if(ximg) XDestroyImage(ximg); XCloseDisplay(disp); return 0; }
解説
背景
今回は、ウィンドウの背景色を設定していません。
ウィンドウサイズが変更された場合、XImage の画像サイズを超える範囲は描画されないので、未定義の状態となります。
描画内容はおかしな感じになりますが、動作自体に問題はありません。
ウィンドウサイズが変更された場合、XImage の画像サイズを超える範囲は描画されないので、未定義の状態となります。
描画内容はおかしな感じになりますが、動作自体に問題はありません。
メモリ確保
XImage は基本的に、XCreateImage() で作成した後、設定された値を使って、後からイメージバッファのメモリを確保した方が良いです。
XCreateImage() 時、bytes_per_line 引数が、実際に必要な最小サイズよりも小さい場合は、NULL が返るので、bytes_per_line を 0 にして自動計算させた後、その値を使ってバッファサイズを計算した方が良いです。
XCreateImage() 時、bytes_per_line 引数が、実際に必要な最小サイズよりも小さい場合は、NULL が返るので、bytes_per_line を 0 にして自動計算させた後、その値を使ってバッファサイズを計算した方が良いです。
ビット数
depth (実際に色として使用されるビット数) が 24 でも、bits_per_pixel (バッファ上の 1px あたりのビット数) が 32 になる場合があります。
その場合、バッファ上では 32bit の数値単位で扱うことになります。
16 bit カラーの場合、depth は 15 か 16 になります。bits_per_pixel は 16 です。
その場合、バッファ上では 32bit の数値単位で扱うことになります。
16 bit カラーの場合、depth は 15 か 16 になります。bits_per_pixel は 16 です。
ピクセル値
XImage のバッファ上では、色を「ピクセル値」として扱うので、RGB 値をピクセル値に変換する必要があります。
最終的にウィンドウへ出力するのであれば、ウィンドウで使うビジュアルからマスク値を取得して、それを元に変換します。
このプログラムの場合、ウィンドウは親 (ルートウィンドウ) と同じビジュアルを指定しているので、スクリーンのデフォルトのビジュアルが使われます。
最終的にウィンドウへ出力するのであれば、ウィンドウで使うビジュアルからマスク値を取得して、それを元に変換します。
このプログラムの場合、ウィンドウは親 (ルートウィンドウ) と同じビジュアルを指定しているので、スクリーンのデフォルトのビジュアルが使われます。