Convert Xamarin.Forms.Image to Byte Array - c#

Simple Question: I have a Xamarin.Forms.Image called "image". I need it to be a byte array. I feel like this has to be a simple thing but I can't find it on the internet anywhere.
var image = Xamarin.Forms.Image();
image.source = "my_image_source.png";
//Magic code
byte[] imgByteArray = ???

Unfortunately, the Image class does not expose methods to access the actual image once loaded.
You will need to read the Image from the file and convert it to the Stream you are expecting.
To prevent reading the image object twice you can set the stream read to your Image control.
var image = Xamarin.Forms.Image();
var assembly = this.GetType().GetTypeInfo().Assembly;
byte[] imgByteArray = null;
using (var s = assembly.GetManifestResourceStream("my_image_source.png"))
{
if (s != null)
{
var length = s.Length;
imgByteArray = new byte[length];
s.Read(buffer, 0, (int)length);
image.Source = ImageSource.FromStream(() => s);
}
}
// here imageByteArray will have the bytes from the image file or it will be null if the file was not loaded.
if (imgByteArray != null)
{
//use your data here.
}
Hope this helps.-
Update:
This code will require adding your my_image_source.png as part of the PCL as an embedded resource.

Related

Convert from HttpPostedFileBase to image produce error due position

I have the following controller, which upon receiveing a file from the view generates a thumbnail and saves both, image abd thumbnail to the database:
public async Task<ActionResult> FileUpload(HttpPostedFileBase file)
{
if (file != null && file.ContentLength > 0)
{
var logo = new Logo() { LogoGuid = Guid.NewGuid() };
var clinica = await GetClinicaAsync();
if (IsClinicNonexistent(clinica))
{
db.Logos.Add(logo);
clinica.LogoGuid = logo.LogoGuid;
}
else
logo = await db.Logos.FirstOrDefaultAsync(l => l.LogoGuid == clinica.LogoGuid);
logo.LogoImage = GetImage(file);
logo.Thumbnail = GetThumbnail(file);
db.SaveChanges();
}
return RedirectToAction("Arquivos");
}
Which, for the matters of this error calls a method:
private byte[] GetThumbnail(HttpPostedFileBase file)
{
var image = Image.FromStream(file.InputStream);
var thumbnailSize = GetThumbnailSize(image);
var memoryStream = new MemoryStream();
var thumbnail = image.GetThumbnailImage(thumbnailSize.Width, thumbnailSize.Height, null, IntPtr.Zero);
image.Save(memoryStream, ImageFormat.Jpeg);
return memoryStream.ToArray();
}
That configuration of code produces an erro while running the line
var image = Image.FromStream(file.InputStream);
The error is: "System.ArgumentException: Parameter is not valid."
All documentation that I could refer to, makes me believe that it's all sintatically and logically correct, yet there is the error...
But, tne funny thing is, if a move the conversion line which produces error, do the calling method, specifically to the begining as:
public async Task<ActionResult> FileUpload(HttpPostedFileBase file)
{
if (file != null && file.ContentLength > 0)
{
var logo = new Logo() { LogoGuid = Guid.NewGuid() };
var clinica = await ClinicaAsync();
var image = Image.FromStream(file.InputStream);
There will be no error. Please notice that the convertion line is in the last line of the code just above, thus in the begining of the controller. Again, if I move that line further down, but still in the controller, there will be the error again...
How can I keep the line where it logically belongs, thus in the GetThumbnail method?
Just as an additional reference, the GetImage method is:
private byte[] GetImage(HttpPostedFileBase file)
{
using (var br = new BinaryReader(file.InputStream))
return br.ReadBytes(file.ContentLength);
}
The first thing you should know is Image.FromStream() requires a stream (MemoryStream, InputStream, etc.) that hasn't been already read, or the position is reset to beginning using Seek() method.
First, try to reset the pointer before calling GetThumbnail() method by using Position property or Seek():
// alternative 1
logo.LogoImage = GetImage(file);
file.InputStream.Position = 0;
logo.Thumbnail = GetThumbnail(file);
// alternative 2
logo.LogoImage = GetImage(file);
file.InputStream.Seek(0, SeekOrigin.Begin);
logo.Thumbnail = GetThumbnail(file);
If this doesn't work, the possible cause is you're calling Image.FromStream() after HttpPostedFileBase.InputStream has already used (and disposed) by BinaryReader which calls Dispose() (the using statement automatically called it), thus the exception is thrown.
You can create a copy of MemoryStream from HttpPostedFileBase.InputStream using Stream.CopyTo() like below example:
public async Task<ActionResult> FileUpload(HttpPostedFileBase file)
{
if (file != null && file.ContentLength > 0)
{
var logo = new Logo() { LogoGuid = Guid.NewGuid() };
var clinica = await GetClinicaAsync();
var ms = new MemoryStream();
file.InputStream.CopyTo(ms); // copy HTTP stream to memory stream
if (IsClinicNonexistent(clinica))
{
db.Logos.Add(logo);
clinica.LogoGuid = logo.LogoGuid;
}
logo.LogoImage = GetImage(file);
ms.Position = 0; // make sure the stream is read from beginning
logo.Thumbnail = GetThumbnail(ms);
db.SaveChanges();
}
return RedirectToAction("Arquivos");
}
Then GetThumbnail() parameter should be changed to accept MemoryStream copied from HTTP stream:
// get image from memory stream
private byte[] GetThumbnail(MemoryStream file)
{
var image = Image.FromStream(ms);
var thumbnailSize = GetThumbnailSize(image);
var thumbnail = image.GetThumbnailImage(thumbnailSize.Width, thumbnailSize.Height, null, IntPtr.Zero);
image.Save(file, ImageFormat.Jpeg);
return file.ToArray();
}
Note: You can also save the stream as image file on the disk and then read it with Server.MapPath() to generate thumbnail.
Related issues:
Image.FromStream(PostedFile.InputStream) Fails. (Parameter is not valid.) (AsyncFileUpload))
How do you convert a HttpPostedFileBase to an Image?

Send InkCanvas signature using WCF call

I have a Windows 10 UWP app that will run on Windows 10 Mobile. A requirement I have is to capture a signature from the user. So far, I am simply using an InkCanvas in XAML and have it wired up to my code behind.
I then have a button that when clicked, will take the signature on the InkCanvas and send it to the server via a WCF call. The server and WCF service is already existing. It takes in the signature image as a base64 serialized string.
I know how to get the base64 once I have either an image or a byte array. However, in my many hours of reading, I am finding that articles/examples were either written for WPF or Windows 8.1 and do not work on Windows 10 UWP. Also, of the examples that I have found that will work, it seems my only option is to save the signature to file as a GIF.
I see that I can call GetStrokes() like this
var strokeCollection = cvsSignature.InkPresenter.StrokeContainer.GetStrokes();
Which will return me a read only list of InkStroke. I guess I can iterate that list and build a byte array? How would I do that? It seems this is not efficient?
Otherwise, I thought I could just change the stream from a file stream to a memory stream but I guess either this is not possible or I am missing something. I am trying this
using (var inkMemStream = new MemoryStream())
{
await cvsSignature.InkPresenter.StrokeContainer.SaveAsync(inkMemStream);
}
But with this type of approach I get an exception that I cannot convert from System.IO.MemoryStream to Windows.Storage.Streams.IOutputStream
Thanks!
I found a way, if you don't want to save your InkCanvas to a file or as a GIF, to manipulate it in memory. I actually have three options below. Note, for some of this, you will need to add a reference to Win2D.uwp, which can be found on NuGet by searching that exact name. It is provided by Microsoft.
Convert the InkCanvas to a byte array:
private byte[] ConvertInkCanvasToByteArray()
{
//First, we need to get all of the strokes in the canvas
var canvasStrokes = myCanvas.InkPresenter.StrokeContainer.GetStrokes();
//Just as a check, make sure to only do work if there are actually strokes (ie not empty)
if (canvasStrokes.Count > 0)
{
var width = (int)myCanvas.ActualWidth;
var height = (int)myCanvas.ActualHeight;
var device = CanvasDevice.GetSharedDevice();
//Create a new renderTarget with the same width and height as myCanvas at 96dpi
var renderTarget = new CanvasRenderTarget(device, width,
height, 96);
using (var ds = renderTarget.CreateDrawingSession())
{
//This will clear the renderTarget with a clean slate of a white background.
ds.Clear(Windows.UI.Colors.White);
//Here is where we actually take the strokes from the canvas and draw them on the render target.
ds.DrawInk(myCanvas.InkPresenter.StrokeContainer.GetStrokes());
}
//Finally, this will return the render target as a byte array.
return renderTarget.GetPixelBytes();
}
else
{
return null;
}
}
If all you need is a byte array of the pixels, you are done. However, if you now want to generate an image, you use the WriteableBitmap to do so. Here are sync and async methods that can be called to do this.
private WriteableBitmap GetSignatureBitmapFull()
{
var bytes = ConvertInkCanvasToByteArray();
if (bytes != null)
{
var width = (int)cvsSignature.ActualWidth;
var height = (int)cvsSignature.ActualHeight;
var bmp = new WriteableBitmap(width, height);
using (var stream = bmp.PixelBuffer.AsStream())
{
stream.Write(bytes, 0, bytes.Length);
return bmp;
}
}
else
return null;
}
private async Task<WriteableBitmap> GetSignatureBitmapFullAsync()
{
var bytes = ConvertInkCanvasToByteArray();
if (bytes != null)
{
var width = (int)cvsSignature.ActualWidth;
var height = (int)cvsSignature.ActualHeight;
var bmp = new WriteableBitmap(width, height);
using (var stream = bmp.PixelBuffer.AsStream())
{
await stream.WriteAsync(bytes, 0, bytes.Length);
return bmp;
}
}
else
return null;
}
Finally, here is an async method that I am using to be able to do a crop on the bitmap if you want to do that. The key thing with this method is notice how with this approach, you don't have to get the pixel bytes as an array first. You just get the strokes from the canvas and then it is saved directly into a memory stream
private async Task<WriteableBitmap> GetSignatureBitmapCropped()
{
try
{
var canvasStrokes = cvsSignature.InkPresenter.StrokeContainer.GetStrokes();
if (canvasStrokes.Count > 0)
{
var bounds = cvsSignature.InkPresenter.StrokeContainer.BoundingRect;
var xOffset = (uint)Math.Round(bounds.X);
var yOffset = (uint)Math.Round(bounds.Y);
var pixelWidth = (int)Math.Round(bounds.Width);
var pixelHeight = (int)Math.Round(bounds.Height);
using (var memStream = new InMemoryRandomAccessStream())
{
await cvsSignature.InkPresenter.StrokeContainer.SaveAsync(memStream);
var decoder = await BitmapDecoder.CreateAsync(memStream);
var transform = new BitmapTransform();
var newBounds = new BitmapBounds();
newBounds.X = 0;
newBounds.Y = 0;
newBounds.Width = (uint)pixelWidth;
newBounds.Height = (uint)pixelHeight;
transform.Bounds = newBounds;
var pdp = await decoder.GetPixelDataAsync(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Straight,
transform,
ExifOrientationMode.IgnoreExifOrientation,
ColorManagementMode.DoNotColorManage);
var pixels = pdp.DetachPixelData();
var cropBmp = new WriteableBitmap(pixelWidth, pixelHeight);
using (var stream = cropBmp.PixelBuffer.AsStream())
{
await stream.WriteAsync(pixels, 0, pixels.Length);
}
return cropBmp;
}
}
else
{
return null;
}
}
catch (Exception ex)
{
return null;
}
}
Hope this helps provide some alternatives when using InkCanvas in Windows 10 Universal.
it seems my only option is to save the signature to file as a GIF.
GIF is not the only choice. The official sample just show GIF, but you can also save the InkStokes collection to JPG and PNG. Code as follows:
var savePicker = new Windows.Storage.Pickers.FileSavePicker();
savePicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
savePicker.FileTypeChoices.Add("Gif,JPG,PNG", new System.Collections.Generic.List<string> { ".jpg" ,".gif",".png"});
Windows.Storage.StorageFile file = await savePicker.PickSaveFileAsync();
But with this type of approach I get an exception that I cannot convert from System.IO.MemoryStream to Windows.Storage.Streams.IOutputStream
As you see, it cannot convert from System.IO.MemoryStream to Windows.Storage.Streams.IOutputStream directly. You need to read the image file you just saved as ImageSource. As you known how to get the base64 once you have either an image or a byte array. So what we just need is to get an image or a byte array from the image file.
Read the image file as byte array and then a memory stream as follows
if (inkCanvas.InkPresenter.StrokeContainer.GetStrokes().Count > 0)
{
var savePicker = new Windows.Storage.Pickers.FileSavePicker();
savePicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
savePicker.FileTypeChoices.Add("Gif,JPG,PNG", new System.Collections.Generic.List<string> { ".jpg", ".gif", ".png" });
Windows.Storage.StorageFile file = await savePicker.PickSaveFileAsync();
if (null != file)
{
using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
await inkCanvas.InkPresenter.StrokeContainer.SaveAsync(stream);
}
using (IRandomAccessStream streamforread = await file.OpenAsync(FileAccessMode.Read))
{
WriteableBitmap inkimagesource = new WriteableBitmap(50, 50);
inkimagesource.SetSource(streamforread);
byte[] imageBuffer = inkimagesource.PixelBuffer.ToArray();
MemoryStream ms = new MemoryStream(imageBuffer);
}
}
}
Read the file as image source just need to change the file reading code as follows:
using (IRandomAccessStream streamforread = await file.OpenAsync(FileAccessMode.Read)
{
var bitmap = new BitmapImage();
bitmap.SetSource(streamforread);
}
I guess I can iterate that list and build a byte array? How would I do that? It seems this is not efficient?
It seems like currently there is no API can build the InkStroke collections as byte array directly. SaveAsync() method has already help you save the InkStroke collection to a stream, you can use code above for using it.

Convert ImageSource to Base64String - WP8

am working on WP8application, I have few images in location Resources\Graphics\ i am trying to display images from these folder, but its not picking the path.
Here is my code :
<img src=\"/Resources;component/Graphics/"+ImageName).Append("\" ") this is in my string which i am using in my WebBrowserControl.
WebBrowserControl.NavigateToString(html); // here html is a string which has all the html code in it.
But its not display the images.
So i want to convert the ImageSource --Resources;component/Graphics/"+ImageName to Base64String how to do it?
I have looked into many examples but none of them is compatible for WP8.
You can get StreamInfo by using this:
Application.GetResourceStream(new Uri("Resources;component/Graphics/"+ImageName", System.UriKind.Relative));
Then you can read this stream into an byte array. After that, use Convert.ToBase64String() to get what you want. Try this. Maybe you can read the MSDN document to find how to use Stream.
var img = Application.GetResourceStream(new Uri("Resources;component/Graphics/"+ImageName", System.UriKind.Relative));
var buffer = new byte[img.Stream.Length];
img.Stream.Seek(0, SeekOrigin.Begin);
img.Stream.Read(buffer, 0, buffer.Length);
var base64 = Convert.ToBase64String(buffer);
It's very simple - load your image into a byte array and call
System.Convert.ToBase64String(imageArray).
That being said, this will not result in displaying the image. The NavigateToString requires html. See documentation
This is my code
byte[] bytearray = null;
using (var ms = new MemoryStream())
{
if (bmp != null)
{
var wbitmp = new WriteableBitmap(bmp);
wbitmp.SaveJpeg(ms, 46, 38, 0, 100);
bytearray = ms.ToArray();
}
}
if (bytearray != null)
{
// image base64 here
string btmStr = Convert.ToBase64String(bytearray);
}

How to store images in an SQL database on WP7

I'm desperately trying to save an image to an SQL database and then load it on my WP. All of the guides online say to convert the image to a Byte array, store it and then load it back into an image.
So far, I've been able to save the image into a Byte array using:
public static byte[] ConvertToBytes(Stream photoStream)
{
byte[] a = new Byte[photoStream.Length];
for (int i = 0; i < photoStream.Length; i++)
{
a[i] = (Byte)photoStream.ReadByte();
}
return (a);
}
This generates a Byte array that is similar in size to the image I'm saving.
The suggested way to load images is:
1 public static BitmapImage ConvertToImage(Byte[] inputBytes)
2 {
3 MemoryStream stream = new MemoryStream(inputBytes);
4 BitmapImage image = new BitmapImage();
5 image.SetSource(stream);
6 return (image);
7 }
This doesn't work.
I get this error (on line 5):
"Unspecified error"
Does anybody have any idea how to fix this or can suggest an alternative method/code?
I know there is information online - i can assure you that i have searched long and hard for a working method and been able to make nothing work.
Any help would be much appreciated!
I managed to solve this using:
public static byte[] ConvertToBytes(String imageLocation)
{
StreamResourceInfo sri = Application.GetResourceStream(new Uri(imageLocation, UriKind.RelativeOrAbsolute));
BinaryReader binary = new BinaryReader(sri.Stream);
byte[] imgByteArray = binary.ReadBytes((int)(sri.Stream.Length));
binary.Close();
binary.Dispose();
return imgByteArray;
}
public static WriteableBitmap ConvertToImage(Byte[] inputBytes)
{
MemoryStream ms = new MemoryStream(inputBytes);
WriteableBitmap img = new WriteableBitmap(400, 400);
img.LoadJpeg(ms);
return (img);
}
Thanks for all your help guys.
i used that code
byte[] Arr = Convert.FromBase64String(Im); >> i think you need that steep
Stream memStream = new MemoryStream(Arr);
WriteableBitmap wbimg = PictureDecoder.DecodeJpeg(memStream);
image2.Source = wbimg;
image2.Tag = IlbumID;
if that nat work
try
memStream.Read (Arr ,0, Arr.Length );

Image to memory stream, based on location

I have an image column in bytes in DB & location (which stores the path of the image),
if the path is empty i used image column, if the path is not empty, i load the image to a byte array and return the image as Memory stream either from file path or direct byte image column.
//query
select image,filepath from tablename;
byte[] Image = { };
if(file path is not empty)
{
System.Net.WebClient Client = new System.Net.WebClient();
Image = Client.DownloadData("Path of the image-http://....gif");
}
else
{
Image= datareader.GetOrdinal("image");
}
How do i assign a byte image to memory stream???
You can use the MemoryStream constructor that takes a byte[] instance:
byte[] data = // get data...
using (var stream = new MemoryStream(data))
{
}
If you already have an open stream, then just write the contents of the array:
stream.Write(data, 0, data.Length);

Categories