第6章 絵を使いまわす


同じ形の複雑な図形をあちこちに描く必要がある場合、いちいち描くのは 無駄です。ひとつだけ描いて、あとはそれをコピーする方が簡単です。本章 では窓の絵の情報をまるごと他の部分に移す方法について説明します。


絵をコピーする

窓の中の一部の長方形領域の絵を別の所にコピーすることが できます。それにはXCopyArea()関数を使います。

Prototype

void XCopyArea( Display* display, Window src, Window dst, GC gc, int src_xo, int src_yo, unsigned int src_width, unsigned int src_height, int dst_xo, int dst_yo );

引数が多いですが意味は簡単です。
src, dst
それぞれコピー元、コピー先のWindow IDです。
gc
コピーの際の描画方法を設定します。
src_xo, src_yo, src_width, src_height
コピー元でのコピーする長方形領域を指定します。
dst_xo, dst_yo
コピー先でのコピーする長方形領域の左上隅の座標です。
この関数は引数にコピー元、コピー先のWindow IDを指定できることから わかるように他の窓へ絵をコピーすることもできます。

コピーをする際につかうGCには実用上次の設定をしておくべきです。

XSetGraphicsExposures( dis, gc, False );

GCにこの設定をしておかないと、コピーをするたびにコピー先の 窓にNoExopseイベントがイベントマスクの設定に関係無しに 送られてしまい、正常なイベント処理を妨げてしまいます。


黒子の窓を開く

絵を窓にコピーするには、当然ながら元の絵がどこかに表示されて なければなりません。しかし一般に、元の絵が見えると具合が悪いことが 多いです。
例えば複雑な図形をコピーでたくさん描くために、最初に窓の隅でこちょ こちょ元図形を描いて準備するのはいかにも格好悪いです。
作業用の窓で表には表示されない窓が必要になります。

そのような黒子の窓にピックスマップと呼ばれる特殊な窓が あります。ピックスマップへの図形の書き込み操作は普通の窓と 全く同じです。描画関数に渡すWindow IDの代わりにこのPixmap ID を渡すだけです。

ピックスマップを生成するには XCreatePixmap() 関数を使います。

Prototype

Pixmap XCreatePixmap( Display* display, Window window, int width, int height, int depth );

これで横 width、縦 height の大きさの窓と同等の広さをもつメモリ領域 が確保されます。depthは1点あたりのメモリのビット数で、通常8です。 windowには表にでている窓のIDを入れておきますが、あまり意味はありません。

ピックスマップはただのメモリであり窓のように特定の値で初期化 されてはいません。なので必ず長方形描画関数で適当な色で塗りつぶし ておかなければなりません。

ピックスマップを使い終えたらそのメモリを解放しておきます。 XFreePixmap() 関数を使います。

Prototype

void XFreePixmap( Display* display, Pixmap pixmap );

pixmapは解放するピックスマップのPixmap IDです。

ピックスマップを使う例

極基本的な例を示しておきます。

名前はptest.ccです。是非おためしください。

ptest.ccのダウンロード

ptest.ccの内容

/* ptest.cc */

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>

unsigned long GetColor( Display* dis, char* color_name )
{
    Colormap cmap;
    XColor near_color, true_color;

    cmap = DefaultColormap( dis, 0 );
    XAllocNamedColor( dis, cmap, color_name, &near_color, &true_color );
    return( near_color.pixel );
}


int main( void )
{
    Display* dis;
    Window win;
    Pixmap pix;
    XSetWindowAttributes att;
    GC gc;
    XEvent ev;

    int i;

    dis = XOpenDisplay( NULL );
    win = XCreateSimpleWindow( dis, RootWindow(dis,0), 100, 100,
        256, 256, 5, WhitePixel(dis,0), BlackPixel(dis,0) );
    pix = XCreatePixmap( dis, win, 32, 32, 8 );

    att.backing_store = WhenMapped;
    XChangeWindowAttributes( dis, win, CWBackingStore, &att );

    gc = XCreateGC( dis, DefaultRootWindow(dis), 0, 0 );
    XSetGraphicsExposures( dis, gc, False );

    XSetForeground( dis, gc, GetColor( dis, "red")  );
    XFillRectangle( dis, pix, gc, 0, 0, 32, 32 );

    XSetForeground( dis, gc, GetColor( dis, "green")  );
    XFillArc( dis, pix, gc, 0, 0, 32, 32, 0, 360*64 );

    XSelectInput( dis, win, ExposureMask );
    XMapWindow( dis, win );

    do{
        XNextEvent( dis, &ev);
    }while( ev.type != Expose );

    for( i=0 ; i<5 ; i++ ){
        XCopyArea( dis, pix, win, gc, 0, 0, 32, 32, i*48+16, 112 );
    }
    XFlush( dis );

    getchar();
    XDestroyWindow( dis , win );
    XCloseDisplay( dis );

    return(0);
}

窓とメモリ間のデータ転送

窓の絵のデータが格納されているメモリのデータのフォーマットは とても簡単で、メモリの1バイトごとに1点のpixel値が格納され、 それがx軸正方向の流れで直線的に連なっています。

このような単純なデータを作成するのに、わざわざXlibの描画関数に 頼らずに、自分でプログラム(Xclient)内のメモリにそれと同じ フォーマットでデータを作って、そのデータを窓に転送すれば、 より高速な描画が可能になります。

Xclientのメモリを窓にコピーするには XPutImage()関数を使います。 ただし、XImage構造を仲介しなければなりません。簡単に使えるように 次の関数を用意します。

void PutImage( Display* dis, Window win, GC gc, char* image_buf, int src_width, int src_height, int dst_xo, int dst_yo )
{
  XImage image;

  image.format           = ZPixmap;
  image.data             = image_buf;
  image.width            = src_width;
  image.height           = src_height;
  image.xoffset          = 0; 
  image.byte_order       = MSBFirst;
  image.bitmap_bit_order = MSBFirst;
  image.bits_per_pixel   = 8;
  image.bytes_per_line   = src_width;
  image.bitmap_unit      = 8;
  image.bitmap_pad       = 8;
  image.depth            = 8;

  XPutImage( dis, win, gc, &image, 0, 0, dst_xo, dst_yo, src_width, src_height );
  XFlush( dis );
}

この関数はchar配列のデータをsrc_width×src_heightのピックスマップ とみなして、その全領域を窓の dst_xo, dst_yo を左上隅としてコピー します。必要に応じてより最適に作り変えると良いでしょう。

逆に窓の絵をXclinet側のメモリに格納するには XGetImage()関数を 使います。これも簡単に使えるように次の関数を用意します。

char* GetImage( Display* dis, Window win, int src_xo, int src_yo, int src_width, int src_height )
{
  XImage* image;

  image = XGetImage( dis, win, src_xo, src_yo, src_width, src_height, 0xff, ZPixmap );

  return( image.data );
}

この関数は窓の src_xo, src_yo, src_width, src_heightの領域を Xclient内のメモリに格納して、そのアドレスを返します。


  • 第7章 アニメーション
  • 目次
    Copyright(C) by Naoki Watanabe. Oct 21st, 1995.
    渡辺尚貴 naoki@cms.phys.s.u-tokyo.ac.jp