Closed JimBobSquarePants closed 7 years ago
From @dampee on December 21, 2015 12:57
If I get through my color space, I'll see what I can do for January. Any startingpoints, hints, ... ?
That'd be great if you could!
Honestly.... Not got much of a clue. I think the source for Cairo might be a place to start looking around as I think a few libraries wrap around it. I'm trying to get my head around this just now.
From @mweber26 on March 16, 2016 12:49
I don't understand this issue. What are the brushes for? There doesn't appear to be any drawing code to use them.
I've expanded on the issue. We'd obviously have to create bezier classes, a draw method, etc
I found a good primer on Beziers here. http://pomax.github.io/bezierinfo/
From @cartman300 on May 25, 2016 0:34
Is there any progress on this? I have quite a good idea how to implement these. Might give it a go.
@cartman300 None whatsoever.
I'm not the greatest mathemagician in the world so I struggle with this kind of stuff so If you know how to do it please give it a go. I would really appreciate it!
I'm essentially looking at first implementing path drawing. I think we should be able to break bezier and quadratic curves into neat methods and take advantage of the new vector types for things like Vector.Dot etc. Being able to set line thickness would very useful also.
Cheers!
From @cartman300 on May 25, 2016 23:11
I've implemented this in my fork
@cartman300 This is incredible! I'm super happy to see this come together. :smile:
The syntax is great also; really clean and pretty much exactly what I wanted. I had a quick look through the source code and I can see that you're still working on thickness. What's the status of antialiasing?
Buzzing with this demo. I appreciate it so much!
From @cartman300 on May 29, 2016 0:23
Eh, i forgot antialiasing was a thing. I should definitely implement it. Maybe even some kind of serial image processor, because stuff like this can't be parallelized easily (in fact, it makes it slower). I also have to fix the bézier paths because they don't stack easily, got no idea why yet.
Excellent. Thanks!
You can actually turn off the parallel nature of the processors by overriding the Parallelism
property, setting it to 1
. I do that here in Resizer
Hey @cartman300, Just found this excellent document on line drawing where at the end they deal with line thickness and antialiasing. We can certainly simplify the code sample by applying some of the Vector methods. I'm hoping it would be as useful to you as it looks.
From @cartman300 on June 5, 2016 23:34
Yes that's useful, but in the following weeks i'm not really sure if i'm gonna have any time to do any programming. But i'm certainly gonna have time after June ends.
Didn't forget about this.
@cartman300 No worries :smile: . I've got a busy time ahead of me also with travel and other projects.
Hey @cartman300 Just a quick checkin. Do you reckon you would get any time soon to have a further look at this?
From @cartman300 on August 5, 2016 1:26
OH yes thanks for reminding me :V I'm gonna take a look tomorrow
@cartman300 Sweet! That's great news!
From @cartman300 on August 6, 2016 6:51
Okay i reforked the repo and restarted the work. Had kind of a hard time figuring out where to put the brush stuffs (might need refactoring later).
Antialiasing should be a separate filter so you can choose different methods and also because i have no idea how to fill polygons with edge antialiasing with reasonable performance.
Yeah, we can always move it around. I'm deliberately keeping a very flat namespace for the library so feature discovery is easy.
Once we can get a working version we can work on improving performance so don't worry about that at this stage. I still have a lot of optimisation work to o on the project.
Just to note, whatever solution you do plese make sure you are using the generic Image<T,TP>
terminology throughout so that the methods can be reused for different pixel formats. I f you get stuck at all or spot anything daft I have done please let me know.
From @cartman300 on August 6, 2016 17:1
How would i go on about directly manipulating pixels in an Image<T, TP>? When i lock it, i get a generic type T for the pixels and i have no idea what to do with it.
Good question. The IPackedVector<TP>
interface allows for conversion to and from Vector4
where the X, Y, Z, & W components represent each RGBA component in applicable packed vector implementations.
Here's an example of me using those methds to change the alpha component of each pixel.
I'm moving this to v1.1 as it's far too big a scope of functionality to include just now. Hopefully we can all club together and get a really great API working for this.
Hey @cartman300 what happened to the source code from when you wrote the demo? I was going to see if I could continue your work but various methods seem to be missing.
From @cartman300 19th September 2016
The source code was very ugly and i deleted the old repository so i can re-clone the project again and reimplement it again. Got unexpectedly very busy lately so i can't guarantee anything, can give general guidelines tho.
Currently what's on top of my mind: The easiest way would be to write a "software renderer" of some kind to render polygons to an image, that part can be later reused.
After that you can use math to generate points on a path with variable resolution, you can change different path types by just changing the formula (just going along the line and spitting out points)
To give thickness to the lines, you can inflate by generating concentric circles with a variable radius (relative to each point along the line) and use math to generate convex polygons for each path segment and draw them in-order
From @cartman300 19th September 2016
This part might come in handy, isn't the most efficient but should get you started https://github.com/cartman300/Libraria/blob/master/LibrariaShared/Maths.cs#L39
From @eByte23 October 7th 2016
I'll have a play around over the weekend and see how I go. Can't promise anything but I'll take a look.
Hi, Thought I would give drawing/brushes a punt thought I should let you guys see my prototype (https://github.com/tocsoft/ImageSharp/tree/drawing) and allow you to comment on it and if/when you think its work pulling I can make a PR for it.
First lets give you some something shiny to look at:
var image = new Image(500, 500);
using (FileStream output = File.OpenWrite($"result.png"))
{
image
.BackgroundColor(Color.Blue)
.DrawLine(Brushes.HotPink, 5, new[] {
new Point(10, 10),
new Point(200, 150),
new Point(50, 300)
})
.Save(output);
}
var image = new Image(500, 500);
using (FileStream output = File.OpenWrite($"result.png"))
{
image
.BackgroundColor(Color.Blue)
.DrawPolygon(Brushes.HotPink, 5, new[] {
new Point(10, 10),
new Point(200, 150),
new Point(50, 300)
})
.Save(output);
}
var image = new Image(500, 500);
using (FileStream output = File.OpenWrite($"result.png"))
{
image
.BackgroundColor(Color.Blue)
.FillPolygon(Brushes.HotPink, new[] {
new Point(10, 10),
new Point(200, 150),
new Point(50, 300)
})
.Save(output);
}
var image = new Image(500, 500);
using (FileStream output = File.OpenWrite($"{path}/Simple.png"))
{
image
.BackgroundColor(Color.Blue)
.DrawBeziers(Brushes.HotPink, 5, new[] {
new PointF(10, 400),
new PointF(30, 10),
new PointF(240, 30),
new PointF(300, 400)
})
.Save(output);
}
var image = new Image(500, 500);
using (FileStream output = File.OpenWrite($"{path}/Simple.png"))
{
image
.BackgroundColor(Color.Blue)
.Fill(Brushes.HotPink,new BezierPolygon(new[] {
new PointF(10, 400),
new PointF(30, 10),
new PointF(240, 30),
new PointF(300, 400)
}))
.Save(output);
}
var simplePath = new LinearPolygon(
new Point(10, 10),
new Point(200, 150),
new Point(50, 300));
var hole1 = new LinearPolygon(
new Point(37, 85),
new Point(93, 85),
new Point(65, 137));
var image = new Image(500, 500);
using (FileStream output = File.OpenWrite($"{path}/Simple.png"))
{
image
.BackgroundColor(Color.Blue)
.Fill(Color.HotPink, new ComplexPolygon(simplePath, hole1))
.Save(output);
}
var image = new Image(500, 500);
var linerSegemnt = new LinearLineSegment(
new Point(10, 10),
new Point(200, 150),
new Point(50, 300)
);
var bazierSegment = new BezierLineSegment(new Point(50, 300),
new Point(500, 500),
new Point(60, 10),
new Point(10, 400));
var p = new CorePath(linerSegemnt, bazierSegment);
using (FileStream output = File.OpenWrite($"{path}/Simple.png"))
{
image
.BackgroundColor(Color.Blue)
.DrawPath(Color.HotPink, 5, p)
.Save(output);
}
Started off with vector drawing (Paths, Polygons etc) and a solid colour brush.
I tried to stick to a public API (extension methods on top of ImageBase<TColor,TBacked>
) that are similar to those found in System.Drawing
.
I decided a Path/Shape aren't really the thing as Brushes so I split there interfaces. An IBrush
then becomes a source of colors and an IShape
/IPath
in turn just control a region of the image to apply the brush to. (therefore there isn't a PathBrush
as it doesn't end up making much sense in this model)
I feel the current method for drawing outlines with details in IPen
isn't quite right. At the moment the current method is limited to a solid line with rounded endcaps/corners and thus I feel it need re-engineering to support things line line patterns (dashes etc) and end caps etc.
The way I envision this working is for an IPen
to take in an IPath
and based on the IPen
s settings convert it into an IShape
then pass that over to the FillShapeProcessor
to fill with the IPen
s configured IBrush
.
There is additional scope to optimise Shapes somewhat, at the moment we support arbitrary polygons via ILineSegment
s, but we can easily add specific implementation for well know shapes Rectangles, Circles etc and they should be able to implement the interface in a mush more efficient manor than the generic implementation.
None of this code (I would think) is particularly optimised, and I'm probably doing some horrible things with colors and allocating way to much memory but its a start.
Hi @tocsoft
I think shiny might be a bit of an understatement. This is simply awesome to see!
You're way out of my comfort zone here (A bit mathsy for me) so the only thing I will be able to offer will be optimization tips and API guidance. (On that not it might be an idea to rebase your fork from my master branch. I've been simplifying things)
The output looks beautiful though and the API neat and tidy and in keeping with what I have tried to do with the rest of the library.
I know that @EvK was having a play around with this also but I haven't seen any public output there yet. There was mention in the conversations we had on Gitter about difficulty offsetting thick Bezier curves. Is this a problem you have already solved? Perhaps you could both share ideas?
I notice you have created a couple of new types PointF
and RectangleF
. I've been contemplating removing Point
and using Vector2
only. What would be your thoughts on that? I've found that every method I've used Point
for so far has involved casting to/from Vector2
so I'm very much inclined to drop it. That looks like it would simplify your API also.
Truly blown away by this. Thanks for making the effort to contribute 💯
The only thing I've spotted so far a bit off is the direct use of Color
. I'd avoid that and stick to TColor
for now like BackgroundColor
does. Eventually there'll probably have to be static implementations of colors for the different IPackedPixel
types but I'm not in any rush to do that as each color can pack a vector or use a constructor anyway.
You're way out of my comfort zone here (A bit mathsy for me)
Its alright, its a bit mathsy for me too... its just stuff I've cobbled together for different sources, I definitely don't understand all the maths in there. 😁
I notice you have created a couple of new types PointF and RectangleF. I've been contemplating removing Point and using Vector2 only. What would be your thoughts on that? I've found that every method I've used Point for so far has involved casting to/from Vector2 so I'm very much inclined to drop it. That looks like it would simplify your API also.
I think I like having Point
and PointF
for a public API as they are more prescriptive to end users but as soon as you get into internals then I can see using Vector2
all the way.
The outlines are a bit of a hack at the moment all i'm doing is filling the region that the thickness
distance away from the shapes edge, which is why the IPen
stuff needs fixing.
Also curves in general are a massive cheat in here, I'm cheating by converting all the curves into a number of liner segments that are very short thus visually identical at the pixel scales I've used thus far, but I imagine that for large images you will see noticeable issues with the curves but that would be solvable by just generating more segments. (possibly calculating the optimal count based on the curve length rather then hard coding it like is happening now.)
I did have a version that used TColor
but changed my mind from it (can't remember why now), but the closer I look I can't see any issue moving over to it.
Now has some initial pattern support.
Not sure if its really worth porting all the System.Drawing
HatchStyle
s in? is there going to be any real want for them?
these have been blown up 4 times otherwise you can struggle to see the patterns at times
The only thing I've spotted so far a bit off is the direct use of Color. I'd avoid that and stick to TColor for now like BackgroundColor does. Eventually there'll probably have to be static implementations of colors for the different IPackedPixel types but I'm not in any rush to do that as each color can pack a vector or use a constructor anyway.
I had a play with just using TColor
throughout but it felt rather dirty when you wanted to instantiate brushes and pens manually instead of using extension methods, or if you wanted to retain one for use between manipulations (and don't use var
).
I want to create a new SolidBrush(new Color("ff5565"))
not a , new SolidBrush<Color, uint>(new Color("ff5565"))
for an end user what is the uint
doing there it would end up being some magic you would have to know to do to get it to work. Also you don't want the confusion around why doesn't this brush work with this image etc. It Just Should Work™️.
So what I've done it I've made the standard IBrush
implementation depend on Color
directly but as soon as you start to process the brush we create an IBrushApplicator<TColor, TPacked>
at which point we convert the color to the really TColor
type and use that throughout the rendering.
This allows a single instance of an IBrush
to be used across multiple images with multiple TColor
types and still wok efficiently.
I had a play with just using TColor throughout but it felt rather dirty when you wanted to instantiate brushes and pens manually instead of using extension methods, or if you wanted to retain one for use between manipulations (and don't use var).
That's not gonna work I'm afraid.... The different IPackedPixel<TPacked>
vectors operate at different ranges when represented by a Vector4
. E.g. Byte4
operates on a scale of 0 to 255, Color
at 0 to 1, NormalizedShort4
at -1 to 1, and probably the weirdest Short2
which operates at -32767 to 32767 for the first component pair and 0 to 1 for the second component pair. (These are all straight from XNA and MonoGame). The ToBytes
and FromBytes
methods handle this scaling but we only ever want to use them in the individual image formats.
The way I have handled this is to make a concrete implementation of my generic types that I know I am going to have to create instances of for Color
.
Image
is really Image<Color,uint>
as are the non-generic PixelAccessor
and PixelArea
.
I think I like having Point and PointF for a public API as they are more prescriptive to end users but as soon as you get into internals then I can see using Vector2 all the way.
Ok, let's keep them for now but I might still drop them in the future.
Don't worry about doing all the HatchBrush styles, I think what you have done so far is more than enough, If someone desperately needs them then they probably know how to write one anyway. Let's focus on correctness and optimization of the algorithms. Regarding that maybe this is useful?
Bugger... guess I'll create some standard wrappers based on Color
versions, I'll put them in and see how it feels then.
I suspect there's going to be some issues dealing with opacity consistently for all those TColor
s I'm currently quite dependent on that for anti-aliasing, I think I'm going to need a battery of tests for them all.
I did see clipper but the licence made me pause as I wasn't sure it's compatiblity with MIT but looking over it it should be fine to include code from there, I'll have a look to see what I can lift from there to make things work smoothly.
Since you would be multiplying the alpha component by a value it should be ok for most cases. Some implementations simply ignore alpha anyway so there's nothing you can do about that.
Yeah, the license is an odd one but as long as we include an original copyright we should be ok.
Oh mean to say. Check out Vector4BlendTransforms.PremultipliedLerp
for a nicer blending function. It automatically premultiplies the values for you when blending. You get a much nicer output.
status update, I've migrated the code to TColor
and to use Vector4BlendTransforms.PremultipliedLerp
for blending.
We now have things like IBrush<TColor, TPacked>
with simplimentations like SolidBrush<TColor, TPacked>
and I even created the default wrapper for Color
so you can do things like new SolidBush(Color.HotPink)
which is ends up being an IBrush<Color, uint>
.
Also I have a couple of new shinies for you, ComplexPolygon
s now work and Pen
s have a sensible api allowing for path patterns.
Automaticaly get simplified and support overlapping holes and outlines this is using the code from http://www.angusj.com/delphi/clipper.php also works for outlines
Pens now work too, you can draw a line with any custom pattern
also work with polygons, both simple and complex
That's a treasure trove of shiny now! The pen path stuff looks simple enough to use. Looking forward to a final PR
Closing this as we have most of this now.
New issue for gradient bushes is #86
From @JimBobSquarePants on December 1, 2015 12:48
This issue wraps several brush types.
I think we should implement at least:
I'm envisioning something like :
The method would draw a Bezier curve following the given path, color, and thickness.
Copied from original issue: JimBobSquarePants/ImageProcessor#264