These pages are written by only Japanese.

Welcom to My Diary.com
最新の日記タイトル一覧カテゴリ別タイトル一覧トップへ戻る〜

こんばんわ♪ 現在は8月9日(日)18時9分。 今日も残業なのでしょうか?


hns - 日記自動生成システム - Version 2.19.5 (色々 Fixed)

先月 2008年09月 来月
01 02 03 04 05 6
07 08 09 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
Namazu for hns による簡易全文検索です。
詳細は 詳細指定/ヘルプをご参照下さい。
検索式:

2008年09月01日(月)

テスト用 PNG ファイル生成

PNG 画像の処理に色々と問題があるので、 テストを綿密にやろうとテスト用の PNG ファイルを作ってみました。
パレット形式かビットマップ形式か、何色使うかといったパターンを 手で作るのはやってられないので、プログラムで生成する事にします。

png_test.php:

GD を使うと楽ですね。
<?php

$width  = 8;
$height = 8;

$im = imagecreatetruecolor($width, $height); /* bitmap */
// $im = imagecreate($width, $height);          /* palette */

$colors = array();
$colors []= imagecolorallocate($im, 255, 0, 0); // red
$colors []= imagecolorallocate($im, 0, 255, 0); // green
$colors []= imagecolorallocate($im, 0, 0, 255); // blue
$colors []= imagecolorallocate($im, 255,255,0); // yellow

for ($y=0 ; $y < $height ; $y++) {
    for ($x=0 ; $x < $width ; $x++) {
        $i = ($x + $y * $width) % 4;
        imagesetpixel($im, $x, $y, $colors[$i]);
    }
}

header("Content-type: image/png");
imagepng($im);

png_dump.c:

とりあえず、何も考えずに dump してみる。
% ./a.out  ~/lang/php/16x16palette.png
png_filename=/home/yoya/lang/php/16x16palette.png
png_buff.data_len=94
(width, height)=(8,8) bpp=2 color_type=3(PALETTE) palette_num=4
y=0: 1b 1b 24 00 80 11 24 00
y=1: 1b 1b 00 00 00 00 00 00
y=2: 1b 1b 00 00 00 00 00 00
y=3: 1b 1b 00 00 00 00 00 00
y=4: 1b 1b 00 00 00 00 00 00
y=5: 1b 1b 00 00 00 00 00 00
y=6: 1b 1b 00 00 00 00 00 00
y=7: 1b 1b 00 00 00 00 00 00
ナルホド。
1b = 00011011 = 00 01 10 11 = 0 1 2 3
って事か。
吸い出したイメージ配列は bpp の bit width で並んでるようです。

png_dump.c 修正版:

ビット処理を書くのが面倒なので、 swfed で作った bitstream.c を使いまわしました。
for (y=0; y < png_height; y++) {
    unsigned char *linedata = image_data[y];
    bitstream_t *bs = bitstream_open();
    bitstream_input(bs, linedata, png_get_rowbytes(png_ptr, png_info));
    printf("y=%lu: ", y);
    for (x=0; x < png_width; x++) {
        int colorindex = bitstream_getbits(bs, bpp);
        printf("%02x  ", colorindex);
    }
    bitstream_close(bs);
    printf("\n");
}
実行結果
% ./a.out 16x16palette.png
png_filename=16x16palette.png
png_buff.data_len=94
(width, height)=(8,8) bpp=2 color_type=3(PALETTE) palette_num=4
y=0: 00  01  02  03  00  01  02  03
y=1: 00  01  02  03  00  01  02  03
y=2: 00  01  02  03  00  01  02  03
y=3: 00  01  02  03  00  01  02  03
y=4: 00  01  02  03  00  01  02  03
y=5: 00  01  02  03  00  01  02  03
y=6: 00  01  02  03  00  01  02  03
y=7: 00  01  02  03  00  01  02  03
よし、完璧。(゜∇゜) コンパイル方法
% gcc -Wall -W png_dump.c -lpng bitstream.c
使い方は上の通り。

追記 (2011/12/19):

「imagemagick palette png」で検索された方はこちらも参考にどうぞ。

2008年09月02日(火)

山登り断念

久しぶりなのでリハビリ兼ねて、大山に登ってみたのですが、 黒い雲が山を覆いだしたので、道の真ん中辺りで断念しました。 残念無念。
中腹の神社が工事中でしたね。

[swfed] 透明度無しパレット形式画像処理の不具合修正

透明度無しのパレット画像(いわゆる色index方式)の入っている SWF ファイルを 読み込めない不具合があるので、その対応をしました。

問題:

% php sample/swfdump.phps bitmap.swf
magic=FWS  version=4  file_length=209
rect=(0, 0)-(240, 350) (f_size=14)
frame_rate=12.0  frame_count=1
[0] tag=SetBackgroundColor(9)  length=3
[1] tag=DefineBitsLossless(20)  length=30
swf_tag_lossless_create_detail: indices_len != origsize - 4 * swf_tag_lossless->colormap_count at line(62)
can't create tag detail (tag=20)
swf_tag_lossless_print_detail: detail == NULL
[2] tag=DefineBitsLossless(20)  length=29
        image_id=2  format=5  width=8  height=8
        xrgb bitmap exists
[3] tag=DefineShape2(22)  length=95
[4] tag=PlaceObject2(26)  length=6
[5] tag=ShowFrame(1)
[6] tag=End(0)

原因:

color を 4 byte 決めうちで処理してましたが、以下のように場合によって変わります。
format 3 (palette 形式)
Lossless: 1color 3byte (rgb)
Lossless2: 1color 4byte (rgba)
format 5 (bitmap 形式)
Lossless: 1pixel 4byte (xrgb) x は padding
Lossless: 1pixel 4byte (argb)
というわけで修正。

修正:

SWF Lossless 内の colormap のフィールド長を計算する際に、 以前は 4 決め打ちだったのを、 format=3 で且つ、tag=20(Lossless)の場合は 3 で処理するように修正。

確認:

% php sample/swfdump.phps bitmap.swf
magic=FWS  version=4  file_length=209
rect=(0, 0)-(240, 350) (f_size=14)
frame_rate=12.0  frame_count=1
[0] tag=SetBackgroundColor(9)  length=3
[1] tag=DefineBitsLossless(20)  length=30
        image_id=1  format=3  width=8  height=8
        colormap_count=4  rgb colormap exists  indices exists
[2] tag=DefineBitsLossless(20)  length=29
        image_id=2  format=5  width=8  height=8
        xrgb bitmap exists
[3] tag=DefineShape2(22)  length=95
[4] tag=PlaceObject2(26)  length=6
[5] tag=ShowFrame(1)
[6] tag=End(0)
% php sample/swfgetpngdata.phps bitmap.swf 1 > 1.png
% php sample/swfgetpngdata.phps bitmap.swf 2 > 2.png
% png_dump 1.png
png_filename=1.png
png_buff.data_len=119
(width, height)=(8,8) bpp=8 color_type=3(PALETTE) palette_num=4
y=0: 00  01  02  03  00  01  02  03
y=1: 00  01  02  03  00  01  02  03
y=2: 00  01  02  03  00  01  02  03
y=3: 00  01  02  03  00  01  02  03
y=4: 00  01  02  03  00  01  02  03
y=5: 00  01  02  03  00  01  02  03
y=6: 00  01  02  03  00  01  02  03
y=7: 00  01  02  03  00  01  02  03
% png_dump 2.png
png_filename=2.png
png_buff.data_len=103
(width, height)=(8,8) bpp=8 color_type=2(RGB)
y=0: ff0000  00ff00  0000ff  ffff00  ff0000  00ff00  0000ff  ffff00
y=1: ff0000  00ff00  0000ff  ffff00  ff0000  00ff00  0000ff  ffff00
y=2: ff0000  00ff00  0000ff  ffff00  ff0000  00ff00  0000ff  ffff00
y=3: ff0000  00ff00  0000ff  ffff00  ff0000  00ff00  0000ff  ffff00
y=4: ff0000  00ff00  0000ff  ffff00  ff0000  00ff00  0000ff  ffff00
y=5: ff0000  00ff00  0000ff  ffff00  ff0000  00ff00  0000ff  ffff00
y=6: ff0000  00ff00  0000ff  ffff00  ff0000  00ff00  0000ff  ffff00
y=7: ff0000  00ff00  0000ff  ffff00  ff0000  00ff00  0000ff  ffff00
問題なし。
吸い出す時に処理をサボる為に bpp = 8 決め打ちにしてるけど、 時間見つけて bpp そのまま吸いだせるように修正しとこう。
あと、palette 形式を差し替える時、bitmap で入っちゃうので、 これは早めに対応しとこう… (焦

追記 (2009/10/23):

「swf lossless 2 RGBA」で検索して辿りついた方がいらっしゃったので。

2008年09月03日(水)

mp3 header 抽出

以下のページを参考に mp3 header の抽出を試みました。 試験対象の MP3 が DefineSound に入っていた時の情報は以下の通り。
[4] tag=DefineSound(14)  length=2505
        sound_id=1
        format=2(MP3) rate=1 is_16bits=1 is_stereo=0 samples_count=11019
        sound_data(length=2498)

実験コード:

実験結果:

mp3_filename=../game-1.mp3
mp3_data_len=2498
sync_word=0x7ff
version=0
layer=1
no_protection=1
bit_rate=2
sample_rate=0
padding=0
reserved=0
channel_mode=3
mode_extension=0
copyright=0
original=0
emphasis=0
----
sync_word=0x7ff
version=2
layer=2
	<略>
試しに sync word を scan してみたところ、 一個目の header は正しく抽出されてそう *1 ですが、2つ目以降はボロボロ。
header の後ろはある程度 skip する必要があるらしいので、 一応想定通りではあります。さて…

skip 処理を入れてみた:

試しに header の後ろを何 byte か skip した所、 いい感じに header が取れました。
while (mp3_dump_frame(bs)) {
//    bitstream_incrpos(bs, 97, 0);  // NG
    bitstream_incrpos(bs, 98, 0); // OK
//    bitstream_incrpos(bs, 100, 0); // OK
}
実行結果
% ./a.out  ../game-1.mp3 | grep version=0 | wc
bitstream_getbit: bs->data_len(2498) <= bs->byte_offset(2498)
     24      24     240
bitstream_getbit の警告は bitstream.c 側の問題なのでご容赦を。(⊃д⊂)
この 98 というのは仮の数字で、実際は header の内容から frame size を算出して (frame size - header size 分だけ) skip する必要があるのですが、 mpglib のコード(lame/mpglib/common.c)を見ると、
witch(fr->lay)

 case 1:
           fr->framesize  = (long) tabsel_123[fr->lsf][0][fr->bitrate_index] * 12000;
           fr->framesize /= freqs[fr->sampling_frequency];
           fr->framesize  = ((fr->framesize+fr->padding)<<2)-4;
           fr->down_sample=0;
           fr->down_sample_sblimit = SBLIMIT>>(fr->down_sample);
   break;

 case 2:
           fr->framesize = (long) tabsel_123[fr->lsf][1][fr->bitrate_index] * 144000;
           fr->framesize /= freqs[fr->sampling_frequency];
           fr->framesize += fr->padding - 4;
           fr->down_sample=0;
           fr->down_sample_sblimit = SBLIMIT>>(fr->down_sample);
   break;
      case 3:
	<略>
なんだか、面倒そうなのでやっぱり自前はやめて mpglib を利用する事にします。(´Д`;)

*1: Winamp のファイル情報の表示内容と一致する。

2008年09月04日(木)

mp3 header 抽出 (2)

煮詰まったので、こんな時は人様のコードをカンニング。:-)

mpglib のヘッダ:

struct buf {
        unsigned char *pnt;
        long size;
        long pos;
        struct buf *next;
        struct buf *prev;
};

struct framebuf {
        struct buf *buf;
        long pos;
        struct frame *next;
        struct frame *prev;
};
あれ?
typedef struct mpstr_tag {
	<略>
    struct frame fr; /* holds the parameters decoded from the header */

むむむ…
struct frame {
    int stereo;
    int jsbound;
    int single;
	<略>
    int bitrate_index;
	<略>
    int framesize; /* computed framesize */
まさしく欲しいデータ構造がそのまま定義されてます。
という訳で、自前で頑張らない方がいいかも。
問題は mpglib は lame の内部で使われる事があっても、 一般には独立してインストールされるとは限らないのが難点。
という訳で、(mpglib を内包してる) lame を使ってみました。
% gcc lame_test.c -L/usr/local/lib -lmp3lame
$ env LD_LIBRARY_PATH=/usr/local/lib ./a.out  game-1.mp3
何となく使い方は分かってきたけど、残念ながら frame header chain を作る API が見つからず。うーむ。

ProjectXI を動かしてみた

FFXI エミュ鯖の実装とバイナリが公開されていたので 試しに動かしてみた。(今月遊ぶ気なかったけど、これだけの為に課金…)

動かす手順:

サーバ側。
  • MySQL を用意する (面倒なので Linux 上で動かしている MySQL に居候)
  • MySQL 上に pxi アカウントを作成 (別のユーザ名でも OK)
  • pxidb を作成する
    sql> CREATE DATABASE pxidb
    
  • ProjectXI\bin\server\misc\pxidb.zip の中にある sql 文を流し込む
    % cat pxidb.sql | mysql -h <秘密のIP> pxidb -u pxi -p
    
  • ProjectXI\bin\server\pxi.ini をいじる
    • 別ホストの MySQL も指定できる
    • pxi 以外のユーザ名をで MySQL にアクセスするなら、設定変更
    • pxi ユーザのパスワードも指定できる (デフォのままだと危険なので要変更)
  • ProjectXI\bin\server\pXI-Server0.6.1.exe を起動
  • ProjectXI\bin\client\pXi-Clientv.exe を起動してアカウント作成
クライアント側
  • ProjectXI\bin\client\pXi-Clientv.exe でログイン (必須?)
  • C:\Windows\system32\drivers\hosts に以下の設定を追記
    127.0.0.1  ffxi00.pol.com
    
  • 普通に FFXI のクライアントを起動。
(09/07 追記) クライアント側は以下の手順じゃないとダメです
  • C:\Windows\system32\drivers\hosts に以下の設定を追記
    127.0.0.1  ffxi00.pol.com
    
  • FFXI のクライアントを起動
  • 同意画面の所まで進める
  • ProjectXI\bin\client\pXi-Clientv.exe でログイン
  • 同意画面で (同意する) のボタンを押す
  • 初めはキャラがいないので、キャラの新規作成から

結果:

問題が発生したため、pXI-Server-0.6.1.exe を終了します。
ご不便をおかけして申し訳ありません。
○rz

めげずに:

エラーダイアログは出てるが、一応プログラムは続行してるので ログコンソールを見ると not found in DB が多発している。 preset 用ファイルではテーブル名が小文字で統一されているが、 大文字の混ざったテーブル名でアクセスしていて、見つからないと怒られてるらしい。
テーブルはファイルにマップされてるので、 Windows で動く MySQL だと問題ないのかな…

それでもめげずに:

試しに接続してみる。
WTF??
Did not find a match?!?!
やっぱりダメか… ○rz

コードを見てみた:

ProjectXI/src/server/src/CNetwork.cpp
if((CGlobal::connectList.size() > 0)) {
        for(i = (CGlobal::connectList.size()-1); i >= 0; i--) {
                //Does the IP match and is the RecvKey free?
                if ((r.ip == CGlobal::connectList.at(i).ip) && (CGlobal::connectList.at(i).s == 0)) {
                        //We found the RecvKey we were looking for!!
                        CConsole::outTime("Found it");
                        break;
                }
        }
}
else {
        //We don't have a list........
        CConsole::outTime("WTF??");
}
//Confirm we are within range and it is valid
if (i < CGlobal::connectList.size()){
        //Store the newly made socket into the connectList
        CGlobal::connectList.at(i).s = r.s;
        CGlobal::connectList.at(i).out = ListenSocket2;
        mysql_thread_init();
        //Run the PolLobbyComm thread
        CreateThread(0,0,PolLobbyComm,&CGlobal::connectList.at(i),0,0);
}
else {
        //How did we not find a match? :(
        CConsole::outTime("Did not find a match?!?!");
}
この connectList に追加する部分は
DWORD WINAPI LobbyServ(LPVOID lpParam)
{
	<略>
while(1) {
        size = sizeof(ip);
        r->out = 0;
        //Listens for our client to connect
        r->s1 = accept(ListenSocket, (struct sockaddr *)&ip, &size);
        if (r->s1 == INVALID_SOCKET) {
        closesocket(ListenSocket);
        CConsole::outErr("accept failed: %d\n", WSAGetLastError());
                WSACleanup();
                return 1;
        }
        //Find the IP address of the client that connected to us
        r->ip = ip.sin_addr.S_un.S_addr;
        r->s = 0;
        //Push the client onto our list
        CGlobal::connectList.push_back(*r);
}
どうも、 54001 の前に 54230 のメッセージを受け取らないといけないのに、 54230 のメッセージが届いてないので、そんなの知らないって言われてそう。 きっと、54230 の thread が落ちてるんだろうなぁ… *1
(ファイアウォールの例外に設定済みなので、届かないって事はないだろうし)
…とりあえず、テーブル名を修正してから考えよう…

テーブル修正後:

SQL preset データがおかしいので修正したら、 エミュ鯖あっさり動いたw
テーブル名の大文字小文字が Preset データとプログラムが要求する文字列とで ずれてたので修正しただけ。
MySQL だけ Linux で動かすという変則的な事したのがまずかったのかも。( ̄― ̄;)
では、オフライン FFXI で遊んできますー 一人ぼっち !||!○rz

動かしてみた:

  • NPC に話しかけても返事なし
  • 魔法のスクロールを使っても習得せず。スクローズ消えないけど
  • 西サルタでバトルが出来た。とりあえず Tiny Mandragora を倒した。
  • 何故か近くの兎(Savanna Rarab)が襲いかかってきたw リンク?ww
  • 戦闘不能になったのでHPに戻るを選択したら、何故かその場で復活したw
  • 試しに兎に近づいたら襲ってきたw アクティブなのかww
楽しすぎるw

GM に変身した所w

コマンド表:

勝手サーバなのでやりたい放題
o @zone - instantly zone yourself to a specified location (e.g. "@zone 0xZZ").
o @additem - puts an item of your choice in your inventory (e.g. "@additem 0xZZZZ").
o @pos - teleport your character to a given position (e.g. "@pos 1 2 3").
o @posr- changes your position relative to your current position (e.g. "@posr 0 2 0").
o @inject - load a packet to be sent to the client (e.g. "@inject packet.dat").
o @debug - print your characters charid and targid to the server
o @gm - toggle GM flag
o @changejob - change your job, the first parameter is the job, the second is the level (e.g. "@changejob 0xA 75")
o @where - echos your current position to the server console.
o @npc - echos the closest npc to you to the server console.
o @zoneR - reloads a zone  (e.g @zoneR 0xf5 (reloads lower jeuno)).
o @hp -	changes your hp, if it sinks to 0, you die. (e.g @hp 0x00 (you die)).
o @mobani - change the animation of the selected mob (e.g @mobani 0x03 (it dies)). 
o @animation - change your character animation (e.g @animation 0x05 (mount a choco)). 
o @cs - play a cutscene in the current zone (e.g. @cs 0x01).
自分以外いないけどねw

その後:

サーバが落ちたので、起動しなおしたら同じ問題(WTF)発生。 さっき動いたのは偶然か。(´Д`;)

*1: (9/9 追記) 54230 は付属の pXi-Clientv.exe でログインする時のパケットみたいです。 同意確認画面が表示されてから、同意するボタンを押すまでの間にログインしないと、 WTF が表示されるのは、54230 のパケットが送られたアドレスを覚えておいて、そこからの 54001 しか許可しないって処理になってる。のかな?

2008年09月05日(金)

ProjectXI を動かしてみた (アイテム追加)

失敗してた理由:

230 :既にその名前は使われています:2008/09/05(金) 02:02:50 ID:9PMMttxt
    WTHが出るのは同意画面に入る前にClientv動かした場合。
    俺はすくなくともそうなった。 
README にもやり方が書いてあるのに。アホでした。
 Launch FFXI and wait at the screen where you get to accept or
decline the terms of agreement (which occurs after the POL version
check, and before the character selection screen).
 Now launch the pXI-client.exe 
とはいえ、少し動かしてるとすぐ落ちるのは変わらず。

着替え実験:

  • 赤魔75にセット
    @changejob 0x5 75
    
  • 上から順に、エクスカリバー、デュエルグローブ+1、デュエルシャポー、デュエルタバード、デュエルタイツ、デュエルタイツのアイテム取得。
    @additem 0x4764
    @additem 0x3A41
    @additem 0x3AE4
    @additem 0x3AF3
    @additem 0x3B11
    @additem 0x3B20
    
  • 結果

2008年09月07日(日)

ProjectXI を動かしてみた (ポジション指定)

@pos コマンドを使ってみた。 引数は (x, -z, y) らしい。

2008年09月08日(月)

[swfed] ming の png の扱い

SWF ファイル内の非可逆圧縮画像のデータは zlib で圧縮されている。 …で、PNG も zlib 圧縮なので、もしかしてうまい事やってないかなと、 ming のコードを覗いてみた。
% mkdir ming
% cd ming
% cvs -d:pserver:anonymous@ming.cvs.sourceforge.net:/cvsroot/ming login 
% cvs -z3 -d:pserver:anonymous@ming.cvs.sourceforge.net:/cvsroot/ming co -P .
ming が PNG を処理するファイルは、ming/util/png2dbl.c,dbl2png.c の2つ。
png2dbl.c の中身
struct pngdata readPNG(FILE *fp)
{
	<略>
  png_read_image(png_ptr, row_pointers);
普通に libpng 経由で zlib 圧縮/伸張してるっぽいです。残念。

学習した事:

やはり、libpng を使って真面目に zlib 圧縮/伸張しよう。
swfed では replacePNGData の API を用意したけど、 これだと差し替える度に、zlib 処理が走るので、 先に PNG を Lossless (か、それに近い形式)に変換しておいて、 replaceLossless で入れ替えた方が実サービスにはいいかも。 と思い始めた。
zlib 圧縮/伸張の処理は重たいので、毎回動くかすのは勿体無い。

追記 (2009年10月7日追記):

「DefineBitsLossless zlib」で検索してこられたかた用のまとめ。

2008年09月09日(火)

php_qr extension

試しに導入。
普通は↓こちらを使いそうですが、 「実行時にはそれなりの負荷がかかる」と明言してるので、 extension の方を試みました。

php_qr-0.1.0.tgz:

コンパイル
phpize
./configure --enable-qr --enable-qr-gd
make
make install
vi /etc/php.d/qr.ini
チェック
%  php -v
PHP Warning:  PHP Startup: Unable to load dynamic library
'/usr/lib/php/modules/qr.so' - /usr/lib/php/modules/qr.so:
undefined symbol: php_input_from_zval in Unknown on line 0
PHP 5.1.6 (cli) (built: Jul 16 2008 19:53:00)
Copyright (c) 1997-2006 The PHP Group
Zend Engine v2.1.0, Copyright (c) 1998-2006 Zend Technologies
怒られてる…

php_qr-0.3.1.tgz:

よく見たら 0.3.1 が出てました。 こちらは問題なく使えました。
<?php

$data = 'http://pwiki.awm.jp/~yoya/';
$options = array(
    'format' => QR_FMT_JPEG,
    'magnify' => 4,
    );
header('Content-Type: image/jpeg');
qr_output_symbol(null, $data, $options);

メモ:

  • php_qr-0.3.1 + php 5.1.x, 5.2.x で動作確認した
  • configure で php-config を指定しないと動かない事があるので注意
  • php_qr-0.1.0 が動かなかった原因の php_input_from_zval だけど、php-3.0.1, php-4.0.0, php-5.0.0 で grep しても見つからない。何だろコレ

2008年09月10日(水)

movie ftyp 処理

最近、動画関連のページを色々と見回っています。 以下のページを見て何となく ftyp 処理のプログラムを作りました。

プログラム:

実行結果:

% php get_ftyp_brand.phps test.3g2
MajorBrand: kddi
MinorVersion: 0
CompatibleBrand: kddi 3g2a 3gp5 3gp4
% php set_ftyp_brand.phps test.3g2 yoya 1 yoya yoyo yaya yayo \
  | fdmp  -
  addr :  0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00000: 0000 0020 6674 7970 796f 7961 0000 0001      ftypyoya
0x00010: 796f 7961 796f 796f 7961 7961 7961 796f  yoyayoyoyayayayo
0x00020: 0000 0024 7575 6964 6d76 6d6c a88c 11d4     $uuidmvml
需要があるかは分かりませんけど。(´д`)

ついでに:

fdmp のプログラムも晒しておきます。便利なので。

2008年09月11日(木)

png_dump.c 不具合修正

何となく透明度がちゃんと取れてないような…
% png_dump kuriboo4.png
png_filename=kuriboo4.png
png_buff.data_len=166321
(width, height)=(192,224) bpp=8 color_type=6(RGB_ALPHA)
y=0: ffffff(ff)  ffffff(ff)  ffffff(ff)  ffffff(ff)  ffffff(ff) 
	<略>
e4d23d(3d)  e4d23e(3e)  e5d13e(3e)  e5d23e(3e)  e6d23e(3e)  e7d33e(3e)
ん? ff(ff), 3e(3e)… あーーっ…
                case PNG_COLOR_TYPE_RGB_ALPHA:
                    printf("%02x%02x%02x(%02x)  ",
                           image_data[y][4*x],
                           image_data[y][4*x+1],
                           image_data[y][4*x+2],
                           image_data[y][4*x+2]);
あほでした… 修正っと

[swfed] 0.09 リリース

Lossless 画像に関する不具合を幾つか修正しました。

[swfed] Lossless で透明度が効かない件

元データ
% png_dump kuriboo4.png
png_filename=kuriboo4.png
png_buff.data_len=166321
(width, height)=(192,224) bpp=8 color_type=6(RGB_ALPHA)
y=0: ffffff(00)  ffffff(00)  ffffff(00)  ffffff(00)  ffffff(00)  ffffff(00)  fff
fff(00)  ffffff(00)  ffffff(00)  ffffff(00)  ffffff(00)  ffffff(00)  ffffff(00)
入れ替えたデータ
% png_dump game-78.png
png_filename=game-78.png
png_buff.data_len=46616
(width, height)=(192,224) bpp=8 color_type=6(RGB_ALPHA)
(width, height)=(192,224) bpp=8 color_type=6(RGB_ALPHA)
y=0: 9cffff(78)  ffffff(00)  ffffff(00)  ffffff(00)  ffffff(00)  ffffff(00)  fff
fff(00)  ffffff(00)  ffffff(00)  ffffff(00)  ffffff(00)  ffffff(00)  ffffff(00)
1つ目のpixelだけデータが壊れてました。これがヒントになれば良いのですが…
SWF 的には ARGB の並びなので 0x789cffff 。
何となくスタックのアドレスっぽいなぁ…

2008年09月12日(金)

Skype API wrapper class (PHP) を試してみた

Skype protocol の仕様は公開されていないので、 Skype のインスタンスを起動して、それを proxy のように利用するのが Skype API の方式。 Linux では D-Bus で通信するらしい。 D-Bus は X protocol 上で動くので、PHP でライブラリを作るのは 無理があるかなと思ったら、それらしいライブラリのページを発見。 (ごめんなさい、発見というは嘘で 当人 からチラっと話しを聞いてました)

Skype の起動:

% wget http://www.skype.com/go/getskype-linux-centos
% wget http://gd.tuwien.ac.at/infosys/phone/skype/rpm-public-key.asc
% su
Password:
# rpm --import rpm-public-key.asc
# yum -y install skype-2.0.0.72-centos.i586.rpm
あとは、xterm 上で
% skype

dbus ライブラリの install:

% su
# yum install dbus-devel

D-Bus extension の install:

% wget http://labs.gree.jp/data/source/php-dbus-0.1.0.tgz
% tar xvfz php-dbus-0.1.0.tgz
% cd php-dbus-0.1.0
% cd  
% phpize
% ./configure
% make
% su 
# make install
# vi /etc/php.d/dbus.ini

Skype API wrapper class の install:

wget http://labs.gree.jp/data/source/php-skype-0.1.0.tgz
tar xvfz php-skype-0.1.0.tgz
cd php-skype-0.1.0
cp -r Skype.php  Skype /usr/share/pear/.

試してみた:

サンプル
require_once 'Skype.php';
$skype = new Skype("skype_api_client", Skype::default_protocol, true);
for (;;) {
  $skype->poll(86400);
}
結果 (X の通信を利用するので Skype を動かした画面上 で xterm を起動して実行。又は、適当な端末上で env DISPLAY=<skype を動かしたホスト>:<skype を動かした画面の番号> としても大丈夫)
% php skype_test.php
poll:   timeout=86400
PHP Fatal error:  Call to a member function poll() on
a non-object in /usr/share/pear/Skype.php on line 126
クラスインスタンスの $dbus_connection が未定義なのが原因らしい。 見たところ connect がそれっぽい。
require_once 'Skype.php';
$skype = new Skype("skype_api_client", Skype::default_protocol, true);
$skype->connect();
for (;;) {
  $skype->poll(86400);
}
再チャレンジ (Skype 側に「接続されてます」的なダイアログが出るので、OK を選択する事)
% php skype_test2.php
invoke: NAME skype_api_client
reply:  OK
invoke: PROTOCOL 7
reply:  PROTOCOL 7
poll:   timeout=86400
notify: CONNSTATUS ONLINE
notify: CURRENTUSERHANDLE (ログインする時のユーザ名が表示される)
notify: USERSTATUS ONLINE
poll:   timeout=86400
それっぽく動いてる感じ。

余談:

コピペで動作チェックしてたら typo に気づいちゃった。連絡しようかしら。
でDBusConnectionオブジェクトを取得します。$typeには

DBUS_BUS_SESSION 
DBUS_BUS_SYSETM   ←☆ SYSTEM ☆
DBUS_BUS_STARTER
あと、 ↑これの
require_once 'Skype.php';
$skype = new Skype("skype_api_client", Skype::default_protocol, true);
for (;;) {
  $skype->poll(86400);
}
このサンプルは、
require_once 'Skype.php';
$skype = new Skype("skype_api_client", Skype::default_protocol, true);
$skype->connect();
for (;;) {
  $skype->poll(86400);
}
とすれば、そのまま動かせるのでいい感じ。
もしかしたら、ドキュメント作成当時は constructor で connect しちゃおうか迷ってたのかしら。

2008年09月13日(土)

[swfed] 0.10 release

不具合修正:

ビットマップ画像にゴミが混ざる問題ですが、
old_buff = bitstream_buffer(bs2, 0);
bitstream_close(bs2);
old_size = bitstream_length(bs2);
compsize = old_size; // too enough size
tmp_buff = malloc(compsize);
compress(tmp_buff, &compsize, old_buff, old_size);
bitstream_buffer は bitstream の中のデータを覗き見る関数なので、 このタイミングで close しちゃダメです。
(関数名が分かりにくいので再考しないと…)
bitstream_close(bs2) の時点で old_buff が開放されるので、 compress で old_buff を参照する前に、どこかで別の目的で使われたり、参照できない領域になってたりと、非常にマズイ事態になってました。
さて、修正っと。
old_buff_ref = bitstream_buffer(bs2, 0);
old_size = bitstream_length(bs2);
compsize = old_size; // too enough size
tmp_buff = malloc(compsize);
compress(tmp_buff, &compsize, old_buff_ref, old_size);
bitstream_putstring(bs, tmp_buff, compsize);
bitstream_close(bs2);
bitstream_close(bs2) はあと1行上でも良いので、後で書き換えよう。

透明度:

リリース:

Lossless 画像データを bitmap 形式 PNG で差し替える際に、
画像データ部にゴミの値が混ざる不具合を修正しました。
ごめんなさい。ごめんなさい。ごめんなさい。

課題:

swfdump を早く公開できるよう、ブラッシュアップを進めないと。 あと、画像一覧で PNG 画像が出てなかった気がするので、そちらも見直そう。

2008年09月14日(日)

giflib の使い方

マニュアル発見

6行で使い方を説明:

ファイル名かファイルディスクリプタ(int fd) を渡して、GIF ファイル画像を開く。
  • GifFileType *DGifOpenFileName(char *GifFileName)
  • GifFileType *DGifOpenFileHandle(int GifFileHandle)
実際に画像を内部表現データ(in-core allocated structures)に取り込むのは、これ。
  • int DGifSlurp(GifFileType *GifFile)
後は、GifFileType 構造体のメンバーを辿って欲しい情報を引き出すだけ。

さて…:

使い方は簡単そうだけど、(ファイルでなく)データ配列から読む方法が欲しいので もちょっと調べないとダメか。

画像合成:

何をやりたいのかというと、いわゆるアバター合成みたい処理を 自前で実装できないかなーと。
そして、giflib のソースツリーを眺めていて、 そのまんま *1 のツールが入っているのを発見。

gifovly.c:

* History:
* 6 May 94 - Version 1.0 by Eric Raymond.
あの 有名な人 かな。同姓同名だよね。きっと。と思ったら、
    "Gif compiler,\t\tEric S. Raymond\n\
(C) Copyright 1992 Eric S. Raymond, all rights reserved.\n";
ホンモノか。(;゜д゜)
gif_lib.h を読むと、
 * 26 Jun 96 - Version 3.0 by Eric S. Raymond (Full GIF89 support)
なるほど。PNG でかなり貢献してる人ってイメージがあったけど、GIF もそうだったのね。

quantize.c:

ついでに、256色への減色処理も発見 うーん。mediun cut 法かな。これ… 工夫の余地がありそう。
続き

*1: 後でよくコードをよく読んだら勘違いでした。詳しくは http://diary.awm.jp/~yoya/?20080916#200809161S5 参照。

2008年09月15日(月)

giflib の使い方 (2)

ファイルでなくデータ配列で GIF データを渡したいと思ってた所に、 便利な関数を発見。(3.1.0 で追加された)
/*******************************************************************
 * GifFileType constructor with user supplied input function (TVT)
 ******************************************************************/
GifFileType *
DGifOpen(void *userData,
         InputFunc readFunc) {

DGifOpen の使い方:

giflib/gba/giftest.c のコードでは、データ配列を渡すのに、
int readFunc(GifFileType* GifFile, GifByteType* buf, int count)
{
    char* ptr = GifFile->UserData;
    memcpy(buf, ptr, count);
    GifFile->UserData = ptr + count;
    return count;
}
の関数を用意して、
u8 *userData;  /* u8 = unsigned char */
GifFileType * GifFile = DGifOpen(userData, readFunc);
のように使ってました。
ただ、これだと用意したデータ配列の長さに関係なく、 GIF 的にキリのよい所までデータを参照するはずなので、 このまま真似すると、buffer overflow しそうです。
libpng だと png_get_io_ptr があって、任意の構造体でデータを渡せるように なっています。 giflib では (VoidPtr)UserData がそれに相当するようなので、 この仕組みを使って境界チェックさせる事にします。

さっそく実装:

DGifOpen & DGifSlurp でデータを取り込んでから、 GifFileType 構造体のメンバー(の幾つか)を表示。
% gcc -W -Wall gif_dump.c -lgif
% ./a.out aria.gif
gif_filename=aria.gif
gif_buff.data_len=24207
(SWidth, SHeight)=(200, 150)  SColorResolution=8  SBackGroundColor=0
ColorMapObject:
        ColorCount=256  BitsPerPixel=8
        Colors= (00,00,00) (ff,ff,ff) (96,53,58) (29,1b,1c) (e6,b0,b8) (b2,69,76) (37,26,29) (d6,96,a1)
image_count=1
# image no=0
(Left, Top) = (0, 0)  (Width, Height) =  (200, 150) Interlace=0
No ColorMap
それっぽく dump 出来ました。
続き

2008年09月16日(火)

giflib の使い方 (3)

アニメーションGIF対応の為なのか、giflib は内部表現として、 (複数枚の)画像データを SavedImage の配列 SavedImages で持っています。
89a GIF ファイルは↑このページから図をコピペすると、
 GENERAL FILE FORMAT

        +-----------------------+
        | +-------------------+ |
        | |   GIF Signature   | |
        | +-------------------+ |
        | +-------------------+ |
        | | Screen Descriptor | |
        | +-------------------+ |
        | +-------------------+ |
        | | Global Color Map  | |
        | +-------------------+ |
        . . .               . . .
        | +-------------------+ |    ---+    
        | |  Image Descriptor | |       |    
        | +-------------------+ |       |    
        | +-------------------+ |       |   
        | |  Local Color Map  | |       |-   Repeated 1 to n times
        | +-------------------+ |       |    
        | +-------------------+ |       |    
        | |    Raster Data    | |       |    
        | +-------------------+ |    ---+    
        . . .               . . .    
        |-    GIF Terminator   -|    
        +-----------------------+
 SCREEN DESCRIPTOR

              bits
         7 6 5 4 3 2 1 0  Byte #
        +---------------+
        |               |  1
        +-Screen Width -+      Raster width in pixels (LSB first)
        |               |  2
        +---------------+
        |               |  3
        +-Screen Height-+      Raster height in pixels (LSB first)
        |               |  4
        +-+-----+-+-----+      M = 1, Global color map follows Descriptor
        |M|  cr |0|pixel|  5   cr+1 = # bits of color resolution
        +-+-----+-+-----+      pixel+1 = # bits/pixel in image
        |   background  |  6   background=Color index of screen background
        +---------------+          (color is defined from the Global color
        |0 0 0 0 0 0 0 0|  7        map or default map if none specified)
        +---------------+
といった構造になっていて、
Dimention 定義としては、まず全フレーム共通の Screen があり、 それと別に各フレームに、その Screen のどの領域に表示するか(mapping 的な意味で)の (left,top)-(width,height) の定義があります。
アニメーション GIF は colormap(色インデックスの表)を、 各フレーム(ローカル)に持てるので、giflib 的には、 全体としての ColorMap と SavedImages の各フレームの Colormap の 両方に構造体メンバーとして用意しておいて、 データを持たない方は ColorMap に NULL を入れる。 といったデータ表現になっています。

dump コードを改良:

以上の情報を元に、もうちょっと見やすく GIF を dump するよう改良。 あと、giflib/windows/GifWin.cpp に dump コードを見つけたので参考にしてます。

実験ページ:

POST したファイルを dump する実験ページを作りました。 SWF Editor では jpeglib を使う必要がなかったので、JPEG の dump は未対応。
ただ、JPEG はセグメント分割処理だけ実装したので、そのページを用意。

SBackGroundColor:

ヘッダを見ると SBackGroundColor のコメントが、
      SBackGroundColor;         /* I hope you understand this one... */
となっています。これは何かの挑戦状かと思い調べてみました。
GIF89a 仕様書によると、
iii) Global Color Table Flag - Flag indicating the presence of a
Global Color Table; if the flag is set, the Global Color Table will
immediately follow the Logical Screen Descriptor. This flag also
selects the interpretation of the Background Color Index; if the
flag is set, the value of the Background Color Index field should
be used as the table index of the background color. (This field is
the most significant bit of the byte.)

Values :    0 -   No Global Color Table follows, the Background
                  Color Index field is meaningless.
            1 -   A Global Color Table will immediately follow, the
                  Background Color Index field is meaningful.
つまり BackGroundIndexColor のフィールドと別に、 そのフィールドの値が意味のあるものかを示すフラグが Global Color Table Flag に入っています。
giflib ではどう表現しているのかとコードを読むと、
DGifGetScreenDesc(GifFileType * GifFile) {
    <↓特に if 文とか無しで>
    GifFile->SBackGroundColor = Buf[1];
ダメじゃん… ('A`)
ちなみに、この SBackGroundColor。
カバーされない pixel に表示する色という事なので、恐らくは、 画像表示アプリケーション側で背景色が定義されていない場合の 透明度インデックスの pixel の場所、又は、 Screen で定義されつつも、どのフレームでも覆い切れていない領域を どの色で表示するかを決めるものとのようですが、 まず使う状況は無さそうですし、 下手に GIF_BACKGROUND_MEANINGLESS なんて定義を追加しても 混乱するだけだろうから、今のままが良い気がします。

ColorMap の Size について:

実際に ColorMap を眺めてみると後半に使われてなさそうな色インデックスの 定義がずらっと並んでいる事が多いです。
仕様書を読むと、
<Packed Fields>  =      Global Color Table Flag       1 Bit
                        Color Resolution              3 Bits
                        Sort Flag                     1 Bit
                        Size of Global Color Table    3 Bits
	<略>
  3 x 2^(Size of Global Color Table+1).
2 のべき乗で表現しているので、例えば 33 個色を使う場合は 64 個の表を作って そのうちの33個を実際に利用し、残り31個はダミーを埋めて置くしかない。
勿体無いけど GIF がフォーマット的に 3 bit で表現しちゃってるし、仕方ない。

gifovly.c:

transparent GIF を期待していたのですが、よくコードを見たら、 どの色インデックスを透明として扱うのか明示的に指定するようになってました。 transparent は GIF89拡張に依存しちゃうのでサンプルとしては この方が良いんだろうけど、目的に合わず残念。
素直に ImageMagick 使いますか…
続き

2008年09月17日(水)

Skype API wrapper class (PHP) を試してみた (2)

試しに、Bot を動かしてみました。
% cd example
% php skype_bot.php
PHP Fatal error:  Uncaught exception 'Exception' with message 'plugin file not found [Skype/Bot/Plugin/Log.php]' in /usr/share/pear/Skype/Bot.php:54
Stack trace:
#0 /home/yoya/src/php/php-skype-0.1.0/example/skype_bot.php(14): Skype_Bot->loadPlugin('log', Array)
#1 {main}
  thrown in /usr/share/pear/Skype/Bot.php on line 54
えーっと…
51 if (class_exists($klass) == false) {
52         $path = sprintf("Skype/Bot/Plugin/%s.php", ucfirst(strtolower($plugin_id)));
53         if (is_readable($path) == false) {
54                 throw new Exception(sprintf("plugin file not found [%s]", $path));
55         }
56         require_once($path);
57 }
あー。Skype.php と同じディレクトリ上で実行する前提なのね。
この前提はよくないので、とりあえず事前チェックをやめて require_once で失敗したら FAULT が発生するに任せてみます。
というわけで、is_readable のブロックをごっそりコメントアウトと…
% php skype_bot.php
invoke: NAME skype_bot
動いt…
(1分後)
きゃーーっ (´Д`;)
% php skype_bot.php
invoke: NAME skype_bot
PHP Warning:  dbusconnection::sendwithreplyandblock(): dbus_connection_send_with_reply_and_block() failed (Did not receive a reply. Possible causes include: the remote application did not send a reply, the message bus security policy blocked the reply, the reply timeout expired, or the network connection was broken.) in /usr/share/pear/Skype.php on line 106
PHP Fatal error:  Uncaught exception 'Exception' with message 'dbus_connection_send_with_reply_and_block() failed' in /usr/share/pear/Skype.php:108
Stack trace:
#0 /usr/share/pear/Skype.php(85): Skype->invoke('NAME skype_bot', -1)
#1 /usr/share/pear/Skype/Bot.php(89): Skype->connect()
#2 /usr/share/pear/Skype/Bot.php(32): Skype_Bot->_startup()
#3 /home/yoya/src/php/php-skype-0.1.0/example/skype_bot.php(16): Skype_Bot->run()
#4 {main}
  thrown in /usr/share/pear/Skype.php on line 108
試しに、"skype_bot" を "skype_api_client" に変えたら
% php skype_bot.php
invoke: NAME skype_api_client
reply:  OK
invoke: PROTOCOL 7
reply:  PROTOCOL 7
	<色々沢山>
あ…
NAME の右には(自己申告の)アプリケーション名を入れますが、 新規のアプリケーション名で Skype に接続すると、Skype 側で
他のプログラムが Skype の使用を希望しています
プログラム "skype_bot" が Skype への接続を試みています。
許可しますか?
[ ] この選択を記憶する
                               [  はい  ] [  いいえ  ]
といったプロンプトが出るので、これに OK しないと接続失敗します。(当たり前)
というわけで、[ はい ]を押したら解決しました。

debug:

とりあえず動きがあるので一度、debug 出力を止めようかと思い、 第二引数の debug フラグを false にしました。
$bot = new Skype_Bot("skype_bot", false);
でも、今まで通り debug っぽいメッセージが沢山出てきます。
なんでだろうと、コードを追ってみると
class Skype_Bot {
    public function __construct($id, $debug = false) {
        $this->debug = $debug;
        $this->skype = new Skype($id, Skype::default_protocol, $debug);
    }
ふむ。
class Skype {
    public function __construct($id, $protocol = self::default_protocol,
                                $debug = false) {
        $this->id = $id;
        $this->protocol = intval($protocol);
        $this->debug = $debug;

ふむふむ。
    public function invoke($s) {
	<略>
        $this->_debug("invoke: %s\n", $s);
ふむふむふむ。
    protected function _debug($format) {
        $args = func_get_args();
        array_shift($args);
        vprintf($format, $args);
    }
… (゜Д゜)!
    protected function _debug($format) {
        if (! $this->debug) {
             return ;
         }
        $args = func_get_args();
        array_shift($args);
        vprintf($format, $args);
↑こんな感じに書き換えとこっ。(実際は行数を変えないように汚い書き方してるけど)

今日の成果:

% php skype_bot.php
exception 'Exception' with message 'unsupported property [DIALOG_PARTNER]' in /usr/share/pear/Skype/Object.php:68
Stack trace:
#0 /usr/share/pear/Skype.php(226): Skype_Object->set('DIALOG_PARTNER', 'yoya_test')
#1 /usr/share/pear/Skype.php(139): Skype->handleChat('#yoya_test/$y...')
#2 [internal function]: Skype->callback(Object(dbusmessage))
#3 /usr/share/pear/Skype.php(126): dbusconnection->poll(86400)
#4 /usr/share/pear/Skype/Bot.php(36): Skype->poll(86400)
#5 /home/yoya/lang/php/skype_bot.php(16): Skype_Bot->run()
#6 {main}
(⊃д⊂)
でも、/var/tmp にはファイルが出来ていて、ログが残ってはいます。
2008/09/17 02:51:52     [SAID]  よや@test(yoya_test)     テスト
知らないメッセージも(利用できるかは別として)とりあえず受け取れるようにはして、 更に、そのメッセージの対処コードを追加すると良さそうですが、 もう眠いので、今日はここまでで… Zzz

2008年09月18日(木)

Skype API wrapper class (PHP) を試してみた (3)

DIALOG_PARTNER でエラーになる件:

Skype/Chat.php:

    protected   $property_def_list = array(
	<略>
        // and more...
        'DIALOG_PARTNER' => array(
            'default'   => true,
            'type'      => Skype::property_type_string,
        ),

今度は MYROLL でエラー:

% php skype_bot.php
exception 'Exception' with message 'unsupported property [MYROLE]' in /usr/share/pear/Skype/Object.php:68
Stack trace:
#0 /usr/share/pear/Skype.php(226): Skype_Object->set('MYROLE', 'USER')
#1 /usr/share/pear/Skype.php(139): Skype->handleChat('#yoya_test/$y...')
#2 [internal function]: Skype->callback(Object(dbusmessage))
#3 /usr/share/pear/Skype.php(126): dbusconnection->poll(86400)
#4 /usr/share/pear/Skype/Bot.php(36): Skype->poll(86400)
#5 /home/yoya/lang/php/skype_bot.php(16): Skype_Bot->run()
#6 {main}
ふむ。MYROLL も追加と。
        // and more...
        'DIALOG_PARTNER' => array(
            'default'   => true,
            'type'      => Skype::property_type_string,
        ),
        'MYROLE' => array(
            'default'   => true,
            'type'      => Skype::property_type_string,
        ),
    );
とりあえず、安定して動くようになりました。(*'ω'*)

常駐開始:

さて /var/tmp はまずいので、自分の home 以下を指定して常駐開始。
(php skype_bot.php >& ~/log/skype/log.txt ) &
[1]+  Exit 255                ( php skype_bot.php >&~/log/skype/log.txt )
あれれ…
log.txt を読むと、
PHP Fatal error:  Uncaught exception 'Exception' with message 'parameter [dir] s
eems to be invalid or not set' in /usr/share/pear/Skype/Bot/Plugin/Log.php:26
Stack trace:
#0 /usr/share/pear/Skype/Bot.php(62): Skype_Bot_Plugin_Log->__construct(Object(S
kype_Bot), Array)
#1 home/yoya/lang/php/skype_bot.php(15): Skype_Bot->loadPlugin('log', Array
)
#2 {main}
  thrown in /usr/share/pear/Skype/Bot/Plugin/Log.php on line 26
うわーぃ。~/log/skype を指定したけど ~ はダメか。
$bot->loadPlugin(
    "log",
    array(
        'dir'               => '~/log/skype',
        'chat_topic_filter' => null,
        'chat_id_filter'    => null,
        ));
/home/yoya/log/skype のように絶対パス指定にしとこう。
        'dir'               => '/home/yoya/log/skype',
常駐成功と♪
さて、特定のキーワードが出たらメールを送るとか出来るといいな。

2008年09月19日(金)

GIF89a Overlap for PHP

giflib/util/gifovly.c に触発されて、gifovlap extension なるものを実装開始。
gifovly.c がアバター画像作成っぽい処理だと思っていましたが、 よくコードを読むと、アニメーションGIFの各フレームを重ね合わせる処理をしていて、 つまり dispose 的な処理をした場合の最終フレームの画像を表示する機能のようです。
だから overlay なんですね。非常に納得。
で、自分が作りたいのは(overlay でなく) overlap 処理なので、 の3本立てで改造を試みて、動いた所で PHP binding を実装する。
といった目論見でいます。 そして、am3:12 頃 SF.NET にプロジェクト登録したら am3:23 に Approved メールが来たw SF.NET はどんだけ即決なんですかww (プロジェクトページは penging になってるので、制限付きで場所だけ許可って所かな)
といった感じで自分で退路を断ったので、作る覚悟が出来ました。
秋分の日明け位に pre-alpha なコードをリリースできる所まで持っていければなぁ。 と思っています。

2008年09月20日(土)

giflib でフィルタ処理

giflib はデータ構造的にはエンコード用とデコード用で共通ですが、 各々の Open と Close が別になっている為、 フィルタを書くのには面倒な API になってます。
こんな感じ。
GifFileType *GifFileIn, *GifFileOut;
GifFileIn  = DGifOpen(...);
GifFileOut = EGifOpen(...);
<データ入力 →  GifFileIn の画像データを GifGileOut にコピー →データ出力>
DGifCloseFile(GifFileIn);
EGifCloseFile(GifFileOut);
仮に、Open と Close が共通であれば 必要な部分だけデータを書き換えるだけですが、 ↑これらを見ると、カラーマップとかピクセルとか Extension ブロック 等を明示的に一つ一つコピーする処理をかいてるので、手間をかけないとダメそう。
rotat や rsize は全部コピーしちゃうと、後で捨てるデータも 出てくるので、これはこれで合理的なんですけど。 例えば、特定のインデックスのピクセルを塗りつぶすとかの処理を 書こうとする時は、無駄な手間って感じがするんですよね。
(処理コストは減らないけど、実装コストを減らすのに) GifFileClone(GifFileIn, GifFileOut); とか作ると楽になるかな。
続き

追記 (2008/09/27):

調べた事を以下のページにまとめました。

2008年09月21日(日)

giflib の使い方 (4)

読み込みや書き出しの方法として、大雑把に2つあります。

順次読み出し:

giflib/util 以下の gifrotat.c と gifrsize.c で使っている方式です。
while ((DGifGetRecordType(&GifFile, &RecordType)) &&
       (RecordType != TERMINATE_RECORD_TYPE)) {
    switch(RecordType) {
      case SCREEN_DESC_RECORD_TYPE:
        DGifGetScreenDesc(&GifFile);
        break;
      case IMAGE_DESC_RECORD_TYPE:
        DGifGetImageDesc(&GifFile);
        break;
      case EXTENSION_RECORD_TYPE:
        DGifGetExtension(&GifFile, &GifExtCode, &GifExtension);
        while ((GifExtension != NULL) &&
	       (DGifGetExtensionNext(&GifFile, &GifExtension) == GIF_ERROR)) ;
        break;
      default:
       break;
}
こんな感じ。
尚、case の中で DGifGet... 系を呼ばないと、 次の DGifGetRecordType で失敗します。
parse 処理を想像すれば仕方ないですが、途中 parse を飛ばせないって事で。
出力は、D(Decode) を E(Encode)に、そして Get を Put に変更すれば大体 OK.

まとめ読み出し:

入力(Decode)は
DGifSlurp(&GifFile);
出力(Encode)は
EGifSpew(&GifFile);
どちらもその関数の中で順次読み出し/書き出しをしてるだけです。
続き

2008年09月22日(月)

最小公倍数

↑このツールで複数GIFファイルのフレーム数の最小公倍数を算出する必要があるので、 とりあえずそれっぽいのを実装。
確か、お互いに引き算し合って残った数値がそれとかいう理屈だったので。 そのままコードにしてみる。 何となく動いてる感じ。
% ./a.out 6 8
gcd(6, 8) => 2
lcm(6, 8) => 24
ふと、ユークリッドの互助法を思い出し google の検索で以下のページを発見。 なるほど。余りを使ってもよいですね。 これで多分、OK
最後の (a > b)?a:b は 片方が0のはずなので a+b でいいかなとか 一瞬誘惑にかられましたが、後で混乱するのであえてこのままで。 *1
あと、確か、もっと速いアルゴリズムがあったと思いますが、 そこそこ複雑だったはずなので、 性能面が問題になるまではこのコードで行きます。
*1: (a)?a:b という手もあったか…

2008年09月23日(火)

giflib でフィルタ処理 (2)

フィルタを作る準備としてとりあえず、Encode/Decode だけするプログラムを 順次処理方式と、そうでない方式で作成してみました。 後者の方がシンプルなコードになるけど、 処理を最小にしようとすると前者を使うしかないかも。
状況によって使い分けって事で。

補足:

念の為に補足しとくと、EGifOpenFileHandle に stdout を渡した方が シンプルになりますが、データ配列のままごにょごにょする予定なので、 こんな回りくどいコードになってます。
swfed でも流用するつもりですし。(*'ω'*)

Close 処理が微妙な…:

GIFアニメはカラーマップをグローバルなのと別にフレーム毎(ローカル)に持てますが、 グローバルなのと最終フレーム(←終了時に処理対象カーソルが指してるフレーム) のカラーマップしか Free してないような… (´Д`;)
582 DGifCloseFile(GifFileType * GifFile) {
	<略>
600     if (GifFile->Image.ColorMap) {
601         FreeMapObject(GifFile->Image.ColorMap);
602         GifFile->Image.ColorMap = NULL;
603     }
604 
605     if (GifFile->SColorMap) {
606         FreeMapObject(GifFile->SColorMap);
607         GifFile->SColorMap = NULL;
608     }
多分、正しくは↓このはず。
for ( i = 0 ; i < GifFile->ImageCount ; i++ ) {
    GifImageDesc *ImageDesc = & GifFile->SavedImages[i].ImageDesc;
    if (ImageDesc->ColorMap) {
        FreeMapObject(ImageDesc->ColorMap);
        ImageDesc->ColorMap = NULL;
    }
}

if (GifFile->SColorMap) {
    FreeMapObject(GifFile->SColorMap);
    GifFile->SColorMap = NULL;
}
でも、何か勘違いしてるのかなぁ…
そんな分かりやすい不具合があったら 誰か困って即刻直りそうですし。
又は、実際のところ、 フレーム毎にカラーマップを持つ GIF アニメ自体絶滅種 (形式的にはありうるけど、実際には作られないorまともに動かない)ので、 頑張っても無駄とか。(無駄な所を頑張るのは好きなのでむしろ嬉しいけどw)
不安なので、SavedImages のデータ構造を生成してる場所、 よくチェックしておこ。

2008年09月24日(水)

Skype API wrapper class (PHP) を試してみた (4)

何時の間にか作者に捕捉されてましたw github での fork & pull をお望みのようなので、 github にアカウントを作成 *1 して、何も考えず fork 。 手元に持ってきて手動 merge 開始。
git clone git://github.com/yoya/php-skype.git
適当にいじって動かすのは簡単ですが、 commit を意識するなら真面目にいじらないとです。
例えば、Char.php の配列のメッセージを追加するにも並び順 (このコードの場合、どの protocol version で追加されたものか) を崩す訳にはいきませんし。
とりあえず間違えようのない *2 debug メッセージを isDebug で分岐する所だけでも、 時間を見つけて commit してみようかしら。
*1: 自分のピクチャを貼り付けたいけどメニューが見つからず。むー。
*2: とはいいつつ、(isDebug() == false) とか (isDebug() == FALSE) とか、 (isDebug() === false) か、はたまた (! isDebug()) にするかは 周りのコードにあわせた方が良いので、 間違いようが無いというのは言いすぎかも。
見たところ、一番初めのもののようでした。 まぁ、自分も一番初めのが好きですし、そもそも (! isDebug()) は C 言語脳的っぽくて嫌んですが。:)


2008年09月25日(木)

PHP で 3D plot

GIF 画像合成では減色処理が必要になるので、 減色アルゴリズムの検証として色んな画像ファイルに対して、 R,G,B ヒストグラムを 3D にマッピングしてみようかと、 軽く 3D plot 処理を考えていたところ、 以下の PHP ライブラリを紹介してもらいました。 早速実験。
$x_axis = $world->createObject('cube', array(128, 4, 4));
$x_axis->setColor(new Image_3D_Color(255, 100, 100));
$x_axis->transform($world->createMatrix('Move', array(64, 0, 0)));
$y_axis = $world->createObject('cube', array(4, 128, 4));
$y_axis->setColor(new Image_3D_Color(100, 255, 100));
$y_axis->transform($world->createMatrix('Move', array(0, 64, 0)));
$z_axis = $world->createObject('cube', array(4, 4, 128));
$z_axis->setColor(new Image_3D_Color(100, 100, 255));
$z_axis->transform($world->createMatrix('Move', array(0, 0, 64)));
うーん。とりあえず軸を書いてみたのですが、使うのめんどいし、 スケールが把握できないです。。(64 の値は探って見つけた値。。)
線(という考え自体ダメダメですが ^^;)をひくにも、 ココからココって指定できなくて、 物体を置いた後に移動(必要なら回転も)しなきゃだし。
そもそもオーバースペックな感じがします…

自分で作っちゃえ:

イメージ図。(図の d は微分ではなく、distance の頭文字です。ごめんなさい)

簡単なグラフを書くのが目的なので、
  • ワイヤーフレームのみで Zバッファ無し
  • 座標変換は世界座標へのオイラー角回転のみ
  • カメラ位置決めうち = (0, 0, $distance_to_eye)
  • その他、一般的な 3D API は殆ど(一切といっていいかも)無し
という究極の手抜きっぷりの簡易3Dライブラリを作ってみました。 仕様は↓こんな感じ。
  • スケールは全て pixel 単位
  • モニタ表面から自分の目までの物理距離は明示的に指定
  • 画像の中央を (0, 0, 0) の原点として計算する
  • x軸は右方向、y軸は上方向、z軸は手前方向。(右手座標系)
  • 一応、原点を中心とした回転は出来る。(x軸, y軸, z軸 の順で適用)
  • 任意の色で点を打てる。任意の色で線が引ける
自分の用途にはこれで十分。これ位なら 30分で作れますし。:-)
続く

2008年09月26日(金)

Skype on Linux がメモリを使いすぎる問題

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
31651 yoya      16   0  782m 652m 3164 R  0.7 65.2 194:34.40 skype
えーっと… メモリ1G で 65% 食ってるのって、幾らなんでも太りすぎ。(´Д`;)

Skype 再起動:

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
25343 yoya      15   0 80376  33m  11m S  1.0  3.3   0:02.03 skype
とりあえず、原因を切り分ける為に Skype bot を止めました。

あたし彼女のガイドライン

乗り遅れないように若者文化を勉強勉強と…
90 :水先案名無い人 :2008/09/25(木) 16:41:54 ID:32KaDK9t0
ガイドライン来て判ったこと。
さんざん馬鹿にしておきながら
原作を超えてない点…
確かにある意味才能だよね。 共感は出来ないけど。^^;

参考:


2008年09月27日(土)

GIF画像の色分布表示

これ の続き
4次元ヒストグラムを作っても見難そうなので、頻度情報は諦めて、 使っている色を RGB=XYZ 空間にプロットするだけのグラフを作ってみました。

例:

画像1の色分布 やはりパステルカラーな絵だとこうなるのね。^^; 画像2の色分布 ますます傾向が顕著にw

プログラム:

以下のページでグラフを生成できます。(JPEG, PNG でも動作します。重たいけど) コードは以下の2つです。.phps の拡張子を .php に変更して使って下さい。 コマンドラインにも対応。
% php colordist.php  chara-298.GIF  > chara-298.PNG

備考:

初め、対応する色でそのまま plot したところ、 (0, 0, 0) に近づくほど量が少ないような(誤った)印象を受けたので、 暗い色を明るく補正するようにしました。
NG 版は↓こんなです。
画像1の色分布 (NG版) 画像2の色分布 (NG版)

KAIST からの Crawl

たまにやたらと重たい事があるので、アクセスログを見ると、 KAIST からバースト的なアクセスが来てました。
[2008/09/27:12:06:31 +0900] dor18184.kaist.ac.kr "20020627-2-S1" ""
"Mozilla/5.0 (compatible; MJ12bot/v1.2.2; http://www.majestic12.co.uk/bot.php?+)"
サーバが貧弱なうちとしては困るので、ブロックする設定を入れました。
多いだけなら google もそうなのですが、 という訳です。

MJ12bot:

どうも去年の10月ぐらいから、ウイルスによって拡大するニセモノのMJ12botが
跋扈しているようで ^^;; ちなみにホンモノのボットの最新版はv1.2.1らしいです。
majestic12のなかのひともたいへんですね。よくよく調べてみると、ホンモノの
MJ12bot (v1.2.1でした)もときどきアクセスにきているようです。そっちは大変
お行儀がよく、4, 5ページクロールしたらすぐいなくなってるようでした。
なるほど?

KAIST:

KAIST 自体はまともな学術機関のようです。 1学生or1ホストが暴走してるだけっぽいですね。

2008年09月28日(日)

memcachedを使ったPHPのシングルトン実装?

以前、以下のページを参考にコードを書かれて困ったのを思い出したので。 先にことわっておくと、このページ自体に罪はない *1 ですし、限定的な条件では利用できる *2 技術だと思いますが、ありがちな罠が幾つかあるので列挙。

排他制御:

ただし今回の実装は排他制御を行っていないのでそのままでは使えませんが…。
アプリケーション全体でのデータ共有が目的なので、 そのレベルのロック処理を自分で入れて使って下さい。
使いたい人はガンバレ…

serialize:

クラスのオブジェクトデータを保存する際に serialize するので、 クラスインスタンスに serialize 不可な変数が入った時点で破綻します。

object サイズ:

この方式では、オブジェクトを生成/破棄する度に unserialize/serialize & memcache との入出力処理が働きます。
つまり、インスタンス変数に巨大なデータを入れたまま放置すると危ないです。 そもそも、「その」リクエスト処理で必要ないデータまで入出力するのはどうかと…

ゴミデータ:

object サイズの件と絡みますが、 データが要らなくなった場合に明示的に破棄しないとダメで、 それをアプリケーション全体レベルで考える必要があります。

クラスの改造:

クラスファイルを update すると、memcache 上のイメージと合わなくなるかも しれません。停止メンテナンスですか、そうですか。

slashdotやmixiも使っている?:

...から大丈夫でしょ。という事を言われたのですが、
パフォーマンスは悪くないのでオブジェクトに限らず画像やテキストなど
キャッシュさせれば負荷を軽減させるのに役立つのではないでしょうか。
slashdotやmixiも使っているようですし。 
この文章の前には、 MySQL やファイルI/O との比較が載ってますし、 「...に限らず...画像やテキストなど...ようですし」という流れなので、 「slashdot や mixi も memcached を使っているようですし」であって、 PHP クラスのインスタンスとして使っているとは言ってないはず。
そもそも mixi が Perl で動いてるのは有名な話ですし。

最後に…:

斜め読み(で終わらす人)って怖いね… (´Д`;)
自分も結構誤読する事があるので気をつけよう。 ひとのふり見て我がふり直せ…

*1: どれも自爆系だし、そもそもこんなアグレッシブな技術はきちんと 理解/消化/吸収してから使わなきゃダメダメ。
*2: メンテナンスまで考えると、その条件を把握し続けられるかって所ですが…

2008年09月29日(月)

gif_dump 改良

gifovlap 用に Extension ブロックのビット処理 *1 を実装してて、 実際の GIF 画像の Extension ブロックのバイナリイメージを調べたくなったので、 gif_dump.c で Extension ブロックを16進ダンプするように改造。
ExtensionBlockCount=1
    Function=0xf9  ByteCount=4  Bytes: 01 00 00 00
    Graphic Control
	Transparent=0 (= #ffffff)
	Dispose=0
↑こんな感じで表示されます。 こちらにも反映済み。
*1: giflib は Extension の細かい操作APIがなくてバイナリをそのまま アプリケーションで勝手にいじれ方式なのです。ケチ…

はてな日記のカテゴリ指定がうまくいかない。

はてな日記のタイトルにカテゴリを付けてみたのですが、 カテゴリ一覧から辿れません。
自分のタイトルの付け方。
* [GIF]GIF画像の色分布表示
 ↑1文字空白
検索パターン (窓に表示されるので分かりやすい)
*[GIF]
なるほど…
ソースが読みにくくなるけど、* と [ をくっつけて書くか…
*[GIF]GIF画像の色分布表示
編集して気づいたけど * と [ の間の空白は表示にも影響するのね。 どのみち詰めた方が良いみたい。

2008年09月30日(火)

giftransmask

gifovlap の実験用素材を作るのに、giftransmask を作成しました。
丁度、ming に付属している gif2mask の逆 に近い動作 *1 をします。
giftransmask foo.gif baa.alpha > baz.gif
とすると、baa.alpha αチャネル情報を元に goo.gif に透明pixelを上書きした baz.gif が生成されます。

Flash からマスク付き画像を抜き出す:

swfed と組み合わせると以下のような感じで使えます。
まず抜き出したい画像を探します。 に Flash の SWF ファイルを放り込んだ後、 左上の画像一覧のリンクを辿ります。
透明度付きの画像は DefineBitsJPEG3(35) が相当するので、 例えばその中から 298 を選ぶと、以下のコマンド *2 を用いてデータを抜き出せます。
php swfgetjpegdata.phps      chara.swf 298 > chara-298.jpg
php swfgetjpegalphadata.phps chara.swf 298 > chara-298.alpha
それから、適当なツール *3 で chara-298.jpg を変換して chara-298.gif にします。 あと、何とかして 透明=on で保存してください… *4 ごめんなさい。ごめんなさい。 そして giftransmask を実行。
giftransmask chara-298.gif chara-298.alpha > chara-298-trans.gif
やったーっ!

プログラム:

コンパイル
gcc -O2 -o giftransmask giftransmask.c -lgif

制限事項:

  • 入力元に transparent 対応 *5 フォーマットでない GIF を指定してもダメです。
  • transparent index を 0xff 決め打ちにしてるので 256 色全部使ってる画像だと問題あり
  • アニメーションGIF 未対応。1枚目のフレームだけ適用します。
そのうち何とかします…

*1: と思ってましたが全然違う処理でした。○rz
gif2mask の方は画像の明るさ(max(red,green,blue))を zlib 圧縮したデータです。

*2: swfgetjpegdata.phps, swfgetjpegalphadata.phps は swfed/samples/ の下にあります。
*3: Photoshop で GIF 保存 > 強制=なし、ディザ=誤差拡散法、同一色の保持=off。 お勧め
*4: 透明度 on でないと処理できないのは、giftransmask の今の所の制限事項です。
*5: transparent index 有る無しに関わらず、フォーマット的に

Skype on Linux がメモリを使いすぎる問題 (2)

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
11222 apache    22   0 53708  21m 5720 D 11.3  2.2   1:58.63 httpd
27944 yoya      15   0 91388  42m  13m R  0.7  4.2   4:02.70 skype
とりあえず動かしっぱなしでも問題はなさそうなので、次は、 Windows Skype から同じアカウントでつないで様子を見ます。
更に、これでも問題がなかったら、 PHP で bot をつないで(Windows Skype は止めて)みようかと。
切り分け切り分け。

これで、29 日分だよ〜。

タイトル一覧
カテゴリ分類
Database
JXTA
Java
XML
awm
bookmark
keyword
memo
news
research
Powered by hns-2.19.5, HyperNikkiSystem Project