写真をハート型に切り抜いて撮影するサンプル
今回紹介するのは、写真をハート型などの形に添って切り抜いたような処理をするサンプルです。カメラアプリや、写真をデコレーションするアプリなどの処理として使うことができます。
はじめに動作内容を説明します。
- プログラムを立ち上げると、ハート型のフレーム内にカメラが表示される。
- 「カメラ」アイコンをタップするとシャッターが切られ、被写体が写し取られる。
- 画像とXMLファイルを保存した旨のメッセージが表示される。
Marketplaceで公開中の「どこカメ」 に使用しています。
この時点で「フォルダ」アイコンが使用可能になり、タップすると映した画像の一覧が表示されます。一覧から好きな画像を選んでタップすると、コンテキストメニューが表示され、「データの削除」をタップすると画像が削除されます(図1)。
注意して欲しいのはカメラの解像度を640×480ピクセルに設定することです。規定値のままでは解像度が大きい(4128×3096ピクセル)ため、ストレージを圧迫してしまいます。
(*)このサンプルはエミュレーターでも動作はしますが、実機での動作確認を原則とします。
図1:ハート型のフレーム内にカメラが表示され、「カメラ」アイコンで写真を撮る。「フォルダ」アイコンで写した画像の一覧が表示される。任意の画像をタップするとコンテキストメニューが表示される。メニュー内の「データの削除」をタップすると、任意の画像が削除される(クリックで拡大) |
サンプル一式は、会員限定特典としてダウンロードできます。記事末尾をご確認ください。
実機(IS12T)で動かした動画はこちらです。
プロジェクトの作成
VS 2010のメニューから[ファイル(F)/新規作成(N)/プロジェクト(P)]と選択します。
次に、「Windows Phone アプリケーション」を選択して、「名前(N)」に任意のプロジェクト名を指定します。ここでは「WP71_ImageInTheCameraFrame」という名前を付けています。Windows Phoneのバージョンは7.1を選択します。
ソリューションエクスプローラー内にImageというフォルダを作り、ハート型の画像を追加しておきます。またIconsというフォルダを作り、C:\Program Files\Microsoft SDKs\Windows Phone\v7.1\Icons\darkにある、appbar.feature.camera.rest.pngとappbar.folder.rest.pngを追加しておきます。
名前もcamera.pngとfolder.pngに変更しておきます。これら2つの画像は、プロパティから「ビルドアクション」にコンテンツを指定してください。デフォルトのResourceのままでは画像は表示されませんので、注意してください。
VS2010メニューの「プロジェクト(P)/参照の追加(R)」と選択して、Microsoft.Phone.Controls.ToolkitとSystem.Xml.Linqを追加しておいてください。
Windows Phone Toolkitは下記URLよりダウンロードできますので、インストールしておいてください。
→参照:Windows Phone Toolkit - Nov 2011 (7.1 SDK)
MainPage.xamlの編集とコントロールの追加
x:NameがPageTitleというTextBlockのTextプロパティに「ハート型フレームを配置してカメラで撮る」と指定します。Styleプロパティに指定されているPhoneTextTitle1Styleでは文字サイズが大きすぎるので、PhoneTextTitle3Styleを指定して小さく表示します。
ツールボックスからInkPresenterコントロールを配置し、その子要素としてImageコントロールを2個と、Rectangleコントロールを1個配置します。一番手前に配置したImageコントロールのSourceプロパティにImageフォルダ内のハート型の画像を指定します。書き出されるXAMLコードをリスト1のように編集します。
リスト1 編集されたXAMLコード(MainPage.xaml)
(1)
(2)
(3)
(4)最前面に表示される
(5)
<phone:PhoneApplicationPage x:Class="WP71_ImageInTheCameraFrame.MainPage" 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" mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="696" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" SupportedOrientations="Portrait" Orientation="Portrait" shell:SystemTray.IsVisible="True"> <!--LayoutRoot は、すべてのページ コンテンツが配置されるルート グリッドです--> <Grid x:Name="LayoutRoot" Background="White"> <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="PageTitle" Text="?型フレームを配置してカメラで撮る" Foreground="Navy" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle3Style}"/> </StackPanel> <!--ContentPanel - 追加コンテンツをここに入力します--> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,12"> <InkPresenter x:Name="InkPresenter1" Margin="0,67,0,122" Width="388" Background="White"> ■(1) <Image Height="386" Name="ellipseImage" Stretch="Fill" Width="434" Canvas.Left="383" Canvas.Top="-7"> ■(2) <Image.RenderTransform> ■(2) <CompositeTransform Rotation="90" CenterX="0.5" CenterY="0.5"/> ■(2) </Image.RenderTransform> ■(2) </Image> ■(2) <Rectangle Height="386" Name="Rectangle1" Stroke="Black" StrokeThickness="1" Width="436" UseLayoutRounding="False" d:LayoutRounding="Auto" Canvas.Left="-128.61" Canvas.Top="68.592"> ■(3) <Rectangle.RenderTransform> ■(3) <CompositeTransform Rotation="90" CenterX="0.5" CenterY="0.5" /> ■(3) </Rectangle.RenderTransform> ■(3) <Rectangle.Fill> ■(3) <VideoBrush x:Name="myVideoBrush" /> ■(3) </Rectangle.Fill> ■(3) </Rectangle> ■(3) <Image Name="Image1" Stretch="Fill" Width="390" Canvas.Left="-3" Canvas.Top="-11" Height="438" Source="/WP71_ImageInTheCameraFrame;component/Image/heart_pink.png" /> </InkPresenter> ■(4) </Grid> </Grid> <!--ApplicationBar の使用法を示すサンプル コード--> <phone:PhoneApplicationPage.ApplicationBar> <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True"> <shell:ApplicationBarIconButton IconUri="/Icons/camera.png" Text="シャッターを切る" Click="GoShutter"/> ■(5) <shell:ApplicationBarIconButton IconUri="/Icons/folder.png" Text="データ一覧" Click="GoIchiran"/> ■(5) </shell:ApplicationBar> </phone:PhoneApplicationPage.ApplicationBar> </phone:PhoneApplicationPage>
レイアウト図は図2のようになります。
図2:各コントロールを配置した(クリックで拡大) |
次に、MainPage.xamlを展開して表示される、MainPage.xaml.vbをダブルクリックしてリスト2のコードを記述します。
ロジックコードを記述する
リスト2 (MainPage.xaml.vb)
Option Strict On Imports Microsoft.Devices
仮想ファイルシステムの作成および、使用するための型が含まれている、System.IO.IsolatedStorage名前空間をインポートします。分離ストレージによって、安全なクライアント側のストレージが提供されます。
Imports System.IO.IsolatedStorage Imports System.Windows.Media.Imaging Imports System.IO Imports System.Xml.Linq Imports Microsoft.Phone Partial Public Class MainPage Inherits PhoneApplicationPage ' コンストラクター Public Sub New() InitializeComponent() End Sub Dim bufferValue As Integer = 5119
myPhotoCameraをPhotoCameraクラスのメンバ変数として宣言します。PhotoCameraクラスには、カメラアプリケーションの基本カメラ機能を提供し、イメージ キャプチャ、フォーカス、解像度、フラッシュ モードなどの機能を有効にして構成するためのメンバが含まれています。
Dim myPhotoCamera As PhotoCamera Dim imageFileName As String Dim recordDate As String
ページがアクティブになった時呼び出されるメソッド
変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。DirectoryExistsメソッドでImageInFrameというフォルダが存在しているかどうかをチェックし、存在していない場合は、CreateDirectoryメソッドでImageInFrameというフォルダを作成します。
PhotoCameraクラスの新しいインスタンスを作成し、myVideoBrushという名前のVideoBrushにSetSourceメソッドでmyPhotoCameraオブジェクトを指定します。
AddHandlerステートメントで、カメラ オブジェクトが初期化されたときに発生する、Initializedイベントにイベントハンドラを追加します。Succeededプロパティで、カメラ操作が失敗した時は処理を抜けます。
それ以外は変数myResolutionを使って、AvailableResolutionsプロパティで、使用できるカメラの解像度を参照します。カメラでキャプチャしたイメージの解像度を設定できるResolutionプロパティに、先に取得したmyResolutionの一番先頭の解像度を指定します。一番先頭の解像度は640×480ピクセルです。
Try~Catch~End Tryで例外処理を行います。例外が発生した場合は、メッセージを表示します。別スレッドからの表示になります。
Path.CombineでImageInFrameというフォルダとImageList.xmlというファイルを連結し、変数xmlFilePathに格納します。ImageList.xmlファイルがImageInFrameフォルダに存在する場合は、「データ一覧」のフォルダアイコンの使用を可能にし、それ以外は使用不可としておきます。
AddHandlerステートメントで、イメージが利用可能な場合に発生する、CaptureImageAvailableイベントに、myPhotoCamera_CaptureImageAvailableイベントハンドラを追加します。
Protected Overrides Sub OnNavigatedTo(e As System.Windows.Navigation.NavigationEventArgs) Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication If storage.DirectoryExists("ImageInFrame") = False Then storage.CreateDirectory("ImageInFrame") End If myPhotoCamera = New PhotoCamera myVideoBrush.SetSource(myPhotoCamera) AddHandler myPhotoCamera.Initialized, Sub(cameraSender As Object, cameraArgs As CameraOperationCompletedEventArgs) Try If cameraArgs.Succeeded = False Then Exit Sub Else Dim myResolution As IEnumerable(Of Size) = myPhotoCamera.AvailableResolutions myPhotoCamera.Resolution = myResolution.First End If Catch Deployment.Current.Dispatcher.BeginInvoke(Sub() MessageBox.Show("カメラが起動するまでお待ちください。") End Sub) End Try End Sub Dim xmlFilePath As String = Path.Combine("ImageInFrame", "ImageList.xml") If storage.FileExists(xmlFilePath) = True Then TryCast(ApplicationBar.Buttons(1), ApplicationBarIconButton).IsEnabled = True Else TryCast(ApplicationBar.Buttons(1), ApplicationBarIconButton).IsEnabled = False End If AddHandler myPhotoCamera.CaptureImageAvailable, AddressOf myPhotoCamera_CaptureImageAvailable MyBase.OnNavigatedTo(e) End Sub
フレームで切り抜いて撮影するサンプル