Friday, 11 April 2014

Using the CameraCaptureUI class in a C++/CX Windows Store app

Previously I extended the C# sample photo viewer app by integrating capturing photos into the app.

This blog posts extends my C++/CX sample photo viewer app by integrating capturing photos into the app. It uses the CameraCaptureUI class to capture photos. This class provides a UI for capturing audio, video, and photos from a camera, and provides controls for cropping photos and videos, time-delayed capture, and for adjusting the camera’s settings such as resolution, audio device, brightness, and contrast.

Implementation

The MainPage now includes an AppBarButton that allows the user to take a photo.

<local:MvvmPage.BottomAppBar>
    <CommandBar HorizontalAlignment="Right">
        <AppBarButton Command="{Binding TakePhotoCommand}"
                      Label="Take Photo"
                      Icon="Camera">
        </AppBarButton>
    </CommandBar>
</local:MvvmPage.BottomAppBar>

The AppBarButton binds to the TakePhotoCommand in the MainPageViewModel class. The TakePhotoCommand property is a DelegateCommand that is initialised in the MainPageViewModel constructor to execute the TakePhoto method when the AppBarButton is selected.

void MainPageViewModel::TakePhoto(Object^ parameter)
{
    auto capturedFile = make_shared<StorageFile^>(nullptr);
 
    m_captureService->CapturePhotoAsync().then([capturedFile](StorageFile^ file)
    {
        assert(IsBackgroundThread());
        if (file == nullptr)
        {
            cancel_current_task();
        }
        (*capturedFile) = file;
        return file->CopyAsync(KnownFolders::PicturesLibrary, file->Name, NameCollisionOption::GenerateUniqueName);
    }, task_continuation_context::use_arbitrary()).then([capturedFile](StorageFile^ file)
    {
        assert(IsBackgroundThread());
        return (*capturedFile)->DeleteAsync();
    }, task_continuation_context::use_arbitrary()).then([this]()
    {
        assert(IsMainThread());
        OnPropertyChanged("Photos");
    });
}

The TakePhoto member function invokes the CapturePhotoAsync member function in the CameraCaptureService class, which returns a StorageFile object representing the captured photo. This photo is saved on disk in the apps TempState directory. Therefore, the continuation chain copies the photo to the pictures library before deleting the version stored in the TempState directory. In the final continuation, property change notification is fired on the Photos property to refresh the thumbnails shown on the MainPage. This causes the GetPhotosAsync member function in the FileSystemRepository class to be executed to retrieve the thumbnail data.

Photo capture is handled by the CameraCaptureService, which implements the pure virtual member functions in the CaptureService abstract base class. The App class contains a member variable, m_captureService, of type CaptureService, which is instantiated as a shared pointer of type CameraCaptureService. This instance is created as a shared pointer so that there’s only a single instance of the CameraCaptureService class in the app, which is then passed between the required classes. The CameraCaptureService class instance is then exposed by the GetCaptureService member function, which is called by the ViewModelLocator constructor. The ViewModelLocator then passes the CameraCaptureService instance into the MainPageViewModel class from where camera capture is invoked.

task<StorageFile^> CameraCaptureService::CapturePhotoAsync()
{
    auto dialog = ref new CameraCaptureUI();
    dialog->PhotoSettings->MaxResolution = CameraCaptureUIMaxPhotoResolution::HighestAvailable;
    dialog->PhotoSettings->Format = CameraCaptureUIPhotoFormat::Jpeg;
    dialog->PhotoSettings->AllowCropping = true;
 
    return create_task(dialog->CaptureFileAsync(CameraCaptureUIMode::Photo)).then([](StorageFile^ file)
    {
        return file;
    });
}

Once the CameraCaptureUI instance is created, several PhotoSettings properties are specified to customize the capture experience. These are the maximum resolution, photo format, and a boolean value that enables the photo cropping interface. The CaptureFileAsync method is then called to launch the UI for capturing a photo from the camera. This method takes a CameraCaptureUIMode enumeration that specifies that the user can only capture a photo, rather than video. The user then has control over when to start the capture. When the CaptureFileAsync operation completes, a StorageFile object is returned, which is then returned to the calling method (in this case the TakePhoto method of the MainPageViewModel class). The UI used by the CameraCaptureUI class is shown in the following screenshot.

screenshot_04082014_115643[2]

The overall effect is that when the user takes a photo, the app returns to the thumbnail view on the MainPage. The thumbnails are then refreshed, with the thumbnail for the new photo appearing.

Summary

This blog post has extended the sample app by further by integrating capturing photos into the app. It uses the CameraCaptureUI class to capture a photo. When the user takes a photo the app returns to the thumbnail view on the MainPage. The thumbnails are then refreshed, with the thumbnail for the new photo appearing.

The sample app can be downloaded here.

No comments:

Post a comment