Hi

IA is in general not Open source, but I don't mind giving this specific part away.

Basically the lossy compression is done by a filtering of the image fore the actual compression, where the filtering modifies the pixels to be better suited for the specific compression scheme used in PNG. The filtering algorithm looks like this:

```
function OptimizeForPNG(Image: TLinearBitmap; QuantizationSteps: Integer; TransparentColor: TColor=-1): Integer;
var
X, Y, P, I : Integer;
Line, LastLine : PByteArray;
BPP, HalfStep : Integer;
NewPal : TPalette;
ColorMap : TColorMap;
XModBPP, NewValue : Integer;
begin
if Image.PixelFormat=pf8bit then
begin
if not Image.IsGrayScale then // Palette image
begin
// Create new palette
SortPalette(Image.Palette^,ColorMap);
if InRange(TransparentColor,0,255) then
begin
I:=TransparentColor;
for X:=0 to 255 do if ColorMap.Map[X]=TransparentColor then
begin
I:=X;
Break;
end;
ColorMap.Map[i]:=ColorMap.Map[TransparentColor];
ColorMap.Map[TransparentColor]:=TransparentColor;
end;
// Apply palette changes
for I:=0 to 255 do NewPal[ColorMap.Map[i]]:=Image.Palette^[i];
ColorMap.Apply(Image);
Image.Palette^:=NewPal;
// Optimize for Paeth filtering
LastLine:=Image.Map;
for Y:=1 to Image.Height-1 do
begin
Line:=Image.ScanLine[Y];
for X:=1 to Image.BytesPerLine-1 do
begin
if (Line^[X]=TransparentColor) and (TransparentColor<>-1) then Continue; // Transparent
P:=PaethPredictor(Line^[X-1],LastLine^[X],LastLine^[X-1]); // Paeth filter
if (ColorDiff(NewPal[P],NewPal[Line^[X]])<QuantizationSteps) and (P<>TransparentColor) then Line^[X]:=P;
end;
LastLine:=Line;
end;
Result:=slfPaeth;
Exit;
end;
BPP:=1;
end
else BPP:=3;
// 24 bit or grayscale, optimize for average filter
HalfStep:=(QuantizationSteps+1) div 2;
LastLine:=Image.Map;
XModBPP:=0;
for Y:=1 to Image.Height-1 do
begin
Line:=Image.ScanLine[Y];
for X:=BPP to Image.BytesPerLine-1 do
begin
if TransparentColor<>-1 then // Dont't change transparent pixels
begin
if BPP=1 then
begin
if Line^[X]=TransparentColor then Continue;
end
else
begin
XModBPP:=X mod 3;
if PInteger(@Line^[X-XModBPP])^ and $ffffff=TransparentColor then Continue;
end;
end;
P:=(Word(Line^[X-BPP])+LastLine^[X]) div 2; // Average filter
if Abs(P-Line^[X])<=QuantizationSteps then NewValue:=P
else
begin
I:=Byte(Line^[X]-P) mod QuantizationSteps;
if I<HalfStep then NewValue:=Max(Line^[X]-I,0)
else NewValue:=Min(Line^[X]+QuantizationSteps-I,255);
end;
// Dont't change pixel to transparent color key
if TransparentColor=-1 then Line^[X]:=NewValue
else if BPP=1 then
begin
if NewValue<>TransparentColor then Line^[X]:=NewValue;
end
else // BPP=3, TransparentColor<>-1
begin
Line^[X]:=NewValue;
if (XModBPP=2) and (PInteger(@Line^[X-2])^ and $ffffff=TransparentColor) then Line^[X]:=NewValue xor 1;
end;
end;
LastLine:=Line;
end;
Result:=slfAverage;
end;
function PaethPredictor(a,b,c: Byte): Byte;
var
p, pa, pb, pc : Integer;
begin
// a = left, b = above, c = upper left
p:=a+b-c; // Initial estimate
pa:=abs(p-a); // Distances to a, b, c
pb:=abs(p-b);
pc:=abs(p-c);
// Return nearest of a,b,c, breaking ties in order a,b,c.
if (pa<=pb) and (pa<=pc) then Result:=a
else if pb<=pc then Result:=b
else Result:=c;
end;
```

You are welcome to create a command line tool that uses this.

Michael Vinther

> software developer <