Introduction
In modern digital cameras you will find an option to paste date and time stamp while capturing the photo. Suddenly I thought whether Windows 8's (i.e. WinRT's) CamaraCaptureUI
provide such option or not, but unfortunately it doesn't have such feature
Date time stamp is helpful while sharing or printing the photos. Ya, you can do editing in any image manipulation software and put date time stamp, but who would like to involve in such procedure if any ready made thing is available. So, here I am presenting you how can you paste date time in photo. This article will be helpful to those devs who want to add CamaraCaptureUI
in their apps.
Background
First of all I am thankful to the team SharpDX for making wrapper of DirectX, which is a wrapper for managed apps. Secondly I am thankful to Christoph Wille. This article and project is heavily based on Christoph Wille's project on GitHub. Last but not the least I am also thankful to Can Bilgin for the helper class for converting byte
array to IRandomAccessStream
.
Devs Are Dissatisfied With WinRT's WritableBitmap
In WPF and Silverlight, the WritableBitmap
is quite useful when you want to develop any graphical based application. Unfornunatly WinRT's WritableBitmap
is quite less useful. Even WritableBitmapEx has some limitation and one of it's limitation is the main aim of this article i.e rendering text on image.
User Interface
The UI is quite simple. It contains just a page with an Image
control and a button. Button let's you to capture an image with buit in CameraCaptureUI
and the captured image with date time stamp will be saved in picture library.
Required Capabilities
You must have these capabilities set. If you havn't set camera permisson, the CameraCaptureUI
will tell you haven't added the capability. If you haven't set picture library access permission then code will throw an exception.
1. Picture Libary
2. Webcam
Camera Capture
It is now too much easy to capture an image in Windows Store App. Windows.Media.Capture
namespace has CameraCaptureUI
, which enables user to capture image as well as video. There is cropping feature and also timer. Now no need to import 3rd party DLLs and no more DllImport
This CameraCaptureUI
saves images in application's TempState folder. After closing the app, TempState is cleared out.
var dialog = new CameraCaptureUI();
dialog.PhotoSettings.AllowCropping = true;
var ImageFile = await dialog.CaptureFileAsync(CameraCaptureUIMode.Photo);
SharpDX
SharpDX is an open-source platform independent Managed DirectX API for the .NET environment. This new API is directly auto-generated from DirectX C++ SDK Headers and SharpDX is an open-source platform independent Managed DirectX API for the .NET environment. This new API is directly auto-generated from DirectX C++ SDK Headers and the generated code is purely written in C# without using any C++/CLI assemblies, while still being able to achieve comparable performance. The API naming convention has been kept as much as possible similar to the well known SlimDX API, which is currently using C++/CLI assemblies. Also, the aim of SharpDX is to provide, on top of the lower APIs (Direct3D11, DXGI, D3DCompiler...), a higher API level similar to XNA but using latest DirectX technology.
Courtsy : Wikipedia
Role of SharpDX Here
In our app, SharpDX is used to load the captured image as SharpDX compatible BitmapSource
. It decode the pixels of image and FormatConverter
returns a dedicated BitmapSource
via ImagingFactory2
class.
public static SharpDX.WIC.BitmapSource LoadBitmap(ImagingFactory2 factory, string ImageFilePath)
{
var bitmapDecoder = new BitmapDecoder(
factory,
ImageFilePath,
DecodeOptions.CacheOnDemand
);
var formatConverter = new FormatConverter(factory);
formatConverter.Initialize(
bitmapDecoder.GetFrame(0),
SharpDX.WIC.PixelFormat.Format32bppPRGBA,
BitmapDitherType.None,
null,
0.0,
BitmapPaletteType.Custom);
return formatConverter;
}
After getting the BitmapSource
, now it's turn to render the text but before calling BeginDraw()
method of class WicRenderTarget
, we will set the format of text and image. Here I have set image width and height as same as source and used the Segoe UI font with red brush.
TextFormat
class also have property to set text allignment, paragraph alignment, word wrapping, font weight, font size, flow direction, etc. and for image formatting you can set height, width, horizontal & verticasl DPI. After setting the formatting, WicRenderTarget
has DrawText()
method which accepts four arguments.
1. String to be paste, here in our case the current date time
2. Text format
3. Layout rectagle (You can set with RectangleF
class)
4. Text brush
Then we will finish drawing with EndDraw()
method. Now we will go for encoding. You can have choice of many BitmapEncoder
. Here I have choosen PngBitmapEncoder
and JpegBitmapEncoder
. I have set condition according to source file extension. At the end it returns a MemoryStream
inherited by System.IO.Stream
as SharpDX deal with that kind of stream.
private async Task<MemoryStream> RenderStaticTextToBitmap(StorageFile ImageFile)
{
var bitmap = new BitmapImage();
using (var strm = await ImageFile.OpenAsync(FileAccessMode.Read))
{
bitmap.SetSource(strm);
}
var width = bitmap.PixelWidth;
var height = bitmap.PixelHeight;
var pixelFormat = WicPixelFormat.Format32bppBGR;
var wicFactory = new ImagingFactory2();
var dddFactory = new SharpDX.Direct2D1.Factory();
var dwFactory = new SharpDX.DirectWrite.Factory();
WicRenderTarget renderTarget;
Bitmap wicBitmap;
using (var bitmapSource = LoadBitmap(wicFactory, ImageFile.Path))
{
wicBitmap = new Bitmap(wicFactory, bitmapSource, BitmapCreateCacheOption.CacheOnLoad);
int pixelWidth = (int)(wicBitmap.Size.Width * DisplayProperties.LogicalDpi / 96.0);
int pixelHeight = (int)(wicBitmap.Size.Height * DisplayProperties.LogicalDpi / 96.0);
var renderTargetProperties = new RenderTargetProperties(RenderTargetType.Default,
new D2DPixelFormat(Format.Unknown, AlphaMode.Unknown), 0, 0, RenderTargetUsage.None,
FeatureLevel.Level_DEFAULT);
renderTarget = new WicRenderTarget(
dddFactory,
wicBitmap,
renderTargetProperties)
{
TextAntialiasMode = TextAntialiasMode.Cleartype
};
}
renderTarget.BeginDraw();
var textFormat = new TextFormat(dwFactory, "Segoe UI Light", 25)
{
TextAlignment = SharpDX.DirectWrite.TextAlignment.Leading,
ParagraphAlignment = ParagraphAlignment.Far
};
var textBrush = new SharpDX.Direct2D1.SolidColorBrush(
renderTarget,
SharpDX.Colors.Red);
renderTarget.DrawText(
DateTime.Now.ToString("t") + "\n" + DateTime.Now.ToString("d") + "\n",
textFormat,
new RectangleF(width - 150, 0, width, height + 25),
textBrush);
renderTarget.EndDraw();
var ms = new MemoryStream();
var stream = new WICStream(
wicFactory,
ms);
BitmapEncoder encoder = null;
if (ImageFile.FileType == ".png")
encoder = new PngBitmapEncoder(wicFactory);
else if (ImageFile.FileType == ".jpg")
encoder = new JpegBitmapEncoder(wicFactory);
encoder.Initialize(stream);
var frameEncoder = new BitmapFrameEncode(encoder);
frameEncoder.Initialize();
frameEncoder.SetSize(width, height);
frameEncoder.PixelFormat = WicPixelFormat.FormatDontCare;
frameEncoder.WriteSource(wicBitmap);
frameEncoder.Commit();
encoder.Commit();
frameEncoder.Dispose();
encoder.Dispose();
stream.Dispose();
ms.Position = 0;
return ms;
}
Role of MemoryRandomAccessStream Here
Now the helper class MemoryRandomAccessStream
is used to convert System.IO.Stream
to IRandomAccessStream
. IRandomAccessStream
is very helpful to generate StorageFile
. After getting IRandomAccessStream
we write an StorageFile
in user's picture library and image will also be displayed.
var ms = await RenderStaticTextToBitmap(ImageFile);
var msrandom = new MemoryRandomAccessStream(ms);
Byte[] bytes = new Byte[ms.Length];
await ms.ReadAsync(bytes, 0, (int)ms.Length);
StorageFile file = await KnownFolders.PicturesLibrary.CreateFileAsync("Image.png", Windows.Storage.CreationCollisionOption.GenerateUniqueName);
using (var strm = await file.OpenStreamForWriteAsync())
{
await strm.WriteAsync(bytes, 0, bytes.Length);
strm.Flush();
}
BitmapImage image = new BitmapImage();
await image.SetSourceAsync(msrandom);
RenderedImage.Source = image;
Point of Interest
Here the main point of interest is to leaverage the CamaraCaptureUI and how to use SharpDX to do image manipulation. Your comments, suggestions and votes will encourage me and will be helpful for me. I am requesting you to share this and also try to improve me. Thanks.