Skip to content

Calculate accurate weighted average in YdownXdown (aka Smooth Bresenham)

When iterating through the destination image while scaling down, we know the fractional source area that should in theory cover a destination pixel. In practice a pixel has size 1 and the averaging box and number of source pixels must be integral. We can correct the resulting error by assigning weights into the averaging box.

The former implementation weighted "only a bit" with the d0 / d1 variables, but it didn't take x fractions into account and also missed to pad up the weight to 1 for consecutive y rows. That seems to be the main reason why the outcome of the former YdownXdown looked "coarse" and "jagged".

This commit implements the wighted average almost exactly as Tim Kientzle suggested it in "Scaling Bitmaps with Bresenham" (C/C++ User's Journal, October 1995), just extended for 2 dimensions and adapted to the exiting Splash.cc code.

This can be used in addition to !626, or as sole alternative, and shall help fixing #950.

I'm not going to touch other {X,Y}down variants in this MR. If requested it can be done in another MR, but I'd spend time rather on a long term replacement for the scaling code.

Visual inspection: Embedded images from #950

From left to right: Splash Bresenham, Splash Smooth Bresenham (proposed change), Cairo.

Scale 0.8: splash-bresen_smoothbresen_cairo_0.8

Scale 0.6: splash-bresen_smoothbresen_cairo_0.6

Scale 0.4: splash-bresen_smoothbresen_cairo_0.4

Performance

We need some additional integer multiplications per source pixel to calculate the weighted average. This shows up as moderate performance penalty in my test:

cputime_bresen_smoothbresen

Edited by Tobias Deiminger

Merge request reports