C# Singleton used as a picture buffer - c#

i want to use the singleton pattern to create a buffer where all needed pictures are stored.
Something like that:
public sealed class BaseBuffer
{
private static readonly Dictionary<string, Bitmap> pictures = new Dictionary<string, Bitmap>();
public static Bitmap GetPicture(string name, ref Bitmap output)
{
//In case the pictureBuffer does not contain the element already
if (!pictures.ContainsKey(name))
{
//Try load picture from resources
try
{
Bitmap bmp = new Bitmap(System.Reflection.Assembly.GetEntryAssembly().GetManifestResourceStream("MyProject.Resources." + name + ".png"));
pictures.Add(name, bmp);
return bmp;
}
catch (Exception ex)
{
Console.WriteLine("Picture {0} cannot be found.", name);
}
}
else
{
return pictures[name];
}
return null;
}
Now im not sure. Does the "GetPicture" method return a copy of the picture or does it return a reference?
The application will need a few pictures which are displayed verry often on different Applications/Forms. Because of that it would be better to have only references to the pictures than copying them around.
Do you have any idea how to do that?

It'll return a reference to the Bitmap (as it is defined as a class) held within the dictionary. So no it doesn't return a distinct copy. Changes to the bitmap returned will be observed in the dictionary also as they will be the same underlying object. I'm not sure what you are doing with your ref Bitmap output as it doesn't appear to be used.
Also, TryGetValue on the dictionary will perform slightly better if this is called often:
Bitmap bmp;
if (!pictures.TryGetValue(name, out bmp))
{
bmp = new Bitmap(System.Reflection.Assembly.GetEntryAssembly().GetManifestResourceStream("MyProject.Resources." + name + ".png"));
pictures.Add(name, bmp);
}
return bmp;

Im not sure about that, butIi guess instead of doing return bmp; you should do output=bmp; and so on. Otherwise you could get rid of the parameter.
I also think, that even return bmp; and so on would return a reference but not a copy of the Bitmap stored in the dict. so either way would be fine...

Related

Why Image.Save() in C# does not always give the same serialized result and how to serialize an image in a deterministic way?

I serialize images using the following code:
public static string SerializeImage(Image image)
{
using (MemoryStream memoryStream = new MemoryStream())
{
image.Save(memoryStream, image.RawFormat);
return Convert.ToBase64String(memoryStream.ToArray());
}
}
and deserialize the images by doing the following
public static Image DeserializeImage(string serializedImage)
{
byte[] imageAsBytes = Convert.FromBase64String(serializedImage);
using (MemoryStream memoryStream = new MemoryStream(imageAsBytes, 0, imageAsBytes.Length))
{
memoryStream.Write(imageAsBytes, 0, imageAsBytes.Length);
return Image.FromStream(memoryStream, true);
}
}
If I have an image and does
string serializedImage1 = SerializeImage(image);
Image deserializedImage = DeserializeImage(serializedImage1);
string serializedImage2 = SerializeImage(deserializedImage );
Then
serializedImage1 == serializedImage2;
as expected. But it is not always the case.
If I serialize an image on Process 1, and then redeserialize and reserialize it on Process 2, then the result of the reserialization on Process 2 is not the same as on the Process 1. Everything works, but a few bytes in the beginning of the serialization are different.
Worst, if I do the same thing on 2 different dll (or thread, I'm not sure), it seems the serialization result is not the same too. Again, the serialization/deserialization works, but a few bytes are different.
The image the first time is loaded with the following function :
public static Image GetImageFromFilePath(string filePath)
{
var uri = new Uri(filePath);
var bitmapImage = new BitmapImage(uri);
bitmapImage.Freeze();
using (var memoryStream = new MemoryStream())
{
var pngBitmapEncoder = new PngBitmapEncoder();
pngBitmapEncoder.Frames.Add(BitmapFrame.Create(bitmapImage));
pngBitmapEncoder.Save(memoryStream);
Image image = Image.FromStream(memoryStream);
return image;
}
}
Note however that it happens even if the image is loaded twice with the DeserializeImage() function.
The tests I have done are with ImageFormat.Jpeg and ImageFormat.Png.
First question, why it does this ? I would have expected the result to be always the same, but I suppose some salt is used when doing the Image.Save().
Second question : I want to have a deterministic way to serialize an image, keeping the image format intact. The goal is to save the image in a DB and also to compare serialized images to know if it already exists in the system where this function is used.
Well, I discovered while trying to solve this that a png or jpeg Image object inside C# has some metadata associated to it and doing what I was doing is just not a reliable way to compare images.
The solution I used was derived from this link
https://insertcode.wordpress.com/2014/05/13/compare-content-of-two-files-images-in-c/
So what I do finally is save the images inside the system with the SerializeImage(Image image) function previously described, and when I want to consume it I deserialize it with the DeserializeImage(string serializedImage) function previously described. But when I want to compare images I use the following functions
public static bool ImagesAreEqual(Image image1, Image image2)
{
string image1Base64Bitmap = GetImageAsBase64Bitmap(image1);
string image2Base64Bitmap = GetImageAsBase64Bitmap(image2);
return image1Base64Bitmap.Equals(image2Base64Bitmap);
}
public static string GetImageAsBase64Bitmap(Image image)
{
using (var memoryStream = new MemoryStream())
{
using (var bitmap = new Bitmap(image))
{
bitmap.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Bmp);
}
return Convert.ToBase64String(memoryStream.ToArray());
}
}
That convert the image to raw bitmap before comparing them.
This does a perfect job for me in all my needed cases : the formats of the images are saved/restored correctly, and I can compare them between them to check if they are the same without having to bother with the possibly different serialization.

C# bitmaps causes memory leak

I have a simple application, which renders images and prints them out. In order to make it easy and convenient to design, I have used custom Control.
Now, I have a simple control of size 1800 x 2400 (yeah, pretty big):
public class PhotoControl : UserControl
{
public PhotoControl()
{
InitializeComponent();
}
}
And an extension class, which uses it for generating images:
public static class PhotoProcessor
{
private static readonly PhotoControl PhotoForm = new PhotoControl(); // Single instance
public static Image GenerateImage(this Photo photo)
{
PhotoForm.SetPhoto(photo); // Apply a photo
Image result;
using (Bitmap b = new Bitmap(PhotoForm.Width, PhotoForm.Height))
{
Rectangle r = new Rectangle(0, 0, b.Width, b.Height);
PhotoForm.DrawToBitmap(b, r); // Draw control to bitmap
using (MemoryStream ms = new MemoryStream())
{
b.Save(ms, ImageFormat.Png); // Save bitmap as PNG
result = Image.FromStream(ms);
}
}
// GC.Collect();
// GC.WaitForPendingFinalizers();
return result; // return
}
Now, I try to generate 30 photos using this:
myPhoto.GenerateImage().Save(#"Output1.png", ImageFormat.Png);
myPhoto.GenerateImage().Save(#"Output2.png", ImageFormat.Png);
I do not store the reference, I just save an image and expect that GC will collect these images after saving to a file.
It will take about 2 GB of memory, and finally throw an exception:
An unhandled exception of type 'System.OutOfMemoryException' occurred in System.Drawing.dll
Additional information: Out of memory.
If I look at Visual Studio diagnostic tools, it looks like:
Let's take a snapshot, and look at the contents of the heap, we will see that there are many MemoryStreams:
What causes MemoryStream to produce a memory leak? As far as I know, using() generates Dispose() call, which should take care of it.
P.S. If I remove comment and call GC.Collect(); GC.WaitForPendingFinalizers();, then it takes 2-3 times less memory, but it still grows - ~200 images will still kill an application.
Changing the image saving to the following:
Image img;
img = myPhoto.GenerateImage();
img.Save(#"Output1.png", ImageFormat.Png);
img.Dispose();
img = myPhoto.GenerateImage();
img.Save(#"Output2.png", ImageFormat.Png);
img.Dispose();
will produce the same result as using GC.Collect();. It will also take less memory, but not eliminate memory leakage.
Image implements IDisposable and thus must be disposed manually. You're currently returning an Image from GenerateImage but not disposing it after saving.

ImageSource in a class library won't show

My application links to a class library (.dll). In the class library project, three images were put into Resources.resx. One image needs to be chosen at run time and shown on a button. After googling around, I choose to use a converter to help the binding in xaml:
[ValueConversion(typeof(Side), typeof(ImageSource))]
public class SideToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Side side = (Side)value;
switch (side)
{
case Side.Left:
return ToWpfBitmap(Properties.Resources.LeftArmOnCart);
case Side.Right:
return ToWpfBitmap(Properties.Resources.RightArmOnCart);
case Side.Both:
return ToWpfBitmap(Properties.Resources.RightArmOnCart);
default:
throw new ArgumentException("Current configuration is invalid");
}
}
private static BitmapSource ToWpfBitmap(Bitmap bitmap)
{
using (MemoryStream stream = new MemoryStream())
{
bitmap.Save(stream, ImageFormat.Png);
stream.Position = 0;
BitmapImage result = new BitmapImage();
result.BeginInit();
// According to MSDN, "The default OnDemand cache option retains access to the stream until the image is needed."
// Force the bitmap to load right now so we can dispose the stream.
result.CacheOption = BitmapCacheOption.OnLoad;
result.StreamSource = stream;
result.EndInit();
result.Freeze();
return result;
}
}
}
The xaml code looks like
<Image Source="{Binding Side, Converter={StaticResource SideToImageConverter}}" .../>
Unfortunately, the image won't show silently without throwing any exception. Debug into the converter code, the bitmap argument in ToWpfBitmap() looks fine (the Width/Height values were correct).
BTW, as another trial, the following code works fine.
[ValueConversion(typeof(Side), typeof(ImageSource))]
public class SideToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Side side = (Side)value;
switch (side)
{
case Side.Left:
return LoadBitmap("LeftArmOnCart.png");
case Side.Right:
return LoadBitmap("RightArmOnCart.png");
case Side.Both:
return LoadBitmap("RightArmOnCart.png");
default:
throw new ArgumentException("Current configuration is invalid");
}
}
private static BitmapSource LoadBitmap(string name)
{
BitmapImage result = new BitmapImage();
result.BeginInit();
string uri = "c:/.../Resources/Images/" + name;
result.CacheOption = BitmapCacheOption.OnLoad;
result.UriSource = new Uri(uri, UriKind.Absolute);
result.EndInit();
return result;
}
}
However, absolute path is not desired. So, do I miss anything?
I understand that it is feasible to use relative path by releasing the three image files. But, is it possible NOT to release the three files but just the class library dll?
Thanks!
There are a few things I think you could read up on to help you with your problem. I would start with Pack URIs in WPF so you can understand how to use a URI to check a specific library's location. Since these images are in another assembly's resource file, read the "Referenced Resource Assembly File" in the link.
I think the other thing worth mentioning is the difference between the two methods is one you provide a UriSource to (bottom) and one you don't (top). Looking at Image.Source, when used in XAML it expects a imageUri. With that hint, we can say it is definitely something to do with URIs probably.
In fact, your images are already saved as a stream inside of the libraries that they exist in, so we don't need to do anything with creating a new bitmap or whatever you're doing, instead we can simply provide a valid pack URI and return a string.
I'm thinking something like this should work, provided the name of the library the images are in is YourApp.Models, in sub folder Resources\Images\:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Side side = (Side)value;
switch (side)
{
case Side.Left:
return "pack://application:,,,/YourApp.Models;component/Resources/Images/LeftArmOnCart.png";
case Side.Right:
case Side.Both:
return "pack://application:,,,/YourApp.Models;component/Resources/Images/RightArmOnCart.png";
default:
throw new ArgumentException("Current configuration is invalid");
}
}
Note: Path names are sensitive to forward/back slash, assembly names, component. Be sure everything is correctly formatted.
This is considered an absolute URI. A relative URI would be just "/Resources/Images/LeftArmOnCart.png" which would only work if that file path existed within the assembly that called it--meaning it wouldn't work for you since you're calling from another assembly.
Hope this helps!
There is already an InteropBitmap class that can be used to convert a GDI bitmap to a WPF ImageSource, though I find it doesn't handle alpha channels very well. This is the mechanism I use:
public static BitmapSource CreateBitmapSourceFromGdiBitmap(Bitmap bitmap)
{
if (bitmap == null)
throw new ArgumentNullException("bitmap");
var rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
var bitmapData = bitmap.LockBits(
rect,
ImageLockMode.ReadWrite,
PixelFormat.Format32bppArgb);
try
{
var size = (rect.Width * rect.Height) * 4;
return BitmapSource.Create(
bitmap.Width,
bitmap.Height,
bitmap.HorizontalResolution,
bitmap.VerticalResolution,
PixelFormats.Bgra32,
null,
bitmapData.Scan0,
size,
bitmapData.Stride);
}
finally
{
bitmap.UnlockBits(bitmapData);
}
}
Note that this implementation assumes 32bpp ARGB pixel format. That should be fine if you're using PNG images. Otherwise, you'll need to add a conversion step.

C# problems with FileInfo List(add Image)

How can I insert Image object into FileInfo list?
I have a list with images of this type
public List<FileInfo> _imagesList = new List<FileInfo>();
But I need to add image to this list using a method like this one:
public void AddImage(Image img)
{
}
I tried using this method
public void AddImage(string pathToImage)
{
try
{
FileInfo file = new FileInfo(pathToImage);
_imagesList.Add(file);
}
catch (Exception e)
{
MessageBox.Show("Не удалось загрузить изображение. Ошибка : " + e);
}
}
Let me explain why you can't do that. Image class does not have any references to file which was used to create that image. When you create Image instance with
Image.FromFile(filename)
File name is not stored anywhere. All you will have inside Image instance is an array of bytes which is initialized this way:
Stream dataStream = File.OpenRead(filename);
image.rawData = new byte[(int) dataStream.Length];
dataStream.Read(image.rawData, 0, (int) dataStream.Length);
So, the point is - you can't get file name from which Image was created. Actually image can be create not from file - from any stream (network stream, memory stream). Image is not related to any file, thus you can't insert it to list of FileInfo objects.
Well, as a workaround, you can save Image to file. And then insert that file to list.
you can either use the method you gave, adding new FileInfo(pathToImage) or change your list so it'll contain Image items
you can use
public List<Image> _imagesList = new List<Image>();
for example. if you share more information i could help you more. this looks like an XY problem so maybe you should tell us what's the real problem
You cannot do like this, FileInfo accept one arguments in its constructor: fileName.
You should have something like this:
public List<FileInfo> _imagesList = new List<FileInfo>();
public void Main()
{
AddImage("YourImagePath1");
AddImage("YourImagePath2");
// ...
}
public void AddImage(string path)
{
_imagesList.Add(new FileInfo(path));
//var img = Image.FromFile(path); // do this if you need to load your image
}
UPDATE:
You changed your question and your code now looks like mine, what was the issue in the code you used to have?

C# Disposing and Ressurrecting/Loading Bitmaps using a Dictionary

i have written a programm that uses some picture ressources and creates a file from them.
As a Customer preview the images are shown before they get worked with. Therefore i need to dispose them before i can work with them. But i want to load the preview again afterwards and since it might be a huge number of pictures I want to do it dynamically, there i fail.
I use a Dictionary with the filename as String:
//Creating a Bitmap so we can preview the Picture.
try
{
mainDrawingBackgroundBitmap = new Bitmap(filePathBackgroundBackgroundImage);
if(mainAllBitmapsDictionary.ContainsKey(mainDrawingBackgroundBitmap))
{
mainAllBitmapsDictionary.Remove(mainDrawingBackgroundBitmap);
}
mainAllBitmapsDictionary.Add(mainDrawingBackgroundBitmap,filePathBackgroundBackgroundImage);
}
Now my Bitmap and path are in the Dictionary, later i dispose them this way:
private void DisposeAllFiles()
{
foreach (var tempBitmap in mainAllBitmapsDictionary.Keys)
{
tempBitmap.Dispose();
}
}
Which works just fine.
Now when i try to recreate the Bitmaps:
private void RessurrectAllFiles()
{
foreach (var tempAllBitmap in mainAllBitmapsDictionary)
{
try
{
var tempBitmap = tempAllBitmap.Key;
tempBitmap = new Bitmap(tempAllBitmap.Value);
}
catch (ArgumentException ae)
{
}
}
}
He doesn't fail or throw an error, the Dictionary even gets filled with Correct Bitmaps and strings but, those don't seem to affect the original objects anymore, so the Dictionary is as it was but when i inspect a Bitmap like the: mainDrawingBackgroundBitmap, i only see ArgumentExceptions.
To put it bluntly, where do i fail?
Keep path to images as keys, and bitmap data as values, so then it will be easy to you to manipulate data and searching dictionary will perform fast.
Dictionary<string, Bitmap>

Categories