- Index
- ImageMagick Examples Preface and Index
- ImageMagick Draw Commands
- MVG - Magick Vector Graphics
- MVG file Handling
- Stroke and StrokeWidth
- Drawing Lines, Line Width and Stroke
- Drawing Symbols as a List of Points
- Drawing Circles
- Drawing Special Characters in the Text String
- IM and SVG Handling
- Non-IM Vector Graphic Programs
- Some simple examples
Drawing in IM is the way to add new elements to IM. While a lot of text
drawing is covered in the examples page for
Compound Font
Effects, and in
Image Annotating, this page
deals with the other more general aspects of the "
-draw
" operator.
The draw command started as a means to create simple images. Now it's the
command line interface to the vector graphic operators used by IM.
ImageMagick Draw Commands
Images in computers are generally saved in two different ways. The first and
most common way you have seen throughout these example pages is known as
Raster Graphics. In this approach, images are stored in terms of a rectangular
array of pixels.
The other way is less common, and less modifiable, but in another sense more
versatile, Object Vector Graphics. In this form the image is described in
terms of lines, arcs, color fills, and sometimes depth. This is useful
because you can scale these images to just about any size you want and they
still display perfectly. You can also describe very large and complex images
in a very small amount of space when compared to the raster format
equivalent.
Examples of vector graphic images include postscript, and the new
SVG -- Scalable Vector Graphics.
True-Type Fonts are also examples of vector graphics, as this allows the
individual character descriptions to be used at any scale.
The "
-draw
" image
operator, is a window into the ImageMagick vector drawing functions, and forms
a set of commands quite separate from the normal command line image operators
of IM.
|
There are only a few vector graphic file formats in general use as every
such format is usually very different from other such formats. The result
is that there is very little in the way of code sharing possible.
For this reason, ImageMagick is more concerned with the use of vector graphics to
draw SVG format images. Postscript and true-type font graphics are passed
to other external 'delegate' libraries
and applications that are much more suited to drawing those kinds of vector graphic
formats.
That is not to say that delegates are not available for SVG. One example is a
RSVG library or GTK SVG library which is available at compile time. IM will link
to those libraries to convert SVG rather than attempting to do it itself.
|
Primitive Draw Commands
Lets start with the oldest, simplest, and most common drawing primitives of
the "
-draw
" image operator
of MVG commands.
Note that all arguments are treated as floating point, and do not have to be
integers, such as I typically use in these examples.
# Single Pixel Draw (two ways -- these have been enlarged)
# Point 'paints' the color pixel
convert -size 10x6 xc:skyblue -fill black \
-draw 'point 3,2' -scale 100x60 draw_point.gif
# Color Point 'replaces' the color pixel (see below)
convert -size 10x6 xc:skyblue -fill black \
-draw 'color 6,3 point' -scale 100x60 draw_color_point.gif
| |
|
# Rectangle / Rounded Rectangle / Rectangular Arc
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "rectangle 20,10 80,50" draw_rect.gif
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "roundrectangle 20,10 80,50 20,15" draw_rrect.gif
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "arc 20,10 80,50 0,360" draw_arc.gif
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "arc 20,10 80,50 45,270" draw_arc_partial.gif
| |
|
# Circle / Ellipse (centered on a point)
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "circle 50,30 40,10" draw_circle.gif
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "ellipse 50,30 40,20 0,360" draw_ellipse.gif
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "ellipse 50,30 40,20 45,270" draw_ellipse_partial.gif
| |
|
# Line / Polyline / Polygon / Bezier
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "line 20,50 90,10" draw_line.gif
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "polyline 40,10 20,50 90,10 70,40" draw_polyline.gif
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "polygon 40,10 20,50 90,10 70,40" draw_polygon.gif
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "bezier 40,10 20,50 90,10 70,40" draw_bezier.gif
| |
|
# text drawing / image
convert -size 100x60 xc:skyblue -fill white -stroke black \
-font Candice -pointsize 40 -gravity center \
-draw "text 0,0 'Hello'" draw_text.gif
convert -size 100x60 xc:skyblue -gravity center \
-draw "image over 0,0 0,0 'terminal.gif'" draw_image.gif
| |
|
These last two fill type operations are currently the only draw operations that are
affected by "
-gravity
".
Other modifiers for these operations, include:
"
-fill
",
"
-tile
",
"
-origin
",
"
-stroke
",
"
-strokewidth
",
"
-font
",
"
-pointsize
",
"
-box
",
Their are other modifiers but these are related to the more advanced
Magick Vector Graphics language.
Bezier Primitive
The '
bezier
' primitive is used to draw curves. Each command will
draw just one curve segment. Typically 4 points (8 numbers) is given: a start
point 'knot', two control points and an end point 'knot'. The two control
points define the direction and how fast the curve deviates from the attached
end 'knot' points.
To join two curves smoothly, the control point from the end should be mirrored
through the 'knot' to form the control point in the next Bezier curve.
For example here I draw two bezier curves that join smoothly together. Note
how the control lines and points (also drawn) mirror straight though the join
coordinate, both in angle and in length. This is important or the curve will
not be smooth.
points="10,10 30,90 25,10 50,50 50,50 75,90 70,10 90,40"
clines=`echo "$points" | sed 's/ /\n/g' |\
while read line; do echo "line $line"; done`
symbols=`echo path "'"; for point in $points; do
echo "M $point l -2,-2 +4,+4 -2,-2 l -2,+2 +4,-4 -2,+2"
done; echo "'"`
convert -size 100x100 xc:skyblue -fill none \
-draw "stroke gray $clines stroke blue $symbols " \
-draw "stroke red bezier 10,10 30,90 25,10 50,50 " \
-draw "stroke red bezier 50,50 75,90 70,10 90,40 " \
draw_bezier_joined.gif
| |
|
If I move one of the control points, so that it is NOT 'reflected' though the
attached 'knot' from the other control point of the same 'knot', then the
curve will be dis-continuous.
points="10,10 30,90 25,10 50,50 50,50 80,50 70,10 90,40"
clines=`echo "$points" | sed 's/ /\n/g' |\
while read line; do echo "line $line"; done`
symbols=`echo path "'"; for point in $points; do
echo "M $point l -2,-2 +4,+4 -2,-2 l -2,+2 +4,-4 -2,+2"
done; echo "'"`
convert -size 100x100 xc:skyblue -fill none \
-draw "stroke gray $clines stroke blue $symbols " \
-draw "stroke red bezier 10,10 30,90 25,10 50,50 " \
-draw "stroke red bezier 50,50 80,50 70,10 90,40 " \
draw_bezier_disjoint.gif
| |
|
If the control point is moved again so that it matches the related 'knot'
point the line will come directly from that point without any 'curve' at all.
points="10,10 30,90 25,10 50,50 50,50 50,50 70,10 90,40"
clines=`echo "$points" | sed 's/ /\n/g' |\
while read line; do echo "line $line"; done`
symbols=`echo path "'"; for point in $points; do
echo "M $point l -2,-2 +4,+4 -2,-2 l -2,+2 +4,-4 -2,+2"
done; echo "'"`
convert -size 100x100 xc:skyblue -fill none \
-draw "stroke gray $clines stroke blue $symbols " \
-draw "stroke red bezier 10,10 30,90 25,10 50,50 " \
-draw "stroke red bezier 50,50 50,50 70,10 90,40 " \
draw_bezier_no_curve.gif
| |
|
If both control points are set to the their respective 'knots', then a
straight line will be generated.
points="10,10 10,10 50,50 50,50 50,50 50,50 90,40 90,40"
clines=`echo "$points" | sed 's/ /\n/g' |\
while read line; do echo "line $line"; done`
symbols=`echo path "'"; for point in $points; do
echo "M $point l -2,-2 +4,+4 -2,-2 l -2,+2 +4,-4 -2,+2"
done; echo "'"`
convert -size 100x100 xc:skyblue -fill none \
-draw "stroke gray $clines stroke blue $symbols " \
-draw "stroke red bezier 10,10 10,10 50,50 50,50 " \
-draw "stroke red bezier 50,50 50,50 90,40 90,40 " \
draw_bezier_lines.gif
| |
|
The '
bezier
' primitive is not really useful without specifying all
4 points. Only the first and last point are classed as 'knots' through which
the curve will pass (or end). All the other intervening points are regarded
purely as control points, effecting the curve in the sequence given, the
further away the control point is the larger its effect on that segment of the
curve.
points="10,10 30,90 25,10 75,90 70,10 90,40"
symbols=`for point in $points; do
echo "M $point l -2,-2 +4,+4 -2,-2 l -2,+2 +4,-4 -2,+2"
done`
convert -size 100x100 xc:skyblue -fill none \
-draw "stroke gray polyline $points " \
-draw "stroke red bezier $points " \
-draw "stroke blue path '$symbols' " \
draw_bezier_multi.gif
| |
|
It is not recommended that you use more or less than 4 points per
'
bezier
' curve segment, to keep things simple.
Actually I recommend you don't use the '
bezier
' primitive at all,
but use the
SVG Path Cubic Bezier instead for generating
curves. This has a special '
S
" curve continuation function that
automatically does the appropriate control point 'reflection' to generate
smoothly joining curve segments and reduces the number of control points you
need to use. You can also define points relative to the last end point
in the path.
Color Fill Primitives
On top of the above 'simple' primitives, "
-draw
" also provides a set of color
fill or modification primitives. These modify the color(s) in the image
starting at the point specified, according to the method chosen.
These fill methods are actually not true 'draw' commands, but color
replacement function. They were added to draw as it was the easiest place to
insert their operations into ImageMagick in a very early version of the
program.
Just as in the above, the color used is set with the "
-fill
" color setting, but if set,
the "
-tile
" image will be
used instead.
The other setting options above are not used, and have no effect on these
operations.
Two extra settings as you will also apply to these primitives, "
-bordercolor" and "
-fuzz" factor settings. However
these settings can NOT be defined within the 'MVG' language, so can only be
set before using the "
-draw"
operator.
The first of these '
color point
' you have already seen as an
alternative to the '
point
' draw primitive in the above examples.
If you look carefully you will see the single white pixel we set in our test
image.
|
convert color_test.png -fill white \
-draw 'color 30,20 point' color_point.png
| |
|
However when drawing transparent and semi-transparent colors, these functions
are not the same.
Here we have a three pixel red image (enlarged), the second or middle pixel we
used the '
point
' function to
paint over the red pixel with
a semi-transparent blue color, giving a purple result. If however use use
the '
color point
' function (last or right pixel), the red color
is completely
replaced by the semi-transparent blue pixel. It is not
overlaid.
convert -size 3x1 xc:red -matte -fill '#00F8' \
-draw 'point 1,0' \
-draw 'color 2,0 point' -scale 33x33 draw_points.png
| |
|
All the '
color
' functions do full color replacement, while all
other color primitive 'paint' the color on top of the image. As such you can
use '
color
' to draw the transparent color.
The '
color replace
' draw function will replace all instances
of the exact given color at the location specified. And as you can see the
areas do not have to be connected.
convert color_test.png -fill white \
-draw 'color 30,20 replace' color_replace.png
convert color_test.png -fill white -fuzz 13%\
-draw 'color 30,20 replace' color_replace_fuzz.png
| |
|
However as you can see in the first result, some pixels along the edges did
not get replaced. These pixels are not
exactly the same color as the
pixel selected, so they were ignored. Adding a small
fuzz factor will also include colors that are similar to the original
color. As shown the second example above.
Of course a 'fuzz factor' is not a great solution, as it will not capture all
such edge pixels. This is a recurring problem with all these 'color fill'
methods, and one that has no general solution.
If you want to replace a specific known color, rather than select a color from
the image itself, then the "
-opaque
" image operator can be used instead. This function also
uses a "
-fuzz
" factor
setting to increase the range of colors that match the given color.
The '
floodfill
' method is also quite simple as it will just fill
the the whole area around the point selected and not select any other area of
similar colors which are not connected in some way.
You can also expand the area being filled by using "
-fuzz
" to include similar colors.
In this case we chose a value high enough to also include the cross border,
allowing the flood fill to 'leak' to the other side of the image.
convert color_test.png -fill white \
-draw 'color 30,20 floodfill' color_floodfill.png
convert color_test.png -fill white -fuzz 15% \
-draw 'color 30,20 floodfill' color_floodfill_fuzz.png
| |
|
Flood-filling areas with a color is not without its problems. The color can
leak across a thin boundary, into areas where it was not wanted, (see
GIFs on a background pattern as a
demonstration of this). Or it may not fill the area selected right to the
edge, (see
Anti-Aliasing and Flood Fill
Problems). But it does work.
The '
filltoborder
' is like '
floodfill
' except your
specify a color which borders the area to be filled, rather that the color to
be replaced by the fill process.
Of course a
fuzz factor is also recommended to
include 'similar colors' in that border color selection, to further limit the
floodfill.
convert color_test.png -fill white -bordercolor royalblue \
-draw 'color 30,20 filltoborder' color_filltoborder.png
convert color_test.png -fill white -bordercolor blue \
-draw 'color 30,20 filltoborder' color_filltoborder2.png
convert color_test.png -fill white -bordercolor blue -fuzz 30% \
-draw 'color 30,20 filltoborder' color_filltoborder_fuzz.png
| |
|
The final draw color method is '
reset
' which just replaces, or
resets the whole image to the fill color. In this case the actual pixel
selected has no bearing on the results at all.
convert color_test.png -fill white \
-draw 'color 30,20 reset' color_reset.png
| |
|
This is actually very useful in that it gives one simple way of generating a
plain solid color (or tiled image) canvas from an existing image. (See
Canvases Sized to an Existing Image) for this and
other ways of doing the same thing.
FUTURE: Using a "
-tile
" pattern to fill the area.
Matte Fill Primitives
The '
matte
' draw primitive works in exactly the same way as the
'
color
' primitive described above, except it will not replace the
color of the areas selected, only the 'matte' channel of the areas selected.
(That is only the 'alpha' or 'matte' channel is adjusted by these fill
functions).
Just as like the '
color
' fill function, the 'matte' value uses
the fill color (unless "
-tile
" as the source of the 'alpha value' to use.
Here we use the same '
color floodfill
' example above, but here
only adjust the matte channel to make the filled parts fully-transparent.
That is the original color is still present, just transparent!
convert color_test.png -fill none \
-draw 'matte 30,20 floodfill' matte_floodfill.png
convert color_test.png -fill none -fuzz 15% \
-draw 'matte 30,20 floodfill' matte_floodfill_fuzz.png
| |
|
The 'matte reset ' function can also be used to make a whole image
semi-transparent. Of course in this case we must output to PNG which can
accept semi-transparent colors in images.
|
convert color_test.png -fill '#00000080' \
-draw 'matte 30,20 reset' matte_reset.png
| |
|
Notice that the '
black
' color component was not used in
operations, only the matte component of the color. The images original color
is left as is.
FUTURE: Using "
-tile
" pattern for in interesting matte effect.
Note that both '
color
' and '
matte
' are full
replacement of color functions, which will always produce a Boolean (all or
nothing) type of color replacement. As such the edges of such areas will
always show
Aliasing effects.
Because of this, these are generally not good image operators for general
image development, except for setting the transparent areas of GIF images
(which are also Boolean). All is not lost however, as can be seen in the
examples for
Re-adding Transparency to
an Image.
MVG - Magick Vector Graphics
As mentioned above the commands understood by the "
-draw
" had in the last few major
releases undergone a major shift to form a special internal language in
ImageMagick, called the
Magick Vector Graphics language. For details
see
Summary of MVG Primitives and Syntax on the IM website.
These special drawing commands are designed with the goal of allowing
ImageMagick to handle the even more complex SVG (
Scalable Vector Graphics) language. It
does this by attempting to convert images in the SVG format to the simpler MVG
format, using an internal MSVG converter. For more details see
SVG handling below.
Consequently what you saw above is only a tiny part of the capabilities of the
"
-draw
" operator. Though
if you want to draw complex objects, I do recommend you create a separate SVG
format image of the object using an SVG editor such as "
Sodipodi
". (See
Non-IM Vector Graphic Programs below).
Unlike SVG, MVG does not have any form of 'containers' or sets of image
commands. These are all removed during the conversion process to produce a
simplified sequence of MVG drawing commands.
Under Construction
Command Line Settings vs MVG Settings
First of all, almost all the settings you set via the command line options that the
the draw primates use have direct equivalents in the MVG drawing commands.
The main difference between setting the via a command line option, (such as
"
-strokewidth") or using
a setting within a MVG drawing string (for example
'
stroke-width
), is that the MVG setting only lasts for the
duration of the MVG command string.
Summary of the General Drawing Settings
__cmd_option__ __draw_MVG__ __Argument__
-fill fill color/tile for inside shapes
-tile fill image tile, replaces fill color
-stroke stroke line color/tile around the shapes
-strokewidth stroke-width pixel width
+antialias stroke-antialias 0/1 aliasing line edges
-font font font_name / font_file
-family font-family ?
-weight ? ?
-stretch ? ?
-pointsize font-size height in points
-kerning - extra inter-character spacing
+antialias text-antialias 0/1 aliasing drawing text
-box text-undercolor fill color for font bounding box
- decorate (None, Underline, LineThrough or Overline)
-gravity gravity (None, North, South-East,...)
-fuzz - color delta / percentage
-bordercolor - color
Notes:
- no such option ? unknown
These settings are usually well understood as they are regularly used and
demonstrated above.
|
A font, stretch, style, and weight are used to identify a font from the
ImageMagick font list. Most people however just select a specific font and
pointsize to use instead. As such they are rarely used in IM.
|
As you can see the special settings for the 'color fill' primates do not have
direct equivalents in the MVG. That is the "
-bordercolor" and the "
-fuzz" factor setting. These must
be specified from the command line before using the "
-draw
" operator.
Some MVG settings would probably be useful as global command line settings,
such as the '
decorate
' setting for font drawing.
WARNING: "
-gravity" is not
part of the SVG specification. Within MVG it is only used for text and image
placement, and justification. Their is currently no justification setting
that is separate to the default 'gravitational' effects. However as
justification is part of SVG text handling, that will probably change sometime
in the future.
Now the global command line settings (outside the MVG draw string) are used to
initialise the settings for each "
-draw" operation you apply, which is why you can set a "
-fill" colour which you can then
use to draw a circle of that color.
|
convert -size 100x60 xc:skyblue -fill red \
-draw "circle 50,30 40,10" draw_circle_global.gif
| |
|
You can override that global setting locally within the "
-draw" MVG argument...
|
convert -size 100x60 xc:skyblue -fill red \
-draw "fill green circle 50,30 40,10" draw_circle_override.gif
| |
|
However settings set within a single "
-draw" MVG argument only exist for the duration of that "
-draw" operation. That is settings
within a "
-draw" are local only
to that draw and do not carry into later separate "
-draw" arguments.
|
convert -size 100x60 xc:skyblue -fill red -draw 'fill green' \
-draw "circle 50,30 40,10" draw_circle_local.gif
| |
|
If you plan to do a lot of operations, it may be better to do them all in the
single MVG string, rather than multiple "
-draw" operations.
|
convert -size 100x60 xc:skyblue \
-draw "fill green circle 41,39 44,57
fill blue circle 59,39 56,57
fill red circle 50,21 50,3 " draw_circle_multi.gif
| |
|
MVG Specific Settings
Other MVG settings that control the way lines and objects are drawn are
also useful to know even when using the primitive operations. These include..
__draw_MVG__ __Description/Argument__
fill-opacity fill transparency, from 0.0 to 1.0
clip-rule fill style for crossed lines (evenodd, nonzero)
stroke-opacity line transparency, number from 0.0 to 1.0
stroke-dasharray list of 'on' and 'off' lengths for lines
stroke-dash
stroke-linecap End of line look: butt round square
stroke-linejoin Lines joins: butt miter round square
stroke-miterlimit Angle when 'miter' joins become 'bevel' (or 'butt')
Remember a fill list of all MVG settings and drawing operators can be seen at
Summary of MVG Primitives and Syntax in the IM website.
Lets look at the effects of some of the simpler settings...
# Stroke Opacity
convert -size 100x60 xc:skyblue -fill none -stroke black \
-draw " path 'M 10,10 L 90,10'" \
-draw "stroke-opacity 0.8 path 'M 10,20 L 90,20'" \
-draw "stroke-opacity 0.6 path 'M 10,30 L 90,30'" \
-draw "stroke-opacity 0.4 path 'M 10,40 L 90,40'" \
-draw "stroke-opacity 0.2 path 'M 10,50 L 90,50'" \
set_stroke_opacity.gif
# Fill Opacity
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw " rectangle 5,10 15,50 " \
-draw "fill-opacity 0.8 rectangle 20,10 30,50 " \
-draw "fill-opacity 0.6 rectangle 35,10 45,50 " \
-draw "fill-opacity 0.4 rectangle 50,10 60,50 " \
-draw "fill-opacity 0.2 rectangle 65,10 75,50 " \
-draw "fill-opacity 0 rectangle 80,10 90,50 " \
set_fill_opacity.gif
| |
|
|
# Plain and Dashed Lines
convert -size 100x60 xc:skyblue -fill none -stroke black \
-draw " path 'M 10,10 L 90,10'" \
-draw "stroke-dasharray 5 3 path 'M 10,20 L 90,20'" \
-draw "stroke-dasharray 5 5 path 'M 10,30 L 90,30'" \
-draw "stroke-dasharray 10 3 3 3 path 'M 10,40 L 90,40'" \
-draw "stroke-dasharray 1 6 path 'M 10,50 L 90,50'" \
set_lines.gif
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw " path 'M 10,10 L 90,10'" \
-draw "stroke-dasharray 5 3 path 'M 10,20 L 90,20'" \
-draw "stroke-dasharray 5 5 path 'M 10,30 L 90,30'" \
-draw "stroke-dasharray 10 3 3 3 path 'M 10,40 L 90,40'" \
-draw "stroke-dasharray 1 6 path 'M 10,50 L 90,50'" \
set_lines_fill.gif
| |
|
|
# Stroke Ends and Joins
convert -size 100x60 xc:skyblue -fill white -stroke black -strokewidth 8 \
-draw " path 'M 20,20 L 20,70'" \
-draw "stroke-linecap butt path 'M 40,20 L 40,70'" \
-draw "stroke-linecap round path 'M 60,20 L 60,70'" \
-draw "stroke-linecap square path 'M 80,20 L 80,70'" \
set_endcaps.gif
convert -size 100x60 xc:skyblue -fill white -stroke black -strokewidth 5 \
-draw " path 'M 5,70 L 20,20 35,70'" \
-draw "stroke-linejoin miter path 'M 35,70 L 50,20 65,70'" \
-draw "stroke-linejoin bevel path 'M 55,70 L 70,20 85,70'" \
-draw "stroke-linejoin round path 'M 75,70 L 90,20 105,70'" \
set_linejoin.gif
convert -size 100x60 xc:skyblue -fill white -stroke black -strokewidth 5 \
-draw " path 'M 5,70 L 20,20 35,70'" \
-draw "stroke-miterlimit 7 path 'M 35,70 L 50,20 65,70'" \
-draw "stroke-miterlimit 6 path 'M 65,70 L 80,20 95,70'" \
set_miterlimit.gif
| |
|
|
|
The '
stroke-miterlimit
' setting is harder to demonstrate. This
property defines the angle at which a 'miter' is changed into a 'bevel' join.
This occurs when the line join becomes 'too sharp' preventing the line extending too
far beyond the join. Its value must be greater than 1.0.
The above shows how for the join angle I am display, the miter is converted to
a bevel somewhere between a value of 6 to 7.
For example, a '
stroke-miterlimit
' of 1.414 converts a
'
miter
' to '
bevel
' for an angle of less than 90
degrees, a limit of 4.0 (the default) converts them for an angle less than
approximately 29 degrees, and a limit of 10.0 converts them for an angle less
than approximately 11.5 degrees.
SVG Path String
The SVG path is the basic drawing primitive of SVG. It is used to draw lines
shapes, circles, curves, arcs and so on. The full specification of the SVG
Paths can be found in the
SVG
Path Specification document.
This however is not a easy document to read as it is really for programmers,
not users, so I'll simplify and summarize the path specification...
- Letters are commands, while all numbers (floating point) are arguments.
- Commas or spaces may be used as argument separators,
otherwise they are completely ignored.
- The last two arguments (x,y) of each path component will become the
end point (or 'knot') of that path component.
- Uppercase letters specify the final point absolute coordinates.
Lowercase letters are relative to the end point of the previous component.
For example: " M 1,2 l 3,4 l 2,-4
"
is the same as " M 1,2 L 4,6 L 6,2
".
That is 3,4 was added to 1,2, to draw a line to 4,6.
Then 2,-4 was added to draw a line to the final coordinate of 6,2.
- The arguments of each element may be repeated without re-issuing the same
path letter, by adding more number argument groups. However for curves, I
recommend you add the function letters anyway for ease of reading.
- Repeated arguments of "
M
" or "m
"
are treated as "L
" or "l
" respectively.
For example: " M 1,2 3,4 5,6
" is the same as
" M 1,2 L 3,4 L 5,6
"
And : " m 1,2 3,4 2,-4
" is the same as
" m 1,2 l 3,4 l 2,-4
"
- For cubic bezier all points (control and end knot points) are given
relative to the end point of the previous path component.
Note how you can specify things as either absolute coordinates or relative
coordinates. Thus you can define an object in terms of relative
coordinates and just supply an initial absolute 'move' coordinate to position
the whole path.
On the other hand you can also use other 'graphic-content' commands to move a
whole drawing within a 'viewbox' or 'translation', (see below). So really it
does not matter if you use absolute or relative coordinates in SVG paths.
Moves, Lines and Path Closures are the initial starting point
for learning about SVG object paths.
# Open, Completed and Closed Paths (same points)
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 40,10 L 20,50 90,10 70,40'" path_open.gif
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 40,10 L 20,50 90,10 70,40 40,10'" path_complete.gif
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 40,10 20,50 90,10 70,40 Z'" path_closed.gif
| |
|
Note however that '
Z
' only closes the loop. It does NOT create a
separate object. As such two 'closed' paths are still classed as being a
single drawn object, weather they are overlapping or completely disconnected.
Here we show two closed but overlapping loops. As only a single path is used
the object is a single object, and the '
fill-rule
' setting
controls how the overlapping region is to be filled.
# Overlapping Paths and Fill Rule
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "fill-rule evenodd \
path 'M 40,10 20,20 70,50 Z
M 20,40 70,40 90,10 Z' " path_evenodd.gif
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "fill-rule nonzero \
path 'M 40,10 20,20 70,50 Z
M 20,40 70,40 90,10 Z' " path_nonzero.gif
| |
|
The '
fill-rule
' is however sensitive to the direction in which
paths are drawn. All the paths should draw in exactly the same direction
relative to the 'inside' of the object.
For example here I draw the second object in the reverse direction to the
first. As such when the two objects overlap that area is circled
'
zero' times. That is it will be unfilled no matter what
'
fill-rule
' is used.
# Overlapping Closed Objects, Second object drawn in reverse
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "fill-rule evenodd \
path 'M 40,10 20,20 70,50 Z
M 20,40 90,10 70,40 Z' " path_rvs_evenodd.gif
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "fill-rule nonzero \
path 'M 40,10 20,20 70,50 Z
M 20,40 90,10 70,40 Z' " path_rvs_nonzero.gif
| |
|
This means that you can generate a 'holes' in an object, by reversing the
direction, so as to keep the 'inside' of the object to the same side of the
direction of travel.
# An object with a reversed drawn hole!
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,10 20,55 70,50 80,5 Z
M 50,20 60,40 40,30 Z' " path_with_hole.gif
| |
|
The result is the same regardless of the '
fill-rule
' setting, as
the hole is both 'even' and 'zero' so is unfilled.
Of course if you use a completely separate '
path
' element, you
will generate a completely separate object. In which case, the
'
fill-rule
' does not apply and the objects are just drawn on top
of each other, in the order given.
# Separate paths are separate objects
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 40,10 20,20 70,50 Z'
path 'M 20,40 70,40 90,10 Z' " path_separate.gif
| |
|
FUTURE: coordinate aligned paths "H" and "V"
Elliptical Arcs are the circle drawing function of SVG Paths...
The 'large' and 'sweep' parameters are especially important as they are
used to determine which of the four ways you will 'arc' from your starting
point to the finishing point for that path component.
'Large' is used for the longer path, while 'sweep' determined what side
of the direct straight line path the arc should be drawn.
# Elliptical Arcs : A radius_x,y angle large,sweep x,y
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 30,15 0 0,0 70,20'" path_arc.gif
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 30,15 0 0,1 70,20'" path_arc2.gif
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 30,15 0 1,0 70,20'" path_arc3.gif
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 30,15 0 1,1 70,20'" path_arc4.gif
| |
|
Closing an arc with a 'Z' just draws a final straight line segment.
To create a full ellipse or circle you will need at least two 'arc' segments,
both with the same 'sweep' setting, and one with the oppisite 'large' setting.
# Closed and angled elliptical arcs (defined by two edge points)
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 30,20 20 0,0 70,20 Z '" path_arc5.gif
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 30,20 20 1,1 70,20 Z '" path_arc6.gif
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 30,20 20 0,0 70,20 \
A 30,20 20 1,0 30,40 Z '" path_arc7.gif
| |
|
Note that if the line is too long to fit the given ellipse size at the angle
given, the size of the ellipse will be enlarged to fit the line with the
ellipse centered on the line.
This means that by using small numbers for the axis radii, you can just
specify a ratio of axis lengths, and gurantee the direct line path goes though
the center point of the ellipse. That is the path forms an elliptical
diameter from one side of the ellipse to the other. This is not nessarilly the
major or minor axis of the ellipse, just a elliptical diameter.
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 3,2 45 0,0 70,20'" path_arc_x.gif
| |
|
Of course using lengths of "
1,1
" results in a perfect
half-circle, going from one point, one to the next point.
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 1,1 0 0,0 70,20'" path_hcircle.gif
| |
|
The SVG definition of 'arc' also declares that if either of the two radii are
zero, then a straight line should be drawn. Thus "
0,0
" is
just a simple straight line arc...
convert -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 0,0 0 0,0 70,20'" path_arc_line.gif
| |
|
To finish off using arcs, lets give an example of typical use for them, namely,
generating pie charts. Of course you may need to use some external
trigonometric mathematics (how good was your senior high school math?) to
determine the path points that are required.
convert -size 140x130 xc:white -stroke black \
-fill red -draw "path 'M 60,70 L 60,20 A 50,50 0 0,1 68.7,20.8 Z'" \
-fill green -draw "path 'M 60,70 L 68.7,20.8 A 50,50 0 0,1 77.1,23.0 Z'" \
-fill blue -draw "path 'M 68,65 L 85.1,18.0 A 50,50 0 0,1 118,65 Z'" \
-fill gold -draw "path 'M 60,70 L 110,70 A 50,50 0 1,1 60,20 Z'" \
-fill black -stroke none -pointsize 10 \
-draw "text 57,19 '10' text 70,20 '10' text 90,19 '70' text 113,78 '270'"\
piechart.jpg
| |
|
Note that all the arcs are drawn to the left of the 'line path', and are
flagged accordingly (using the 'sweep' flag). But if the arc covers an angle
larger than 180 degrees, the 'large' flag needs to be set. see the last 'gold'
component in the example above.
Also note that you should draw each section completely, even though this means
you may have to draw bordering lines twice. If you don't you will probably
either not completely fill that section with color, or the fill color will
overlay a previously drawn section outline.
The only way to avoid doubling up multiple lines is to draw all the filled
areas, then repeat so as to draw the outlines. That is you will need to
drawing everything twice, ensuring things match up properly. Thus, doubling
up of the outlines is probably the simplist solution.
Cubic Bezier curves can be defined using a '
C
' function
defining two control points, and the final end point. For continuing Cubic
Bezier curves that use a mirror image of the last control point (for a
continuous curve), you can use a '
S
' function.
Here is an example. Because of the complexity of this function, I
pre-prepared a canvas showing the location of the control points, as well as
the 'assumed mirror' of the last control point.
# Cubic Bezier: C control_1_x,y control_2_x,y x,y
# Smooth " : S control_2_x,y x,y
convert path_cubic_canvas.gif -fill white -stroke black \
-draw "path 'M 10,30 C 10,4 50,4 50,30 S 90,55 90,30' " \
path_cubic.gif
| |
|
The line connecting the control point to the final point on the path of that
path segment (control line) basically defines the direction of the curve
though that point on the path. A long control line will produce a smoother
curve at that point, while a short line generates a sharper curve at that
point. If the control point matches the point of the curve (control line is
zero in length) the curve with have a sharp discontinuity at that point, as if
only straight line segments were used.
As a more practical example, the following bit of code is extracted from the
IM Examples Logo Generator Script
which creates the curvy splash area of the
IM
Examples Logo
The tricky part of the example is that I convert the Cubic Bezier path string
I use, into another path showing the control lines used to generate the bezier
curve. This lets me see the curve's control line angles and lengths, making
it a lot easier to adjust the results. Only one set of points needs to be
adjusted to show both curve and controls, keeping mistakes to a minimum.
curve="M 12,27 C 7,37 18,50 18,60 S 0,80 10,94
S 40,74 50,78 S 60,99 76,95 S 72,70 75,65
S 95,55 95,42 S 69,37 66,32 S 67,2 53,7
S 43,17 35,22 S 17,17 12,27 Z"
c_ctrls=`echo $curve | \
sed '1s/\([0-9]\) *\([0-9]\)/\1 M \2/;
s/S/M/g; s/C/ /;' -`
convert -size 100x100 xc:white \
-draw "stroke None fill Green path '$curve'" \
-draw "stroke Red fill None path '$c_ctrls'" \
curvy_splash.gif
| |
|
If you look closely at the image you will see that the start and end of the
curve has two control lines facing in opposite directions. For a closed
continuous path, both start and end control lines should be at the same angle
(just in the opposite, mirror direction), and of course the same length. This
is important to remember, as it is easy to get this wrong.
All the other points alone the curve only has a single control point/line
which points in the opposite direction to the direction the curve is drawn.
The '
S
' function internally generates first control point/line
for the next segment from the data of the previous segment, so as to produce a
smooth continuation of the curve.
For more examples of this path function see,
SVG:
Cubic Bezier Curve Commands.
Manually Generating a Bezier Curve is relatively straight forward
without needing any fancy GUI tools.
- First define all the coordinate points you want you curve to go though,
repeat the starting coordinate at the end of the list.
- Now expand this list by doubling all the x,y coordinate points into pairs
and add a '
S
' (Smooth Cubic) function before every pair. The
first number in each pair is the control point connected to the second
number representing the point on the curve. The first point pair however
has this reversed, with the first point being the start of the curve and
the second representing the first and only inverted control point.
- Change the function letter of the first pair of coordinates from an
'
S
' into an 'M
', then add a 'C
'
between this pair of coordinates. Finally remove the
'S
' from the second pair of coordinates, to complete the
initial Cubic ('C
') function.
- Complete the path by adding a final '
Z
' to close the curve.
See the example sequence above as to how it should look.
- At this point you can test draw your path. The path will only consist of
straight line segments as all control lines will be of length zero.
- All you need to do is now slowly and carefully adjust the position each
control point (the first coordinate of each '
S
' pair) to get
the final curve you want. Do not make control lines too long, or in the
wrong direction or you'll get a very funny looking curve.
- To help see your changes and find mistakes, use the conversion
"
sed
" command above to draw the control lines between the on
path points and the associated control point. Note however that zero
length control lines are not visible, except as a sharp corner on the
curve.
- Finally ensure the first control point/line after a '
C
' is
exactly opposite that of the of the ending control/line, at that same
position.
Interactive Curve Generation is also possible by using some vector
graphic editors.
For example
Luis Guerra reports that "
Inkscape" generated bezier curves can be
made accessible using the "Edit -> XML Editor" function then selecting the
path or shape you want the control points for.
Do you know of other ways of extracting a bezier curve (giving either two or
one control point per point on curve) using a GUI tool. Or perhaps some other
technique for generating such curves? Email me! I'd love to hear about it.
You will be credited with the technique as others have.
Quadratic Bezier is a simplification of the Cubic Bezier function, when
the two control points are merged into a single control point. Again you can
start the curve with a '
Q
' function, and then use a
'
T
' function to continue the curve, mirroring the last control
point.
# Quadratic Bezier: Q control_x,y x,y
# Smooth " : T x,y
convert path_quad_canvas.gif -fill white -stroke black \
-draw "path 'M 10,30 Q 20,4 50,30 T 90,30' " \
path_quad.gif
| |
|
I should warn you however that the '
T
' continuing function really
only works for paths which connect points that are equally spaced.
I do not recommend the use of a Quadratic Bezier Curves in any but the simplest
of cases. IE: for simple curved arcs between line segments.
For more examples of this path function see,
SVG: Quadratic Bezier Curve Commands.
Warping of the Drawing Surface
On top of these abilities, the drawing surface on which the objects are drawn
can be warped in various ways to allow you to do some amazing things.
First you can apply some general drawing surface modifications such as...
'
translate
', '
rotate
', '
scale
',
'
skewX
', '
skewY
', and '
affine
'.
For example given a 'path' of lines we can "translate" the
origin or 0,0 point of the drawing surface to another location.
convert -size 100x60 xc:skyblue \
-draw "translate 50,30
image over 3,3 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " transform_translate.gif
| |
|
Notice that the '
0,0
' or origin of the drawing area is now
centered on the image, though the Y axis remains negative at the top and
positive at the bottom of the image.
The "
rotate
" operation will rotate the drawing surface
so anything later drawn on that surface will be drawn rotated. Of course it
will rotate around the translated origin, so it is a good idea to use both
transformation operators together.
convert -size 100x60 xc:skyblue \
-draw "translate 50,30 rotate -30
image over 4,4 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " transform_rotate.gif
| |
|
"
scale
" will magnify and shrink the drawing surface around the
origin.
convert -size 100x60 xc:skyblue \
-draw "translate 50,30 scale 1.5,1.5
image over 4,4 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " transform_scale.gif
| |
|
One common use of "
scale
" is to flip the Y axis so that the a
positive Y value is upward. Of course the origin should also be moved either
to the center, or the lower left corner, to keep things in order.
convert -size 100x60 xc:skyblue \
-draw "translate 50,30 scale 1,-1
image over 4,4 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " transform_flip.gif
| |
|
And finally, "
skewX
" and "
skewY
" shear the
image in the X and Y directions. For example, here we use "
skewX
"
to give the vertical Y axis of the image a slant.
convert -size 100x60 xc:skyblue \
-draw "translate 50,30 skewX 20
image over 4,4 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " transform_skewY.gif
| |
|
These operators have equivalents outside the MVG "
-draw
" string, for general use.
However these command line versions are operators and are applied immediately
to images already existing in memory rather that to a drawn surface only which
vector objects have yet to be drawn. For more details see
Distorting Images.
Affine Warping of the Drawing Surface
All five of the above canvas transformations can be combined into a general
affine matrix operator.
WARNING: The the command line -affine -transform combination will immediately distort
existing images already in memory. The MVG version is a setting
which modifies the drawing canvas onto which objects and images are later
drawn.
affine sx, rx, ry, sy, tx, ty # a transformation & translation matrix
These values are in fact a transformation matrix.
Which form a affine transformation matrix, to map old pixel coordinates
into new coordinates...
| sx rx 0 |
| x', y', 0 | = | x, y, 1 | * | ry sy 0 |
| tx ty 1 |
This equates to the mathematical formula...
( x', y' ) = ( x * sx + y * ry + tx, y * sy + x * rx + ty )
To rotate a point (x, y) by an angle R (in radians), you get
( x', y' ) = ( x cos R + y sin R, -x sin R + y cos R)
Thus, to rotate counterclockwise angle R, you set an affine matrix
cosR, sinR, -sinR, cosR, 0, 0
After you set your rotation, you can multiply the first two figure by the same
constant to scale the x coord, and multiply the middle two value by some
constant to scale the y coord. Finally set the last two values for the
placement of the origin, or point of rotation and scaling.
Just set a central origin relative to while objects are drawn...
convert -size 100x60 xc:skyblue \
-draw "affine 1,0,0,1,50,30
image over 4,4 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " affine_null.gif
| |
|
Flip image over...
convert -size 100x60 xc:skyblue \
-draw "affine 1,0,0,-1,50,30
image over 4,4 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " affine_flip.gif
| |
|
Rotate by 30 degrees around our origin...
convert -size 100x60 xc:skyblue \
-draw "affine .866,-.5,.5,.866,50,30
image over 4,4 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " affine_rot.gif
| |
|
Under Construction
MVG Save/Recover drawing settings
push graphic-context
pop graphic-context
More settings used specifically for MVG handling of SVG format.
font-family font-stretch font-style font-weight
encoding 'UTF-8'
push defs
push gradient 'def_name' linear X1,Y1 X2,Y2
stop-color 'color' where
stop-color 'color' where
# where is a point between the two pixels given (0 = X1,Y1 1= X2,Y2)
gradient-units 'objectBoundingBox|userSpaceOnUse'
affine ....
pop gradient
push gradient 'def_name' radial CX,CY FX,FY R
# Here CX,CY is the center of the radial gradient of radius R
# the FX,FY is the focal, and is usually the same a CX,CY
# unless you are trying to warp the gradient in a specific direction
stop-color 'color' where
...
pop gradient
pop defs
push graphic-context
fill 'url(#def_name)'
... draw things here ...
pop graphic-context
Currently some parts of MVG format are incomplete and do nothing. Of course
those same parts in the SVG format are also not yet working, if the internal
SVG conversion is used by IM. This is however being looked at, but is now at a
lower priority.
For examples see Florent Monnier's development site...
http://www.linux-nantes.fr.eu.org/~fmonnier/OCaml/MVG/
MVG File Handling
Reading MVG Files
As you can see in the above examples the MVG "
-draw" arguments can become very long. In fact the conversion of
SVG to MVG can produce some extremely long MVG drawing arguments (see below).
However the general command line interface of IM allows you to read any string
argument from a file by using a "
@filename
" argument instead.
This is handy as it means you can read in your very long and complex MVG
drawing commands from a separate file.
For example, If I put MVG operations into a file called
"
draw_circles.mvg
", I can then draw it like this...
convert -size 100x60 xc:skyblue -draw @mvg_circles.mvg mvg_draw.gif
| |
|
|
Not only that but ImageMagick also understands reading "MVG:" image file
format directly allowing you to draw such commands more directly. However
unless the MVG file defines a canvas, you may need to specify the initial
canvas ("
-size" and "
-background") for it to draw
onto.
convert -size 100x60 -background limegreen mvg_circles.mvg mvg_file.gif
| |
|
|
You can move the initial canvas settings into the MVG image by adding a
'
viewbox
' to the MVG file, with appropriate background color fill
draws. That completes the MVG image file as a complete image definition.
convert mvg_circles2.mvg mvg_image.gif
| |
|
|
|
There is currently only one way of reading a external MVG file from inside
a MVG argument string, and that is using a 'image ' drawing
primitive. Unfortunately this converts the MVG include into a image
BEFORE overlaying that image onto the drawing surface. In other words it
is not drawn as a vector image, but a raster image.
In other words there is currently no MVG 'include' function.
:-(
|
Under Construction
You can generate the low level draw operations of IM, using the "+render
" to record them.
When you then give a "-render
" setting/operator, IM will immediately draw those saved
operations.
Strangely just using a "MVG" format output file also seems to do this...
convert ... -draw '....' draw_commands.mvg
NOTE: if you draw a curve while outputting a MVG format file, the file lists
the curve as a series of short line segments, rather than the original curve.
You can of course go the whole way and use the more universal SVG format.
See "SVG format handling" below.
Stroke, StrokeWidth and Fill Interaction
The "
-stroke
" and
"
-strokewidth
"
options are used when drawing an outline around a font's
edge.
These options commonly used with "
-fill
" to make text more interesting, for very little effort.
convert -size 380x70 xc:lightblue -pointsize 50 -font Chisel \
-fill green -stroke black -draw 'text 10,55 "Black Border"' \
stroke_font.jpg
|
As you can see above, the default setting of "
-strokewidth
" is 1. However
the outline stroke of draw is by default turned off as if you used,
"
-stroke none
", which in effect draws the transparent color,
resulting in no outline stroke.
Here we generated a image of the same font drawn as normal, and 5 different
"
-strokewidth
"
settings, so you can see their effect.
convert -size 320x420 xc:lightblue -pointsize 70 -font Vademecum \
-fill red -stroke none -draw 'text 30,80 "Stroke -"' \
-fill red -stroke black -strokewidth 0 -draw 'text 30,160 "Stroke 0"' \
-fill red -stroke black -strokewidth 1 -draw 'text 30,240 "Stroke 1"' \
-fill red -stroke black -strokewidth 2 -draw 'text 30,320 "Stroke 2"' \
-fill red -stroke black -strokewidth 3 -draw 'text 30,400 "Stroke 3"' \
stroke_table.jpg
|
Note from the the above examples that setting a "
-strokewidth
" of
'
0
' is NOT the same as setting the "
-stroke
" color to
'
none
' (the default). The former makes a very very thin stroke
outline, while the latter effectively turns it off. In both cases the stroke
is still drawn.
Here is an example of using an extreme stroke width.
convert -size 320x100 xc:lightblue -font Candice -pointsize 72 -fill white \
-stroke black -strokewidth 15 -draw "text 25,65 'Anthony'" \
stroke_thick.jpg
|
Note that "
-strokewidth
" expands both lines inward and outward. Here is the
same example but with the font re-drawn, without the stroke outline, to remove
the inside part of the very thick stroke.
convert -size 320x100 xc:lightblue -font Candice -pointsize 72 -fill white \
-stroke black -strokewidth 15 -draw "text 25,65 'Anthony'" \
-stroke none -draw "text 25,65 'Anthony'" \
stroke_outline.jpg
|
For more examples of using stroke see
Compound Font
Effects. Have a special look at the "
Balloon
Effect".
Drawing Lines, Line Width and Stroke
The default line drawing in IM has few weird behaviours, which are worth
knowing about.
Here is the default line draw...
convert -size 100x40 xc:lightblue \
-draw "line 5,35 95,5" \
line_default.jpg
| |
|
You can set the color of the line with a "
-fill
" option.
convert -size 100x40 xc:lightblue \
-fill white -draw "line 5,35 95,5" \
line.jpg
| |
|
Also you can make a line slightly thicker by setting the "
-stroke
" color.
convert -size 100x40 xc:lightblue \
-fill white -stroke black -draw "line 5,35 95,5" \
line_stroke.jpg
| |
|
But what happened to the white color we specified with the "
-fill
" option?
This is the tricky aspect of drawing lines in ImageMagick. What the program
does is actually consider the line as a filled object about 1 pixel wide.
This is natural, as typically multiple lines are generally used to sweep out
an area that is to be filled.
So just as when we used stroke with fonts in the previous section, IM draws
the line (or object) using the fill color, then draws around it with the
stroke color. The result is that the above stroke color line is now slightly
thicker, with the fill color completely hidden underneath. If you make the
stroke color semi-transparent you can make that fill color visible again.
To summarize, lines will appear to be drawn with the "
-fill
" color, but that option is of
no consequence once the "
-stroke
" color has been defined as something other than the
default "
none
" or "
transparent
" colors.
|
The option "-linewidth " is really only an alias for
"-strokewidth ".
|
For example, you would probably think that this command would produce a very
thick line. It does, but as the "
-stroke
" color is invisible you can't see it. You only see the
inside 'fill' of the one pixel wide area of the line.
convert -size 100x40 xc:lightblue \
-fill white -strokewidth 3 -draw "line 5,35 95,5" \
line_fill_3.jpg
| |
|
But if the stroke color is also defined, you will get the thick line
requested...
convert -size 100x40 xc:lightblue \
-stroke black -strokewidth 3 -draw "line 5,35 95,5" \
line_stroke_3.jpg
| |
|
If the "
-strokewidth
" setting is set to one, the above line will be
completely covered.
convert -size 100x40 xc:lightblue \
-stroke black -strokewidth 1 -draw "line 5,35 95,5" \
line_stroke_1.jpg
| |
|
Of course when you are armed with this knowledge, you can use it to be
creative, just as you can with font drawing.
convert -size 100x40 xc:lightblue \
-stroke black -strokewidth 5 -draw "line 5,35 95,5" \
-stroke white -strokewidth 2 -draw "line 5,35 95,5" \
line_multi.jpg
| |
|
Here I used the thinnest "
-strokewidth
" setting of '
0
', just as I did for the
fonts above.
convert -size 100x40 xc:lightblue \
-fill white -stroke black -strokewidth 0 -draw "line 5,35 95,5" \
line_stroke_0.jpg
| |
|
This produces the very strange result of a dotted line, consisting of black
dots and grey segments. This is the result of a weird "color beat frequency"
between the stroke, fill and background colors.
Here is an enlarged view of the line...
convert -size 25x10 xc:lightblue \
-fill white -stroke black -strokewidth 0 -draw "line 2,8 22,1" \
-scale 400% line_stroke_0_white.jpg
| |
|
|
The "color beat frequency" effect is not unlike that of a "sound beat"
you get when you have two guitars which are very slightly out of tune. In
this case you get a black dot where the stroke color completely overrides
the underlying fill color and you get a grey dot where the stroke color
mixes with BOTH the fill and the background colors.
The color mixing is a natural consequence of the anti-aliasing processes
which IM uses to try to improve the look of lines and other draw objects.
For more information see my Anti-Aliasing in
IM discussion and examples page.
|
Note that this effect only appears on slanted lines, not pure horizontal or
vertical lines, where aliasing has no effect and thus no "color beat frequency" effects.
convert -size 100x40 xc:lightblue \
-fill white -stroke black -strokewidth 0 -draw "line 5,20 95,20" \
line_stroke_horz.jpg
| |
|
Here I used different underlying fill colors on the enlarged view, so
you can see how the color changes the resulting beat.
convert -size 25x10 xc:lightblue \
-fill none -stroke black -strokewidth 0 -draw "line 2,8 22,1" \
-scale 400% line_stroke_0_none.jpg
| |
|
convert -size 25x10 xc:lightblue \
-fill red -stroke black -strokewidth 0 -draw "line 2,8 22,1" \
-scale 400% line_stroke_0_red.jpg
| |
|
convert -size 25x10 xc:lightblue \
-fill black -stroke black -strokewidth 0 -draw "line 2,8 22,1" \
-scale 400% line_stroke_0_black.jpg
| |
|
Lets compare that to a stroke of none...
convert -size 25x10 xc:lightblue \
-fill black -stroke none -draw "line 2,8 22,1" \
-scale 400% line_stroke_-_black.jpg
| |
|
As you can see, when drawing very thin lines, you can reduce that 'beat' by
either using the same fill and stroke colors, OR setting one of the colors to
none to turn it off. While the later is the best idea, the former may be more
practical for your specific programming needs.
Note the fill line thickness is '0'. But the the stroke line can have a larger
thickness, and is a floating point value! A 1/2 pixel wide line is perfectly
valid.
Drawing Symbols as a List of Points
Sometimes you have a set of points on an image where you want to draw
reference symbols, like crosses, circles, etc... Unfortunately at this time IM
does not have commands to draw such symbols easily, but with a little bit of
extra work you can draw such symbols.
The trick is to generate the MVG drawing commands using a shell script, or
whatever API you are using, so as to transform the given a set of points into
the appropriate drawing commands.
For example here I convert a line of points, into a 'plus' at each of
those points...
# Define a string of X and Y coordinates
# comma between values, space between coordinates.
points="6.6,7.7 25.0,75.0 42.2,85.4 75.8,94.7 51.5,39.3 92.5,66.6"
# convert each point into a draw command for a cross (using 'awk')
# the 'tr' converts spaces into 'newlines' (one point per line).
crosses=`echo $points | tr -s ' ' '\012' |\
awk -F, '{ print "line " $1-3 "," $2 " " $1+3 "," $2 ;
print "line " $1 "," $2-3 " " $1 "," $2+3 ; }' -`
# draw a red line between the points, and blue crosses on the points.
convert -size 100x100 xc:white \
-draw "fill none stroke red polyline $points " \
-draw "fill none stroke blue $crosses " \
points_plus.gif
| |
|
The above uses "
tr
" to separate each point into one point per line,
then uses "
awk
" to do all the mathematical calculations needed to
draw the 'plus' over the given point. As you can see it can become quite
complex.
However you may not be able to rely on '
awk
' or
'
perl
' or even '
bc
' to do floating point
calculations needed to draw a 'symbol'. On the other hand you know that the
"
convert
" program is available, so you can use it to do the
required floating point calculations. For example here I use it to calculate
a point on the circumference of the circle, for this 'point symbol'.
# Define a string of X and Y coordinates
# comma between values, space between coordinates.
points="6.6,7.7 25.0,75.0 42.2,85.4 75.8,94.7 51.5,39.3 92.5,66.6"
# circle radius (or symbol size) to draw around each point.
radius=3.5
# convert each point into a draw command for a cross
# In this case, points are space separated by the shell
circles=$(for point in $points; do
x=$(echo "$point" | cut -d, -f1)
y=$(echo "$point" | cut -d, -f2)
# use IM to do some floating point math, EG: y2=$y+$radius
y2=$(convert xc: -format '%[fx:'"$y"'+'"$radius"']' info:)
echo "circle $x,$y $x,$y2"
done)
# Draw a red line between the points, and blue circles on the points.
convert -size 100x100 xc:white \
-draw "fill none stroke red polyline $points " \
-draw "fill none stroke blue $circles " \
points_circle.gif
| |
|
Now the draw strings you generate can get fairly long, and could start
to cause problems. So rather than converting the points into long strings
which we then pass to IM on the command line, you can pipe draw commands
instead.
I also this time use a
SVG Path instead of a
Draw Primitive, this time generating a triangle around
each point.
# Define a string of X and Y coordinates
# comma between values, space between coordinates.
points="6.6,7.7 25.0,75.0 42.2,85.4 75.8,94.7 51.5,39.3 92.5,66.6"
# convert each point into a draw commands to draw a triangle
for point in $points; do
echo "path 'M $point m 0,-5 -4,+8 +8,0 -4,-8'"
done |\
convert -size 100x100 xc:white \
-fill none -stroke red -draw "path 'M $points' " \
-fill none -stroke blue -draw '@-' \
points_tri.gif
| |
|
The
SVG Path actually makes this easier, by allowing
relative pixel moves, allowing you to design the symbol so it only requires a
single initial absolute move '
M
' before giving the sequence to
draw the symbol. Because of this you actually do not need any floating point
calculations at all.
|
The relative move SVG Path item 'm ' is
broken before IM v6.4.3-5. If your IM is older than this, the above (and
next) examples may not draw anything. You can fix this for older versions
by replacing the relative moves 'm ' in the above with an
appropriate sequence of relative lines, 'l '.
|
Now you can take this even one step further, feed a fully formed MVG file,
complete with draw canvas specification, directly into IM as a pipeline of
drawing commands. This time lets do a 'cross' which is similar to the
first 'plus' example above which needed a lot of calculations.
# Define a string of X and Y coordinates
# comma between values, space between coordinates.
points="6.6,7.7 25.0,75.0 42.2,85.4 75.8,94.7 51.5,39.3 92.5,66.6"
# Generate a MVG file for IM to draw all components
( echo "viewbox 0 0 100 100 fill white rectangle 0,0 100 100"
echo "fill none stroke red path 'M $points'"
echo "fill none stroke blue path '"
for point in $points; do
echo " M $point m -2,-2 +4,+4 m -4,0 +4,-4"
done
echo "'"
) | convert mvg:- points_cross.gif
| |
|
This uses the special shell programming technique where anything and everything
that is 'echoed' within the shell parenthesis will be fed into the final
"
convert
" command as a MVG file. The first 'echo' defines and
fills the drawing canvas for the image, while the 'while' loop converts each
'point' giving into a circle of the given radius.
The advantage of this method is that you don't get any string limitation that
you may get using the other two methods.
Other symbols that you could generate include boxes, diamonds, error-bars,
etc...
Also see '
Drawing Circles below, for other circle
methods, including a no-calculate relative 'path' circle draw.
It would be nice if the MVG could be expanded to allow you to more easily 'draw' simple
symbols at a list of coordinates.
Drawing Circles
The draw options provide you with a number of ways to do something very
basic... Drawing Circles.
You can, for example, draw a circle though
any point on its circumference. Thus you will need to calculate a center point
and a second point that is the radius (say 25 pixels) away from the first point.
convert -size 100x60 xc: -stroke Firebrick -fill tomato -strokewidth 2 \
-draw 'circle 50,30 50,55' circle_circle.gif
| |
|
But you can also specify an ellipse with a fixed radius set of arguments.
convert -size 100x60 xc: -stroke SeaGreen -fill PaleGreen -strokewidth 2 \
-draw 'ellipse 50,30 25,25 0,360' circle_ellipse.gif
| |
|
You can also generate a circle by drawing a very very short line with
'
stroke-linecap round
'. The stroke width then sets the circles
diameter. NOTE the line must have some length (no matter how small) or draw
will draw nothing.
convert -size 100x60 xc: -stroke Blue -strokewidth 50 \
-draw 'stroke-linecap round line 50,30 50,30.0001' \
circle_line.gif
| |
|
This technique, unfortunately can not outline the generated circle, but for
covering large areas, large stroke widths can be useful. See
Some simple examples below.
The last method makes use of the
SVG Path drawing method
so that the circle can be drawn without needing to calculate any extra
coordinates.
convert -size 100x60 xc: -stroke Navy -fill DodgerBlue -strokewidth 2 \
-draw "path 'M 50,30 m 0,25 a 1,1 0 0,0 0,-50 a 1,1 0 1,0 0,50'" \
circle_path.gif
| |
|
Only the initial absolute move '
M
' is needed to define the
center, the '
25
' and '
50
' in the rest of the path
components that follow define the circle radius and diameter relative to this
center.
|
The relative move SVG Path item 'm ' is
broken before IM v6.4.3-5. If your IM is older than this, the circle may
only appear as a single pixel. You can fix this for older versions by
replacing the 'm ' in the above with a 'l '.
|
Fred Weinhaus added the
following bezier circle method. It is very close to a real circle (though not
exact), and requires a floating point calculation.
r=25; cx=50; cy=30;
x1=25; x2=75; # = cx ± radius
y1=-3.25; y2=63.25; # = cy ± radius*1.275
convert -size 100x60 xc: -stroke Purple -fill Violet -strokewidth 2 \
-draw "bezier $x1,$cy $x1,$y1 $x2,$y1 $x2,$cy" \
-draw "bezier $x1,$cy $x1,$y2 $x2,$y2 $x2,$cy" \
circle_bezier.gif
| |
|
If drawing an exact circle is not important, you can use this 4 Bezier segment
SVG path, that only uses the X and Y bounds of the circle for its calculation.
r=25; cx=50; cy=30;
x1=25; x2=75; # X bounds = cx ± radius
y1=5; y2=55; # Y bounds = cy ± radius
convert -size 100x60 xc: -stroke Tomato -fill Gold -strokewidth 2 \
-draw "path 'M $cx,$y1 Q $x1,$y1 $x1,$cy T $cx,$y2 $x2,$cy $cx,$y1 z'" \
circle_bezier_path.gif
| |
|
If you like one that is drawn completely relative to a center starting point,
you can use this technique. Only the radius value is used, making it simple to
generate, using only string functions in an API.
convert -size 100x60 xc: -stroke Orange -fill LemonChiffon -strokewidth 2 \
-draw "path 'M 50,30 m 0,25 q 25,0 25,-25 t -25,-25 -25,25 25,25 z'"\
circle_bezier_path_rel.gif
| |
|
Can you think of other ways to draw circles.
Drawing Special Characters in the Text String
To Quote or Backslash?
One of the biggest problem people have with -draw is the drawing of
characters that also have special significance to UNIX shells and the DOS
command line or even other languages like C, Perl, PHP, R, or Visual Basic.
The biggest culprit in this regard are the two types of quoting characters, as
well as variable substitutions characters like dollars '
$
' and
the shell and ImageMagick escape character, backslash '
\
'.
Basically as the MVG argument to "
-draw
" needs to be quoted,
and the '
text
'
string argument within also may need some extra quoting as well.
To solve this, users typically use two different quote characters, one for the
shell and a different one for the MVG text string.
-draw '... text 0,0 "string" ...'
or they would swap the quotes, and use...
-draw "... text 0,0 'string' ..."
This solves most problems, but some characters still present difficulties, and
the solutions depend on exactly what set of quotes you use.
Here are the four cases of quoting...
- Using Single Quotes for the shell argument,
with Double Quotes around the MVG text string.
The simplest technique for handling draw text strings is to use a single
quote for the wrapping shell argument. This however means that to include
an apostrophe in the drawn string you need to leave the shells 'single
quote mode' and supply that apostrophe outside the shells single quotes.
For example here is how to handle the four special characters I talked
about.
convert -size 250x50 xc:none -box white -pointsize 20 -gravity center \
-draw 'text 0,0 " '\'' \" $ \\ " ' \
-trim +repage text_special_sd.gif
| |
|
Note that as the dollar sign is not needing escaping, you also can not use
it to substitute the contents of a shell variable.
It is important to remember that the backslash is the only special
character that the IM draw string handles. Also its reason for existence
is purely to allow you to escape any 'IM draw string quotes', such as we
used above for the double quotes. Beyond this, all the other weirdness is
caused by the UNIX command line shell and not IM.
PS-DOS has its own weirdness, and I would appreciate input on escaping
special characters when using IM from environment.
- Using Double Quotes for the shell argument,
with Single Quotes around the MVG text string.
If you do want want to insert a 'shell variable' into the drawn string,
then you will have to use double quotes for the outside shell argument.
This makes the whole matter much more complex, as you lose the protection
of the shell and you now have to not only escape the dollar '$
'
signs, but also backslashes '\
' as well.
On the other hand, the shell will not then need to use single quote characters as
its end-of-argument de-limiting character, so that aspect is simplified.
Lets summarize the results for our short list of special characters.
convert -size 250x50 xc:none -box white -pointsize 20 -gravity center \
-draw "text 0,0 ' \\' \" \$ \\\\ ' " \
-trim +repage text_special_ds.gif
| |
|
Notice that if you want to draw a backslash itself, the MVG text string
needs the backslash to be doubled (as in the previous example), but the
shell itself also needs each of those backslashes doubled, producing a
total of four backslashes just to produce one such character.
This doubling can very quickly become overwhelming and confusing, requiring
lots of backslashes to achieve what you want. Just take is slow and easy,
and you will figure it out for your situation.
- Using Single Quotes for the shell argument,
with Single Quotes around the MVG text string.
Lets finish this off with a summary of the final two quoting combinations.
I'll leave you to figure out how they are decoded by the shell and MVG.
convert -size 250x50 xc:none -box white -pointsize 20 -gravity center \
-draw 'text 0,0 '\'' \'\'' " $ \\ '\'' ' \
-trim +repage text_special_ss.gif
| |
|
- Using Double Quotes for the shell argument,
with Double Quotes around the MVG text string.
convert -size 250x50 xc:none -box white -pointsize 20 -gravity center \
-draw "text 0,0 \" ' \\\" \$ \\\\ \"" \
-trim +repage text_special_dd.gif
| |
|
As you can see "
-draw
"
arguments from the command line have to deal both with the command line shell
as well as backslash and quote escaping within the MVG text string. The
results can be confusing and tricky. Just remember that the shell treats the two
types of quotes differently, while the MVG text string does not.
Of course in complex scripts, the better way may be to avoid the shell and any
scripting problems entirely. You can do this by reading the "
-draw
" arguments from a MVG draw
file.
-draw @drawfile.mvg
Of course you will still need to backslash whatever quote character you decide
to use around your draw text string, if you want to include that character as
well as backslash any backslashes within the text, but it is a lot simpler
than trying to dealing with a shell's own quote and backslash interference at
the same time.
convert -size 500x50 xc:lightblue -font Candice -pointsize 36 \
-gravity center -draw @text_quotes.mvg text_quotes.gif
|
|
|
|
The first image is from one of the "
MVG
" text files I used. It
contains no shell escapes or quoting. As such only the MVG quoting and
escapes are present.
Note that in the above if I had used single quotes for the MVG text string,
the only change is that I would need to backslash the single quote characters
rather than the double quote characters in the string.
About Percent Characters
Just one final point about special 'escape' characters in the "-draw text
" operator. Percent
characters '%
' should draw 'as is'. You should not need to
do anything special to draw them. If they don't draw 'as is', then you have an old
version of IM and should upgrade ASAP.
|
Up until IM version 6.2.4, the '% ' character was used as an
escape character to include extra image information in the drawn text
string. This is no longer the case as such escapes were confusing and
incorrect when SVG images also tried to draw percent characters.
|
This use of percent 'escapes' (as well as '
\n
' newline escapes)
was deemed incompatible with the "
-draw
" operator, and MVG format's intended use for handling SVG
image formats. As such from IM version 6.2.4 onward, % escapes do not work,
and backslashes only escape itself and the surround quotes.
convert -size 250x50 xc:none -box white -pointsize 20 -gravity center \
-draw 'text 0,0 "%w\n%h"' -trim +repage text_escapes.gif
| |
|
For more details of the 'percent bug', and ways to avoid it when using
"
-draw
" in older
ImageMagick's, see the
Drawing a Percent
Bug page.
Annotate instead of Draw
The better way of avoiding these types of problems is to use "-annotate
" rather than draw for
text drawing. This operator is a wrapper around the draw operator and allows
the use of all the capabilities of draw, but in a simpler form.
Basically this operator only need one set of quoting (for the shell). This
makes dealing with special characters a much much simpler.
Unfortunately while you no longer need to escape quotes for IM, you now have
to deal with '@
' file reads, newline escapes
'\n
', and the percent information includes.
For example, using single quotes...
convert -size 200x50 xc:none -box white -pointsize 20 -gravity center \
-annotate 0 '\@ '\'' " $ \\ %% ' \
-trim +repage annotate_s.gif
| |
|
and for double quotes...
convert -size 200x50 xc:none -box white -pointsize 20 -gravity center \
-annotate 0 "\@ ' \" \$ \\\\ %% " \
-trim +repage annotate_d.gif
| |
|
However all annotation quotes and escapes are completely ignored if you use
the at '
@
' escape to read the string from a file.
For example here we include information on an images width and height!
convert -size 200x50 xc:none -box white -pointsize 20 -gravity center \
-annotate 0 '%w\n%h' -trim +repage annotate_percents.gif
| |
|
But here the escapes are ignored when read from a file.
echo -n '@ %w\n%h' |\
convert -size 200x50 xc:none -box white -pointsize 20 -gravity center \
-annotate 0 '@-' -trim +repage annotate_file.gif
| |
|
For more information see
Annotate Text Drawing
Operator, and especially
Annotate Escape
Characters.
MVG Alpha Composition
Under Construction
I have not seen any use of Alpha composition (other than 'painters' algorithm
which is basically a 'over' alpha composition) for the drawing of objects.
However that is not to say it can not be done.
If you like to compose your rectangle, ellipse, circle, or whatever with a
different alpha composition (such as 'DstOver' which is an Under-like
composition), then draw your figure on a blank transparent canvas the same
size as the original and compose it onto your image.
However as SVG allows you to use alpha composition to draw text and other
items onto images, I would imagine that it will be a future addition.
Stay Tuned!
IM and SVG handling
Handling the actual SVG image format is a very complex business. The engine
needed to handle all its aspects, as defined by the
SVG -- Scalable Vector Graphics document
requires a lot of effort, and time.
Therefore, ImageMagick provides two methods in the handling of SVG format
images. The first is to use a open source RSVG library, to convert the SVG
format into a raster image that IM has no problems handling.
The second method is for IM to try to convert SVG into MVG, so that it can
draw the image itself. MVG was (and is) being designed for this purpose.
The method is known as the internal
MSVG conversion.
You can force the use of the internal MSVG converter by reading the SVG image
using using the special input format "
MSVG:
" (added IM v6.3.4).
However the only way to add RSVG library support is to compile IM with the
'development' support for the RSVG library installed on your machine.
To find out what your IM will do use...
convert -list format | grep SVG
|
As you can see by the "
RSVG
" in parenthesis, my own IM will use
the RSVG library that was provided by the system on my personal workstation.
IM is using this library because it was present when I last compiled IM, which
is almost daily. See the IM version and compile date used for these examples,
at the bottom of the page.
For example "diagonal.svg" is a very small
SVG image (from user penciledin), which creates a rectangle with a simple diagonal
gradient.
convert diagonal.svg diagonal_rsvg.gif
| |
|
Perfect. A proper diagonal gradient is produced.
However if you render this using the internal MSVG (the default if the RSVG
was not present when when IM was compiled)...
convert msvg:diagonal.svg diagonal_msvg.gif
| |
|
As you can see the internal MSVG conversion fails, returning a vertical
gradient rather than a diagonal one. This is a known problem and is being
fixed as time allows.
You can also see the actual MVG commands IM generates by inspecting the "
diagonal.svg (txt)" source.
convert msvg:diagonal.svg diagonal.mvg
|
Things the current internal MSVG is known to fail with include...
- Non-vertical Gradients
- Text along a curved path
- Text justification (as separate to gravity)
However most basically drawing actions are handled.
Remember the MVG language can actually handle things that SVG can not,
including the use of gravity for positioning image and text. Gravity is not
part of the the SVG specification, though it is an integral part of IM text
and font handling.
Also remember that MVG does not have the same container mechanism that SVG has.
The internal MSVG converter replaces the XML containers with the pushing and
popping of graphic contexts (see the MVG output above), which has the same
effect.
SVG settings
As the SVG image format is a vector format (See
A word about Vector Image formats) the image does not have a default
'size'. Instead it is 'drawn' or 'rendered' at a particular "
-density
" just like postscript
(default density is 72 dpi).
Also if the SVG does not 'paint' the background, you can specify the
background color to use by using the "
-background
" setting.
For example here is another SVG image which has been 'rendered' using 3
different densities, and to 3 different backgrounds, including a
transparent background.
convert -density 36 home.svg home_1.gif
convert -background skyblue home.svg home_2.gif
convert -density 144 -background none home.svg home_3.png
|
Note that I used a PNG format image for the larger transparent background
version of the above example. This produces a cleaner image than a GIF image
format would produce due to semi-transparent edge pixels. PNG is always
recommended when transparency is involved in the final image.
|
I have found that some SVG images will not scale. That is they are
defining there internal image components in terms of pixels, rather than
real-world lengths such as 'points', 'inches' or 'millimeters'. As a
consequence while the "-density " setting may change the overall image size of the
result, the image itself will not change size to match. Such SVG images
are however quite rare.
|
SVG Output Handling
As of IM v6.4.2, IM can convert ANY bitmap image into an SVG vector graphic!
The conversion is not always successful, but larger and/or simpler images
(like a bitmap mask) will convert very well.
For example here I convert a horrible bitmap shape into a SVG image, then
convert it back again, so as to smooth the bitmap into a proper anti-aliased
shape.
convert -pointsize 72 -font Candice label:A -threshold 50% \
-trim +repage -bordercolor white -border 5x5 A.gif
convert A.gif A.svg
convert A.svg A.png
|
For this to work however the 'development' "
AutoTrace
" library must
be installed, and IM configured with a "
--with-autotrace
" switch.
There is also a "
autotrace:
' input delegate command to do exactly
what the last example did when reading in a bitmap image, so as to smooth the
resulting image in memory. This does not require the delegate library, only
the "
autotrace
" command to be present on the system.
convert autotrace:A.gif A_traced.png
| |
|
Of course this will NOT get you the SVG output from the
"
autotrace
" command, just filter the input image through SVG to
smooth it.
See
Raster to Vector Edging for a
example of using the SVG generated from "
autotrace
", as a image
processing technique.
Non-IM Vector Graphic Editors
ImageMagick is a pixel array processor, It will generally not save vector
images ('MVG' is the only exception to this), only read them and convert them
to pixel arrays.
The same is true of other pixel image editors, such as Gimp, Photoshop,
and so on.
For editing and handling vector based images use programs such as
Sodipodi -- SVG based Vector Graphics Editor
Xfig -- Simple, but very good, Vector Object Editor
(Great for signs, maps, and arranging photos on a page)
Dia
AutoTrace convert a shape in a bitmap array to vector outlines
Sketch python based vector editor with curved text.
This is of course not the be-all-end-all as Word for Windows, OpenOffice
and other word processors also have various simple, but difficult to
use, object editors.
For converting a vector graphic format to a different vector format, do
not use ImageMagick. ImageMagick is, and always will be, essentially
a raster image or bitmap graphics converter and manipulator.
For converting from vector to vector take a look at these programs...
pstoedit Translate PS and PDF to other vector formats (like SVG)
Convertps commercial version of a PS to a vector language convertor
ps2svg PostScript to SVG - based on GhostScript
Some simple examples
A background for a sign.
convert -size 100x100 xc: -fill none -stroke powderblue \
-draw 'stroke-width 70 ellipse -30,0 90,90 10,50' \
-rotate 180 arc_background.gif
| |
|
Start of a Clown face. Can you complete it?
convert -size 100x100 xc: \
-draw 'fill none stroke-linecap round
stroke-width 40 stroke tomato ellipse 50,0 70,70 65,115
stroke-width 2 stroke black ellipse 50,0 70,70 60,120
stroke-width 40 stroke palegreen line 50,40 50,41' clown.gif
| |
|