PR

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

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
Think IT会員限定特典
  • 写真を切りぬいて新しい写真を撮影するサンプル

四国の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のWebサイトにログインすることでさまざまな限定特典を入手できるようになります。

Think IT会員サービスの概要とメリットをチェック

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