選択した画像と、テクスチャとなる画像を合成して保存するには:2カ月で160本作った還暦開発者が送る10のアプリ開発ノウハウ(7)(3/4 ページ)
古(いにしえ)からのVBでWindows 8.1向けのさまざまな機能のアプリを開発する手順やコツを解説していく。今回は、選択した画像と、テクスチャとなる画像を合成して保存する方法をサンプルを交えて解説する。
メイン画面のロジックコードを記述する(MainWindow.xaml.vb)
次に、[ソリューション・エクスプローラー内]の「MainWindow.xaml」を展開して表示される、「MainWindow.xaml.vb」のコードを記述する。
ここではコードが長くなるため今回のテーマと肝となる「画像の合成」「データの一覧表示」部分の解説にとどめている。名前空間の読み込み、メンバー変数の宣言も省略している。全てのコードを見る場合は、サンプルをダウンロードして「MainWindow.xaml.vb」ファイルを見ていただきたい。
ページがアクティブになった時の処理(OnNavigatedToメソッド処理)
Protected Overrides Async Sub OnNavigatedTo(e As Navigation.NavigationEventArgs) ichiranButton.Visibility = Xaml.Visibility.Visible Dim myFolder As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary Dim SubFolder = Await myFolder.CreateFolderAsync("BiltImage", CreationCollisionOption.OpenIfExists) Dim myPictureFile = Await SubFolder.GetFilesAsync If myPictureFile.Count > 0 Then ichiranButton.IsEnabled = True Else ichiranButton.IsEnabled = False End If MyBase.OnNavigatedTo(e) End Sub
以降、上記コードの中身を詳細に見ていこう。
まず、非同期処理で行われるためメソッドの先頭にAsyncを追加している。
ピクチャライブラリーにアクセスし、CreateFolderAsyncメソッドで「BiltImage」というフォルダーを作成する。その際、「CreationCollisionOption.OpenIfExists」を指定すると、同名フォルダーがあるときは、そのフォルダー名を返し、ない時は新規に作成する。「CreationCollisionOption Enumeration」の詳細については、下記のURLを参照してほしい。
GetFilesAsyncメソッドでは、「BiltImage」フォルダー内のファイルを取得し、変数myPictureFileで参照しておく。
Countプロパティでは、そのフォルダー内のファイルの数を取得し、ファイルが存在する場合は、「データ一覧」ボタンの使用を可能にする。それ以外は使用を不可とする。
「元画像」ボタンがタップされた時の処理(readButton1_Clickメソッド処理)
Private Async Sub readButton1_Click(sender As Object, e As RoutedEventArgs) Handles readButton1.Click resultImage.Source = Nothing Image1.Source = Nothing Dim myFileOpenPicker As New FileOpenPicker myFileOpenPicker.ViewMode = PickerViewMode.Thumbnail myFileOpenPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary myFileOpenPicker.FileTypeFilter.Add(".png") myFileOpenPicker.FileTypeFilter.Add(".jpg") myFile = Await myFileOpenPicker.PickSingleFileAsync() If myFile Is Nothing = False Then Dim myBmp As New BitmapImage myBmp.SetSource(Await myFile.OpenReadAsync) If myBmp.PixelWidth <> 640 OrElse myBmp.PixelHeight <> 480 Then Dim message As New MessageDialog("640×480サイズの画像を指定してください") Await message.ShowAsync Exit Sub Else Image1.Source = myBmp End If readButton2.IsEnabled = True Else readButton2.IsEnabled = False End If End Sub
以降、上記コードの中身を詳細に見ていこう。
まず、非同期処理で行われるためメソッドの先頭にAsyncを追加している。
FileOpenPickerクラスの新しいインスタンスmyFileOpenPickerオブジェクトを作成する。FileOpenPickerクラスは、ユーザーが選択し、ファイルを開くことのできるUI要素を表すクラスだ。
ファイルの表示モードを指定するViewModeプロパティにサムネイル表示を指定する。サムネイル表示の他にリスト(List)表示がある。ファイルを開く最初の場所を設定するSuggestedStartLocationプロパティに、ピクチャライブラリーを指定しておく。開くファイルタイプを指定するFileTypeFilter.Addで「.png」と「.jpg」を指定しておく。
PNGファイルを指定する場合は、FileTyleFilter.Add(".png")と指定する。"*.pn"ではないので注意してほしい。
PickSingleFileAsynメソッドで、ユーザーが1つのファイルを選択できるようにファイルピッカーを表示し、変数myFileで参照する。
変数myFileがファイルを参照している場合は、新しいBitmapImageクラスのインスタンスmyBmpオブジェクトに、SetSourceメソッドで「Await myFile.OpenReadAsync」と指定する。そして、ファイルの内容を読み込むために、現在のファイルをランダムアクセスストリームで開く。
もし指定したファイルの画像サイズが640×480より大きかった場合は、メッセージを表示して処理を抜ける。それ以外の場合は、Image1のSourceプロパティにmyBmpオブジェクトを指定する。これで、ファイルピッカーで指定したファイルが表示される。
また、画像を選択する際、選択をやめてキャンセルした場合も、「合成画像」ボタンの使用は不可としておく。それ以外の場合は使用可とする。
「合成画像」ボタンがタップされた時の処理(readButton2_Clickメソッド処理)
以下の処理は、『「元画像」ボタンがタップされた時の処理(readButton1_Clickメソッド処理)』と同じであるため、そちらを参照してほしい。
Private Async Sub readButton2_Click(sender As Object, e As RoutedEventArgs) Handles readButton2.Click Image2.Source = Nothing Dim myFileOpenPicker As New FileOpenPicker myFileOpenPicker.ViewMode = PickerViewMode.Thumbnail myFileOpenPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary myFileOpenPicker.FileTypeFilter.Add(".png") myFileOpenPicker.FileTypeFilter.Add(".jpg") myFile2 = Await myFileOpenPicker.PickSingleFileAsync() If myFile2 Is Nothing = False Then Dim myBmp As New BitmapImage myBmp.SetSource(Await myFile2.OpenReadAsync) If myBmp.PixelWidth <> 640 OrElse myBmp.PixelHeight <> 480 Then Dim message As New MessageDialog("640×480サイズの画像を指定してください") Await message.ShowAsync Exit Sub Else Image2.Source = myBmp End If makeButton.IsEnabled = True Else makeButton.IsEnabled = False End If End Sub
「合成」ボタンがタップされた時の処理(makeButton_Clickメソッド処理)
このアプリの肝となる、2枚の画像を合成する処理だ。Nugetで取得したWriteableBitmapEのBiltメソッドを使用する。
Private Async Sub makeButton_Click(sender As Object, e As RoutedEventArgs) Handles makeButton.Click Dim myColor = Colors.White Dim wb1 As WriteableBitmap = New WriteableBitmap(CInt(Image1.Width), CInt(Image1.Height)) Dim wb2 As WriteableBitmap = New WriteableBitmap(CInt(Image2.Width), CInt(Image2.Height)) wb1.SetSource(Await myFile.OpenReadAsync) wb2.SetSource(Await myFile2.OpenReadAsync) Dim myRect As New Rect(0, 0, Image1.Width, Image1.Height) ‘ このアプリの肝。2枚の画像をBiltメソッドで合成する。 wb1.Blit(myRect, wb2, myRect, myColor, WriteableBitmapExtensions.BlendMode.Multiply) wb1.Invalidate() Using myStream As Stream = wb1.PixelBuffer.AsStream Await myStream.FlushAsync() End Using Dim myFolder As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary Dim SubFolder = Await myFolder.CreateFolderAsync("BiltImage", CreationCollisionOption.OpenIfExists) myFile = Await SubFolder.CreateFileAsync(DateTime.Now.ToString("yyyy年MM月dd日HH時mm分ss秒") & ".png") mySaveFile = myFile FileName = DateTime.Now.ToString("yyyy年MM月dd日HH時mm分ss秒") & ".png" Dim myID As Guid = Windows.Graphics.Imaging.BitmapEncoder.PngEncoderId Await WriteableBitmapSaveExtensions.SaveToFile(wb1, myFile, myID) ichiranButton.IsEnabled = True Image1.Source = Nothing Image2.Source = Nothing resultImage.Source = wb1 makeButton.IsEnabled = False readButton2.IsEnabled = False End Sub
以降、上記コードの中身を詳細に見ていこう。まず、非同期処理で行われるためメソッドの先頭にAsyncを追加している。
次に、色をWhiteとしておく。他の色にすると画像自体にその色のフィルターが掛かってしまう。
「元画像」ボタンで取り込んだ、台紙となる画像サイズが640×480で初期化された、WriteableBitmapオブジェクトのインスタンスwb1を作成する。
続いて、「合成画像」ボタンで取り込んだ、合成する画像サイズが640×480で初期化された、新しいWriteableBitmapオブジェクトのインスタンス、wb2を作成する。
ストリームにアクセスしてBitmapSourceのソースイメージ(この場合、Await myFile.OPenReadAsync)を設定し、変数wb1で参照する(合成元の画像)。同じく、変数wb2で合成する画像を参照する。
四角形の幅、高さ、および原点を示す新しいインスタンスmyRectを作成する。myRectのWidthに「元画像」ボタンで取り込んで表示されたImage1の「Widthの値」を、HeightにはImage1の「Heightの値」を指定する。
WriteableBitmapEx のBiltメソッドで「元画像」と「合成画像」を合成する。書式は下記の通りだ。
Wb.Bilt(destRect As Rect,Source As Media.Imaging.WritableBitMap,sourceRect As Rect,color As Windows.UI.Color,BlendMode as Media.Imaging.WriteableBitmapExtensions.BlendMode)
BlendModeには、必ずWriteableBitmapExtensions.BlendMode.Multiplyと指定する。Invalidateメソッドで、ビットマップ全体を再描画する。
合成された画像の、各ピクセルの書き込み先のディレクトリバッファーのアクセスを取得し、Stream型の変数myStreamで参照する。FlushAsyncメソッドで、ストリームに対応する全てのバッファーを非同期にクリアし、バッファー内のデータをデバイスに書き込む。ピクチャライブラリーの「BiltImage」サブフォルダーにアクセスする。
CreateFileAsyncメソッドで、「BiltImage」フォルダー内に「yyyy年MM月dd日HH時mm分ss秒.png」形式のファイルを作成する。
Windows.Graphics.Imaging.BitmapEncoder.PngEncoderIdで、PNG画像のグローバル一意識別子を取得して、変数myIDに格納しておく。
WinRTXamlToolkit.ImagingのWriteableBitmapSaveExtensions.SaveToFileメソッドで、合成した画像(wb1)を「yyyy年MM月dd日HH時mm分ss秒.png」形式に書き出す。WriteableBitmapSaveExtensions.SaveToFileメソッドの書式は、下記の通りだ。
WriteableBitmapSaveExtensions.SaveToFile(writeableBitmap as Windows.UI.Xaml.Media.Imageing.WriteableBitmap,output as Windows.Storages.StorageFile,encodeId as System.GUID)
「データ一覧」アイコンを使用可能にし、Image1とImage2をクリアし、resultImageに合成された画像を表示する。「合成」ボタンと「合成画像」ボタンの使用を不可とする。
Copyright © ITmedia, Inc. All Rights Reserved.