Click here to Skip to main content
Click here to Skip to main content

Paste Date Time Stamp While Capturing Image in Windows Store App

, 20 Dec 2012
Rate this:
Please Sign up or sign in to vote.
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.

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 Frown | <img src=  

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 Smile | <img src=

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. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Farhan Ghumra
Software Developer (Senior) Simform Solutions Pvt. Ltd.
India India
Windows Metro Store Apps Developer having 1+ year of experience
 
Follow me on: My Blog on Windows 8 | Twitter | Facebook | LinkedIn
 
Check Out My Articles & Tips on CodeProject
Follow on   Twitter   Google+

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web03 | 2.8.140709.1 | Last Updated 20 Dec 2012
Article Copyright 2012 by Farhan Ghumra
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid