I've managed to translate the alternative computation of weights into Graphics32. It was actually quite easy :). The idea is, to compute the intergral for the convolution with the filter via the midpoint-rule. Before I've used the exact antiderivatives, leading to constant underestimations of peaks and valleys in the bitmap function, and thus to a loss of detail. Now pixels not lying totally within the support of the filter get their weight reduced, leading to less artefacts, but the peaks are better estimated, so contrast and detail is better preserved (the math is for readability):
//Precision of weights,
//Totals Cb,Cg,Cr,Ca in Resample need to be unscaled by Prec * Prec
const Prec = $800;
function BuildMappingTableNew(DstLo, DstHi: Integer; ClipLo, ClipHi: Integer;
SrcLo, SrcHi: Integer; Kernel: TCustomKernel): TMappingTable;
var
...
begin
...
else if Scale < 1 then
begin
OldScale := Scale;
Scale := 1 / Scale;
FilterWidth := FilterWidth * Scale;
for I := 0 to ClipW - 1 do
begin
if FullEdge then
Center := SrcLo - 0.5 + (I - DstLo + ClipLo + 0.5) * Scale
else
Center := SrcLo + (I - DstLo + ClipLo) * Scale;
Left := Floor(Center - FilterWidth);
Right := Ceil(Center + FilterWidth);
Count := -Prec;
for J := Left to Right do
begin
//changed part
x0 := J - Center; // old weight: Filter(x0*Oldscale)*Oldscale
x1 := max(x0 - 0.5, -FilterWidth);
x2 := min(x0 + 0.5, FilterWidth); // intersect symmetric interval of length 1 about x0 with support of scaled filter
x3 := 0.5 * (x2 + x1); // new center
Weight := Round(Prec * Filter(x3 * OldScale) * OldScale * (x2 - x1)); // intersection with support entered into the weight
if Weight <> 0 then
begin
Inc(Count, Weight);
K := Length(Result[I]);
SetLength(Result[I], K + 1);
Result[I][K].Pos := Constrain(J, SrcLo, SrcHi - 1);
Result[I][K].Weight := Weight;
end;
end;
...
At first the results were getting too dark and contrast was increased. By increasing the accuracy of the weights and using my own way of rounding the averaged result into bytes, this seems no longer the case:
If RangeCheck then
begin
C.B := min((max(Cb, 0) + $1FFFFF) shr 22, 255); //unscale and round
C.G := min((max(Cg, 0) + $1FFFFF) shr 22, 255);
C.R := min((max(Cr, 0) + $1FFFFF) shr 22, 255);
C.A := min((max(Ca, 0) + $1FFFFF) shr 22, 255);
end
else
begin
C.B := (Cb + $1FFFFF) shr 22;
C.G := (Cg + $1FFFFF) shr 22;
C.R := (Cr + $1FFFFF) shr 22;
C.A := (Ca + $1FFFFF) shr 22;
end;
// Combine it with the background
case CombineOp of
dmOpaque:
DstLine[I] := C.ARGB;
...
The changed file uScalingProcsGR32.pas is attached.
If you are interested in a test, here is a short video, zooms and pans have been done with the new Lanczos. The second picture is one of the most notorious in my collection.
uScalingProcsGR32.zip