Pregunta

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:

An image of the brush alignment issues

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(@"pack://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.

¿Fue útil?

Solución

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.

Otros consejos

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).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top