12 March 2008
Silverlight 2 の新機能 - Deep Zoom
MIX 08 の基調講演において Silverlight 2 の新機能として注目を集めたものに Deep Zoom という技術があります。ちょうど、Virutal Earth 技術を使っている Live Search Maps がマウスホイールを使って地図の拡大・縮小を行えるように、巨大なビットマップの全体から特定の部分までをスムーズに拡大(ズームイン)・縮小(ズームアウト)できるものです。Live Search Maps では AJAX を使っているので、少し動きがぎこちない面はありますが、Deep Zoom は、とてもスムーズにズームできます。
百聞は一見にしかず、まずは基調講演でも取り上げられた Hard Rock Memorabilia をご覧ください(もちろん、Silverlight 2 Beta 1 をインストールしておく必要があります)。マウスホイールによってイメージをズームしたり、マウスドラッグでイメージ全体を移動できますが、ズーム倍率をあげると、どんどん細かいイメージが表示されてくることがわかります。
さて、Deep Zoom 技術を使うにはどうすればよいでしょうか。これは、単純にビットマップ全体をダウンロードして表示しているのではありません(そんなことをしたら、最初に巨大なイメージデータをダウンロードすることになってしまいます)。そこで、Deep Zoom で使うためのデータをサーバー側に用意しなければならないのです。このデータを作るのが Deep Zoom Composer というツールです。まずは、このツールをインストールしてください。
※Deep Zoom Composer User Guide(PDF) が、Expression Blend and Design ブログで紹介されていますが、簡単な使い方は以下のとおりです。
- 新しいプロジェクトを作成する
- (上部のタブが "Import" になっているのを確認して)[Add Image...] ボタンで使いたいイメージを追加する(Importing と表示されてイメージが取り込まれます)
- 上部のタブで "Compose" を選び、右側に取り込まれているイメージを左側の領域に配置していきます。
- 上部のタブで "Export" を選び、右側で適当な名前(Name)を設定したのち、[Export] ボタンで出力する。
※エクスポート処理には、多少時間がかかります。
これで指定したフォルダにデータが作成されます。
Visual Studio 2008(Silverlight Tools をインストールした状態)で、新しい Silverlight 2 プロジェクトを作成し、さきほど生成したデータ(info.binの入っているフォルダ以下のすべて)を、このプロジェクトの Web サイトの ClientBin フォルダにコピーします(※追記。プロジェクトをビルドして .xap ファイルが作成されるまでは、このフォルダがないので注意してください)。Silverlight の XAML でこのイメージデータを表示するためには次のようにするだけです。
<MultiScaleImage x:Name="msi" ViewportWidth="1.0" Source="teched2007/info.bin" />
ただし、これだけではマウスによるズームイン、ズームアウトは実装されません。そこで、次のようにマウスイベントを処理するように定義します。なお、UserControl のサイズ(HeightとWidth)は大きな値に変更するか、削除しておくとよいでしょう)。
<MultiScaleImage x:Name="msi" ViewportWidth="1.0" Source="teched2007/info.bin"
MouseLeftButtonDown="OnMouseLeftButtonDown"
MouseLeftButtonUp="OnMouseLeftButtonUp"
MouseMove="OnMouseMove"
MouseLeave="OnMouseLeave" />
イベントハンドラは、以下のように定義します。
public partial class Page : UserControl
{
private bool dragging = false;
private bool dragged = false;
Point dragStart;
Point currentOrigin;
public Page()
{
InitializeComponent();
}
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
dragging = true;
dragged = false;
dragStart = e.GetPosition(msi);
currentOrigin = msi.LogicalToElementPoint(msi.ViewportOrigin);
}
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (dragged == false)
{
// [Ctrl] を押しているときはズームアウト(縮小)、押していないときはズームイン(拡大)
if (Keyboard.Modifiers == ModifierKeys.Control)
Zoom(e.GetPosition(msi), 0.8); // 縮小倍率
else
Zoom(e.GetPosition(msi), 1.2); // 拡大倍率
}
dragging = false;
dragged = false;
}
private void Zoom(Point P, double ZoomFactor)
{
P = msi.ElementToLogicalPoint(P);
this.msi.ZoomAboutLogicalPoint(ZoomFactor, P.X, P.Y);
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (dragging == true)
{
// ドラッグしたら、イメージを移動
dragged = true;
Point p = new Point();
p.X = currentOrigin.X - (e.GetPosition(msi).X - dragStart.X);
p.Y = currentOrigin.Y - (e.GetPosition(msi).Y - dragStart.Y);
msi.ViewportOrigin = msi.ElementToLogicalPoint(p);
}
}
private void OnMouseLeave(object sender, MouseEventArgs e)
{
dragging = false;
dragged = false;
}
}
これで、マウスの左ボタンをクリックしてズームインしたり、[Ctrl] を押しながらクリックすることでズームアウトできるようになります。(ホイール処理については、改めて)
実際の例については、Tech Ed 2007 で撮影した写真を素材に作成してみた例をご覧ください。
→ http://ohno.members.winisp.net/deepzoomtest/DeepZoomTestTestPage.html
※ズームインしてもピンボケしている部分はありますが、単に撮影者の腕です^_^;