画像の任意の部分をトリミングして保存するプログラムを作る
タッチを開始した時の処理
RectangleのMarginプロパティに、タッチした位置に合わせてRectangleを移動した位置を指定します。Marginプロパティの書式は下記の通りです。
FrameworkElement.Margin(left,,top, right, buttom)
たとえばMargin=”10”は全てのプロパティが10に設定されます。またMargin=”10,20”はLeftとRightが10、TopとBottomが20に設定されたThicknessとして解釈されます。
Marginプロパティの値はNew Thickness(Left、Top、Right、Bottom)と指定します。この場合はleftにe.Position.Xを、topにe.Position.Yの値を指定しています。
RectangleのWidthとHeightを0で初期化します。非表示になっていたRectangleを表示します。
Private Sub ContentPanel_ManipulationStarted(sender As Object, e As ManipulationStartedRoutedEventArgs) Handles ContentPanel.ManipulationStarted Rectangle1.Margin = New Thickness(e.Position.X, e.Position.Y, 0, 0) Rectangle1.Width = 0 Rectangle1.Height = 0 Rectangle1.Visibility = Windows.UI.Xaml.Visibility.Visible End Sub
矩形上でドラッグした時の処理
矩形の位置をドラッグした移動量分だけ動かします。e.Delta.Translation.X、e.Delta.Translation.Yでx-y画面座標の変化を取得しています。e.Handled=Trueと指定して、Gridにイベントが伝わらないようにします。
Private Sub Rectangle1_ManipulationDelta(sender As Object, e As ManipulationDeltaRoutedEventArgs) Handles Rectangle1.ManipulationDelta Rectangle1.Margin = New Thickness(Rectangle1.Margin.Left + e.Delta.Translation.X, Rectangle1.Margin.Top + e.Delta.Translation.Y, 0, 0) e.Handled = True End Sub
矩形をタッチした時の処理
e.Handled=Trueと指定して、Gridにイベントが伝わらないようにします。
Private Sub Rectangle1_ManipulationStarted(sender As Object, e As ManipulationStartedRoutedEventArgs) Handles Rectangle1.ManipulationStarted e.Handled = True End Sub
[切り出す]ボタンがクリックされた時の処理
Rectangle(矩形)のWidthとHeightで初期化された、新しいWriteableBitmapのインスタンスmyWriteableBitmapオブジェクトを作成します。
イメージデータやファイルからデータを読み取る、FromStreamメソッドで、FileOpenPickerで指定したファイルのデータを読み取り、myCrop変数で参照しておきます。
Cropメソッドで、Rectangleで指定した領域を切り出し、WriteableBitmap型のメンバ変数mySelectedImageで参照しておきます。Cropメソッドの書式は下記の通りです
WriteableBitmap.Crop(x As Integer,y As Integer,width As Integer,height As Integer)
切り出された画像を表示するresultImageのWidthにRectangleのWidthを指定し、HeightにはRectangleのHeightの値を指定します。
SourceプロパティにmySelectedImageオブジェクトを指定します。
Invalidateメソッドでビットマップ全体を再描画します。[保存]ボタンの使用を可能にします。
非同期処理で行われるため、メソッドの先頭にAsyncを追加します。Asyncが追加されていると、その処理が非同期で行われることを意味します。
Private Async Sub Button1_Click(sender As Object, e As RoutedEventArgs) Handles Button1.Click Dim myWriteableBitmap = New WriteableBitmap(CInt(Rectangle1.Width), CInt(Rectangle1.Height)) Dim myCrop = Await myWriteableBitmap.FromStream(Await myFile.OpenReadAsync) mySelectedImage = myCrop.Crop(CInt(Rectangle1.Margin.Left), CInt(Rectangle1.Margin.Top), CInt(Rectangle1.Width), CInt(Rectangle1.Height)) resultImage.Width = Rectangle1.Width resultImage.Height = Rectangle1.Height resultImage.Source = mySelectedImage mySelectedImage.Invalidate() saveButton.IsEnabled = True End Sub
[画像選択]ボタンがクリックされた時の処理
Rectangleを非表示にし、切り出され表示されていた画像を消去します。画像を選択するSelectImageプロシージャを実行します。
Private Sub imageSelectButton_Click(sender As Object, e As RoutedEventArgs) Handles imageSelectButton.Click Rectangle1.Visibility = Windows.UI.Xaml.Visibility.Collapsed resultImage.Source = Nothing SelectImage() End Sub
[保存]ボタンがクリックされた時の処理
切り出されたされた画像(mySelectedImage)のストリームを取得し、FlushAsyncメソッドで、ストリームに対応する全てのバッファを非同期にクリアし、バッファ内のデータを基となるデバイスに書き込みます。
切り出された画像のストリームの長さで初期化された、バイト配列のオブジェクトmyPixelを作成します。ReadAsyncメソッドで、現在のストリームからバイトシーケンスを非同期的に読み取り、読み取ったバイト数だけストリームの位置を進めます。
インデックスとなる変数offSetを宣言します。切り出された画像の高さ(Pixelheight)と幅(PixelWidth)に対して、反復処理を行います。offSet変数には、4ビットを乗算したバイト型の配列変数myPixelの、インデックスになる値を格納します。
バイト型のBRGA(青、赤、緑、アルファ)変数に、ピクセルのバイト数を転送し、offSetに対応するmyPixelバイト変数にRGBAの値を指定します。この画像処理を行わないと、保存する画像が実際の画像の色と異なって保存されますので、注意してください。
ピクチャライブラリのサブフォルダーCutImageにアクセスします。CreateFileAsyncメソッドで、「年月日時間分秒.png」ファイルを作成します。CreateFileAsyncメソッドは、フォルダーまたはファイルグループに新規ファイルを作成するメソッドです。
OpenAsyncメソッドで読み取りと書き込み可能な状態で、作成したPNG画像を開きます。OpenAsyncメソッドは、ファイルのランダムアクセスストリームを開くメソッドです。
イメージデータの作成、編集、保存に使用されるイメージエンコーダーを含む、BitmapEncoderクラス用の変数myEncoderを宣言します。CreateAsyncメソッドで、指定済みコーデックのために新しいBitmapEncoderを非同期的に作成し、ストリーム上でこれを初期化します。CreateAsyncメソッドには、「指定したエンコーダーのCLSID」と「ストリームのイメージファイルが書き込まれる場所」を指定します。ここでは、「指定したエンコーダーのCLSID」には、BitmapEncoder.PngEncoderIdを、「ストリームのイメージファイルが書き込まれる場所」にはmyFileStreamを指定しています。
BitmapEncoderクラスのSetPixelDataメソッドで、引数で指定されたパラメーターを使用して、フレームビットマップピクセルデータを指定します。SetPixelDataの書式は下記の通りです。
BitmapEncoder.SetPixelData(ピクセルデータのピクセル形式,ピクセルデータのアルファモード,ピクセルの幅,ピクセルの高さ, 水平方向の解像度のドット/インチ、ピクセル データ(通常96), 垂直方向の解像度のドット/インチ、ピクセル データ(通常96),ピクセルデータ)
BitmapEncoderのFlushAsyncメソッドで、非同期的に現在のフレームデータをコミットし、イメージファイルのデータ全てをフラッシュします。切り出された画像が保存されます。
保存した旨のメッセージを表示します。
非同期処理で行われるため、メソッドの先頭にAsyncを追加します。Asyncが追加されていると、その処理が非同期で行われることを意味します。
Private Async Sub saveButton_Click(sender As Object, e As RoutedEventArgs) Handles saveButton.Click Using myStream As Stream = mySelectedImage.PixelBuffer.AsStream Await myStream.FlushAsync() Dim myPixel As Byte() = New Byte(CInt(myStream.Length - 1)) {} Await myStream.ReadAsync(myPixel, 0, myPixel.Length - 1) Dim offSet As Integer For row As Integer = 0 To CInt(CUInt(mySelectedImage.PixelHeight) - 1) For col As Integer = 0 To CInt(CUInt(mySelectedImage.PixelWidth) - 1) offSet = CInt((row * CUInt(mySelectedImage.PixelWidth) * 4) + (col * 4)) Dim B As Byte = myPixel(offSet) Dim G As Byte = myPixel(offSet + 1) Dim R As Byte = myPixel(offSet + 2) Dim A As Byte = myPixel(offSet + 3) myPixel(offSet) = R myPixel(offSet + 1) = G myPixel(offSet + 2) = B myPixel(offSet + 3) = A Next Next Dim myFolder As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary Dim SubFolder = Await myFolder.CreateFolderAsync("CutImage", CreationCollisionOption.OpenIfExists) Dim mySaveFile As StorageFile = Await SubFolder.CreateFileAsync(DateTime.Now.ToString("yyyyMMddHHmmss") & ".png") Using myFileStream As IRandomAccessStream = Await mySaveFile.OpenAsync(FileAccessMode.ReadWrite) Dim myEncoder As BitmapEncoder = Await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, myFileStream) myEncoder.SetPixelData(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Premultiplied, CUInt(mySelectedImage.PixelWidth), CUInt(mySelectedImage.PixelHeight), 96, 96, myPixel) Await myEncoder.FlushAsync End Using Dim myMessage As New MessageDialog("保存しました。") Await myMessage.ShowAsync End Using End Sub End Class
今回はここまでです。ありがとうございました。
任意の画像をトリミングして保存するWindowsアプリ