Microsoft.Maui.Graphics icon indicating copy to clipboard operation
Microsoft.Maui.Graphics copied to clipboard

Create a "paint" object to hold all the drawing properties

Open mattleibow opened this issue 3 years ago • 6 comments

Currently all the drawing properties are on the canvas object, and that is mostly fine.

However, this makes swapping to a new set of styles a bit laborious. For example, assume I have to populate some list of blocks. One set is has a red border, a blue fill and dashed lines. The other has a single green border. I would end up with code like this:

while (true) {
  canvas.Color = Red;
  canvas.Dash = Dotted;
  canvas.DrawRect();

  canvas.Color = Blue;
  canvas.Dash = None;
  canvas.FillRect();

  canvas.Color = Green;
  canvas.Dash = None;
  canvas.DrawRect();
}

If we had a paint object, we could do this:

var redBorder = new Paint { Color = Red, Dash = Dotted };
var blueBox = new Paint { Color = Blue };
var greenBorder = new Paint { Color = Green };

while (true) {
  canvas.DrawRect(redBorder);
  canvas.FillRect(blueBox);
  canvas.DrawRect(greenBorder);
}

Not only is it less code for me, but I don't have to remember to unset everything each time I draw - especially if I am making some methods that could set any properties.

I am even a fan of having a Style property on the Paint so that we don't need a DrawRect and a FillRect. We can just do DrawRect and the Paint has the border or fill option.

https://github.com/dotnet/System.Graphics/blob/main/src/System.Graphics/ICanvas.cs#L10-L22

mattleibow avatar Mar 02 '21 15:03 mattleibow

@mattleibow It would be trivial to add that onto the API using extension methods. The hardest part would be deciding what the name of this construct should be as there is already a Paint class in this library (https://github.com/dotnet/Microsoft.Maui.Graphics/blob/main/src/Microsoft.Maui.Graphics/Paint.cs) so you can decide how to fill a shape (Solid, Gradient or Pattern).

Based on your last comment about a Style property on the object, what it seems to me that you want is a "Shape" class.

`var redRect = new Rectangle { StrokeColor = Red, Dash = Dotted }; var blueRect = new Rectangle { FillColor = Blue }; var greenEllipse = new Ellipse { FillColor = Green };

while (true) { canvas.DrawShape(redRect); canvas.DrawShape(blueRect); canvas.DrawShape(greenEllipse); }`

jonlipsky avatar Apr 01 '21 19:04 jonlipsky

The reason is that no matter what we do, we will always have to set every property on the canvas - just in case it was set somewhere.

If we have some Style object, then we always pull from the that object and not worry about unsettling things.

This will need to handle drawing paths and things. We could add a shape class, but then if I have multiple shapes with the same style, I have to set values multiple times. I suppose I am thinking to keep canvas, operation, paint and style separate.

mattleibow avatar Apr 02 '21 17:04 mattleibow

It would be trivial to add that onto the API using extension methods.

Why is there a need to preserve underlying global mutable operations and APIs?

If they need to be preserved, can they be hidden so that users who don't want to deal with global mutation can avoid accidentally depending on mutable state?

charlesroddie avatar Apr 21 '21 19:04 charlesroddie

I also notice a FillColor and FillPaint :|

mattleibow avatar Jun 09 '21 14:06 mattleibow

@mattleibow The reason that there are "SaveState" and "RestoreState" methods are so that you don't need to worry about restoring the canvas to what it was the mutable properties were.

Second, regarding FillColor and FillPaint, it's done that way to avoid extra object allocations. In my experience, the majority of the time someone is filling a shape with a color as opposed to paint (gradient, image, pattern, etc...).

jonlipsky avatar Jun 09 '21 14:06 jonlipsky

@jonlipsky - I find Skia's SKPaint very convenient. I use it extensively in Skia, in a 2D game sample that I have been working on.

What about defining an interface, e.g. IPaint? Then it would be cheap to have class SolidColorPaint : IPaint. Which only modifies Color.

interface IPaint
{
    void ApplyTo(Canvas canvas);
}

Or something like that.

ToolmakerSteve avatar Dec 11 '21 03:12 ToolmakerSteve