So I'm making a Xamarin.Forms app. I have a StackLayout, of which I'm taking a snapshot(only the element, not the whole screen.)
This is the interface:
public interface IViewSnapShot
{
Task<byte[]> CaptureAsync(Xamarin.Forms.View view);
}
this is the event:
private async Task SavePic_ClickedAsync(object sender, EventArgs e)
{
var imageByte = await DependencyService.Get<IViewSnapShot>().CaptureAsync(BlueprintSnapshot);
}
and this is Android platform specific:
public class MakeViewSnapshot : IViewSnapShot
{
Task<byte[]> IViewSnapShot.CaptureAsync(Xamarin.Forms.View view)
{
var nativeView = view.GetRenderer().View;
var wasDrawingCacheEnabled = nativeView.DrawingCacheEnabled;
nativeView.DrawingCacheEnabled = true;
nativeView.BuildDrawingCache(false);
Bitmap bitmap = nativeView.GetDrawingCache(false);
// TODO: Save bitmap and return filepath
nativeView.DrawingCacheEnabled = wasDrawingCacheEnabled;
byte[] bitmapData;
using (var stream = new MemoryStream())
{
bitmap.Compress(Bitmap.CompressFormat.Png, 0, stream);
bitmapData = stream.ToArray();
}
return bitmapData;
}
}
The problem is bitmapData gives error
Cannot implicitly convert type 'byte[]' to 'System.Threading.Tasks.Task'
I have search internet, and every post says that this is the way to convert bitmap to byte[] array. Any idea how to fix this error?
Later I'll want to upload the byte[] array to web api.
Instead of returning a byte[], you can use the Task.FromResult() to wrap a result into a Task:
return Task.FromResult(bitmapData);
Your code might looks like this:
public class MakeViewSnapshot : IViewSnapShot
{
Task<byte[]> IViewSnapShot.CaptureAsync(Xamarin.Forms.View view)
{
var nativeView = view.GetRenderer().View;
var wasDrawingCacheEnabled = nativeView.DrawingCacheEnabled;
nativeView.DrawingCacheEnabled = true;
nativeView.BuildDrawingCache(false);
Bitmap bitmap = nativeView.GetDrawingCache(false);
// TODO: Save bitmap and return filepath
nativeView.DrawingCacheEnabled = wasDrawingCacheEnabled;
byte[] bitmapData;
using (var stream = new MemoryStream())
{
bitmap.Compress(Bitmap.CompressFormat.Png, 0, stream);
bitmapData = stream.ToArray();
}
return Task.FromResult(bitmapData);
}
}
And then later when you want to get the byte[] returned by CaptureAsync() you just need to call:
byte[] result = CaptureAsync(<Your_parameters>).Result;
Related
I want to store a WPF BitmapImage to XML. (I know this is not usually recommended but for my case, it makes sense as I want to embed all my resource to a single XML file besides other data I have).
So Here are my extension methods:
public static string ToBase64(this BitmapImage image, string format)
{
return Convert.ToBase64String(Encode(image, format));
}
public static Stream FromBase64(this string content)
{
var bytes = Convert.FromBase64String(content);
var stream = new MemoryStream();
stream.Write(bytes, 0, bytes.Length);
return stream;
}
private static byte[] Encode(BitmapImage bitmapImage, string format)
{
byte[] data = null;
BitmapEncoder encoder = null;
switch (format.ToUpper())
{
case "PNG": encoder = new PngBitmapEncoder();
break;
case "GIF": encoder = new GifBitmapEncoder();
break;
case "BMP": encoder = new BmpBitmapEncoder();
break;
case "JPG": encoder = new JpegBitmapEncoder();
break;
}
if (encoder != null)
{
encoder.Frames.Add(BitmapFrame.Create(bitmapImage));
using (var ms = new MemoryStream())
{
encoder.Save(ms);
ms.Seek(0, SeekOrigin.Begin);
data = ms.ToArray();
}
}
return data;
}
public static BitmapImage ToBitmapImage(this Stream stream)
{
try
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
return bitmap;
}
catch (Exception ex)
{
}
return null;
}
and here is my XML logics:
public async void LoadImage(Guid imageSourceGuid)
{
var sourceElement = await GetImageSource(imageSourceGuid);
if (sourceElement != null)
{
var data = sourceElement.Element("Value").Value;
Format = sourceElement.Attribute("Format").Value.ToUpper();
if (string.IsNullOrEmpty(data) == false)
{
using (var stream = data.FromBase64())
{
SetImage(stream.ToBitmapImage());
}
}
}
}
private void SetImage(BitmapImage bitmap)
{
this.ImageShape.Source = bitmap;
}
public async Task<XElement> GetImageSource(Guid id)
{
XElement result = null;
await Task.Run(() =>
{
var settings = new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Fragment, IgnoreWhitespace = true, IgnoreComments = true, Async = true };
using (var reader = XmlReader.Create(FilePath, settings))
{
while (reader.Read())
{
if (reader.IsStartElement() && reader.Name == "ImageSource")
{
var att = reader.GetAttribute("Id");
if (att != null && Guid.Parse(att) == id)
{
result = XNode.ReadFrom(reader) as XElement;
break;
}
}
}
}
});
return result;
}
My XML file looks like:
<?xml version="1.0" encoding="utf-8"?>
<ImageSources>
<ImageSource Id="1b1e4ebc-484c-4f63-bbed-bf33430f85f2" Format="JPG" OriginalWidth="534" OriginalHeight="338">
<Value><![CDATA[....]]<Value>
</ImageSource>
</ImageSources>
...
But when I try to create a BitmapImage using ToBitmapImage method from the XML data I saved earlier I get "The image cannot be decoded. The image header might be corrupted." exception.
This only happens for JPG files I have no issue with PNG files at all.
You should rewind the MemoryStream after writing in your FromBase64 method:
public static Stream FromBase64(this string content)
{
var bytes = Convert.FromBase64String(content);
var stream = new MemoryStream();
stream.Write(bytes, 0, bytes.Length);
stream.Seek(0, SeekOrigin.Begin); // here
return stream;
}
Or construct it directly from the byte array:
public static Stream FromBase64(this string content)
{
return new MemoryStream(Convert.FromBase64String(content));
}
Afaik, JpegBitmapDecoder is the only BitmapDecoder in WPF that is affected by the source stream's actual Position.
Since two days I try to manage my images in a WPF application but I have errors and errors errors, ...
The image is show in a System.Windows.Control.Image.
For this reason I try to work with variable BitMapImage.
Now I have the error : "Impossible to access close stream" and I can not find a solution.
I have created two function for convert :
public static BitmapImage ConvertToBitMapImage(byte[] bytes)
{
if (bytes == null || bytes.Length == 0) return null;
var image = new BitmapImage();
using (var mem = new MemoryStream(bytes))
{
mem.Position = 0;
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = null;
image.StreamSource = mem;
image.EndInit();
}
//image.Freeze();
return image;
}
public static byte[] ImageToByte(BitmapImage imageSource)
{
Stream stream = imageSource.StreamSource;
Byte[] buffer = null;
if (stream != null && stream.Length > 0)
{
using (BinaryReader br = new BinaryReader(stream))
{
buffer = br.ReadBytes((Int32)stream.Length);
}
}
return buffer;
}
In my object I have a property :
public BitmapImage MiniatureApp
{
get
{
if (IMAGES != null)
_MiniatureApp = Tools.BinaryImageConverter.ConvertToBitMapImage(IMAGES.IMAGE);
return _MiniatureApp;
}
set
{
if (this.IMAGES != null)
this.IMAGES.IMAGE = Tools.BinaryImageConverter.ImageToByte((BitmapImage)value);
else
{
IMAGES img = new IMAGES();
img.NOM = "";
img.IMAGE = Tools.BinaryImageConverter.ImageToByte((BitmapImage)value);
this.IMAGES = img;
}
NotifyPropertyChanged();
}
}
And in my main I do this :
FileStream fs = new FileStream(pathImage, FileMode.Open, FileAccess.Read);
byte[] data = new byte[fs.Length];
fs.Read(data, 0, System.Convert.ToInt32(fs.Length));
fs.Close();
VMAppareil.VMApp.CurrentAppareil.MiniatureApp = Tools.BinaryImageConverter.ConvertToBitMapImage(data);
Exist a solution for my problem or exist a best way to do this ?
Gobelet.
You can fix your solution replacing the ImageToByte method with the following (borrowed from https://stackoverflow.com/a/6597746/5574010)
public static byte[] ImageToByte(BitmapImage imageSource)
{
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(imageSource));
using (var ms = new MemoryStream())
{
encoder.Save(ms);
return ms.ToArray();
}
}
Your previous solution did not work because the ConvertToBitMapImage method close the stream that you assign to image.StreamSource as soon as your code exit the unit statement. When you call the ImageToByte method as part of the MiniatureApp setter the StreamSource will be close and you get an error.
I'm not really sure what you're doing with the getter-setter.
For getting a byte array from a file, I would have this:
public static byte[] FileToByteArray(string fileName)
{
byte[] fileData = null;
using (FileStream fs = new File.OpenRead(fileName))
{
var binaryReader = new BinaryReader(fs);
fileData = binaryReader.ReadBytes((int)fs.Length);
}
return fileData;
}
And once you have the bytes from that function, you can use ConvertToBitMapImage() and assign that to the Image.Source, like this:
byte[] bytes = FileToByteArray(fileName);
YourImage.Source = ConvertToBitMapImage(bytes);
I'm not sure why you would need to convert a BitmapImage into bytes, but you should be able to call your function directly for that:
BitmapImage bImg = new BitmapImage();
bImg.Source = ConvertToBitMapImage(bytes);
byte[] bytes = ImageToByte(bImg); // these should be the same bytes as went in
Set it up like this and step through the code to see where it snags. It's possible the bytes might need encoding when you grab it in ImageToByte().
But also, you don't just then set the byte[] directly to the IMAGES or this.IMAGES.IMAGE, because that isn't an Image object, it's a byte[]. Without knowing what your IMAGES model construct looks like with its property types, it's a little vague to know what is being set to what and why, but this seems like a bad idea to over-complicate what you should just do with function calls as it is needed, without a getter-setter and an IMAGES model in the way.
I need to pass an image (Image _thresholdedImage) like byte array... I don't know how I can do this. Any idea? Thank you!
_thresholdedImage.Source = ImageSource.FromStream (() => photo.Source);
var tessResult = await _tesseractApi.SetImage(imageBytes);
I was unable to convert it in X.Forms, instead I use the following code with a dependency service. Thank you to all.
public async Task<byte[]> GetBytesFromImage(string filePath)
{
ConvertImageToBW(filePath);
// Create another bitmap that will hold the results of the filter.
Bitmap thresholdedBitmap = Bitmap.CreateBitmap (BitmapFactory.DecodeFile(filePath));
thresholdedBitmap = BitmapFactory.DecodeFile (thresholdedImagePath);
byte[] bitmapData;
using (var stream = new MemoryStream())
{
thresholdedBitmap.Compress(Bitmap.CompressFormat.Png, 0, stream);
bitmapData = stream.ToArray();
}
return bitmapData;
}
Have you tried using converters?
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
ImageSource retSource = null;
if (value != null)
{
byte[] imageAsBytes = (byte[])value;
retSource = ImageSource.FromStream(() => new MemoryStream(imageAsBytes));
}
return retSource;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
It turns out that you can convert the MediaFile object to a byte array. So no need to convert the file to ImageSource or Image.
var photo = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions() { });
byte[] imageArray = null;
using (MemoryStream memory = new MemoryStream()) {
Stream stream = photo.GetStream();
stream.CopyTo(memory);
imageArray = memory.ToArray();
}
Source: https://forums.xamarin.com/discussion/156236/how-to-get-the-bytes-from-the-imagesource-in-xamarin-forms
Shoutout to user "ajaxer" in the Xamarin forums whose solution saved me after spending 3 days on this issue. 😁
You could be using the following function:
public async Task<byte[]> Download(string url)
{
using (HttpClient client = new HttpClient())
{
byte[] fileArray = await client.GetByteArrayAsync(url);
return fileArray;
}
}
By Using Java.Io in xamairn forms we can get byte array from imagePath.
It will work for all platforms.
private static byte[] GetBytesFromImage(string imagePath)
{
var imgFile = new File(imagePath);
var stream = new FileInputStream(imgFile);
var bytes = new byte[imgFile.Length()];
stream.Read(bytes);
return bytes;
}
byte[] bitmapData;
using (var stm = new MemoryStream())
{
snapshot.Compress(Bitmap.CompressFormat.Png, 0, stm);
bitmapData = stm.ToArray();
}
Following code can use to convert image into byte array.Here CrossMedia plugin is used to capture image from camera.
public byte[] imageByte;
Stream imageStream = null;
var file = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
{ Name = "pic.jpg"});
if (file == null) return;
imageStream = file.GetStream();
BinaryReader br = new BinaryReader(imageStream);
imageByte = br.ReadBytes((int)imageStream.Length);
In my c# application, I'm trying to upload an png image using ftp, the problem is I loose transparency (the transparent area turn to black!).
PS : The image I send is stored in an Sql Server database, and they look fine in my application. Here is the code I use :
private const int defultbuffersize = 1024 * 60;
public void UploadImage(Image image, string uri, string filename)
{
var request = WebRequest.Create(String.Format(#"{0}/{1}", uri, filename)) as FtpWebRequest;
if (request == null)
return;
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Credentials = new NetworkCredential("userName", "password");
request.UsePassive = true;
request.UseBinary = true;
request.KeepAlive = true;
using (var writer = request.GetRequestStream())
{
var bytesData = image.ToByteArray();
var position = 0;
while (position < bytesData.Length)
{
var buffer = readData(bytesData, position);
writer.Write(buffer, 0, buffer.Length);
position += buffer.Length;
}
}
}
private static byte[] readData(byte[] bytesData, int position)
{
var buffer = new byte[defultbuffersize];
var lenght = position + defultbuffersize > bytesData.Length ?
bytesData.Length - position : defultbuffersize;
Array.Copy(bytesData.ToArray(), position, buffer, 0, lenght);
return buffer;
}
Extension method in an other class
public static byte[] ToByteArray(this Image image)
{
var stream = new MemoryStream();
image.Save(stream, ImageFormat.Jpeg);
return stream.ToArray();
}
Thanks in advance
The reason you lose transparency is found in your extension method
public static byte[] ToByteArray(this Image image)
{
var stream = new MemoryStream();
image.Save(stream, ImageFormat.Jpeg);
return stream.ToArray();
}
You are trying to serialize an png image using jpeg format
Change the extension method to this.
public static byte[] ToByteArray(this Image image)
{
var stream = new MemoryStream();
image.Save(stream, ImageFormat.Png);
return stream.ToArray();
}
This should fix your transparency problem.
In my application, I allow users to add people from contact using the ContactPicker.
I try to convert IRandomAccessStreamWithContentType to Byte[]
IRandomAccessStreamWithContentType stream = await contactInformation.GetThumbnailAsync();
if (stream != null && stream.Size > 0)
{
Byte[] bytes = new Byte[stream.Size];
await stream.ReadAsync(bytes.AsBuffer(), (uint)stream.Size, Windows.Storage.Streams.InputStreamOptions.None);
My Byte[] is not empty (approximately 10000 bytes)
But when I use my Converter Byte[] to ImageSource, the BitmapImage has 0 for width and heigth.
I use this converter for another application and it work great...
public object Convert(object value, Type targetType, object parameter, string language)
{
try
{
Byte[] bytes = (Byte[])value;
if (bytes == null)
return (new BitmapImage(new Uri((String)parameter)));
BitmapImage bitmapImage = new BitmapImage();
IRandomAccessStream stream = this.ConvertToRandomAccessStream(new MemoryStream(bytes));
bitmapImage.SetSource(stream);
return (bitmapImage);
}
catch
{
return (new BitmapImage(new Uri((String)parameter)));
}
}
private IRandomAccessStream ConvertToRandomAccessStream(MemoryStream memoryStream)
{
var randomAccessStream = new InMemoryRandomAccessStream();
var outputStream = randomAccessStream.GetOutputStreamAt(0);
outputStream.AsStreamForWrite().Write(memoryStream.ToArray(), 0, (Int32)memoryStream.Length);
return randomAccessStream;
}
If anybody know what is the problem...
Thanks in advance.
NeoKript
Edit : I already use my converter with another project and it works great.
The main difference is that the stream has not the same origine:
var reader = await file.OpenReadAsync();
using (DataReader dataReader = new DataReader(reader))
{
var bytes = new byte[reader.Size];
await dataReader.LoadAsync((uint)reader.Size);
dataReader.ReadBytes(bytes);
// Use of bytes
}
I'm pretty sure bytes in your case contains only zeros. You should modify your reading code:
var buffer = await stream.ReadAsync(bytes.AsBuffer(), (uint)stream.Size, InputStreamOptions.None);
bytes = buffer.ToArray();
Or even better, avoid IBuffer alltogether:
await stream.AsStream().ReadAsync(bytes, 0, bytes.Length);
In both cases the byte array will now contain actual values. I still couldn't get your converter to work and I don't have the time to troubleshoot it further now. Maybe somebody else will help you with that.
EDIT:
I'm not really sure you're using the converter in another project but I couldn't get it to work without using async methods which makes it impossible to use inside IValueConverter. After I changed ConvertToRandomAccessStream the bitmap was created as expected without any other modifications (except for the required await):
private async Task<IRandomAccessStream> ConvertToRandomAccessStream(byte[] bytes)
{
var randomAccessStream = new InMemoryRandomAccessStream();
using (var writer = new DataWriter(randomAccessStream))
{
writer.WriteBytes(bytes);
await writer.StoreAsync();
await writer.FlushAsync();
writer.DetachStream();
}
randomAccessStream.Seek(0);
return randomAccessStream;
}
Instead of using a converter I put an additional property in my view model returning the bitmap. Inside the bytes property I called the async method for creating the bitmap:
public Byte[] ImageBytes
{
get { return _imageBytes; }
set
{
if (Equals(value, _imageBytes)) return;
_imageBytes = value;
OnPropertyChanged();
CreateBitmap();
}
}
public BitmapSource Image
{
get { return _image; }
set
{
if (Equals(value, _image)) return;
_image = value;
OnPropertyChanged();
}
}
private async Task CreateBitmap()
{
try
{
BitmapImage bitmapImage = new BitmapImage();
IRandomAccessStream stream = await this.ConvertToRandomAccessStream(ImageBytes);
bitmapImage.SetSource(stream);
Image = bitmapImage;
}
catch
{
Image = null;
}
}