Home > Archives > 2005年06月17日

2005年06月17日

美しいパターン

画像回転でデフォルト画像としてパターン画像を生成していたのだが,友人に美しいパターンの生成方法を教わった.生成式は x 座標と y 座標の XOR で.これをやると見事なパターンがでる.

モノクロならこんな感じで.(パターンを描いたスクリーンショット

    for(int i = 0 ; i < ImageHeight ; i++) {
      for(int j = 0 ; j < ImageWidth ; j++) {
        *p++ = (i^j)&0xff;
      }
    }

色つけるならこんな感じで.

    for(int i = 0 ; i < ImageHeight ; i++) {
      int r = (i * 0xFF) / ImageHeight;
      for(int j = 0 ; j < ImageWidth ; j++) {
        int y = (i^j)&0xff;
        int ry = r * y/0xff;
        int gy = (( j * 0xFF ) / ImageWidth) * y/0xff;
        int by = (~r&0xff) * y/0xff;
        *p++ = 0xff000000 | ry << 16 | gy << 8| by;
      }
    }

SSE と SSE2

画像回転のメインルーチンで double による計算をしていたのだが,この部分を固定小数演算に置き換えたら劇的に速くなった.キャスト要らずだし,やっぱ整数演算は速いなぁと思っていたら,コンパイル時に -msse2 をつければ double でも SSE2 命令で速くなった.同様に,double を float に置き換えても -msse (SSE) で速くなる.いまさらながらだが,これらの拡張命令のすごさを体験してみたり.ちなみに fps はノートPC で30に達した.描画しなければ 90 だけど.

ということで,記念にソースと実行ファイル一式を置いておこう.馬鹿みたいに重たいスクリーンセーバーもどきか?

テクスチャのほうが速い

画像回転でOpenGLを使っていたのだが,どうやらビットマップよりテクスチャ貼り付けのほうが速いらしい.というわけで,描画を glDrawPixels からテクスチャに切り替え.

    glEnable( GL_TEXTURE_2D );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D , 0 , GL_RGBA , ImageWidth , ImageHeight, 0 , GL_RGBA, GL_UNSIGNED_BYTE , rotatedPixels);
    glBegin(GL_POLYGON);
        glTexCoord2f(0 , 0); glVertex2f(whiteSpace ,whiteSpace);
        glTexCoord2f(0 , 1); glVertex2f(whiteSpace , ImageHeight+whiteSpace);
        glTexCoord2f(1 , 1); glVertex2f(ImageWidth + whiteSpace, ImageHeight+whiteSpace);
        glTexCoord2f(1 , 0); glVertex2f(ImageWidth + whiteSpace, whiteSpace);
    glEnd();
    glDisable( GL_TEXTURE_2D );

40fpsキター

1024x1024 の画像回転プログラム SDL+OpenGL 版(もちろん回転は自前).Java でアルゴリズムを確かめた後にノートPC上でC++移植していたため 10fps しか出てなかった.しかし,メインマシンに移したら 40fps で動くではないか.OpenGL速いなぁ,そしてノートはやっぱ遅いんだなぁ.さーて任意の画像を白黒で回転できるようにしよう.

libpng

どうせ画像を回転させるなら png でも読み込ませようと考えて, libpng を使ってみることに.面倒なのでフルカラー png のみをひとつの大きな位置次元配列に展開する関数を作った.

// full color PNG を一列のバッファにして返す
int readPNG(const char *fname, unsigned char**buf, int *iHeight, int *iWidth, bool *bAlpha, int *iRowbytes)
{
    const int number = 8;
    unsigned char header[number];
    FILE *fp = fopen(fname, "rb");
    if (!fp) return -1;
    fread(header, 1, number, fp);
    bool is_png = !png_sig_cmp(header, 0, number);   // Signature check
    if (!is_png) return -1;
    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (!png_ptr) return -1;
    png_infop info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr){
        png_destroy_read_struct(&png_ptr,
           (png_infopp)NULL, (png_infopp)NULL);
        return -1;
    }
    png_infop end_info = png_create_info_struct(png_ptr);
    if (!end_info){
        png_destroy_read_struct(&png_ptr, &info_ptr,
          (png_infopp)NULL);
        return -1;
    }    
    png_init_io(png_ptr, fp);                 // init
    png_set_sig_bytes(png_ptr, number);       // inform libpng read-bytes
    png_read_info(png_ptr, info_ptr);         // get info    
    png_uint_32 width, height;
    int bit_depth, color_type, interlace_type;
    int compression_type, filter_method;
    png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method);
    int channels = png_get_channels(png_ptr, info_ptr);
    int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
    switch(color_type){
    case PNG_COLOR_TYPE_RGB:             // RGB only
    case PNG_COLOR_TYPE_RGB_ALPHA:
        break;
    case PNG_COLOR_TYPE_GRAY:
    case PNG_COLOR_TYPE_PALETTE:
    case PNG_COLOR_MASK_PALETTE:
        //    case PNG_COLOR_MASK_COLOR:
    case PNG_COLOR_MASK_ALPHA:
        return -1;
    }
    if(bit_depth!=8) return -1;
    *buf = new unsigned char[rowbytes*height];
    unsigned char **rows = new unsigned char*[height];
    for(int i = 0; i < height; i++){
        rows[i] = &(*buf)[i*rowbytes];
    }
    png_read_image(png_ptr, rows);
    png_read_end(png_ptr, end_info);
    png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
    *iHeight = height;
    *iWidth = width;
    *bAlpha = color_type == PNG_COLOR_TYPE_RGB_ALPHA;
    *iRowbytes = rowbytes;
    delete rows;
    return 0;
}

コンパイルは png.h をインクルードしてから -lpng12 -lz -lm をつければいいはず.

ちなみに,Cygwin には mingw の libpng がないのでソースから作る必要があるっぽい.cygwin の libpng を使ったらプログラムが止まらなくなったりしたし.作るのは普通どおりにただ cygwin 使うなと指定してやれば良いみたい.

CFLAGS="-Wall -O3 -funroll-loops -mno-cygwin" ./configure --libdir=/lib/mingw/ --includedir=/usr/include/mingw/
make
make install

Home > Archives > 2005年06月17日

Search
Feeds

Page Top