elixir-image/image

Add "stroke_width" option for Image.Draw.line!() function

Closed this issue · 7 comments

Just a litte detail here, but its nice to be able to draw lines of different widths with the svg Draw module so one doesn't have to use a graphing library.

  • Is it possible to add a width option to draw line?
  • Is it also possible to add "stroke_type" option as well? {dotted, solid, dashed etc.}

The functions in Image.Draw are very simple - the underlying functions in libvips are very simple. The reason I could add :stroke_width to Draw.circle and Draw.rectangle is that I can fake it with concentric shapes and use a flood fill. But with a line the angle of the line means its very difficult to render a wider stroke because then I have to get into the mechanics of aliasing the ends of the line at an angle. Which I don't think it worth it because the Image.Draw functions mutate the underlying image which also means the image has to be fully rendered which is against the overall design strategy of libvips.

I would prefer to focus on more functions in Image.Shape which I've never properly built out. It uses SVG shapes so the rendering works properly and we can leverage all of the options that SVG provides. The upside is flexibility, and it being the right fit for the libvips architecture. The downside is that its compositing process (base image composed with the shape image) not a mutating function.

Anyway, I can definitely do what you're after by adding Image.Shape.line which I can do this weekend. But it won't be the same as Image.Draw.line for the reasons outlined above.

@kipcole9 aha I see what you mean. I mean I could just draw X lines and stack them and it should be fine for now in terms of creating a width mechanic. SVG is very useful but I can see how within the scope of the entire project how it might take a back seat for now. In my mind though, it would be so powerful to fold SVG into the Image stack. Image is such a joy to use so far and SVG additions just send me to the moon as I work in data visualization.

I may have miscommunicated. SVG is absolutely a first-class citizen in libvips is is to be preferred in nearly every case. It's the functions in Image.Draw that are the "only use as a last resort or for a specific use case".

The functions in Image.Shape all use SVG for drawing - but there are no functions yet for the basics like square or ellipse or line. I will add those functions.

I can probably still add stroke_width to Image.Draw.line but thats the limit of practical implementation. There's no practical way to add line style in those functions (the Image.Draw functions). Of course its trivial in the Image.Shape functions since they are SVG driven and thats where I would focus.

Oh ok that is great! I am still learning the code base here so that is good news. No worries, I look forward to the Shape module "taking shape" :D

lawik commented

Huh! I added resvg to my app because I assumed svg would not work. Maybe I don't need that then? For converting to a raster image like a PNG?

@lawik Converting SVG to a raster image is most definitely part of Vix/libvips. You can see some examples in the Image.Shape module. It uses libsvg under the hood although there is some discussion about moving to librsvg.

You can use both Image.open/2 and Image.from_binary/1:

# An image from an svg string
iex> {:ok, i} = Image.from_binary "<svg viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\">
...>   <line x1=\"0\" y1=\"80\" x2=\"100\" y2=\"20\" stroke=\"green\" />
...> </svg>"
{:ok, %Vix.Vips.Image{ref: #Reference<0.3301218185.3652845603.95591>}}

# Or an image path with the .svg suffix
iex> Image.open!("/Users/kip/Desktop/svg_test.svg")
%Vix.Vips.Image{ref: #Reference<0.3301218185.3652845603.95595>}

It's been a long time - and maybe this isn't anywhere top of your list now :-).

But I have added Image.Shape.line/5 to draw lines using SVG - including stroke width. It's not a mutation, it returns a new image of a line that would need to be Image.compose/2d. But since stroke width isn't possible with the current mutation functions its better - and arguably since its a pure function - the best approach.

I will close this for now but please reopen if required.