I'm an iOS developer learning Windows Store App development with no previous Microsoft technology experience.
I need to save an image to a file. I figured this would be easy, but I've been at it for a day and I've got nothing to show for it. Either this is extremely difficult in Windows, or I'm missing something due to ignorance of the platform / APIs.
The source of the image I need to save is Windows.UI.Xaml.Media.Imaging.RenderTargetBitmap. RenderTargetBitmap can return an image as either a source for Windows.UI.Xaml.Controls.Image or as a IBuffer.
I can verify that RenderTargetBitmap is rendering the image correctly. However, I have not been able to save the image to a file. I'd hoped that Image would have a Save() method, but it doesn't. So I figured I'd need to use the IBuffer somehow. I got close, I was able to save the buffer to disk, but it was unencoded, so I couldn't open it in anything.
So, next, I tried converting the buffer to a stream, encoding that into PNG with BitmapEncoder. That worked, but that left me with my PNG data in a IRandomAccessStream. I have no idea how to save a IRandomAccessStream to a file.
I tried 5-10 different convoluted approaches, such as going through type conversion hell to attempt to turn a IRandomAccessStream into an IBuffer so I could use .WriteAsync() on a file stream. I tried using a DataWriter on a file stream fed by a DataReader on my IRandomAccessStream but I ran into a type mismatch problem. Etc, etc.
So, how can I save my file? Here's what I got so far:
private async void saveButton_Click(object sender, RoutedEventArgs e)
{
//Create a new temporary image for saving
//Image tempImage = new Image();
//Create a render object
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
//Render the app's display buffer
await renderTargetBitmap.RenderAsync(null);
//Set the temp image to the contents of the app's display buffer
//tempImage.Source = renderTargetBitmap;
//Create a new file picker, set the default name, and extenstion
FileSavePicker savePicker = new FileSavePicker();
savePicker.SuggestedFileName = "New LightTable.png";
savePicker.FileTypeChoices.Add("Image", new List<string>(){".png"});
//Get the file the user selected
StorageFile saveFile = await savePicker.PickSaveFileAsync();
//Only move on if the user actually selected a file
if (saveFile != null)
{
//Get a buffer of the pixels captured from the screen
Windows.Storage.Streams.IBuffer buffer = await renderTargetBitmap.GetPixelsAsync();
//Get a stream of the data in the buffer
System.IO.Stream stream = buffer.AsStream();
//Convert the stream into a IRandomAccessStream because I don't know what I'm doing.
Windows.Storage.Streams.IRandomAccessStream raStream = stream.AsRandomAccessStream();
//Attempt to encode the stream into a PNG
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, raStream);
//Get a stream for the file the user selected
Windows.Storage.Streams.IRandomAccessStream fileStream = await saveFile.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite);
//FIND SOME WAY TO SAVE raStream TO fileStream
//Something like:
// await fileStream.WriteAsync(raStream.AsStreamForRead());
}
}
Either I'm just missing the very end: how to write my raStream to a file, or my approach is totally off.
I appreciate the help. Keep in mind I've only been developing with Microsoft tech for a week now. I have no .NET, Silverlight, or other MS tech experience. Saving an encoded image from a UIImage control on iOS is a single method call, so the shear complexity of the solution I'm circling makes me think I'm missing something really easy that I just don't know about.
You need to set the pixel data into the StorageFile stream. See http://basquang.wordpress.com/2013/09/26/windows-store-8-1-save-visual-element-to-bitmap-image-file/ for an example.
Related
I try to save my images on my server, but I can't let my server save file and virus because of that I want to get image content as pixels of rgb and after that I create image by myself.
I can't use bitmap (or other type in C# like bitmapImage, ... etc) and I don't know how I can do this with sixlabors.ImageSharp.
I have some code that I tried but I can't implement the exact logic that I want (code shown here):
[HttpPost("[action]")]
public async Task<IActionResult> Get([FromForm] ImageFormat file)
{
await using var memoryStream = new MemoryStream();
await file.File.CopyToAsync(memoryStream);
IImageFormat format;
using (var image = Image.Load(memoryStream.ToArray(), out format))
{
using (var output = new MemoryStream())
{
image.Save(output, format);
var responseType = format.Name.ToLower();
return File(output.ToArray(), "application/octet-stream", file.File.FileName);
}
}
return null;
}
Can anybody help me with this problem?
i don't see a reason to convert image into image: there are several format zip-algorythms etc.wich you have to support in that case. example jpg is not bitmap, there is convertion issue - quality of image becomes less each conversion time. Image itself is not executable - it can be used only as container for virus body, can't harm your OSystem itself, another executable part should works somewhere.
But even if you would like to store images on disk, in other format - you can convert image to base64 text (one line of code, like example) - it less harmful and well known way to work with any file type. you can zip image by cszip, you can change file name and extension to hide file type.
I don't see a reasson to convert one image to another for this scenario/task.
I've got a bunch of images stored on disk, with the mimetype of the image stored in a database. If I want to save the image to disk I can use Image.Save(string) (or the Async version) without needing to know the MimeType of the image.
However, if I want to save to a stream (for example, Response.Body) then I have to use Image.Save(Stream, IImageEncoder).
How do I get the IImageEncoder I need to save to the stream (without just declaring that "All my images ar png/jpeg/bmp" and using e.g. JpegEncoder)?
ImageSharp can already tell you the mimetype of the input image. With your code if the
mimetype doesn't match the input stream then the decode will fail.
(Image Image, IImageFormat Format) imf = await Image.LoadWithFormatAsync(file.OnDiskFilename);
using Image img = imf.Image;
img.Save(Response.Body, imf.Format);
What we are missing however in the current API v1.0.1 is the ability to save asynchronously while passing the format. We need to add that.
It took a bit of messing about, but I've figured it out:
var file = ... // From DB
ImageFormatManager ifm = Configuration.Default.ImageFormatsManager;
IImageFormat format = ifm.FindFormatByMimeType(file.MimeType);
IImageDecoder decoder = ifm.FindDecoder(format);
IImageEncoder encoder = ifm.FindEncoder(format);
using Image img = await Image.LoadAsync(file.OnDiskFilename, decoder);
// Do what you want with the Image, e.g.: img.Mutate(i => i.Resize(width.Value, height.Value));
Response.ContentType = format.DefaultMimeType; // In case the stored mimetype was something weird
await img.SaveAsync(Response.Body, encoder);
(Although I'm happy to be shown a better way)
I want to take a picture with the CameraCaptureTask, and then save it in a specific folder, in my Project(!). I found some Tutorials, where you can save a pic in the MediaLibrary. But i want to store it right in the Project.
I have the Image as a Bitmap in my code, just have to save it now.
I would really apreciate a tip.
thanks
You can store it in three ways
Using Isolated Storage
Here you just get the stream of the captured image and store it in Isolated Storage as shown here
here
Using Memory Stream
Here you store the captured image stream in the memory
using (MemoryStream stream = new MemoryStream())
{
bitmap2.SaveJpeg(stream, width, height, 0, 100); //Don't change other unless necessary
stream.Close();
}
Using Local Folder Storage
Here you save the captured image stream in Local Folder of your App
StorageFile sampleFile = await dataFolder.CreateFileAsync("image.png");
await FileIO.WriteBytesAsync(sampleFile, capturedImageStream);
I have a javascript Windows 8 application, and I need to convert a bunch of canvases into an animated gif. Here's what I have so far:
I can convert one canvas to a base64 encoded png like this (in javascript):
var base64png = myCanvas.toDataURL()
I can then convert this encoded image into an array of bytes (in a c# class library):
private byte[] GetBytesFromBase64(string base64)
{
string data = base64.Split(',')[1]; // get everything after comma
return Convert.FromBase64String(data);
}
I can then use these bytes to create a gif, and save it on the disk (again, in the c# class library):
public async void ConvertToGif(string image)
{
// Note: The following line includes methods not shown in the question, but they work
IRandomAccessStream stream = await ConvertToRandomAccessStream(
ConvertToMemoryStream(
GetBytesFromBase64(image)));
var decoder = await BitmapDecoder.CreateAsync(stream);
var pixels = await decoder.GetPixelDataAsync();
var file = await KnownFolders.PicturesLibrary.CreateFileAsync("test.gif", CreationCollisionOption.ReplaceExisting);
var outStream = await file.OpenAsync(FileAccessMode.ReadWrite);
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.GifEncoderId, outStream);
encoder.SetPixelData(
decoder.BitmapPixelFormat,
BitmapAlphaMode.Ignore,
decoder.PixelWidth,
decoder.PixelHeight,
decoder.DpiX,
decoder.DpiY,
ReplaceTransparentWithWhite(pixels));
await encoder.FlushAsync();
outStream.Dispose();
}
This, however, only saves one canvas as one gif. How do I add frames to the gif? There is a GifEncoder class in the System.Media.Imaging namespace, but it seems to not be included in the WinRT .Net framework. Any help would be appreciated.
I'd suggest you build it your self as you've already got code to convert a Canvas to a single GIF file. Basically, an animated GIF is just a series of GIF files within a container GIF file. While the specification is a bit wordy, you should find this example from the .NET 1.1 days very useful.
Although the standard file format didn't directly allow animation, through "extensions" animations are allowed. The details are well documented on this Wikipedia page.
The core of what you'll need to do is write out a custom header block for the file, and then the individual streams for each animated GIF frame.
I was loading a Bitmap Image from a File. When I tried to save the Image to another file I got the following error "A generic error occurred in GDI+". I believe this is because the file is locked by the image object.
Ok so tried calling the Image.Clone function. This still locks the file.
hmm. Next I try loading a Bitmap Image from a FileStream and load the image into memory so GDI+ doesn't lock the file. This works great except I need to generate thumbnails using Image.GetThumbnailImage method it throws an out of memory exception. Apparently I need to keep the stream open to stop this exception but if I keep the stream open then the file remains locked.
So no good with that method. In the end I created a copy of the file. So now I have 2 versions of the file. 1 I can lock and manipulate in my c# program. This other original file remains unlocked to which I can save modifications to. This has the bonus of allowing me to revert changes even after saving them because I'm manipulating the copy of the file which cant change.
Surely there is a better way of achieving this without having to have 2 versions of the image file. Any ideas?
Well if you're looking for other ways to do what you're asking, I reckon it should work to create a MemoryStream, and read out the FileStream to it, and load the Image from that stream...
var stream = new FileStream("original-image", FileMode.Open);
var bufr = new byte[stream.Length];
stream.Read(bufr, 0, (int)stream.Length);
stream.Dispose();
var memstream = new MemoryStream(bufr);
var image = Image.FromStream(memstream);
Or something prettier to that extent.
Whether or not that's the way you should go about solving that problem, I don't know. :)
I've had a similar problem and wound up fixing it like this.
I have since found an alternative method to clone the image without locking the file. Bob Powell has it all plus more GDI resources.
//open the file
Image i = Image.FromFile(path);
//create temporary
Image t=new Bitmap(i.Width,i.Height);
//get graphics
Graphics g=Graphics.FromImage(t);
//copy original
g.DrawImage(i,0,0);
//close original
i.Dispose();
//Can now save
t.Save(path)
I had a similar problem. But I knew, that I will save the image as a bitmap-file. So I did this:
public void SaveHeightmap(string path)
{
if (File.Exists(path))
{
Bitmap bitmap = new Bitmap(image); //create bitmap from image
image.Dispose(); //delete image, so the file
bitmap.Save(path); //save bitmap
image = (Image) bitmap; //recreate image from bitmap
}
else
//...
}
Sure, thats not the best way, but its working :-)