I've played a little bit with premultiply procedures and it looks like precalculation is not necessary and in general I can't see really big differences...
AM+ = Anders Melander's version with precalculation
AM- = Anders Melander's version without precalculation
CShr = Circular's Shr-version
CDiv = Circular's Div-version
JPG 1920 x 1200
AM+ AM- CShr CDiv
1. 0, 16, 0, 15 ms
2. 0, 0, 16, 0 ms
3. 0, 0, 15, 0 ms
JPG 2602 x 1734
AM+ AM- CShr CDiv
1. 16, 15, 16, 15 ms
2. 15, 16, 0, 16 ms
3. 16, 0, 16, 15 ms
JPG 8310 x 5500
AM+ AM- CShr CDiv
1. 156, 156, 141, 171 ms
2. 141, 140, 125, 156 ms
3. 140, 140, 110, 171 ms
Obviously when it comes to very big images then the CSHR-version is the best, but normally the pictures I use are a lot smaller so it doesn't matter what version, at least on my system and in this little test... funny...
And the winner is... CShr-version
Procedure PremultiplyBGRA(BMP: TBGRABitmap);
Var
iX, iY: Integer;
p: PBGRAPixel;
Begin
For iY:= 0 To BMP.Height-1
Do
Begin
p:= BMP.Scanline[iY];
For iX:= BMP.Width-1 Downto 0
Do
Begin
If p^.Alpha = 0
Then p^:= BGRAPixelTransparent
Else
Begin
p^.Red := p^.Red *(p^.Alpha+1) SHR 8;
p^.Green:= p^.Green*(p^.Alpha+1) SHR 8;
p^.Blue := p^.Blue *(p^.Alpha+1) SHR 8;
End;
Inc(p);
End;
End;
End;
CDiv-version
Procedure PremultiplyBMP(BMP: TBGRABitmap);
Var
iX, iY: Integer;
p: PBGRAPixel;
Begin
For iY:= 0 To BMP.Height-1
Do
Begin
p:= BMP.Scanline[iY];
For iX:= BMP.Width-1 DownTo 0
Do
Begin
p^.Red := p^.Red *p^.Alpha Div 255;
p^.Green:= p^.Green*p^.Alpha Div 255;
p^.Blue := p^.Blue *p^.Alpha Div 255;
Inc(p);
End;
End;
End;
AM+ version
Procedure Premultiply(BMP: TBGRABitmap);
Var
iRow, iCol: Integer;
p: PBGRAPixel;
arrByte: Array[Byte, Byte] Of Byte;
Begin
For iRow:= 0 To 255
Do
For iCol:= iRow To 255
Do
Begin
arrByte[iRow, iCol]:= iRow*iCol Div 255;
If (iRow <> iCol)
Then arrByte[iCol, iRow]:= arrByte[iRow, iCol];
End;
For iRow:= 0 To BMP.Height-1
Do
Begin
iCol:= BMP.Width;
p := BMP.ScanLine[iRow];
While (iCol > 0)
Do
Begin
p^.Blue := arrByte[p^.Alpha, p^.Blue];
p^.Green:= arrByte[p^.Alpha, p^.Green];
p^.Red := arrByte[p^.Alpha, p^.Red];
Inc(p);
Dec(iCol);
End;
End;
End;
AM original version
Procedure PremultiplyBitmap(Bitmap: TBitmap);
type
pRGBQuad = ^TRGBQuad;
TRGBQuad = PACKED RECORD
Blue : BYTE;
Green : BYTE;
Red : BYTE;
Reserved: BYTE
end;
var
Row, Col: integer;
p: PRGBQuad;
PreMult: array[byte, byte] of byte;
begin
// precalculate all possible values of a*b
for Row := 0 to 255 do
for Col := Row to 255 do
begin
PreMult[Row, Col] := Row*Col div 255;
if (Row <> Col) then
PreMult[Col, Row] := PreMult[Row, Col]; // a*b = b*a
end;
for Row := 0 to Bitmap.Height-1 do
begin
Col := Bitmap.Width;
p := Bitmap.ScanLine[Row];
while (Col > 0) do
begin
p^.Blue := PreMult[p^.Reserved, p^.Blue];
p^.Green := PreMult[p^.Reserved, p^.Green];
p^.Red := PreMult[p^.Reserved, p^.Red];
inc(p);
dec(Col);
end;
end;
end;
Little test...
Procedure TForm1.Button1Click(Sender: TObject);
Var
cStart, cStop: Cardinal;
BGRA: TBGRABitmap;
Begin
BGRA:= TBGRABitmap.Create(Image2.Picture.Bitmap, False);
Try
cStart:= GetTickCount64;
PremultiplyCalc(BGRA);
cStop:= GetTickCount64-cStart;
Label1.Caption:= IntToStr(cStop);
cStart:= GetTickCount64;
Premultiply(BGRA);
cStop:= GetTickCount64-cStart;
Label2.Caption:= IntToStr(cStop);
cStart:= GetTickCount64;
PremultiplyBGRA(BGRA);
cStop:= GetTickCount64-cStart;
Label3.Caption:= IntToStr(cStop);
cStart:= GetTickCount64;
PremultiplyBMP(BGRA);
cStop:= GetTickCount64-cStart;
Label4.Caption:= IntToStr(cStop);
Finally
BGRA.Free;
End;
End;