Archives

February 2009 の記事

以前申し込んだ UQ WiMAX、サービス開始当日 26日に端末が届きました。
モニターではなく購入です。

USB タイプの UD01SS を VAIO type P + Windows7 で使ってみます。

WiMAX USB TYPE UD01SS

uqwimax
↑何となく取れそうに見えた先端の蓋部分はやっぱり外すことができた。アンテナ端子?


●Windows7 Beta へのインストール (UD01SS)

ドライバはそのままで OK。ユーティリティの方は OS のバージョンではじかれました。
互換モードで Windows Vista を指定するとインストールできます。

・ドライバ

UD01SS を差し込むだけで認識し、組み込まれているドライバのインストーラが
起動します。最初は USB の CD-ROM ドライブとして認識されるようです。
あとは手順に従うだけ。Windows7 beta でも特に問題はありません。

・ユーティリティ

ユーティリティソフトは付属の CD-ROM に入っています。
Web からも落とせるようです。
CD-ROM の Setup.exe をそのまま実行するとエラーがでるので、先にプロパティから
「互換性→互換モード→Windows Vista」を選んでおきます。


●接続

WiMAX GO のアイコンをクリックするだけ。
ユーティリティを起動しておけば電波状況を表示し、圏内なら接続します。
特に設定もなくアダプタの抜き差しなども判定してくれるので手軽です。
Windows7 beta でも問題なく使えています。

接続や切断の管理も不要で、中断する場合も PC のモニタを閉じてスリープする
だけで大丈夫。再接続も自動で行われます。
スリープ解除後の再接続は、端末の接続チェックと初期化が入るので数秒待たされます。


●速度

VAIO type P + Windows7 beta + Chrome
という特殊な環境なので参考程度に。

・下り
笹塚駅 2Mbps 前後
麹町 4Mbps ~ 9Mbps (測定サイトによって異なる)

測定サイトによって結構ばらつきがあります。
駅のホームで試した場合も、場所によって結構違うし電車の通過によって
影響を受けることもありました。

つながらない場合は完全に圏外になります。つながらない場所は結構あります。
最初に試したサイトが低い数値しか出さなかったのであまり参考にならないかも
しれませんが、安定して圏内を維持する場合は 1Mbps 以上出てるようです。
ブラウザを使ってる分には普段無線 LAN でつないでるのと変わらず速いです。


関連エントリ
UQ WiMAX 申し込み




ミニノートを持ち歩いていて、普段ネット端末やメモ機として使っています。
この場合外部ディスプレイ出力を使うことがまずないのですが、でもごくまれに
必要になることがあります。

小型なノートPC で特殊なコネクタを採用している場合は少々やっかいです。
専用アダプタや専用ケーブル経由で接続しなければならず、
かさばるし、滅多に使わないのに専用品を購入しなければならないし、
必要になったときにすぐ手に入らないかもしれません。

また実際に必要になって購入を考えると結構高いものです。
MobileGear2 の時はモニタ用ケーブルだけで 4000円、利用回数一回きり。
LOOX U50 店頭モデルでは本体同梱、使ったのは 2~3回。
VAIO type P の場合別売りで VGP-DA10 4980円 (LANアダプタ兼用)。

そこで専用品ではなく USB 接続のモニタアダプタを使うことにしました。
これだと機種を変えても使い回せるし、普段はデスクトップ PC につないだりと
他の用途でも使えます。

IO DATA USB-RGB
IO DATA USB-RGB/D

最近 USB で接続できる液晶モニタも話題になっていますが同じ原理のようです。

ぼくらは「USB-RGB」を誤解していたかもしれない (1/4)

USB バスパワーで動作するので外部ディスプレイ出力アダプタとほぼ同じ感覚で使え
そうです。

実際に試したところ、現在の Windows7 beta 1 では正しく動作しませんでした。
一応ソフトウエアのインストールが可能で、絵は出るものの解像度やモード
切り替えなどが出来ません。

VAIO type P にクリーンインストールした Vista の方では正常に動作しました。
ただし VAIO type P の場合あらかじめ Aero を切っておいた方がよいです。
接続時にエラーになりました。
タスクトレイアイコンからのモード切り替えも可能で 640x480 も使えています。

小型プロジェクター ADTEC AD-MP15A との接続も確認。
800x600 でもきちんと表示できました。

純正アダプタによる直接出力と比べると描画速度が多少遅いはずですが思ったより
気になりません。打ち合わせなどで文書や資料を表示するには十分です。


関連エントリ
ADTEC AD-MP15A 小型プロジェクター



マウスの感度を示す単位はミッキーというらしい。
じゃあどうやってミッキーを測るんだろう、ということで簡単に試してみました。

wikipedia マウス

マウスの入力値の取得には DirectInput を使用します。
DirectInput の Buffer モードを使って移動量の変化を取り出します。そのままだと
DInput の取得値にも速度に応じて加速がついてしまうので設定が必要です。

・コントロールパネルのマウスから
  ポインタオプションのタブ→速度のグループで
  「ポインタの精度を高める」のチェックを外す

これで加速補正が無くなります。
「ポインタの速度」は DirectInput の値に影響しません。

マウス付属のドライバやユーティリティは使わず、Windows 標準のドライバのみ
使用しています。

こんな感じでマウスにガイドを付けます。
出来るだけ水平、または垂直に移動できるように。

d2dmouse

カウンタをリセットしてだいたい 1インチ分だけ移動して得られた値を調べます。
精度が高いため手で試してもかなり誤差が入り厳密な値はとれません。
以下数値はかなりいい加減です。

だいたい 800~1000 くらいの範囲の値を返します。
おそらくこれがマウスセンサーのカウント値です。
1インチ移動する間に何回センサーの On/Off をカウントできるのか、どれだけ細かく
動きを読み取れるのか表す単位として cpi (count per inch ) があります。
メーカーによっては dpi (dot per inch) と表現されているものもありますがおそらく
同じものです。

テストしたマウス
(1) BlueTrack
Microsoft Explorer Mini Mouse
1000dpi/8000fps

結果
・800~1025 (8~10.2ミッキー)


(2) Laser
SIGMA APO SLATWRF
800,1600cpi/6600fps

結果
・800cpi モードで 800~900 (8~9ミッキー)
・1600cpi モードで 950~1450 (9.5~14.5ミッキー)


◎速く動かした方が値が小さい

センサーの読み取りエラーが発生しやすくなるためと思われます。速く動かした場合の
耐性を表す目安が、マウススペックに記載されている fps となるようです。


◎精度を切り替え可能な場合

低い cpi モードの方がほぼ期待通り、安定した数値になっています。

高い cpi モードの場合、全体的に数値は増えますがスペック通りの上限には達しま
せんでした。材質の相性や速度の影響を受けやすくなっているためと思われます。
精度は上がるがよりノイズを含んでいる状態といった感じでしょうか。


◎相性

机やマウスパッドの材質とセンサーの相性でかなり変わります。
反応が悪いと半分くらいの値になることも。


カタログスペック通りと想定するなら、800cpi(dpi) のマウスは 8ミッキーでカウント
値からマウスの移動量がわかります。もし正確ならば、マウスを使って長さを測る
こともできるでしょう。

でも実際は机やマウスパッドとの相性と速度の影響が大きく、頻繁に読み取りエラーが
発生していることがわかります。平均ミッキーはこれらの条件によって変わります。
特に速度は重要なパラメータで、cpi だけでなくマウスの性能として fps も併記
されているのはそのためでしょう。

きちんとツール化して調べれば、最もエラーの少ないマウスパッドの組み合わせを
探し出すなど活用できそうです。


参考にさせていただいたページ
その37.Windows標準のポインタ(カーソル)速度の変遷
Windows XP でマウス ポインタの加速を調節する方法

// DirectInput
DWORD	elems= 32;
DIDEVICEOBJECTDATA	data[32];
HRESULT	hr= iDevice->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), data, &elems, 0 );
if( FAILED( hr ) ){
    return;
}
DWORD	ac= elems;
for( DWORD i= 0 ; i< ac ; i++ ){
    switch( data[i].dwOfs ){
    case DIMOFS_X:
	PositionX-= *(int*)(&data[i].dwData);
	break;
    case DIMOFS_Y:
	PositionY-= *(int*)(&data[i].dwData);
	break;
    }
}




Mercurial のサーバー設定まで

●Debian install

Debian -- The Universal Operating System

5.0 Lenny が release されたので "stable" になりました。
今回は Network install より Small CDs の i386
debian-500-i386-netinst.iso を使用しています。

VirtualBox 2.1.4 を使っています。
手順は 前回 と全く同じです。


●Debian の設定

# apt-get update
# apt-get install ssh

以後 putty 経由

# apt-get install mercurial
# apt-get install hgsvn

subversion から

# svn ls https://SVNSERVER/svn/src2

# mkdir /var/lib/hg
# cd /var/lib/hg
# hgimportsvn https://SVNSERVER/svn/src2 src2
# cd src2
# hgpullsvn
# chown -R www-data.www-data src2

SSL 設定

# apt-get install ssl-cert
# make-ssl-cert generate-default-snakeoil --force-overwrite
# a2ensite default-ssl
# a2enmod ssl
# /etc/init.d/apache2 restart

Mercurial 設定

# mkdir /var/www/hg
# cd /var/www/hg
# cp /usr/share/doc/mercurial/examples/hgweb.cgi ./index.cgi
# chmod 700 index.cgi
# chown www-data.www-data index.cgi
# vi index.cgi

application = hgweb("/var/lib/hg/src2", "src2")

# vi /etc/apache2/sites-available/hg-src2

Alias /hg /var/www/hg
<Directory "/var/www/hg">
 DirectoryIndex index.cgi
 AddHandler cgi-script .cgi
 Options ExexCGI
 AuthType Basic
 AuthName "hg repository"
 AuthUserFile /etc/apache2/hg.passwd
 Require valid-user
</Directory>

# htpasswd -c /etc/apache2/hg.passwd USER
# a2ensite hg-src2
# /etc/init.d/apache2 restart

# vi /etc/mercurial/hgrc

[web]
allow_push = *



●確認

Windows Mercurial-1.0.2 にて

C:\hg> hg clone https://HGSERVER/hg


参考ページ
Setting up a Mercurial CGI Server
MercurialのCGIサーバをセットアップする
hgsvn



WindowsMobile6.x がもう一世代継続されるようです。

米マイクロソフト、スペインで「Windows Mobile 6.5」発表

今でもプラグインが多いと SIP が読み込めなかったり切り替わらなかったり、
ブラウザのようなメモリを多く消費するアプリが不安定になったりしています。
WindowsMobile6.x のカーネルは WindowsCE5.2 で、WindowsMobile5.0 とそれほど
大きく変わっていません。
下記のページで詳しく解説されています。

Windows CE .NET の高度なメモリ管理

これを見ると CE4~5 のメモリ管理は下記の構造になっていることがわかります。

(1プロセス 32MB × 32プロセス) + 32MB の XIP DLL 空間 (ROM DLL)
  + ラージメモリ空間(992MB) + カーネル空間(2GB)

CE の限界は、上記の通り 1プロセスあたりのメモリ空間が 32MB に制限されていること。
最近の端末では 128MB~256MB の RAM を搭載していますが、1つのアプリケーションが
メモリ空間的に限界に達しているときは、いくら物理メモリを増やしても効果がない
ことがわかります。(desktop Windows でも 32bit OS だと 2GB まで、という制限に似ています)

32MB の XIP DLL 空間は、ROM にあらかじめ組み込まれている DLL を配置するための
領域だそうです。system の共有 DLL が XIP 空間に配置されている場合は 32MB の
プロセス領域を消費しません。

ROM に含まれていない (XIP でない) DLL は XIP 空間に配置できません。
こちらはプロセスのメモリを消費します。さらに DLL はプロセス間で共有されるために
ロードしたアドレスが固定されてしまうようです。
よって SIP プラグインなど、後から追加した dll が多数読み込まれている場合は、
各アプリケーションで使用可能なメモリ空間を食いつぶしてしまう可能性があります。

たとえば XIP でない DLL の合計が 16MB あって、かつ最もよく使われる dll が
一番下位のメモリにロードされてしまった場合。またはアプリケーションが複数の
dll をロードする構造の場合、最悪アプリケーションが使用可能なメモリ空間も
16MB になってしまうということです。

これらのこの問題に拍車をかけているのがもう 1つの制限、プロセス数の上限が
32個ということ。常駐するアプリケーションはプロセスを消費しないようサービス
として dll 化することが推奨されていているため、dll 空間を余計圧迫している
可能性があります。また単一のプロセス空間を取り合うため、dll を配置する
サービス用の空間自体も限界に達してしまう可能性もあります。

常駐する dll が多い場合、追加で dll 自体をロードできなくなる場合があること、
またブラウザなどの大きなアプリが不安定になる可能性があることがわかります。
メモリが大量にある場合、dll の共有はかえって逆効果なのかもしれません。

ただ実際に touchkeysip が読み込まれなくなる場合、どの段階で限界が来ており
どこのメモリ確保でエラーが出ているのかきちんと調べたわけではないので、
上の説明もいろいろと憶測が含まれています。あらかじめご了承ください。
単に touchkeysip が悪いだけの可能性もあります。

データ領域に限っていえば、ラージメモリ領域を使用することが出来るためまだ余裕は
ありそうです。上の MSDN のページに書いてある方法で大きなメモリブロックを
まとめて VirtualAlloc で予約すると、ラージメモリ領域に配置されるとのこと。

・常駐する dll を減らす。
・常駐する dll は一番先に読み込ませておく。
・アプリが消費するヒープメモリは自分でラージメモリから確保する。
・アプリを使い終わったらすぐ終了させる。特に独自の dll を使うもの。

等が有効かもしれません。

WindowsCE6.0 (WindowsMobile7) 以降は一般的な 32bit OS と同じ 2GB に拡張され、
プロセス数の制限もなくなるはずです。逆に普通の Windows に近づくので
CE である必要性が徐々に薄れていくような気もします。
先があることがわかっている中に出る 6.5 は Me を思い出してしまいます。


関連エントリ
emobile EM・ONE 使い切れないメインメモリ



久しぶりに思い出したのは、昔作った BASIC コンパイラが欲しいとのメールを
いただいたからです。雑誌に掲載されたのが 1989年なので 20年くらい前になります。

コンパイラといっても、当時の SHARP ポケットコンピュータ PC-1261/62 用に
見よう見まねで作ったもの。
名前は「 LC-3 コンパイラ 」といいますが、今検索で引っかかる
LC-3 compiler とは名称が同じだけで別物です。

なにせ RAM も 10.4KB と小さく、外部記憶装置はカセットテープしかありません。
そのかわり RAM はバッテリーバックアップされており、電源を切っても中身が
消えずに保持されます。

BASIC プログラムは中間コードでメモリに格納されています。それを読み出して
機械語に置き換えるわけです。エディタと字句解析に BASIC を使っているだけなので、
中間コードやキーワードを流用しつつも構文や命令は独自で完全互換ではありません。
知識も無かったので本当に単純なものでした。
その代わりメモリ容量との戦いがあります。一晩考え抜いて 1byte 削っては喜んで、
といった感じに。

資料がないので記憶だけですが、中間コードのソース領域が 3KB くらい、コンパイラ
本体が 2.0KB、ランタイムルーチンが 1.5KB、変換後のバイナリが 3KB くらいの割り
当てだったと思います。

一式全部オンメモリで動作するので、一度入力すればカセットデッキが無くても
プログラムを書けるし、そのまま動作もデバッグもできるのが特徴です。


当時すでにポケコン向けの BASIC コンパイラはいくつか発表されていました。
PiO の「ポケコンパイラ」や The BASIC のコンパイラ(正式名称失念)など。
さらにメモリが少ない RAM 4.2KB PC-1251 で動作しており、どれも出たときは
かなりすごいと思ったものです。

生成コードが洗練されていなくても BASIC インタプリタと比べるとかなり高速に
動作しました。たとえばポケコンの BASIC は整数型が無く、全部 BCD の 10進数演算が
使われています。変数 1つで 8byte。今だと double と同サイズですがポケコンでは
これでも単精度です。2進化の誤差も発生しない代わりに、ただのループ変数の
インクリメントすら決して高速とはいえません。
コンパイラが生成するコードは 1byte 整数型を使用するので、出来ることは減るけど
高速です。


LC-3 は後発なのでさまざまな改良点があります。
その 1つが出力コードの最適化。中でも CPU の内 RAM を変数に割り当てたことで
高速化とともに生成コードの縮小化が出来ました。
もう 1つは用途を考えてゲーム向きの命令を用意したこと。

実際に作ったのはおそらく 1987年頃。1988年に掲載された Kind-1 などの一連の
ゲームも、最初 LC-3 上で作っていたからです。
コンパイラ自体が雑誌に未掲載だったため、投稿前にマシン語で書き直しました。


ネットが無い時代なので何らかの情報入手も雑誌が頼りでした。
プログラムの入手も誌面を埋め尽くすダンプリストを見ながら手打ちです。

投稿も同様、ワープロもなくて原稿用紙に手書きです。
プログラムの説明や入力方法、使い方、メモリマップやワークエリア等を原稿用紙に
手で書いて、カセットテープと一緒に郵送します。
だから 8bit パソコン時代のプログラムはデジタルデータとして手元に残って
いないのです。

もしあるなら web ページとかに掲載できるのですが、プログラムの保存方法は紙に
印字するかカセットテープだけ。
CE-125 は感熱紙なので消えているかもしれないし、仮にテープから読めても
通信機能がないので PC に転送する手段がありません。
手書きの資料はまだ残ってると思いますが、雑誌を探した方が確実な気がしてきました。

雑誌は投稿しても必ず掲載されるわけではありません。
だいたい半数くらいは没で、初期の頃はもっとたくさん。
投稿した原稿のコピーも全く無いので失われているものは結構多いのかもしれません。


便利な環境に慣れすぎたので、原稿用紙に文字数を数えながら清書するなんてたぶん
もう出来ないでしょう。送っても結果が出るまで何ヶ月もかかります。

今なら文章も簡単に書けるし編集可能だし、紙を媒体にする必要もありません。
ネットにはすぐ反映されるし、おそらく一度デジタル化したデータは簡単に失われたり
読めなくなったりすることもないでしょう。複製も容易に取っておけます。
手で打ち込み直す必要もないし配布も入手も簡単です。

そのときは全く考えもしなかったけどいろいろなことが不便でした。
配信、バックアップのための複製、転送、蓄積など。
紙や印刷の登場以前と以後といったら大げさかもしれませんが、コンピュータの
ネットワーク化とデータのデジタル化は、同じように生活を変えたのかもしれません。



昨日の Tessellate 結果を図示したものです。

FillGeometry() 相当の場合
ポリゴン化の状態をわかりやすくするために flatteningTolerance = 4 で Tessellate
しています。

direct2d

iFactory->CreateEllipseGeometry(
	D2D1::Ellipse( D2D1::Point2F( 0,0f, 0.0f ), 80.0f, 80.0f ), &iElli );
DummySink	tsink;
iElli->Tessellate( NULL, 4.0f, &tsink );

DrawGeometry() 相当の場合
strokeWidth = 4.0 で描画した場合のポリゴン分割です。
こちらも見やすいように flatteningTolerance = 4 で分割しています。

direct2d

iFactory->CreatePathGeometry( &iPath );
ID2D1GeometrySink*	isink;
iPath->Open( &isink );
iElli->Widen( 10.0f, NULL, NULL, isink );
isink->Close();
DummySink	tsink;
iPath->Tessellate( NULL, 4.0f, &tsink );

デフォルトのままだとこれくらいの分割です。
水平方向の分割かつ Strip になっているのがわかります。

direct2d

テストに使った DummySink の定義。

class DummySink : public ID2D1TessellationSink {
private:
    static ID2D1PathGeometry*	iPath;
    static ID2D1GeometrySink*	iSink;
public:
    DummySink()
    {
    }
public:
    STDMETHOD_( void, AddTriangles )( const D2D1_TRIANGLE* triangles, UINT trianglecount )
    {
	for( UINT i= 0 ; i< trianglecount ; i++, triangles++ ){
	    iSink->BeginFigure( triangles->point1, D2D1_FIGURE_BEGIN_FILLED );
	    iSink->AddLine( triangles->point2 );
	    iSink->AddLine( triangles->point3 );
	    iSink->EndFigure( D2D1_FIGURE_END_CLOSED );
	}
    }

    STDMETHOD_(ULONG, AddRef )( THIS )
    {
	return	0;
    }
    STDMETHOD_(ULONG, Release )( THIS )
    {
	return	0;
    }
    STDMETHOD( QueryInterface )( THIS_ REFIID riid, void** obj )
    {
	return	E_UNEXPECTED;
    }
    STDMETHOD( Close )()
    {
	return	S_OK;
    }
    static void	Create( ID2D1Factory* factory )
    {
	factory->CreatePathGeometry( &iPath );
	iPath->Open( &iSink );
    }
    static void	ClosePath()
    {
	if( iSink ){
	    iSink->Close();
	    iSink= NULL;
	}
    }
};

ID2D1PathGeometry*	DummySink::iPath= NULL;
ID2D1GeometrySink*	DummySink::iSink= NULL;

そのまま DrawGeometry() で path を描画すると、とがった角にトゲが生えたような
描画になることがあります。折れ線のつなぎ処理が入っているようです。

direct2d

描画用に ID2D1StrokeStyle を作成しておくとトゲが無くなります。


関連エントリ
Direct2D (6) Tessellate
Direct2D (5) ID2D1Geometry を使う
Direct2D (4) Direct2D の描画
Direct2D (3) 互換性の検証と Vista で Direct2D
Direct2D その(2) インターフェース
Direct2D と Direct3D10.1 の下位互換



FillMesh() はまだ成功していません。
Tessellate() で座標を取ってみました。

iFactory->CreateRectangleGeometry(
		D2D1::RectF( 0.0f, 0.0f, 10.0f, 10.0f ), &iRect );
DummySink	tsink;
iRect->Tessellate( NULL, &tsink );

まずはただの矩形から。
少々勘違いしていたのは、AddTriangles() はまとめて一度だけ呼ばれるのかと思ったら
1 Triangle 毎に何度も呼び出されること。
結果は次の通り。

t0= 10.000000 0.000000, 10.000000 10.000000,  0.000000 10.000000
t1=  0.000000 0.000000,  0.000000 10.000000, 10.000000  0.000000

頂点の並びが少々おかしい気がします。
それぞれ回転方向が逆になっているので、Strip を展開したような形でしょうか。

ID2D1RectangleGeometry ではなく Path で同様の形を作ってみます。

ID2D1GeometrySink*	isink;
iFactory->CreatePathGeometry( &iPath );
iPath->Open( &isink );
isink->BeginFigure( D2D1::Point2F( 0.0f, 0.0f ), D2D1_FIGURE_BEGIN_FILLED );
	isink->AddLine( D2D1::Point2F( 20.0f,  0.0f ) );
	isink->AddLine( D2D1::Point2F( 20.0f, 20.0f ) );
	isink->AddLine( D2D1::Point2F(  0.0f, 20.0f ) );
	isink->EndFigure( D2D1_FIGURE_END_CLOSED );
	isink->Close();
iPath->Tessellate( NULL, &tsink );

全く同じです。

t0= 20.000000 0.000000, 20.000000 20.000000,  0.000000 20.000000
t1=  0.000000 0.000000,  0.000000 20.000000, 20.000000  0.000000

パスの方向を逆にしても同じでした。

isink->BeginFigure( D2D1::Point2F( 0.0f, 0.0f ), D2D1_FIGURE_BEGIN_FILLED );
isink->AddLine( D2D1::Point2F(  0.0f, 20.0f ) );
isink->AddLine( D2D1::Point2F( 20.0f, 20.0f ) );
isink->AddLine( D2D1::Point2F( 20.0f,  0.0f ) );
isink->EndFigure( D2D1_FIGURE_END_CLOSED );

t0= 20.000000 0.000000, 20.000000 20.000000,  0.000000 20.000000
t1=  0.000000 0.000000,  0.000000 20.000000, 20.000000  0.000000

半径 10 の円の場合。

iFactory->CreateEllipseGeometry(
	D2D1::Ellipse( D2D1::Point2F( 0,0f, 0.0f ), 10.0f, 10.0f ), &iElli );
iElli->Tessellate( NULL, &tsink );

22個の Triangle に分割されています。ちなみに半径 30だと 30個。

t0= -2.015625 9.796875, 0.000000 10.000000, 2.015625 9.796875
t1= 3.890625 9.218750, 2.015625 9.796875, -2.015625 9.796875
t2= -3.890625 9.218750, -2.015625 9.796875, 3.890625 9.218750
t3= 7.062500 7.062500, 3.890625 9.218750, -3.890625 9.218750
t4= -7.062500 7.062500, -3.890625 9.218750, 7.062500 7.062500
t5= 9.218750 3.890625, 7.062500 7.062500, -7.062500 7.062500
t6= -9.218750 3.890625, -7.062500 7.062500, 9.218750 3.890625
t7= 9.796875 2.015625, 9.218750 3.890625, -9.218750 3.890625
t8= -9.796875 2.015625, -9.218750 3.890625, 9.796875 2.015625
t9= 10.000000 0.000000, 9.796875 2.015625, -9.796875 2.015625
t10= -10.000000 0.000000, -9.796875 2.015625, 10.000000 0.000000
t11= 9.796875 -2.015625, 10.000000 0.000000, -10.000000 0.000000
t12= -9.796875 -2.015625, -10.000000 0.000000, 9.796875 -2.015625
t13= 9.218750 -3.890625, 9.796875 -2.015625, -9.796875 -2.015625
t14= -9.218750 -3.890625, -9.796875 -2.015625, 9.218750 -3.890625
t15= 7.062500 -7.062500, 9.218750 -3.890625, -9.218750 -3.890625
t16= -7.062500 -7.062500, -9.218750 -3.890625, 7.062500 -7.062500
t17= 3.890625 -9.218750, 7.062500 -7.062500, -7.062500 -7.062500
t18= -3.890625 -9.218750, -7.062500 -7.062500, 3.890625 -9.218750
t19= 2.015625 -9.796875, 3.890625 -9.218750, -3.890625 -9.218750
t20= -2.015625 -9.796875, -3.890625 -9.218750, 2.015625 -9.796875
t21= 0.000000 -10.000000, -2.015625 -9.796875, 2.015625 -9.796875

円の中心に頂点がありません。水平方向にスライスされているので、やはり Strip に
展開されているのだと思われます。

分割数は Tessellate() 2番目のパラメータで指定出来ます。
  Tessellate( NULL, ここ, &tsink );
省略時は D2D1_DEFAULT_FLATTENING_TOLERANCE が渡されており、これは 0.25f です。

値を大きくすると、5.0 くらいで菱形の 4角形になります。
0.0f を与えると 3862個に分割されました。

# 5.0
t0= -10.000000 0.000000, 0.000000 10.000000, 10.000000 0.000000
t1=  0.000000 -10.000000, -10.000000 0.000000, 10.000000 0.000000

RenderTarget 作成時 D2D1_RENDER_TARGET_PROPERTIES dpiX/dpiY に 0 を与えると
デフォルトの 96dpi になります。
96dpi の時、長さは pixel 相当となり、dpi 値を増やすとピクセル密度が高くなります。
相対的に同じサイズの RenderTarget に描かれる図形は大きくなります。

dpi を考慮した実際に描画される大きさと、Tessellate() 2番目の数値によって
生成される Triangle の数が決められています。


関連エントリ
Direct2D (5) ID2D1Geometry を使う
Direct2D (4) Direct2D の描画
Direct2D (3) 互換性の検証と Vista で Direct2D
Direct2D その(2) インターフェース
Direct2D と Direct3D10.1 の下位互換



ADTEC の小型プロジェクター AD-MP15A を手に入れました。

ADTEC AD-MP15Aシリーズ Bit LED Projector

わずか 15ルーメンで解像度は VGA 640x480。
ビデオ入力だけでなく VGA 端子もあるので PC 画面も映すことが出来ます。
VGA 入力信号は 640x480 または 800x600 に対応。

周りが暗く、かつ近い距離だと思ったよりもきれいに出ます。
ピントさえ合えば画像はくっきり。
30cm の距離で実測 8.2inch くらい。
夜明かり無しの暗闇だったら 1.6m で 39inch 程度。一応みれました。

AD-MP15A

↑PS3 の画面をビデオ入力で。背景がゆがんでるのはスクリーンがただの封筒だから。

周囲の明るさにかなり依存します。
黒い領域=スクリーンの色 となるわけですが、普段スクリーンが白に見える部屋で
あればそれはそのまま白です。
投影映像にはそれを打ち消して、スクリーンの色が暗く見えるだけの明るさが
必要となります。そのためには

・周囲を暗くする
・プロジェクタをスクリーンに近づける

のどちらかです。
また映像ソースにも依存するので、明るいシーンだと大丈夫でも背景が真っ黒な
暗いシーンになると見にくくなります。

・夜や暗い部屋で使用する
・バッテリー駆動で持ち歩けて VGA 入力のある超小型モニタとして
・スクリーンに非常に近づけて小さい画面を一人で見る
・比較的明るい画面が中心

といったケースが合っているかもしれません。

フォーカス調整可能な範囲が決まっています。
試したところ、だいたい 16cm~160cm くらいでした。

16cm = 4.5inch
30cm = 8.3inch
58cm = 15inch
95cm = 22inch
160cm = 39inch

小さいけれどそれなりに熱を持つのかファンが入っているようです。
音はわずかに聞こえる程度でほとんど気になりません。

本体サイズは非常に小さくバッテリーも内蔵して 147g。
バッテリー駆動時間は 40分。

AD-MP15A
↑側面にコネクタ類あり

右の赤いのはたまたま近くにあったサイズ比較用(?)のビジュアルメモリ(Dreamcast)で、
付属品ではないです。

コネクタやスイッチは全部側面にあります。裏はバッテリーカバー。
映像入力コネクタは独自なので専用のケーブルも必要となります。
ケーブルはどれも非常に細くできており取り回ししやすくなっています。

AD-MP15A
↑付属のケーブルと専用コネクタ

パッケージには三脚も同梱されています。
全体的に軽いので、ケーブルに引っ張られるとすぐに動いてしまうのは仕方ないところ。

スピーカーも内蔵されておりヘッドホン端子もありますがボリューム調整は出来ません。

個人的には気に入りました。
小さいし使いどころが限られてくるけど、今までとは違った活用方法が考えられます。
プロジェクターといえばこう使う、といった先入観さえ無ければ非常におもしろいと思います。
今後は同様の小型プロジェクタが登場して結構早いペースで進化するのではないでしょうか。



すでにモニターテストの受付が始まっていますが、エリア外の人も直接電話で
申し込めば端末を購入することができます。
ユーザーサポートセンターに問い合わせるも混雑してるようでなかなかつながらず、
何度か待たされつつ申込書の送付を頼むことが出来ました。

UQ WiMAX

気になっていたのはこの部分。モニター募集のところに

>発送: 2009年2月26日(木)~2009年3月中旬にかけてデータ通信カードを順次
>発送いたします。

発送が 3月中旬までかかると書いてあります。

念のためきいてみると、購入者には優先して発送するので大丈夫だそうです。
ただしサービス開始の 2/26 必着にはならず、26 日以降に発送するため数日は
余裕を見てくださいとのことでした。



GPU の活用によりラスタライズはたいしたウエイトを占めておらず、CPU の仕事は
与えるデータの構築だけとなりました。それゆえ Direct2D はただの描画命令の
集まりにはなっていません。
ベクター描画の途中に介入し、ベクトルの情報のままデータを扱うことが可能です。
Direct2D が単なる GDI の置き換えではなく、可能性を秘めているのはこの Geometry
ではないかと思っています。

実際に ID2D1Geometry がどのようなものか興味あるので、機能を 1つ 1つ追いかけて
みました。

● Path の作成

Path の作成は下記の通り。

ID2D1Factory*	iFactory;
ID2D1PathGeometry*	iPath;
iFactory->CreatePathGeometry( &iPath );

ID2D1GeometrySink*	isink;
iPath->Open( &isink );
isink->SetFillMode( D2D1_FILL_MODE_WINDING );
isink->BeginFigure( startpos, D2D1_FIGURE_BEGIN_FILLED ); // = SVG M
isink->AddLine( pos1 );	    // = SVG L
isink->AddBezier( D2D1::BezierSegment( c2, c3, c4 ) );	 // = SVG C
 ...
isink->EndFigure( D2D1_FIGURE_END_CLOSED );   // = SVG Z
isink->Close();

ID2D1GeometrySink::Close() (ID2D1SimplifiedGeometrySink::Close()) は
SVG 等の Z コマンド相当かと思っていたら EndFigure( D2D1_FIGURE_END_CLOSED )
の方でした。

D2D1_FIGURE_END_OPEN     そのまま
D2D1_FIGURE_END_CLOSED   BeginFigure() の位置に直線で接続(閉じたパスにする)

ID2D1GeometrySink::SetFillMode() では塗りつぶしのルールを決定します。
SVG 等の vector graphics にある evenodd / nonzero に相当します。

・D2D1_FILL_MODE_ALTERNATE
 "evenodd" 相当。外からのレイが交差した回数で決定します。
 交差回数が偶数なら外、奇数は中。パスの回転方向は影響しません。

・D2D1_FILL_MODE_WINDING
 "nonzero" 相当。外からのレイが交差するとき、交わった線の方向を考慮します。
 たとえば閉じているパスが仮に右回りなら +1、左回りなら -1、結果が 0 なら外。
 つまりパスの回転方向を見ており、逆回転はくり抜き。


● Sink

Sink は値の追加、データをためることだけが出来るバッファです。
Add 系命令のみで Get はありません。
ID2D1Geometry では各オペレーションに対して受け皿である Sink を渡します。

最初にただの box を作ってみます。

ID2D1RectangleGeometry*	iRect;
iFactory->CreateRectangleGeometry(
	D2D1::RectF( 0.0f, 0.0f, 60.0f, 50.0f ), &iRect );

この box を元に、線を太らせる処理を行います。
もともとは描画時に太い幅 (strokeWidth) の線で描画するための変換ですが、
描画前に Path として受け取ることが出来るわけです。

// 変換結果を格納する ID2D1PathGeometry を作る
ID2D1PathGeometry*	iPath2;
iFactory->CreatePathGeometry( &iPath2 );

// ID2D1PathGeometry の sink に変換結果を受け取る
ID2D1GeometrySink*	isink;
iPath2->Open( &isink );
// iRect の変換結果を isink へ
iRect->Widen( 10.0f, NULL, NULL, isink ); // strokeWidth= 10.0f
isink->Close();

実際に描画したのがこちら。

Direct2D sample

↑左から順に
 ・元の Box
 ・元の Box を幅 10 の線で描画したもの
 ・幅 10 の描画を Path で受け取ったもの
 ・幅 10 で Path 化したものをさらに 幅 4 の線で描画したもの

// 描画コード
iRenderTarget->SetTransform( D2D1::Matrix3x2F::Translation( 20.5f, 20.5f ) );
iRenderTarget->DrawGeometry( iRect, iBrush0 );

iRenderTarget->SetTransform( D2D1::Matrix3x2F::Translation( 120.5f, 20.5f ) );
iRenderTarget->DrawGeometry( iRect, iBrush0, 10.0f );

iRenderTarget->SetTransform( D2D1::Matrix3x2F::Translation( 220.5f, 20.5f ) );
iRenderTarget->DrawGeometry( iPath2, iBrush0 );

iRenderTarget->SetTransform( D2D1::Matrix3x2F::Translation( 320.5f, 20.5f ) );
iRenderTarget->DrawGeometry( iPath2, iBrush0, 4.0f );

RenderTarget には直接描画可能な DrawRectangle() 命令があります。
Direct2D では内部的には全く同じように

(1) CreateRectangleGeometry() 等でプリミティブを作成
(2) Widen() など与えたオプションによって必要な変換を行う
(3) Tessellate() で ID2D1Mesh 化
(4) 最終的にすべて ID2D1RenderTarget::FillMesh()

という手順で描画しているだけかもしれません。この場合一番低レベルな描画は
FillMesh() になります。
ただし現在 FillMesh() での直接描画が成功しておらず、これらは予想の範囲を
超えておりません。

もし予想が合っているのなら、Geometry を扱うことでより柔軟なデータ管理ができます。
たとえば比較的 Mesh に近い状態のままキャッシュすることで、高速化や再利用が
可能となるでしょう。毎回 stroke を指定して Draw するよりも Geometry か Mesh
化しておいた方が高速だと思います。


●変換形状の読み出し

Geometry の中身は隠蔽されており、パスの座標値などデータを直接さわることが
できなくなっています。
ところが SDK には、テセレートされたアウトラインフォントの形状を取り出して立体化し、
3D ポリゴンとして描画するサンプルプログラム Interactive3dTextSample があります。

その仕組みはこうです。

(1) Sink 互換のインターフェースを自分で定義しておく
(2) Geometry の Tessellate() 等に渡して変換結果を受け取る

たとえば ID2D1Geometry::Tessellate() であれば、変換後の結果を格納するために
内部で ID2D1TessellationSink::AddTriangles() を呼び出しています。
互換インターフェースで AddTriangles() を定義しておけばトラップ可能となるわけです。

このように、Tessellate() など Geometry で変換した座標値をアプリケーションで
使うことができます。


関連エントリ
Direct2D (4) Direct2D の描画
Direct2D (3) 互換性の検証と Vista で Direct2D
Direct2D その(2) インターフェース
Direct2D と Direct3D10.1 の下位互換



「 Hyperでんち 」 の古くなっていたいくつかの項目を更新しました。

DirectX 一覧
GPU 年表

DirectX 一覧には Direct3D11 を追加 (リリース前なので未完)
GPU 年表は Mobile の項目を分離。

表を見てわかるとおり Mobile はちょうど今が固定機能パイプラインで DirectX7 世代。
その次は Unitifed Shader が当たり前で、一気に Direct3D10/ShaderModel4 世代と
なる可能性があります。

PowerVR SGX はすでに Unified Shader ベースであり、Intel US15W の GMA500 や
OMAP3 をはじめいくつかのデバイスで使われています。
Qualcomm の MSM に搭載されていた ATI Imageon も、新たに Xbox 360 同様の
Unified Shader Architecture を元に開発が行われていたようです。

AMD Licenses 3D Graphics Core Technology to QUALCOMM, Delivering The Ultimate Visual Experience? to Tomorrow’s Phones

GeometryShader が存在するかどうかわかりませんが Shader ベースであることは確かです。
PowerVR SGX 同様、Vertex/ Pixel の演算ユニットを共有できること、
Graphics 以外への応用が容易なこと、設計時に演算能力をスケーラブルに変更できる
ことがメリットであると考えられます。

Wikipedia Imageon

上記 wikipedia のページには Z460 という新しい Imageon core の名前が載っています。
直接 AMD の pdf 資料へのリンクもあります。登場は 2009 年以降とのこと。

これらのデータより、HTC Diamond 等に使われている現行 MSM7201A の 3D core は
ATI Imageon 2380 シリーズをベースにしたものだと考えられます。

2006/1/30 ImageonR 2380 および ImageonR 2388 で驚くべきゲームおよびマルチメディア体験を可能にし、ハンドヘルド 3D グラフィックスの水準を引き上げる ATI

AMD Imageon 2388/2380

公式ページの資料が参考になります。
Direct3DMobile では使えないものの CubeMap など一通り機能がそろっているようです。
D3D8 相当のスキニング用固定 Matrix Palette もありますが、残念ながら D3DM 側に
対応する機能がありません。
頂点演算は float で行われており、やはり NATIVEFLAOT で正解でした。
違いは VRAM のみで、2388 が内蔵 8MB、2380 が外付けで 16MB 以上に対応とのこと。


関連エントリ
Intel GMA500 のスペックについて考える。続き (2)
Intel GMA500 の機能と性能と Aero
Qualcomm と 3D
HTC Touch Diamond で Direct3DMobile その(11) 問題まとめその他



せっかくなのでもう少し送受信 API 周りを解説してみます。
前回 列挙して各デバイスのパスがわかったので、あとは Open して実際の通信を行います。
問題は Bluetooth stack によって使用可能な API が異なっていることです。

// デバイスの Open
HANDLE	hdev= CreateFile(
				path,
				GENERIC_READ|GENERIC_WRITE,
				FILE_SHARE_READ|FILE_SHARE_WRITE,
				NULL,
				OPEN_EXISTING,
				FILE_FLAG_OVERLAPPED,
				NULL
			);

if( !HidD_GetPreparsedData( hdev, &Ppd ) ){
	Close();
	return	FALSE;
}

HIDP_CAPS	caps;
if( !HidP_GetCaps( Ppd, &caps ) ){
	Close();
	return	FALSE;
}

// 必要なバッファサイズを調べておく。これを元にキューを作る。
InputByteLength= caps.InputReportByteLength;
OutputByteLength= caps.OutputReportByteLength;


● API テスト

コマンド送信に使う API を選択します。

WindowsVista 標準の bluetooth Stack では HidD_SetOutputReport() を使います。
ところが Toshiba bluetooth Stack / Bluesoleil ではこの命令が使えませんでした。

このあたり、各 stack による違いは下記のエントリでまとめています。
Toshiba bluetooth stack と Bluesoleil 対応

// テストコマンドを送信、直後の ReadFile() で Timeout 判定
if( !HidD_SetOutputReport(
		hDev,
		command,
		cmdlength
			) ){
	return	FALSE;
}

まず最初に HidD_SetOutputReport() を使って何らかのリターンがあるコマンドを
送信してみます。

この API が直接エラーを返す場合は WriteFile() の選択で決定です。ただしエラー
を返してくれるのは Toshiba Stack の場合だけ。Bluesoleil v5 ではエラー無しで
送信も出来ません。

判定の手順

(1) HidD_SetOutputReport() でリターンのあるコマンドを送信してみる。

(2) HidD_SetOutputReport() がエラーを返す場合は WriteFile() を選択で決定。
  Toshiba Bluetooth stack の場合。

(3) エラーが返らない場合は直後に ReadFile() でデバイスの返答を待ちます。

(4) 結果が返ってきたら HidD_SetOutputReport() を選択。Windows stack。

(5) Timeout 判定。一定時間待っても結果が来なければ Bluesoleil。
  送信は WriteFile() を選択する。


●データ送信

HidD_SetOutputReport() には送信するデータを直接渡すことが可能です。
たとえば 4byte なら 4byte のみで構いません。
WriteFile() を使う場合は、必ず OutputReportByteLength 分のバッファを作って
から転送を行います。

char*	bufp= Queue.WriteAlloc();
assert( length <= OutputByteLength );

if( !bufp ){
	// error
	return;
}
memcpy( bufp, command, length );

memset( &WriteOverlap, 0, sizeof(OVERLAPPED) );
WriteOverlap.hEvent= handle;
if( !WriteFileEx(
			hDev,
			bufp,
			OutputByteLength,
			&WriteOverlap,
			_WriteFileCB ) ){
	int	err= GetLastError();
	// error
}

このあたりも、成功するまで何度も試して調べた部分です。
Windows7 など Bluetooth のサポートも強化されるので、おそらく今後は
Windows stack の方が標準になっていくものと考えられます。
そのうちきっと HidD_SetOutputReport() だけ済む時代が来るでしょう。


関連エントリ
複数の HID から情報を読み出すには
Toshiba bluetooth stack と Bluesoleil 対応



以前公開したプログラムに関する質問のメールがほぼ同時に 2通ありました。
どちらも同じ内容でしたので、少々昔のプログラムを引っ張り出してみます。
「接続した複数の HID から情報を読み出すにはどうしたらいいのか」
制作時に参考にしたのはサンプル「\WinDDK\6000\src\hid\hclient\pnp.c」です。

HMODULE hModule= LoadLibrary( "hid.dll" );
HMODULE sModule= LoadLibrary( "setupapi.dll" );
GetProcAddress( hModule, "HidD_GetAttributes" ) )
GetProcAddress( hModule, "HidP_GetCaps" ) )
GetProcAddress( hModule, "HidD_FreePreparsedData" ) )
GetProcAddress( hModule, "HidD_GetPreparsedData" ) )
GetProcAddress( hModule, "HidD_SetOutputReport" ) )
GetProcAddress( hModule, "HidD_GetHidGuid" ) )
GetProcAddress( sModule, "SetupDiGetClassDevsA" ) )
GetProcAddress( sModule, "SetupDiEnumDeviceInterfaces" ) )
GetProcAddress( sModule, "SetupDiGetDeviceInterfaceDetailA" ) )
GetProcAddress( sModule, "SetupDiDestroyDeviceInfoList" ) )

以後列挙して目的のデバイスを探しています。

GUID	hidGuid;
HidD_GetHidGuid( &hidGuid );

HDEVINFO	hdevinfo;
hdevinfo= SetupDiGetClassDevs(
		&hidGuid,
		NULL,
		NULL,
		DIGCF_PRESENT
		|DIGCF_DEVICEINTERFACE
		|DIGCF_ALLCLASSES
	);


SP_DEVICE_INTERFACE_DETAIL_DATA*	funcClassData= NULL;

int	retCode= TRUE;
for( int i= 0 ; i< DEVICELIST_LIMIT ; i++ ){
	SP_DEVICE_INTERFACE_DATA	deviceInfoData;
	memset( &deviceInfoData, 0, sizeof(SP_DEVICE_INTERFACE_DATA) );
	deviceInfoData.cbSize= sizeof( SP_DEVICE_INTERFACE_DATA );
	if( SetupDiEnumDeviceInterfaces(
			hdevinfo,
			0,
			&hidGuid,
			i,
			&deviceInfoData
			) ){
		ULONG	reqLength= 0;
		SetupDiGetDeviceInterfaceDetail(
				hdevinfo,
				&deviceInfoData,
				NULL,
				0,
				&reqLength,
				NULL );

		funcClassData=
			reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA*>(
					dAlloc( reqLength ) );
		if( sizeof(void*) == 4 ){
			funcClassData->cbSize= 5;
		}else{
			funcClassData->cbSize= 
				sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
		}
		if( !funcClassData ){
			retCode= FALSE;
			break;
		}
		if( !SetupDiGetDeviceInterfaceDetail(
				hdevinfo,
				&deviceInfoData,
				funcClassData,
				reqLength,
				&reqLength,
				NULL ) ){
			retCode= FALSE;
			break;
		}
		if( _OpenTestHidDevice( funcClassData->DevicePath,
					VENDOR_ID, PRODUCT_ID ) ){
			// 目的のデバイスを保存しておく
			CheckDeviceOpen<DeviceType>( funcClassData->DevicePath );
		}
		if( funcClassData ){
			dFree( funcClassData );
			funcClassData= NULL;
		}
	}else{
		if( GetLastError() == ERROR_NO_MORE_ITEMS ){
			break;
		}
	}
}

SetupDiDestroyDeviceInfoList( hdevinfo );
if( funcClassData ){
	dFree( funcClassData );
	funcClassData= NULL;
}

VENDOR_ID, PRODUCT_ID はデバイスマネージャーのプロパティから
詳細→ハードウエア ID で調べることが出来ます。
このあたりのコードは力業です。たしか作成時 1つ 1つ試しながらだったので、
あまりきれいじゃないと思いつつ書いていた覚えがあります。

int _OpenTestHidDevice( const char* path, int vid, int pid )
{
	HANDLE	hdev= CreateFile(
					path,
					GENERIC_READ|GENERIC_WRITE,
					FILE_SHARE_READ|FILE_SHARE_WRITE,
					NULL,
					OPEN_EXISTING,
					0,
					NULL
				);
	int	found= FALSE;
	if( hdev == INVALID_HANDLE_VALUE ){
		return	FALSE;
	}

	HIDD_ATTRIBUTES	attr;
	if( HidD_GetAttributes( hdev, &attr ) ){
		if( attr.VendorID == vid && attr.ProductID == pid ){
			found= TRUE;
		}
	}
	CloseHandle( hdev );
	return	found;
}

検索して条件に合うデバイスをリストに保持し、それぞれ個別に通信を行っています。
CheckDeviceOpen() は内部でリストに追加しているだけです。
見つかった最初のデバイスだけ見ているか、それとも全部列挙しているかの違い
だけだと思います。

実際のデータ送受信はこのあとですが、Bluetooth Stack の違いを吸収するために
最初に API のテストや通信タイムアウトの判定などを行っています。
ここでの注意点は、複数のデバイスがあっても API の判定は最初の一度だけで
いいということ。

最初にリリースしたバージョンでは新たなデバイスが接続されるたびに API テストが
走ってしまい、すでにつながってるデバイスの通信も一時中断されてしまう問題が
ありました。このあたりの処理をきれいに組み込めずに少々苦労した覚えがあります。

また定期的に接続されているデバイスを確認して、動的な接続&切断に備えています。
すでにオープンしたデバイスかどうかの区別が必要となりますが、結局保持している
パスの文字列比較というもっとも安易な方法で済ませました。



●描画

Direct2D の描画は下記の 4通りあります。

・線描画
・塗りつぶし
・イメージ描画
・文字

ID2D1RenderTarget に対して適用できるものです。
それぞれグループ分けと、グループごとに指定出来る共通プロパティをまとめます。

◎線描画
・DrawEllipse (楕円)
・DrawGeometry (パス)
・DrawLine (直線)
・DrawRectangle (四角)
・DrawRoundedRectangle (角が丸い四角形)

    ID2D1Brush
        色や模様

    Stroke
        線の太さ (strokeWidth)
        ID2D1StrokeStyle 線のスタイル
	 (端の形状、丸めたりとんがったり。線の形状、点線やダッシュ点線など)


◎塗りつぶし
・FillEllipse
・FillGeometry
・FillMesh (ID2D1Mesh の描画)
・FillOpacityMask (bitmap でマスク付き塗りつぶし)
・FillRectangle
・FillRoundedRectangle

    ID2D1Brush
        色や模様


◎イメージ描画
・DrawBitmap
・(FillOpacityMask)


◎文字
・DrawGlyphRun (IDWriteFontFace)
・DrawText (IDTextFormatIDWriteTextFormat) 2009/02/06 修正
・DrawTextLayout (IDWriteLeyoutIDWriteTextLayout) 2009/02/06 修正

    ID2D1Brush
        色や模様


●形状

形状の操作は Geometry で行います。
その場合も描画同様のプリミティブが使用できます。
多くの操作では同時に 3x2 Matrix (D2D1_MATRIX_3X2_F) を与えることが可能で、
形や座標判定用の命令も用意されています。

・ID2D1EllipseGeometry
・ID2D1PathGeometry
・ID2D1RectangleGeometry
・ID2D1RoundedRectangleGeometry
・ID2D1TransformedGeometry

CombineWithGeometry
 複数の Geometry 同士の合成や演算を行います。

Tessellate
 テセレートし、トライアングル化した状態にアクセスできるようにします。

Widen
 線として描画する場合に、線を太らせた形状を取り出します。


●Mesh

ID2D1Mesh は直接描画データとしてポリゴン (Triangle) を与える場合に使用します。
非常にデバイスに近い低レベルな状態でデータをもてるものと思われます。
おそらく Geometry → Tessellate も Mesh 相当への変換です。


●DirectWrite

DirectWrite はフォント扱うモジュールとして完全に独立しているようです。
描画対象として Direct2D に拘る必要はなく Direct3D など何でも良いとのこと。
Direct3D11 との組み合わせもおそらく出来るでしょう。

当初は Direct2D のデータに変換して出力しているのかと思いましたが、独自の
レンダラを持っているようです。よく考えると ClearType などフォントに依存する
部分が大きいので当然かも。

フォントを扱うためにはデータとして形状を読み込むだけでなく、同時にレイアウト
も必要となります。このあたりのインターフェースは次の通り。

IDWriteFormat → IDWriteLeyout
IDWriteTextFormat → IDWriteTextLayout

Direct2D のテキスト描画も上記 2つの Interface に対応しています。
また文字単位の形状を直接描画することも可能で、それが GlyphRun 系の命令と
なっているようです。


今回はあまりきちんと試しておらず、メモ程度の内容となっています。
間違いなどあったら訂正していきます。


関連エントリ
Direct2D (3) 互換性の検証と Vista で Direct2D
Direct2D その(2) インターフェース
Direct2D と Direct3D10.1 の下位互換



こちらで書いたように、Windows7 では Direct3D11 と同じく
Direct3D10.1 でも下位の GPU が新たにサポートされることになりました。

実際にデバイスを作成して試してみました。
今度は Direct2D ではなく、直接 Direct3D10.1 ( D3D10CreateDevice1() ) を
使用しています。

Direct3D10.1
  D3D10_FEATURE_LEVEL_10_0
  D3D10_FEATURE_LEVEL_10_1
  D3D10_FEATURE_LEVEL_9_1
  D3D10_FEATURE_LEVEL_9_2
  D3D10_FEATURE_LEVEL_9_3

すべて成功しました。
これは、今までの Windows Vista + Direct3D10.0 / Direct3D10.1 では
出来なかったことなのです。

まず Windows SDK for Windows 7 BETA を install しなければ、
D3D10_FEATURE_LEVEL_9_1 ~ D3D10_FEATURE_LEVEL_9_3 のシンボルが
定義されていないということ。
Vista 以前で使える現在の DirectX SDK ではコンパイル出来ませんでした。
DirectX SDK Nov2008 の help では、ヘッダファイルにシンボルが無いのに次の
ように書かれていたりします。

D3D10_FEATURE_LEVEL_9_1 
 This value is in the header, but this feature is not yet implemented. 
D3D10_FEATURE_LEVEL_9_2 
 This value is in the header, but this feature is not yet implemented. 
D3D10_FEATURE_LEVEL_9_3 
 This value is in the header, but this feature is not yet implemented. 

この Windows7 用の FEATURE テストプログラムを WindowsVista 上で走らせて
みたところ、全く同じようにデバイスの生成が成功してしまいました。

考えられるのは Windows Vista でも Direct3D SDK の Direct3D11 beta ランタイム
を導入した時点で使えるようになっていたのではないかということ。
Direct3D11 では実際に D3D9_1~3 の下位の FeatureLevel に対応しています。
Direct3D10.1 で同じように利用できてもおかしくないのかもしれません。


過去の DirectX はすべて互換性を保ちながら移行するのが当たり前でした。

 実際に Direct3D 9 は Direct3D 8 のハードウエアでも動作します。

デバイスは作れますし API も呼べますしシェーダーモデル 1 を扱うことが出来ます。
Direct3D9 の特徴であるシェーダーモデル 2~3 は利用できませんが、それは
Direct3D10.1 で Level9 を扱っても全く同じこと。

さらに前、たとえば DirectX7 までは毎年メジャーナンバーがアップデートされる
ことになっていました。わずか 1年で GPU が使えなくなっていたら、とても
ついて行くのが大変ですよね。

DirectX/GPU 年表
DirectX list

互換性を完全に切り捨てたのは Direct3D9 → Direct3D10 のタイミングだけなのです。
ドライバモデルが変わったり OS と融合されたりと、内部的に困難な理由が
いろいろあったのではないかと考えられます。

だから今回 Windows7 と同じタイミングで、Windows7 だけでなく Vista でも
同等の機能がサポートされるのは画期的なことかもしれません。

でも古くから DirectX を触ってるものからすれば画期的でも何でもなくて、
互換性の切り捨てを撤回して、やっぱり以前と同じように使えるようにしました、
といってるだけに見えるのです。


● Windows7 SDK の導入

Windows7 SDK があれば、Vista でも Direct3D10.1 で Direct3D9 GPU 向けの
プログラムを作成できることがわかりました。
DirectX SDK の更新と思って Windows SDK for Windows 7 BETA を早めに導入して
おくのもありかもしれません。
あらかじめ DirectX SDK November 2008 をインストールしておく必要があります。

VisualStudio2008 を起動する前に、スタートメニューから
 Visual Studio Registration → Windows SDK Configuration Tool
を起動して SDK を切り替えておきます。

include パスの順番には注意が必要です。
 Tools → Options → Projects and Solutions → VC++ Directories
 Win32 / Include files

$(DXSDK_DIR)\include よりも上に $(WindowsSdkDir)\include が来るようにします。
DirectX SDK の install で直接 C:\Program Files ~とパスが書き込まれている
ことがあるので、その行は削除して $(DXSDK_DIR)include に置き換えておくことを
お勧めします。
lib も同じようにします。


● Vista で Direct2D が動く

Windows7 SDK beta があれば Direct2D のプログラムを作ることが出来ます。
実際に走らせるには Windows7 beta をインストールした PC が必要です。

Direct2D は結局 Direct3D10.1 を呼び出しているだけのはず。
Direct3D10.1 で下位 GPU もなぜか動くことがわかったので、もしやと思って
Windows7 beta から d2d1.dll だけ vista に持ってきたら拍子抜けするほどあっさり
動作しました。

とりあえず build したプログラムと同じ場所に d2d1.dll と DWrite.dll だけ置いておけば
Vsita でも Direct2D のプログラムを作って走らせることが出来るようです。


関連エントリ
Direct2D その(2) インターフェース
Direct2D と Direct3D10.1 の下位互換



ヘッダを読みながら整理してみました。


● RenderTarget

基本となるのが ID2D1RenderTarget です。いわゆるキャンバスのこと。
各種描画命令を使って RenderTarget の上に絵を描いていきます。
次のバリエーションがあります。

ID2D1RenderTarget
 → ID2D1BitmapRenderTarget
 → ID2D1HwndRenderTarget
 → ID2D1GdiInteropRenderTarget
 → ID2D1DCRenderTarget

それぞれ Bitmap (Texture) に描いたり、そのままウィンドウに描いたりできるということ。
ラインなどプリミティブを描画する命令もありますが、パス(Geometry) を使ったり
DirectWrite を併用してテキストも描くことができます。
描画時に SetTransform() で渡した 3x2 matrix が適用されるようです。
2D の回転、拡大縮小、平行移動などが可能。


● Geometry

Direct2D を特徴付ける存在といえるかもしれません。
ID2D1Geometry はパスや形状などジオメトリを格納します。
無理矢理例えれば Direct3D の VertexBuffer/IndexBuffer のような存在でしょうか。

パスを構成する要素、ラインやベジェ(2次、3次)、Arc を使用することができます。
Geometry の段階で Transform したり合成したり交差を考慮して演算したり、
といった操作が可能。結構自由度が高そうです。
ID2D1Geometry を継承しているインターフェースは次の通り。

ID2D1Geometry
 → ID2D1RectangleGeometry
 → ID2D1RoundedRectangleGeometry
 → ID2D1EllipseGeometry
 → ID2D1GeometryGroup
 → ID2D1TransformedGeometry
 → ID2D1PathGeometry


● Brush

ID2D1Brush は描画時に使用する状態を保持します。マテリアルやレンダーステートを
保持した StateBlock のようなイメージでしょうか。GDI にもあります。
下記の種類があります。

ID2D1Brush
 → ID2D1BitmapBrush
 → ID2D1SolidColorBrush
 → ID2D1LinearGradientBrush
 → ID2D1RadialGradientBrush


大きいのがこの 3つのグループです。
その関係は「 Geometry を Brush を使って RenderTarget に描画する 」ということ。


ID2D1RenderTarget, ID2D1Geometry, ID2D1Brush はどれも ID2D1Resource に
属しています。ID2D1Resource を継承しているインターフェースは他にもあります。

ID2D1Resource
 → ID2D1Bitmap
 → ID2D1GradientStopCollection
 → ID2D1Brush
 → ID2D1StrokeStyle
 → ID2D1Geometry
 → ID2D1Mesh
 → ID2D1Layer
 → ID2D1DrawingStateBlock
 → ID2D1RenderTarget

ID2D1Resource に属していないのは ~ Sink 系と ID2D1Factory のみ。

ID2D1SimplifiedGeometrySink
ID2D1GeometrySink
ID2D1TessellationSink

~Sink はまだよく理解していませんが、Geometry を操作する場合のイテレータの
ような存在ではないかと思われます。


関連エントリ
Direct2D と Direct3D10.1 の下位互換


● Direct2D とは

Direct3D に似ていますが別物です。
DirectDraw とも似ていますが役割が異なります。

現在 DirectX SDK ではなく Windows SDK for Windows 7 の方に含まれます。
下記のページより Windows7 SDK の beta 版を入手することが可能です。

Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1: BETA

Direct3D11 と違い WindowsVista では動かないので、動作確認には Windows7 beta が
必要となります。ドキュメントはこちらから。

MSDN Direct2D

Direct2D は Direct3D よりも上位のレイヤに位置します。
レンダーバックエンドとして Direct3D を活用しつつ、ベクターや bitmap 等の 2D
レンダリングを可能とします。Direct3D や DirectDraw のようなハードウエア寄りの
プリミティブではなく、GDI のような高度な描画命令を多数有しています。
SVG/FXG/XAML のようなベクターグラフィックスに対応し、ポリゴンを表示するかわり
に美しい 2D をレンダリングすることに注力した API セットであるといえるでしょう。

現在の Direct2D が利用するのは Direct3D10.1 です。
10.1 といえば WARP によって高速なソフトウエアラスタライズも可能だし、リモート
デスクトップ等で利用可能なCommand Remoting だって使えます。

Direct2D の狙いはまさにここにあります。
ハードウエアアクセラレーション、ソフトウエアラスタライザ、そしてクライアント
サイドのレンダリングなど、2D の描画でもこれらの恩恵を受けられるようになります。


● Direct2D の謎

最初に疑問に思うのは Direct3D10.1 だと使えるハードがかなり限られてくるのでは
ないかということ。
従来 Direct3D10 は専用のビデオカードしか使えず D3D9 世代の GPU とは互換性が
ありませんでした。逆に Direct3D11 の方が下位互換性が強化されており、D3D9 世代の
GPU でも動作します。そのあたりをまとめたのは こちら

どうやら D3D10 でも下位互換がサポートされることになった模様です。
たとえば一番新しい Direct3D SDK November 2008 のヘッダファイル D3D10_1.h を
見ても、下記の通り FEATURE LEVEL には 10 世代の GPU しか記載されていません。

typedef 
enum D3D10_FEATURE_LEVEL1
    {	D3D10_FEATURE_LEVEL_10_0	= 0xa000,
	D3D10_FEATURE_LEVEL_10_1	= 0xa100
    } 	D3D10_FEATURE_LEVEL1;

ところが Windows7 SDK beta 付属の D3D10_1.h を見てみると・・

typedef 
enum D3D10_FEATURE_LEVEL1
    {	D3D10_FEATURE_LEVEL_10_0	= 0xa000,
	D3D10_FEATURE_LEVEL_10_1	= 0xa100,
	D3D10_FEATURE_LEVEL_9_1	= 0x9100,
	D3D10_FEATURE_LEVEL_9_2	= 0x9200,
	D3D10_FEATURE_LEVEL_9_3	= 0x9300
    } 	D3D10_FEATURE_LEVEL1;

いつの間にか下位の FEATURE LEVEL が追加されています。
我慢を強いられた Direct3D10/Vista 世代はいったい何だったのでしょうか。

つまり Direct2D は Direct3D9 世代の古い GPU でもハードウエアアクセラレーション
がかかります。(実際に試してみました→後述)


さらに Direct2D は対応ハードウエアがなくてもソフトウエアラスタライザによって
動作可能と書かれています。これが WARP を指しているのか、それとも Direct2D が
さらに独自でラスタライザを有しているのかわかりません。

ハードウエアアクセラレーションといいつつも、おそらくジオメトリ部分は CPU の
割合がそれなりに高いのではないかと思われます。アンチエリアス等のピクセル合成
部分において GPU が活用されているのではないでしょうか。
Direct3D11 世代になればジオメトリ処理でも GPU の割合が高くなるかもしれません。

以前アウトラインフォントの GPU 描画に取り組んだのはジオメトリも GPU で処理させる
ことが目的でした。


もう一つ疑問に思ったのは Direct2D のインターフェースが最初から D2D1 と "1" が
ついていること。DXGI1 など他の API と同調するためなのか、"2" の登場
(Direct3D11 版?) を想定しているのかわかりません。
DirectWrite には "1" がついていませんでした。


●下位互換性の確認

3台とも Windows7 beta

・DesktopPC RADEON HD4850 (Direct3D10.1 ShaderModel4.1) (Aero 有効)
  ○ D2D1_RENDER_TARGET_USAGE_FORCE_HARDWARE_RENDERING
  ○ D2D1_RENDER_TARGET_USAGE_FORCE_SOFTWARE_RENDERING

・EeePC901 945GE GMA950 (Direct3D9 ShaderModel2.0) (Aero 有効)
  ○ D2D1_RENDER_TARGET_USAGE_FORCE_HARDWARE_RENDERING
  ○ D2D1_RENDER_TARGET_USAGE_FORCE_SOFTWARE_RENDERING

・VAIO type P US15W GMA500 (Direct3D9 ShaderModel3.0) (Aero 無効)
  × D2D1_RENDER_TARGET_USAGE_FORCE_HARDWARE_RENDERING
  ○ D2D1_RENDER_TARGET_USAGE_FORCE_SOFTWARE_RENDERING

Aero 無効かつ D3D10/11 全滅の GMA500 では HARDWARE_RENDERING だと
動きません。SOFTWARE_RENDERING では動作しました。
このことから HARDWARE/SOFTWARE のフラグ設定は機能しているものと思われます。
GMA950 の EeePC では両方動作しているので、ShaderModel2.0 でも Direct2D で
ハードウエアアクセラレーションが有効になっているのではないかと考えられます。
つまり Windows7 では Direct3D10.1 で ShaderModel2~3 が動いているということ。
直接 Direct3D10.1 を試した方が早かったかもしれません。


関連エントリ
Intel GMA500 のスペックについて考える。続き (2)
Windows7 リモートデスクトップと Direct3D
Direct3D11/DirectX11 (18) GPU を使ったアウトラインフォントの描画の(6)
Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
Direct3D11 Technical Preview D3D11の互換性、WARP Driver


WindowsMobile 用のソフトウエアキーボード touchkeysip に、
前から考えていた機能をいくつか追加しました。

ダウンロード
touchkeysip v1.10


●加速センサー対応

加速センサーに対応した専用バージョン touchkeysip GS をリリースしました。
アーカイブやインストーラを標準版と分けてあります。

Script 内でセンサー値を直接受け取ることが出来ます。
サンプル gsensorsip では本体を傾けることで画像をスクロールしているだけですが、
本体を傾ける向きで 日本語、英数、記号などの入力パネルを切り替えたり、
カーソルを移動させるといった応用も考えられます。
本体のアクションをきちんと処理するのは難しいですが、Script 処理次第では
何らかの動作で文字入力することも出来るかもしれません。


●ステータス領域のアイコン画像の切り替え

標準の SIP だと「あ」「A」など入力文字の状態が表示されているエリアです。
あらかじめ作成したアイコン画像を用意しておけば、状態に合わせて表示を切り替える
ことが出来るようになりました。

またキーボードをデザインした人が touchkeysip ではなく自前のアイコンを表示
したい、といった用途にも使えます。


●管理可能な画像を増量

アイコン表示追加に伴い、複数枚の画像の読み込みにきちんと対応しました。
従来は 2枚まで読み込めましたが、DisplayList に使えないなど中途半端な実装でした。
8 枚まで読み込めてかつ表示やステータスアイコンとして利用できます。
でも表示するデータはできるだけ 1枚にまとめた方が処理は高速だと思われます。


●タイマーの追加

加速センサー対応によって、従来のキーリピートとは別にポーリング用のタイマーが
必要となりました。よって複数のタイマー機能を使えるようにしています。
上限は容易に撤廃できますが API としてはとりあえず 4個まで。


●読み込める画像形式の追加

bmp 以外も読み込めるようになりました。jpeg など。
Script の命令自体は LoadBitmap のままです。
16bit RGB bitmap は直接読み込めないことがあるようです。


●サンプル

gsensorsip v1.00 はこれらの追加機能を実際に使用しています。
実用性は全く無く、あくまで機能のテスト用です。


●注意点

機能の追加や変更が多いため、互換性など何らかの問題がある場合は旧バージョン
v1.09 等を使用してください。
制作時に動作テスト可能な機種や環境も限られるため、必ずしも完全にテストが
行われているわけではない点にご注意ください。


関連エントリ
HTC Touch Diamond タッチセンサー API
WindowsMobile touchkeysip v1.07
WindowsMobile touchkeysip / ctrlswapmini