写真をハート型に切り抜いて撮影するサンプル
前ページからの続きです。
イメージが利用可能な場合に発生する処理
変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。Path.CombineでImageInFrameというフォルダとimageFileNameに格納されている画像名とを連結し、変数filePathに格納します。同様にImageInFrameというフォルダとImageList.xmlというファイル名を連結して、変数xmlFilePathに格納します。
別スレッドで以下の処理を行います。
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を指定) ,書き込む最大バイト数,)
変数imageStorageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数stream変数を用意し、IsolatedStorageFile.OpenFileメソッドでfilePathに格納しているファイルを、指定したモード、指定したファイルアクセスモードで開きます。
WriteableBitmap型の変数imageSourceを宣言し、PictureDecoder.DecodeJpeg(stream)で、撮った写真をJPEGファイルとしてWriteableBitmapオブジェクトにデコードします。PictureDecoder.DecodeJpegメソッドはMicrosof.Phone名前空間に属しています。WriteableBitmapクラスは書き込み更新することのできるBitmapSourceを提供するクラスです。ellipseImageのSourceプロパティにimageSourceオブジェクトを指定します。一度分離ストレージに保存した画像を読み込んで、ellipseImageに表示しています。
InkPresenterで初期化された、新しいWriteableBitmapのインスタンスを作成します。ellipseImageに画像を表示させた時点で、分離ストレージのImageInFrameフォルダ内の保存した画像を、DeleteFileメソッドで削除しておきます。
分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数Stream変数を用意し、IsolatedStorageFile.CreateFileメソッドで分離ストレージのImageInFrameフォルダ内に、変数imageFileNameに格納されている画像ファイルを作成します。
Extensions.SaveJpegメソッドで、WriteableBitmapオブジェクトを、JPEGストリームにエンコードします。これは、JPEGファイルのターゲットとなる幅と高さを設定するためのパラメータを持っています。書式は下記の通りです。
Extensions.SaveJpeg(WriteableBitmapオブジェクト,イメージデータストリーム,WriteableBitmapオブジェクトのPixelWidth, WriteableBitmapオブジェクトのPixelHeight,0(固定),0~100の間の写真の品質(70以上を指定))
分離ストレージのImageInFrame内に指定した画像ファイルが作成されます。
変数xmlStorageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。ImageInFrameというフォルダ内にImageList.xmlが存在していない場合は、Visual Basicの埋め込み式を用いて、XML宣言とルート要素、その子要素として、属性に”記録日”を指定し、その子要素として要素を作成します。埋め込み式の構文である を用いて“記録日”属性の値にメンバ変数recordDateの値を、要素の値に、メンバ変数imageFileNameの値を指定します。これは ASP.NET で使用される構文と同じです。
分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数xmlStreamを用意し、IsolatedStorageFile.CreateFileメソッドで、分離ストレージ内にxmlFilePath変数の持っているフォルダ名付きXMLファイルを作成します。
ImageList.xmlファイルが存在する場合は、新しいStreamWriter 生成し、IsolatedStorageFile.OpenFileメソッドで、指定したファイルアクセスを使用して指定したモードでファイルを開き、初期化します。Writeメソッドで埋め込み式のXMLをストリームに書き込みます。別スレッドで、保存した旨のメッセージを表示し、「データ一覧」のフォルダアイコンの使用を可能にします。
次は、既にImageInFrameフォルダ内にImageList.xmlが存在する場合の処理です。
IsolatedStorageFileクラスのOpenFileメソッドで、ImageInFrameフォルダ内のImageList.xmlファイルを、指定したファイルアクセスを使用して指定したモードで開きます。開いたファイルをStreamReaderで読み込んだら、ReadToEndメソッドでファイルの最後まで読み取り、変数readXmldocに格納しておきます。読み込んだXMLテキストをParseメソッドでXElementとして読み込みます。
追加する要素を、埋め込み式を用いて生成し、”記録名”属性の値や、の内容テキストを指定します。新しく生成したXML要素を、読み込んだXMLにAddメソッドで追加します。IsolatedStorageFileStreamを閉じます。
ImageList.xmlファイルが存在する場合は、新しいStreamWriter 生成し、IsolatedStorageFile.OpenFileメソッドで、指定したファイルアクセスを使用して指定したモードでファイルを開きます。新しいXML要素の追加されたXMLを、Writeメソッドでストリームに書き込みます。別スレッドで、保存した旨のメッセージを表示し、「データ一覧」のフォルダアイコンの使用を可能にします。
Private Sub myPhotoCamera_CaptureImageAvailable(sender As Object, e As ContentReadyEventArgs) Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication Dim filePath As String = Path.Combine("ImageInFrame", imageFileName) Dim xmlFilePath As String = Path.Combine("ImageInFrame", "ImageList.xml") Deployment.Current.Dispatcher.BeginInvoke(Sub() 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 Dim imageStorage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication Using stream As IsolatedStorageFileStream = imageStorage.OpenFile(filePath, FileMode.Open, FileAccess.Read) Dim imageSource As WriteableBitmap = PictureDecoder.DecodeJpeg(stream) ellipseImage.Source = imageSource End Using Dim myWriteableBitmap As WriteableBitmap myWriteableBitmap = New WriteableBitmap(InkPresenter1, Nothing) If imageStorage.FileExists(Path.Combine("ImageInFrame", imageFileName)) = True Then imageStorage.DeleteFile(Path.Combine("ImageInFrame", imageFileName)) End If Using Stream As IsolatedStorageFileStream = storage.CreateFile(Path.Combine("ImageInFrame", imageFileName)) System.Windows.Media.Imaging.Extensions.SaveJpeg(myWriteableBitmap, Stream, 480, 640, 0, 85) End Using 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"?> <思い出> <風景 記録日=<%= recordDate %>> <画像名><%= imageFileName %></画像名> </風景> </思い出> 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 Me.Dispatcher.BeginInvoke(Sub() MessageBox.Show("画像とXMLファイルを保存しました。") TryCast(ApplicationBar.Buttons(1), ApplicationBarIconButton).IsEnabled = True 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 = <風景 記録日=<%= recordDate %>> <画像名><%= imageFileName %></画像名> </風景> 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) Me.Dispatcher.BeginInvoke(Sub() MessageBox.Show("画像とXMLファイルを保存しました。") TryCast(ApplicationBar.Buttons(1), ApplicationBarIconButton).IsEnabled = True End Sub) End Using End If End Using End If End Sub
カメラのアイコンがタップされた時の処理
PhotoCameraにファインダーに表示されるイメージがある場合は、現在の年月日時間分秒.jpgファイル名を作成し、メンバ変数imageFileNameに格納しておきます。また、現在の何年何月何日何時何分何秒という文字列を、メンバ変数recordDateに格納しておきます。
CaptureImageメソッドで、ファインダーに表示される現在のイメージの、フル解像度キャプチャを開始します。例外が発生した場合は別スレッドで、メッセージを表示します。
Private Sub GoShutter(sender As System.Object, e As EventArgs) If myPhotoCamera Is Nothing = False Then Try imageFileName = DateTime.Now.ToString("yyyyMMddHHmmss") & ".jpg" recordDate = DateTime.Now.ToString("yyyy年MM月dd日HH時mm分ss秒") myPhotoCamera.CaptureImage() Catch Me.Dispatcher.BeginInvoke(Sub() MessageBox.Show("エラーが発生しましたが、データは保存されますのでご安心ください。")) End Try End If End Sub
フォルダアイコンがタップされた時の処理
RemoveHandlerステートメントで、イベントとイベント ハンドラの関連付けを解除します。これから作成する、DataIchiranPage.xamlに遷移します。
Private Sub GoIchiran(sender As Object, e As EventArgs) RemoveHandler myPhotoCamera.CaptureImageAvailable, AddressOf myPhotoCamera_CaptureImageAvailable myPhotoCamera = Nothing NavigationService.Navigate(New Uri("/DataIchiranPage.xaml", UriKind.Relative)) End Sub End Class
「Windows Phone 縦向きのページ」(DataIchiranPage.xaml)の作成
VS2010メニューの「プロジェクト(P)/新しい項目の追加(W)」と選択し、「Windows Phone 縦向きのページ」を選択します。「名前(N)」にはDataIchiranPage.xamlと入力します。
DataIchiranPage.xamlの編集とコントロールの配置
x:NameがApplicationTitleというTextBlockのTextプロパティに「データの一覧」と指定します。ツールボックスからListBoxコントロールを1個配置します(図3)。
図3:ListBoxコントロールを配置した(クリックで拡大) |
書き出されるXAMLをリスト3のように編集します。
リスト3 編集されたXAMLコード(DataIchiranPage.xaml)
(1)
(2)(1)で定義したテンプレートを、
<phone:PhoneApplicationPage x:Class="WP71_ImageInTheCameraFrame.DataIchiranPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" SupportedOrientations="Portrait" Orientation="Portrait" mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480" shell:SystemTray.IsVisible="True"> <phone:PhoneApplicationPage.Resources> ■(1) <DataTemplate x:Key="ListBoxTemplate"> ■(1) <StackPanel Margin="20"> ■(1) <Image Width="240" Height="320" Stretch="Fill" Source="{Binding imageFileName}" Margin="35"/> ■(1) <TextBlock Text="{Binding recordDate}"/> ■(1) </StackPanel> ■(1) </DataTemplate> ■(1) </phone:PhoneApplicationPage.Resources> ■(1) <!--LayoutRoot は、すべてのページ コンテンツが配置されるルート グリッドです--> <Grid x:Name="LayoutRoot" Background="Transparent"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!--TitlePanel は、アプリケーション名とページ タイトルを格納します--> <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28"> <TextBlock x:Name="ApplicationTitle" Text="データの一覧" Style="{StaticResource PhoneTextNormalStyle}"/> </StackPanel> <!--ContentPanel - 追加コンテンツをここに入力します--> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <ListBox Height="663" Margin="12,27,18,0" Name="ListBox1" VerticalAlignment="Top" ItemTemplate="{StaticResource ListBoxTemplate}"/> ■(2) </Grid> </Grid> <!--ApplicationBar の使用法を示すサンプル コード--> ~コード略~ </phone:PhoneApplicationPage>
フレームで切り抜いて撮影するサンプル