unit filterRotation;
(* ***** BEGIN LICENSE BLOCK *****
* Copyright (C) 2004 Durand Emmanuel
* Copyright (C) 2004 Burgel Eric
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Contact :
* filters@edurand.com
* filters@burgel.com
*
* ***** END LICENSE BLOCK ***** *)
{
eburgel (filters@burgel.com)
edurand (filters@edurand.com)
}
interface
uses
filter, fparameters, image;
type
TFilterRotation = class(TFilter)
public
constructor Create; override;
destructor Destroy; override;
procedure Run(); override;
private
imageIn, imgOutMonitoring : PBitmap32 ;
outImage : PBitmap32;
CenterX, CenterY : Single ;
AngleR, sinus, cosinus : Extended ;
deltaH, deltaW : Integer;
missingPixelColorMode : Integer;
missingPixelColor : TColor32;
parameterImageIn : TParameterImage;
outputparameterImageOut : TParameterImage;
parameterImageOutMonitoring : TParameterImage;
parameterMonitoring : TParameterBoolean;
parameterAngle : TParameterSingle ;
parameterCenterX, parameterCenterY : TParameterSingle ;
parameterInterpolationMode : TParameterInteger ;
parameterMissingPixelColorMode : TParameterString;
parameterAutoAdjustSize : TParameterBoolean;
procedure _run();
procedure rotationInterpolationNearestNeighbor();
procedure rotationInterpolationBasicFast();
procedure deleteImages;
end;
implementation
uses
imageIO, Math, divers;
constructor TFilterRotation.Create;
begin
inherited;
parameterImageIn:=addParameterImage('inImage', 'input image to invert');
outputparameterImageOut := addOutputParameterImage('outImage', 'outImage');
parameterImageOutMonitoring:=addParameterImage('outImageMonitoring', 'image out monitoring');
parameterMonitoring:=addParameterBoolean('monitoring','monitoring',True);
parameterAngle:=addParameterSingle('angle', 'angle in degree', -10000, 10000, 90);
parameterCenterX:=addParameterSingle('xCenter', 'X center of rotation', -10000, 10000, 0);
parameterCenterY:=addParameterSingle('yCenter', 'Y center of rotation', -10000, 10000, 0);
parameterInterpolationMode := addParameterInteger('interpolationMode', 'Interpolation mode', 0, 2, 0) ;
parameterMissingPixelColorMode:=addParameterString('missingPixelColorMode','how the filter must set the missing pixel due to the rotation [MIRROR/WHITE/BLACK]','MIRROR');
parameterAutoAdjustSize:=addParameterBoolean('autoAdjustSize','auto adjust imageOut size to show all imageIn pixel',true);
end;
destructor TFilterRotation.Destroy;
begin
deleteImages;
inherited;
end;
procedure TFilterRotation.deleteImages;
begin
image.freeImage(outImage);
end;
procedure TFilterRotation.Run();
begin
imageIn := parameterImageIn.Image ;
if (imageIn<>nil) then begin
_run();
end;
end;
procedure TFilterRotation._run();
var
InterpolationMode : Integer ;
imageOutWidth, imageOutHeight : Integer;
minX, maxX, minY, maxY : Integer;
procedure searchMinMaxXY(inPointX,inPointY:Integer);
var
outPoint : TFPoint;
begin
outPoint:=divers.getRotatedPoint(divers.fpoint(inPointX,inPointY),divers.fpoint(CenterX,CenterY),AngleR);
minX:=Min(minX,Floor(outPoint.x));
maxX:=Max(maxX,Floor(outPoint.x));
minY:=Min(minY,Floor(outPoint.y));
maxY:=Max(maxY,Floor(outPoint.y));
end;
begin
deleteImages;
AngleR := -parameterAngle.Value *PI/180 ;
CenterX := parameterCenterX.Value ;
CenterY := parameterCenterY.value ;
imageOutWidth:=imageIn.Width;
imageOutHeight:=imageIn.Height;
deltaW:=0;
deltaH:=0;
if parameterAutoAdjustSize.Value=true then begin
CenterX:=imageIn.Width div 2;
CenterY:=imageIn.Height div 2;
minX:=MAXINT; maxX:=-MAXINT; minY:=MAXINT; maxY:=-MAXINT;
searchMinMaxXY(0,0);
searchMinMaxXY(imageIn.Width-1,0);
searchMinMaxXY(imageIn.Width-1,imageIn.Height-1);
searchMinMaxXY(0,imageIn.Height-1);
deltaW:=((maxX-minX)-imageIn.Width);
deltaH:=((maxY-minY)-imageIn.Height);
Inc(imageOutWidth,deltaW);
Inc(imageOutHeight,deltaH);
end;
outImage:=createImage(imageOutWidth,imageOutHeight);
InterpolationMode := parameterInterpolationMode.Value ;
if parameterMissingPixelColorMode.Value='MIRROR' then begin
missingPixelColorMode:=0;
missingPixelColor:=clBlack32;
end
else if parameterMissingPixelColorMode.Value='WHITE' then begin
missingPixelColorMode:=1;
missingPixelColor:=clWhite32;
end
else if parameterMissingPixelColorMode.Value='BLACK' then begin
missingPixelColorMode:=2;
missingPixelColor:=clBlack32;
end;
sinCos(AngleR, sinus, cosinus) ;
// monitoring
imgOutMonitoring:=parameterImageOutMonitoring.Image;
if (parameterMonitoring.Value=True) and (imgOutMonitoring<>nil) then begin
image.copyImageToImage(imageIn,imgOutMonitoring);
end else
if imgOutMonitoring<>nil then begin
image.eraseImage(imgOutMonitoring);
end;
case InterpolationMode of
0 : rotationInterpolationNearestNeighbor() ;
1, 2 : rotationInterpolationBasicFast();
end ;
setOutputParameterImage('outImage',outImage);
end;
procedure TFilterRotation.rotationInterpolationNearestNeighbor();
var
pSrc, PDest : PColor32Array;
x, y, maxX, maxY : Integer ;
newX, newY : Integer ;
imgInWidth, imgInHeight : Integer ;
Kx, Ky : Extended ;
deltaWdiv2, deltaHdiv2 : Single ;
begin
imgInWidth := imageIn.Width ;
imgInHeight := imageIn.Height ;
deltaWdiv2:=deltaW / 2;
deltaHdiv2:=deltaH / 2;
maxX := outImage.Width -1 ;
maxY := outImage.Height -1 ;
Kx := CenterX - cosinus*(CenterX+deltaWdiv2) - sinus*(CenterY+deltaHdiv2) + 0.5 ;
ky := CenterY - cosinus*(CenterY+deltaHdiv2) + sinus*(CenterX+deltaWdiv2) + 0.5 ;
for y:=0 to maxY do begin
pDest := scanLine(outImage, y) ;
for x:=0 to maxX do begin
// compute X
//newX := trunc((x-centerX-deltaWdiv2)*cosinus + (y-centerY-deltaHdiv2)*sinus + centerX) ;
newX := trunc(cosinus*x + sinus*y + kx) ;
while newX >= imgInWidth do newX := newX - imgInWidth ;
while newX < 0 do newX := newX + imgInWidth ;
// compute Y
//newY := trunc((y-centerY-deltaHdiv2)*cosinus - (x-centerX-deltaWdiv2)*sinus + CenterY) ;
newY := trunc(cosinus*y - sinus*x + ky) ;
while newY >= imgInHeight do newY := newY - imgInHeight ;
while newY < 0 do newY := newY + imgInHeight ;
pSrc := scanLine(imageIn, newY) ;
pDest^[0] := pSrc^[NewX] ;
inc(pDest) ;
end ;
end ;
end;
procedure TFilterRotation.rotationInterpolationBasicFast();
var
pSrc1, pSrc2, PDest : PColor32Array;
x, y, maxX, maxY : Integer ;
newX, newY : Single ;
floorX, floorY : Integer ;
T, U : Single ;
Color1, Color2 : TColor32 ;
cosinusMulX : array of Single;
sinusMulY : array of Single;
cosinusMulY : array of Single;
sinusMulX : array of Single;
sinusMulYy, cosinusMulYy : Single;
pcosinusMulXx, psinusMulXx : ^Single;
bPixelProcessed : boolean;
newXYmin, newXYmax : TFPoint;
tmpColor : TColor32;
deltaWdiv2, deltaHdiv2 : Integer;
T1, T2, U1, U2 : Word ;
r_b1, _v_1, r_b2, _v_2 : TColor32 ;
basicFast : boolean ;
begin
basicFast := (parameterInterpolationMode.Value=1) ;
maxX := outImage.Width -1 ;
maxY := outImage.Height -1 ;
newXYmin.x:=maxX;
newXYmin.y:=maxY;
newXYmax.x:=0;
newXYmax.y:=0;
deltaWdiv2:=deltaW div 2;
deltaHdiv2:=deltaH div 2;
SetLength(cosinusMulX,maxX+1);
SetLength(sinusMulX,maxX+1);
for x:=0 to maxX do begin
cosinusMulX[x]:=cosinus*(x-CenterX-deltaWdiv2);
sinusMulX[x]:=-sinus*(x-CenterX-deltaWdiv2)+CenterY;
end;
SetLength(sinusMulY,maxY+1);
SetLength(cosinusMulY,maxY+1);
for y:=0 to maxY do begin
sinusMulY[y]:=sinus*(y-CenterY-deltaHdiv2)+CenterX;
cosinusMulY[y]:=cosinus*(y-CenterY-deltaHdiv2);
end;
pDest := outImage.Bits;
for y:=0 to maxY do begin
sinusMulYy:=sinusMulY[y];
cosinusMulYy:=cosinusMulY[y];
pcosinusMulXx:=@cosinusMulX[0];
psinusMulXx:=@sinusMulX[0];
for x:=0 to maxX do begin
bPixelProcessed:=false;
// without pre-calculation, we would use this formula
//newX := cosinus*(x-CenterX) + sinus*(y-CenterY)+CenterX;
//newY := cosinus*(y-CenterY) - sinus*(x-CenterX)+CenterY;
newX := pcosinusMulXx^ + sinusMulYy;
newY := cosinusMulYy + psinusMulXx^;
// the newX/Y can correspond of an nonexisting pixel in imageIn
if (newX<0) or (newY<0) or
(newX>=imageIn.Width-1) or (newY>=imageIn.Height-1) then begin
if missingPixelColorMode<>0 then begin
pDest^[0] := missingPixelColor;
bPixelProcessed:=true;
end else begin
newX:=Abs(newX);
if newX>=imageIn.Width-1 then begin
newX := newX - imageIn.Width+1;
if newX>=imageIn.Width then newX := imageIn.Width-1;
end;
newY:=Abs(newY);
if newY>=imageIn.Height-1 then begin
newY := newY - imageIn.Height+1;
if newY>=imageIn.Height then newY := imageIn.Height-1;
end;
end;
end;
if parameterMonitoring.Value=true then begin
floorX := floor(newX);
floorY := floor(newY);
if (floorX>=0) and (floorY>=0) and
(floorX<imageIn.Width) and (floorY<imageIn.Height) then begin
if floorX<newXYmin.x then newXYmin.x:=floorX;
if floorY<newXYmin.y then newXYmin.y:=floorY;
if floorX>newXYmax.x then newXYmax.x:=floorX;
if floorY>newXYmax.y then newXYmax.y:=floorY;
tmpColor:=image.getPixel(imgOutMonitoring,floorX,floorY);
tmpColor:=tmpColor or $0000AA00;
image.setPixel(imgOutMonitoring,floorX,floorY,tmpColor);
end;
end;
if bPixelProcessed=false then begin
floorX := floor(newX);
floorY := floor(newY);
T := frac(newX) ;
U := frac(newY) ;
if(floorX>(imageIn.Width-2))then begin
floorX:=imageIn.Width-2;
end;
pSrc1:=imageIn.Bits;
Inc(pSrc1,floorY*imageIn.Width);
pSrc2:=pSrc1;
Inc(pSrc2,imageIn.Width);
IF basicFast then Begin
if T < 0.34 Then
color1 := pSrc1[floorX]
else if T > 0.66 then
color1 := pSrc1[floorX+1]
else
color1 := ((pSrc1[floorX] and $FEFEFE) shr 1) + ((pSrc1[floorX+1] and $FEFEFE) shr 1) ;
if T < 0.34 Then
color2 := pSrc2[floorX]
else if T > 0.66 then
color2 := pSrc2[floorX+1]
else
color2 := ((pSrc2[floorX] and $FEFEFE) shr 1) + ((pSrc2[floorX+1] and $FEFEFE) shr 1) ;
if U < 0.34 Then
pDest^[0] := color1
else if U > 0.66 then
pDest^[0] := color2
else
pDest^[0] := ((color1 and $FEFEFE) shr 1) + ((color2 and $FEFEFE) shr 1) ;
end else Begin
// Horizontal
T2 := floor(T*128) ;
T1 := 128-T2 ;
r_b1 := pSrc1[floorX] and $00FF00FF ;
_v_1 := pSrc1[floorX] and $0000FF00 ;
r_b2 := pSrc1[floorX+1] and $00FF00FF ;
_v_2 := pSrc1[floorX+1] and $0000FF00 ;
color1 := (((r_b1* t1 + r_b2* t2) div 128) and $00FF00FF) or (((_v_1*t1 + _v_2*t2)div 128) and $0000FF00) ;
r_b1 := pSrc2[floorX] and $00FF00FF ;
_v_1 := pSrc2[floorX] and $0000FF00 ;
r_b2 := pSrc2[floorX+1] and $00FF00FF ;
_v_2 := pSrc2[floorX+1] and $0000FF00 ;
color2 := (((r_b1* t1 + r_b2* t2) div 128) and $00FF00FF) or (((_v_1*t1 + _v_2*t2)div 128) and $0000FF00) ;
// Vertical
U2 := floor(U*128) ;
U1 := 128-U2 ;
r_b1 := color1 and $00FF00FF ;
_v_1 := color1 and $0000FF00 ;
r_b2 := color2 and $00FF00FF ;
_v_2 := color2 and $0000FF00 ;
pDest^[0] := (((r_b1* U1 + r_b2* U2) div 128) and $00FF00FF) or (((_v_1*U1 + _v_2*U2)div 128) and $0000FF00) ;
end ;
end;
Inc(pDest) ;
Inc(pcosinusMulXx);
Inc(psinusMulXx);
end;
end ;
if parameterMonitoring.Value=true then begin
image.drawLine(imgOutMonitoring,newXYmin,newXYmax,clYellow32);
end;
end;
end.