「Expression Studio 3」日本語版が完成

9月10日、「Expression Studio 3」日本語版の開発が終了したことが発表されました。現在、公式ページから評価版をダウンロードして試用できます。ボリュームライセンスの販売は10月から、パッケージの販売は11月からの予定です。すでにSilverlightアプリケーション開発者向けに「Silverlight 3 SDK」や「Microsoft Silverlight 3 Tools」が公開されていますが、Expression Studio 3の登場でExpression Blendを用いた本格的なデザインをSilverlight 3開発に導入できます。新機能のSketch Flowなどの開発支援などにも注目です。

Expression Blend 3 の SketchFlow を使った画面遷移の設計

これでSilverlight 3世代のツールが一通り揃ったことになります。Silverlight 2までは、メディアの再生環境としては優れた能力を持っていたものの、実行時にメディアを生成または制御する機能が貧弱だったため、ゲームやマルチメディア分野での応用に限界がありました。Silverlight 3では低水準なメディアAPIがサポートされてるようになったためメディアの再生環境の枠を超え、メディアの作成や編集ツールなどの実装も可能となります。

書き換え可能なイメージ

今回は、Silverlight 3で追加された新機能の1つである「WriteableBitmapクラス」を紹介します。このクラスは、プログラムから動的にイメージを作成・更新できる機能を提供します。アプリケーションのスナップショットの撮影や、アルゴリズムからフラクタルなどの画像を実行時に作成して描画できます。ピクセル変換が目的であれば前述したエフェクトを使えますが、WriteableBitmapクラスは何もないところから新しくイメージを作成できます。

System.Windows.Media.Imaging.WriteableBitmap クラス

public sealed class WriteableBitmap : BitmapSource
System.Object
   System.Windows.DependencyObject
     System.Windows.Media.ImageSource
       System.Windows.Media.Imaging.BitmapSource
        System.Windows.Media.Imaging.WriteableBitmap

このクラスはImageSourceクラスから派生しているため、ImageコントロールのSourceプロパティに設定して表示できます。つまり、インスタンスを生成した後はURIから読み込んだ画像と同じ扱いができます。

WriteableBitmapクラスの大きな特徴は、任意のUIElementオブジェクトをビットマップ化できることです。コンストラクタは、幅と高さを表す2つの整数を受け取ってイメージのサイズを固定する方法と、UIElementオブジェクトと変換用のTransformオブジェクトを受け取って、対象のUIElementオブジェクトをイメージ化する2つにオーバーロードされています。

WriteableBitmap クラスのコンストラクタ

public WriteableBitmap(
    int pixelWidth,
    int pixelHeight
)
public WriteableBitmap(
    UIElement element,
    Transform transform
)

pixelWidthパラメータとpixelHeightパラメータを渡した場合、イメージは指定した幅と高さに固定され、内容は後から後述するRender()メソッドで描画します。

もう一方のコンストラクタでは、elementパラメータに指定したUIElementオブジェクトの外観をそのままイメージ化します。このとき、transformパラメータに指定した変換オブジェクトを使って座標、角度、サイズなどを調整できます。変換しない(オリジナルのままイメージ化する)場合はnullを渡してください。

UIElementオブジェクトを渡した場合、対象のオブジェクトのスナップショットが得られます。Silverlightでは、描画可能なオブジェクトはすべてUIElementなので、コントロール、図形、画像、動画から容易にイメージを作ることができます。

コード01 XAMLファイル

<Grid x:Name="LayoutRoot">
 <StackPanel>
  <TextBlock Text="--コントロール--" />
  <Border Margin="5,0,5,5" BorderThickness="2" BorderBrush="Black">
   <StackPanel x:Name="capPanel">
    <TextBox Margin="5" Text="TextBox" />
    <Slider Value="50" Minimum="0" Maximum="100" />
    <CheckBox Content="CheckBox1" IsChecked="true" />
    <CheckBox Content="CheckBox2" />
   </StackPanel>
  </Border>

  <Button x:Name="capButton" Margin="5" Content="キャプチャ" Click="capButton_Click" />

  <TextBlock Text="--キャプチャ画像--" />
  <Border Margin="5,0,5,5" BorderThickness="2" BorderBrush="Black">
   <Image x:Name="image" />
  </Border>
 </StackPanel>
</Grid>

コード01 capButton_Click() メソッド(C#)

private void capButton_Click(object sender, RoutedEventArgs e)
{
    WriteableBitmap bitmap = new WriteableBitmap(capPanel, null);
    image.Source = bitmap;
}

実行結果

コード01は、「キャプチャ」ボタンを押すと上部のコントロールを含むスタックパネルからWritableBitmapオブジェクトを生成し、イメージとして下部に表示します。テキストボックスやスライダー、チェックボックスの現在の状態がそのままイメージ化されていることが結果から確認できます。

動画を再生するMediaElementクラスもまたUIElementクラスから派生しているため、Silverlight 3では再生中の動画から容易にスナップショットを作成できます。ちなみに、Silverlight 3からは動画の再生にH.264(MPEG-4)がサポートされました。モバイルや動画共有サイトで一般的なフォーマットなので、多くの動画がそのままSilverlightで再生できるようになりました。

コード02 XAML

<Grid x:Name="LayoutRoot">
 <StackPanel>
  <TextBlock Text="--動画--" />
  <Border Margin="5,0,5,5" BorderThickness="2" BorderBrush="Black">
   <MediaElement x:Name="capMedia" Source="test.mp4" />
  </Border>

  <Button x:Name="capButton" Margin="5" Content="キャプチャ" Click="capButton_Click" />

  <TextBlock Text="--キャプチャ画像--" />
  <Border Margin="5,0,5,5" BorderThickness="2" BorderBrush="Black">
   <Image x:Name="image" />
  </Border>
 </StackPanel>
</Grid>

コード02 capButton_Click() メソッド(C#)

private void capButton_Click(object sender, RoutedEventArgs e)

{
        WriteableBitmap bitmap = new WriteableBitmap(capMedia, null);
        image.Source = bitmap;
    }

実行結果

コード02は、コード01を改良してWritableBitmapクラスのコンストラクタに渡すオブジェクトを、動画を再生するMediaElementに変更したプログラムです。「キャプチャ」ボタンを押すと、ボタンを押した瞬間のフレームがイメージとして下部に貼り付けられます。

WritableBitmapクラスは、インスタンス生成後も任意のタイミングでイメージを更新できます。イメージの内容を上書きするにはRender()メソッドを呼び出してください。メソッドのパラメータはUIElementオブジェクトを渡すコンストラクタと同じです。

WriteableBitmap クラス Render() メソッド

public void Render(
    UIElement element,
    Transform transform
)

UIElementオブジェクトのサイズが描画先のイメージよりも大きい場合、イメージのサイズにクリップされます。

Render()メソッドでWriteableBitmapオブジェクトの内容を更新しても、Imageコントロールで描画しているイメージは自動的に更新されません。書き換えたイメージを画面に表示するにはInvalidate()メソッドで描画を要求してください。

WriteableBitmap クラス Invalidate() メソッド

public void Invalidate()

WritableBitmapクラスによって作成されるイメージは、内部でピクセルの列を保持するビットマップです。ビットマップはサイズに比例してメモリを消費するため、扱いに注意しなければなりません。短いサイクルでインスタンス化と破棄を繰り返すと、ガベージコレクションが頻繁に発生してパフォーマンスに影響します。

コード03 XAML

 <Grid x:Name="LayoutRoot">
 <StackPanel>
  <TextBlock Text="--動画--" />
  <Border Margin="5,0,5,5" BorderThickness="2" BorderBrush="Black">
   <MediaElement x:Name="capMedia" Source="test.mp4" />
  </Border>

  <StackPanel Orientation="Horizontal">
   <Button x:Name="capButton" Margin="5" Content="キャプチャ" Click="capButton_Click" />
   <RadioButton x:Name="newButton" Margin="5" VerticalAlignment="Center" Content="新規作成" IsChecked="True" />
   <RadioButton x:Name="renderButton" Margin="5" VerticalAlignment="Center" Content="上書き" />
   <TextBlock x:Name="memoryText" Margin="5" VerticalAlignment="Center" Text="メモリ使用量" />
  </StackPanel>

  <TextBlock Text="--キャプチャ画像--" />
  <Border Margin="5,0,5,5" BorderThickness="2" BorderBrush="Black">
   <Image x:Name="image" />
  </Border>
 </StackPanel>
</Grid>

コード03 capButton_Click()メソッド(C#)

private WriteableBitmap bitmap;
private void capButton_Click(object sender, RoutedEventArgs e)
{
    if (bitmap == null || newButton.IsChecked.Value)
    {
        bitmap = new WriteableBitmap(capMedia, null);
    }
    else if (renderButton.IsChecked.Value)
    {
        bitmap.Render(capMedia, null);
        bitmap.Invalidate();
    }

    memoryText.Text = GC.GetTotalMemory(false) + "byte";
    image.Source = bitmap;
}

実行結果

コード03は、new演算子で新しいWritableBitmapオブジェクトをインスタンス化する場合と、既存のWritableBitmapオブジェクトを書き換える場合とで、どの程度メモリの消費量が異なるかをテストするためのプログラムです。「新規作成」ラジオボタンをチェックしている場合は「キャプチャ」ボタンを押すたびにWritableBitmapインスタンスを生成します。一方「上書き」ラジオボタンがチェックしている場合は、既存のWritableBitmapオブジェクトの内容をRender()メソッドで書き換えます。

連続で「キャプチャ」ボタンを押し続けると、インスタンスを新規作成している場合はメモリを大きく消費してすぐにガベージコレクションが発生することを確認できます。これに対して、Render()メソッドで内容を上書きする場合は既存のインスタンスを再利用しているだけなので、ほとんどメモリの消費量は変わりません。

著者プロフィール:赤坂玲音

フリーランスのテクニカルライタ兼アプリケーション開発者。主にクライアント技術、プレゼンテーション技術が専門。2005年から現在まで「Microsoft MVP Visual C++」受賞。技術解説書を中心に著書多数。近著に『Silverlight入門』(翔泳社)などがある。