* Patch from Mattias Gaertner to fix memory leak, improve speed and compute correcter

git-svn-id: trunk@12261 -
This commit is contained in:
michael 2008-11-28 19:08:45 +00:00
parent 5ef7b7fd04
commit bd36bdc577

View File

@ -12,9 +12,25 @@ of Bessel and Sinc are windowed with Blackman filter.
interface
uses
Classes, SysUtils, FPImage, FPCanvas;
Math, Classes, SysUtils, FPImage, FPCanvas;
type
{ TFPBase2Interpolation
As TFPBaseInterpolation, but
- faster (precomputes pixel weights)
- without temporary image (saving a lot of memory)
- always fills the entire destination
- does not ignore pixels when shrink factor > MaxSupport }
TFPBase2Interpolation = class(TFPCustomInterpolation)
private
procedure CreatePixelWeights (OldSize, NewSize: integer;
out Entries: Pointer; out EntrySize: integer; out Support: integer);
protected
procedure Execute (x,y,w,h : integer); override;
function Filter (x : double): double; virtual;
function MaxSupport : double; virtual;
end;
{ TBlackmanInterpolation }
@ -514,6 +530,229 @@ begin
Result := 1.0;
end;
{ TFPBase2Interpolation }
procedure TFPBase2Interpolation.CreatePixelWeights(OldSize, NewSize: integer;
out Entries: Pointer; out EntrySize: integer; out Support: integer);
// create an array of #NewSize entries. Each entry starts with an integer
// for the StartIndex, followed by #Support singles for the pixel weights.
// The sum of weights for each entry is 1.
var
Entry: Pointer;
procedure SetSupport(NewSupport: integer);
begin
Support:=NewSupport;
EntrySize:=SizeOf(integer)+SizeOf(Single)*Support;
Getmem(Entries,EntrySize*NewSize);
Entry:=Entries;
end;
var
i: Integer;
Factor: double;
StartPos: Double;
StartIndex: Integer;
j: Integer;
FirstValue: Double;
//Sum: double;
begin
if NewSize=OldSize then
begin
SetSupport(1);
for i:=0 to NewSize-1 do
begin
// 1:1
PInteger(Entry)^:=i;
inc(Entry,SizeOf(Integer));
PSingle(Entry)^:=1.0;
inc(Entry,SizeOf(Single));
end;
end else if NewSize<OldSize then
begin
// shrink
SetSupport(Max(2,(OldSize+NewSize-1) div NewSize));
Factor:=double(OldSize)/double(NewSize);
for i:=0 to NewSize-1 do
begin
StartPos:=Factor*i;
StartIndex:=Floor(StartPos);
PInteger(Entry)^:=StartIndex;
inc(Entry,SizeOf(Integer));
// first pixel
FirstValue:=(1.0-(StartPos-double(StartIndex)));
PSingle(Entry)^:=FirstValue/Factor;
inc(Entry,SizeOf(Single));
// middle pixel
for j:=1 to Support-2 do
begin
PSingle(Entry)^:=1.0/Factor;
inc(Entry,SizeOf(Single));
end;
// last pixel
PSingle(Entry)^:=(Factor-FirstValue-(Support-2))/Factor;
inc(Entry,SizeOf(Single));
end;
end else
begin
// enlarge
if OldSize=1 then
begin
SetSupport(1);
for i:=0 to NewSize-1 do
begin
// nothing to interpolate
PInteger(Entry)^:=0;
inc(Entry,SizeOf(Integer));
PSingle(Entry)^:=1.0;
inc(Entry,SizeOf(Single));
end;
end else
begin
SetSupport(2);
Factor:=double(OldSize-1)/double(NewSize);
for i:=0 to NewSize-1 do
begin
StartPos:=Factor*i+Factor/2;
StartIndex:=Floor(StartPos);
PInteger(Entry)^:=StartIndex;
inc(Entry,SizeOf(Integer));
// first pixel
FirstValue:=(1.0-(StartPos-double(StartIndex)));
// convert linear distribution
FirstValue:=Min(1.0,Max(0.0,Filter(FirstValue/MaxSupport)));
PSingle(Entry)^:=FirstValue;
inc(Entry,SizeOf(Single));
// last pixel
PSingle(Entry)^:=1.0-FirstValue;
inc(Entry,SizeOf(Single));
end;
end;
end;
if Entry<>Entries+EntrySize*NewSize then
raise Exception.Create('TFPBase2Interpolation.Execute inconsistency');
end;
procedure TFPBase2Interpolation.Execute(x, y, w, h: integer);
// paint Image on Canvas at x,y,w*h
var
dy: Integer;
dx: Integer;
HorzResized: PFPColor;
xEntries: Pointer;
xEntrySize: integer;
xSupport: integer;// how many horizontal pixel are needed to create one pixel
yEntries: Pointer;
yEntrySize: integer;
ySupport: integer;// how many vertizontal pixel are needed to create one pixel
NewSupportLines: LongInt;
yEntry: Pointer;
SrcStartY: LongInt;
LastSrcStartY: LongInt;
LastyEntry: Pointer;
sy: Integer;
xEntry: Pointer;
sx: LongInt;
cx: Integer;
f: Single;
NewCol: TFPColor;
Col: TFPColor;
CurEntry: Pointer;
begin
if (w<=0) or (h<=0) or (image.Width=0) or (image.Height=0) then
exit;
xEntries:=nil;
yEntries:=nil;
HorzResized:=nil;
try
CreatePixelWeights(image.Width,w,xEntries,xEntrySize,xSupport);
CreatePixelWeights(image.Height,h,yEntries,yEntrySize,ySupport);
// create temporary buffer for the horizontally resized pixel for the
// current y line
GetMem(HorzResized,w*ySupport*SizeOf(TFPColor));
LastyEntry:=nil;
SrcStartY:=0;
for dy:=0 to h-1 do
begin
if dy=0 then
begin
yEntry:=yEntries;
SrcStartY:=PInteger(yEntry)^;
NewSupportLines:=ySupport;
end else
begin
LastyEntry:=yEntry;
LastSrcStartY:=SrcStartY;
inc(yEntry,yEntrySize);
SrcStartY:=PInteger(yEntry)^;
NewSupportLines:=SrcStartY-LastSrcStartY;
// move lines up
if (NewSupportLines>0) and (ySupport>NewSupportLines) then
System.Move(HorzResized[NewSupportLines*w],
HorzResized[0],
(ySupport-NewSupportLines)*w*SizeOf(TFPColor));
end;
// compute new horizontally resized line(s)
for sy:=ySupport-NewSupportLines to ySupport-1 do
begin
xEntry:=xEntries;
for dx:=0 to w-1 do
begin
sx:=PInteger(xEntry)^;
inc(xEntry,SizeOf(integer));
NewCol:=colBlack;
for cx:=0 to xSupport-1 do
begin
f:=PSingle(xEntry)^;
inc(xEntry,SizeOf(Single));
Col:=image.Colors[sx+cx,SrcStartY+sy];
NewCol.red:=Min(NewCol.red+round(Col.red*f),$ffff);
NewCol.green:=Min(NewCol.green+round(Col.green*f),$ffff);
NewCol.blue:=Min(NewCol.blue+round(Col.blue*f),$ffff);
NewCol.alpha:=Min(NewCol.alpha+round(Col.alpha*f),$ffff);
end;
HorzResized[dx+sy*w]:=NewCol;
end;
end;
// compute new vertically resized line
for dx:=0 to w-1 do
begin
CurEntry:=yEntry+SizeOf(integer);
NewCol:=colBlack;
for sy:=0 to ySupport-1 do
begin
f:=PSingle(CurEntry)^;
inc(CurEntry,SizeOf(Single));
Col:=HorzResized[dx+sy*w];
NewCol.red:=Min(NewCol.red+round(Col.red*f),$ffff);
NewCol.green:=Min(NewCol.green+round(Col.green*f),$ffff);
NewCol.blue:=Min(NewCol.blue+round(Col.blue*f),$ffff);
NewCol.alpha:=Min(NewCol.alpha+round(Col.alpha*f),$ffff);
end;
Canvas.Colors[x+dx,y+dy]:=NewCol;
end;
end;
finally
if xEntries<>nil then FreeMem(xEntries);
if yEntries<>nil then FreeMem(yEntries);
if HorzResized<>nil then FreeMem(HorzResized);
end;
end;
function TFPBase2Interpolation.Filter(x: double): double;
begin
Result:=x;
end;
function TFPBase2Interpolation.MaxSupport: double;
begin
Result:=1.0;
end;
end.