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.
Related
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);
}
}
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();
}
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.
I'm looking for way to save canvas from windows store app, I have found:
private void CreateSaveBitmap(Canvas canvas, string filename)
{
RenderTargetBitmap renderBitmap = new RenderTargetBitmap(
(int)canvas.Width, (int)canvas.Height,
96d, 96d, PixelFormats.Pbgra32);
// needed otherwise the image output is black
canvas.Measure(new Size((int)canvas.Width, (int)canvas.Height));
canvas.Arrange(new Rect(new Size((int)canvas.Width, (int)canvas.Height)));
renderBitmap.Render(canvas);
//JpegBitmapEncoder encoder = new JpegBitmapEncoder();
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
using (FileStream file = File.Create(filename))
{
encoder.Save(file);
}
}
But that method won't work in windows store app (there is no 5 argument constructor for RenderTargetBitmap no PngBitmapEncoder). So my question is how can I save canvas from windows store app as some kind of image file (jpg, png, etc.) is there any way to do this?
Try this
using System.Runtime.InteropServices.WindowsRuntime
private async Task CreateSaveBitmapAsync(Canvas canvas)
{
if (canvas != null)
{
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(canvas);
var picker = new FileSavePicker();
picker.FileTypeChoices.Add("JPEG Image", new string[] { ".jpg" });
StorageFile file = await picker.PickSaveFileAsync();
if (file != null)
{
var pixels = await renderTargetBitmap.GetPixelsAsync();
using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
var encoder = await
BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);
byte[] bytes = pixels.ToArray();
encoder.SetPixelData(BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Ignore,
(uint)canvas.Width, (uint)canvas.Height,
96, 96, bytes);
await encoder.FlushAsync();
}
}
}
}
I came across this reply and although it looked exactly what I was looking for it didn't work. Adding an error handler revealed:
Value does not fall within the expected range.
Finally I found that:
(uint)canvas.Width, (uint)canvas.Height,
were both 0. After replacing it with:
(uint)canvas.ActualWidth, (uint)canvas.ActualHeight,
it worked. No idea why it worked for Carlos28 and not for me, but with this change it worked for me too and so I thank Xyriod for the answer
Thank you, Xyroid, for your idea and structure.
To get a version of CreateSaveBitmapAsync() to work on 04/24/20, I had to replace your using block with the following.
The changes of your hard-coded "96"'s, and the replacement of "bytes" with its definition, are optional.
using (Windows.Storage.Streams.IRandomAccessStream stream = await fileToWhichToSave.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite))
{
Windows.Graphics.Imaging.BitmapEncoder encoder = await Windows.Graphics.Imaging.BitmapEncoder.CreateAsync(Windows.Graphics.Imaging.BitmapEncoder.JpegEncoderId, stream);
encoder.SetPixelData(
Windows.Graphics.Imaging.BitmapPixelFormat.Bgra8,
Windows.Graphics.Imaging.BitmapAlphaMode.Ignore,
(uint)renderTargetBitmap.PixelWidth,
(uint)renderTargetBitmap.PixelHeight,
Windows.Graphics.Display.DisplayInformation.GetForCurrentView().LogicalDpi,
Windows.Graphics.Display.DisplayInformation.GetForCurrentView().LogicalDpi,
pixels.ToArray());
await encoder.FlushAsync();
}
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/