×
Namespaces

Variants
Actions
(Difference between revisions)

Handwriting overlay lens app for Windows Phone

From Nokia Developer Wiki
Jump to: navigation, search
GuruuMeditation (Talk | contribs)
(GuruuMeditation -)
GuruuMeditation (Talk | contribs)
(GuruuMeditation - Developing a lens application)
Line 31: Line 31:
 
In the app manifest, you must add those two capabilities : '''ID_CAP_ISV_CAMERA''' and '''ID_CAP_MEDIALIB_PHOTO''' so you can have access to the camera and the Medial Library to save images.
 
In the app manifest, you must add those two capabilities : '''ID_CAP_ISV_CAMERA''' and '''ID_CAP_MEDIALIB_PHOTO''' so you can have access to the camera and the Medial Library to save images.
  
[[File:Lens1.png|200px]]
+
[[File:Lens1.png|300px]]
  
 
Open now the manifest in XML and, after the '''Tokens''' element, declare your app as Lens app with :
 
Open now the manifest in XML and, after the '''Tokens''' element, declare your app as Lens app with :
Line 49: Line 49:
 
If you deploy the app now and go to the Lens page, you’ll see the icon :
 
If you deploy the app now and go to the Lens page, you’ll see the icon :
  
[[File:Lens2.jpg|200px]]</br>
+
[[File:Lens2.jpg|400px]]   [[File:Lens3.jpg|400px]]
[[File:Lens3.jpg|200px]]
+
  
 
When your app is called from the Lens page, the OS adds “?Action=ViewfinderLaunch” to the start uri of your app (example : ''“Mainpage.xaml?Action=ViewfinderLaunch”.'' That is how you can detect if your app was launched from the Lens page or not.
 
When your app is called from the Lens page, the OS adds “?Action=ViewfinderLaunch” to the start uri of your app (example : ''“Mainpage.xaml?Action=ViewfinderLaunch”.'' That is how you can detect if your app was launched from the Lens page or not.
Line 243: Line 242:
 
</code>
 
</code>
  
The lens in action :
+
The lens in action :<br/>
[[File:Lens4.jpg|200px]]
+
 
And the result image :
+
[[File:Lens4.jpg|400px]]:<br/>
[[File:Lens5.jpg|200px]]
+
And the result image :<br/>
 +
 
 +
[[File:Lens5.jpg|400px]]
  
 
[[File:DrawLens.zip|thumb|Sources]]
 
[[File:DrawLens.zip|thumb|Sources]]

Revision as of 21:45, 6 October 2013

This article explains how to develop a lens application for Windows Phone. The example will be a lens where you can handwrite on the picture

WP Metro Icon Multimedia.png
SignpostIcon XAML 40.png
WP Metro Icon WP8.png
Article Metadata
Code Example
Source file: File:DrawLens.zip
Tested with
SDK: Windows Phone 8.0 SDK
Devices(s): Nokia Lumia 920
Compatibility
Platform(s):
Windows Phone 8
Dependencies: WritebeableBitmapEx
Platform Security
Capabilities: ID_CAP_ISV_CAMERA,ID_CAP_MEDIALIB_PHOTO
Article
Created: User:Guruumeditation (06 Oct 2013)
Updated: User:Guruumeditation (06 Oct 2013)
Last edited: GuruuMeditation (06 Oct 2013)

Introduction

The Nokia Lumia serie devices are known for their excellent camera. One way to use it is a feature introduced with Windows Phone 8 : the camera lenses.

Basically, it is a software that can be called from the camera Lens page and that will (most probably) process images taken with the camera in a certain way. It can be effect like black/white, panorama composition,etc…. In fact, it is not especially different from a regular app, though. It still appears on the application list of your phone, and can be launched like any other app.

Declaring the app as Lens

In the app manifest, you must add those two capabilities : ID_CAP_ISV_CAMERA and ID_CAP_MEDIALIB_PHOTO so you can have access to the camera and the Medial Library to save images.

Lens1.png

Open now the manifest in XML and, after the Tokens element, declare your app as Lens app with :

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

One more thing to do, is to add the icon that will appears in the Lens page. You must add 3 icons images in the Assets directory :

  • Lens.Screen-720p.png (173 x 173)
  • Lens.Screen-WVGA.png (259 x 259)
  • Lens.Screen-WXGA.png (277 x 277)

If you deploy the app now and go to the Lens page, you’ll see the icon :

Lens2.jpg Lens3.jpg

When your app is called from the Lens page, the OS adds “?Action=ViewfinderLaunch” to the start uri of your app (example : “Mainpage.xaml?Action=ViewfinderLaunch”. That is how you can detect if your app was launched from the Lens page or not.

If you want the user to land on different page depending of launched from Lens page or from start menu, you can do it using a custom UriMapper. For instance :

class CustomUriMapper : UriMapperBase
{
public override Uri MapUri(Uri uri)
{
string tempUri = uri.ToString();
if (tempUri.Contains("ViewfinderLaunch"))
{
return new Uri("/LensPage.xaml", UriKind.Relative);
}
else
{
return uri;
}
}
}

This mapper will redirect to the page LensPage.xaml if coming from the Lens page. To activate this wrapper, you can put it in the InitializePhoneApplication method in App.cs :

RootFrame.UriMapper = new CustomUriMapper();

You need now to create a new page in your project called LensPage.xaml.

Lens exemple

Now we have a Lens, app, but it doesn’t to anything. Now we need to implement the functionality.

The example I’ll show here is a Lens app where you can see the camera feed (nothing revolutionary) and can draw on it. Then you can take a snapshot.

For the drawing part, I’ll use the InkPresenter control for the drawing, and the WriteableBitmapEx Nuget package for the image processing.

My LensPage.xaml will look like this :

<phone:PhoneApplicationPage x:Class="DrawLens.LensPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Landscape"
Orientation="Landscape"
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.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
 
<StackPanel Orientation="Vertical"
x:Name="ButtonsSP"
Visibility="Collapsed">
<Button Content="Clear"
Margin="10,10"
Width="100"
Click="Clear_OnClick"></Button>
<Button Content="Save"
Width="100"
Click="Save_OnClick">
</Button>
</StackPanel>
<TextBlock Text="Processing..."
Visibility="Visible"
x:Name="ProcessingLabel"></TextBlock>
<InkPresenter Width="640"
Height="480"
MouseLeftButtonDown="TheInkPresenter_MouseLeftButtonDown"
LostMouseCapture="TheInkPresenter_LostMouseCapture"
MouseMove="TheInkPresenter_MouseMove"
x:Name="TheInkPresenter"
Grid.Column="1">
<InkPresenter.Background>
<VideoBrush x:Name="CameraBrush"></VideoBrush>
</InkPresenter.Background>
</InkPresenter>
</Grid>
 
</phone:PhoneApplicationPage>

There are two columns. One with the buttons Clear and Save and the other with an InkPresenter that has a video brush. Camera feed will be streamed to this brush. I will not explain how the InkPresenter is drawing the strokes, it is not the purpose of this wiki and it is explained in the InkPresenter link above.

First, when the page is loaded, we initialize the camera and redirect the feed to the InkPresenter videobrush background :

private PhotoCamera _camera;
 
public LensPage()
{
InitializeComponent();
 
this.Loaded += LensPage_Loaded;
}
 
void LensPage_Loaded(object sender, RoutedEventArgs e)
{
if (PhotoCamera.IsCameraTypeSupported(CameraType.Primary))
{
// Initialize camera, and show buttons only when camera ready
_camera = new PhotoCamera(CameraType.Primary);
// Event launched when the camera as taken a picture
_camera.CaptureImageAvailable += _camera_CaptureImageAvailable;
CameraBrush.SetSource(_camera);
}
 
}

When the user press on the Save button, I capture an image:

private void Save_OnClick(object sender, RoutedEventArgs e)  
{
_camera.CaptureImage();
}

When the image is captured, the camera CaptureImageAvailable event is launched. There you get the bitmap stream.

I will make a bitmap from that stream. Then I’ll take all the strokes data of the InkPresenter and redraw those strokes on the bitmap (The DrawLineBresenham method in the code is based on the WritebeableBitmapEx one and allow to draw lines thicker than 1 pixel. See source code at the end of the post). As the InkPresenter size is different than the size of the image, I compute the magnification ratio by dividing the image size by the InkPresenter size.

After drawing all the strokes, I save the bitmap to the Media Library.

/// <summary>
/// Called when image is available
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void _camera_CaptureImageAvailable(object sender, ContentReadyEventArgs e)
{
Dispatcher.BeginInvoke(() => CreateAndSave(e.ImageStream));
}
/// <summary>
/// Process the image
/// </summary>
/// <param name="stream"></param>
private void CreateAndSave(Stream stream)
{
// Get bitmap from stream
var _bitmap = new WriteableBitmap(1, 1);
 
_bitmap = _bitmap.FromStream(stream);
 
var width = _bitmap.PixelWidth;
 
var height = _bitmap.PixelHeight;
 
// Get ration between the inkpresenter size and the bitmap size
var ratiox = width / TheInkPresenter.Width;
 
var ratioy = height / TheInkPresenter.Height;
 
// Draw each strokes on the bitmap
foreach (var stroke in TheInkPresenter.Strokes)
{
var x1 = stroke.StylusPoints[0].X * ratiox;
var y1 = stroke.StylusPoints[0].Y * ratioy;
 
foreach (var point in stroke.StylusPoints.Skip(1))
{
var x2 = point.X * ratiox;
 
var y2 = point.Y * ratioy;
 
DrawLineBresenham(_bitmap, (int)x1, (int)y1, (int)x2, (int)y2, Colors.Red, (int)ratiox, (int)ratioy);
 
x1 = x2;
y1 = y2;
}
}
// Save into library
_bitmap.SaveToMediaLibrary("DrawLensGenerated.jpg");
 
 
MessageBox.Show("Image saved !");
}

The lens in action :

Lens4.jpg:
And the result image :

Lens5.jpg

File:DrawLens.zip

202 page views in the last 30 days.