Modify existing BitmapImage in memory - c#

My WinRT XAML page loads an image from a file asset like this:
Image img = new Image(); // Windows.UI.Xaml.Controls.Image
BitmapImage bmp = new BitmapImage(new Uri(Page.BaseUri, "Assets/myImage.png"));
img.Source = bmp;
So far so good. Now, at a later time, I'd like to do some transformations such as resizing and cropping.
It looks like this can be done using BitmapEncoder and BitmapTransform, but will require reading and writing to disk - in particular, writing out the modified image to a new file.
Since my app does potentially do many transformations, I'd prefer to do this in memory without any disk I/O but can't figure out how.
Any ideas?

Have a look at the WriteableBitmap class, this allows modifications in memory.
WriteableBitmap bmi2 = new WriteableBitmap(bitmapSource);
More infomation can be found here:
http://msdn.microsoft.com/en-us/library/windows/apps/BR243259

Related

Creating a Bitmap object with transparency from a URI of a PNG file

I'm having issues trying to use a PNG image with a build action of 'Resource' with the map marker generation of GMap.NET, which expects type Bitmap.
I create a BitmapImage using the pack Uri, and then convert the BitmapImage to a Bitmap by using the code provided by Sascha Henning in their answer here:
Converting BitmapImage to Bitmap and vice versa
The resulting code is as follows:
BitmapImage bmi = new BitmapImage(
new Uri("pack://application:,,,/Images/MapMarker_JobComplete.png"));
System.Drawing.Bitmap markerBitmap = Helpers.BitmapImageToBitmap(bmi);
GMarkerGoogle marker = new GMarkerGoogle(jobLoc, markerBitmap);
Where Helpers.BitmapImageToBitmap() is:
public static Bitmap BitmapImageToBitmap(BitmapImage bitmapSource)
{
using( MemoryStream outstream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapSource));
enc.Save(outstream);
return new System.Drawing.Bitmap(outstream);
}
}
The above works, but the image is rendered with a black background instead of a transparent background. I can only assume that the conversion to a BitmapImage or from a BitmapImage to Bitmap is responsible.
I found a reference to needing to save PNG images to a lower depth, but with currently installed software (Affinity), I couldn't export with a bit depth of 16 as said, only PNG-8 (bit depth of 8; red background, presumaby due to reduced quality) or the standard PNG-24 (bit depth of 32; black background)
PNG-24 PNG-8
Currently to get the desired results (transparency supported), I use the PNG with a build action of 'Content', with the following code:
System.Drawing.Image imgMarker = System.Drawing.Bitmap.FromFile(
System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory,
"Images/MapMarker_JobComplete.png"));
GMarkerGoogle marker = new GMarkerGoogle(jobLoc, (System.Drawing.Bitmap)imgMarker);
The above works fine, so Bitmap must support transparency, but this leaves me with the PNG files as content and I would prefer them embedded into the executable, as well as be able to use the pack Uri in XAML if/when needed.
It's clear that there's some knowledge that I'm missing or haven't been able to glean out, and there's quite a few topics about people wanting to preserve transparency, but with the end result of a BitmapImage and not a Bitmap like I require.
Copying user2819245's old comment here, as it was/is the answer:
I am not sure, but i guess (based on your explanation) that BmpBitmapEncoder does not support (creating) 32bpp BMPs. Why don't you try using PngBitmapEncoder instead?

C# WPF Image loading and saving

I have been trying to load my image in bitmap image class. But whenever I am trying to save an image (using a third party library called Image Processor) the quality drops. So my question is that:
Is there any way i can save image in its full quality?
src.BeginInit();
src.UriSource = new Uri("picture.jpg", UriKind.Relative);
src.CacheOption = BitmapCacheOption.OnLoad;
src.EndInit();
You can achieve image saving without any thirdparty libraries. But you should know, that saving JPEG always drops quality a bit - its a nature of that format, not an issue with library. Using the JpegBitmapEncoder you can configure the quality, and with the maximum value you will not see a picture degradation, but anyway it present. So, consider using of PNG format instead.
Here is a short snippet how to load and save a JPEG image:
// Load from file or any other source.
var uri = new Uri(#"D:\InputImage.jpg");
var bitmap = new BitmapImage(uri);
// Save to file.
var encoder = new JpegBitmapEncoder(); // Or any other, e.g. PngBitmapEncoder for PNG.
encoder.Frames.Add(BitmapFrame.Create(bitmap));
encoder.QualityLevel = 100; // Set quality level 1-100.
using (var stream = new FileStream(#"D:\OutputImage.jpg", FileMode.Create))
{
encoder.Save(stream);
}
WebImage works very well and I use often this class in my application. It has a lot of nice built-in features, for example, saving proportins when resizing an image:
WebImage wi = new WebImage(imageName);
wi.Resize(314, 235, true);
wi.Save(imageName, "png");

Why does listbox consume so much memory when images are aquired from Isolated Storage as opposed to an image embedded in the assembly?

I have created a List Box in Windows Phone Mango.
I will be using it to display many photos in one long list.
For testing purposes I am just using one photo and repeating it for each index.
When loading the image from Isolated Storage 100 images consume 170MB of memory.
When I embbed the same image in the assembly as a resource (ie /Images/image1.jpg) 10'000 images only consume 40MB. In fact it never goes above 40MB, whatever is happeing here (UI Virtualization?) it works well.
I have to use Isolated Storage for my images, as image updates are downloaded periodically to the phone. Can I make it perform the same as an embedded image?
Can someone explain to me;
When I aquire the image from Isolated Storage, why does it uses so much memory, the more I load the higher it gets?
When I aquire it from the images folder, when it is part of the assembly, how can it load tens of thousands of images and the memory never increases, is this UI Virtualization?
Thanks for any help in advance.
Here is my code. (Only started developing 6 months ago if it looks a bit dodgy!)
//GET IMAGE FROM ISOLATED STORAGE
IsolatedStorageFile insISF = IsolatedStorageFile.GetUserStoreForApplication();
for (int i = 0; i < 100; i++)
{
byte[] byte1;
using (IsolatedStorageFileStream isfs = insISF.OpenFile("\\Photos\\image1.jpg", FileMode.Open, FileAccess.Read))
{
byte1 = new byte[isfs.Length];
isfs.Read(byte1, 0, byte1.Length);
isfs.Close();
}
Image image = new Image();
MemoryStream ms = new MemoryStream(byte1);
BitmapImage bi = new BitmapImage();
bi.SetSource(ms);
image.Source = bi;
listBox2.Items.Add(image);
//NOTE I HAVE TRIED "ms.Dispose();" HERE BUT IT DOES NOT HELP.
}
//END
//GET IMAGE FROM IMAGES FOLDER AS PART OF ASSEMBLY
for (int i = 0; i < 10000; i++)
{
BitmapImage bi = new BitmapImage(new Uri("/Images/image1.jpg", UriKind.Relative));
Image image = new Image();
image.Source = bi;
listBox2.Items.Add(image);
}
//END
When you load image from Isolated Storage, you load it byte-by-byte, e.i. all images are loaded completely.
In opposite, when you create BitmapImage, it uses DelayCreation option by default. So, application loaded images that only on screen now. See CreateOption enum for more details.
Off-thread decoding of images on Mango
Could it be the IsolatedStorageFileStream and MemoryStream objects???
Try using GC to monitor the memory usage of each iteration in your for loop.
The virtualization on your listbox should work the same in either case.
Btw, you should use the using construct on MemoryStream as well.
In your code the byte array is disposed by the Garbage Collector, the only objects that should be explicitly disposed by the garbage collector once they are out of scope are the one that can be included in a using statement...
I never tried, but I think that if you read the images directly from the stream instead of reading it from the stream, writing it to an array and re-reading it from an array, and including the stream in a using statement the memory should be freed.
Hope this help.

C# - WPF how to unreference a BitmapImage so I can delete the source file?

This seems like a fairly simple issue, but I can't seem to figure a way to work around it.
In a WPF window I have an image, image_small_pic. In the associated C# file I set the value of that using this code:
Uri src = new Uri(image_source, UriKind.RelativeOrAbsolute);
small_image_bmp = new BitmapImage(src);
image_small_pic.Source = small_image_bmp;
Where small_image_bmp is a public BitmapImage object. But then if then, later on, if I change small_image_bmp to another file and reassign image_small_pic.Source, then the original image is still locked and I can't delete it. Even if I try later it's still locked. Any thoughts how I can free this up?
Check out this article. There's some odd behaviour with WPF images that you're coming across. The solution is to read in the bytes yourself and then create an image based on them, since if you let the framework handle it, the file will remain locked.
Uri src = new Uri(image_source, UriKind.RelativeOrAbsolute);
var small_image_bmp = new BitmapImage();
small_image_bmp.BeginInit();
small_image_bmp.CacheOption = BitmapCacheOption.OnLoad;
small_image_bmp.UriSource = src;
small_image_bmp.EndInit();
image_small_pic.Source = small_image_bmp;

Does WriteableBitmap has new features in Silverlight 4?

and if someone could give me an example on how to use it, with a stream? (not sure how that works) I know how to create a BitmapImage from an URI now I need to convert this image to a WriteableBitmap, but I get a null exception error with something like this:
BitmapImage image = new BitmapImage(new Uri("http://www.example.com/example.png"));
WriteableBitmap newImage = new WriteableBitmap(image);
In short: Nope, there are no new features in Silverlight 4. The WriteableBitmapEx tries to compensate the missing functionality.
Regarding your real problem:
You should add a handler to the BitmapImage.ImageFailed event to see if there's an error when the image should be downloaded. And you you should create the WriteableBitmap in the ImageOpened event handler.
var image = new BitmapImage(new Uri("http://www.example.com/example.png"));
WriteableBitmap newImage = null;
image.ImageOpened += (s, e) => newImage = new WriteableBitmap(image);
Please also note that cross-domain references are permitted. See the MSDN page for details. You should put the image into the Web Project's ClientBin folder and use a relative path instead.
As an alternative you can also compile the image into the assembly as resource and load it from there. The WriteableBitmapEx has an extension method to make this task a bit easier. But keep in mind that this blows the assembly size up and the initial XAP loading time will increase.
// Load an image from the calling Assembly's resources only by passing the relative path
var writeableBmp = new WriteableBitmap(0, 0).FromResource("example.png");

Categories