using BitmapSource as Image source in WPF - wpf

I'm trying to update an Image (_browserScreenshot below) object in XAML by changing the source image every time an event determines the source needs updating. Right now I have this:
public BitmapSource GetScreen()
{
Bitmap bitmap = new Bitmap(app.Browser.ClientRectangle.Width, app.Browser.ClientRectangle.Height);
app.Browser.DrawToBitmap(bitmap, app.Browser.Bounds);
BitmapSource bitmapSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bitmap.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
bitmapSource.Freeze();
bitmap.Dispose();
bitmap = null;
return bitmapSource;
}
Then I have an event handler as shown:
app.BitmapSource.Changed += new EventHandler(BitmapSource_Changed);
void BitmapSource_Changed(object sender, EventArgs e)
{
Window1._browserScreenshot.Source = app.GetScreen();
}
Now whenever this event fires a new screenshot is taken and the source of the Image (called _browserScreenshot here) control should be updated. I keep getting an error about changing the IsFrozen propery, but I can't figure out how to change this correctly and have this work the way I want it to. Thanks in advance everyone.

In all likelyhood you want to Freeze the object. The problem you are having is that you want to create a completely new BitmapSource every time and let the garbage collector dispose of the old image.

The following line turned out to be my problem:
bitmapSource.Freeze();

Related

How Can I load image dynamically when the image is visible at FlowDocument?

I want the image which is added to FlowDocument is loaded as a bitmap data when this image is visible by page change or scrolling.(not IsVisible property)
because My senario is that images(png, jpg...) are in a zip file.
and I will load zip file to memory and decompress it to memory stream.
So, this memory stream has png, jpg binary(low size).
and I change this png binary data to BitmapImage class.
This bitmapImage class is added to flowDocument.
The problem is zip file has many image files and after i changed the images to BitmapImage classes, it takes so many Memory Size.
So, I want to change it to below.
1. Save decompressed image(png, jpg...) data to MemoryStream.
2. Add this images without changing it to BitmapClasses.
3. Dynamically change images data to BitmapClasses when the image should be displayed by chainging page or scrolling.
However, I cannot find the way of number 2 above.
I tried Display Image from Byte Array in WPF - Memory Issues but it is different situation.
How Can I load image dynamically when the image is visible at FlowDocument?
You can try this way.
1. extend Image Class and Add IsVisibleChanged event handler.
2. The class has memory stream variable.
3. when you add images, just add memory stream except Source.
4. when IsVisibleChanged handler is called, add memory stream to source.
This is my sample code.
public class sampleImage : Image
{
public MemoryStream memoryStream = null;
public sampleImage () : base()
{
IsVisibleChanged += new DependencyPropertyChangedEventHandler(shandler);
}
void shandler(object sender, DependencyPropertyChangedEventArgs e)
{
if (IsVisible)
{
memoryStream.Position = 0;
var bitmap = BitmapFrame.Create(memoryStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
Source = bitmap;
} else {
Source = null;
GC.Collect(); // it depends on you.
}
}
}

Threading with WPF Images (System.InvalidOperationException)

I'm using a thread to get an image from a website and shoot it back to the parent form (WPF) to display. I ran into an issue and have managed to debug it to this example:
public void Watch()
{
while (true)
{
Bitmap bmp = new Bitmap(1, 1);
BitmapImage bmpImg = new BitmapImage();
this.SetImage(bmp, bmpImg);
}
}
public delegate void SetImageCallback(Bitmap bmp, BitmapImage bmpImg);
private void SetImage(Bitmap bmp, BitmapImage bmpImg)
{
if (!this.imgVideo.Dispatcher.CheckAccess())
{
SetImageCallback del = new SetImageCallback(SetImage);
this.Dispatcher.Invoke(del, bmp, bmpImg);
}
else
{
Bitmap bitmap = bmp;
BitmapImage bitmapImage = bmpImg;
}
}
Keep in mind that Watch() runs on its own thread. If I use the bitmap object (which I can use with PictureBox in Window Forms) everything works great. That is, debugging this code, when I get to the line
Bitmap bitmap = bmp;
And inspect the variable bmp, everything is great and works as expected. HOWEVER, when I get to the next line
BitmapImage bitmapImage = bmpImg;
And inpsect the variable bmpImage, I get a ton of System.InvalidOperationException's. When this is in practice and gets assigned to a WPF Image object, it says that "The calling thread cannot access this object because a different thread owns it." Why am I running into this issue with WPF BitmapImages (which are required to set an ImageSource) but NOT in Windows Forms Bitmap objects (which can be used to set a PictureBox)? How do I fix this in WPF?
Most objects in WPF are of this category: they cannot be shared between different threads. However certain low-level resources such as brushes and bitmaps are derived from a special class called Freezable that if frozen can be shared between different threads. Of course once an object is frozen is can no longer be modified in any way. To freeze a freezable object simply call Freeze and this will prevent cross-thread exceptions.
Instead of
if (!this.imgVideo.Dispatcher.CheckAccess())
{
SetImageCallback del = new SetImageCallback(SetImage);
this.Dispatcher.Invoke(del, bmp, bmpImg);
}
try using :
if (!App.Current.Dispatcher.CheckAccess())
App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action<CustomObject>(SetImage),CustomeObjectInstance );
Here Cutom object will be a wrapper class wrapping
Bitmap bmp, BitmapImage bmpImg
Obviously, your SetImage signature will change to
SetImage(CutomObject custObj)
I have not tested the code but this may solve the issue.
Let us know if this works so that some poor soul can be benefitted from this post.
All the best!
Sid

Blank - Black Image control in WPF if image source is absolute Uri

I use on listbox control own datatemplate. Listbox item consist one image control and some textblock.
On image source I bind property type of Uri (absolute url - for example: http://u.aimg.sk/fotky/1730/71/17307141.jpg?v=2)
Listbox have about 50 - 300 items.
If I test app, I sometimes see blank - white or black image instead user images.
The problem you can see on this images:
I would like to know what cause this problem and how can I solve this problem.
Image sources are good, I check it in browser.
Thank for advice.
I think what's happening is a race condition. Some of your images haven't completed downloading by the time you are asking them to display. There is a pretty good example given here http://social.msdn.microsoft.com/Forums/en/wpf/thread/dc4d6aa9-299f-4ee8-8cd4-27a21ccfc4d0 which I'll sum up:
private ImageSource _Src;
public ImageSource Src
{
get { return _Src; }
set
{
_Src = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Src"));
((BitmapImage)_Src).DownloadCompleted += new EventHandler(MainWindow_DownloadCompleted);
}
}
void MainWindow_DownloadCompleted(object sender, EventArgs e)
{
PropertyChanged(this, new PropertyChangedEventArgs("Src"));
((BitmapImage)_Src).DownloadCompleted -= MainWindow_DownloadCompleted;
}
With the above code, your images that are binding to your property will be told to update with the PropertyChanged call when the value is first assigned as well as AFTER the images have downloaded 100%. This is taken care of in the DownloadCompleted event handler that is utilized in the above example. This should make them not appear as a black image anymore, but as their fully-ready selves.
Also, if you are using a stream to as the source for your images, you need to make sure you use BitmapCacheOption.OnLoad. Such as:
BitmapImage source = new BitmapImage();
source.BeginInit();
source.CacheOption = BitmapCacheOption.OnLoad;
source.StreamSource = yourStream;
source.EndInit();
This is because by default the image using the source will lazy load it and by then your stream is probably closed, which could also be why you get blank/black images.
Good luck.

How to fetch attributes of image in silverlight?

I am loading an image to my silverlight application. This image will fit in a 3d model as a texture map. I need to fetch image attributes. For that I am using ImageOpened event, like this:
public MainPage()
{
BitmapImage img = new BitmapImage(new Uri("imagens/textura.jpg", UriKind.Relative));
img.ImageOpened += new EventHandler<RoutedEventArgs>(img_ImageOpened);
img.ImageFailed += new EventHandler<ExceptionRoutedEventArgs>(img_ImageFailed);
imageBrush.ImageSource = img;
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
this.MouseLeftButtonUp += new MouseButtonEventHandler(MainPage_MouseLeftButtonUp);
(...)
and then:
private void img_ImageOpened(object sender, RoutedEventArgs e)
{
BitmapImage i = sender as BitmapImage;
ImgSize.Width = i.PixelWidth;
ImgSize.Height = i.PixelHeight;
MessageBox.Show("LOADED IMAGE SIZE\n W:" + ImgSize.Width.ToString() + " H:" + ImgSize.Height.ToString());
}
The messagebox is showing the correct values for the loaded picture. But this is running after the scene is loaded, so the size is always default (0,0) ... I don't know how to fix this. I've runned the debugger, i've noticed that the scene and the model is rendered and the picture width and height is zero. After this, the event is fired ... I can't figure it out.
Thanks in advance,
Jose'
First off this is a little confusing:-
imageBrush.ImageSource = img;
InitializeComponent();
Unless you have something quite unusual happening the imageBrush object will be null until after the InitializeComponent has run.
As to your question at a guess you load the 3D model in MainPage_Loaded. The problem then is that his happens asynchronously with the arrival of the bitmap. Hence you do not want to actually load the 3D Model until both Loaded and ImageOpened events have both occured. Note it would be dangerous to assume that ImageOpened would always happen last.
The simplest solution I can think of would be to move all your existing code in the MainPage_Loaded event to ImageOpened then move the code that fetches the image to the MainPage_Loaded. This serialises the sequence, when execution of ImageOpened you are guaranteed that the page has loaded.
Not the most sophisticated solution and doesn't make use of benefits of the asynchronous nature of SL. However it should get you going and you can assess whether there is any benefit in having Page loading and bitmap downloading operating in parallel.

Resize an Uploaded Image in Silverlight 3

I'm trying to resize an image in Silverlight 3 that has been submitted by a user via the OpenFileDialog control. I can grab the contents of the file and put it into a WriteableBitmap object and then display it on the screen just fine into an Image control. The Image control will even resize it to fit the size of the image control for me which is great.
The problem is the in memory image is still the original full resolution image, I kinda need to resize it in memory because I have a bunch of expensive operations I need to perform on it on a per pixel basis. So far I have the following code...
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
btnUploadPhoto.Click += new RoutedEventHandler(UploadPhoto_Click);
}
private void UploadPhoto_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog dialog = new OpenFileDialog();
dialog.Filter = "Image files (*.png;*.jpg;*.gif;*.bmp)|*.png;*.jpg;*.gif;*.bmp";
if (dialog.ShowDialog() == true)
{
WriteableBitmap bitmap = new WriteableBitmap(500, 500);
bitmap.SetSource(dialog.File.OpenRead());
imgMainImage.Source = bitmap;
txtMessage.Text = "Image size: " + bitmap.PixelWidth + " x " + bitmap.PixelHeight;
}
}
}
Problem is the WriteableBitmap class doesn't have a Resize method on it, and setting the height and width in the constructor doesn't seem to have any effect.
What you can do is create a new Image element and set its source to a Writeable bitmap created from the stream. Don't add this Image element to the visual tree. Create another WriteableBitmap of the final size you want. Then call Render on this WriteableBitmap passing the Image element and a ScaleTransform to resize the image to the appropriate size. You can then use the second WriteableBitmap as the source for a second Image element and add that to the visual tree. You can then allow the first Image and WriteableBitmap objects to get GCed so you get the memory back.
Have you looked at the WriteableBitmapEx project? It's an open source project with a tonne of extension methods for the WriteableBitmap class. Here's how you resize:
BitmapImage image = new BitmapImage();
image.SetSource(dialog.File.OpenRead());
WriteableBitmap bitmap = new WriteableBitmap(image);
WriteableBitmap resizedBitmap = bitmap.Resize(500, 500, WriteableBitmapExtensions.Interpolation.Bilinear);
// For uploading
byte[] data = resizedBitmap.ToByteArray();
I have used FJCore with some success, it's an open source C# imaging toolkit from Occipital. Includes in-memory resizing capability.
Also check out ImageMagick.

Resources