How can you do custom drawing but with a mask? - wpf

We have a control which we do our own custom drawing in OnRender. However, we would like to use a PNG with transparency as sort of a stencil for various drawing 'passes' if you will.
Now we already know that we can simply use a PNG in an ImageBrush and set it as the control's OpacityMask, but we actually want to do several drawing passes with several different stencils. If we wanted to go the OpacityMask route, we'd have to create separate controls, separate ImageMasks, then stack them all up on top of each other which also clutters up your visual tree.
We don't want to do that. We want to do all of the drawing in the OnRender override of a single Control subclass. We just want those draw calls to be masked out by an image. We then want to repeat that over and over until our drawing is done.
Any way this can be done?

HA! Found it! Odd that the S/O community has been so quiet on this one, but for those looking for it, it's called DrawingContext.PushOpacityMask (and the corresponding 'Pop()') and does exactly what you think it does... it pushes an opacity mask (via a brush) onto the DC and all subsequent drawing is relative to the brush's opacity values.
You can also layer 'masks' for some pretty cool effects too. They are additive, not just the last one set.
I'm pretty sure the built-in OpacityMask is just used with this function in the OnRender call. What this means is you can still use the OpacityMask (provided you push that on first) then your own mask(s) for your own drawing calls. Pretty neat stuff!
Hope this helps others who were looking for this.

Related

Canvas - dynamic drawing and managing shapes

I'm trying to make an application which allows the user to draw shapes to the canvas. Once drawn, I would like for the user to be able to select, move, resize, basically manipulate the shapes in any which way.
I have done something similar in XNA and that was quite easy due to the fact that there was a draw loop. In Silverlight there is no such thing as far as I understand and I am having trouble figuring out how to manage the objects on the canvas. As in what is the best way to manage the canvas' children collection to ensure appropriate response of the UI to what the user does.
Most examples out there are pretty basic and do not go anywhere near this kind of thing. I would be grateful if somebody who has done this before could tell me how they approached the problem.
Thinking about it for a while I think I figured out how it works.
There is a draw loop for the canvas, which is the draw loop of the top level parent container which it lives on.
The difference with XNA I guess is that the collection of items to draw on the canvas doesn't need to be explicitly drawn, since the canvas takes care of drawing its children automatically.
So, what I need is some way to hold on to any object I add to the canvas' children... I can then update the objects drawing properties and the changes will be reflected in the canvas next time it refreshes.
I guess a dictionary of some sort in which to store the items I put in the list might be best...?
Not a finished answer yet, but I guess I understand half of it now.

Rectangular shaped Gradient Fill

I'm currently looking to achieve a gradient effect a bit like the rectangle in http://pjnicholson.com/Fireworks/fillgradients.htm
If I compromise a little I can get close to this using RadialGradientBrush... but is there any (not too painful) way to achieve the rectangular effect?
Use an ImageBrush instead and use this image (or a similar image generated using some image editor) for the background of your rectangle.
One solution a colleague and I came with was to derive a new Panel that used a WriteableBitmap as the source for its background.
The panel will give you the dimensions you need to make your WriteableBitmap. Using whatever algorithm you want you can fill it appropriately. In our case, we needed a radial or cone gradient, but the same concept applies.
Additionally, you can create several properties on your new control to specify the colors for the gradient. We adapted a LinearGradientBrush for our needs, but if you're working on just two colors, simple properties may suffice.
I don't have the code handy but will try to find it and post an update later. But the above should get you going.

What is the best way to copy / clone brushes?

Before drawing a shape on a canvas I have a preview that displays how the shape will look. I can adjust the opacity and then draw the shape. I may then wish to draw a second shape with a different opacity. My problem is that altering the opacity of the preview also alters the opacity of the shape that I have already drawn.
This has led me to believe that I need to create a copy of the brush used for the preview each time before drawing the shape.
There are various different brushes and for example, the gradient brushes require making a copy of the not just the gradient stop collection, but a new gradient stop for each gradient stop in the to-be-copied collection.
Am I down the right track here or should I be doing something else? Should I be copying or cloning? Would an extension method be the best way to go? Thoughts please.
What you need is cloning, it would be easy in wpf with XamlWriter/Reader, unfortunately you cannot do it in Silverlight. An extension method on Brush that makes a deep copy would work fine in your case though. You will have to handle the different brush type separately but it should not be an issue as there aren't that many.

Change Alpha Blend Mode in WPF?

The System.Drawing.Graphics class has a property CompositionMode with two options: SourceOver (which, based on the alpha component, blends whatever is drawn with the background already existing) or SourceCopy which simply overwrites the background with whatever is being drawn.
Does something similar exist in WPF?
In WPF when i draw a PolyLine for example on top of another the new PolyLine always alphablends with the background. I think that is independent of the container being used. I am using a Canvas but could not find a blend mode property anywhere. What I want to do is what the SourceCopy compositionmode mentioned above does. I.e. the new PolyLine should simply overwrite whatever is already on the Canvas.
Is there a simple way to do that, short of using pixel shaders (which - as far as I understand - wouldn't work anyways because I don't have access to the Canvas backbuffer).
I am not stuck with a Canvas and would be happy to use any container that supports overwrite mode.
I currently have a solution based on a WriteableBitmap for which I obtain a System.Drawing.Graphics context and then manipulate the CompositionMode. It works but since my window is fullscreen that solution has serious performance impacts.
Clarification and example:
The WPF window is fully transparent and so is the Canvas (back ground color(0,0,0,0)). Now I draw a PolyLine with a Color.FromArgb(128,128,0,0). I now have a semi-transparent red polyline. Next I draw the same PolyLine with Color.FromArgb(0,0,0,0). The result is the same as before because of the alpha blending taking place. What I want, however, is that the red polyline is erased with the second polyline (which is exactly what the SourceCopy mode in the Graphics class does.
I think all you need to do is make sure that the brushes used to fill/stroke the PolyLine have fully opaque alpha values (i.e. 255). Then the background shouldn't be blended into it.
You could apply a Clipping Mask, this way you can provide the path to clip over the elements that are below it, but it might be tough to maintain after a lot elements are required to be clipped...

Altering the Center of Rotation of a ScatterViewItem

By default, when manipulating a ScatterViewItem control it will rotate about its own center point.
Is there any way to alter this such that the control rotates about another point, for example rotate about its own top left corner?
I don't think this is supported by the ScatterViewItem control. Technically I think it's just adjusting the rendertransform as it's being manipulated, but a simple test of altering that didn't seem to work. It's likely resetting it at runtime. Not sure why you need this functionality (it seems like it wouldn't feel very natural), but I'd recommend creating a custom control to do it.

Resources