Xamarin forms display an image issue - c#

I use a photo picker to let the user choose a profile pic and then I want to display the pic inside of an Image view. the following code works fine for me but then I would like to convert the image to a byte array, once I call the method to convert it the image doesn't display after the user picks it. I also tried to call the method outside the try and catch but no luck.
the image is getting selected (it's getting saved in local storage) so the only issue is that for some reason it doesn't display it in the view.
async void showMediaPicker()
{
var res = await MediaPicker.PickPhotoAsync();
try
{
var stream = await res.OpenReadAsync();
var finalImage = ImageSource.FromStream(() => stream);
myImage.Source = finalImage;
imgBytes = ImageSourceToBytes(finalImage);
}
catch (Exception e)
{
Console.Write("error" + e.Message) ;
}
}

If you want to convert the photo to Byte array , you could use the plugin Media.Plugin from Nuget to pick or take photo . Don't forget to add the relevant permissions on android and iOS platform .
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
DisplayAlert("No Camera", ":( No camera available.", "OK");
return;
}
var file = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
{
SaveToAlbum = true,
Name = "test.jpg"
});
if (file == null)
return;
Then convert it to Stream firstly before convert it to byte array .
Stream stream = file.GetStream();
public byte[] GetImageStreamAsBytes(Stream input)
{
var buffer = new byte[16*1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
Now you can get the byte array like following
var imgDate = GetImageStreamAsBytes(stream);

Related

Convert imageSource coming from Xam.Plugin.Media 5.0.1 to byte array in Xamarinforms?

if (!CrossMedia.Current.IsPickPhotoSupported)
{
await Application.Current.MainPage.DisplayAlert("Photos Not Supported", ":( Permission not granted to photos.", "OK");
return;
}
var file = await Plugin.Media.CrossMedia.Current.PickPhotoAsync(new Plugin.Media.Abstractions.PickMediaOptions
{
PhotoSize = Plugin.Media.Abstractions.PhotoSize.Medium,
});
if (file == null)
return;
var tmpSrc = ImageSource.FromStream(() =>
{
var stream = file.GetStream();
file.Dispose();
return stream;
});
ImageSource toBeConverted = tmpSrc;
I want the variable toBeConverted to be converted into Byte[] so
that I can send it to my webapi ...
ImageSource is a way to provide a source image for Xamarin.Forms.Image to show some content. If you're already showing something on the screen your Image view was populated with data that came from elsewhere, such as a file or resource or stored in an array in memory... or however else you got that in the first place. Instead of trying to get that data back from ImageSource you can keep a reference to it and upload it as needed.
So you could get the byte array from the file after you pick the photo.
var file = await Plugin.Media.CrossMedia.Current.PickPhotoAsync(new Plugin.Media.Abstractions.PickMediaOptions
{
PhotoSize = Plugin.Media.Abstractions.PhotoSize.Medium,
});
if (file == null)
return;
var bytes = File.ReadAllBytes(file.Path); // you could get the byte[] here from the file path.
This code also worked for me ...
private async void Capture()
{
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
await Application.Current.MainPage.DisplayAlert("No Camera", ":( No camera available.", "OK");
return;
}
var file = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
{
Directory = "Test",
SaveToAlbum = true,
CompressionQuality = 75,
CustomPhotoSize = 50,
PhotoSize = PhotoSize.Medium,
DefaultCamera = CameraDevice.Front
});
if (file == null)
return;
var stream = file.GetStream();
if (stream != null)
{
var StreamByte = ReadAllBytes(stream);
var NewStream = new MemoryStream(StreamByte);
// stream = mystream;
Device.BeginInvokeOnMainThread(() => {
ImageSource = ImageSource.FromStream(() => NewStream);
});
student.ProfilePicture = StreamByte;
}
}
public byte[] ReadAllBytes(Stream instream)
{
if (instream is MemoryStream)
return ((MemoryStream)instream).ToArray();
using (var memoryStream = new MemoryStream())
{
instream.CopyTo(memoryStream);
return memoryStream.ToArray();
}
}

Xamarin Forms saving image in sqlite database

How do I save an image I captured directly to sqlite(sqlite-net-pcl) database?
Here's my code but it can only save in "Internal Storage" of the phone.
private async void TakePhotoButton_Clicked(object sender, EventArgs e)
{
try
{
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
await DisplayAlert("No Camera", "No Camera Available", "OK");
return;
}
var file = await CrossMedia.Current.TakePhotoAsync(
new StoreCameraMediaOptions
{
SaveToAlbum = true,
//Directory = "Sample",
//Name = "Test.jpg"
});
if (file == null)
return;
PathLabel.Text = file.AlbumPath;
MainImage.Source = ImageSource.FromStream(() =>
{
var stream = file.GetStream();
file.Dispose();
return stream;
});
}
catch (Exception ex)
{
await DisplayAlert("error", ex.ToString(), "OK");
}
}
You should convert the stream to Byte array so that you save them to sqlite.
public byte[] GetImageStreamAsBytes(Stream input)
{
var buffer = new byte[16*1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
var imgDate = GetImageStreamAsBytes(file.GetStream());
Concerning the SaveToAlbum option
This will restult in 2 photos being saved for the photo. One in your private folder and one in a public directory that is shown. The value will be returned at AlbumPath. (Source)
Unless you really need the photo in the camera roll, there is no need to use SaveToAlbum.
Anyway, the file is still saved to your apps sandbox (the part of the devices storage that is reserved exclusively for your app) and you can retrieve the path with file.Path.
Having said that, it's easy to obtain the binary data representing your image
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
await DisplayAlert("No Camera", "No Camera Available", "OK");
return;
}
var file = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions());
if (file == null)
return;
var imageData = File.ReadAllBytes(file.Path);
Obviously (see here and here) it's possibly to store binary data (BLOBs) in an SQLite database. The simplest conceivable model to store an image in the database would be something like
class Image
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public byte[] Data { get; set; }
}
Assuming that _imageRepository is your repository abstraction you are saving the images in, the data could be saved as
// ...
var file = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions());
if (file == null)
return;
var imageData = File.ReadAllBytes(file.Path);
_imageRepository.Add(new Image()
{
Data = imageData
});
Later on, to display the image, you can get the Image from the repository and use the Data property, e.g. by passing it to a MemoryStream (if you need a stream)
// example: Loading by ID, loading all images is conceivable, too
var image = _imageRepository.LoadImage(id);
ImageControl.ImageSource = ImageSource.FromStream(() => new MemoryStream(image.Data));

Problems selecting a photo or image from the cell phone

I have a problem with views, when I open the camera and take the photo it sends me directly to the home screen, the same happens with an image of the gallery, this only happens with ios, since Android works without any problem. I would like to know what is happening because I have not found the error.
public async Task TakePicture()
{
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
await Application.Current.MainPage.DisplayAlert("Error", "No se encontro una cámara disponible.", "OK");
return;
}
var file = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
{
SaveToAlbum = false,
CompressionQuality = 30,
PhotoSize = PhotoSize.Small
});
if (file == null)
{
return;
}
byte[] array;
using (var memoryStream = new MemoryStream())
{
int count = 0;
file.GetStream().CopyTo(memoryStream);
array = memoryStream.ToArray();
while (count < 10)
{
if (array.Count() == 0)
{
file.GetStream().CopyTo(memoryStream);
array = memoryStream.ToArray();
await Task.Delay(1000);
}
count++;
}
}
Stream stream = new MemoryStream(array);
var image = ImageSource.FromStream(() => stream);
FotoPerfil = image;
User.Foto = array;
}
This part is to take the photo directly from the cell phone, I have no problem opening the camera, the error comes when I choose that photo sends me to another screen, I do not know if it is closing and reopening the application but I do not know what happens.
public async Task PickPicture()
{
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsPickPhotoSupported)
{
await Application.Current.MainPage.DisplayAlert("Error",
"No se otorgaron permisos para accesar a las fotos.", "OK");
return;
}
var file = await CrossMedia.Current.PickPhotoAsync(new PickMediaOptions
{
PhotoSize = PhotoSize.Medium,
CompressionQuality = 30,
});
if (file == null)
{
return;
}
byte[] array;
using (var memoryStream = new MemoryStream())
{
int count = 0;
file.GetStream().CopyTo(memoryStream);
array = memoryStream.ToArray();
while (count < 10)
{
if (array.Count() == 0)
{
file.GetStream().CopyTo(memoryStream);
array = memoryStream.ToArray();
await Task.Delay(1000);
}
count++;
}
}
await Application.Current.MainPage.DisplayAlert("Error", array.ToString(), "OK");
User.Foto = array;
Stream stream = new MemoryStream(array);
var image = ImageSource.FromStream(() => stream);
FotoPerfil = image;
}
The latter is to select any image from the gallery, and exactly the same happens.

How to convert image to base64string Xamarin Forms?

I want to convert my image to base64string. I want to save the base64string to my database. I can get the path of my image. How can I get the image from my path and convert the image to base64string and what datatype do I need to put inorder to save base64string to my database is it BLOB?
try
{
var cafNo = entCafNo.Text;
var time = tpTime.Time;
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
await DisplayAlert("No Camera", "No Camera Available", "Ok");
return;
}
var file = await CrossMedia.Current.TakePhotoAsync(
new StoreCameraMediaOptions
{
SaveToAlbum = true,
Name = cafNo + "_IMG_01.jpg"
}
);
// provide read access to the file
FileStream fs = new FileStream(file.Path, FileMode.Open, FileAccess.Read);
// Create a byte array of file stream length
byte[] ImageData = new byte[fs.Length];
//Read block of bytes from stream into the byte array
fs.Read(ImageData, 0, System.Convert.ToInt32(fs.Length));
//Close the File Stream
fs.Close();
string _base64String = Convert.ToBase64String(ImageData);
entPhoto1Url.Text = _base64String;
}
catch(Exception ex)
{
await DisplayAlert("Error", ex.Message, "OK");
}

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.

Categories