×
Namespaces

Variants
Actions
(Redirected from ImagePlus)

Filter Testing app using the Imaging SDK

From Nokia Developer Wiki
Jump to: navigation, search

This article covers how to use Nokia Imaging SDK efficiently in order to create stunning images.

WP Metro Icon UI.png
WP Metro Icon Tools.png
SignpostIcon XAML 40.png
WP Metro Icon WP8.png
Article Metadata
Code Example
Source file: Media:ImagePlus.zip
Tested 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, ID_CAP_ISV_CAMERA
Article
Created: mehul_raje (30 Jul 2013)
Last edited: hamishwillee (14 Oct 2013)

Contents

Introduction

Nokia launched Lumia 1020 an evolutionary device, Nokia also launched Nokia Imaging sdk along with, the SDK have lot of APIs to play with images, it provides near about 44 filters/effects which can be applied to the image.

This article mainly concentrates on Nokia Imaging SDK, i.e. how to create stunning images with help of SDK.This sample app in the article covers some filters and effects provided by Nokia imaging sdk, even though some filters are not added the architecture of this project is so easy that you can add your filter/effects easily. In this app I have used Telerik Rad Controls for Windows Phone along with Nokia imaging SDK. Telerik Rad Controls comes with various best inbuilt controls (which are not available in WP8 sdk) which can be use easily in Windows Phone applications in order to create fluent UI with less effort.

Prerequisite

  1. Install Nokia imaging SDK on your running machine. Please find the complete help on how to install Nokia Imaging SDK and how to add libraries to the project here.
  2. Install Telerik Rad Controls on your running machine. Please find complete help on below these links: Basic Information and Examples.

Getting Started

  1. Create a Windows Phone App project in your workspace.
  2. Make sure that you have added Nokia imaging SDK libraries and Telerik rad controls libraries in your project.


From Telerik Rad controls you need to add only following libraries in your project.

  • Telerik.Windows.Controls.Input
  • Telerik.Windows.Controls.Primitives
  • Telerik.Windows.Core

You will find these libraries in installation path of RadControls (\Program Files (x86)\Telerik\RadControls for Windows Phone 8 Q2 2013\Binaries)

Important Capabilities

  • ID_CAP_MEDIALIB_PHOTO
  • ID_CAP_ISV_CAMERA

Support Classes

Before Starting with actual flow we need to add some support classes in the project, you will learn significance of these classes in next few topics in this article

  • FilterEnum: This enum contains enum equivalent of all possible filter/effects implemented in the project
  • DataContext:This class uses singleton pattern, this class is used globally through the project, it holds original image stream and filter image stream.
  • ImageHelper: Preview all functionality rely on class ImageHelper to render modified image in grid cell (Preview all functionality is covered later in this article).
    • The ImageHelper class uses some standard value(or default) for rendering some filter/effect for e.g. for blur effect default value for blur level is 20. But all these values are user configurable from GenericFilterControl (from filter image container FilterContainerPage.xaml)
    • RenderFilterImage does the job of creating EditingSession, and it keeps adding filters which are listed in aFilterTypes to the original image stream.
    • RenderFilterImage also fetch jpeg stream from session, which will be usefull in order to save modified image to the album.
    • The advantage of taking List<FilterEnum> as function parameter for RenderFilterImage is, user can easily add number of filters to an exsisting image stream in single session.

Import Image Stream

First step while creating imaging app is - you have to provide some provision in the application through which user can provide input image stream to the application. In this application user can provide image input from below two sources - from gallery and the other from camera capture.

To start with this,

  1. Add One Windows phone portrait page in your project let’s say “LaunchPage.xaml”.
  2. Copy following code in your xaml file.

LaunchPage.xaml

<phone:PhoneApplicationPage
x:Class="WPIS3.LaunchPage"
xmlns:Tile="clr-namespace:WPIS3.LiveTilesControls"
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"
xmlns:telerikPrimitives="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Primitives"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d"
shell:SystemTray.IsVisible="True">
 
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
 
<!--TitlePanel contains the name of the application and page title-->
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="Select Image Source" Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel>
 
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<telerikPrimitives:RadUniformGrid x:Name="UGTilesHolder" NumberOfRows="1" NumberOfColumns="2">
<Tile:MediumTileUC x:Name="TileCamera" VerticalAlignment="Top" TxtFooter="Camera" TxtDescription="Capture image from camera" ImageHeaderSource="/Assets/camera.png" Tap="TileCamera_Tap"/>
<Tile:MediumTileUC x:Name="TileAlbum" VerticalAlignment="Top" TxtFooter="Album" TxtDescription="Pick image from camera album." ImageHeaderSource="/Assets/folder.png" Tap="TileAlbum_Tap"/>
</telerikPrimitives:RadUniformGrid>
</Grid>
</Grid>
</phone:PhoneApplicationPage>


  • xmlns:telerikPrimitives declares namespace as Telerik.Windows.Controls.Primitives.
  • In the XAML you can see I have created one RadUniformGrid of dimension 1X1 and added two tile controls one in each row.
  • Actually the Tile:MediumTileUC is one of the user control defined in this project, this user controls provides basic flip animation with two face to the tile.
  • This user control is just created for good user experience you can just look this code briefly.
  • Please refer the source code for this UserControl implementation.

So the final screen shot of Launch page is as follow

Before Flip
After Flip

Now we will go for business logic for how to import image from camera album, please refer the below code for the same.
LaunchPage.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using Microsoft.Phone.Tasks;
using System.IO;
 
namespace WPIS3
{
public partial class LaunchPage : PhoneApplicationPage
{
private DataContext dataContext;
 
public LaunchPage()
{
InitializeComponent();
dataContext = WPIS3.DataContext.Singleton;
}
 
private void TileCamera_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
NavigationService.Navigate(new Uri("/CameraCapture.xaml", UriKind.Relative));
}
 
private void PickImageCallback(Object sender, PhotoResult e)
{
if (e.TaskResult != TaskResult.OK)
{
return;
}
if (dataContext.ImageStream != null)
{
dataContext.CreateStreams();//reset streams
dataContext.ImageStream.Flush();
e.ChosenPhoto.CopyTo(dataContext.ImageStream);
}
NavigationService.Navigate(new Uri("/FilterPages/FilterContainerPage.xaml", UriKind.Relative));
}
 
private void TileAlbum_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
PhotoChooserTask chooser = new PhotoChooserTask();
chooser.Completed += PickImageCallback;
chooser.Show();
}
}
}
  • From the code you can see that event TileAlbum_Tap opens PhotoChoserTask and callback function PickImageCallback receives corresponding image stream and copies that stream in global DataContext’s ImageStream.
  • ChosenPhoto property of PhotoResult gives image stream and CopyTo api of Stream class is use to copy image stream to other stream.



Get Image From Camera This functionality is as it is copied from sample-projects provided by Nokia, you will get more information on this topic on below links

  1. Projects.
  2. photo capture.

Apply filters/effects to image

Now we have original image stream in DataContext so we can apply various filters/effects to that stream.

  1. For each supported filter I have created separate user control (let’s say filter control) which hold all information related to the corresponding filter/effect.
  2. In order to have some additional common functionality from all filter controls I have created one abstract class named GenericFilterControl which is strictly inherited by all filter controls.

The GenericFilterControl is inherited from UserControl, please refer below code.

GenericFilterControl.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
 
using System.Windows.Media.Imaging;
using Windows.Storage.Streams;
using Nokia.Graphics.Imaging;
 
namespace WPIS3.FilterPages
{
public abstract class GenericFilterControl :UserControl
{
public DataContext dataContext;
private IBuffer jpegStream;
public EditingSession session;
 
public GenericFilterControl()
{
dataContext = WPIS3.DataContext.Singleton;
}
 
public async Task<IBuffer> getJpegStream()
{
if (session != null)
{
jpegStream = await session.RenderToJpegAsync();
}
return jpegStream;
}
 
public abstract void GetFilterImage();
 
}
}


  • This class contains data member and member functions which are used by all filter controls,jpegStream is used to get stream for saving image into phone’s album, and we will discuss this in detail in further classes.
  • EditingSession: This is core class of Nokia Imaging SDK, with the help of editing session you can add one or more filters or effects to the image, it also helps to render image into bitmap or in buffer, while creating EditingSession you must provide source image to it.
  • You will get more information on editing session here.

Filter/Effect Control

Please have look on below two code files which is nothing but User Control representing blur effect.

BlurUC.xaml

<local:GenericFilterControl
xmlns:local="clr-namespace:WPIS3.FilterPages"
x:Class="WPIS3.FilterPages.BlurUC"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:telerikCore="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Core"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="800" d:DesignWidth="480">
 
<UserControl.Resources>
<telerikCore:RadMoveAndFadeAnimation x:Key="rectangleMoveFadeAnimation"/>
</UserControl.Resources>
 
<Grid x:Name="LayoutRoot" Background="Black">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
 
<!--TitlePanel contains the name of the application and page title-->
<StackPanel Grid.Row="0" Margin="0,0,0,0" HorizontalAlignment="Center">
<TextBlock Text="Blurr" Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel>
 
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Image x:Name="ImgFiltered" Height="500" Width="480" VerticalAlignment="Top" Tap="ImgFiltered_Tap"/>
</Grid>
 
<Grid x:Name="EditPanel" Grid.Row="2" MinWidth="800" HorizontalAlignment="Center" Background="Black" Margin="0,20,0,0" Visibility="Collapsed">
<Rectangle Fill="Gray" Opacity="0.2"/>
<StackPanel Width="400">
<TextBlock Text="Blur Level" Margin="10,0,0,0"/>
<Slider x:Name="SLBlurLevel" Value="20" Maximum="100" Minimum="10" ValueChanged="SLBlurLevel_ValueChanged"/>
</StackPanel>
</Grid>
</Grid>
</local:GenericFilterControl>
  • You can see the root class for the xaml is GenericFilterControl.
    local:GenericFilterControl : Page root class is GenericFilterControl
    xmlns:local="clr-namespace:WPIS3.FilterPages" : Declares namespace of root class
  • ContentPanel holds Image control which is used to show image after applying filter/effect.
  • EditPanel holds controls used to set various parameter of corresponding filter, for eg in case of blur filter Slider SLBlurLevel is used to adjust blur level.
  • telerikCore:RadMoveAndFadeAnimation is inbuilt animation provided by telerik rad controls, in the code this animation is applied to the EditPanel.

Refer following code for code behind file

BlurUC.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using System.IO;
using Nokia.Graphics.Imaging;
using System.Windows.Media.Imaging;
using Nokia.InteropServices.WindowsRuntime;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Storage.Streams;
using Microsoft.Xna.Framework.Media;
using Telerik.Windows.Controls;
 
namespace WPIS3.FilterPages
{
public partial class BlurUC : GenericFilterControl
{
private WriteableBitmap filterBitmap;
private int _blurLevel = 20;
 
public BlurUC():base()
{
InitializeComponent();
this.LayoutRoot.Height = 800;
this.LayoutRoot.Width = 480;
 
filterBitmap = new WriteableBitmap(ImgFiltered, null);
ImgFiltered.Source = filterBitmap;
 
GetFilterImage();
}
 
public void SetAnimation()
{
RadMoveAndFadeAnimation moveFadeAnimation = Resources["rectangleMoveFadeAnimation"] as RadMoveAndFadeAnimation;
moveFadeAnimation.FadeAnimation.StartOpacity =0.2;
moveFadeAnimation.FadeAnimation.EndOpacity = 1.0;
moveFadeAnimation.MoveAnimation.EndPoint = new Point(0,0);
moveFadeAnimation.MoveAnimation.StartPoint = new Point(0,System.Windows.Application.Current.Host.Content.ActualHeight - 400);
RadAnimationManager.Play(this.EditPanel, moveFadeAnimation);
}
 
public override async void GetFilterImage()
{
if (dataContext != null && filterBitmap != null)
{
if (dataContext != null && filterBitmap != null)
{
try
{
IBuffer buffer = dataContext.ImageStream.GetWindowsRuntimeBuffer();
session = new EditingSession(buffer);
session.AddFilter(FilterFactory.CreateBlurFilter(_blurLevel));
await session.RenderToBitmapAsync(filterBitmap.AsBitmap());
filterBitmap.Invalidate();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
 
private void SLBlurLevel_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
this._blurLevel = (int)e.NewValue;
GetFilterImage();
}
 
private void ImgFiltered_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
SetAnimation();
if (this.EditPanel.Visibility == System.Windows.Visibility.Visible)
{
this.EditPanel.Visibility = System.Windows.Visibility.Collapsed;
}
else
{
this.EditPanel.Visibility = System.Windows.Visibility.Visible;
 
}
}
}
}
  • BlurUC is inherited from GenericFilterControl, GenericFilterControl does the job of initializing dataContext.
  • SetAnimation function is use to set and play RadMoveAndFadeAnimation animation to the Edit panel.
  • Data member _blurLevel is used to set blur effect level of filtered image.
  • GetFilterImage actually does the job of applying filter/effect to the image, it
a.Initializes session
b.Adds filter to the session with the help of FilterFactory.CreateBlurFilter
c.Renders filtered image to the WriteableBitmap.
  • SLBlurLevel_ValueChanged re-renders image as per blur level set by the user.
  • To see implementation of more filters/effects please download the complete source code of this project.



Set Container to hold filtered image

  • Now we have control corresponding to blur effect, the next task is defining container which will present modified images to the user, so that user can navigate through newly created images or he may want to edit it.
  • The container (simply windows phone portrait page) provided in this projects provides following functionalities to the user.
    1. View all filtered images through touch navigation (supported navigations are slide and flip).
      RadSlideView supports two mode of transitions
      Telerik.Windows.Controls.SlideViewTransitionMode.Slide and
      Telerik.Windows.Controls.SlideViewTransitionMode.Flip
    2. Edit modified image.
    3. View slide show of images.
    4. Save filtered image
    5. View all filtered images in grid.
  • Before going to actual code of container just go through the following screen shots of container to get the brief idea.
LaunchPage
Application bar
Edit image


Now we will go through container implementation.

FilterContainerPage.xaml

<phone:PhoneApplicationPage x:Class="WPIS3.FilterPages.Cartoon"
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"
xmlns:telerikPrimitives="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Primitives"
xmlns:telerikCore="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Core"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d"
shell:SystemTray.IsVisible="True">
 
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="800"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
 
 
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="0" Margin="12,0,12,0" VerticalAlignment="Top">
<ProgressBar x:Name="PBSaveImage" IsIndeterminate="True" VerticalAlignment="Top" Foreground="Blue" Visibility="Collapsed"/>
<telerikPrimitives:RadSlideView x:Name="radSlideView" ItemsSource="{Binding}" ItemRealizationMode="ViewportItem" VerticalAlignment="Top" Height="800" TransitionMode="Slide">
<telerikPrimitives:RadSlideView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<UserControl Content="{Binding FilterImageContainer}" Grid.Row="1" Margin="0,12,0,0" VerticalAlignment="Top"/>
</Grid>
</DataTemplate>
</telerikPrimitives:RadSlideView.ItemTemplate>
<telerikPrimitives:RadSlideView.ItemPreviewTemplate>
<DataTemplate>
<telerikPrimitives:RadBusyIndicator IsRunning="True"/>
</DataTemplate>
</telerikPrimitives:RadSlideView.ItemPreviewTemplate>
</telerikPrimitives:RadSlideView>
</Grid>
 
<Grid x:Name="BottomPanel" Grid.Row="0" Height="120" VerticalAlignment="Bottom" Margin="0,20,0,0">
 
</Grid>
</Grid>
 
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar Mode="Default" Opacity="0.5" IsMenuEnabled="True" IsVisible="True">
<shell:ApplicationBarIconButton x:Name="btnPrev" IconUri="/Assets/back.png" Text="prev" Click="btnPrev_Click" />
<shell:ApplicationBarIconButton x:Name="btnSave" IconUri="/Assets/Save.png" Text="save" Click="btnSave_Click"/>
<shell:ApplicationBarIconButton x:Name="btnSetting" IconUri="/Assets/Settings.png" Text="settings" Click="btnSetting_Click"/>
<shell:ApplicationBarIconButton x:Name="btnNext" IconUri="/Assets/next.png" Text="next" Click="btnNext_Click"/>
 
<shell:ApplicationBar.MenuItems>
<shell:ApplicationBarMenuItem x:Name="MIStartSShow" Text="Slide Show" Click="MIStartSShow_Click" />
<shell:ApplicationBarMenuItem x:Name="MIPreviewAll" Text="View All" Click="MIPreviewAll_Click" />
</shell:ApplicationBar.MenuItems>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>
</phone:PhoneApplicationPage>
  • In the xaml you can see I have used RadSlideView from telerikPrimitives to hold filtered images.
  • RadSlideView provides you a consistent slide navigation through the items which are added in it, you can add any control as item of slide view, and which is highly configurable from xaml as well as from code behind file, here I have added pictures to slide view and gives extremely native appearance.
  • I have also used telerikPrimitives:RadBusyIndicator, which is shown to the user during navigation from one picture to another picture.
  • Also DataTemplate is defined for slide control, data template binds UserControl to the slider, from code behind file i will bind GenericFilterControl to the slide view as it is derived from UserControl itself.
  • This Container also contains application bar with four buttons and two menu items which are used to activate various functionalities of container which I mentioned above, we will go through these functionalities in code behind file.

Please refer following code for code behind file.

FilterContainerPage.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using System.IO;
using System.IO.IsolatedStorage;
using Nokia.Graphics.Imaging;
using System.Windows.Media.Imaging;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Storage.Streams;
using Microsoft.Xna.Framework.Media;
 
namespace WPIS3.FilterPages
{
public partial class Cartoon : PhoneApplicationPage
{
const String TRANSITIONMODE = "transitionmode";
const String TRANSITIONSLIDE = "slide";
const String TRANSITIONFLIP = "flip";
 
public static String SELECTEDITEM = "selecteditem";
 
List<ViewModel> _filterItems;
 
public Cartoon()
{
InitializeComponent();
 
_filterItems = new List<ViewModel>();
ViewModel one = new ViewModel()
{
FilterImageContainer = new CartoonUC(),
FilterType = FilterEnum.Cartoon
};
_filterItems.Add(one);
 
ViewModel two = new ViewModel()
{
FilterImageContainer = new BlurUC(),
FilterType = FilterEnum.Blur
};
_filterItems.Add(two);
 
ViewModel three = new ViewModel()
{
FilterImageContainer = new WaterColorUC(),
FilterType = FilterEnum.WaterColor
};
_filterItems.Add(three);
 
ViewModel four = new ViewModel()
{
FilterImageContainer = new Antique(),
FilterType = FilterEnum.Antique
};
_filterItems.Add(four);
 
ViewModel five = new ViewModel()
{
FilterImageContainer = new AutoEnhanceUC(),
FilterType = FilterEnum.AutoEnhance
};
_filterItems.Add(five);
 
ViewModel six = new ViewModel()
{
FilterImageContainer = new ColorBoostUC(),
FilterType = FilterEnum.ColorBoost
};
_filterItems.Add(six);
 
ViewModel seven = new ViewModel()
{
FilterImageContainer = new ColorSwapUC(),
FilterType = FilterEnum.ColorSwap
};
_filterItems.Add(seven);
 
ViewModel eight = new ViewModel()
{
FilterImageContainer = new BlendUC(),
FilterType = FilterEnum.Blend
};
_filterItems.Add(eight);
 
ViewModel nine = new ViewModel()
{
FilterImageContainer = new EmbossUC(),
FilterType = FilterEnum.Emboss
};
_filterItems.Add(nine);
 
ViewModel ten = new ViewModel()
{
FilterImageContainer = new WhiteBalanceUC(),
FilterType = FilterEnum.WhiteBalance
};
_filterItems.Add(ten);
 
ViewModel eleven = new ViewModel()
{
FilterImageContainer = new MirrorUC(),
FilterType = FilterEnum.Mirror
};
_filterItems.Add(eleven);
 
ViewModel twelve = new ViewModel()
{
FilterImageContainer = new FreeSketchUC(),
FilterType = FilterEnum.FreeSketch
};
_filterItems.Add(twelve);
 
this.DataContext = _filterItems;
}
 
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (NavigationContext.QueryString.ContainsKey(SELECTEDITEM))
{
String strFilterType;
NavigationContext.QueryString.TryGetValue(SELECTEDITEM, out strFilterType);
ManipulateSelectedItem(int.Parse(strFilterType));
}
}
 
private void btnPrev_Click(object sender, EventArgs e)
{
radSlideView.MoveToPreviousItem();
}
 
private void btnSave_Click(object sender, EventArgs e)
{
SaveImage();
}
 
public void ManipulateSelectedItem(int aSelectedItem)
{
ViewModel currentItem = null;
switch (aSelectedItem)
{
case (int)FilterEnum.Antique:
currentItem = _filterItems.Find(ViewModel => ViewModel.FilterType == FilterEnum.Antique);
break;
 
case (int)FilterEnum.AutoEnhance:
currentItem = _filterItems.Find(ViewModel => ViewModel.FilterType == FilterEnum.AutoEnhance);
break;
 
case (int)FilterEnum.Blend:
currentItem = _filterItems.Find(ViewModel => ViewModel.FilterType == FilterEnum.Blend);
break;
 
case (int)FilterEnum.Blur:
currentItem = _filterItems.Find(ViewModel => ViewModel.FilterType == FilterEnum.Blur);
break;
 
case (int)FilterEnum.Cartoon:
currentItem = _filterItems.Find(ViewModel => ViewModel.FilterType == FilterEnum.Cartoon);
break;
 
case (int)FilterEnum.ColorBoost:
currentItem = _filterItems.Find(ViewModel => ViewModel.FilterType == FilterEnum.ColorBoost);
break;
 
case (int)FilterEnum.ColorSwap:
currentItem = _filterItems.Find(ViewModel => ViewModel.FilterType == FilterEnum.ColorSwap);
break;
 
case (int)FilterEnum.Emboss:
currentItem = _filterItems.Find(ViewModel => ViewModel.FilterType == FilterEnum.Emboss);
break;
 
case (int)FilterEnum.WaterColor:
currentItem = _filterItems.Find(ViewModel => ViewModel.FilterType == FilterEnum.WaterColor);
break;
 
case (int)FilterEnum.WhiteBalance:
currentItem = _filterItems.Find(ViewModel => ViewModel.FilterType == FilterEnum.WhiteBalance);
break;
 
case (int)FilterEnum.Mirror:
currentItem = _filterItems.Find(ViewModel => ViewModel.FilterType == FilterEnum.Mirror);
break;
 
case (int)FilterEnum.FreeSketch:
currentItem = _filterItems.Find(ViewModel => ViewModel.FilterType == FilterEnum.FreeSketch);
break;
}
radSlideView.SelectedItem = currentItem;
}
 
public async void SaveImage()
{
SetProgressBarVisibility(true);
try
{
ViewModel currentitem = (ViewModel)radSlideView.SelectedItem;
IBuffer jpegout = null;
GenericFilterControl item = currentitem.FilterImageContainer;
jpegout = await item.getJpegStream();
MediaLibrary library = new MediaLibrary();
string filename = "savedpicture_" + DateTime.Now.ToString("g");
Picture pic = library.SavePictureToCameraRoll(filename, jpegout.ToArray());
ShowImageSavedPopup();
SetProgressBarVisibility(false);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
 
/// <summary>
///
/// </summary>
/// <param name="aMakeVisible">pass true to activate</param>
void SetProgressBarVisibility(bool aMakeVisible)
{
if (aMakeVisible)
{
this.PBSaveImage.Visibility = System.Windows.Visibility.Visible;
}
else
{
this.PBSaveImage.Visibility = System.Windows.Visibility.Collapsed;
}
}
 
void btnStopSlideShow_Click(object sender, EventArgs e)
{
if (radSlideView.IsSlideShowRunning)
{
radSlideView.StopSlideShow();
PrepareAppBarNormal();
}
}
 
void btnPlaySlideShow_Click(object sender, EventArgs e)
{
if (!radSlideView.IsSlideShowRunning)
{
radSlideView.StartSlideShow();
}
}
 
private void btnNext_Click(object sender, EventArgs e)
{
radSlideView.MoveToNextItem();
}
 
private void MIStartSShow_Click(object sender, EventArgs e)
{
PrepareAppBarForSlideShow();
}
 
private void PrepareAppBarForSlideShow()
{
ApplicationBar.IsVisible = false;
 
ApplicationBar.Buttons.Clear();
ApplicationBar.MenuItems.Clear();
 
//add button for stopping slide show
ApplicationBarIconButton btnPlaySlideShow = new ApplicationBarIconButton();
btnPlaySlideShow.IconUri = new Uri("/Assets/play.png", UriKind.Relative);
btnPlaySlideShow.Click += btnPlaySlideShow_Click;
btnPlaySlideShow.Text = "Play";
ApplicationBar.Buttons.Add(btnPlaySlideShow);
 
ApplicationBarIconButton btnStopSlideShow = new ApplicationBarIconButton();
btnStopSlideShow.IconUri = new Uri("/Assets/stop.png", UriKind.Relative);
btnStopSlideShow.Click += btnStopSlideShow_Click;
btnStopSlideShow.Text = "Stop";
ApplicationBar.Buttons.Add(btnStopSlideShow);
 
ApplicationBar.IsVisible = true;
}
 
private void PrepareAppBarNormal()
{
ApplicationBar.IsVisible = false;
ApplicationBar.Buttons.Clear();
ApplicationBar.MenuItems.Clear();
 
//add button for stopping slide show
ApplicationBarIconButton btnPrev = new ApplicationBarIconButton();
btnPrev.IconUri = new Uri("/Assets/back.png", UriKind.Relative);
btnPrev.Click += btnPrev_Click;
btnPrev.Text = "prev";
ApplicationBar.Buttons.Add(btnPrev);
 
ApplicationBarIconButton btnSave = new ApplicationBarIconButton();
btnSave.IconUri = new Uri("/Assets/save.png", UriKind.Relative);
btnSave.Click += btnSave_Click;
btnSave.Text = "save";
ApplicationBar.Buttons.Add(btnSave);
 
ApplicationBarIconButton btnSetting = new ApplicationBarIconButton();
btnSetting.IconUri = new Uri("/Assets/settings.png", UriKind.Relative);
btnSetting.Click+=btnSetting_Click;
btnSetting.Text = "save";
ApplicationBar.Buttons.Add(btnSetting);
 
ApplicationBarIconButton btnNext = new ApplicationBarIconButton();
btnNext.IconUri = new Uri("/Assets/next.png", UriKind.Relative);
btnNext.Click += btnNext_Click;
btnNext.Text = "next";
ApplicationBar.Buttons.Add(btnNext);
 
ApplicationBarMenuItem MIStartSShow = new ApplicationBarMenuItem();
MIStartSShow.Text = "slide show";
MIStartSShow.Click += MIStartSShow_Click;
ApplicationBar.MenuItems.Add(MIStartSShow);
 
ApplicationBarMenuItem MIPreviewAll = new ApplicationBarMenuItem();
MIPreviewAll.Text = "View All";
MIPreviewAll.Click += MIPreviewAll_Click;
ApplicationBar.MenuItems.Add(MIPreviewAll);
 
ApplicationBar.IsVisible = true;
}
 
private void btnSetting_Click(object sender, EventArgs e)
{
WPIS3.Other.SettingUC settingsControl = new Other.SettingUC();
 
if (GetTransitionMode().CompareTo(TRANSITIONSLIDE) == 0)
{
settingsControl.RBSlide.IsChecked = true;
}
else
{
settingsControl.RBFlip.IsChecked = true;
}
settingsControl.RBFlip.Checked += RBFlip_Checked;
settingsControl.RBSlide.Checked += RBSlide_Checked;
BottomPanel.Children.Clear();
BottomPanel.Children.Add(settingsControl);
}
 
public void ShowImageSavedPopup()
{
WPIS3.Other.ImageSavedUC imageSavedPopup = new Other.ImageSavedUC();
imageSavedPopup.VerticalAlignment = System.Windows.VerticalAlignment.Top;
BottomPanel.Children.Clear();
BottomPanel.Children.Add(imageSavedPopup);
}
 
void RBSlide_Checked(object sender, RoutedEventArgs e)
{
BottomPanel.Children.Clear();
radSlideView.TransitionMode = Telerik.Windows.Controls.SlideViewTransitionMode.Slide;
SaveTransitionMode(TRANSITIONSLIDE);
}
 
void RBFlip_Checked(object sender, RoutedEventArgs e)
{
BottomPanel.Children.Clear();
radSlideView.TransitionMode = Telerik.Windows.Controls.SlideViewTransitionMode.Flip;
SaveTransitionMode(TRANSITIONFLIP);
}
 
void SaveTransitionMode(string aMode)
{
IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
if (!settings.Contains(TRANSITIONMODE))
{
settings.Add(TRANSITIONMODE, aMode);
}
else
{
settings[TRANSITIONMODE] = aMode;
}
}
 
string GetTransitionMode()
{
IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
if (!settings.Contains(TRANSITIONMODE))
{
return TRANSITIONSLIDE;
}
else
{
return settings[TRANSITIONMODE].ToString();
}
}
 
private void MIPreviewAll_Click(object sender, EventArgs e)
{
NavigationService.Navigate(new Uri("/FilterPages/PreviewAll.xaml", UriKind.Relative));
}
}
}
  • In default constructor you can see there are twelve ViewModel class objects are created, each object represents one filter control, finally a list containing these objects is set as DataContext of current page.
  • In this article I have covered only blur filter control for other filters download source code.
  • FilterImageContainer data member of ViewModel gets bound to the UserControl of radSlideView in xaml.
  • OnNavigatedTo method is overridden to just set current item(image) in slide control according to FilterEnum int equivalent received in query string.

Eg. If query string contains value FilterEnum.blur then blur image will be shown to the user.

  • ManipulateSelectedItem decides which filter image needs to be activated in slide control. To find out ViewModel representation of filter image it performs search operation on _filterItems based on FilterType (data member of ViewModel).
  • SaveImage function does the job of saving image in media album, in this function, it uses getJpegStream of GenericFilterControl to get Ibuffer.
  • PrepareAppBarForSlideShow make changes in current application bar, during the slide show of filtered images application bar contains buttons for play and stop and this function does the job of adding this buttons to the application bar. Whereas PrepareAppBarNormal resets application bar to its normal state.
  • As previously discussed RadSlideView supports two slide modes viz slide and flip, slide mode gives sliding like experience and flip gives book pages flip like experience. And user can change this mode according to his need, for making this functionality user configurable container provides user a simple popup(nothing but UserControl and is enhanced with RadFadeAnimation for better user experience.) [http://msdn.microsoft.com/en-us/library/windowsphone/develop/system.io.isolatedstorage.isolatedstoragesettings.applicationsettings(v=vs.105).aspx
  • IsolatedStorageSettings.ApplicationSettings] is used to store transition mode.

Preview All

This application also provides preview all functionality, i.e. user can view all available filters/effects in single page in the form of grid.
Refer following screen shot.

Preview All


Preview all functionality rely on class ImageHelper to render modified image in grid cell.

Refer the following code for ImageHelper class implementation.

PreviewAll.xaml

<phone:PhoneApplicationPage
x:Class="WPIS3.FilterPages.PreviewAll"
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"
xmlns:telerikPrimitives="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Primitives"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d"
shell:SystemTray.IsVisible="True">
 
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.Resources>
<Style x:Key="ThickBorder" TargetType="Border">
<Setter Property="BorderThickness" Value="4"/>
<Setter Property="CornerRadius" Value="4"/>
<Setter Property="BorderBrush" Value="Gray"/>
</Style>
 
<Style x:Key="FilterImageText" TargetType="TextBlock">
<Setter Property="FontSize" Value="13"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="FontFamily" Value="Courier New"/>
</Style>
</Grid.Resources>
 
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
 
<!--TitlePanel contains the name of the application and page title-->
<StackPanel Grid.Row="0" Margin="0,0,0,0">
<TextBlock Text="" Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel>
 
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<telerikPrimitives:RadUniformGrid x:Name="UGAllPreview" NumberOfRows="4" NumberOfColumns="3" >
<Border Style="{StaticResource ThickBorder}" Height="180">
<StackPanel Margin="0,2,0,0">
<Image x:Name="ImgAntique" Height="150" Width="150" Tap="ImgAntique_Tap"/>
<TextBlock Text="Antique" Style="{StaticResource FilterImageText}"/>
</StackPanel>
</Border>
 
<Border Style="{StaticResource ThickBorder}" Height="180">
<StackPanel Margin="0,2,0,0">
<Image x:Name="ImgAutoEnhance" Height="150" Width="150" Tap="ImgAutoEnhance_Tap"/>
<TextBlock Text="AutoEnhance" Style="{StaticResource FilterImageText}"/>
</StackPanel>
</Border>
 
<Border Style="{StaticResource ThickBorder}" Height="180">
<StackPanel Margin="0,2,0,0">
<Image x:Name="ImgBlend" Height="150" Width="150" Tap="ImgBlend_Tap"/>
<TextBlock Text="Blend" Style="{StaticResource FilterImageText}"/>
</StackPanel>
 
</Border>
 
<Border Style="{StaticResource ThickBorder}" Height="180">
<StackPanel Margin="0,2,0,0">
<Image x:Name="ImgBlur" Height="150" Width="150" Tap="ImgBlur_Tap"/>
<TextBlock Text="Blur" Style="{StaticResource FilterImageText}"/>
</StackPanel>
 
</Border>
 
<Border Style="{StaticResource ThickBorder}" Height="180">
<StackPanel Margin="0,2,0,0">
<Image x:Name="ImgCartoon" Height="150" Width="150" Tap="ImgCartoon_Tap"/>
<TextBlock Text="Cartoon" Style="{StaticResource FilterImageText}"/>
</StackPanel>
 
</Border>
 
<Border Style="{StaticResource ThickBorder}" Height="180">
<StackPanel Margin="0,2,0,0">
<Image x:Name="ImgColorBoost" Height="150" Width="150" Tap="ImgColorBoost_Tap"/>
<TextBlock Text="ColorBoost" Style="{StaticResource FilterImageText}"/>
</StackPanel>
 
</Border>
 
<Border Style="{StaticResource ThickBorder}" Height="180">
<StackPanel Margin="0,2,0,0">
<Image x:Name="ImgColorSwap" Height="150" Width="150" Tap="ImgColorSwap_Tap"/>
<TextBlock Text="ColorSwap" Style="{StaticResource FilterImageText}"/>
</StackPanel>
 
</Border>
 
<Border Style="{StaticResource ThickBorder}" Height="180">
<StackPanel Margin="0,2,0,0">
<Image x:Name="ImgEmboss" Height="150" Width="150" Tap="ImgEmboss_Tap"/>
<TextBlock Text="Emboss" Style="{StaticResource FilterImageText}"/>
</StackPanel>
 
</Border>
 
<Border Style="{StaticResource ThickBorder}" Height="180">
<StackPanel Margin="0,2,0,0">
<Image x:Name="ImgWaterColor" Height="150" Width="150" Tap="ImgWaterColor_Tap"/>
<TextBlock Text="WaterColor" Style="{StaticResource FilterImageText}"/>
</StackPanel>
 
</Border>
 
<Border Style="{StaticResource ThickBorder}" Height="180">
<StackPanel Margin="0,2,0,0">
<Image x:Name="ImgWhiteBalance" Height="150" Width="150" Tap="ImgWhiteBalance_Tap"/>
<TextBlock Text="WhiteBalance" Style="{StaticResource FilterImageText}"/>
</StackPanel>
 
</Border>
 
<Border Style="{StaticResource ThickBorder}" Height="180">
<StackPanel Margin="0,2,0,0">
<Image x:Name="ImgMirror" Height="150" Width="150" Tap="ImgMirror_Tap" Margin="-1,0,-5,0"/>
<TextBlock Text="Mirror" Style="{StaticResource FilterImageText}"/>
</StackPanel>
 
</Border>
 
<Border Style="{StaticResource ThickBorder}" Height="180">
<StackPanel Margin="0,2,0,0">
<Image x:Name="ImgFreeSketch" Height="150" Width="150" Tap="ImgFreeSketch_Tap"/>
<TextBlock Text="FreeSketch" Style="{StaticResource FilterImageText}"/>
</StackPanel>
</Border>
</telerikPrimitives:RadUniformGrid>
</Grid>
</Grid>
 
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar Mode="Minimized" Opacity="0.5" IsMenuEnabled="True" IsVisible="True">
<shell:ApplicationBarIconButton x:Name="btnEdit" IconUri="/Assets/edit.png" Text="Custom Filters" Click="btnEdit_Click" />
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>
 
</phone:PhoneApplicationPage>
  • Here again I have used RadUniformGrid to show all filter image consistently on single page, RadUniformGrid provides consistent user interface independent of screen resolution.
  • You will find RadUniformGrid in Telerik.Windows.Controls.Primitives assembly (refer xmlns: telerikPrimitives declaration in above xaml file).
  • Each cell of grid holds single filter image, each cell also contains textual information of the filter image.

Refer following code for code behind file.

PreviewAll.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
 
namespace WPIS3.FilterPages
{
public partial class PreviewAll : PhoneApplicationPage
{
ImageHelper imageHelper;
int _selectedItem = (int)FilterEnum.Antique;
public PreviewAll()
{
InitializeComponent();
imageHelper = new ImageHelper();
PopulateGrid();
}
 
private void PopulateGrid()
{
imageHelper.RenderFilterImage(ImgAntique,new List<FilterEnum>(){FilterEnum.Antique});
imageHelper.RenderFilterImage(ImgAutoEnhance, new List<FilterEnum>() { FilterEnum.AutoEnhance });
imageHelper.RenderFilterImage(ImgBlend,new List<FilterEnum>(){FilterEnum.Blend});
imageHelper.RenderFilterImage(ImgBlur,new List<FilterEnum>(){FilterEnum.Blur});
imageHelper.RenderFilterImage(ImgCartoon,new List<FilterEnum>(){FilterEnum.Cartoon});
imageHelper.RenderFilterImage(ImgColorBoost,new List<FilterEnum>(){FilterEnum.ColorBoost});
imageHelper.RenderFilterImage(ImgColorSwap,new List<FilterEnum>(){FilterEnum.ColorSwap});
imageHelper.RenderFilterImage(ImgEmboss,new List<FilterEnum>(){FilterEnum.Emboss});
imageHelper.RenderFilterImage(ImgWaterColor,new List<FilterEnum>(){FilterEnum.WaterColor});
imageHelper.RenderFilterImage(ImgWhiteBalance,new List<FilterEnum>(){FilterEnum.WhiteBalance});
imageHelper.RenderFilterImage(ImgMirror,new List<FilterEnum>(){FilterEnum.Mirror});
imageHelper.RenderFilterImage(ImgFreeSketch,new List<FilterEnum>(){FilterEnum.FreeSketch});
}
 
private void ImgAntique_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
_selectedItem = (int)FilterEnum.Antique;
GoToFilterContainerPage();
}
 
private void ImgAutoEnhance_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
_selectedItem = (int)FilterEnum.AutoEnhance;
GoToFilterContainerPage();
}
 
private void ImgBlend_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
_selectedItem = (int)FilterEnum.Blend;
GoToFilterContainerPage();
}
 
private void ImgBlur_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
_selectedItem = (int)FilterEnum.Blur;
GoToFilterContainerPage();
}
 
private void ImgCartoon_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
_selectedItem = (int)FilterEnum.Cartoon;
GoToFilterContainerPage();
}
 
private void ImgColorBoost_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
_selectedItem = (int)FilterEnum.ColorBoost;
GoToFilterContainerPage();
}
 
private void ImgColorSwap_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
_selectedItem = (int)FilterEnum.ColorSwap;
GoToFilterContainerPage();
}
 
private void ImgEmboss_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
_selectedItem = (int)FilterEnum.Emboss;
GoToFilterContainerPage();
}
 
private void ImgWaterColor_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
_selectedItem = (int)FilterEnum.WaterColor;
GoToFilterContainerPage();
}
 
private void ImgWhiteBalance_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
_selectedItem = (int)FilterEnum.WhiteBalance;
GoToFilterContainerPage();
}
 
private void ImgMirror_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
_selectedItem = (int)FilterEnum.Mirror;
GoToFilterContainerPage();
}
 
private void ImgFreeSketch_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
_selectedItem = (int)FilterEnum.FreeSketch;
GoToFilterContainerPage();
}
 
private void GoToFilterContainerPage()
{
this.NavigationService.Navigate(new Uri("/FilterPages/FilterContainerPage.xaml?" + Cartoon.SELECTEDITEM + "=" + _selectedItem.ToString(), UriKind.Relative));
}
 
private void btnEdit_Click(object sender, EventArgs e)
{
this.NavigationService.Navigate(new Uri("/FilterPages/CustomPreview.xaml", UriKind.Relative));
}
}
}
  • You can see PopulateGrid function does the job of rendering image to each cell of grid with help of RenderFilterImage function of ImageHelper class.
  • For each image tap event is handled, in order to pass the value of corresponding FilterEnum to the FilterContainerPage.xaml so that container points to the same image (we already disussed business logic of this feature in FilterContainerPage.xaml.cs).

Custom Effects

  • This Application also has custom filter functionality where user can add/remove any filter/effect to the original image stream and also save the modified image to the album.
  • This feature provides complete freedom to the user for enhancing image in the way he/she wants.
  • Currently for each filter some default values are used but I am working on making all settings user configurable the same way we provided in FilterContainerPage.

Refer following screen shots

Custom Filter
Add or remove filter

Refer following code for UI and code behind file.

CustomPreview.xaml

<phone:PhoneApplicationPage
x:Class="WPIS3.FilterPages.CustomPreview"
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"
xmlns:telerikInput="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Input"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d"
shell:SystemTray.IsVisible="True">
 
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
 
<!--TitlePanel contains the name of the application and page title-->
<StackPanel Grid.Row="0" Margin="0,0,0,0">
<TextBlock Text="Custom Filter" Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel>
 
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Image x:Name="ImgCustom" Grid.Row="0" Height="700" Width="480"/>
 
<telerikInput:RadListPicker x:Name="ListFilter" Grid.Row="0" Header="Filter:" VerticalAlignment="Top" Background="Black" Foreground="White" SelectionMode="Multiple" Opacity="0" OkButtonIconUri="/Assets/check.png" CancelButtonIconUri="/Assets/cancel.png" NoSelectionContent="Select Filters" SelectionChanged="ListFilter_SelectionChanged" IsEnabled="False">
<telerikInput:RadListPicker.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding FilterItem}" FontSize="22" FontFamily="Courier New"/>
</DataTemplate>
</telerikInput:RadListPicker.ItemTemplate>
</telerikInput:RadListPicker>
</Grid>
</Grid>
 
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar Mode="Minimized" Opacity="0.5" IsMenuEnabled="True" IsVisible="True">
<shell:ApplicationBarIconButton x:Name="btnAddFilter" IconUri="/Assets/add.png" Text="add filter" Click="btnAddFilter_Click" />
<shell:ApplicationBarIconButton x:Name="btnSave" IconUri="/Assets/save.png" Text="Save" Click="btnSave_Click" />
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>
 
</phone:PhoneApplicationPage>
  • ImgCustom is use to hold edited/filtered image.
  • I have used RadListPicker to provide user a list showing filters to be applied to the image.
  • This control provides list to the user, list can be represented to the user in two mode inline and popup it also supports multiselect feature.
  • I this page DataTemplate of RadListPicker binds TextBlock as item of list, textblock actually represents name of filter/effect.
  • {{Icode|OkButtonIconUri} and CancelButtonIconUri are the properties used to set icon images of application bar buttons in case of multiselect scenario (refer above image)
  • To bind values to the RadListPicker I have created one simple ViewModel class named FilterItemsViewModel.

Refer the following code for the same.

public class FilterItemsViewModel
{
public String FilterItem
{
get;
set;
}
 
public FilterEnum FilterType
{
get;
set;
}
}


Now refer the following code for the code behind of Custom Filter.
CustomPreview.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using Windows.Storage.Streams;
using Microsoft.Xna.Framework.Media;
using System.Runtime.InteropServices.WindowsRuntime;
 
namespace WPIS3.FilterPages
{
public partial class CustomPreview : PhoneApplicationPage
{
private List<FilterEnum> lstSelectedFilters;
private ImageHelper imageHelper;
 
public CustomPreview()
{
InitializeComponent();
lstSelectedFilters = new List<FilterEnum>();
imageHelper = new ImageHelper();
InitFilterList();
}
 
private void InitFilterList()
{
List<FilterItemsViewModel> lstAvailableFilters = new List<FilterItemsViewModel>();
lstAvailableFilters.Add(new FilterItemsViewModel() { FilterItem = "Antique", FilterType = FilterEnum.Antique });
lstAvailableFilters.Add(new FilterItemsViewModel() { FilterItem = "Auto Enhance", FilterType = FilterEnum.AutoEnhance });
lstAvailableFilters.Add(new FilterItemsViewModel() { FilterItem = "Blend", FilterType = FilterEnum.Blend });
lstAvailableFilters.Add(new FilterItemsViewModel() { FilterItem = "Blur", FilterType = FilterEnum.Blur });
lstAvailableFilters.Add(new FilterItemsViewModel() { FilterItem = "Cartoon", FilterType = FilterEnum.Cartoon });
lstAvailableFilters.Add(new FilterItemsViewModel() { FilterItem = "ColorBoost", FilterType = FilterEnum.ColorBoost });
lstAvailableFilters.Add(new FilterItemsViewModel() { FilterItem = "ColorSwap", FilterType = FilterEnum.ColorSwap });
lstAvailableFilters.Add(new FilterItemsViewModel() { FilterItem = "Emboss", FilterType = FilterEnum.Emboss });
lstAvailableFilters.Add(new FilterItemsViewModel() { FilterItem = "Mirror", FilterType = FilterEnum.Mirror });
lstAvailableFilters.Add(new FilterItemsViewModel() { FilterItem = "WaterColor", FilterType = FilterEnum.WaterColor });
lstAvailableFilters.Add(new FilterItemsViewModel() { FilterItem = "WhiteBalance", FilterType = FilterEnum.WhiteBalance });
this.ListFilter.ItemsSource = lstAvailableFilters;
}
 
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (lstSelectedFilters != null)
{
if (lstSelectedFilters.Count == 0)
{
imageHelper.RenderFilterImage(ImgCustom, new List<FilterEnum>() { FilterEnum.Antique });
}
else
{
imageHelper.RenderFilterImage(ImgCustom,lstSelectedFilters);
}
}
}
 
private void btnAddFilter_Click(object sender, EventArgs e)
{
ListFilter.IsExpanded = true;
}
 
private void ListFilter_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (lstSelectedFilters != null)
{
lstSelectedFilters.Clear();
 
foreach (FilterItemsViewModel item in this.ListFilter.SelectedItems)
{
lstSelectedFilters.Add(item.FilterType);
}
imageHelper.RenderFilterImage(ImgCustom, lstSelectedFilters);
}
}
 
public void SaveImage()
{
if (imageHelper!=null)
{
IBuffer jpegout = imageHelper.JpegStream;
MediaLibrary library = new MediaLibrary();
string filename = "savedpicture_" + DateTime.Now.ToString("g");
Picture pic = library.SavePictureToCameraRoll(filename, jpegout.ToArray());
}
}
 
private void btnSave_Click(object sender, EventArgs e)
{
SaveImage();
}
 
}
}
  • InitFilterList function initiates list of FilterItemsViewModel and set it as a ItemSource of ListFilter (RadListPicker)
  • In ListFilter_SelectionChanged event, all the elements selected by the user is retrieved using SelectedItems property of RadListPicker.
  • And all these elements are nothing but filter/effect which user wants to apply for original image stream.
  • Using this information actual filtered image is rendered with help of RenderFilterImage API of ImageHelper class.

Source Code

Download full source code from File:ImagePlus.zip.

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

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×