Refresh an ImageSource efficiently: BitmapImage vs DrawingImage - wpf

I have a WPF control with a GeometryModel3D with DiffuseMaterial using an ImageBrush with data bound ImageSource.
My goal is to display a "cross-section" while hovering the mouse, and for that I can already get the proper position on the object. The problem is: There is no "3D Line" in WPF.
I have made a test drawing the line in the image I use for ImageSource and the visual result is exactely what I want.
So I plan to do the following: generate the base image once, and then draw the appropriate line on it, setting the property used as ImageSource.
I have considered two approaches:
Have the bound ImageSource of type BitmapImage. Then I create a System.Drawing.Bitmap once with the blank texture (without the line), and then each time I want the line to change position, I recreate the BitmapImage drawing a line with System.Drawing.Graphics;
Have the bound ImageSource of type DrawingGroup. Then I create a System.Windows.Media.ImageDrawing once, and then each time I want the line to change position, I recreate the DrawingGroup by changing only the GeometryDrawing composed by one single line.
Is there any inherent advantage of one method over the other?

I must admit that I've never actually tested the performance difference, but I know that the Drawing class enables a light-weight way to manipulate basic shapes and images. From the Drawing Class page on MSDN:
Drawing objects are light-weight objects that enable you to add geometric shapes, images, text, and media to an application. Drawing objects are considered light-weight because they do not provide support for Layout, Input Overview, and focus. Because of their performance benefits, drawings are ideal for backgrounds and clip art. You also use drawings when programming at the Visual level.
Conversely, the BitmapImage class has all sorts of additional extra functionality and convenience features built into it, making it less light-weight than the Drawing class. From the Imaging Overview page on MSDN:
BitmapImage is a specialized BitmapSource that is optimized for Extensible Application Markup Language (XAML) loading and is an easy way to display images as the Source of an Image control.

I ended up replacing the old GDI+ based "Graphics over Bitmap and then convert to BitmapImage"
by a very WPF/XAML friendly way of generating the image with a somewhat bulky but actually very straightforward way of stacking "layers" of Drawing objects, with elimination of A LOT of code-behind.
The line itself is a hard-coded one, with its Geometry.Transform.Y property being directly set by events in code-behind using the alturaFatia Name.
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush>
<ImageBrush.ImageSource>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.ClipGeometry>
<RectangleGeometry Rect="{Binding LimitesMapa}" />
</DrawingGroup.ClipGeometry>
<ImageDrawing x:Name="MapaAtivo"
ImageSource="{Binding MapaAtivo}" Rect="{Binding LimitesMapa}"/>
<GeometryDrawing>
<GeometryDrawing.Pen>
<Pen Thickness="2" Brush="Blue"/>
</GeometryDrawing.Pen>
<GeometryDrawing.Geometry>
<LineGeometry>
<LineGeometry.StartPoint>
<Point X="-200" Y="0"/>
</LineGeometry.StartPoint>
<LineGeometry.EndPoint>
<Point X="200" Y="0" />
</LineGeometry.EndPoint>
<LineGeometry.Transform>
<TranslateTransform x:Name="alturaFatia" Y="-200" />
</LineGeometry.Transform>
</LineGeometry>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</ImageBrush.ImageSource>
</ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>

Related

Can you set a Canvas the Background of an element in WPF?

Apologies if this has been asked but I can't find the answer. I have a couple of items (Windows, UserControls and DockPanels) that need dynamic backgrounds.
A Canvas object suits my needs well for the dynamic drawing, however, what I can't figure out is how (or even if) I can set a <Canvas> object as the value of a Background for a control object.
Can this be done and if yes, how?
Yes you can do that.
<Window.Background>
<VisualBrush>
<VisualBrush.Visual>
<Rectangle Width="50" Height="50" Fill="Red"></Rectangle><!-- Replace this with a reference to your Canvas-->
</VisualBrush.Visual>
</VisualBrush>
</Window.Background>
Code is referenced from this site there is a lot more that you can do with that background as well. Like set clipping etc ...
Note
Because you haven't specified the language you are developing it, I provided answer in the language I use namely C#, I don't use VB.
Dou you want to set the Background property of a control with the "contents" of a Canvas already populated with elements?
If so, yes; it's possible to accomplish this task creating a Brush from your Canvas. To do it, you need the VisualBrush class. In VB.NET, use something like this:
Dim oBrush As New VisualBrush(myCanvas)
myControl.Background = oBrush
Good luck, good code

Geometry drawing in WPF

I'm kinda new to all this geometry part, but I can see it gives me the ability to draw basically whatever I want. I can't find a good manual that teaches how to create whatever image I have in my head. I really hoped to find some kinda painter that extracts the data of the geometry for me but no luck so far.
For example, I found this online:
<Geometry x:Key="MagnifierIconGeometry">M44,12 C32,12 22,22 22,34 22,46 32,56 44,56 56,56 66,46 66,34 66,22 56,12 44,12z M44,0 C63,0 78,15 78,34 78,53 63,68 44,68 40,68 36.5,67.5 33,66 L32.5,66 14,90 0,79.5 18,55.5 17,55 C13,49 10,42 10,34 10,15 25,0 44,0z</Geometry>
You can tell by the name what it draws, but I would like to know how to do it myself?
If anyone could point me to a manual/any kind of program that would be fantastic!
Thanks.
To display vector graphics, in this case you can use the Path like this:
<Window.Resources>
<Geometry x:Key="MagnifierIconGeometry">M44,12 C32,12 22,22 22,34 22,46 32,56 44,56 56,56 66,46 66,34 66,22 56,12 44,12z M44,0 C63,0 78,15 78,34 78,53 63,68 44,68 40,68 36.5,67.5 33,66 L32.5,66 14,90 0,79.5 18,55.5 17,55 C13,49 10,42 10,34 10,15 25,0 44,0z</Geometry>
</Window.Resources>
<Grid>
<Path Data="{StaticResource MagnifierIconGeometry}"
Width="30"
Height="30"
Fill="Aqua"
Stretch="Fill" />
</Grid>
Output
More info
For more information you can see this:
MSDN: Shapes and Basic Drawing in WPF Overview
Charles Petzold: Vector Graphics and the WPF Shape Class
Graphics in WPF
Source of vector images
The www.modernuiicons.com contains a huge amount of vector images that you can use in your applications.
Program for working with vector images
To work with vector graphics you can use Microsoft Expression Blend:
MSDN: Drawing overview
Convert SVG to XAML and use it in Silverlight or WPF

Why does WPF make working with images so much more difficult?

I used to be able to do so very much with just the Bitmap and Graphics objects. Now that I've been using WPF the only thing I seem to be able to do is load an image and show it and make it dance around the stupid screen. Why did they get rid of these very useful tools. Are they trying to stupefy .Net?
All I want to do is load an image from a file and cut it into two parts. This was easy with .Net 2.0 and System.Drawing. But with WPF, I'm hitting a brick wall without using some very low level code. I've tried working with WriteableBitmap. But it doesn't seem to really be what I'm wanting. Is there no way to wrap a DrawingContext around a BitmapImage or something?
Please tell me that WPF is more than HTML for applications. I am REALLY frustrated!!
Edits:
Also, how on earth does one save an image to a file?
If you want to cut the image in two parts, why not use the CroppedBitmap class?
Consider the following XAML. One source BitmapImage shared by two CroppedBitmaps, each showing different parts of the source.
<Window.Resources>
<BitmapImage x:Key="bmp" UriSource="SomeBitmap.jpg" />
</Window.Resources>
<StackPanel>
<Image>
<Image.Source>
<CroppedBitmap Source="{StaticResource ResourceKey=bmp}">
<CroppedBitmap.SourceRect>
<Int32Rect X="0" Y="0" Width="100" Height="100" />
</CroppedBitmap.SourceRect>
</CroppedBitmap>
</Image.Source>
</Image>
<Image>
<Image.Source>
<CroppedBitmap Source="{StaticResource ResourceKey=bmp}">
<CroppedBitmap.SourceRect>
<Int32Rect X="100" Y="150" Width="50" Height="50" />
</CroppedBitmap.SourceRect>
</CroppedBitmap>
</Image.Source>
</Image>
</StackPanel>
Update: to do something similar in code:
var bitmapImage = new BitmapImage(new Uri(...));
var sourceRect = new Int32Rect(10, 10, 50, 50);
var croppedBitmap = new CroppedBitmap(bitmapImage, sourceRect);
Well there is this http://www.nerdparadise.com/tech/csharp/wpfimageediting/
or perhaps you could add a reference to System.Drawing to your project and then do the editing the way you are comfortable with.
You are probably best off using TransformedBitmap. Load your Bitmap as a BitmapSource, then set the Transform property to however you want the image to transform. You have several different transformation options here. This allows you to rotate, screw, matrix, etc. transformations. If you want to apply more than one, use a TransformGroup and apply several transformations at once.
You can also use BitmapFrame.Create(...) to work with the transformed image more.
Some Pseudo code:
var image = new BitmapSource(...); //Your image
var transformBitmap = new TransformedBitmap(image);
var transformBitmap.Transform = ..//Set your transform;
//optionally:
var frame = BitmapFrame.Create(transformBitmap);

WPF. How to show only part of big canvas?

Lets say I have a canvas defined to be 1000x1000 big. Is it possible to only show a 100x100 part of it in a Viewbox(or a rectangel)?
Any help is apreciated.....
If you work with Brushes, you might want to take a look at Viewbox and Viewport in WPF
Edit: I just realised that Viewbox and Viewport are used for Brushes
This is not really appropiate in your situation. I looked it up, and I think you will like the Clip property on UIElement.
Since Canvas is also a UIElement, you can use the Clip property to simulate a viewport on your Canvas..
Click here for some simple Geometry types
I think you would suffice with a RectangleGeometry
<Canvas>
<Canvas.Clip>
<RectangleGeometry Rect="50,50,25,25" />
</Canvas.Clip>
</Canvas>
Edit #2:
Hehe ok.. if you want your total Canvas displayed, only smaller, perheps you should take a look and LayoutTransform. Then use a ScaleTranform to resize your Canvas ;).
<Canvas>
<Canvas.LayoutTransform>
<ScaleTransform CenterX="0" CenterY="0" ScaleX="0.5" ScaleY="0.5" />
</Canvas.LayoutTransform>
</Canvas>
Tweak the parameters until you receive the desired effect ;)

Round window playing movie using WPF

Is it possible with WPF to create a window that has the shape of a circle and uses a playing movie as the background?
To make a non-rectangular window, you need to first do three things.
Set Window.WindowStyle to WindowStyle.None
Set Window.AllowsTransparency to True
Set Window.Background to Transparent (or {x:Null})
Now, your window is completely transparent. You can use the other tips in this thread to paint a piece of media onto the window's geometry.
You should just need to throw something like this in your xaml:
<Ellipse Height="80" Width="80">
<Ellipse.Fill>
<VisualBrush TileMode="None">
<VisualBrush.Visual>
<MediaElement Source="myMovie.wmv" />
</VisualBrush.Visual>
</VisualBrush>
</Ellipse.Fill>
</Ellipse>
Actually making the window round would be more difficult. Have a look at this if you want the window to be round, it should help figure that part out.
HTH
Don't use AllowsTransparency, it has very poor performance and a lot of compatibility problems, go to this link for alternatives:
http://blogs.msdn.com/wpfsdk/archive/2008/09/08/custom-window-chrome-in-wpf.aspx
EDIT: there is an example there how to use SetWindowRgn to get rounded corners for a rectangular windows, if you pass an ellipse region instead of a rounded-rect region you will get an elliptic window, it's easy to create a region for any shape you can imagine.
you can have a canvas as your parent container (set to transparent) then add a circle with a media brush as it's background. that should do it. :)

Resources