LibGD is the current contender. It isn't perfect for our needs but it's pretty good. It's also simple and well-written in plain C, so it's a lot more amenable to hacking than something like VIPS.
I have a fork of LibGD here.
Pros:
- Designed for the server
- Easily callable from scripting languages
- Simple, clean C code
- Has a large, established user base
Cons:
- Documentation is out of date
- Resizing is a bit of a mess
GD has three different resizing "systems": classic, fixed-point and floating-point.
The latter two are accessible via the function 'gdImageScale()'. This
resizes images according to a mode, which is set via the function
gdImageSetInterpolationMethod()
.
The classic resizers are gdImageCopyResized()
and gdImageCopyResampled()
.
gdImageCopyResized()
is a simple non-interpolating resize (i.e. it
just copyies the closest pixel when shrinking and enlarges the pixels
when enlarging.) It is extremely fast but produces poor output.
gdImageCopyResampled()
does some kind of averaging but I don't know
which algorithm it uses. It is pretty fast with so-so image quality.
The fixed point interpolations come from this Code Project
class. They are accessed via gdImageScale()
by setting the mode to one of:
GD_NEAREST_NEIGHBOUR
GD_BILINEAR_FIXED
GD_BICUBIC_FIXED
(Nearest Neighbour isn't technically a fixed-point algorithm, but it comes from the same source so I'm lumping it in with them.)
These are (IMHO) not very good. They use fixed-point arithmetic to avoid the overhead of floating-point math but are inefficient in other ways. They recompute the weights for each pixel rather than computing them once for each axis and reusing them. They also resize the entire image in one pass rather than doing horizontal and vertical separately, (probably) resulting in poor cache performance.
In addition, the code is hellishly complex.
These come from the Graphics Gems III example
code. They
are accessed via gdImageScale()
and the remaining modes.
All of these work the same way: they generate an array of weights
which are used to average the pixel values. The weights themselves
are generated by a function which depends on the mode. The code is
simple and generally efficient and I was able to get GD_BICUBIC
to
run significantly faster than GD_BICUBIC_FIXED
with this bug
fix.