写真を切りぬいて新しい写真を撮影するサンプル

2012年6月15日(金)
PROJECT KySS

前ページからの続きです。

ページがアクティブでなくなった時の処理

「フロッピー」の「保存」ボタンの使用を不可とします。backgroundImageとdummyEllipseコントロールの表示を非表示にします。

    Protected Overrides Sub OnNavigatingFrom(e As System.Windows.Navigation.NavigatingCancelEventArgs)
      TryCast(ApplicationBar.Buttons(1), ApplicationBarIconButton).IsEnabled = False
      myImage = Nothing
      backgroundImage.Visibility = Windows.Visibility.Collapsed
      dummyEllipse.Visibility = Windows.Visibility.Collapsed
      MyBase.OnNavigatingFrom(e)
    End Sub

イメージが利用可能な場合に発生する処理

変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。Path.CombineでCameraInCameraというフォルダとimageFileNameに格納されている画像名とを連結し、変数filePathに格納します。
同様にCameraInCameraというフォルダとImageList.xmlというファイル名を連結して、変数xmlFilePathに格納します。

別スレッドで以下の処理を行います。

変数imageStorageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。
IsolatedStorageFileクラスのCreateFileメソッドでCameraInCameraフォルダ内にbackgroundImage.jpgというファイルを作成し、Writeメソッドでストリームに書き出します。

分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数myStream変数を用意し、IsolatedStorageFileクラスのCreateFileメソッドで、変数filePathに格納されているファイルを作成します。バイト型として5119の領域を確保します。
e.ImageStream.Readメソッドで、イメージストリームからバイト数を読み取り、変数myByteReadに格納します。この変数が、0より大きい間、IsolatedStorageFileStream.Writeメソッドで、読み込んだイメージストリームのバイト数をバッファに書き込んでいきます。

写真の撮影とエンコードが正しくできているかは、ContentReadyEventArgs.ImageStreamプロパティから取得できますので、これを分離ストレージへ保存します。e.ImageStreamを閉じます。
e.ImageStream.Readの書式は下記の通りです。

e.ImageStream.Read(バイト配列, データの格納を開始するバッファ内のバイト オフセット(通常0を指定), 現在のストリームから読み取るバイトの最大数。)

IsolatedStorageFileStream.Writeメソッドの書式は下記の通りです。

IsolatedStorageFileStream.Write(書き込むバッファ, 開始位置を示すバッファ内のバイト オフセット(通常0を指定) ,書き込む最大バイト数,)

分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数stream変数を用意します。IsolatedStorageFileクラスのOpenFileメソッドで、分離ストレージのCameraInCameraに保存しておいた、backgroundImage.jpgを指定したモード、指定したファイルアクセスを使用して開きます。
Image1コントロールを非表示にして、代わりにbackgroundImageコントロールを表示します。
WriteableBitmap型の変数myImageSourceに、PictureDecoder.DecodeJpeg(stream)で、撮った写真をJPEGファイルとしてWriteableBitmapオブジェクトにデコードします。
backgroundImageコントロールのSourceプロパティにmyImageSourceオブジェクトを指定します。

分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数stream2変数を用意します。IsolatedStorageFileクラスのOpenFileメソッドで、変数filePathに格納されているファイルを、指定したモード、指定したファイルアクセスを使用して開きます。
WriteableBitmap型の変数myImageSourceに、PictureDecoder.DecodeJpeg(stream2)で、撮った写真をJPEGファイルとしてWriteableBitmapオブジェクトにデコードします。
Ellipse1コントロールを非表示にし、代わりにdummyEllipseコントロールを表示します。
myImageBrushという名前のImageBrushにmyImageSourceオブジェクトを指定します。

InkPresenter1で初期化されたWriteableBitmapクラス用オブジェクト変数myWriteableBitmapを作成します。

分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数stream3変数を用意し、IsolatedStorageFileクラスのCreateFileメソッドで、分離ストレージのCameraInCameraフォルダ内にメンバ変数imageFileNameの画像ファイルを作成します。
WriteableBitmap.SaveJpegメソッドで、WriteableBitmapオブジェクトを、JPEGストリームにエンコードします。これは、JPEGファイルのターゲットとなる幅と高さを設定するためのパラメータを持っています。書式は下記の通りです。

WriteableBitmap.SaveJpeg(イメージデータストリーム,WriteableBitmapオブジェクトのPixelWidth, WriteableBitmapオブジェクトのPixelHeight,0(固定),0~100の間の写真の品質(70以上を指定))

分離ストレージのCameraInCamera内に480×640サイズのJPGファイルが保存されます。
不要になったbackgroundImage.jpgファイルを削除します。

変数filePathに格納されているファイルを、指定したモード、指定したファイルアクセスを使用して開きます。
WriteableBitmap型の変数myImageSourceに、PictureDecoder.DecodeJpeg(stream2)で、撮った写真をJPEGファイルとしてWriteableBitmapオブジェクトにデコードします。
Ellipse1コントロールを非表示にし、代わりにdummyEllipseコントロールを表示します。myImageBrushという名前のImageBrushにmyImageSourceオブジェクトを指定します。

別スレッドの下記の処理を行います。

変数xmlStorageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。CameraInCameraというフォルダ内にImageList.xmlが存在していない場合は、Visual Basic の埋め込み式を用いて、XML宣言とルート要素、その子要素として、属性に”RecordDate”を指定します。
要素の子要素としてを定義します。埋め込み式の構文である を用いて“RecordDate”属性の値にメンバ変数recordDateの値を、要素の値に、メンバ変数imageFileNameの値を指定します。これは ASP.NET で使用される構文と同じです。

分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数xmlStreamを用意し、IsolatedStorageFile.CreateFileメソッドで、分離ストレージ内にxmlFilePath変数の持っているフォルダ名付きXMLファイルを作成します。

ImageList.xmlファイルが存在する場合は、新しいStreamWriter 生成し、IsolatedStorageFile.OpenFileメソッドで、指定したファイルアクセスを使用して指定したモードでファイルを開き、初期化します。Writeメソッドで埋め込み式のXMLをストリームに書き込みます。別スレッドで、保存した旨のメッセージを表示し、Image1とEllipse1コントロールを表示し、backgroundImageとdummyEllipsコントロールを非表示にします。

次は、既に最初からCameraInCameraフォルダ内にImageList.xmlが存在する場合の処理です。

IsolatedStorageFileクラスのOpenFileメソッドでCameraInCameraフォルダ内のImageList.xmlファイルを、指定したファイルアクセスを使用して指定したモードで開きます。
開いたファイルをStreamReaderで読み込んだら、ReadToEndメソッドでファイルの最後まで読み取り、変数readXmldoc変数に格納しておきます。
読み込んだXMLテキストをParseメソッドでXElementとして読み込みます。

追加する要素を、埋め込み式を用いて生成し、”RecordDate”属性の値や、の内容テキストを指定します。
新しく生成したXML要素を、読み込んだXMLにAddメソッドで追加します。IsolatedStorageFileStreamを閉じます。

ImageList.xmlファイルが存在する場合は、新しいStreamWriter 生成し、IsolatedStorageFile.OpenFileメソッドで、指定したファイルアクセスを使用して指定したモードで開きます。
新しい要素の追加されたXMLを、Writeメソッドでストリームに書き込みます。別スレッドで、保存した旨のメッセージを表示し、Image1とEllipseコントロールを表示し、backgroundImageとdummyEllipseコントロールを非表示にします。

    Private Sub myCamera_CaptureImageAvailable(sender As Object, e As ContentReadyEventArgs)
      Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication
 
      Dim filePath As String = Path.Combine("CameraInCamera", imageFileName)
      Dim xmlFilePath As String = Path.Combine("CameraInCamera", "ImageList.xml")
      Deployment.Current.Dispatcher.BeginInvoke(Sub()
        Dim myImageSource As WriteableBitmap = Nothing
        Dim imageStorage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication
        Using cameraStream As IsolatedStorageFileStream = storage.CreateFile(Path.Combine("CameraInCamera", "backgroundImage.jpg"))
          cameraStream.Write(imageByte, 0, imageByte.Length)
        End Using
        Using myStream As IsolatedStorageFileStream = storage.CreateFile(filePath)
          Dim myBuffer(bufferValue) As Byte
          Dim myByteRead As Integer = -1
            Do
              myByteRead = e.ImageStream.Read(myBuffer, 0, myBuffer.Length)
              myStream.Write(myBuffer, 0, myByteRead)
            Loop While (myByteRead > 0)
                e.ImageStream.Close()
        End Using
 
        Using stream As IsolatedStorageFileStream = imageStorage.OpenFile(Path.Combine("CameraInCamera", "backgroundImage.jpg"), FileMode.Open, FileAccess.Read)
          Image1.Visibility = Windows.Visibility.Collapsed
          backgroundImage.Visibility = Windows.Visibility.Visible
          myImageSource = PictureDecoder.DecodeJpeg(stream, 480, 640)
          backgroundImage.Source = myImageSource
        End Using
 
        Using stream2 As IsolatedStorageFileStream = imageStorage.OpenFile(filePath, FileMode.Open, FileAccess.Read)
          myImageSource = PictureDecoder.DecodeJpeg(stream2, 480, 640)
          Ellipse1.Visibility = Windows.Visibility.Collapsed
          dummyEllipse.Visibility = Windows.Visibility.Visible
          myImageBrush.ImageSource = myImageSource
        End Using
          Dim myWriteableBitmap As WriteableBitmap = Nothing
          myWriteableBitmap = New WriteableBitmap(Inkpresenter1, Nothing)
 
          Using Stream3 As IsolatedStorageFileStream = storage.CreateFile(Path.Combine("CameraInCamera", imageFileName))
            myWriteableBitmap.SaveJpeg(Stream3, 480, 640, 0, 85)
          End Using
 
          If storage.FileExists(Path.Combine("CameraInCamera", "backgroundImage.jpg")) = True Then
              storage.DeleteFile(Path.Combine("CameraInCamera", "backgroundImage.jpg"))
          End If
                         End Sub)
 
          'XMLファイルの保存
          Dim xmlStorage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication
          If xmlStorage.FileExists(xmlFilePath) = False Then
            Dim xmldoc As XDocument = <?xml version="1.0" encoding="utf-8"?>
                                      <Pictures>
                                        <Picture RecordDate=<%= recordDate %>>
                                          <PictureName><%= imageFileName %></PictureName>
                                        </Picture>
                                      </Pictures>
            Using xmlStream As IsolatedStorageFileStream = xmlStorage.CreateFile(xmlFilePath)
            End Using
            If xmlStorage.FileExists(xmlFilePath) = True Then
                Using xmlwriter As StreamWriter = New StreamWriter(xmlStorage.OpenFile(xmlFilePath, FileMode.Open, FileAccess.Write), System.Text.Encoding.UTF8)
                  xmlwriter.Flush()
                  xmlwriter.Write(xmldoc.ToString)
                End Using
                Deployment.Current.Dispatcher.BeginInvoke(Sub()
                  MessageBox.Show("画像とXMLファイルを保存しました。")
                  Image1.Visibility = Windows.Visibility.Visible
                  Ellipse1.Visibility = Windows.Visibility.Visible
                  backgroundImage.Visibility = Windows.Visibility.Collapsed
                  dummyEllipse.Visibility = Windows.Visibility.Collapsed
                                                       End Sub)
            End If
          Else
            Dim xmlStream As IsolatedStorageFileStream = xmlStorage.OpenFile(xmlFilePath, FileMode.Open, FileAccess.Read)
            Using xmlreader As StreamReader = New StreamReader(xmlStream)
 
              Dim readXmldoc As String = xmlreader.ReadToEnd
              Dim doc As XElement = XElement.Parse(readXmldoc)
              Dim addXml As XElement = <Picture RecordDate=<%= recordDate %>>
                                         <PictureName><%= imageFileName %></PictureName>
                                       </Picture>
              doc.Add(addXml)
              xmlStream.Close()
 
              If xmlStorage.FileExists(xmlFilePath) = True Then
                  Using xmlwriter As StreamWriter = New StreamWriter(storage.OpenFile(xmlFilePath, FileMode.Open, FileAccess.Write), System.Text.Encoding.UTF8)
                    xmlwriter.Flush()
                    xmlwriter.Write(doc.ToString)
                    Deployment.Current.Dispatcher.BeginInvoke(Sub()
                        MessageBox.Show("画像とXMLファイルを保存しました。")
                        Image1.Visibility = Windows.Visibility.Visible
                        Ellipse1.Visibility = Windows.Visibility.Visible
                        backgroundImage.Visibility = Windows.Visibility.Collapsed
                        dummyEllipse.Visibility = Windows.Visibility.Collapsed
                                                                End Sub)
                  End Using
              End If
            End Using
      End If
    End Sub

「カメラ」アイコンの「カメラ起動」ボタンがタップされた時の処理

「フロッピー」アイコンの「保存」ボタンの使用を可能にします。CameraCaptureTaskをShowメソッドで表示します。

    Private Sub CameraGo(sender As Object, e As EventArgs)
      TryCast(ApplicationBar.Buttons(1), ApplicationBarIconButton).IsEnabled = True
      myCameraTask.Show()
    End Sub

「フロッピー」アイコンの「保存」ボタンがクリックされた時の処理

「フォルダ」アイコンの「データ一覧」ボタンの使用を可能にします。
メンバ変数imageFileNameに現在の、年月日時間分秒.jpgファイルの名前を格納します。メンバ変数recordDateに現在の、年/月/日/時間:分:秒の値を格納します。
CaptureImageメソッドで、ファインダーに表示される現在のイメージのフル解像度キャプチャを開始します。
例外が発生した場合は別スレッドで、メッセージを表示します。

    Private Sub SaveGo(sender As Object, e As EventArgs)
      If myCamera Is Nothing = False Then
        Try
          TryCast(ApplicationBar.Buttons(2), ApplicationBarIconButton).IsEnabled = True
          imageFileName = DateTime.Now.ToString("yyyyMMddHHmmss") & ".jpg"
          recordDate = DateTime.Now.ToString("yyyy/MM/dd/HH:mm:ss")
          myCamera.CaptureImage()
        Catch
          Me.Dispatcher.BeginInvoke(Sub() MessageBox.Show("エラーが発生しましたが、データは保存されました。"))
        End Try
      End If
    End Sub

「フォルダ」アイコンの「データ一覧」ボタンがタップされ他時の処理

これから作成する、DataIchiranPage.xamlに遷移します。

    Private Sub ListGo(sender As Object, e As EventArgs)
      NavigationService.Navigate(New Uri("/DataIchiranPage.xaml", UriKind.Relative))
    End Sub
  End Class
  • 写真を切りぬいて新しい写真を撮影するサンプル

四国のSOHO。薬師寺国安(VBプログラマ)と、薬師寺聖(デザイナ、エンジニア)によるコラボレーション・ユニット。1997年6月、Dynamic HTMLとDirectAnimationの普及を目的として結成。共同開発やユニット名義での執筆活動を行う。XMLおよび.NETに関する著書や連載多数。最新刊は「Silverlight実践プログラミング」両名とも、Microsoft MVP for Development Platforms - Client App Dev (Oct 2003-Sep 2012)。http://www.PROJECTKySS.NET/

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています