开发者

WPF TileBrush and DrawingContext.DrawRectangle -- relative to upper-left of rectangle?

开发者 https://www.devze.com 2023-03-28 04:10 出处:网络
When using DrawingContext.DrawRectangle with a TileBrush, I noticed that the top-left of the rectangle is not the top-left of the underlying image. This occurs with both a DrawingBrush and an ImageBru

When using DrawingContext.DrawRectangle with a TileBrush, I noticed that the top-left of the rectangle is not the top-left of the underlying image. This occurs with both a DrawingBrush and an ImageBrush with the source as a DrawingImage. Is there a way to force DrawRectangle to always snap the underlying brush to the top-left corner? Here's a picture:

WPF TileBrush and DrawingContext.DrawRectangle -- relative to upper-left of rectangle?

The two rectangles are using the same brush but are at different points on the screen. As you can see, the brush's origin is different (it seems the brush is continuous over a much larger area). Here's a minimal repro:

    private static readonly Brush _brush;

    static CustomControl()
    {
        Uri uri = new Uri(@"p开发者_Go百科ack://application:,,,/WpfApplication1;component/image.png", UriKind.Absolute);
        BitmapImage img = new BitmapImage(uri);
        Rect rect = new Rect(0, 0, img.Width, img.Height);
        ImageDrawing drawing = new ImageDrawing(img, rect);
        _brush = new DrawingBrush
        {
            Drawing = drawing, 
            Viewport = rect,
            ViewportUnits = BrushMappingMode.Absolute,
            TileMode = TileMode.Tile 
        };
        _brush.Freeze();
    }

    protected override void OnRender(DrawingContext dc)
    {
        dc.DrawRectangle(_brush, null, new Rect(70, 70, 100, 150));
        dc.DrawRectangle(_brush, null, new Rect(200, 200, 80, 120));
    }

* In this case an ImageBrush would give the correct results, but in my program, I'm dealing with a DrawingBrush with a custom GeometryDrawing.

I've tried using a pen instead of a brush, changing the TileMode, setting the stretch to Uniform, changing AlignmentX and AlignmentY... nothing seems to work. The origin of the brush is never set to the origin of the rectangle.


The solution I used was to render it at (0,0) with a TranslateTransform. So instead of...

dc.DrawRectangle(_brush, null, new Rect(70, 70, 100, 150));

You can use:

TranslateTransform transform = new TranslateTransform(70, 70);
transform.Freeze();
dc.PushTransform(transform);
dc.DrawRectangle(_brush, null, new Rect(0, 0, 100, 150));
dc.Pop();

This isn't ideal, since you have to allocate a new TranslateTransform (and therefore another underlying DUCE resource) every time you draw a new rectangle, but it performs well enough in my tests.

IMPORTANT NOTE: You may still encounter some strange (but subtle) distortions on re-rendering, such as when scrolling. These might be caused by the subpixel rendering system. You can either play around with GuidelineSets or just round the start point's X and Y to the nearest integer value.


From my experience, tiling brushes is very dependent on the relation between Viewbox, ViewboxUnits, Viewport and ViewportUnits properties of the brush.

Both properties for the units should be set to Absolute; the Viewport and Viewbox should be the same size (99% of the cases).

To elaborate, this is not something "official" that I've read, it's only from (a lot of) observation: the Viewbox is the size considered for rendering the drawing. If it's not set, the Data of the path will determine the Viewbox size. On the other hand, Viewport is the size considered while tiling the brush.

Setting the Viewbox to smaller size than Viewport will introduce spacing between each of the 'tiles' of the brush. Setting the Viewbox larger than Viewport will cause the 'tiles' to overlap (logically, on the screen the 'tile' will seem to be cut).

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号