How to change default image folder on Xamarin forms (android) - c#

I am having the issue of "Canvas Drawing too large bitmaps". After a quick search, I found the following thread, which promptly helped me know what is the issue.
The solution is to put the image in drawable-xxhdpi/ instead of simply drawable/. And here lies the issue: the image is not static, it is imported when I need it. As such, I do not chose where the image ends up stored. It store itself in drawable. Is there 1) A solution to chose which folder to use, or 2) a way to tell it not get the image if it's too heavy?
var file = new SmbFile(path, auth);
try
{
if (file.Exists())
{
// Get readable stream.
var readStream = file.GetInputStream();
//Create reading buffer.
MemoryStream memStream = new MemoryStream();
//Get bytes.
((Stream)readStream).CopyTo(memStream);
var stream1 = new MemoryStream(memStream.ToArray());
if (stream1.Length < 120188100)
{
//Save image
ProductImage = ImageSource.FromStream(() => stream1);
//Dispose readable stream.
readStream.Dispose();
InfoColSpan = 1;
}
else
{
Common.AlertError("Image trop lourde pour l'affichage");
}
}
}

Related

Send InkCanvas strokes directly to IRandomnaccessStream

I'm trying to get a byte array from the InkCanvas control, but the method i've come up with so far seems a bit long winded.
Currently I use the following:
StorageFolder folder = await Windows.Storage.ApplicationData.Current.LocalFolder.GetFolderAsync("Temp");
StorageFile file = await folder.CreateFileAsync(GenerateString(5)+".zzx", Windows.Storage.CreationCollisionOption.GenerateUniqueName);
using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
await SignatureCanvas.InkPresenter.StrokeContainer.SaveAsync(stream);
var array = await IRandomAccessStreamToByteArray(stream);
}
The custom stream reader is as follows.
private async Task<byte[]> IRandomAccessStreamToByteArray(IRandomAccessStream stream)
{
var reader = new DataReader(stream.GetInputStreamAt(0));
var bytes = new byte[stream.Size];
await reader.LoadAsync((uint)stream.Size);
reader.ReadBytes(bytes);
return bytes;
}
This works, and gives me the byte array that i need, but also leaves me with unwanted images. Was having some access issues due to files still being written when another call wanted to replace the file so decided to go down the multiple images route. Is there a way to skip the image file entirely? It's not too much of an issue to clear out a temp folder, but if it can be avoided that would be preferable.
I had read somewhere already that InkCanvas doesn't support direct to array dumps, so any suggestions would be appreciated!
If you want to save your InkCanvas strokes to bytes Array without create the file, you should be able to use the Win2D.uwp.
To install Win2D.uwp, run the "Install-Package Win2D.uwp" command in the Package Manager Console.
There is a CanvasDrawingSession.DrawInk method to draw a collection of ink strokes. That we should be able to use CanvasBitmap.GetPixelBytes method to get an array of raw byte data for the entire bitmap.
For example:
private byte[] ConvertInkCanvasToByteArray()
{
var canvasStrokes = SignatureCanvas.InkPresenter.StrokeContainer.GetStrokes();
if (canvasStrokes.Count > 0)
{
var width = (int)SignatureCanvas.ActualWidth;
var height = (int)SignatureCanvas.ActualHeight;
var device = CanvasDevice.GetSharedDevice();
device.DeviceLost += DeviceOnDeviceLost;
var renderTarget = new CanvasRenderTarget(device, width, height, 96);
using (var ds = renderTarget.CreateDrawingSession())
{
ds.Clear(Windows.UI.Colors.White);
ds.DrawInk(SignatureCanvas.InkPresenter.StrokeContainer.GetStrokes());
}
return renderTarget.GetPixelBytes();
}
else
{
return null;
}
}
private void DeviceOnDeviceLost(CanvasDevice sender, object args)
{
Debug.WriteLine("DeviceOnDeviceLost");
}
Also if we want to convert the bytes array to image, we should be able to use following code:
WriteableBitmap bitmap = new WriteableBitmap((int)SignatureCanvas.ActualWidth, (int)SignatureCanvas.ActualHeight);
await bitmap.PixelBuffer.AsStream().WriteAsync(mybytes, 0, mybytes.Length);

Loading Album art with TagLib sharp and then saving it to same/different file in C# causes memory error

My application lists all MP3's in a directory and when the user selects a file it loads the tag info, including album art. The art is loaded into a variable to be used when the user saves the data. The art is also loaded into a picture frame for the user to see.
// Global to all methods
System.Drawing.Image currentImage = null;
// In method onclick of the listbox showing all mp3's
TagLib.File f = new TagLib.Mpeg.AudioFile(file);
if (f.Tag.Pictures.Length > 0)
{
TagLib.IPicture pic = f.Tag.Pictures[0];
MemoryStream ms = new MemoryStream(pic.Data.Data);
if (ms != null && ms.Length > 4096)
{
currentImage = System.Drawing.Image.FromStream(ms);
// Load thumbnail into PictureBox
AlbumArt.Image = currentImage.GetThumbnailImage(100,100, null, System.IntPtr.Zero);
}
ms.Close();
}
// Method to save album art
TagLib.Picture pic = new TagLib.Picture();
pic.Type = TagLib.PictureType.FrontCover;
pic.MimeType = System.Net.Mime.MediaTypeNames.Image.Jpeg;
pic.Description = "Cover";
MemoryStream ms = new MemoryStream();
currentImage.Save(ms, ImageFormat.Jpeg); // <-- Error occurs on this line
ms.Position = 0;
pic.Data = TagLib.ByteVector.FromStream(ms);
f.Tag.Pictures = new TagLib.IPicture[1] { pic };
f.save();
ms.Close();
If I load the image and try to save it right away I get this "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." If I try to save currentImage as a ImageFormat.Bmp I get this "A generic error occurred in GDI+."
My save method works correctly if I load an image from a url like this:
WebRequest req = WebRequest.Create(urlToImg);
WebResponse response = req.GetResponse();
Stream stream = response.GetResponseStream();
currentImage = Image.FromStream(stream);
stream.Close();
So I'm guessing there is an issue with the way that I am loading the image into currentImage when the user selects an MP3 from the listbox.
I have found a lot of examples of loading and saving images to mp3's but no one seems to be having this issue when they try to save the are immediately after loading it.
Thanks for the help Jim but I couldn't really get it working using the 'using' blocks so I'm guessing the stream was still closing somewhere. I found another way to do what I was looking for by storing/saving a byte[] instead of an Image. And then just saving it using:
using (MemoryStream ms = new MemoryStream(currentImageBytes))
{
pic.Data = TagLib.ByteVector.FromStream(ms);
f.Tag.Pictures = new TagLib.IPicture[1] { pic };
if (save)
f.Save();
}
Your stream stuff should be in using blocks, which will dispose of your goods automagically and close them too. Not terribly important, but easier to manage.
Your generic GDI+ error is likely because you are trying to perform an operation or call a method on a file for which the stream is already closed.
Check it out...
your method isn't really false, only a few things had to be changed:
// Method to save album art
TagLib.Picture pic = new TagLib.Picture();
pic.Type = TagLib.PictureType.FrontCover;
pic.MimeType = System.Net.Mime.MediaTypeNames.Image.Jpeg;
pic.Description = "Cover";
MemoryStream ms = new MemoryStream();
currentImage.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); // <-- Error doesn't occur anymore
ms.Position = 0;
pic.Data = TagLib.ByteVector.FromStream(ms);
f.Tag.Pictures = new TagLib.IPicture[1] { pic };
f.save();
ms.Close();

Loading image in picturebox control repeatedly from local machine

I have some c# code that gets an image from a webpage then downloads it to my local machine. This is done in the background 1/sec. If I leave this running it works fine and my pictures get updated correctly. These pictures are basically feeds from a camera. I want to put these pictures into a picturebox or some other control so that I can display the images as if they were a camera feed. However when I tried doing this I've got errors saying the image is being used so I can not load it into my picturebox. Is there a better way to do this?
Thanks,
byte[] lnBuffer;
byte[] lnFile;
HttpWebRequest lxRequest = (HttpWebRequest)WebRequest.Create(uri);
lxRequest.Credentials = credentials;
using (HttpWebResponse lxResponse = (HttpWebResponse)lxRequest.GetResponse())
{
using (BinaryReader lxBR = new BinaryReader(lxResponse.GetResponseStream()))
{
using (MemoryStream lxMS = new MemoryStream())
{
lnBuffer = lxBR.ReadBytes(1024);
while (lnBuffer.Length > 0)
{
lxMS.Write(lnBuffer, 0, lnBuffer.Length);
lnBuffer = lxBR.ReadBytes(1024);
}
lnFile = new byte[(int)lxMS.Length];
lxMS.Position = 0;
lxMS.Read(lnFile, 0, lnFile.Length);
lxMS.Close();
lxBR.Close();
}
}
lxResponse.Close();
}
using (System.IO.FileStream lxFS = new FileStream("images/camppic1.jpg", FileMode.Create))
{
lxFS.Write(lnFile, 0, lnFile.Length);
lxFS.Close();
}
This is what I use to create the file. Then in the same method after this code I do this:
image = Image.FromFile("C:\camppic1.jpg");
pictureBox23.Image = image;
If you need the file, then load the file content and copy to a MemoryStream and use Image.FromStream. If you don't need the file, you could skip it and use the MemoryStream directly from the downloading... (Faster since no disc access would be needed.)

C#: Retrieve JPEG Comment (not Exif)

I've been stumped trying to figure this one out.
I'm trying to retrieve the "Jpeg Comment" out of a jpg file via C#.
The code below works but I need the basic comment NOT the Exif comment.
I'm using FastStone Image Viewer to set the basic comment. Help me retrieve it.
I can use the commandline program exiv2 to verify that the comment is there.
exiv2 -pc c:\test.jpg (it spits out the basic comment).
exiv2 -pa c:\test.jpg (it spits out the EXIF comment)
I've used several C# libs to get at it but they get the EXIF data.
Image x = Image.FromFile(#"c:\test.jpg");
PropertyItem prop;
prop = x.GetPropertyItem(0x9286);
string Comment = Encoding.ASCII.GetString(prop.Value);
You could refer to this link.
(Thanks for those who already have answered the same question, although the answer was quite right but not 100% to solve this problem.)
Here are three steps you need to do:
Be aware that you should have the Jpeg file cloned.
Set the comment of the cloned file.
Replace the file by deleting the original jpeg file.
Here is the sample code:
public void addImageComment(string imageFlePath, string comments)
{
BitmapDecoder decoder = null;
BitmapFrame bitmapFrame = null;
BitmapMetadata metadata = null;
FileInfo originalImage = new FileInfo(imageFlePath);
if (File.Exists(imageFlePath))
{
// load the jpg file with a JpegBitmapDecoder
using (Stream jpegStreamIn = File.Open(imageFlePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
decoder = new JpegBitmapDecoder(jpegStreamIn, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
}
bitmapFrame = decoder.Frames[0];
metadata = (BitmapMetadata)bitmapFrame.Metadata;
if (bitmapFrame != null)
{
BitmapMetadata metaData = (BitmapMetadata)bitmapFrame.Metadata.Clone();
if (metaData != null)
{
// modify the metadata
metaData.Comment = comments;
// get an encoder to create a new jpg file with the new metadata.
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapFrame, bitmapFrame.Thumbnail, metaData, bitmapFrame.ColorContexts));
//string jpegNewFileName = Path.Combine(jpegDirectory, "JpegTemp.jpg");
// Delete the original
originalImage.Delete();
// Save the new image
using (Stream jpegStreamOut = File.Open(imageFlePath, FileMode.CreateNew, FileAccess.ReadWrite))
{
encoder.Save(jpegStreamOut);
}
}
}
}
}
You can do this quite simply with the MetadataExtractor library (available via NuGet):
JpegCommentDirectory jpegCommentDirectory = ImageMetadataReader.ReadMetadata(imagePath)
.OfType<JpegCommentDirectory>()
.FirstOrDefault();
string comment = jpegCommentDirectory?.GetDescription(JpegCommentDirectory.TagComment);

Image.Save(..) throws a GDI+ exception because the memory stream is closed

i've got some binary data which i want to save as an image. When i try to save the image, it throws an exception if the memory stream used to create the image, was closed before the save. The reason i do this is because i'm dynamically creating images and as such .. i need to use a memory stream.
this is the code:
[TestMethod]
public void TestMethod1()
{
// Grab the binary data.
byte[] data = File.ReadAllBytes("Chick.jpg");
// Read in the data but do not close, before using the stream.
Stream originalBinaryDataStream = new MemoryStream(data);
Bitmap image = new Bitmap(originalBinaryDataStream);
image.Save(#"c:\test.jpg");
originalBinaryDataStream.Dispose();
// Now lets use a nice dispose, etc...
Bitmap2 image2;
using (Stream originalBinaryDataStream2 = new MemoryStream(data))
{
image2 = new Bitmap(originalBinaryDataStream2);
}
image2.Save(#"C:\temp\pewpew.jpg"); // This throws the GDI+ exception.
}
Does anyone have any suggestions to how i could save an image with the stream closed? I cannot rely on the developers to remember to close the stream after the image is saved. In fact, the developer would have NO IDEA that the image was generated using a memory stream (because it happens in some other code, elsewhere).
I'm really confused :(
As it's a MemoryStream, you really don't need to close the stream - nothing bad will happen if you don't, although obviously it's good practice to dispose anything that's disposable anyway. (See this question for more on this.)
However, you should be disposing the Bitmap - and that will close the stream for you. Basically once you give the Bitmap constructor a stream, it "owns" the stream and you shouldn't close it. As the docs for that constructor say:
You must keep the stream open for the
lifetime of the Bitmap.
I can't find any docs promising to close the stream when you dispose the bitmap, but you should be able to verify that fairly easily.
A generic error occurred in GDI+.
May also result from incorrect save path!
Took me half a day to notice that.
So make sure that you have double checked the path to save the image as well.
Perhaps it is worth mentioning that if the C:\Temp directory does not exist, it will also throw this exception even if your stream is still existent.
Copy the Bitmap. You have to keep the stream open for the lifetime of the bitmap.
When drawing an image: System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI
public static Image ToImage(this byte[] bytes)
{
using (var stream = new MemoryStream(bytes))
using (var image = Image.FromStream(stream, false, true))
{
return new Bitmap(image);
}
}
[Test]
public void ShouldCreateImageThatCanBeSavedWithoutOpenStream()
{
var imageBytes = File.ReadAllBytes("bitmap.bmp");
var image = imageBytes.ToImage();
image.Save("output.bmp");
}
I had the same problem but actually the cause was that the application didn't have permission to save files on C. When I changed to "D:\.." the picture has been saved.
You can try to create another copy of bitmap:
using (var memoryStream = new MemoryStream())
{
// write to memory stream here
memoryStream.Position = 0;
using (var bitmap = new Bitmap(memoryStream))
{
var bitmap2 = new Bitmap(bitmap);
return bitmap2;
}
}
This error occurred to me when I was trying from Citrix. The image folder was set to C:\ in the server, for which I do not have privilege. Once the image folder was moved to a shared drive, the error was gone.
A generic error occurred in GDI+. It can occur because of image storing paths issues,I got this error because my storing path is too long, I fixed this by first storing the image in a shortest path and move it to the correct location with long path handling techniques.
I was getting this error, because the automated test I was executing, was trying to store snapshots into a folder that didn't exist. After I created the folder, the error resolved
One strange solution which made my code to work.
Open the image in paint and save it as a new file with same format(.jpg). Now try with this new file and it works. It clearly explains you that the file might be corrupted in someway.
This can help only if your code has every other bugs fixed
It has also appeared with me when I was trying to save an image into path
C:\Program Files (x86)\some_directory
and the .exe wasn't executed to run as administrator, I hope this may help someone who has same issue too.
For me the code below crashed with A generic error occurred in GDI+on the line which Saves to a MemoryStream. The code was running on a web server and I resolved it by stopping and starting the Application Pool that was running the site.
Must have been some internal error in GDI+
private static string GetThumbnailImageAsBase64String(string path)
{
if (path == null || !File.Exists(path))
{
var log = ContainerResolver.Container.GetInstance<ILog>();
log.Info($"No file was found at path: {path}");
return null;
}
var width = LibraryItemFileSettings.Instance.ThumbnailImageWidth;
using (var image = Image.FromFile(path))
{
using (var thumbnail = image.GetThumbnailImage(width, width * image.Height / image.Width, null, IntPtr.Zero))
{
using (var memoryStream = new MemoryStream())
{
thumbnail.Save(memoryStream, ImageFormat.Png); // <= crash here
var bytes = new byte[memoryStream.Length];
memoryStream.Position = 0;
memoryStream.Read(bytes, 0, bytes.Length);
return Convert.ToBase64String(bytes, 0, bytes.Length);
}
}
}
}
I came across this error when I was trying a simple image editing in a WPF app.
Setting an Image element's Source to the bitmap prevents file saving.
Even setting Source=null doesn't seem to release the file.
Now I just never use the image as the Source of Image element, so I can overwrite after editing!
EDIT
After hearing about the CacheOption property(Thanks to #Nyerguds) I found the solution:
So instead of using the Bitmap constructor I must set the Uri after setting CacheOption BitmapCacheOption.OnLoad.(Image1 below is the Wpf Image element)
Instead of
Image1.Source = new BitmapImage(new Uri(filepath));
Use:
var image = new BitmapImage();
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(filepath);
image.EndInit();
Image1.Source = image;
See this: WPF Image Caching
Try this code:
static void Main(string[] args)
{
byte[] data = null;
string fullPath = #"c:\testimage.jpg";
using (MemoryStream ms = new MemoryStream())
using (Bitmap tmp = (Bitmap)Bitmap.FromFile(fullPath))
using (Bitmap bm = new Bitmap(tmp))
{
bm.SetResolution(96, 96);
using (EncoderParameters eps = new EncoderParameters(1))
{
eps.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
bm.Save(ms, GetEncoderInfo("image/jpeg"), eps);
}
data = ms.ToArray();
}
File.WriteAllBytes(fullPath, data);
}
private static ImageCodecInfo GetEncoderInfo(string mimeType)
{
ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
for (int j = 0; j < encoders.Length; ++j)
{
if (String.Equals(encoders[j].MimeType, mimeType, StringComparison.InvariantCultureIgnoreCase))
return encoders[j];
}
return null;
}
I used imageprocessor to resize images and one day I got "A generic error occurred in GDI+" exception.
After looked up a while I tried to recycle the application pool and bingo it works. So I note it here, hope it help ;)
Cheers
I was getting this error today on a server when the same code worked fine locally and on our DEV server but not on PRODUCTION. Rebooting the server resolved it.
public static byte[] SetImageToByte(Image img)
{
ImageConverter converter = new ImageConverter();
return (byte[])converter.ConvertTo(img, typeof(byte[]));
}
public static Bitmap SetByteToImage(byte[] blob)
{
MemoryStream mStream = new MemoryStream();
byte[] pData = blob;
mStream.Write(pData, 0, Convert.ToInt32(pData.Length));
Bitmap bm = new Bitmap(mStream, false);
mStream.Dispose();
return bm;
}

Categories

Resources