Greetings,
I made some patches to include a stretchdraw function in the TFPCanvas
object.
The algorithmes for the interpolation routines come from ImageMagick,
reimplemented in pascal.
Luk Vandelaer
{ TFPCustomInterpolation }
procedure TFPCustomInterpolation.Initialize(aimage: TFPCustomImage; acanvas:
TFPCustomCanvas);
begin
fimage := aimage;
fcanvas := acanvas;
end;
{ TFPBaseInterpolation }
type
TInterpolationContribution = record
weight : double;
place : integer;
end;
function ColorRound (c : double) : word;
begin
if c > $FFFF then
result := $FFFF
else if c < 0.0 then
result := 0
else
result := round(c);
end;
procedure TFPBaseInterpolation.Horizontal (width : integer);
var x,y,r : integer;
start, stop, maxcontribs : integer;
center, re,gr,bl, density : double;
contributions : array[0..10] of TInterpolationContribution;
dif, w, gamma, a : double;
c : TFPColor;
begin
for x := 0 to width-1 do
begin
center := x * xfactor;
start := round (center-xsupport);
if start < 0 then
start := 0;
stop := round(center+xsupport);
if stop >= image.Width then
stop := image.Width-1;
density := 0.0;
maxcontribs := -1;
for r := start to stop do
begin
dif := r - center;
w := Filter (dif);
if w > 0.0 then
begin
inc (maxcontribs);
with contributions[maxcontribs] do
begin
weight := w;
density := density + w;
place := r;
end;
end;
end;
if (density <> 0.0) and (density <> 1.0) then
begin
density := 1.0 / density;
for r := 0 to maxcontribs do
contributions[r].weight := contributions[r].weight * density;
end;
for y := 0 to image.height-1 do
begin
gamma := 0.0;
re := 0.0;
gr := 0.0;
bl := 0.0;
for r := 0 to maxcontribs do
with contributions[r] do
with image.colors[place,y] do
begin
a := weight * alpha / $FFFF;
re := re + a * image.colors[place,y].red;
gr := gr + a * image.colors[place,y].green;
bl := bl + a * image.colors[place,y].blue;
gamma := gamma + a;
end;
with c do
begin
red := ColorRound (re);
green := ColorRound (gr);
blue := ColorRound (bl);
alpha := ColorRound (gamma * $FFFF) ;
end;
tempimage.colors[x,y] := c;
end;
end;
end;
procedure TFPBaseInterpolation.vertical(dx,dy,width,height: integer);
var x,y,r : integer;
start, stop, maxcontribs : integer;
center, re,gr,bl, density : double;
contributions : array[0..10] of TInterpolationContribution;
dif, w, gamma, a : double;
c : TFPColor;
begin
for y := 0 to height-1 do
begin
center := y * yfactor;
start := round (center-ysupport);
if start < 0 then
start := 0;
stop := round(center+ysupport);
if stop >= tempimage.height then
stop := tempimage.height-1;
density := 0.0;
maxcontribs := -1;
for r := start to stop do
begin
dif := r - center;
w := Filter (dif);
if w > 0.0 then
begin
inc (maxcontribs);
with contributions[maxcontribs] do
begin
weight := w;
density := density + w;
place := r;
end;
end;
end;
if (density <> 0.0) and (density <> 1.0) then
begin
density := 1.0 / density;
for r := 0 to maxcontribs do
contributions[r].weight := contributions[r].weight * density;
end;
for x := 0 to width-1 do
begin
gamma := 0.0;
re := 0.0;
gr := 0.0;
bl := 0.0;
for r := 0 to maxcontribs do
with contributions[r] do
with tempimage.colors[x,place] do
begin
a := weight * alpha / $FFFF;
re := re + a * red;
gr := gr + a * green;
bl := bl + a * blue;
gamma := gamma + a;
end;
with c do
begin
red := ColorRound (re);
green := ColorRound (gr);
blue := ColorRound (bl);
alpha := ColorRound (gamma * $FFFF);
end;
canvas.colors[x+dx,y+dy] := c;
end;
end;
end;
procedure TFPBaseInterpolation.Execute(x, y, w, h: integer);
var maxy : integer;
rx,ry : integer;
begin
tempimage := TFPMemoryImage.Create (w,image.height);
tempimage.UsePalette := false;
xfactor := image.Width / w;
yfactor := image.Height / h;
if xfactor > 1.0 then
xsupport := MaxSupport
else
xsupport := xfactor * MaxSupport;
if yfactor > 1.0 then
ysupport := MaxSupport
else
ysupport := yfactor * MaxSupport;
Horizontal (w);
Vertical (x,y,w,h);
end;
{ TMitchelInterpolation }
function TMitchelInterpolation.Filter(x: double): double;
const
B = (1.0/3.0);
C = (1.0/3.0);
P0 = (( 6.0- 2.0*B )/6.0);
P2 = ((-18.0+12.0*B+ 6.0*C)/6.0);
P3 = (( 12.0- 9.0*B- 6.0*C)/6.0);
Q0 = (( 8.0*B+24.0*C)/6.0);
Q1 = (( -12.0*B-48.0*C)/6.0);
Q2 = (( 6.0*B+30.0*C)/6.0);
Q3 = (( - 1.0*B- 6.0*C)/6.0);
begin
if (x < -2.0) then
result := 0.0
else if (x < -1.0) then
result := Q0-x*(Q1-x*(Q2-x*Q3))
else if (x < 0.0) then
result := P0+x*x*(P2-x*P3)
else if (x < 1.0) then
result := P0+x*x*(P2+x*P3)
else if (x < 2.0) then
result := Q0+x*(Q1+x*(Q2+x*Q3))
else
result := 0.0;
end;
function TMitchelInterpolation.MaxSupport: double;
begin
result := 2.0;
end;
--- fcl/image/fpcanvas.pp 2006-06-29 20:31:38.000000000 +0200
+++ ../fotoweb/canvas.pp 2006-09-11 13:42:26.171875000 +0200
@@ -153,6 +153,43 @@
end;
TFPCustomBrushClass = class of TFPCustomBrush;
+ { TFPCustomInterpolation }
+
+ TFPCustomInterpolation = class
+ private
+ fcanvas: TFPCustomCanvas;
+ fimage: TFPCustomImage;
+ protected
+ procedure Initialize (aimage:TFPCustomImage; acanvas:TFPCustomCanvas);
virtual;
+ procedure Execute (x,y,w,h:integer); virtual; abstract;
+ public
+ property Canvas : TFPCustomCanvas read fcanvas;
+ property Image : TFPCustomImage read fimage;
+ end;
+
+ { TFPBaseInterpolation }
+
+ TFPBaseInterpolation = class (TFPCustomInterpolation)
+ private
+ xfactor, yfactor : double;
+ xsupport,ysupport : double;
+ tempimage : TFPCustomImage;
+ procedure Horizontal (width : integer);
+ procedure vertical (dx,dy,width,height: integer);
+ protected
+ procedure Execute (x,y,w,h:integer); override;
+ function Filter (x : double) : double; virtual; abstract;
+ function MaxSupport : double; virtual; abstract;
+ end;
+
+ { TMitchelInterpolation }
+
+ TMitchelInterpolation = class (TFPBaseInterpolation)
+ protected
+ function Filter (x : double) : double; override;
+ function MaxSupport : double; override;
+ end;
+
{ TFPCustomCanvas }
TFPCustomCanvas = class(TPersistent)
@@ -170,6 +207,7 @@
FClipRect : TRect;
FHelpers : TList;
FLocks : integer;
+ FInterpolation : TFPCustomInterpolation;
function AllowFont (AFont : TFPCustomFont) : boolean;
function AllowBrush (ABrush : TFPCustomBrush) : boolean;
function AllowPen (APen : TFPCustomPen) : boolean;
@@ -219,8 +257,6 @@
procedure DoMoveTo (x,y:integer); virtual;
procedure DoLineTo (x,y:integer); virtual;
procedure DoLine (x1,y1,x2,y2:integer); virtual; abstract;
- procedure DoCopyRect (x,y:integer; canvas:TFPCustomCanvas; Const
SourceRect:TRect); virtual; abstract;
- procedure DoDraw (x,y:integer; Const image:TFPCustomImage); virtual;
abstract;
procedure CheckHelper (AHelper:TFPCanvasHelper); virtual;
procedure AddHelper (AHelper:TFPCanvasHelper);
public
@@ -259,11 +295,13 @@
// other procedures
procedure CopyRect (x,y:integer; canvas:TFPCustomCanvas; SourceRect:TRect);
procedure Draw (x,y:integer; image:TFPCustomImage);
+ procedure StretchDraw (x,y,w,h:integer; source:TFPCustomImage);
procedure Erase;virtual;
// properties
property Font : TFPCustomFont read GetFont write SetFont;
property Pen : TFPCustomPen read GetPen write SetPen;
property Brush : TFPCustomBrush read GetBrush write SetBrush;
+ property Interpolation : TFPCustomInterpolation read FInterpolation write
FInterpolation;
property Colors [x,y:integer] : TFPColor read GetColor write SetColor;
property ClipRect : TRect read GetClipRect write SetClipRect;
property Clipping : boolean read FClipping write FClipping;
@@ -375,6 +413,7 @@
{$i FPFont.inc}
{$i FPPen.inc}
{$i FPBrush.inc}
+{$i fpinterpolation.inc}
{$i FPCanvas.inc}
{$i FPCDrawH.inc}
--- fcl/image/fpcanvas.inc 2006-06-29 20:31:38.000000000 +0200
+++ ../fotoweb/fpcanvas.inc 2006-08-20 23:36:50.000000000 +0200
@@ -605,3 +605,26 @@
end;
end;
+procedure TFPCustomCanvas.StretchDraw(x, y, w, h: integer; source:
TFPCustomImage);
+var i : TFPCustomInterpolation;
+ FreeInterpolation : boolean;
+ IP : TFPCustomInterpolation;
+begin
+ FreeInterpolation := not assigned (FInterpolation);
+ if FreeInterpolation then
+ IP := TMitchelInterpolation.Create
+ else
+ IP := FInterpolation;
+ try
+ with IP do
+ begin
+ Initialize (source, self);
+ Execute (x,y,w,h);
+ end;
+ finally
+ if FreeInterpolation then
+ IP.Free;
+ end;
+end;
+
+
unit moreinterpolation;
{
Some more interpolation filters for TFPCanvas.StretchDraw:
Bessel, Gaussian and Sinc are infinite impulse response (IIR),
the other are finite impulse response (FIR). The implementation
of Bessel and Sinc are windowed with Blackman filter.
}
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FPImage, FPCanvas;
type
{ TBlackmanInterpolation }
TBlackmanInterpolation = class (TFPBaseInterpolation)
protected
function Filter (x : double) : double; override;
function MaxSupport : double; override;
end;
{ TBlackmanSincInterpolation }
TBlackmanSincInterpolation = class (TFPBaseInterpolation)
protected
function Filter (x : double) : double; override;
function MaxSupport : double; override;
end;
{ TBlackmanBesselInterpolation }
TBlackmanBesselInterpolation = class (TFPBaseInterpolation)
protected
function Filter (x : double) : double; override;
function MaxSupport : double; override;
end;
{ TGaussianInterpolation }
TGaussianInterpolation = class (TFPBaseInterpolation)
protected
function Filter (x : double) : double; override;
function MaxSupport : double; override;
end;
{ TBoxInterpolation }
TBoxInterpolation = class (TFPBaseInterpolation)
protected
function Filter (x : double) : double; override;
function MaxSupport : double; override;
end;
{ THermiteInterpolation }
THermiteInterpolation = class (TFPBaseInterpolation)
protected
function Filter (x : double) : double; override;
function MaxSupport : double; override;
end;
{ TLanczosInterpolation }
TLanczosInterpolation = class (TFPBaseInterpolation)
protected
function Filter (x : double) : double; override;
function MaxSupport : double; override;
end;
{ TQuadraticInterpolation }
TQuadraticInterpolation = class (TFPBaseInterpolation)
protected
function Filter (x : double) : double; override;
function MaxSupport : double; override;
end;
{ TCubicInterpolation }
TCubicInterpolation = class (TFPBaseInterpolation)
protected
function Filter (x : double) : double; override;
function MaxSupport : double; override;
end;
{ TCatromInterpolation }
TCatromInterpolation = class (TFPBaseInterpolation)
protected
function Filter (x : double) : double; override;
function MaxSupport : double; override;
end;
{ TBilineairInterpolation }
TBilineairInterpolation = class (TFPBaseInterpolation)
protected
function Filter (x : double) : double; override;
function MaxSupport : double; override;
end;
{ THanningInterpolation }
THanningInterpolation = class (TFPBaseInterpolation)
protected
function Filter (x : double) : double; override;
function MaxSupport : double; override;
end;
{ THammingInterpolation }
THammingInterpolation = class (TFPBaseInterpolation)
protected
function Filter (x : double) : double; override;
function MaxSupport : double; override;
end;
implementation
// BesselOrderOne: computes Bessel function of x in the first kind of order 0
function J1 (x : double) : double;
const Pone : array[0..8] of double =
( 0.581199354001606143928050809e+21,
-0.6672106568924916298020941484e+20,
0.2316433580634002297931815435e+19,
-0.3588817569910106050743641413e+17,
0.2908795263834775409737601689e+15,
-0.1322983480332126453125473247e+13,
0.3413234182301700539091292655e+10,
-0.4695753530642995859767162166e+7,
0.270112271089232341485679099e+4
);
Qone : array [0..8] of double =
( 0.11623987080032122878585294e+22,
0.1185770712190320999837113348e+20,
0.6092061398917521746105196863e+17,
0.2081661221307607351240184229e+15,
0.5243710262167649715406728642e+12,
0.1013863514358673989967045588e+10,
0.1501793594998585505921097578e+7,
0.1606931573481487801970916749e+4,
0.1e+1
);
var p,q : double;
r : 0..8;
begin
p := Pone[8];
q := Qone[8];
for r := 7 downto 0 do
begin
p := p*x*x+pOne[r];
q := q*X*X+Qone[r];
end;
result := p / q;
end;
function P1 (x : double) : double;
const Pone : array[0..5] of double =
( 0.352246649133679798341724373e+5,
0.62758845247161281269005675e+5,
0.313539631109159574238669888e+5,
0.49854832060594338434500455e+4,
0.2111529182853962382105718e+3,
0.12571716929145341558495e+1
);
Qone : array [0..5] of double =
( 0.352246649133679798068390431e+5,
0.626943469593560511888833731e+5,
0.312404063819041039923015703e+5,
0.4930396490181088979386097e+4,
0.2030775189134759322293574e+3,
0.1e+1
);
var x8,p,q : double;
r : 0..5;
begin
p := Pone[5];
q := Qone[5];
x8 := 8.0 / x;
for r := 4 downto 0 do
begin
p := p*x8*x8+pOne[r];
q := q*x8*x8+Qone[r];
end;
result := p / q;
end;
function Q1 (x : double) : double;
const Pone : array[0..5] of double =
( 0.3511751914303552822533318e+3,
0.7210391804904475039280863e+3,
0.4259873011654442389886993e+3,
0.831898957673850827325226e+2,
0.45681716295512267064405e+1,
0.3532840052740123642735e-1
);
Qone : array [0..5] of double =
( 0.74917374171809127714519505e+4,
0.154141773392650970499848051e+5,
0.91522317015169922705904727e+4,
0.18111867005523513506724158e+4,
0.1038187585462133728776636e+3,
0.1e+1
);
var x8,p,q : double;
r : 0..5;
begin
p := Pone[5];
q := Qone[5];
x8 := 8.0 / x;
for r := 4 downto 0 do
begin
p := p*x8*x8+pOne[r];
q := q*x8*x8+Qone[r];
end;
result := p / q;
end;
function BesselOrderOne (x : double) : double;
var p,q, OneOverSqrt2,sinx,cosx : double;
begin
if x = 0.0 then
result := 0.0
else
begin
p := x;
if x < 0.0 then
x := -x;
if x < 8.0 then
result := p * J1(x)
else
begin
OneOverSqrt2 := 1.0 / sqrt(2.0);
sinx := sin(x);
cosx := cos(x);
result := sqrt(2.0/(PI*x)) *
( P1(x)*(OneOverSqrt2*(sinx-cosx))
- 8.0/x*Q1(x)*(-OneOverSqrt2*(sinx+cosx))
);
if p < 0.0 then
result := -result;
end
end;
end;
// Functions to aid calculations
function Bessel (x : double) : double;
begin
if x = 0.0 then
result := PI / 4.0
else
result := BesselOrderOne(PI * x) / (2.0 * x);
end;
function Sinc (x : double) : double;
var xx : double;
begin
if x = 0.0 then
result := 1.0
else
begin
xx := PI*x;
result := sin(xx) / (xx);
end;
end;
function Blackman (x : double) : double;
var xpi : double;
begin
xpi := PI * x;
result := 0.42 + 0.5 * cos(xpi) + 0.08 * cos(2*xpi);
end;
{ THermiteInterpolation }
function THermiteInterpolation.Filter(x: double): double;
begin
if x < -1.0 then
result := 0.0
else if x < 0.0 then
result := (2.0*(-x)-3.0)*(-x)*(-x)+1.0
else if x < 1.0 then
result := (2.0*x-3.0)*x*x+1.0
else
result := 0;
end;
function THermiteInterpolation.MaxSupport: double;
begin
result := 1.0;
end;
{ TLanczosInterpolation }
function TLanczosInterpolation.Filter(x: double): double;
begin
if x < -3.0 then
result := 0.0
else if x < 0.0 then
result := sinc(-x)*sinc(-x/3.0)
else if x < 3.0 then
result := sinc(x)*sinc(x/3.0)
else
result := 0.0;
end;
function TLanczosInterpolation.MaxSupport: double;
begin
result := 3.0;
end;
{ TQuadraticInterpolation }
function TQuadraticInterpolation.Filter(x: double): double;
begin
if x < -1.5 then
result := 0.0
else if x < -0.5 then
begin
x := x + 1.5;
result := 0.5*x*x;
end
else if x < 0.5 then
result := 0.75 - x*x
else if x < 1.5 then
begin
x := x - 1.5;
result := 0.5*x*x;
end
else
result := 0.0;
end;
function TQuadraticInterpolation.MaxSupport: double;
begin
result := 1.5;
end;
{ TCubicInterpolation }
function TCubicInterpolation.Filter(x: double): double;
begin
if x < -2.0 then
result := 0.0
else if x < -1.0 then
begin
x := x +2.0;
result := x*x*x / 6.0;
end
else if x < 0.0 then
result := (4.0+x*x*(-6.0-3.0*x)) / 6.0
else if x < 1.0 then
result := (4.0+x*x*(-6.0+3.0*x)) / 6.0
else if x < 2.0 then
begin
x := 2.0 - x;
result := x*x*x / 6.0;
end
else
result := 0.0;
end;
function TCubicInterpolation.MaxSupport: double;
begin
result := 2.0;
end;
{ TCatromInterpolation }
function TCatromInterpolation.Filter(x: double): double;
begin
if x < -2.0 then
result := 0.0
else if x < -1.0 then
result := 0.5*(4.0+x*(8.0+x*(5.0+x)))
else if x < 0.0 then
result := 0.5*(2.0+x*x*(-5.0-3.0*x))
else if x < 1.0 then
result := 0.5*(2.0+x*x*(-5.0+3.0*x))
else if x < 2.0 then
result := 0.5*(4.0+x*(-8.0+x*(5.0-x)))
else
result := 0.0;
end;
function TCatromInterpolation.MaxSupport: double;
begin
result := 2.0;
end;
{ THanningInterpolation }
function THanningInterpolation.Filter(x: double): double;
begin
if x < -1.0 then
result := 0.0
else if x <= 1.0 then
result := 0.5+0.5*cos(PI*x)
else
result := 0.0;
end;
function THanningInterpolation.MaxSupport: double;
begin
result := 1.0;
end;
{ THammingInterpolation }
function THammingInterpolation.Filter(x: double): double;
begin
if x < -1.0 then
result := 0.0
else if x <= 1.0 then
result := 0.54+0.46*cos(PI*x)
else
result := 0.0;
end;
function THammingInterpolation.MaxSupport: double;
begin
result := 1.0;
end;
{ TBilineairInterpolation }
function TBilineairInterpolation.Filter(x: double): double;
begin
if x < -1.0 then
result := 0.0
else if x < 0.0 then
result := 1 + x
else if x < 1.0 then
result := 1 - x
else
result := 0.0;
end;
function TBilineairInterpolation.MaxSupport: double;
begin
result := 1.0;
end;
{ TBoxInterpolation }
function TBoxInterpolation.Filter(x: double): double;
begin
if x < -0.5 then
result := 0.0
else if x < 0.5 then
result := 1.0
else
result := 0.0;
end;
function TBoxInterpolation.MaxSupport: double;
begin
result := 0.5;
end;
{ TGaussianInterpolation }
function TGaussianInterpolation.Filter(x: double): double;
begin
result := exp(-2.0*x*x) * sqrt(2.0/PI);
end;
function TGaussianInterpolation.MaxSupport: double;
begin
result := 1.25;
end;
{ TBlackmanBesselInterpolation }
function TBlackmanBesselInterpolation.Filter(x: double): double;
begin
result := Blackman(x/MaxSupport) * Bessel (x);
end;
function TBlackmanBesselInterpolation.MaxSupport: double;
begin
Result := 3.2383;
end;
{ TBlackmanSincInterpolation }
function TBlackmanSincInterpolation.Filter(x: double): double;
begin
Result := Blackman(x/MaxSupport) * Sinc(x);
end;
function TBlackmanSincInterpolation.MaxSupport: double;
begin
Result := 4.0;
end;
{ TBlackmanInterpolation }
function TBlackmanInterpolation.Filter(x: double): double;
begin
Result := Blackman (x);
end;
function TBlackmanInterpolation.MaxSupport: double;
begin
Result := 1.0;
end;
end.
_______________________________________________
fpc-devel maillist - fpc-devel@lists.freepascal.org
http://lists.freepascal.org/mailman/listinfo/fpc-devel