2004年08月01日

PC切り替え器

RATOC Systems, Inc.REX-210と言うPC切り替え器を使っているのだが、時々PCがおかしな挙動をしていた。
片方は、マウスのスクロールを動かすと進むや戻るが押されたような効果が出て、HPなど見ていたらページ飛びまくって大変なことになる。
もう片方はディスプレイの解像度が変になる。
何となく原因というか対処法らしき物がわかっていたのだが、今日ほぼ間違いないと思った。
それは、現在アクティブになっていない状態でPCを起動すると、前述の症状が出る。
つまり、アクティブにして起動すれば問題ない。
でも、こんな症状は自分だけなのだろうか。
似たような症状が出ている人っていますか?

投稿者 Takenori : 01:53 | トラックバック

2004年08月02日

IRC チャンネルでのチャンネルの作り方

IRCでは、参加者が一人もいないチャンネルは自動的に消滅します。
逆に、それまで存在しなかったチャンネル名を入力すると自動的にチャンネルが作られます。

例えば、チャンネル名を『#gはいうおvhせ』などと適当に打って入室してみましょう。
すると、勝手に『gはいうおvhせ』というチャンネルが出来上がります。
当然、他の人が『gはいうおvhせ』に参加して普通に話す事も出来ます。

つまり、自分独自の部屋名を入力するだけで誰でも簡単にチャンネルを作る事が出来るのです。
-----------
そうだったのか。
知らなかった。
つまり、PC立ち上げっぱなしにして、ログインしておけば、そのチャンネルは維持されるわけか。

投稿者 Takenori : 05:43 | コメント (2) | トラックバック

Amazonで買えない時

Amazonで在庫切れになっていて買えない時、出版社に直接注文したら買える場合もあるようだ。

投稿者 Takenori : 08:46 | トラックバック

2004年09月30日

perlの1行スクリプト

コマンドラインで、正規表現を入力してリネームするツールでも作ろうかなぁと考えていてふと気付く。
perlにやらせれば事足りると。
次のような感じ。
perl -e "foreach( glob(\"*.jpg\") ){ $nam=$_;s/00\.jpg/\.jpg/g;rename($nam,$_);}"
スクリプト内の"をエスケープしないといけないのは面倒だけど、結構お手軽。
ミスることも考えて待避しておけば万全かな。

1行スクリプトは他にもいろいろと使いでがありそう。
PerlMagic(ImageMagic)とか。

投稿者 Takenori : 03:19 | トラックバック

2005年01月13日

cygwinで各ドライブへアクセス

/cygdrive/drive/
driveは任意ドライブ。

投稿者 Takenori : 07:17 | トラックバック

2005年01月14日

enscriptを使ってみた

WebSVNでenscriptをC++からHTMLへの変換に使っていたので、cygwinを入れて試してみた。
cygwinで何もせずにインストールすると、enscriptはインストールされない様子。
チェックされているかどうか確認する必要がある。
で、試してみたところPREタグで出力された。
期待したものと違った。
CSharpToHTMLの方が良いかも。
変換後ブログに貼るために変換するから。
色分けなしで特定の文字だけ変換するならperlとかで作った方がいいかも。

s/\t/\&nbsp\;\&nbsp\;\&nbsp\;\&nbsp\;/g;
s/ /\&nbsp\;/g;
s/\</\&lt\;/g;
s/\>/\&gt\;/g;
s/\&/\&amp\;/g;

って感じかな?

投稿者 Takenori : 12:44 | トラックバック

2005年02月04日

日本語でプログラミング「なでしこ」

なでしこ

日本語で10行プログラミング

面白そうだけど、逆に読解に時間がかかりそう。
C/C++のコードなら記号として認識しているようなものだけど、日本語だと読んでしまう。
でも、Perlのように気軽に書くスクリプトのような用途には適しているかも。
暇を見つけていじってみたい。
もうじき正式版がリリースみたいだし。

投稿者 Takenori : 10:06 | トラックバック

2005年07月08日

perlでSQLite

ppm install DBD-SQLite
でSQLiteモジュールをインストール出来る。

投稿者 Takenori : 23:32 | トラックバック

2005年09月05日

FMR-50HVを探す

FMR-50HVを探し中。
ググってヒットするのは、ソフマップの買取価格のみ。
しかも、0円。
いろいろと検索したところ、そういう古い機種を専門に扱っているテイク オフと言うところにありそうなので、とりあえず在庫問い合わせした。
でも、FMR-50HLが7万ちょいもするとは…… 意外に高いな。
ジャンクで5千円ぐらいで手に入れられるだろうと思っていたのに。
ま、在庫と値段がわかった後に、値段次第ではジャンク漁りに行くつもリだけど。

後、つなげられるディスプレイもあれば欲しい。
で、コネクタはD-Sub15ピン(非ミニ)だと思い込んでいたが、検索するとそうでない機種も存在するよう (普通のディスプレイがつなげられるだろうと思っていたのに)。
ぴんあさいんのへやによると、富士通 FMR-50 9pin デジタルRGBコネクタとか言うのがある。
なんてマニアックなコネクタなんだ。
でも、普通のアナログRGBコネクタのものもあるらしい。
これはコネクタ形状を確認しないと。
普通のであれば、FMTOWNSのD-Sub15ピン(非ミニ)とほぼ同じらしい。
D-Sub15ピン(非ミニ)とD-Sub15ピン(ミニ)であれば、サンワサプライ AD-D15NEが使えそう。
結線図を見た感じだけど (『同期信号は独立同期で繋いでください』の一言が少し気がかり。意味が良くわからない。)

って事でまずは在庫確認&ディスプレイコネクタ形状確認。
D-Sub15ピン(非ミニ)なら、どこでも取り寄せっぽいのでAmazonで注文
届いたら、適当にディスプレイをつなげてみよう。
FMR-50 9pin デジタルRGBコネクタだったら…… どうしよう?
ま、その時に調べよう。
で、後はジャンク漁りだな。

投稿者 Takenori : 00:32 | トラックバック

2005年09月08日

続・FMR-50HV

問い合わせたところには在庫がなかった。

ディスプレイのコネクタだが、FMR-50HVには9ピンと15ピンの両方が付いていた。
すごい。
まさか両方ついているとは。
と言うことで、D-sub15ピンの変換コネクタを注文した。
届いたら適当なディスプレイをつないでみよう。

FMR-50をベースにFM-TOWNSは作られており、いくつかのFMR-50用ソフトはFM-TOWNSでも動くとか。
そーだったのか。
知らなかった。
でも、やっぱりFMR-50HVを探すことにする。
見付からなかったら、FMR-50系ならいいかな。
ジャンクで探して動くかどうか試してみよう。

投稿者 Takenori : 01:42 | トラックバック

2005年09月09日

Firefoxのメモリ喰いまくり対策

Firefox 1.0系 はメモリを解放しない?によると、Firefoxはキャッシュのサイズを搭載メモリに応じて変更するとか。
通りで気付いたら700MBとか喰ってるわけだ。

リンク先にも書かれているが、対策は次のとおり
1) about:config とロケーションバー(アドレスバー)に入力する
2) 表示された設定リストの上でマウスの右クリックをしポップアップメニューを表示する
3) 新規作成 > 整数値というメニュー項目を選ぶ
4) 表示されるダイアログに次の文字列をペーストする browser.cache.memory.capacity
5) OK をクリック
6) 次に表示されるダイアログで、サイズを4096と指定する
7) Firefoxを再起動する

6で指定しているサイズは任意だけど、4096(KB)ぐらいで十分っぽい。
これでかなり軽快に動くようになった。
良かった。

投稿者 Takenori : 21:53 | トラックバック

2005年10月15日

Visual C++のコンパイラが無償ダウンロード可能になってた

Visual C++のコンパイラが無償ダウンロード可能に
いつの間にか無償ダウンロード可能になってたのか。
てか、去年の話しだし。
まあ、VCはエディタとデバッガが使いやすいので、コマンドラインのコンパイラが無償になってても、パッケージで買いそうだな。
ま、VC.NET 2003は持ってるのでどっちでもいいといえば、どっちでもいい。
ただ、Standard を買ってコンパイラだけ差し替えれば、最適化コンパイラを利用できるようになるってのは魅力的。

後、持っていない人が単にビルドだけしたいという時には使えそうだ。

投稿者 Takenori : 07:03 | トラックバック

2005年11月28日

開発マシンのファン類を交換した

1週間ほど前から開発用PCから変な音が聞こえてくる。
どうも、ビデオかCPUのファンのようだ。
最近のビデオチップやCPUにとってファンは生命線。
ファンが壊れたら共に死んでいく。
で、いいものはないかといろいろと探す。

ヒートシンクをでかくして、複数のファンからの風があたるようにした方が良いのではないだろうか?
つまりファンの冗長化だ。

検討した結果、CPUのヒートシンクはこれSI-120にした。
XP-120にしたかったのだが、調べるといろいろと干渉しそうなので、SI-120にした。
ビデオのヒートシンクはこれVM-102

CPUのヒートシンクの冷却は、ケースのサイドに吸気口があり、今まではヒートシンクに直接ついていたファン用ダクトのように機能していたものを交換して、それに直接ファンをつけることに。
ヒートシンクに直接つけるのに比べて少しだけ距離が空くけど、それほど離れると言うことはなさそうなので大丈夫だろう。
後は、ケース背面にある12cmファンと新たに5インチベイファンFF-503を買ったので、これによって冷却されるはず。
つまり、この3つのファンでヒートシンクに風が当たり、1個ぐらいぶっ壊れても何とかなるだろうと言う目論見。

ビデオへは上述の背面のファンと側面のファンの風が当たるはず。
後、拡張スロットファンRSF-02も買ったので、これによっても冷却されるはず。

後は電源ファンだけが冗長化されていないが、ま、電源なら大丈夫でしょう。(根拠なし、片手落ち)


これで、ファンは6個になる。今は5個なので1個増えるだけか。
まあ、ヒートシンクをでかくしてファンを共用するような構成にしたせいか意外と増えなかったな。
ちなみに選んだファンなどは静音を意識にしているので25dB以下になり、今よりも静かになるはず。
大体の騒音源は、CPUのリファレンスファンと電源、ビデオのファンなので、これさえ交換すればかなり静かになる。

で、これらを25日の夜に注文、27日に届いた。
大事なデータをバックアップして作業に取り掛かる。

まずはケース横のダクト部に7cm-12cmのファン変換パーツをつけようとするが…… つかない。
サイズが違う。
穴の中心間距離が7cmだったので、7cmでいけると思っていたが、8cmのが必要だったようだ。
仕方ないので、余っていた8cmファンをそこにつけた。

次にビデオカードのヒートシンクを交換。
しかしビデオカードのヒートシンクを交換することがあるとはなぁ。
ちなみにビデオカードはこれELSA GLADIAC 743 GT
もともとのヒートシンクも銅製なので結構重い。
取り説わかりづらいなぁと思いつつ交換。
確認のためにマザーボードに取り付けようとしたら、チップセットのヒートシンクと干渉してる。。。
ヒートパイプはずらせる構造になっているので、少しずらして取り付けてみたら干渉しなくなった。

次はケースのファンを交換して、マザーボードをはずす。
マザーボードの裏側からリテンションキットを固定しないといけないので、わざわざはずさないといけない。
で、取り付けて12cmファンを取り付けようとしたら…… つかない orz
リブなしファンしかつかないようだ。
そう言えば、ファンの注文ページにリブあり、なしって書いてあった。
リブって何? ま、いいやと無視していたのだが、リブなししかつかないヒートシンクとかあったのか。
ちなみに、ファンのリブとは…… 図がないと説明し辛い。
このページがわかりやすい。
フランジタイプって書いてあるほうがリブなしを指すようだ。

他に12cmファンはないので、明日に買いに行くかと諦める。
でも、ケース横のダクト部につけたファンの風がCPUのヒートシンクに当たるので動作させても大丈夫だろうと思い起動。
しばらくBIOS画面でCPU温度を見てみるがそれほど高くないのでOS起動。
一応温度を測るツールをDLして見ながら作業。
負荷の高い処理をさせると50℃ぐらいになる。
うーん…… リテールファンの時に何度ぐらいか測っていなかったので、高いのか低いのかわからない。
でも、ツールをググっていた時に見たページの第1回 熱対策その1 まずは温度計測でPentium 4のCPU限界温度 66℃と言うものを目にしていたので、50℃はやや危険な雰囲気。
ただ、普通に使っている分にはそこまで温度は上がらない。
だけどやっぱり心配なので、あまり作業せずにシャットダウン。

音はだいぶ静かになったようだ。
HDの音が以前よりうるさく感じる。
でも、高周波成分が多くなって不快になったような。。。

明日はファンを買うためだけにPCショップまで出かけるのかと気重。
何とかならないかと考えていて思い出す。
いつも使っているPCに12cmファンつけてた!
ケースを開けてみると、 ビンゴ!
リブなしファン発見。
早速はずしてリブありファンと交換。
が、眠いのでこの日は続きをしなかった。
でも、ファイルサーバーや普段使っているマシンの構成についてあれこれ考えつつ、いろいろと調べていたらかなり寝るのが遅くなった。

で、今日はリブなし12cmファンを取り付けた。
後、ケース横のファンは要らないだろうと思って取り外し、拡張スロットファンが騒音源だと思い電源ケーブルをはずしておく。
昨日確認したら、拡張スロットファンからの風は妙に冷たかったので、あってもなくても同じだろうと言うことで取り外した。
そして起動。
静かになった。
しかも、負荷をかけても40℃ぐらいまでしか上がらない。
いい感じだ。

投稿者 Takenori : 22:19 | トラックバック

2006年02月17日

Win版Margarineを動かす

ここのソースを取ってきて、Perlのを動かそうとしたが落ちまくり。
どうやら茶筅のDLL版を使うと良く落ちるようだ。
そこで、COM版にしたら落ちることはなくなった。
ただし、COM版茶筅を使うには、
regsvr32 ComCha.dll
として登録する必要がある。

でも、ソなどがなぜかソ\になってしまう。
Perlの問題だろうか?
とりあえず、実行前に
$sql =~ s/([\x80-\x9f])\x5c\\/$1\x5c/g;
としたらうまくいった。

投稿者 Takenori : 21:33 | トラックバック

2006年02月21日

Wiki メモ

WikiMatrix が比較にはいい。
選択したWikiの機能一覧が表示出来る。
ウィザード形式で選ぶことも可能。
ただし、英語。
ただ、ウィザードで欲しい機能についての選択がなかったのが残念。

各Wikiの文法比較でいろいろなWikiへのリンクがある。

投稿者 Takenori : 15:54 | トラックバック

2007年01月29日

フォルダにドライブを割り当てる

substコマンドでフォルダにドライブを割り当てられる。
SUBST H: "C:\Program Files\Apache Software Foundation\Apache2.2\htdocs"
のように入力すれば、Hドライブに"C:\Program Files\Apache Software Foundation\Apache2.2\htdocs"が割り当てられる。
深いパスに頻繁にアクセスする時に便利。
詳細は 「subst /?」 で。

投稿者 Takenori : 20:38 | トラックバック

2007年02月05日

ソースコード検索エンジン gonzui

ソースコードから何かを探す時、今までgrepかdoxygen、namazuで検索していた。
それでもいいけれど、ソースコード検索エンジンgonzuiと言うのがあるようなので、使ってみようとgonzui-win32でソースをインポートしようとしたがうまく行かず。
gonzuiサーバーの方はうまく立ち上がってブラウザからちゃんと見える。
インポートがうまくいかない。
当然インポート出来ないと意味がない。

Linuxに入れたほうがいいかな。
win32版はsubversionからのインポートできないようだし。

投稿者 Takenori : 22:46 | トラックバック

2007年02月24日

C++ Builderの次バージョン

C++ Builderが一向にバージョンアップされていなくて、心配になっていたがDelphi 2007が出るということで、C++ Builderも出そうですね。
プレスリリースを見ると、C++ Builderについても少し触れられているけど、詳細は2007年3月12日からの「CodeRage 2007 Virtual Developer Conference」でと書いてある。
他に気になったのは、Win2k,XP,Vistaでの開発と実行のサポート となっている部分。
Win9X系は切られるのかどうかが気になる。開発は良いとしても、実行は出来て欲しいが。
後、AJAXサポートを含むビジュアルWebアプリケーション開発機能VCL for Webってのも気になる。
Delphi for PHPとかも出るようで、少し楽しみだ。
細々としたWebアプリをC++ Builderでちょっとしたツールを作るように簡単に出来れば、いろいろと実験が出来る。

投稿者 Takenori : 13:16 | トラックバック

2007年03月03日

エクスプローラーでzipを展開しないようにする

以下のようにコマンドラインで入力することで、エクスプローラーでzipとcabが展開されなくなる。

regsvr32 /u zipfldr.dll
regsvr32 /u cabview.dll

Windows XPになってから、Zipファイルがエクスプローラーでフォルダのように展開されるようになて、重くて嫌でlzh圧縮を使うようにしていたけど、これで解決。

参考
「.ZIP」「.CAB」を検索対象から外す

投稿者 Takenori : 18:37 | トラックバック

2007年03月04日

1ヶ月以上前のファイルを全部消す

foreach( glob("*") ) { if( -M > 30 ) { unlink; } }

投稿者 Takenori : 20:59 | トラックバック

2007年07月01日

バーンダウンチャート

trac + TracBurndownプラグインでスクラム開発のすすめ
第12回 バーンダウン・チャートで「終わるかどうか」を見える化する
第13回 バーンダウン・チャートによるプロジェクト管理の進め方

スクラム開発云々はいいとして、いつ頃終わりそうかと言うのが視覚的にわかるのは良いかもしれない。
試験実施工程で、全試験項目数と消化項目数から残項目数を出して、それをグラフ化しているのに近いだろうか。

ただ、多人数で開発していて、他の人にブロッキングされて進まないと言うような状況はこのグラフではわからない。
各個人の作業がいつ頃終わりそうかはわかる。

TRACのプラグインは試していないが、見た感じだと実績のみが表示されているように見える。
過去の作業ペースから予想終了日などを出してくれるとフルタイムで開発していない時にもわかりやすそうだ。
そうすれば、いつぐらいには完成しそうだなぁと言うのが、感覚のみではなく、ある程度データに基づいた物になる。
ただ、見積もりには感覚的なものが含まれるので、結局感覚に依存してしまうことにはなるが。

TRACのプラグインを試そうと思ったら、TRACのバージョンが0.9.5だったので、まずTRACのバージョンを上げないといけないようだ。
バージョンを上げようと思ったが、All-In-One Tracで入れたせいかうまく行かない。
前にHDDが壊れて放置していたLinuxサーバーのHDDを乗せ変えて再び入れようと思っているので、そちらに新しいバージョンを入れてプラグインも入れるかな。

投稿者 Takenori : 17:48 | トラックバック

2007年07月08日

TRACのブログプラグイン

TickTackBlog プラグインという TRAC ブログプラグインを入れてみた。
このプラグインではチケットが1エントリーとなるのだが、チケットを登録した直後は open 状態なので未解決チケットに表示されてしまう。
未解決に残るのが嫌なので close する。
この close が少し手間。

TRAC の中にブログが入っているのはなかなか良いかもしれない。
思いついたことやその日の作業などをブログに上げて、その中で関連するチケットやリポジトリへリンクを貼ればわかりやすい。
公開可能なことはこのブログに良く書いているが、そうでないのは内部の TRAC のブログに書くかな。
今は TRAC の Wiki に書くか、机の横に置いているホワイトボードにメモしている。

A4より少し大きくぐらいのホワイトボードを置いているけど、これはなかなか便利。
寝る前などに明日あれしないととかを書いておくと忘れない。
手軽に書けるのがいい。
ちょっとしたメモもよく書いている。
ただ、しばらく消さないと消えなくなってしまうのが難点。
まあ、上からまたマジック塗って消せば消えるけど、しばらく置いておくものは別に写したほうがいい。

投稿者 Takenori : 17:39 | トラックバック

2007年08月23日

Media Player Classic をビルド

書いていなかったのでメモ。

添付ドキュメントのビルド方法を読めば大体ビルドできるけど、FLV と MPEG4 が少し曲者。
FLV は VP6 だったか 7 が曲者で、流出したソースを組み込んで対応され、文句を言われてリポジトリからソースが消されている。
なので、ソースがないけど、ビルドすると有効になっているのでエラーになる。
だから、その周りをコメントアウトするなりなんなりしてビルドを通す必要がある。
基本的にフィルタとして組み込まれているため、独立していてコメントアウトする箇所は少ない。

MPEG4 は別のライブラリが使われていて、それはソースが公開されているんだけど、最新版はインターフェイスが変わっているようですんなり組み込むことが出来ない。
古いバージョンを探したけどすぐには見付からなったのでOFFにして通した。

後、何箇所か直さないとうまく行かない箇所があった気がする。


で、やりたかったことは何かと言うと、動画の振り分けが楽に出来るようにすること。
プレイリストにエクスプローラの「送る」のようなメニューを付けて、動画の整理をやりやすくする。
後、直接ファイルの削除も出来るように。
そして、今日ビルドできるようになってから1ヶ月ぐらい放置してることに気付いた。

投稿者 Takenori : 19:32 | トラックバック

2007年10月23日

BCC 用の libjpeg を作るメモ

IJG JPEG library 高速化版 を C++ Builder 2007 で使えるスタティックライブラリ化する時のメモ。
まずは NASM の V.0.98.40 以降を手に入れないといけないのだが、公式サイトの Win32 用のバイナリでは、V.0.98.39 から V.0.99.03 に跳んでおり、V.0.98.40 がない。
で、V.0.99.03を試しても、エラーになるので V.0.98.40 を探す必要がある。
単純に「 NASM 0.98.40 」で検索すれば見付かるはず。

後は……
jconfig.bc5 を jconfig.h にリネーム。
makefile.bc5 を makefile にリネーム。
C++ Builder 2007 のRAD Studio コマンドプロンプトを立ち上げ make で OK 。
そうすれば、libjpeg.lib が出来る。

使う側のアライメントのサイズや enum ( 列挙型 ) のサイズを変更している時は注意が必要。
makefile を書き換えてそれらを指定してやる必要がある。
アライメントのサイズは -a4 などと指定する。
デフォルトは、8 byte なので、上の -a4 と言う指定では、4 byte になる。
enum のサイズはデフォルトで int 型になっているけど、short にする場合は、 -b- を付ける必要がある。
C++ Builder 2007 の GUI では逆で、「整数サイズの列挙型 」と言う指定になっている。
コマンドラインオプションは CFLAGS に付け加えてやれば OK 。

アライメントサイズは C++ Builder 6 の時も 8 がデフォルトだが、enum 型は異なるよう。
C++ Builder 6 だと「enum 型を int 型として扱う」のチェックが外れている。
なので、昔のプロジェクトファイルをコンバートした場合は、注意する必要がある。

投稿者 Takenori : 15:14 | トラックバック

2007年12月05日

マウスとキーボードをソフトウェアで共有する

以前は CPU 切り替え器を使っていたのだが、キーボードもマウスも USB になって使えなくなった。
そこで、USB 切り替え器で切り替えるようにしていた。
が、ある日切り替えても認識されなくなった。
あれ?と思って、直接つなぐも認識されない。
まったく操作できなくなってしまった。
Theora のエンコード中なのでどうするか悩んだが、仕方なく電源ボタンでシャットダウン。

しばらくして、今度は別のマシンでうまく切り替えられなくなった。
でも、今回は直接つなぐと大丈夫。どうも今回は切り替え器が怪しい。

と言うことで、自分の中でこの切り替え器の信頼性が低くなってきたと言うこともあり、代替方法を探すことにした。
切り替えられなくなって騒いでいた時、どこドアと言うものを教えてもらった ( どこドア2もあるみたい ) 。
でも、シェアウェアかーと言うことで少し躊躇。

それで見付けたのが Synergy
Windows でも Linux でも使えてフリー。
参考:
Synergyでキーボード、マウスを共有する
1組のマウスとキーボードを複数のPCで共有するには

最初、サーバーからクライアントの切り替えのみ設定していて、クライアントからサーバー側へ戻れなくなってあせった。
当たり前だけど、ちゃんと両方設定してから起動した方がいい。
その後は良好。
さらにコピー&ペーストも共有される。
片方のPCでコピーして、違うPCへ移動してペーストするとペーストできる。今まで共有にテキストファイル置いてコピペしていたので、これはかなり便利。
でも、出来るのは文字列のみっぽい。
ファイルはコピーできなかった。
オープンソースなので、自分で何とかできなくはないが。

1.3.1 での設定方法は、このページがわかりやすい。

Synergy 1.3.1 での問題。
Win XP のクライアント側で半角/全角 キーが効かない日本語キーパッチを当てたやつを使えば直るみたい。今度試す。
Win XP のログイン画面で使えない>どうも、しばらく待てば使える様子。今度しばらく待ってみて確認する予定。

投稿者 Takenori : 19:34 | トラックバック

2007年12月14日

Synergy の難点

前に Synergy を使うことにしたと書いたが、その続き。
クライアント側に日本語キーコードパッチ版を入れたら、半角/全角は通るようになった。
でも、いくつかのキーが反応しない。
まず、かな打ちで「を」が打てない。つまり、「Shift + わ」が認識されない。
「ー」が「ろ」になる。この時 Shift を押していると正しく入力できるので変換ミスっていそう。
それと、「無変換」キーが効かない。
さらに致命的なことに、「Ctrl + Alt + Del」がクライアントで効かない。Windows ではこれがないといろいろ困る。しかも、「Ctrl + Alt + Del」はサーバー側で反応している。

「を」が打てないのには困っているけど、それ以外はまあ我慢できる範囲。
ただ、もうひとつサーバー側で入力していると Alt キーが時々押下状態になっている。
ファイル開こうとダブルクリックしたらプロパティが開いて、「なんだこれ?」となる。初めは良くわからなかったが、Alt キーが時々押下状態になることに気付いて、Alt をもう一度押せば回復することがわかった。

ログイン画面では、しばらく待てば入力できた。
しばらく待っているとマウスカーソルが表示されるので、表示されたら入力できるようになるようだ。


キーボード周りでいろいろと難点は多いが、マウス関係は問題ないので、サブマシン側がクライアントであれば「を」を打てないのに困るが、まあ使える。
イライラが臨界点に達したらパッチを作るかも。
まあ、入力をサーバー側でやってコピペでクライアントへと出来るので、めちゃくちゃ困ると言うことはないんだが。

投稿者 Takenori : 17:16 | トラックバック

2007年12月16日

数式を書く方法

ほとんどは () を使って普通のテキストで書いてもわかる範囲の数式なのでそれで済ませられるが、そうでない時は仕方なく MS-Word の数式エディタで書いていた。
でも、MS-Word の数式エディタでは書くのに時間がかかる。
そこで、TeX/LaTeX 。
少しだけ覚えなければならないことがあるが、比較的直感的に書ける。
LaTeX における数式の書き方(簡易版) のページなどを見ればそれほど難しくないことがわかる。
ただ、TeX/LaTeX のセットアップが面倒。
と言うことで、オンラインサービス。
TeXclip ( TeXをPNG画像にしてくれるオンラインサービス ) がある。
ここで TeX で数式書いて、Generate Image でPNG画像が出来上がり。
後はその画像をダウンロードして貼ればいい。
MathML で出したいなら Demonstration of itex2MML
上のボックスに TeX で数式書いて、Send text to be processed で MathML になる。

PC で数式を書くとなると面倒で書きたくなくなるが、これらのページを使えばそんなに苦じゃなくなるのではないだろうか。

投稿者 Takenori : 16:56 | トラックバック

壊れたハードディスクからデータを読む

最近、物理的に壊れたハードディスクからデータが読み出せたので、その方法を書きとめておく。
ここで物理的に壊れたと言っているのは、HDD メーカーのチェックツールで Physical defect と表示されるようなケース。
外形が変わるような壊れ方ではない。

事例1.
問題のハードディスクはサーバーに使っていたもので、ある日気付くとサーバーが応答しなくなっていた。
サーバーを再起動すると HDD が認識されない。
ケースを開けて確認すると回転自体していないようだった。
で、この時は仕方なく HDD を交換して対処し、壊れた HDD はしばらく放置していた。
その後、IDE - USB で HDD を裸でつなげる機器を買ったことから、何気につなげてみたがやはり回転せず。
やはりダメかと思って HDD 手にとって、HDD を縦にしてから電源を入れなおすこと2回。
突然モーターが回転を始めた。
これは!と思って PC に接続すると普通に読めた。
縦か横かでモーターへの負荷のかかり方が変わるからだろうか? 理由はわからないが、HDD のモーターが回転しなくなった時は HDD を立てて電源を入れてみると回転しだすかもしれない。

事例2.
ある日外付け HDD を PC につなぐと HDD から変な音がひたすら繰り返し鳴るだけで一向に認識されない。
前日まで快調に動いていて、前日データを保存したばかりなのだが……
何度つなぎなおしても向きを変えてもダメで、PC に直接つなぎメーカーのチェックツールを実行すると Physical defect と出る。
完全セクタチェックは数時間経ってもほとんどプログレスバーが進まない状態。
これはダメかと思ったが、ふと Linux ( Debian Etch ) につないでみた。
すると認識されてデータも読めた。
とりあえず、データを吸い出した後、再度 Windows につなぐもやはり読めない。
ドライバの違いか。
ATA HDD には、PIO モードと DMA モードがあり、さらにその中にいくつかスピードに応じてモードがある。
普通は、読み出せる最速のモードで使う。
組み込み機器などで ATA ドライバを作ったことがあれば経験していることがあるかもしれないが、低速のモードの方が読み出せる確率が高い。
また、コマンドを発行してから何ミリ秒待つ必要があったりもする。
これらのことから、HDD のドライバにはいくつかのノウハウがあり、それによって安定性や傾向が異なる。
安定性を重視しすぎるとパフォーマンスが落ちたりもするので、その辺りのバランスは難しいだろう。
と言うことで今回は、Windows で読みなかったものが Linux で読めたのかもしれない。
ある OS で HDD が読めない時、ドライバの異なる他の OS で試すと読み出せることがあるかもしれない。
ちなみにこのハードディスクは Giga LAN Disk で読めたことから、そこでフォーマットした後、Windows につないで再度フォーマットしたら使えた。
が、かなり不安なのでほとんど使ってない。
いくつかセクタが死んでいるかもしれないし。

壊れたハードディスクからデータを吸い出してくれるサービスもあるが、数万~数十万と高いのでかなり大事なデータでないと利用し辛い。
そこまで大事ではないが、読み出したいデータが入っているハードディスクが壊れた時、これらの方法を試してみると読み出せるかもしれない。

投稿者 Takenori : 18:06 | トラックバック

2008年01月01日

テンプレートと多重定義された関数のポインタ

オーバーロードされたメンバ関数の関数ポインタを取得してテンプレートで定義された関数に渡そうとしたらうまく行かなかった。
解決方法がわかったのでメモしておく。
まず定義されていた関数は以下の通り。

template<typename sr_t, typename sc_t, typename prop_t, typename gc_t >
inline CPropertyLoader<std::mem_fun1_t<sr_t,sc_t,prop_t>,std::const_mem_fun_t<prop_t,gc_t> >*
 createLoader( sr_t (sc_t::*pms)(prop_t), prop_t (gc_t::*pmg)() const )
{
  return new CPropertyLoader<std::mem_fun1_t<sr_t,sc_t,prop_t>,std::const_mem_fun_t<prop_t,gc_t> >(
    new std::mem_fun1_t<sr_t,sc_t,prop_t>(pms),
    new std::const_mem_fun_t<prop_t,gc_t>(pmg) );
}

対象としていたクラスの一部は以下の通り。

class CSpriteBase
{
public:
  void SetVisible( int frame, bool visible );
  void SetVisible( bool visible );
  bool IsVisible() const;
};

この時、以下のように記述すると void SetVisible( int frame, bool visible ) のポインタが得られてコンパイルエラーになる。
未指定の場合、宣言順で関数ポインタが得られるようだ。

createLoader(&CSpriteBase::SetVisible,&CSpriteBase::IsVisible) );

メンバ関数ポインタは、ポインタの型に合わせて代入される関数が決定されるようなのだが、代入先がテンプレートになっているのでうまく行かない。
どうするかわからなかったが、以下のように書けばうまく行った。

createLoader((void(CSpriteBase::*)(bool))&CSpriteBase::SetVisible,&CSpriteBase::IsVisible) );

デバッガで追うと、きちんと意図した関数がコールされた。
これは、キャストすることでテンプレートの型が決定して、それで代入される型が確定して、関数ポインタが確定するのではないかと推測している。
この辺りのことが書かれているものを見たことはないので、正しいのかどうかはわからないが、動作は問題ない。

参考ページ:
オーバーロードした関数ポインタの取り方
関数のポインタとオーバーロード

投稿者 Takenori : 16:09 | トラックバック

2008年01月03日

HTMLをSubversionで管理しながらWebで参照可能に

HTMLファイルもバージョン管理したい時がある。
そして、出来れば公開の手間も省きたい。
ということで、Subversion でバージョン管理しながら、ブラウザでそのパスにアクセスした時にきちんと見れるようにする方法。

Subversion のリポジトリを WebDAV で公開すると、そのパスをブラウザでもアクセスできる。
ただ、特に何もしないと HTML ファイルを見ても単なる HTML のソースがテキストとして表示されてしまう。
これをきちんと表示させるためには、ファイルに属性を設定する。
具体的には、属性に「svn:mime-type」、値に「text/html; charset=SHIFT_JIS」 などと設定する。
CSS の場合は、値に「text/css; charset=SHIFT_JIS」とする。
このようにすると、ブラウザでアクセスした時、ソースではなくきちんとページが表示される。

ローカルで編集した後、コミットすればそのままそのページが公開されるので便利。
仕様書などのドキュメントをバージョン管理しつつ、いつでもブラウザで参照できる。
多人数で編集できるし、コンクリフトが発生したらマージすればいい。
ただ、大幅に構造を変えられるとマージが大変なので、ある程度テンプレートなど用意しておいた方がよいかも。
それと、HTML をテキストエディタで直接編集する派でないと、少しマージが辛いかも。
後、フォルダにアクセスした時、index.html などが表示されるのではなく、Subversion のファイル一覧ページが出るのでそこも注意する必要がある。リンク時などは明示的に index.html を指定する必要がある。

書きかけだけど、ブラウザでの表示はこんな感じになる。
普通のページと同じ。

属性を設定する時の注意事項として、きちんとコミットすること。
当たり前だけど、属性を設定した後、コミットしないと反映されない。
でも、属性を設定しただけで反映されると勘違いしてしまうことは多い。
少なくとも私は何度もあれ?おかしいな?となっているので、注意。

投稿者 Takenori : 10:48 | トラックバック

2008年01月06日

SVNで古いリポジトリと新しいリポジトリをつなげる

サーバーのHDDが死んでSubversionのリポジトリを失った。
そこで、仕方なくローカルの作業コピーを使って、新たにリポジトリを作っていた。
が、死んだと思っていたHDDからデータを取り出すことに成功したため、古いリポジトリのデータを復活できた。
と言うことで、この2つのリポジトリのコミットログをマージして、つなげられないかと試行錯誤。
svnadmin dump と load とスクリプトで何とか出来たので、その方法を記しておく。
ただ、ある程度理解してやらないとうまくつながらないので注意。

まず、古いリポジトリの最後と新しいリポジトリでインポート後の最初のリビジョンのtrunkの構成を一致させる必要がある。
今回、不幸なことに新しいリポジトリに入れるまでに間があったことから、だいぶ異なっていた。
また、リポジトリの構成も、古いのはフォルダを切った下に trunk を作っていたのに対して、新しいのはtrunk の下にフォルダを切っていた。
なんと言う適当っぷり。
まあ、新しいのはAll In One Trac で入れたので、それがデフォルトでtrunkを一番下に作るせいでそうしたのだろう。
ただ、新しい方は trunk の下に分断されたものしか入れていなかったのが救い。

と言うことで、古い方は対象のもののみ取り出す必要がある。
これは、ダンプの時に svndumpfilter を使えばいい。
svnadmin dump /repos | svndumpfilter include project1 > dump.dat
ってな感じ。
単純にリビジョンごとにダンプするperlスクリプトは以下のような感じ。
svndumpfilter を使う時はこれを少し変えればいい。

$repo_path = 'C:\trac\svn_repo\projects\tools';
$svn_admin = 'C:\trac\subversion\bin\svnadmin';
$svn_filter = 'C:\trac\subversion\bin\svndumpfilter';
$max_revision = 302;
for( $i = 0; $i < $max_revision; $i++ ) {
  system("$svn_admin dump --incremental -r $i:$i $repo_path > .\\dump_tools\\dump$i.dat");
}

古い方から対象もののみ取り出したら、新たにリポジトリ作ってそこにロード。
連番から読むのは以下のようなperlスクリプトでOK。

$repo_path = 'C:\trac\svn_repo\projects\new_tools';
$svn_admin = 'C:\trac\subversion\bin\svnadmin';
$max_revision = 513;
for( $i = 0; $i < $max_revision; $i++ ) {
  system("$svn_admin load $repo_path <  .\\dump_tools\\dump$i.dat");
}

次に、新しい方のインポート後の最初のリビジョンと上の古いほうの最後のリビジョンを取り出して、古いほうにマージ ( と言うかコピー。タイムスタンプが変わらないようにするためにもコピーでないとダメかも ) 。なくなったものは消す必要がある。
で、古いほうにコミット。
古いほうは、ディレクトリ構成が1段深くなっているので、1つ上げて新しいのと同じにする。
これで、古いのの最後と新しいのの最初が同じになってつながるように。
tagsとかbranchesはtrunkからコピーしたりしているだけで、分断されていないので放置。
で、この状態のものをダンプする。

次に、新しい方のリポジトリをダンプして、ダンプ結果のリビジョンを上げる。
リビジョン番号は古いものの最後の次の番号と新しい方のインポート後の次のリビジョンが一致するようにする。
リビジョンを上げるには、以下のような perl スクリプトを使った。
最初、Node-copyfrom-rev: の方に気付かずにうまくいかなかった。

$rev_shift = 302;
foreach( glob("*.dat") ) {
  $org_name = $_;
  /([0-9]+)/;
  $num = $1;
  $new_num = $num + $rev_shift;

  open IN, "<$org_name" or die $!;
  open OUT, ">./new/dump$new_num.dat" or die $!;
  binmode(IN);
  binmode(OUT);

  while(<IN>) {
    if(/^Revision-number: [0-9]+/ ) {
      s/[0-9]+/$new_num/;
    } elsif( /^Node-copyfrom-rev: ([0-9]+)/ ) {
      $ref_num = $1 + $rev_shift;
      s/[0-9]+/$ref_num/;
    }
    print OUT $_;
  }
  close(IN);
  close(OUT);
}

これは、実行するディレクトリに new ってディレクトリを作っておいてから実行する。
インポートしたリビジョンまではいらないので削除して、古いもののダンプ結果と新しいもののリビジョンを上げたのをひとつのディレクトリに入れて、新しいリポジトリを作ってロード。
これでひとつにつながった。

投稿者 Takenori : 00:46 | トラックバック

2008年01月19日

Synergy のソースを追う

Synergy の 日本語キーパッチ 版を使っていると次のような問題がある。
サーバー側で突然 ALT キーが押しっぱなしになる、かな入力が突然ローマ字入力になるという問題がある。(パッチ関係ないかも)
あと、クライアント側では、「を」が入力できない。( 「わ」と入れて変換すればなんとか入力出来る )
「-」と「?」も入力できない。( 「-」は位置が違う。「?」は「はてな」と入れて変換すれば出来る )
クライアント側で「無変換」キーが効かない。
この中で「かな入力が突然ローマ字入力になる」がかなりイライラする。
クライアント側の入力出来ない文字も気になるが、突然ローマ字入力になるのに比べればストレスは少ない。
と言うことで、ソースを追って改善できないか調べることにした。

まず、基本的には、グローバルフック ( システムフック ) を用いて、キーボードとマウス、クリップボード関係のメッセージをフックし、それをネットワーク経由で送っているようだ。
仕組み自体はそれほど複雑ではない。
ALTはローマ字入力はグローバルフック の実装が悪いのだろうか?

日本語キーパッチを入れると、サーバー側の IME を切り替えるようになるようだ。
元は、クライアント側だった気がする。
と言うか、クライアントの設定出して切り替えていたか。

かな打ちの時、「a」と「ち」では仮想キーコードが違う。
「を」は、「わ」のコードが得られるが、Shiftを押下しているので、それで判定しているようだ。
構造的には、抽象クラスがあって、その派生クラスとして、Windows と Linux、 MacOS X 用が実装されている。
基本構造はどれでも共通化されているようだ。

後、フック時の CSynergyHook.cpp 内の doKeyboardHookHandler で「VK_HANGUL」が特別扱いされている。
このキーは、「VK_KANA」と仮想キーコードが同じなので、「IME かなモード」になる。
処理は、ローレベルフックが使われている時はこのキーをスルーし、そうでない時は捨てるようだ。
VK_CAPITAL と VK_NUMLOCK、VK_SCROLL も同様にスルーするようになっているので、これがサーバー側で処理すると言うことかな?
が、この辺りを変更してみても意味なさげ。
NO_LOWLEVEL_HOOKS を 1 にしてビルドすると、変換モードが変わる問題は発生しなくなった。
でも、クライアント側でいろいろと問題が出て、まともにキーボー入力とかできない。
ローレベルフック関係で何かあるのだろうか?
ただ、Synergy を終了すると変換モードがおかしくても元に戻るので、サーバー側がおかしい気もする。
何らかのきっかけで、Synergy の内部状態がおかしくなって、キーコードに変な変換をかけているんじゃなかろうか。
そうだとすると追うのはかなり大変。
後、ローマ字入力になると思っていたけど、どうも全角英数字入力になっているようだ。
全角英数字入力は、「Shift + 無変換」で切り替わる。
うーん・・・
フックDLLだけではなく、サーバーやクライアントの構造、プロトコル周りも見ないと辛いかなぁ。
そうなると、かなり面倒。
で、とりあえず、 NO_GRAB_KEYBOARD を 1 にしてビルドし、キーボードの共有を止めた。
クライアント側で入力できないのは少し不便だけど、だいたいはマウスで事足りるのでそれでなんとかする。

フックDLLがキーボード入力を受け取って後、他のスレッドにキーコードなどをメッセージで送っていて、その後の処理はあまり見ていないけど、その辺りも見て全体構造と状態をどのように持っているかを把握しないと解決は難しいか・・・
ちょっと時間かかりそうなので、今はこれ以上手をつけられないな。
また後でやろう。

投稿者 Takenori : 17:21 | トラックバック

2008年01月21日

Synergy の犯人はローレベルフック

やはり、クライアント側で入力できないのは辛いので、再び何とかしようとした。
で、気付いたのは、正攻法でやらなくてもいいということ。
クライアント側に移動すると IME バーが出て、フォーカスがどこかに奪われる。
どうも見えないウィンドウがフォアグラウンドになっているようだ。
つまり、このフォアグラウンドのウィンドウのプロシージャの WM_KEYDOWN などを処理してやればいいのではないかと気付いた。
で、最初は、サーバーとなる CMSWindowsScreen.cpp のウィンドウのプロシージャに書いたが無反応。
そこで、Spy++ で Synergy 関係のウィンドウを調べてみると、クライアントに移った時 "SynergyDesk" という名前のウィンドウがメッセージを受けていた。
と言うことで、"SynergyDesk"で Grep すると、CMSWindowsDesks.cpp にあった。
で、ここから サーバー側にメッセージを送らなければならないので、CMSWindowsScreen.cpp のクラス名とウィンドウ名を調べると共に"Synergy"となっていた。
と言うことで、::FindWindow( "Synergy", "Synergy" ); でウィンドウハンドルを得て、PostMessage で送る。
CMSWindowsScreen.cpp のウィンドウのプロシージャで、クライアントに送る。
が、どうも文字がおかしい。
まあ、通常ルートと完全に同じにようには書いていないので、それが原因だろう。
同じようにするのは結構面倒。
ここで行き詰るが、そもそもサーバー上にある時には、メッセージをフックせずにそのまま流してくれればいいのに、何か処理して自分に投げているのが問題なんじゃないかと気付く。
なら、サーバー上にある時は、フックハンドラでスルーするように作り変えればいいんじゃないか?と言うことで、再びフックDLLをいじりだす。
どうやら、g_mode == kHOOK_RELAY_EVENTS となっている時が、クライアント上にカーソルがある時のようだ。
と言うことは、g_mode != kHOOK_RELAY_EVENTS で doKeyboardHookHandler の最初でスルーするようにすればうまく行くはず……と思ったけど、やっぱり時々変換モードがおかしくなる。
なんだろうと思って、ローレベルフックの部分をよく見てみる。
この中でも doKeyboardHookHandler をコールしているので、doKeyboardHookHandler 内ではじけばいいと思ったのだが……って、パラメータ変換してるし。
なんか lParam にフラグ追加している。
で、それを次のフックハンドラにそのまま流しているんですがこの人。
ほぼ間違いなくこれが原因だろう。
ここだけ直せば済むかもしれないけど、サーバー上にある時はスルーした方がいいだろうと言うことで、上の doKeyboardHookHandler はそのままにして、このローレベルフックの処理を g_mode == kHOOK_RELAY_EVENTS で囲んで、クライアントに流す時のみ通るようにした。
で、ビルドして実行。
問題なさそうだ。
この対応で、突然ローマ字入力になったり、全角英数字入力になったりしない。
このブログ書いている間で一度も切り替わらなかったので大丈夫だろう。
たぶん、ALT キーをつかんだままになる問題も修正されているはずだ。

後は、かな打ち時の「を」か。
これは、対応するキーコードがないので難しいと日本語対応版を作っているいろいろなページで書かれている。
確かに、いろいろやるもうまくいかない。
s_decomposeTable で何とかできるんじゃないかと言う気もするがよくわからない。
で、無理やり渡してやることにした。
「を」は、仮想キーコード 0xE5 + shift で、「0」は 0x30 。
サーバー側で変換できなくて、仮想キーが 0x30 で shift が押されている時、0xE5 + shift を送る。
これをクライアント側で受けると変換できないので、変換できない時に 0xE5 + shift なら scan code を 0x0B, 仮想キーを 0x30 として押してやる。
こうすれば「を」が入力できるようになった。
ピンポイントで特別対応なので問題ないとは思うが、もしかしたら何かあるかもしれない。
まあ、これで少し使ってみよう。

投稿者 Takenori : 01:09 | コメント (4) | トラックバック

キーボード関係のメモ

Synergy をいじっていて知ったことについてのメモ。
多分、かなり役立たない。

仮想キーコードから文字に変換する API に ToUnicode と ToAscii がある。
これには、Ex 版も存在していて、そちらは入力ロケール識別子を指定できる。
入力ロケール識別子は、LoadKeyboardLayout で取得できる。
入力ロケール識別子の名前は、日本語106キーボードの場合、"00000411" か "E0010411" のどちらかのようだ。

仮想キーコードをスキャンコード、または文字の値に、スキャンコードを仮想コードへ変換する API には MapVirtualKey がある。

スキャンコードは、純粋に一つ一つのキーに対応しているっぽい。

投稿者 Takenori : 01:42 | トラックバック

2008年01月31日

エラーレポートに WMI から取得した情報を含める

今まで、あまり詳しくは知らなかったのだが、WMI を使うとコンピュータの管理情報の多くを取得出来るようだ。
WMI では、MSDN2 の Win32 Classes の左のツリーに並ぶさまざまなクラスの情報を取得できる。
この中で役立ちそうなのはプロセッサーの情報やビデオ、サウンドの情報だろうか。
と言うことで、ビデオの情報を書き出すプログラムを組んでみた。
ソースと実行ファイルはこちら
ダウンロードしなくても見れるようにソースの中身を以下に貼っておく。

#ifndef _WIN32_DCOM
#define _WIN32_DCOM
#endif

#include <windows.h>
#include <wbemidl.h>
#include <oleauto.h>
#include <objbase.h>
#include <comdef.h>
#include <comutil.h>
#include <atlbase.h>
#include <iostream>

#pragma comment(lib, "comsuppw.lib")

void PrintDeviceDescription( IWbemClassObject* pDevice, const wchar_t* type )
{
  HRESULT  hr;
  VARIANT var;
  if( SUCCEEDED(hr = pDevice->Get( _bstr_t(type), 0L, &var, NULL, NULL )) ) {
    VARTYPE vt = V_VT(&var);
    VARIANT *va;
    if( var.vt == (VT_VARIANT|VT_BYREF) ) {
      va = var.pvarVal;
    } else {
      va = &var;
    }

    vt = va->vt; 
    if( (vt&VT_ARRAY) == VT_ARRAY ) {
      SAFEARRAY *psa;
      if( vt & VT_BYREF ) {
        psa = *va->pparray;
      } else {
        psa = va->parray;
      }
      LONG lb, ub;
      SafeArrayGetLBound( psa, 1, &lb );
      SafeArrayGetUBound( psa, 1, &ub );
      ULONG nTargetLen = ub - lb + 1;

      VARIANT *pi;
      hr = SafeArrayAccessData(psa, (void **)&pi);
      for( ULONG i = 0; i < nTargetLen ; i++ ) {
        if( (pi[i].vt != VT_DISPATCH) && (pi[i].vt != VT_VARIANT) && (pi[i].vt != VT_UNKNOWN) && (pi[i].vt != VT_NULL) && (pi[i].vt != VT_EMPTY ) ) {
          std::wcout << type << L" : " << (const wchar_t *)(_bstr_t)_variant_t(var) << std::endl;
        }
      }
      SafeArrayUnaccessData(psa);
    } else if( (vt != VT_DISPATCH) && (vt != VT_VARIANT) && (vt != VT_UNKNOWN) && (vt != VT_NULL) && (vt != VT_EMPTY ) ) {
      std::wcout << type << L" : " << (const wchar_t *)(_bstr_t)_variant_t(*va) << std::endl;
    } else {
      std::wcout << type << L" : unknown" << std::endl;
    }
  }
}
HRESULT PrintVideoController()
{
  HRESULT  hr;
  try {
      if( FAILED(hr = CoInitialize(NULL)) )
      throw _com_error(hr);

    CComPtr<IWbemLocator> pIWbemLocator;
    if( FAILED(hr = pIWbemLocator.CoCreateInstance(__uuidof(WbemLocator), NULL, CLSCTX_INPROC_SERVER ) ) )
      throw _com_error(hr);

    CComPtr<IWbemServices> pIWbemServices;
    if( FAILED(hr = pIWbemLocator->ConnectServer( _bstr_t(L"\\\\.\\root\\cimv2"), NULL, NULL, 0L, 0L, NULL, NULL, &pIWbemServices )) )
      throw _com_error(hr);

    CoSetProxyBlanket( pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0 );                    

    CComPtr<IEnumWbemClassObject> pEnumDevices;
    if( FAILED(hr = pIWbemServices->CreateInstanceEnum( _bstr_t(L"Win32_VideoController"), 0, NULL, &pEnumDevices ) ) )
      throw _com_error(hr);

      for( ;; ) {
      DWORD  uReturned = 0;
      CComPtr<IWbemClassObject> pDevice;
      if( FAILED(hr = pEnumDevices->Next( 10000, 1, &pDevice, &uReturned ) ) )
        throw _com_error(hr);
      if( uReturned == 0 ) break;

      PrintDeviceDescription( pDevice, L"AcceleratorCapabilities" );
      PrintDeviceDescription( pDevice, L"AdapterCompatibility" );
      PrintDeviceDescription( pDevice, L"AdapterDACType" );
      PrintDeviceDescription( pDevice, L"AdapterRAM" );
      PrintDeviceDescription( pDevice, L"Availability" );
      PrintDeviceDescription( pDevice, L"CapabilityDescriptions" );
      PrintDeviceDescription( pDevice, L"Caption" );
      PrintDeviceDescription( pDevice, L"ColorTableEntries" );
      PrintDeviceDescription( pDevice, L"ConfigManagerErrorCode" );
      PrintDeviceDescription( pDevice, L"ConfigManagerUserConfig" );
      PrintDeviceDescription( pDevice, L"CreationClassName" );
      PrintDeviceDescription( pDevice, L"CurrentBitsPerPixel" );
      PrintDeviceDescription( pDevice, L"CurrentHorizontalResolution" );
      PrintDeviceDescription( pDevice, L"CurrentNumberOfColors" );
      PrintDeviceDescription( pDevice, L"CurrentNumberOfColumns" );
      PrintDeviceDescription( pDevice, L"CurrentNumberOfRows" );
      PrintDeviceDescription( pDevice, L"CurrentRefreshRate" );
      PrintDeviceDescription( pDevice, L"CurrentScanMode" );
      PrintDeviceDescription( pDevice, L"CurrentVerticalResolution" );
      PrintDeviceDescription( pDevice, L"Description" );
      PrintDeviceDescription( pDevice, L"DeviceID" );
      PrintDeviceDescription( pDevice, L"DeviceSpecificPens" );
      PrintDeviceDescription( pDevice, L"DitherType" );
      PrintDeviceDescription( pDevice, L"DriverDate" );
      PrintDeviceDescription( pDevice, L"DriverVersion" );
      PrintDeviceDescription( pDevice, L"ErrorCleared" );
      PrintDeviceDescription( pDevice, L"ErrorDescription" );
      PrintDeviceDescription( pDevice, L"ICMIntent" );
      PrintDeviceDescription( pDevice, L"ICMMethod" );
      PrintDeviceDescription( pDevice, L"InfFilename" );
      PrintDeviceDescription( pDevice, L"InfSection" );
      PrintDeviceDescription( pDevice, L"InstallDate" );
      PrintDeviceDescription( pDevice, L"InstalledDisplayDrivers" );
      PrintDeviceDescription( pDevice, L"LastErrorCode" );
      PrintDeviceDescription( pDevice, L"MaxMemorySupported" );
      PrintDeviceDescription( pDevice, L"MaxNumberControlled" );
      PrintDeviceDescription( pDevice, L"MaxRefreshRate" );
      PrintDeviceDescription( pDevice, L"MinRefreshRate" );
      PrintDeviceDescription( pDevice, L"Monochrome" );
      PrintDeviceDescription( pDevice, L"Name" );
      PrintDeviceDescription( pDevice, L"NumberOfColorPlanes" );
      PrintDeviceDescription( pDevice, L"NumberOfVideoPages" );
      PrintDeviceDescription( pDevice, L"PNPDeviceID" );
      PrintDeviceDescription( pDevice, L"PowerManagementCapabilities" );
      PrintDeviceDescription( pDevice, L"PowerManagementSupported" );
      PrintDeviceDescription( pDevice, L"ProtocolSupported" );
      PrintDeviceDescription( pDevice, L"ReservedSystemPaletteEntries" );
      PrintDeviceDescription( pDevice, L"SpecificationVersion" );
      PrintDeviceDescription( pDevice, L"Status" );
      PrintDeviceDescription( pDevice, L"StatusInfo" );
      PrintDeviceDescription( pDevice, L"SystemCreationClassName" );
      PrintDeviceDescription( pDevice, L"SystemName" );
      PrintDeviceDescription( pDevice, L"SystemPaletteEntries" );
      PrintDeviceDescription( pDevice, L"TimeOfLastReset" );
      PrintDeviceDescription( pDevice, L"VideoArchitecture" );
      PrintDeviceDescription( pDevice, L"VideoMemoryType" );
      PrintDeviceDescription( pDevice, L"VideoMode" );
      PrintDeviceDescription( pDevice, L"VideoModeDescription" );
      PrintDeviceDescription( pDevice, L"VideoProcessor" );
    }
  } catch( _com_error &e ) {
    std::wcout << (const wchar_t *)e.Description() << std::endl;
  }
  CoUninitialize();

    return hr;
}

int _tmain(int argc, _TCHAR* argv[])
{
  PrintVideoController();
  return 0;
}

これでビデオの情報をいろいろと取得できる。
一部、数値のままで意味がわからないものがあるが、それはWin32_VideoController Class を見れば意味がわかる。
後、配列がうまく取れていない。
どうも取得方法が違うようだ。

ただ、難点があって、ドライバが対応していないと情報がない。
ゲームパッドの情報を取ろうとしたら、マニュファクチャー (標準なんとか) とか、標準 HID デバイス とか出て、取得してもどのメーカーの何かさっぱりわからなかったりした。
まあ、MS のパッドや Logitech はちゃんと取得できるので、取得できないメーカーのドライバが手抜きなだけなのだが。

投稿者 Takenori : 21:35 | トラックバック

2008年02月02日

小手先の最適化は遅くなる?

まず、以下のようなコードがあったとする。

float CalculateGradients( FloatImage& H, FloatImage& G, int k )
{
  int width = H.width();
  int height = H.height();
  float divider = pow( 2.0f, k+1 );
  float avgGrad = 0.0f;

  forint y = 0; y < height; y++ ) {
    forint x = 0; x < width; x++ ) {
      int w = (x == 0 ? 0 : x-1);
      int n = (y == 0 ? 0 : y-1);
      int s = (y+1 == height ? y : y+1);
      int e = (x+1 == width ? x : x+1);

      float gx = ( H[y][w] - H[y][e] ) / divider;
      float gy = ( H[s][x] - H[n][x] ) / divider;

      G[y][x] = sqrt( gx * gx + gy * gy );
      avgGrad += G[y][x];
    }
  }
  return avgGrad / (width*height);
}

このコードを見ると、ループ内に判定があって、その判定が一番端でしか成立しないのは見てすぐにわかる。
ループ内に判定があると遅いので、この判定を外に出して最適化を試みようとしてしまったりする。
が、それは止めた方が良い。
VC2005で外に出したものとそうでないもので速度を計測したところ、外に出したほうが 10%ほど遅かった。
最近の最適化はかなり賢いようだ。
小手先の最適化はかえって遅くなってしまうかもしれない。
今回計ってみてそのことに気付いた。
私は、こういうコードがあるとつい判定文を外に出してしまいがちなのだが、きちんと計測して速くなったかどうか見たほうが良いようだ。

投稿者 Takenori : 01:27 | トラックバック

2008年02月05日

CPU拡張命令とコア数

CPU拡張命令とコア数を調べるものを作ってみた。
ソースと実行ファイルをここにおいて置く。
OS は NT系のみ。
ソースの中身は以下と同じ。

Core 2 Duo E6750 と Athlon X2 64 3800+ で実行したら、正しいと思われるのが取得できているが、他でもきちんと動くかどうかわからない。
論理CPU数と物理コア数も両方2だし。
おかしかったら教えてもらえるとありがたいです。

#include <stdio.h>
#include <windows.h>
#include <intrin.h>

#define CPU_X86_MMX      (1<<0)
#define CPU_X86_3DNOW    (1<<1)
#define CPU_X86_3DNOWEXT (1<<2)
#define CPU_X86_MMXEXT   (1<<3)
#define CPU_X86_SSE      (1<<4)
#define CPU_X86_SSE2     (1<<5)
#define CPU_X86_SSE3     (1<<6)
#define CPU_X86_SSSE3    (1<<7)
#define CPU_X86_SSE41    (1<<8)
#define CPU_X86_SSE42    (1<<9)
#define CPU_X86_SSE4a    (1<<10)

enum CpuVendor {
  CPU_V_Unknown,
  CPU_V_Intel,
  CPU_V_AMD,
  CPU_V_Transmeta,
  CPU_V_Cyrix,
  CPU_V_IDT,
  CPU_V_Rise,
  CPU_V_NexGen,
  CPU_V_UMC,
  CPU_V_NSC,
  CPU_V_Compaq,
  CPU_V_EOT
};
void GetCpuid( int op, int& eax, int& ebx, int& ecx, int& edx)
{
  int info[4] = {0,0,0,0};
  __cpuid( info, op );
  eax = info[0];
  ebx = info[1];
  ecx = info[2];
  edx = info[3];
}
static int GetCpuVendor( int& max )
{
  enum CpuVendor  ret = CPU_V_Unknown;
  int eax, ebx, ecx, edx;
  GetCpuid( 0, eax, ebx, ecx, edx );
  max = eax;

#define MAKE_VENDOR_CODE( a, b, c, d )  ( ((d) << 24) | ((c) << 16) | ((b) << 8) | (a) )
  // GenuineIntel
  static const int Intel_EBX = MAKE_VENDOR_CODE( 'G', 'e', 'n', 'u' );
  static const int Intel_EDX = MAKE_VENDOR_CODE( 'i', 'n', 'e', 'I' );
  static const int Intel_ECX = MAKE_VENDOR_CODE( 'n', 't', 'e', 'l' );

  // AuthenticAMD
  static const int AMD_EBX = MAKE_VENDOR_CODE( 'A', 'u', 't', 'h' );
  static const int AMD_EDX = MAKE_VENDOR_CODE( 'e', 'n', 't', 'i' );
  static const int AMD_ECX = MAKE_VENDOR_CODE( 'c', 'A', 'M', 'D' );

  // GenuineTMx86
  static const int Transmeta_EBX = MAKE_VENDOR_CODE( 'G', 'e', 'n', 'u' );
  static const int Transmeta_EDX = MAKE_VENDOR_CODE( 'i', 'n', 'e', 'T' );
  static const int Transmeta_ECX = MAKE_VENDOR_CODE( 'M', 'x', '8', '6' );

  // CyrixInstead
  static const int Cyrix_EBX = MAKE_VENDOR_CODE( 'C', 'y', 'r', 'i' );
  static const int Cyrix_EDX = MAKE_VENDOR_CODE( 'x', 'I', 'n', 's' );
  static const int Cyrix_ECX = MAKE_VENDOR_CODE( 't', 'e', 'a', 'd' );

  // CentaurHauls
  static const int IDT_EBX = MAKE_VENDOR_CODE( 'C', 'e', 'n', 't' );
  static const int IDT_EDX = MAKE_VENDOR_CODE( 'a', 'u', 'r', 'H' );
  static const int IDT_ECX = MAKE_VENDOR_CODE( 'a', 'u', 'l', 's' );

  // RiseRiseRise
  static const int Rise_EBX = MAKE_VENDOR_CODE( 'R', 'i', 's', 'e' );
  static const int Rise_EDX = MAKE_VENDOR_CODE( 'R', 'i', 's', 'e' );
  static const int Rise_ECX = MAKE_VENDOR_CODE( 'R', 'i', 's', 'e' );

  // NexGenDriven
  static const int NexGen_EBX = MAKE_VENDOR_CODE( 'N', 'e', 'x', 'G' );
  static const int NexGen_EDX = MAKE_VENDOR_CODE( 'e', 'n', 'D', 'r' );
  static const int NexGen_ECX = MAKE_VENDOR_CODE( 'i', 'v', 'e', 'n' );

  // UMC UMC UMC 
  static const int UMC_EBX = MAKE_VENDOR_CODE( 'U', 'M', 'C', ' ' );
  static const int UMC_EDX = MAKE_VENDOR_CODE( 'U', 'M', 'C', ' ' );
  static const int UMC_ECX = MAKE_VENDOR_CODE( 'U', 'M', 'C', ' ' );

  // Geode By NSC
  static const int NSC_EBX = MAKE_VENDOR_CODE( 'G', 'e', 'o', 'd' );
  static const int NSC_EDX = MAKE_VENDOR_CODE( 'e', ' ', 'B', 'y' );
  static const int NSC_ECX = MAKE_VENDOR_CODE( 'N', 'S', 'C', ' ' );

  // Compaq FX32!
  static const int Compaq_EBX = MAKE_VENDOR_CODE( 'C', 'o', 'm', 'p' );
  static const int Compaq_EDX = MAKE_VENDOR_CODE( 'a', 'q', ' ', 'F' );
  static const int Compaq_ECX = MAKE_VENDOR_CODE( 'X', '3', '2', '!' );
#undef MAKE_VENDOR_CODE

  if( ebx == Intel_EBX && edx == Intel_EDX && ecx == Intel_ECX ) {
    ret = CPU_V_Intel;
  } else if( ebx == AMD_EBX && edx == AMD_EDX && ecx == AMD_ECX ) {
    ret = CPU_V_AMD;
  } else if( ebx == Transmeta_EBX && edx == Transmeta_EDX && ecx == Transmeta_ECX ) {
    ret = CPU_V_Transmeta;
  } else if( ebx == Cyrix_EBX && edx == Cyrix_EDX && ecx == Cyrix_ECX ) {
    ret = CPU_V_Cyrix;
  } else if( ebx == IDT_EBX && edx == IDT_EDX && ecx == IDT_ECX ) {
    ret = CPU_V_IDT;
  } else if( ebx == Rise_EBX && edx == Rise_EDX && ecx == Rise_ECX ) {
    ret = CPU_V_Rise;
  } else if( ebx == NexGen_EBX && edx == NexGen_EDX && ecx == NexGen_ECX ) {
    ret = CPU_V_NexGen;
  } else if( ebx == UMC_EBX && edx == UMC_EDX && ecx == UMC_ECX ) {
    ret = CPU_V_UMC;
  } else if( ebx == NSC_EBX && edx == NSC_EDX && ecx == NSC_ECX ) {
    ret = CPU_V_NSC;
  } else if( ebx == Compaq_EBX && edx == Compaq_EDX && ecx == Compaq_ECX ) {
    ret = CPU_V_Compaq;
  }
  return ret;
}
unsigned long GetCpuFeature( int& physicalCore )
{
  physicalCore = 1;

  int maxCpuId = 0;
  int vendor = GetCpuVendor( maxCpuId );

  unsigned long flags = 0;
  int eax, ebx, ecx, edx;
  GetCpuid( 0x80000000, eax, ebx, ecx, edx );
  int maxCpuIdEx = eax;

  int featureEx = 0, featureExEcx = 0;
  if( maxCpuIdEx > 0x80000000 ) {
    GetCpuid( 0x80000001, eax, ebx, ecx, edx );
    featureExEcx = ecx;
    featureEx = edx;
  }
  if( featureEx & (1<<22) ) flags |= CPU_X86_MMXEXT;
  if( featureEx & (1<<31) ) flags |= CPU_X86_3DNOW;
  if( featureEx & (1<<30) ) flags |= CPU_X86_3DNOWEXT;
  if( featureExEcx & (1<<6) ) flags |= CPU_X86_SSE4a;

  GetCpuid(1,eax,ebx,ecx,edx);
  int featureEcx = ecx;
  int feature = edx;

  if( feature & (1<<23) ) flags |= CPU_X86_MMX;
  if( feature & (1<<25) ) {
    if( vendor == CPU_V_Intel ) flags |= CPU_X86_SSE|CPU_X86_MMXEXT;
    else  flags |= CPU_X86_SSE;
  }
  if( feature & (1<<26) ) flags |= CPU_X86_SSE2;
  if( featureEcx & 1 ) flags |= CPU_X86_SSE3;
  if( featureEcx & (1<<9) ) flags |= CPU_X86_SSSE3;
  if( featureEcx & (1<<19) ) flags |= CPU_X86_SSE41;
  if( featureEcx & (1<<20) ) flags |= CPU_X86_SSE42;

  if( vendor == CPU_V_Intel && maxCpuId >= 0x00000004 ) {
    GetCpuid( 0x00000004, eax, ebx, ecx, edx );
    physicalCore = ((eax >> 26) & 0x3F) + 1;
  }

  if( vendor == CPU_V_AMD && maxCpuIdEx >= 0x80000008 ) {
    GetCpuid( 0x80000008, eax, ebx, ecx, edx );
    physicalCore = (ecx & 0xFF) + 1;
  }
  return flags;
}

#ifdef _TEST_
#include <iostream>
int main(int argc, char* argv[])
{
  int  logicalCore = 1;
  DWORD  pamask, samask;
  if( GetProcessAffinityMask( GetCurrentProcess(), &pamask, &samask) ) {
    unsigned long  ret;
    _BitScanReverse( &ret, pamask );
    logicalCore = ret+1;
  }
  int physicalCore = 1;
  unsigned long flag = GetCpuFeature( physicalCore );

  std::cout << "Logical core : " << logicalCore << std::endl;
  std::cout << "Physical core : " << physicalCore << std::endl;
  std::cout << "Supported feature :";
  if( flag & CPU_X86_MMX ) std::cout << " MMX";
  if( flag & CPU_X86_3DNOW ) std::cout << " 3DNow";
  if( flag & CPU_X86_3DNOWEXT ) std::cout << " 3DNowEx";
  if( flag & CPU_X86_MMXEXT ) std::cout << " MMX2";
  if( flag & CPU_X86_SSE ) std::cout << " SSE";
  if( flag & CPU_X86_SSE2 ) std::cout << " SSE2";
  if( flag & CPU_X86_SSE3 ) std::cout << " SSE3";
  if( flag & CPU_X86_SSSE3 ) std::cout << " SSSE3";
  if( flag & CPU_X86_SSE41 ) std::cout << " SSE4.1";
  if( flag & CPU_X86_SSE42 ) std::cout << " SSE4.2";
  if( flag & CPU_X86_SSE4a ) std::cout << " SSE4a";
  std::cout << std::endl;

  return 0;
}
#endif // _TEST_

投稿者 Takenori : 21:27 | トラックバック

2008年02月06日

最大公約数 - ユークリッドの互除法

最大公約数求める方法として、ユークリッドの互除法がある。
ユークリッドの互除法を見ると、ブレントが改善した方法が書いてある。
で、ブレント版を探したけど見つけられなかったので書いてみた。
以下のような感じ。

// 最大公約数 ( Greatest Common Divisor ) を求める
// ユークリッドの互除法による実装
inline long gcd(long m, long n) {
  while( n != 0 ) {
    long temp = m % n;
    m = n;
    n = temp;
  }
  return m;
}

// 最大公約数 ( Greatest Common Divisor ) を求める
// ユークリッドの互除法のブレント改良版
inline long gcd_brent(long m, long n) {
  // まずは2の倍数を取り除く
  long  c = 0;
  while( ((m|n) & 1) == 0 ) {
    m >>= 1;
    n >>= 1;
    c++;
  }
  // 2の倍数は取り除いたことから、偶数は最大公約数の共通因子とはなり得ないので
  // 2で割って奇数にする
  while( (m&1) == 0 ) m >>= 1;
  while( (n&1) == 0 ) n >>= 1;

  while( m != n ) {
    if( m > n ) {
      m-=n;
      // 奇数同士の引き算は偶数になるので1度は必ず2で割れる
      do { m >>= 1; } while( (m&1) == 0 );
    } else {
      n-=m;
      // 奇数同士の引き算は偶数になるので1度は必ず2で割れる
      do { n >>= 1; } while( (n&1) == 0 );
    }
  }
  return n << c;
}

10万までの組み合わせで確認したところ、ユークリッドの互除法と結果が一致することは確認した。
桁数が大きい場合は特に効果的と書いていたので、測ってみた。

・1~1万までの組み合わせ
Core 2 Duo E6750の結果
 ブレント版 4300 msec ぐらい ( 100 % )
 オリジナル 3150 msec ぐらい ( 73 % )
Athlon 64 X2 3800+ の結果
 ブレント版 5780 msec ぐらい ( 100 % )
 オリジナル 8140 msec ぐらい ( 141 % )

・100万~101万の組み合わせ
Core 2 Duo E6750の結果
 ブレント版 7500 msec ぐらい ( 100 % )
 オリジナル 3930 msec ぐらい ( 52 % )
Athlon 64 X2 3800+ の結果
 ブレント版 9730 msec ぐらい ( 100 % )
 オリジナル 9930 msec ぐらい ( 102 % )

Core 2 Duo の場合は、普通のユークリッド互除法のほうが両方速い。
剰余算が速いのか。
Athlon 64 の場合は、ブレント版の方が早いけど、桁数が大きくなるとかなり差が縮まっている。

なんか、普通のユークリッド互除法使ったほうがいいんじゃないの?
ブレント版は、ビット演算と足し算、引き算しか使ってないので、割り算とかが苦手なCPUだと効果的かも。
でも、それは今は昔か……

ブレント版は、もっと改善できるんだろうか?
ソースコードが悪いのかもしれない。

追記:
ブレント版は分岐予測に失敗する可能性が高くて遅いかもしれないとか。

投稿者 Takenori : 23:55 | コメント (1) | トラックバック

2008年02月07日

色温度の導出

色温度という言葉は良く聞くけど、それが何かは良くわからなかった。
調べてみると、理想的な黒体がある温度において放射する光の色なんだとか。
よくある理想物体か。
まあ、それはどうでも良くて、ある温度での色がわかると炎や爆発などの色を作るときに都合が良い。
と思ったんだけど、どうも計算式で求められるものではなく、「黒体の絶対温度と色度座標との関係」というデータテーブルが存在し、そこから導出するっぽい。
でも、そのデータテーブルが見つけられなかったので、とりあえずこの色温度-色色雑学 | コニカミノルタのページのグラフから、交点っぽいところをリストアップして、このデータを基に近似式を作ることにした。
近似式は、xy色度図上の x と y の関係、ケルビンと x との関係を出し、ケルビンから x と y を求められるようにする。
ということで、このデータを Excel に入れて近似曲線を描かせる。
x と y の関係は、単純な2次の多項式近似でかなり近いものが出た。
※ ちなみに Excel のグラフの近似曲線は、近似曲線の書式設定のオプションの数式の表示にチェックを入れてやると数式を見ることが出来る。また、多項式近似は、Excel で回帰分析した結果と一致するようだ。

が、ケルビンとの関係は誤差が大きい。
いい近似曲線が出せない。
誤差が大きくてもいいかと思ったが、よくよく色温度の図を見てみるとケルビンは対数的な変化をしているように見える。
試しにケルビンの自然対数値を求め、それと x とのグラフを作ってやって3次の多項式近似で近似曲線を描くとかなり一致している。
ケルビンは対数値を取ってから求めた方が良さそうだ。

以上の方法で求めた近似式から出した色を描画すると以下のようになった。

2008_02_07_color_temperture.png

範囲は、自然対数値で 7.0 ~ 10.0 。
ケルビンに直すと 1096K ~ 22026K 。
Wikipedia の 色温度 のカラーチャートと上の図はだいぶ似ていると思う。

ってことで、ケルビンから色温度の色を求めるソースは以下の通り。

#include <math.h>

class ColorTemperture
{
  //! ケルビンの自然対数値からx座標に変換する
  static inline float KelvinLnToX( float x ) {
    return( -0.0108*x*x*x + 0.3407*x*x - 3.5987*x + 12.929 );
  }
  //! ケルビンからx座標に変換する
  static inline float KelvinToX( float K ) {
    return KelvinLnToX( log(K) );
  }
  //! x座標からy座標を求める
  static inline float XToY( float X ) {
    return -2.700931324 * X * X + 2.675633166 * X - 0.250545382;
  }
public:
  //! @param K : ケルビン
  //! @param Y : 輝度 - 0.0 ~ 1.0
  //! @return RGB24値 - 0x00RRGGBB
  static unsigned long GetColor( float K, float Y );
  //! @param K : ケルビンの自然対数値
  //! @param Y : 輝度 - 0.0 ~ 1.0
  //! @return RGB24値 - 0x00RRGGBB
  static unsigned long GetColorLn( float K, float Y );
};
unsigned long ColorTemperture::GetColor( float K, float Y )
{
  RColor  Yxy;
  Yxy.Y = Y;
  Yxy.x = KelvinToX( K );
  Yxy.y = XToY( Yxy.x );
  return ColorConversion::YxyToRGB32( Yxy );
}

unsigned long ColorTemperture::GetColorLn( float K, float Y )
{
  RColor  Yxy;
  Yxy.Y = Y;
  Yxy.x = KelvinLnToX( K );
  Yxy.y = XToY( Yxy.x );
  return ColorConversion::YxyToRGB32( Yxy );
}

ケルビンとの関係で出したのは、Yxy 色空間の色なので、これは RGB 色空間に変換してやらないと使い辛い。
上のソースで呼んでいる色変換メソッドのソースは以下の通り。

//! 実数形式での色
union RColor {
  struct { float  R, G, B; };
  struct { float  Y, x, y; };
};
//! 色変換
struct ColorConversion {
  // RGB -> Yxy
  static void RGBToYxy( const RColor& rgb, RColor& Yxy ) {
    float X = 0.412453 * rgb.R + 0.35758 * rgb.G + 0.180423 * rgb.B;
    Yxy.Y   = 0.212671 * rgb.R + 0.71516 * rgb.G + 0.072169 * rgb.B;
    float Z = 0.019334 * rgb.R + 0.119193 * rgb.G + 0.950227 * rgb.B;
    Yxy.x = X / ( X + Yxy.Y + Z);
    Yxy.y = Yxy.Y / ( X + Yxy.Y + Z);
  }
  // Yxy -> RGB
  static void YxyToRGB( const RColor& Yxy, RColor& rgb ) {
    float X = Yxy.x / Yxy.y * Yxy.Y;
    float Z = (1.0 - Yxy.x - Yxy.y) / Yxy.y * Yxy.Y;
    rgb.R = 3.240479 * X - 1.53715 * Yxy.Y - 0.498535 * Z;
    rgb.G = -0.969256 * X + 1.875991 * Yxy.Y + 0.041556 * Z;
    rgb.B = 0.055648 * X - 0.204043 * Yxy.Y + 1.057311 * Z;
  }
  // Yxy -> RGB24 (0x00RRGGBB)
  static unsigned long YxyToRGB32( const RColor& Yxy ) {
    RColor  rgb;
    YxyToRGB( Yxy, rgb );
    // 各値を0 - 255にする
    int r = rgb.R * 255;
    int g = rgb.G * 255;
    int b = rgb.B * 255;
    r = r >= 255 ? 255 : r <= 0 ? 0 : r;
    g = g >= 255 ? 255 : g <= 0 ? 0 : g;
    b = b >= 255 ? 255 : b <= 0 ? 0 : b;
    return ( (r << 16) | (g << 8) | b );
  }
};

ってことで、このソースがあればケルビンから色が出せる。

「黒体の絶対温度と色度座標との関係」というデータテーブルがどこにあるか知っている人がいたら教えてくれるとありがたいです。
上のソースでもかなり近いものが出ているけど、そのデータがあればもっと近づけられるはず。
まあ、ゲームなどの用途であれば上のソースでもなんら問題ないんだけど。

投稿者 Takenori : 23:01 | コメント (4) | トラックバック

2008年02月08日

関数オブジェクトの名前

STL には、unary_function と binary_function が定義されていて、これを継承して作るが、もし引数がこれ以上多かったりした場合、名前はどうする?
ということで、調べた。
0 ~ 12 まで。
nullary, unary, binary, ternary, quaternary, quinary, senary, septenary, octonary, nonary, denary, undenary, duodenary
らしい。
これで、引数 12個まで作れる。

投稿者 Takenori : 20:40 | トラックバック

2010年01月30日

Visual Studio 2008 と Windows 98

気付いてなかったんだけど、Visual Studio 2008 でビルドすると Windows 98 では動かない実行ファイル/DLLが出来る。
何でかなぁと思った調べていたら、対応が削除された様子。
互換性に影響する変更点
実行ファイルに動作する Windows のバージョンが埋め込まれているようなんだけど、この値が変更されて動作しなくなったようだ。
ただ、その値を書き換えればうまくいくかというと、それでもだめなよう。
Visual C++ 2008 で Win98/Me/NT4 対応バイナリを作りたい このページにその辺りのことが書かれている。
リンカのオプションで /SUBSYSTEM:WINDOWS,4.0 とかしてもエラーになってリンクできない。
Windows 98 な対応させたかったら Visual Studio 2005 使わないとダメなよう。

Windows98とVisualStudio2008 ここでも同じことが書かれている。
と言うか、気付くの遅すぎか。

今公開している吉里吉里2についている krmovie.dll ってどっちでビルドしたっけ?
まあ、9X 系のサポートは吉里吉里2でも削除されてるのでいいと言えばいいけど。
1年半くらい誰にも何も言われてないから 2005 でビルドしてたのかな。
次ビルドする時は、気をつけておこう。
ただ、Windows SDK の対応表を見ると、Windows 7 で動く 6.1.7600.16385 では、Visual Studio 2005 の対応が、非推奨になっているので、また面倒。
krmovie.dll のビルドに必要な DirectShow は、Windows SDK に移っているので、Windows 7 で新しく環境作ってビルドしようとしたら、はまるかもしれない。

Windows 98 は、主要コンポーネントだけ見ると DirectX 9、Windows Media Player 9、IE6 とまだまだ使えそうなんだけど、開発する側の環境が厳しくなってきているか。
今リリースしているプラグインは、Visual Studio 2008 でビルドしているので、Windows 98 では動かないものがほとんどなんだけど。気付いてなかったから。
最近 Windows 98マシンを入手したので、その辺りの確認をしたから気付いただけと言う状態。
Visual Studio 2008 にして、Windows 9x系 を切るのなら UNICODE 前提でやってしまっていいから、その辺りは楽になる。

2010/01/31 追記
かなり強引な方法でDLLのインポートを横取りする(未参照を防ぐ)
Windows 95 で動くプログラムを作る (VS2008編)
ここに無理矢理動かす方法がのっていたのでリンク。
試してみると、バージョン書き換えすれば読み込みは少し進むけど、フリーズした。
リンク先のアセンブリコード追加すると、また DLL がロードできなくなった。
アセンブラのオプションとかがまずいのかな。
まあ、そこまで VS2008 でなんとかしたいわけではないからいいか。

投稿者 Takenori : 18:41 | トラックバック

2012年03月25日

とりあえずソースコード等を貼り付けるサービス

Pastie
よく忘れてしまうのでメモしておく。
twitter などでソースコードの断片やログなどを貼って知らせたり、例を見せたりしたい時に便利に使える。
テキストを貼り付けると固有のURLが生成されるので、それをツイートするだけ。
ソースコードの予約語のハイライト等も行ってくれるので見やすい。

投稿者 Takenori : 15:38 | トラックバック

2012年12月18日

色ビット数拡張

RGB555 から RGB888 へ変換する時、単純にシフトでやると、最大値の 0x1F が 0xF8 と少し暗くなってしまう。
(0x1F * 0x21) >> 2 = 0xFF とやると、最大値は最大値のままに出来る。
他もそれなりの幅で拡大される。
よく忘れてしまうのでメモ。

4bit -> 8bit
0xF * 0x11 = 0xFF

5bit -> 8bit
(0x1F * 0x21) >> 2 = 0xFF

6bit -> 8bit
(0x3F * 0x41) >> 4 = 0xFF

7bit -> 8bit
(0x7F * 0x81) >> 6 = 0xFF
----------------------------------
ビット演算で出来るとコメントもらい追記。

4bit -> 8bit: (0xF << 4) | 0xF = 0xFF
5bit -> 8bit: (0x1F << 3) | (0x1F >> 2) = 0xFF
6bit -> 8bit: (0x3F << 2) | (0x3F >> 4) = 0xFF
7bit -> 8bit: (0x7F << 1) | (0x7F >> 6) = 0xFF

関係ないけどコメントの方>>がうまくエスケープされてないな……
なんだろう。

投稿者 Takenori : 23:23 | コメント (2) | トラックバック

2013年09月19日

項目メタデータ マクロ

カスタムビルドの対象ファイル名などを指定するのに項目メタデータ マクロと言うものを使うが、リンクがつながっておらず簡単には見付けられない。
旧バージョンなら $(InputFileName)などで指定可能だが、Visual Studio 2012 だと、項目メタデータ マクロで指定する。
で、これがどこにあるかというと MSBuild 既知のアイテム メタデータ にあった。

こんな感じで指定する。
ml /coff /Zi /c /safeseh /Fl$(Configuration)\%(Filename).lst /Fo $(Configuration)\%(Filename).obj %(RelativeDir)%(Filename).asm

投稿者 Takenori : 23:46 | トラックバック

2017年05月22日

Git submodule メモ

自分で使う場合によく出くわすものについて。
前提 : Git の submodule は、特定リポジトリの特定コミットとフォルダを関連付ける物。
特定コミットなのが曲者で、submodule 内をいじるとその度に何をすればいいんだったか検索している。

自分の場合は、git status で見た時ほぼ以下のどちらか。

(untracked content) : ローカル submodule 側が更新されているけど、参照側でコミットしていないなど。
TortoiseGit でコミットしようとしたときにサブモジュールがリストに現れる時はこれかな?
自分の場合は、.gitignore 追加漏れで発生していることが多い。
コミットする必要のないファイルで更新がかかっているけど、コミット漏れ扱いになってる。

対策は、.gitignore に追加する必要のないものはきちんと追加する。
まだコミットしていないものがあるのならコミットする。


(new commits) : submodule 側が進んでいるので、参照先コミットを差し替える。
サブフォルダの submodule に変更加えて、そのままそこでコミット/push を行っていると出る。
git add submodule_path コマンドで参照先を差し替えてやる。
例 : git add external/zlib
参照先を差し替えた後はコミット。

自分の場合はほぼこの2パターン。
サブフォルダで編集して、そのまま submodule の方へコミットするというような更新の仕方をしているからだと思う。

確かこうだったはずと、このエントリーを書いているので、次に同じことになった時、間違っていたらこの記事は更新する。

投稿者 Takenori : 16:55

 
Total : Today : Yesterday :