mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-09-13 13:09:16 +02:00
* Patch from Mattias Gaertner to fix memory leak, improve speed and compute correcter
git-svn-id: trunk@12261 -
This commit is contained in:
parent
5ef7b7fd04
commit
bd36bdc577
@ -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.
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user