Horizontally threaded drawing primitives #3998
Replies: 2 comments 8 replies
-
Hi @andreas-kupries, This is a great idea, and you can actually do this now in libvips with the #!/usr/bin/env python3
import pyvips
# a two-band image where pixels are equal to their coordinates
image = pyvips.Image.xyz(1000, 1000)
# move the origin to the centre of the image
image -= [500, 500]
# find pixels within 300 pixels of the origin
image = (image[0] ** 2 + image[1] ** 2) < 300 ** 2
image.write_to_file("x.png") To make: As you say, it threads really well, it's easy to write, it scales to huge image sizes with low memory use, etc. etc. Things like polygon fill are really easy to implement like this. The downside is that you can't easily modify an existing image. The draw operations in libvips were added for the nip2 paintbox: I wanted to be able to paint on (in this case) complex images to experiment with filtering. |
Beta Was this translation helpful? Give feedback.
-
You can make a constant image by adding a constant to https://github.com/libvips/pyvips/blob/master/pyvips/vimage.py#L712-L737 The same thing is used to make eg. |
Beta Was this translation helpful? Give feedback.
-
This is an idea I had to implement horizontally threadable drawing primitives for better integration into pipelines.
When I looked at the code for drawing lines and circles I noted that these functions seem to directly write into/over an existing image using algorithms like Bresenham, etc, with no horizontal threading in sight.
The basic idea is to use
signed distance functions
(SDF
). In short an SDF for a drawing primitiveDP
takes a pointP
and the parameters of the DP (*), returning the distance from the point to the parameterized DP (outside > 0, inside < 0, and 0 when exactly on the DP). See https://iquilezles.org/articles/distfunctions2d for a lot of them, and background (in Raytracing).This is something which can be trivially calculated in parallel for all points of an image, thus fits neatly into horizontal threading.
Using
we now have a parameterizable generator for the primitive DP which uses no memory at all (beyond the parameters) and fits directly into system like other virtual images (Example:
vips_xyz()
).To actually draw DP over some image
I
, with a colorC
we can employvips_ifthenelse()
with the drawing primitive as the controlling element to select either the draw color, or the background image.where
vips_const()
would be a virtual image simply returning the same color bands for all pixels.(I currently see only
vips_black()
, and nothing for arbitrary colors, so lets add that to the idea/proposal).Note: The generator as described above generates jaggy/blocky lines and curves, etc.
To draw a smooth primitive with antialiasing at the border change the generator to
where
clamp
limits the values to the interval[0...1]
. This generates1
inside DP,0
outside, and a value between both in the border region (where we have0 < distance < 1
), falling from1
to0
.For this drawing is now blending
I
andvips_const (C)
, under the control ofDP
.I.e. DP is the opacity of
const(C)
which shows the background where DP is not, and blends at the border.(Ad *) For a circle the parameter are, for example, the location of the center, radius, and stroke-width. For a line segment start and endpoint. And so on.
Beta Was this translation helpful? Give feedback.
All reactions