wpfdrawingcontext

WPF Clear Region on a Drawing Context?


So I am producing a transparent PNG using a DrawingContext and DrawingVisual.

Inside the DrawingContext, I drew a rectange.

I would now like to "cut out" a circle inside of the rectangle. How do I do this? I did not find any functions in drawing context to clear a region.


Solution

  • You can try using CombinedGeometry to combine 2 geometries (each time). It has GeometryCombineMode allowing you to specify some logic combination. In this case what you need is GeometryCombineMode.Xor. The intersection of the Rect and the Ellipse (cirlce) will be cut out. Here is the simple code demonstrating it:

    DrawingVisual dv = new DrawingVisual();
    using (var dc = dv.RenderOpen()) {                    
      var rect = new Rect(0, 0, 300, 200);        
      var cb = new CombinedGeometry(GeometryCombineMode.Xor, 
                                    new RectangleGeometry(rect),
                                   new EllipseGeometry(new Point(150, 100), 50, 50));
      dc.DrawGeometry(Brushes.Blue, null, cb);                    
    }
    

    I hope you know how to render the DrawingVisual. You can use some RenderTargetBitmap to capture it into some kind of BitmapSource and then you have many ways to show this bitmap.

    Here is the screenshot:

    enter image description here

    The Black region means the color is transparent.

    In case you want to cut out some complex image (such as drawn text or image). You can turn the CombinedGeometry into some kind of OpacityMask (type of Brush). We can turn it into a DrawingBrush and this brush can be used as OpacityMask which can be passed into DrawingContext.PushOpacityMask method:

    DrawingVisual dv = new DrawingVisual();                
    using (var dc = dv.RenderOpen()) {                    
      var rect = new Rect(0, 0, 300, 200);
      var cb = new CombinedGeometry(GeometryCombineMode.Xor, 
                                    new RectangleGeometry(rect),
                                   new EllipseGeometry(new Point(150, 100), 50, 50));
      var mask = new DrawingBrush(new GeometryDrawing(Brushes.Blue, null, cb));      
      dc.PushOpacityMask(mask);
      dc.DrawImage(someImage, rect);
      dc.DrawText(new FormattedText("Windows Presentation Foundation", 
                                     System.Globalization.CultureInfo.CurrentCulture, 
                                     System.Windows.FlowDirection.LeftToRight,
                                     new Typeface("Lucida Bright"), 30, Brushes.Red){ 
                                       MaxTextWidth = rect.Width, 
                                       MaxTextHeight = rect.Height, 
                                       TextAlignment = TextAlignment.Center
                                     }, new Point());
    }
    

    enter image description here

    Note that the rect should have the size of your whole drawing. Then positioning the hole and other drawn stuff will be exact as what you want.

    Finally the DrawingVisual also has a useful property called Clip which is a Geometry. So you can prepare some CombinedGeometry and assign it to DrawingVisual.Clip property.

    Suppose you already have your DrawingVisual (with some drawn stuff including text, images, ...). The following code will punch a hole through it:

    //prepare the geometry, which can be considered as the puncher.
    var rect = new Rect(0, 0, 300, 200);
    var cb = new CombinedGeometry(GeometryCombineMode.Xor, 
                                  new RectangleGeometry(rect),
                                 new EllipseGeometry(new Point(150, 100), 50, 50));
    //punch the DrawingVisual
    yourDrawingVisual.Clip = cb;