[cairo] downscaling capabilities

Bill Spitzak spitzak at thefoundry.co.uk
Fri Apr 18 14:46:10 PDT 2008

I'm not sure about calling this "prefiltering". I think it is generally 
done in a single step and that step replaces the current bilinear 
filtering. In a general sense:

For a given output pixel, the center point of that pixel (X+.5, Y+.5) is 
back-transformed to the input image location, and then .5 is subtracted 
from each to get the source x,y.

Also the derivatives of this back-transformation are used to figure out 
two vectors (these vectors when drawn from the x,y go to the 
back-transform of X+1,Y and X,Y+1)..

Then "some algorithim" is used to assign weights to the pixels near the 
center point based on the fractional value of the center point and the 
derivative vectors. This weighted sum is then returned as the value for 
the output pixel.

The "some algorithim" is the variable part:

The current bilinear algorithm appears to be: ignore the derivatives, 
and use the fractional part of the x/y to weigh the four pixels at 
floor() and ceil() of the x/y.

A very popular algorithim is box filtering, which has the primary 
advantage that it is identical to bilinear if the scale is 1, and only 
three weights for each direction need to be calculated:

The vectors are converted to an axis-aligned rectangle with the same 
area as the parallogram they define, and as much as possible the same 
shape. This is then centered on the x/y. The weight for a pixel is the 
area of it that intersects this rectangle, divided by the area of the 

Any real implementation does not literally do the above, but is 
optimized by doing a "two pass" algorithim: you apply the horizontal 
weight first to a bunch of rows, then apply the vertical weight only 
once to all those sums. Also any real implementation takes advantage of 
constant derivatives or fractional portions of x/y from one pixel to 

It is extremely common to enlarge the rectangle to be at least 1 in each 
direction. This makes any scaling up identical to bilinear filtering. 
OS/X Quartz seems to *not* do this, however, and lots of people like 
that, it will produce large antialiased squares for the pixels rather 
than blurriness, and that appears to be much less objectionable. It's 
slower than bilinear so you may only want to do this if the scale is 
large enough that it looks better.

You will also see "filter functions" mentioned a lot. These use some 
method to convert the distance from the center of a pixel to the x,y 
position to a number, and that number is then passed through this 
function to get the weight. You will see truncated sync, gaussian, 
Mitchell, and all kinds of other ones, there are books full of these. 
Often for speed the source is reduced to a rectangle and the x/y are 
used independently in the filter formula and the two results multiplied 
to get the weight. For many filters this is quite close to the same 
result, and allows a much faster two-pass algorithim described above.

The other thing you are going to see is mip-mapping. This means you 
precalculate powers-of-two scales of the image (1/2, 1/4, etc). You then 
can bilinearly filter the one that is just below the size you want. The 
problem with this is that the precalculation takes time and memory and 
must be redone whenever the source changes, there are artifacts when the 
horizontal & vertical scale are different and when they pass thorough 
the powers of two, and that the padding must be pre-decided if the 
source is not a power of 2 in size. My belief is that mipmapping is a 
poor fit to Cairo and pixman's usage. However it is possible that 
mipmaping is what you are thinking of when you said "prefiltering".

More information about the cairo mailing list