Can I convert image/bitmap to writeablebitmap? - c#

I have two bitmap images that i would like to merge together. I found that I can do that with writeablebitmap but how do I first convert these images to writeablebitmaps?
UPDATE: I could not find out a way to convert bitmaps to writeable bitmap directly so what I did was to write my bitmap in isolated storage and read it again in a stream object. After which the code given below by Xyroid can be used to merge the images and convert the merged image to bitmap.

Here I am giving you the code to merge two images. WinRT's WriteableBitmap is different, the constructor of it takes height and width as argument. I have used WriteableBitmapEx for some functions.
XAML
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Canvas x:Name="BaseCanvas" Width="683" Height="768">
<Image Source="Assets/img1.png" />
<Image Source="Assets/img2.png" Canvas.Top="308" />
</Canvas>
<Image x:Name="imgTarget" Grid.Column="1" Stretch="None"/>
</Grid>
C#
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
StorageFile destiFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync("Merged.png", CreationCollisionOption.ReplaceExisting);
WriteableBitmap wb;
wb = await Render();
using (IRandomAccessStream stream = await destiFile.OpenAsync(FileAccessMode.ReadWrite))
{
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(
BitmapEncoder.PngEncoderId, stream);
Stream pixelStream = wb.PixelBuffer.AsStream();
byte[] pixels = new byte[pixelStream.Length];
await pixelStream.ReadAsync(pixels, 0, pixels.Length);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore,
(uint)wb.PixelWidth, (uint)wb.PixelHeight, 96.0, 96.0, pixels);
await encoder.FlushAsync();
}
var bitmp = new BitmapImage();
using (var strm = await destiFile.OpenReadAsync())
{
bitmp.SetSource(strm);
imgTarget.Source = bitmp;
}
}
private async Task<WriteableBitmap> Render()
{
var Assets = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFolderAsync("Assets");
StorageFile file1 = await Assets.GetFileAsync("img1.png");
StorageFile file2 = await Assets.GetFileAsync("img2.png");
BitmapImage i1 = new BitmapImage();
BitmapImage i2 = new BitmapImage();
using (IRandomAccessStream strm = await file1.OpenReadAsync())
{
i1.SetSource(strm);
}
using (IRandomAccessStream strm = await file2.OpenReadAsync())
{
i2.SetSource(strm);
}
WriteableBitmap img1 = new WriteableBitmap(i1.PixelWidth, i1.PixelHeight);
WriteableBitmap img2 = new WriteableBitmap(i2.PixelWidth, i2.PixelHeight);
using (IRandomAccessStream strm = await file1.OpenReadAsync())
{
img1.SetSource(strm);
}
using (IRandomAccessStream strm = await file2.OpenReadAsync())
{
img2.SetSource(strm);
}
WriteableBitmap destination = new WriteableBitmap((int)(img1.PixelWidth > img2.PixelWidth ? img1.PixelWidth : img2.PixelWidth), (int)(img1.PixelHeight + img1.PixelHeight));
destination.Clear(Colors.White);
destination.Blit(new Rect(0, 0, (int)img1.PixelWidth, (int)img1.PixelHeight),img1,new Rect(0, 0, (int)img1.PixelWidth, (int)img1.PixelHeight));
destination.Blit(new Rect(0, (int)img1.PixelHeight, (int)img2.PixelWidth, (int)img2.PixelHeight), img2, new Rect(0, 0, (int)img2.PixelWidth, (int)img2.PixelHeight));
return destination;
}
Please note you have to add System.Runtime.InteropServices.WindowsRuntime namespace.
UPDATE 1
Suppose if you have already two BitmapImage img1 and img2, then do like this
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
StorageFile destiFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync("Merged.png", CreationCollisionOption.ReplaceExisting);
WriteableBitmap wb;
wb = await Render();
using (IRandomAccessStream stream = await destiFile.OpenAsync(FileAccessMode.ReadWrite))
{
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(
BitmapEncoder.PngEncoderId, stream);
Stream pixelStream = wb.PixelBuffer.AsStream();
byte[] pixels = new byte[pixelStream.Length];
await pixelStream.ReadAsync(pixels, 0, pixels.Length);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore,
(uint)wb.PixelWidth, (uint)wb.PixelHeight, 96.0, 96.0, pixels);
await encoder.FlushAsync();
}
var bitmp = new BitmapImage();
using (var strm = await destiFile.OpenReadAsync())
{
bitmp.SetSource(strm);
imgTarget.Source = bitmp;
}
}
private async Task<WriteableBitmap> Render()
{
WriteableBitmap destination = new WriteableBitmap((int)(img1.PixelWidth > img2.PixelWidth ? img1.PixelWidth : img2.PixelWidth), (int)(img1.PixelHeight + img1.PixelHeight));
destination.Clear(Colors.White);
destination.Blit(new Rect(0, 0, (int)img1.PixelWidth, (int)img1.PixelHeight),img1,new Rect(0, 0, (int)img1.PixelWidth, (int)img1.PixelHeight));
destination.Blit(new Rect(0, (int)img1.PixelHeight, (int)img2.PixelWidth, (int)img2.PixelHeight), img2, new Rect(0, 0, (int)img2.PixelWidth, (int)img2.PixelHeight));
return destination;
}

I've done a lot of work with Silverlight, which I believe the store apps are similar to in many ways.
Consider this constructor:
WriteableBitmap(BitmapSource)
- Initializes a new instance of the WriteableBitmap class using the
provided BitmapSource.
The next question is, how to get 'BitmapSource' from an image? You can do it this way:
(BitmapSource)MyImage.Source
Although this assumes that the source (which is of type 'ImageSource') is actually a 'BitmapSource' instance. That said, as of Silverlight 5.0, the only class derived from ImageSource in Silverlight is BitmapSource, so I doubt that would be an issue.
So something like this may work:
WriteableBitmap((BitmapSource)MyImage.Source)
Finally, there's an open source project here which may be of some help: http://writeablebitmapex.codeplex.com/

Related

How to concat two images programatically using UWP

I can get an image of a grid in my application using:
RenderTargetBitmap rtb_grid = new RenderTargetBitmap();
await rtb_grid.RenderAsync(grid);
var grid_pixel_buffer = await rtb_grid.GetPixelsAsync();
var grid_pixels = grid_pixel_buffer.ToArray();
And I know I can save this to external file (in this case a stream) using:
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
encoder.SetPixelData(BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Premultiplied,
(uint)rtb_grid.PixelWidth,
(uint)rtb_grid.PixelHeight,
displayInformation.RawDpiX,
displayInformation.RawDpiY,
grid_pixels);
But in my scenario, now I have two different grids in different places with their corresponding pixels (byte[]). So how can I concat these images (left to right) in just one, to perform the second step saving only a bigger image with the first two inside?
To concat two images programatically, you can try to use Win2D method to achieve. Create a CanvasRenderTarget among with the total size of two images first, then draw these two images onto it with bytes. After that, saving it to your file. For example:
.xaml:
<StackPanel>
<Grid x:Name="ImageA">
<Image Source="Assets/StoreLogo.png"></Image>
</Grid>
<Grid x:Name="ImageB">
<Image Source="Assets/3.jpg"></Image>
</Grid>
</StackPanel>
.cs:
public async void SaveImage()
{
RenderTargetBitmap rtb_grid = new RenderTargetBitmap();
await rtb_grid.RenderAsync(ImageA);
var grid_pixel_buffer = await rtb_grid.GetPixelsAsync();
byte[] grid_pixels = grid_pixel_buffer.ToArray();
RenderTargetBitmap rtb_grid2 = new RenderTargetBitmap();
await rtb_grid2.RenderAsync(ImageB);
var grid_pixel_buffer2 = await rtb_grid2.GetPixelsAsync();
byte[] grid_pixels2 = grid_pixel_buffer2.ToArray();
StorageFile destinationFile = await ApplicationData.Current.LocalFolder.CreateFileAsync("MyImge.png", CreationCollisionOption.ReplaceExisting);
CanvasDevice device = CanvasDevice.GetSharedDevice();
CanvasRenderTarget renderTarget = new CanvasRenderTarget(device, (int)(rtb_grid.PixelWidth + rtb_grid2.PixelWidth), Math.Max(rtb_grid.PixelHeight, rtb_grid2.PixelHeight), 96);
using (var ds = renderTarget.CreateDrawingSession())
{
ds.Clear(Colors.White);
var image = CanvasBitmap.CreateFromBytes(device, grid_pixels,rtb_grid.PixelWidth,rtb_grid.PixelHeight, DirectXPixelFormat.B8G8R8A8UIntNormalized);
ds.DrawImage(image,0,0);
var image2 = CanvasBitmap.CreateFromBytes(device, grid_pixels2, rtb_grid2.PixelWidth, rtb_grid2.PixelHeight, DirectXPixelFormat.B8G8R8A8UIntNormalized);
ds.DrawImage(image2, (float)rtb_grid.PixelWidth, 0);
}
using (var fileStream = await destinationFile.OpenAsync(FileAccessMode.ReadWrite))
{
await renderTarget.SaveAsync(fileStream, CanvasBitmapFileFormat.Png, 1f);
}
}

How to save rotated image with specific rotation value

I need help with saving rotated image in uwp. The code is in C#. The rotation is made by slider and the rotation value is a specific value(not 90,180,270).
This is my code
<Image
Name="RotatingImage"
Margin="0,100,500,100"
HorizontalAlignment="Right"
VerticalAlignment="Center"
RenderTransformOrigin="0.5,0.5"
Stretch="Uniform"
Tapped="RotatingImage_Tapped">
<Image.RenderTransform>
<!-- That's the part which I've added, on top of the auto-generated xaml -->
<RotateTransform />
<!-- because the ThumbnailImageStyle defines width and height as 228 -->
</Image.RenderTransform>
</Image>
<Slider
x:Name="rotationSlider"
Maximum="360"
Margin="1058,120,82,0"
VerticalAlignment="Top"
ValueChanged="RotationSlider_ValueChanged" />
C#
private void RotationSlider_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
Slider slider = sender as Slider;
if (slider != null)
{
RotationAngle = (int)slider.Value;
}
RotatingImage.RenderTransform = new RotateTransform
{
Angle = RotationAngle
};
}
Put the image in a grid named imageGrid, set the grid's background to Transparent.
Then try below code:
InMemoryRandomAccessStream newStream = new InMemoryRandomAccessStream();
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(this.imageGrid);
var pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, newStream);
encoder.SetPixelData(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Straight,
(uint)renderTargetBitmap.PixelWidth,
(uint)renderTargetBitmap.PixelHeight,
DisplayInformation.GetForCurrentView().LogicalDpi,
DisplayInformation.GetForCurrentView().LogicalDpi,
pixelBuffer.ToArray());
await encoder.FlushAsync();
Then you can save the stream.
Hope this can help you.
As has been said above, first you will need to place the image in a Grid or a Canvas.
Then, try the following snippet.
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(MyGrid, MyGrid.ActualWidth, MyGrid.ActualHeight);
var pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
var pixels = pixelBuffer.ToArray();
var displayInformation = DisplayInformation.GetForCurrentView();
var folder = await ApplicationData.Current.LocalFolder.CreateFolderAsync("Images", CreationCollisionOption.OpenIfExists);
var file = await folder.CreateFileAsync("RotatedImage" + ".png", CreationCollisionOption.ReplaceExisting);
using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
encoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.Cubic;
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied, (uint)renderTargetBitmap.PixelWidth, (uint)renderTargetBitmap.PixelHeight, displayInformation.RawDpiX, displayInformation.RawDpiY, pixels);
await encoder.FlushAsync();
}
This will save the image into a folder named "Images" within your packages LocalFolder.
Use RenderTargetBitmap to save current view.
for example,
RenderTargetBitmap bmp = new RenderTargetBitmap({Width}, {Height}, {DpiX}, {DpiY}, PixelFormats.Default);
bmp.Render({ControlWhichYouWantToSave});
PngBitmapEncoder encoder = new PngBitmapEncoder();
encover.Frames.Add(BitmapFrame.Create(bmp));
using ( FileStream fs = File.Open({Location}, FileMode.Create))
{
encoder.Save(fs);
fs.Close();
}
if this doesn't work, change
bmp.Render({ControlWhichYouWantToSave});
as
bmp.Render({ContrplParentWhichYouWantToSave});
hope this work.

How I get real size image when I use RenderTargetBitmap or Capture UI

I want to get real size image when I use RenderTargetBitmap or Capture UI because When I RenderTargetBitmap or Capture UI. Image is blur and don't clear but when I expand Image to original size image don't blur and clear.
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(ImageAndSticker,2500,3750);
StorageFile file = await KnownFolders.CameraRoll.CreateFileAsync("snapshot" + DateTime.Now.ToString("MM-dd-yyyy h.mm.ss.fff tt") + ".jpg", CreationCollisionOption.GenerateUniqueName);
storageFile = file;
var pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
var pixels = pixelBuffer.ToArray();
var displayInformation = DisplayInformation.GetForCurrentView();
using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)renderTargetBitmap.PixelWidth, (uint)renderTargetBitmap.PixelHeight, displayInformation.RawDpiX, displayInformation.RawDpiY, pixels);
await encoder.FlushAsync();
}
<Viewbox Margin="254.8,8,659,474" Stretch="Uniform" StretchDirection="Both" x:Name="ViewImage" Grid.Column="1" Grid.Row="0">
<Image x:Name="frameimage" Margin="176.8,2,459,135" Grid.Column="1" Height="3750" Width="2500" Canvas.Left="-458" Canvas.Top="-641"/>
</Viewbox>
Use
frameimage.Source.Width
to get source image's width
samething do with Height will get source image's Height value like this
frameImage.Source.Height
I think you wish to save Source image as file.
if then, use this code.
BitmapSource source = frameImage.Source as BitmapSource;
using ( var FileStream = new FileStream( FileLoc, FileMode.Create, FileAccess.ReadWrite) ) {
BitmapEncoder Encoder = new JpegBitmapEncoder();
Encoder.Frames.Add(BitmapFrame.Create(source));
Encoder.Save(FileStream);
FileStream.Dispose();
}

How to save contents of CanvasControl as an image file

I'm making a paint-like program using Win2D. My CanvasControl contains some text, images and some lines which the user has drawn. I want to save the entire contents of this CanvasControl as a file on disk (in any standard image format). I want to do this as I want to display it (at a later time) inside a standard Image control.
How would I do this? I tried using RenderTargetBitmap to load the CanvasControl (code below) but for some reason it clips the image and only a small horizontal top-portion image is made.
async private void Button_Click(object sender, RoutedEventArgs e)
{
#region (c) rendering UIElement to bitmap code
var bitmap = new RenderTargetBitmap();
await bitmap.RenderAsync(ccDraw); // ccDraw is CanvasControl
// get the pixels
IBuffer pixelBuffer = await bitmap.GetPixelsAsync();
byte[] pixels = pixelBuffer.ToArray();
// write the pixels to a InMemoryRandomAccessStream
var stream = new InMemoryRandomAccessStream();
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.BmpEncoderId, stream);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight, (uint)bitmap.PixelWidth, (uint)bitmap.PixelHeight, 96, 96, pixels);
await encoder.FlushAsync();
stream.Seek(0);
Image iNew = new Image();
iNew.Stretch = Stretch.None;
iNew.Source = bitmap;
gOuter.Children.Add(iNew);
ccDraw.Visibility = Visibility.Collapsed; // hide CanvasControl so we can see added image
#endregion
}
Here is how i did this for me. imageSize is size of your image, for example var imageSize = new Size(500,500);
var displayInformation = DisplayInformation.GetForCurrentView();
ccDraw.Measure(imageSize);
ccDraw.UpdateLayout();
ccDraw.Arrange(new Rect(0, 0, imageSize.Width, imageSize.Height));
var renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(ccDraw, Convert.ToInt32(imageSize.Width), Convert.ToInt32(imageSize.Height));
var pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
var file = await ApplicationData.Current.LocalFolder.CreateFileAsync("Screen.jpg", CreationCollisionOption.ReplaceExisting);
using (var fileStream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, fileStream);
encoder.SetPixelData(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Ignore,
(uint)renderTargetBitmap.PixelWidth,
(uint)renderTargetBitmap.PixelHeight,
displayInformation.LogicalDpi,
displayInformation.LogicalDpi,
pixelBuffer.ToArray());
await encoder.FlushAsync();
}
Try this:
private void SaveCanvasAsImage()
{
Rect yourRect = new Rect(ccDraw.RenderSize);
RenderTargetBitmap rtBitmap = new RenderTargetBitmap((int)yourRect.Right,
(int)yourRect.Bottom, 100d, 100d, System.Windows.Media.PixelFormats.Default);
rtBitmap.Render(ccDraw);
BitmapEncoder pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(BitmapFrame.Create(rtBitmap));
System.IO.MemoryStream ms = new System.IO.MemoryStream();
pngEncoder.Save(ms);
ms.Close();
System.IO.File.WriteAllBytes("yourPng.png", ms.ToArray());
}
You cannot directly save the contents of a CanvasControl.
However, if you draw to an intermediate CanvasRenderTarget then you can save that using SaveAsync.
More information on drawing offscreen can be found at http://microsoft.github.io/Win2D/html/Offscreen.htm.
There's other benefits to drawing to a CanvasRenderTarget rather than drawing directly to the CanvasControl - the most obvious is that it allows you to do additive rendering, rather than having to redraw the entire display each time.

How to share a screenshot?

I want to share the screenshot of the app on Twitter, Facebook, etc. This is my code: it saves the picture, but doesn't open the share media task. I know the problem is in the path :{
var wb = new WriteableBitmap(LayoutRoot, new TranslateTransform());
using (var mediaLibrary = new MediaLibrary()) {
using (var stream = new MemoryStream()) {
var fileName = string.Format("{0}.jpg", DateTime.Now.ToString("yyyy-MM-dd-hh-mm-ss"));
wb.SaveJpeg(stream, wb.PixelWidth, wb.PixelHeight, 0, 100);
stream.Seek(0, SeekOrigin.Begin);
mediaLibrary.SavePicture(fileName, stream);
shareMediaTask = new ShareMediaTask();
shareMediaTask.FilePath = fileName;
shareMediaTask.Show();
}
}
How can I get the saved picture's path?
Isn't it possible to just simply take a screenshot and share it without saving it on the phone?
To get the real path for the MediaLibrary file, you'll need to use the GetPath() extension method, something like;
using Microsoft.Xna.Framework.Media.PhoneExtensions;
...
var picture = mediaLibrary.SavePicture(fileName, stream);
shareMediaTask = new ShareMediaTask();
shareMediaTask.FilePath = picture.GetPath();
shareMediaTask.Show();
For sharing the screen shot it is not required to save the image , in windows 8.1 it is very easy.
Here is the code, enjoy!
async void dataTransferMgr_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
DataRequest request = args.Request;
request.Data.Properties.Title = "Title";
request.Data.Properties.Description = "brief description";
request.Data.SetText("detailed information");
RandomAccessStreamReference imageStreamRef = await ScreenshotToStreamReferenceAsync(yourChartControlName);
request.Data.Properties.Thumbnail = imageStreamRef;
request.Data.SetBitmap(imageStreamRef);
}
private async Task ScreenshotToStreamAsync(FrameworkElement element, IRandomAccessStream stream)
{
var renderTargetBitmap = new Windows.UI.Xaml.Media.Imaging.RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(element);
var pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
var dpi = Windows.Graphics.Display.DisplayInformation.GetForCurrentView().LogicalDpi;
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
encoder.SetPixelData(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Ignore,
(uint)renderTargetBitmap.PixelWidth,
(uint)renderTargetBitmap.PixelHeight,
dpi,
dpi,
pixelBuffer.ToArray());
await encoder.FlushAsync();
}
private async Task<RandomAccessStreamReference> ScreenshotToStreamReferenceAsync(FrameworkElement element)
{
var ms = new InMemoryRandomAccessStream();
await ScreenshotToStreamAsync(element, ms);
ms.Seek(0);
return RandomAccessStreamReference.CreateFromStream(ms);
}

Categories