When I try to set an image every 20ms from TcpClient as a Memory stream the Image source is not set in the Image control in Xamarin, I wonder if there is some sort of limitation the time that takes to set an ImageSource in an an Image control the output from the debugger,:
https://pastebin.com/8FgN5Rai
private Task<ImageSource> GetImageAsync(System.IO.Stream stream)
{
TaskCompletionSource<ImageSource> tcs = new TaskCompletionSource<ImageSource>();
tcs.TrySetResult(ImageSource.FromStream(() => (MemoryStream)formatter.Deserialize(client.GetStream())));
return tcs.Task;
}
private async void ReceiveImage()
{
while (client.Connected)
{
Xamarin.Forms.Device.BeginInvokeOnMainThread(new Action(async () =>
{
imageControl.Source = await GetImageAsync(client.GetStream());
}));
}
}
ok i got to set the image but then it says is not a valid image object
Ok i tried a newer version but the image in the stream isnt valid?
Image loading: Image load failed: System.Runtime.Serialization.SerializationException: The input stream is not a valid binary format. The starting contents (in bytes) are: 10-10-FF-10-10-10-FF-10-10-10-FF-10-10-10-FF-10-10
Related
I have a ListView binded to an ObservableCollection. This ListView uses an ItemTemplate which consists of only one Image control, which Source property is binded to a URL string.
For some URLs images fail to load because the remote server would return "Forbidden (403)" - this could be solved by adding a certain header to the HTTP request that gets the image, but the problem is I don't know how I should go about modifying said request.
I tried two different approaches:
Creating an IValueConverter object. I would take the URL and get image data myself - put it into a MemoryStream and use the stream to initialize a BitmapImage object and return that to the Image control. This approach proved to be really slow and it blocked the UI thread.
Creating a new property to bind to that would return a byte array containing image data. This data would be lazily initialized the first time it's called using a Task. When the data would finish downloading, the PropertyChanged event would be fired that would update the Image visually. This approach did not block the UI thread, but was extremely slow for some reason.
I want to know 2 things:
Why are my approaches significantly slower?
How do I directly modify the way Image control gets images from a remote server without affecting the performance/speed that much?
Consider the following example:
public byte[] ImageThumbnail
{
get
{
if (img == null) img = GetImage(ImageUrls.Thumbnail);
return img;
}
}
public byte[] GetImage(string url) {
HttpClient client = new HttpClient();
// add some headers
return client.GetByteArrayAsync(url).Result;
}
Image would be binded to "ImageThumbnail" with IsAsync set to True. The images are downloaded significantly slower in this case compared to just binding the URL directly to Image source.
You should use an asynchronous Binding for the Source property of the Image control in the ItemTemplate:
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Image, IsAsync=True}"/>
</DataTemplate>
</ListBox.ItemTemplate>
Since the Image property getter is now called in a background thread, the returned ImageSource must be made cross-thread accessible by freezing.
public class ImageItem
{
private string url;
public ImageItem(string url)
{
this.url = url;
}
public ImageSource Image
{
get
{
var image = new BitmapImage();
var buffer = new WebClient().DownloadData(url);
using (var stream = new MemoryStream(buffer))
{
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = stream;
image.EndInit();
}
image.Freeze();
return image;
}
}
}
The Image property might be written a little shorter by using BitmapFrame instead of BitmapImage. The BitmapFrame.Create method shown below already returns a frozen BitmapFrame.
public ImageSource Image
{
get
{
var buffer = new WebClient().DownloadData(url);
using (var stream = new MemoryStream(buffer))
{
return BitmapFrame.Create(
stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
}
}
Hello we are using InkCanvas control on the wpf page. And we are saving Base64String string in the database. See code
private void submitButtonsCommand_Event(object sender)
{
byte[] sigByte;
InkCanvas icSignature = sender as InkCanvas;
using (var memoryStream = new MemoryStream())
{
icSignature.Strokes.Save(memoryStream);
sigByte = memoryStream.ToArray();
}
ServiceCallReportSignatureModel.SigCustomerSignature = Convert.ToBase64String(sigByte);
UpdateRecord();
}
And when we are retrieving data from the database and converting into the FromBase64String and after that when we creating image then we are getting the error.
public Image LoadImage(base64string)
{
//data:image/gif;base64,
//this image is a single pixel (black)
byte[] bytes = Convert.FromBase64String(base64string);
Image image;
using (MemoryStream ms = new MemoryStream(bytes))
{
image = Image.FromStream(ms);
}
return image;
}
I don't know why this is occurring. Please help me find out the error.
Our base64 string is : "AIwBAwxIEEWfARsCAAb/RjURAACAPx8JEQAAAAAAAPA/Cmwsh/EgR4kP+Etw/ojDWG7NZ8OZQxBN5otEzmU1mk04I7DnATcLHomzzUbxw/h3Dtp16yhlTQ2UsoTQh+uE64XgjsN6Js2UZpAZmFpmaZTKzzKZ4ctFmw3Mlmm0Bw9h/DtpTPKu79CYgm+VJqA="
Actually we are taking signature from the ink canvas control and we want to display in the pdf when we need. So we want create image from the base 64 data but we are getting the error.
The StrokeCollection.Save(Stream) method does not save a bitmap. Instead,
The Save method saves the StrokeCollection as Ink Serialized Format (ISF).
You can restore the saved StrokeCollection by passing a Stream with the saved data to the StrokeCollection(Stream) constructor.
I am trying to write a method that takes image data in base64 string and saves it into a binary file while preserving transparency (for example in case of PNGs).
My other requirement is that this needs to be done in C# in PCL (Portable Class Library).
I know that you can use Image or WriteableBitmap to solve this issue but such classes are not available in PCL.
I have the following method that does the job of taking the base64 data and saving it to a file:
public static async Task Base64ToBinaryImageFile(IFile file, string base64Content)
{
var bytes = Convert.FromBase64String(base64Content);
using (var stream = await file.OpenAsync(FileAccess.ReadAndWrite))
{
stream.Seek(0, SeekOrigin.Begin);
using (var writer = new BinaryWriter(stream))
{
writer.Write(content);
writer.Flush();
}
}
}
It works fine except that:
I lose the transparency data (so transparent pixels show up as black).
The file that is created using this method has a larger size (in bytes) than the original file.
Any idea on what's the cause and how to fix these issues?
Update: Here is the JavaScript code that sends the base64 data to C#:
function onPaste(event) {
var $event = event.data.$;
var clipboardData = $event.clipboardData;
var found = false;
var imageType = /^image/;
if (!clipboardData) {
return false;
}
return Array.prototype.forEach.call(clipboardData.types, function (type, i) {
if (found) {
return false;
}
if (type.match(imageType) || clipboardData.items[i].type.match(imageType)) {
readImageAsBase64(clipboardData.items[i]);
return found = true;
}
return false;
});
}
function readImageAsBase64(item) {
if (!item || typeof item.getAsFile !== "function") {
return;
}
var file = item.getAsFile();
var reader = new FileReader();
reader.onload = function (evt) {
window.external.notify("pasteImageBase64/" + evt.target.result);
};
reader.readAsDataURL(file);
}
I foresee a few possible issues:
Your issue could reside in the base64Content provided by the caller. It's possible that the conversion to base64Content that is provided as input to your method is reading the PNG image with an incorrect file format.
Related to #1, it's possible that someone calling the method took a .JPG or .BMP image file, naively renamed it to .PNG extension and called your method assuming that they were sending a PNG image, when in fact they were not.
You may be opening the .PNG image in testing with an image viewer/editor that does not support transparency or handle it well (IE mspaint.exe)
I am trying to save image date to physical file
Below is image data which I got from a jpeg image (via some browser response):
data:image/jpeg;base64,/9j/4AAQSkZJRgABAgEASABIAAD/.....blah blah .....//2Q==
Below is the code I am using to save image data string to Image
Image image = LoadImage(dataURL);
image.Save(saveLocation);
image.Dispose();
public Image LoadImage(string imageString)
{
imageString = imageString.Substring(imageString.IndexOf(',') + 1);
byte[] bytes = Convert.FromBase64String(imageString);
Image image;
using (MemoryStream ms = new MemoryStream(bytes))
{
image = Image.FromStream(ms);
}
return image;
}
Getting the following exception at image.Save(saveLocation); line
A generic error occurred in GDI+.
I have no issues when the image source with png images with the same code but with jpeg images no matter what the size of jpeg is I am getting that exception every time.
I am able to save the png images with same code at same location.
Edit: Please note that I am getting just data image string from browser (which I am capturing via clipboard of browser) i.e I am not getting bytes from client.
Is there any limit for the bytes which can be converted to image stream and then save back to physical file?
Is there any other approach to do the same?
There doesn't seem to be any reason to go through Image at all here.
Assuming the posted data is valid, just write the content to a file directly:
SaveImage(dataURL, saveLocation);
public bool SaveImage(string imageString, string location)
{
try {
imageString = imageString.Substring(imageString.IndexOf(',') + 1);
byte[] bytes = Convert.FromBase64String(imageString);
using (FileStream fs = new FileStream(location, FileMode.Create))
{
fs.Write(bytes, 0, bytes.Count);
}
}
catch(Exception)
{
return false;
}
return true;
}
Im writing my own cache for for our Silverlight application to get around any size problems.
The cache works fine, however, it doesnt work for images.
Ive narrowed down the problem to a little test application that converts a BitmapImage to bytes (to simulate reading it our from the cache) and then convert those bytes back into an image.
Here is the code:
public MainPage()
{
InitializeComponent();
firstImage.ImageOpened += first_Loaded;
}
void first_Loaded(object sender, RoutedEventArgs e)
{
var bmp = firstImage.Source;
var bytes = ToByteArray(bmp as BitmapImage);
otherImage.Source = CreateImageFromBytes(bytes);
}
private byte[] ToByteArray(BitmapImage bi)
{
var enc = new BitmapEncoder(bi);
return enc.GetBitmapData();
}
private BitmapImage CreateImageFromBytes(byte[] data)
{
var bitmapImage = new BitmapImage();
bitmapImage.ImageFailed += bitmapImage_ImageFailed;
var ms = new MemoryStream(data);
bitmapImage.SetSource(ms);
return bitmapImage;
}
void bitmapImage_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
Console.WriteLine(e.ErrorException.Message);
}
The ImageFailed event is always raised with the message "AG_E_NETWORK_ERROR"
Ive read a little about the problem and it seems to have to do with the origin of the image being different but surely I should be able to load an image from bytes in memory right?
Any ideas how to get around this?
I should add that the converting to bytes works fine since if I save those bytes down to disk I can open the image.
From the Remarks in the Silverlight BitmapImage page on MSDN:
The BitmapImage can be used to reference images in the JPEG and PNG
file formats.
Unfortunately the BitmapEncoder class that you are using seems to encode bitmaps in BMP format. Hence the encoded buffer can't be decoded by BitmapImage.
You would have to find another implementation of a BitmapEncoder, which is capable of encoding JPEG or PNG.