How to create WPF vector images - wpf

I have an application that has icons which at the moment are PNG's. I'm trying to create vector images, however I cant seem to find any good tutorials.
There are a few that are pointing to this http://templarian.com/2011/08/06/tutorial_creating_an_icon/
but this is just exporting the file as a png.
I have seen an icon online that is in the same kind of xaml format i want
<DrawingImage x:Key="RestoreIcon">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#FFFFFFFF" Geometry="F1 M 898.726,800.022L 913.274,800.022L 913.274,804.022L 918.607,804.022L 918.607,815.978L 904.06,815.978L 904.06,811.978L 898.726,811.978L 898.726,800.022 Z M 913.274,811.978L 906.57,811.978L 906.57,813.278L 915.964,813.278L 915.964,807.909L 913.274,807.909L 913.274,811.978 Z M 901.237,803.908L 901.237,809.278L 904.06,809.278L 904.06,804.022L 910.631,804.022L 910.631,803.909L 901.237,803.908 Z M 910.631,809.278L 910.631,807.909L 906.57,807.908L 906.57,809.278L 910.631,809.278 Z ">
<GeometryDrawing.Pen>
<Pen Thickness="1" LineJoin="Round" Brush="#C7141414"/>
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
This is drawing an icon from the GeometryDrawing information. I'm looking how i can go about making my own GeometryDrawing data?
Ideally i would like to be able to import my png image, draw over the tope of it and get GeometryDrawing data.

If you want to "import my png image, draw over the tope of it and get GeometryDrawing data", then the standard way is to use Blend. Or, you could use InkScape. How to do this is another subject itself, but Blend is probably where you want to start.

Related

Re-sizing WPF Geometry Path

I'm working on a WPF application. Given a geometry string path, such as:
F1 M 27,18L 23,26L 33,30L 24,38L 33,46L 23,50L 27,58L 45,58L 55,38L 45,18L 27,18 Z
Is it possible to scale the drawing to a width and height (no matter how small/large the original was) while keeping the figure as a whole, and then finally return the string path representation of the new scaled figure?
There is no need to scale the values in a path geometry string. Just put it in the Data property of a Path control and set its Width, Height and Stretch properties as needed:
<Path Data="F1 M27,18 L23,26 33,30 24,38 33,46 23,50 27,58 45,58 55,38 45,18 27,18 Z"
Width="100" Height="100" Stretch="Uniform" Fill="Black"/>
Yes you can do it! The only thing you need to do, is to use a Viewbox for wrapping the item. This is a sample with a code that I had done, in this case I used the geometry as a DrawingBrush
...
<UserControl.Resources>
<DrawingBrush x:Key="Field" Stretch="Uniform">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="{StaticResource FieldGrassBrush}" Geometry="F1 M 91.733,119.946C 92.8352,122.738 93.9374,125.529 92.9241,129.209C 91.9107,132.889 88.7819,137.458 84.4263,139.271C 80.0707,141.084 74.4885,140.142 70.8885,137.982C 67.2885,135.822 65.6707,132.444 65.1819,129.182C 64.693,125.92 65.333,122.773 65.973,119.626L 0.16,53.9203C 0.444319,53.4758 0.728638,53.0312 3.48413,48.7023C 6.23962,44.3733 11.4663,36.16 18.5596,28C 25.653,19.84 34.613,11.7333
........
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</UserControl.Resources>
...
Then the View Box (Note that the Grid has fixed Height and Width, but it will be stretched to the Viewbox's size, in this case, in an uniform way):
...
<Viewbox Stretch="Uniform" Grid.Row="2" Grid.ColumnSpan="2">
<Grid Height="300" Width="390">
<Rectangle Fill="{DynamicResource Field}" StrokeThickness="0"/>
...

Refresh an ImageSource efficiently: BitmapImage vs DrawingImage

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>

Reveal a picture covered with a solid color

I'm trying to build an application that reveals a picture covered initially with a solid color(black). The idea is that when I click somewhere on that black screen (initially) a circular area around the black pixel I clicked to be made transparent so I can see the image behind.
The solution that I came up with is to use a canvas with the picture I want covered set as background. The black cover will be divided in pixels (stored on a matrix perhaps - the image will be maximum 500x500). All those pixels will be colored black initially and then, knowing the pixel the user clicks make transparent (or delete) all pixels around that one until I clear a circular area with a preset diameter.
I don't have much experience with canvases and this solution seems quite rough to me. I wanted ask a second opinion, or if someone can give me a better solution
I'm developing the app in Silverlight, if that's relevant.
Thanks!
After some time I spend reading about silverlight controls I ended up using Image.Clip. Here's an example in case somebody like me needs it:
<Image Source="Images/img.jpg" Width="150" Height="150" Canvas.Left="30" Canvas.Top="19">
<Image.Clip>
<GeometryGroup FillRule="Nonzero">
<EllipseGeometry RadiusX="20" RadiusY="20" Center="100,100"></EllipseGeometry>
<EllipseGeometry RadiusX="20" RadiusY="20" Center="80,100"></EllipseGeometry>
<EllipseGeometry RadiusX="20" RadiusY="20" Center="80,83"></EllipseGeometry>
<EllipseGeometry RadiusX="20" RadiusY="20" Center="60,60"></EllipseGeometry>
</GeometryGroup>
</Image.Clip>
</Image>
Of course, ellipses can be placed programmatic.

How to remap colors in a DrawingBrush?

Say that I have a DrawingBrush that has three colors hard-coded, i.e. a border, a foreground, and a background.
<!-- Resource -->
<DrawingBrush x:Key="EventIcon" Stretch="Uniform">
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#FF9200CE" Geometry="F1 M 51.2119,61.4688L 43.4193,61.4688L 43.4194,29.318L 27.8341,29.318L 27.834,61.4688L 20.0414,61.4688L 35.6267,77.1353L 51.2119,61.4688 Z "/>
<GeometryDrawing Brush="#FFB400FF" Geometry="F1 M 44.4789,64.2014L 40.2667,64.2667L 40.13,29.318L 27.8341,29.318L 27.834,61.4688L 20.0414,61.4688L 33.8667,75.1467L 44.4789,64.2014 Z "/>
<GeometryDrawing Geometry="F1 M 51.2119,61.4688L 43.4193,61.4688L 43.4194,29.318L 27.8341,29.318L 27.834,61.4688L 20.0414,61.4688L 35.6267,77.1353L 51.2119,61.4688 Z ">
<GeometryDrawing.Pen>
<Pen Thickness="2" StartLineCap="Round" EndLineCap="Round" LineJoin="Round" Brush="#FF3D0033"/>
</GeometryDrawing.Pen>
</GeometryDrawing>
<GeometryDrawing Brush="#FFFFFFFF" Geometry="F1 M 33.7559,53.2538L 32.6202,40.9989L 32.6202,35.3362L 37.3531,35.3362L 37.3531,40.9989L 36.2333,53.2538L 33.7559,53.2538 Z M 32.6202,59.6771L 32.6202,54.9442L 37.3531,54.9442L 37.3531,59.6771L 32.6202,59.6771 Z "/>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
<!-- Usage -->
<Rectangle Width="16" Height="16" Fill="{StaticResource EventIcon}" />
Question
What would be the best approach to be able to change these colors from the parent Rectangle, yet still have a default fallback?
As I write this question, I have thought to two possible solutions...
Possible Solution #1
Using a RelativeSource binding to connect each to their equivalent property, e.g. {Binding Path=BorderBrush, RelativeSource={RelativeSource AncestorType={x:type Rectangle}} however:
Rectangle being a Shape does not have BorderBrush properties;
I could not provide a default value. Specifying FallbackValue in the binding won't work as the binding would resolve and take it's default value. (Edit: As I write this, I thinking that I could possibly use the NullValue property).
Possible Solution #2
Write an attached property that takes an array of colors/brushes and then have a converter to map it to the GeometryDrawing.Brush. Provide a default value using the Binding.IsNull property as I can guarantee a null value is return if it cannot map if the attached property is null or that color is not remapped.
I would go with a dynamic resource reference, define the defaults at the application level (Application.Resources) and change them locally by adding brushes with the same key in some control's resources.
You could create attached properties for each of the three colours, and then create a default style to give them default values.
You could then override these values in your Rectangle declaration if you wanted using normal attached property syntax.
The only other way I can think of is to have the colours as static resources, which you could recreate in your Rectangle's resource dictionary if you wanted to override.

Drawing a 2px Hatch brush in WPF

I'm trying to build a WPF DrawingBrush that will draw a hatch pattern using two 1px by 1px rectangles. The resulting pattern would look like the background on classic Macintosh apps.
Here's what I'm working with:
<Canvas SnapsToDevicePixels="True">
<Canvas.Background>
<DrawingBrush x:Name="gridBackgroundBrush"
Viewport="0,0,10,10"
ViewportUnits="Absolute"
TileMode="Tile">
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Geometry="M0,0 L10,0 10,10, 0,10Z" Brush="Green"/>
<GeometryDrawing Geometry="M10,10 L20,10 20,20, 10,20Z" Brush="Green" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Canvas.Background>
</Canvas>
Everything looks clear and sharp, except that the boxes are way too big. As I adjust the Viewport on the brush, things start to get blurry. It looks like the anti-aliasing is what is killing me; it wants to use 3px to fade from solid green to nothing, which doesn't work when I get to sizes below 3-4px. Is there anything I can do to totally disable the anti-aliasing and do pixel-precise drawing?
Offset the drawing with 0.5px and you'll get rid of the antialiasing effect. It happens because drawing is done on the edge of the pixel offset, rather than on the actual pixel offset that you have specified. By offset'ing X,Y with half a pixel, you tell the drawing engine to draw on the pixel itself, which eliminates the need of antialiasing.
For some reason this doesn't work with gradientbrushes though.
This behaviour is similar to what you have in Quarts drawing on Mac.
Note: It's not the viewport that you should offset, but the actual shape you're drawing when using the specified brush.
For a more complete answer, please read this article.

Resources