×
Namespaces

Variants
Actions

How to Use the Nokia Imaging SDK to create a "Photo Cookbook" for Windows Phone 8 Devices

From Nokia Developer Wiki
Jump to: navigation, search

This article walks the reader through the steps required to apply multiple Nokia Imaging SDK Filters on a single image, and then store that "recipe" for later images.

SignpostIcon XAML 40.png
WP Metro Icon WP8.png
Article Metadata
Code ExampleTested with
SDK: Windows Phone 8.0 SDK, Nokia Imaging SDK Beta 1
Devices(s): Nokia Lumia 920
Compatibility
Platform(s):
Windows Phone 8
Platform Security
Capabilities: ID_CAP_MEDIALIB_PHOTO
Article
Created: matthewthepc (15 Jul 2013)
Last edited: hamishwillee (14 Oct 2013)

Contents

Introduction

The new Nokia Imaging SDK provides (among other features) a simple way for developers to apply filters such as "Antique," "Grayscale," and "Sketch." These filters can produce a noticeable change in the photo even when you use them separately, but when you combine them, you can create a much more interesting result.

With this app, we'll be expanding on the basic ideas of the Filter Explorer app to allow users to store filter "recipes" which can then be applied to future filters automatically.

The app will allow users to:

  • either take a new picture or use a previously taken picture.
  • add any number of filters, crops, or modifications to the initial picture.
  • save modifications (and the order they were made) in a recipe, which can be reused later.
  • save a copy of the filtered photo.
  • launch our app as a lens from the Windows Phone's stock Camera app
  • launch our app as a photo editor from (almost) anywhere in the OS.


By the end of this tutorial, you will understand how to use the Nokia Imaging SDK to start create your own real-world imaging applications.

This tutorial is divided into three main parts. In the first part, we set up the backend code and Windows Phone project, including any abstractions and custom classes. For the second part, we implement those abstractions page-by-page, eventually creating a compelling app ready to be published in the Windows Phone Store. Finally, we'll review some of the features of the Nokia Imaging SDK we used, and how they can be used more generally in different apps.

Prerequisites

In order to follow along with this tutorial, you'll need to have a copy of Visual Studio compatible with the Windows Phone 8 SDK. You'll also need to have the Windows Phone 8 SDK installed and working with your version of Visual Studio. If you don't have either of these, you can download a copy of Visual Studio Express 2012 for Windows Phone here. I'll be using Visual Studio 2013, but you should be able to follow along easily in Visual Studio 2012 or 2010.

Setup

Create the App

The first thing we need to do is create a new Windows Phone app - to do this, start Visual Studio and select New Project.... In the window that pops up, make sure you have expanded the "Visual C#" tree (Express users may not need to do this), and then selected "Windows Phone." Select Windows Phone App in the main panel, then under Name type "Photo Cookbook." Your window should look something similar like this before pressing "OK:"

Create the new project

Tip.pngTip: If prompted, make sure you're targeting Windows Phone 8.

Reference the Nokia Imaging SDK

Now that we have the app created, we need to add the Nokia Imaging SDK. The Nokia Imaging SDK is available as a NuGet package. For installing the SDK, visit Adding Libraries to the Project(Nokia Developer Library), and follow the instructions under "Installing the SDK and including the libraries to a project using NuGet".

As of writing this article, it looks difficult (if not impossible) to obtain a list of available filters and their names from Nokia Imaging SDK, which we'll need to allow the user to pick from a list of possible filters. To overcome this issue, we'll need to hand-code all of the filters in FilterFactory to a list of filters, or we can use the FiltersModel which is available in the Filter Explorer application.

Note.pngNote: Another way you could go around this problem would be using reflection, but since FilterFactory, and the Nokia Imaging SDK itself, is oriented around an interface (IFilter) it gets a little difficult. If you manage to work it out, please update this (or start a new article!).

Download Filter Explorer application, then copy the "Models" folder (or just Filter- and FiltersModel.cs) to the Photo_Cookbook project. By default, FiltersModel separates the filters into artistic and enhancement filters, but we'll also want to have a list of all filters. To do this, we'll replace some of the code from FiltersModel.cs.

Replace the first couple of lines (until RandomFilter()) with the following:

using System;
using System.Collections.Generic;
 
namespace ImageProcessingApp.Models
{
//http://stackoverflow.com/questions/720609/merge-two-object-lists-with-linq
static class Ext
{
public static IEnumerable<FilterModel> Append(this IEnumerable<FilterModel> source, IEnumerable<FilterModel> second)
{
foreach (FilterModel t in source) { yield return t; }
foreach (FilterModel t in second) { yield return t; }
}
}
public class FiltersModel
{
#region Properties
 
public List<FilterModel> ArtisticFilters { get; private set; }
public List<FilterModel> EnhancementFilters { get; private set; }
public List<FilterModel> AllFilters { get; private set; }
 
#endregion
 
public FiltersModel(bool LoadAll = true)
{
LoadArtisticFilters();
LoadEnhancementFilters();
if (LoadAll)
{
AllFilters = ArtisticFilters;
AllFilters.Append(EnhancementFilters);
}
}

Essentially, we've extended IEnumerable<FilterModel> to give it an Append() method, added an AllFilters list, and then loaded both of the filter lists into AllFilters using the Append() extension.

Main Backend

Great, now that we've gotten the basic stuff out of the way, we can start working on the actual app. You should be looking at a window with a MainPage.xaml tab open and a few other things made for us by Visual Studio. Please close any currently open tabs (there should be an X next to the tab name), and we'll start working on the backend.

Capabilities, Extensions, and more

Since we want to be able to register our app as a lens and photo picker, we have to have the right capabilities declared in our WMAppManifest.xml. In the Solution Explorer, expand "Properties" and double-click on WMAppManifest.xml. Flipping over to the "Capabilities" tab, ensure that ID_CAP_MEDIALIB_PHOTO is checked, then save and close WMAppManifest.xml before right-clicking it in the Solution Explorer and selecting View Code. Copy the following snippet after the end </Tokens> tag:

<Extensions>
<Extension ExtensionName="Camera_Capture_App"
ConsumerID="{5B04B775-356B-4AA0-AAF8-6491FFEA5631}"
TaskID="_default" />
<Extension ExtensionName="Photos_Extra_Image_Editor"
ConsumerID="{5B04B775-356B-4AA0-AAF8-6491FFEA5632}"
TaskID="_default" />
</Extensions>

Basically, we're telling the OS that we support being launched as a lens or a photo editor, and to use the "_default" task (which should navigate to MainPage.xaml, you can make sure under the <Tasks> element in the same file).

We need a global place to store our main ImagingSDK and a FiltersModel (so that we don't have to initialize a new one every time we need it). Let's add them as the first lines below our App class, in App.xaml.cs:

public partial class App : Application
{
public static ImagingSDK CurrPhoto;
public static readonly ImageProcessingApp.Models.FiltersModel Filters = new ImageProcessingApp.Models.FiltersModel();

Further down, in Application_Launching, we'll check whether we have our virtual "Cookbook" stored in ApplicationSettings, and if not we'll add it.

// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
if (!IsolatedStorageSettings.ApplicationSettings.Contains("Recipes"))
{
IsolatedStorageSettings.ApplicationSettings.Add("Recipes", new Dictionary<string, List<string>>());
IsolatedStorageSettings.ApplicationSettings.Save();
}
}

Note.pngNote: The Nokia Imaging SDK comes with a FilterGroup, which is essentially a native implementation of recipes in the Nokia Imaging SDK. The reason we're not using that here is, since the SDK is so focused on interfaces, most serialization libraries (including the default one used with IsolatedStorageSettings and JSON.Net) can't serialize it. If you're not using any kind of serialization, or if you find a library that can serialize a FilterGroup, feel free to use that.

ImagingSDK Wrapper

Press Shift + Alt + C, then add a new class file with the name ImagingSDK.cs. Since we want to keep track of what filters have been applied to an image, we'll create helper functions in the ImagingSDK class to replace the default ones from EditingSession.

First, change class class ImagingSDK to class ImagingSDK : IDisposable, since we should have a way to dispose of our EditingSession once we're done with it.

Within ImagingSDK, we'll need the following:

  • A raw EditingSession, so we can manipulate the image.
  • A List<string> of filter names, so we can save the recipes in ApplicationSettings.
  • AddFilter(), AddFilters(), Undo(), and Reset() functions to update our List<string> whenever filters are added or removed on the image.
  • A StringsToFilterModels() function to convert our List<string> into a List<FilterModel>.
  • And, finally, a Dispose() method to dispose of our EditingSession

Most of the code is self-explanatory, but we'll go over the parts specific to the Imaging SDK.

using ImageProcessingApp.Models;
using Nokia.Graphics.Imaging;
using System;
using System.Collections.Generic;
using System.Linq;
 
namespace Photo_Cookbook
{
public class ImagingSDK : IDisposable
{
public EditingSession RawSession;
public List<string> Filters = new List<string>();
public ImagingSDK(Windows.Storage.Streams.IBuffer Input)
{
RawSession = new EditingSession(Input);
}
public void AddFilter(FilterModel ToAdd)
{
foreach (var Filter in ToAdd.Components)
{
RawSession.AddFilter(Filter);
}
Filters.Add(ToAdd.Name);
}
public void AddFilters(List<FilterModel> Recipe)
{
foreach (FilterModel filter in Recipe)
{
AddFilter(filter);
}
}
public void Undo()
{
if (RawSession.CanUndo())
{
RawSession.Undo();
Filters.Remove(Filters.Last());
}
}
public void Reset()
{
if (RawSession.CanUndo())
{
RawSession.UndoAll();
Filters.RemoveAll(a => true);
}
}
public static List<FilterModel> StringsToFilterModels(List<string> ToParse)
{
return App.Filters.AllFilters.Where(a => ToParse.Contains(a.Name)).ToList<FilterModel>();
}
public void Dispose()
{
if (RawSession != null)
{
RawSession.Dispose();
}
}
}
}

ImagingSDK accepts a Buffer as input, which is one of the two inputs an EditingSession accepts (the other being Bitmap) as a parameter, then initializing RawSession with that. One thing to remember when using Nokia's FilterModel is that the filter itself is stored in the FilterModel's Components, so you should loop over each of those when adding a FilterModel. This theoretically allows for multiple IFilters per FilterModel, but as far as I know, all FilterModels currently only have one Component.

Pages

We'll have three pages

  • FilterPicker.xaml
  • Cookbook.xaml and
  • MainPage.xaml.
Photo Cookbook Codemap.PNG

MainPage.xaml is where the user sees changes made to the photo, and can undo filters and save recipes. It also allows the user to navigate to FilterPicker.xaml and Cookbook.xaml, where they can add filters and recipes to the current image.

MainPage

In MainPage, we'll need three main parts - an <Image> to hold the currently editing image, a <Grid> to hold the interface for saving a recipe, and an app bar to contain buttons for adding/removing filters and saving files and recipes.

Frontend

Replace everything within <phone:ApplicationPage> with the following:

<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Image x:Name="NowEditing"></Image>
<Grid x:Name="SaveRecipeBox" Visibility="Collapsed">
<TextBlock Margin="113,148,120,497" FontSize="36">Recipe Name</TextBlock>
<TextBox Margin="10,203,10,0" Height="83" VerticalAlignment="Top" x:Name="RecipeName" FontSize="36"></TextBox>
<Button FontSize="36" Margin="10,291,173,310" Click="SaveRecipe">Save Recipe</Button>
<Button FontSize="36" Margin="274,291,10,310" Click="CancelRecipe">Cancel</Button>
</Grid>
</Grid>
 
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar Mode="Default" Opacity="1.0" IsMenuEnabled="True" IsVisible="True">
<shell:ApplicationBarIconButton Click="Add_Click" IconUri="/Images/add.png" Text="add filter" />
<shell:ApplicationBarIconButton Click="Save_Click" IconUri="/Images/save.png" Text="save" />
 
<shell:ApplicationBar.MenuItems>
<shell:ApplicationBarMenuItem x:Name="UndoBtn" Click="Undo_Click" Text="Undo" IsEnabled="False"/>
<shell:ApplicationBarMenuItem x:Name="ResetBtn" Click="UndoAll_Click" Text="Reset" IsEnabled="False"/>
<shell:ApplicationBarMenuItem Click="SaveRecipe_Click" Text="Save Recipe"/>
<shell:ApplicationBarMenuItem Click="ApplyRecipe_Click" Text="Apply Recipe"/>
</shell:ApplicationBar.MenuItems>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

Tip.pngTip: In the full source code, the images for the Add and Save buttons are from the great Syncfusion Metro Studio product, a free collection of over 2500 Metro-style icons.

Backend

Now open up MainPage.xaml.cs (you can access the .xaml.cs files by clicking the arrows next to the .xaml file in Solution Explorer). We'll need to override OnNavigatedTo, along with providing event handlers for the buttons and menu items we added in the frontend.

Add the following within the MainPage class:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
while (NavigationService.RemoveBackEntry() != null); //remove previous app pages from memory
base.OnNavigatedTo(e);
if (e.Uri.ToString().Contains("ViewfinderLaunch"))
{
if (e.NavigationMode != NavigationMode.Back)
{
CameraCaptureTask CamTask = new CameraCaptureTask();
CamTask.Completed += PhotoPicker_Completed;
CamTask.Show();
}
}
else if (e.Uri.ToString().Contains("Action=EditPhotoContent")) //http://msdn.microsoft.com/en-US/library/windowsphone/develop/jj662932(v=vs.105).aspx
{
// Retrieve the photo from the media library using the FileID passed to the app.
MediaLibrary library = new MediaLibrary();
Picture photoFromLibrary = library.GetPictureFromToken(NavigationContext.QueryString["FileId"]);
// Create a BitmapImage object and add set it as the image control source.
// To retrieve a full-resolution image, use the GetImage() method instead.
BitmapImage bitmapFromPhoto = new BitmapImage();
bitmapFromPhoto.SetSource(photoFromLibrary.GetImage());
MemoryStream stream = new MemoryStream();
photoFromLibrary.GetImage().CopyTo(stream);
App.CurrPhoto = new ImagingSDK(stream.GetWindowsRuntimeBuffer());
UpdateImageAsync();
}
else if (NavigationContext.QueryString.ContainsKey("WasUpdated"))
{
UpdateImageAsync();
}
else if (App.CurrPhoto == null)
{
PhotoChooserTask PhotoPicker = new PhotoChooserTask();
PhotoPicker.Completed += PhotoPicker_Completed;
PhotoPicker.ShowCamera = true;
PhotoPicker.Show();
}
}

There are four main 'flows' we want to support with MainPage; as a lens, as a photo editor, coming back from Cookbook.xaml or FilterPicker.xaml; and as a regular app launch.

  • If it's being launched as a lens, the navigation string will contain ViewfinderLaunch, and we'll show a page where the user can take a picture.
  • If it's launching as a photo editor, the navigation string will contain Action=EditPhotoContent, and we can get the image from the FileId query string.
  • If the navigation string contains WasUpdated, we know it was coming from another page in the application, and we should refresh it.
  • Otherwise, if we're not already editing a photo, ask the user to choose a photo with the phone's default PhotoChooserTask.

Note.pngNote: Both CamTask and PhotoPicker use PhotoPicker_Completed as their Completed event handler, which is possible because they both return a PhotoResult

Next we'll add PhotoPicker_Completed and UpdateImageAsync, so we'll be able to receive and display the user's chosen (or captured) photo;

async void PhotoPicker_Completed(object sender, PhotoResult e)
{
//modified from FilterExplorer
if (e.TaskResult == TaskResult.OK)
{
MemoryStream stream = new MemoryStream();
e.ChosenPhoto.CopyTo(stream);
App.CurrPhoto = new ImagingSDK(stream.GetWindowsRuntimeBuffer());
await UpdateImageAsync();
}
else
{
App.Current.Terminate();
}
}
 
private async Task UpdateImageAsync()
{
//ionut @ http://stackoverflow.com/questions/5334574/applicationbariconbutton-is-null
Microsoft.Phone.Shell.ApplicationBarMenuItem Undo = ApplicationBar.MenuItems[0] as Microsoft.Phone.Shell.ApplicationBarMenuItem;
Microsoft.Phone.Shell.ApplicationBarMenuItem Reset = ApplicationBar.MenuItems[1] as Microsoft.Phone.Shell.ApplicationBarMenuItem;
Microsoft.Phone.Shell.ApplicationBarMenuItem SaveRecipe = ApplicationBar.MenuItems[2] as Microsoft.Phone.Shell.ApplicationBarMenuItem;
Microsoft.Phone.Shell.ApplicationBarMenuItem Filters = ApplicationBar.MenuItems[4] as Microsoft.Phone.Shell.ApplicationBarMenuItem;
if (!App.CurrPhoto.RawSession.CanUndo())
{
Undo.IsEnabled = false;
Reset.IsEnabled = false;
SaveRecipe.IsEnabled = false;
Filters.IsEnabled = false;
}
else
{
Undo.IsEnabled = true;
Reset.IsEnabled = true;
SaveRecipe.IsEnabled = true;
Filters.IsEnabled = true;
}
WriteableBitmap bitmp = new WriteableBitmap((int)App.CurrPhoto.RawSession.Dimensions.Width, (int)App.CurrPhoto.RawSession.Dimensions.Height);
await App.CurrPhoto.RawSession.RenderToWriteableBitmapAsync(bitmp);
NowEditing.Source = bitmp;
}

All PhotoPicker_Completed does is copy the chosen photo's stream to our global ImagingSDK as a Windows.Storage.Streams.IBuffer, then uses the UpdateImageAsync() function to display it to the user. UpdateImageAsync() is an awaitable, asynchronous method which gets a WritableBitmap from the EditingSession, and then sets that as the source of the NowEditing <Image>. It also checks whether there are any filters to undo, and disables or enables UndoBtn and ResetBtn accordingly.

Warning.pngWarning: Things on the App Bar are somewhat difficult to work with - you can't just reference them by their x:Name. Instead, we're using a workaround by finding the first and second buttons on the app bar and casting them to the correct type.

Tip.pngTip: In addition to the naming problems, AppBars will sometimes not update until you uninstall and reinstall the app (at least in debug mode). If you're getting errors around UpdateImageAsync, try to reinstall Photo Cookbook

To allow the user to add and remove filters and recipes, we'll add event handlers for the appbar next:

void Add_Click(object sender, EventArgs e)
{
App.RootFrame.Navigate(new Uri("/FilterPicker.xaml", UriKind.RelativeOrAbsolute));
}
 
async void Save_Click(object sender, EventArgs e)
{
//Save a copy of the image
using (MediaLibrary library = new MediaLibrary())
{
var imgStream = (await App.CurrPhoto.RawSession.RenderToJpegAsync()).AsStream();
library.SavePicture(DateTime.UtcNow.Ticks.ToString(), imgStream);
MessageBox.Show("Picture saved!");
}
}
 
void Undo_Click(object sender, EventArgs e)
{
App.CurrPhoto.Undo();
UpdateImageAsync();
}
 
void UndoAll_Click(object sender, EventArgs e)
{
App.CurrPhoto.Reset();
UpdateImageAsync();
}
 
void ApplyRecipe_Click(object sender, EventArgs e)
{
App.RootFrame.Navigate(new Uri("/Cookbook.xaml", UriKind.RelativeOrAbsolute));
}
 
private void SaveRecipe_Click(object sender, EventArgs e)
{
SaveRecipeBox.Visibility = System.Windows.Visibility.Visible;
NowEditing.Opacity = 0.5;
}

Undo_Click and UndoAll_Click make use of the Undo and Reset methods that we wrapped with ImagingSDK, while Add_Click and ApplyRecipe_Click navigate to their respective pages. SaveRecipe_Click shows the SaveRecipeBox grid, and dims the NowEditing image.

SaveRecipeBox has a few buttons which need handlers, so let's go ahead and add those:

void SaveRecipe(object sender, EventArgs e)
{
var Cookbook = ((Dictionary<string, List<string>>)IsolatedStorageSettings.ApplicationSettings["Recipes"]);
if (!Cookbook.ContainsKey(RecipeName.Text))
{
Cookbook.Add(RecipeName.Text, App.CurrPhoto.Filters);
IsolatedStorageSettings.ApplicationSettings.Save();
CancelRecipe(sender, e);
}
else
{
MessageBox.Show("Sorry, you already have a recipe with that name! Please try again with a different name.");
}
}
 
void CancelRecipe(object sender, EventArgs e)
{
RecipeName.Text = "";
SaveRecipeBox.Visibility = System.Windows.Visibility.Collapsed;
NowEditing.Opacity = 1;
}

And, as a final "cherry on top," let's go ahead and make sure the user wants to exit the editing session, instead of just accidentally pressing the back button and loosing all his/her hard work. Going back to MainPage.xaml, add BackKeyPress="AppExiting" as the final property in the opening <phone:PhoneApplicationPage> tag, then go to MainPage.xaml.cs and add the handler for it at the end of the page:

private void AppExiting(object sender, System.ComponentModel.CancelEventArgs e)
{
//http://stackoverflow.com/questions/8975822/prompt-confirmation-dialog-when-exit-app
var IsSure = MessageBox.Show("Are you sure you want to exit?", "Exit", MessageBoxButton.OKCancel);
if (IsSure != MessageBoxResult.OK)
{
e.Cancel = true;
}
}

FilterPicker

Add a new "Windows Phone Portrait Page," and name it FilterPicker.xaml. In FilterPicker.xaml, we're basically giving a list of all enhancement and artistic filters, and allowing the user to select one of them.

Frontend

Replace everything withing <phone:PhoneApplicationPage> with the following:

<!--LayoutRoot contains the root grid where all other page content is placed-->
<Grid x:Name="LayoutRoot">
<phone:Panorama Title="filters">
<!--Panorama item one-->
<phone:PanoramaItem Header="artistic">
<ScrollViewer>
<StackPanel x:Name="ArtisticFilters">
</StackPanel>
</ScrollViewer>
</phone:PanoramaItem>
 
<!--Panorama item two-->
<phone:PanoramaItem Header="enhancements">
<ScrollViewer>
<StackPanel x:Name="EnhancementFilters">
</StackPanel>
</ScrollViewer>
</phone:PanoramaItem>
</phone:Panorama>
</Grid>

We now have a panorama, with two StackPanels (one for artistic and one for enhancement filters) which we'll fill in the codebehind.

Backend

Open FilterPicker.xaml.cs to get to the codebehind.

First, ensure you have the following usings:

using ImageProcessingApp.Models;
using Microsoft.Phone.Controls;
using System;
using System.Windows.Controls;
using System.Windows.Navigation;

Next, copy the following at the end of the FilterPicker class:

private TextBlock GetBlock(FilterModel Filter)
{
TextBlock FilterBlock = new TextBlock();
FilterBlock.Text = Filter.Name;
FilterBlock.Tap += (a, b) =>
{
App.CurrPhoto.AddFilter(Filter);
App.RootFrame.Navigate(new Uri("/MainPage.xaml?WasUpdated=Yes", UriKind.RelativeOrAbsolute));
};
return FilterBlock;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
foreach (var Filter in App.Filters.ArtisticFilters)
{
ArtisticFilters.Children.Add(GetBlock(Filter));
}
 
foreach (var Filter in App.Filters.EnhancementFilters)
{
EnhancementFilters.Children.Add(GetBlock(Filter));
}
}

We're basically looping through all the filters in FiltersModel, then turning it into a TextBlock with GetBlock and adding that to its relative StackPanel. In GetBlock, we're just creating a TextBlock with a Tap handler which adds the chosen filter to the image, and then navigates to MainPage.xaml with the instruction to update.

We'll be doing essentially the same thing with Cookbook.xaml, except we'll be dealing with recipes instead of filters.

Cookbook

Frontend

Create a new Windows Phone Page named Cookbook.xaml, and replace everything within <phone:PhoneApplicationPage> with a simple StackPanel:

<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<ScrollViewer>
<StackPanel x:Name="Recipes">
</StackPanel>
</ScrollViewer>
</Grid>

Backend

In the codebehind, we'll be using the same logic as in FilterPicker.xaml.cs - apply filters, then navigate back to MainPage.xaml with a ?WasUpdated parameter.

First, ensure you have the following using statements:

using Microsoft.Phone.Controls;
using System;
using System.Collections.Generic;
using System.IO.IsolatedStorage;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;

Then, add the following OnNavigatedTo under public Cookbook():

protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var Cookbook = ((Dictionary<string, List<string>>)IsolatedStorageSettings.ApplicationSettings["Recipes"]);
foreach (var RecipeNm in Cookbook.Keys)
{
TextBlock RecipeName = new TextBlock();
RecipeName.Text = RecipeNm;
RecipeName.FontSize = 36;
RecipeName.Tap += (a, b) =>
{
var Filters = ImagingSDK.StringsToFilterModels(Cookbook[RecipeNm]);
App.CurrPhoto.AddFilters(Filters);
App.RootFrame.Navigate(new Uri("/MainPage.xaml?WasUpdated=Yes", UriKind.RelativeOrAbsolute));
};
var Recipe = (a as TextBlock).Text;
var Filters = Cookbook[Recipe];
string Listed = string.Join("\r\n", Filters);
var Result = MessageBox.Show(Listed + "\r\nPress OK to delete the recipe.", "Applied Filters", MessageBoxButton.OKCancel);
 
if (Result == MessageBoxResult.OK)
{
var Resp = MessageBox.Show("Delete recipe \"" + Recipe + "\"?", "Are you sure?", MessageBoxButton.OKCancel);
if (Resp == MessageBoxResult.OK)
{
Cookbook.Remove(Recipe);
IsolatedStorageSettings.ApplicationSettings.Save();
Recipes.Children.Remove((a as TextBlock));
}
}
Recipes.Children.Add(RecipeName);
}
}

More specifically, we're:

  • Getting the user's cookbook from IsolatedStorage
  • Looping over each recipe, and creating a TextBox from that recipe's name
  • When the recipe is tapped, use our wrapper's StringsToFilterModels and AddFilters functions to turn the List<string> into a List<FilterModel, and add the recipe. Then it navigates back to MainPage.xaml with the instruction to refresh.
  • When the recipe is held, ask the user if they'd like to remove it. If they choose to do so, remove the recipe from the cookbook and save our cookbook.

Recap

We used the Nokia Imaging SDK a lot, here's some more generalized snippets from the app:

  • After installing the SDK, make sure to remove "Any CPU" from your build configuration manager. Also, be sure to read the documentation on adding the SDK if you have any problems.
  • You're probably going to need to write some kind of wrapper around EditingSession if you want to do more than just apply a filter to an image.
  • To keep your memory use low (and user satisfaction high), make sure your wrappers (and most anything you use an EditingSession in) have custom Dispose methods which call EditingSession.Dispose().
  • In theory, there aren't a set number of filters - you can write your own as an implementation of the IFilter class, but (as far as I can tell) you would have to use some combination of the filters in FilterFactory.
  • If you need the user to pick one of the "stock" filters, check out FilterModel.cs and FiltersModel.cs from the Nokia Filter Explorer sample app. They list most of the filters provided in FilterFactory, along with the filter's name.

Other than that, filtering an image using the SDK is pretty straight-forward - you create an EditingSession, then use the AddFilter, Undo, etc. methods to modify the image with filters.

Final Steps

Congrats - we just put together a WP8 app making use of the Nokia Imaging SDK! Hopefully you've gained some practical experience with it that will help in the development of your own apps. Now you can take a look at the documentation, or jump right in and start making your own apps with the SDK.

References

This page was last modified on 14 October 2013, at 04:26.
597 page views in the last 30 days.