GD and ImageMagick when you need them#
Imager is the default, but two other raster libraries are worth knowing. GD is older, smaller, and everywhere: a great deal of existing Perl draws with it, and its color-index model is worth understanding even if you never choose it new. ImageMagick, through Image::Magick, is the heavyweight: the widest format support and the deepest bag of transformations, at the cost of a large dependency. This chapter shows each, and says when it beats Imager.
GD#
GD wraps libgd. Its defining quirk is the color index: on a classic (palette) image you allocate each color once and then draw with the integer index it returns, not with the color itself. This is a relic of 256-color GIF images, and it still shows in the API even though truecolor images are now the norm.
use GD;
# the third argument requests a truecolor image; omit it for palette
my $im = GD::Image->new(200, 200, 1);
# even on truecolor, colors come from colorAllocate
my $white = $im->colorAllocate(255, 255, 255);
my $blue = $im->colorAllocate(0, 0, 255);
my $red = $im->colorAllocate(255, 0, 0);
$im->filledRectangle(0, 0, 199, 199, $white);
$im->line(0, 0, 199, 199, $blue);
The primitives are positional, and two of them differ from what you might expect. arc and ellipse take a center plus width and height (the full extents, not a radius), and angles in degrees:
# center (100,100), 80 wide, 80 tall, full circle
$im->arc(100, 100, 80, 80, 0, 360, $blue);
$im->filledArc(100, 100, 120, 60, 0, 180, $red); # a filled half-ellipse
$im->rectangle(10, 10, 60, 60, $blue);
$im->filledRectangle(140, 10, 190, 60, $red);
$im->setPixel(100, 100, $blue);
$im->fill(30, 30, $red); # flood fill
Polygons are drawn from a GD::Polygon object, not raw coordinates:
my $poly = GD::Polygon->new;
$poly->addPt(50, 10);
$poly->addPt(150, 10);
$poly->addPt(100, 90);
$im->polygon($poly, $blue);
$im->filledPolygon($poly, $red);
Output is a method per format that returns the encoded bytes, so you print them to a filehandle yourself:
open my $fh, '>', 'out.png' or die $!;
binmode $fh;
print $fh $im->png; # or ->jpeg($quality), ->gif
close $fh;
When GD wins: you are maintaining code that already uses it (a large amount of older Perl does), you want the smallest dependency that still draws, or you specifically need libgd behavior to match another tool in a pipeline that also uses libgd. For new drawing work with no such constraint, Imager’s named-color and hash-argument API is friendlier and its filter set is richer.
ImageMagick (Image::Magick)#
Image::Magick, also called PerlMagick, ships with ImageMagick itself and exposes its full engine. Its strengths are format breadth (it reads and writes formats nothing else on CPAN touches) and transformations (hundreds of operations from Resize and Blur through Distort, Morphology, and format-specific tricks). Its weakness is the dependency: you need a matching ImageMagick library installed, and it is large.
The API is verb-oriented: you create an object, Read or size it, then call named operations, each returning an error string that is empty on success.
use Image::Magick;
my $im = Image::Magick->new;
$im->Set(size => '200x200');
$im->ReadImage('canvas:white'); # a blank white canvas
# drawing is one Draw call per primitive, SVG-path-like
$im->Draw(primitive => 'line', points => '0,0 199,199',
stroke => 'blue');
$im->Draw(primitive => 'circle', points => '100,100 100,40',
stroke => 'red', fill => 'none');
$im->Draw(primitive => 'polygon', points => '50,10 150,10 100,90',
fill => 'skyblue');
# transformations are named methods
$im->Blur(radius => 0, sigma => 1.5);
$im->Resize(geometry => '100x100');
my $err = $im->Write('out.png');
warn "$err" if $err;
The circle primitive takes the center followed by any point on the perimeter (so 100,100 100,40 is center (100,100) with radius 60), which is ImageMagick’s convention rather than a radius. Every geometry argument is ImageMagick’s string mini-language ('200x200', '100x100+10+10'), so it is worth a glance at ImageMagick’s own geometry documentation for the details.
When ImageMagick wins: you need a format nothing else reads, you are doing heavy batch transformation (thumbnailing thousands of files, format conversion pipelines), or you want one of its many operations that would be a project to reimplement. For interactive drawing or a tight dependency footprint, it is more machine than you need, and Imager or GD fit better.
Choosing#
You want … | Reach for |
|---|---|
New raster drawing, friendly API, good filters |
|
Smallest dependency, or an existing GD codebase |
|
Widest formats, heavy batch transforms |
|
All three are raster and share the raster ceiling: fixed resolution, degradation on upscaling. When you need output that scales cleanly, the next chapter moves to vector graphics with Cairo and SVG.