如何实现象photoshop中的“色相/饱和度”的调节功能,请给出具体算法代码,谢了(100分)

  • 主题发起人 主题发起人 breath
  • 开始时间 开始时间
B

breath

Unregistered / Unconfirmed
GUEST, unregistred user!
如何实现象photoshop中的“色相/饱和度”的调节功能,请给出具体算法代码,谢了
 
把RGB-->HSV
调整H、S
再把HSV-->RGB
 
原理我知道,就是想知道转么换,请给出代码,tks
 
procedure TFormMain.mnuHSLAdjustClick(Sender: TObject);
begin
//HSL调整
// DoSthForUndo('色调/饱和度调整');
HSLchangeForm:=THSLchangeForm.Create(Application);
try
HSLchangeForm.PreviewPanel1.SourceBitmap.Assign(Image1.Picture.Bitmap);
HSLchangeForm.ShowModal;
if HSLchangeForm.ModalResult=IDOK then
begin
Image1.Picture.Bitmap.Assign(HSLchangeForm.ResultBitmap);
Image1.Repaint;
end;
finally
HSLchangeForm.Free;
end;

end;

 
下面是文件HSLchange1.pas

unit HSLchange1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, StdCtrls,hhx_basedef,hhx_effects,hhx_effectex,hhx_Rotresize,hhx_files, Mask, ExtDlgs,io_files,
Histogram, ComCtrls, PreviewPanel, Spin, ExtTrackBar,hhxCommon, inifiles;
type
THSLchangeForm1 = class(TForm)
Button1: TButton;
Button3: TButton;
ButtonLoad: TButton;
ButtonSave: TButton;
OpenDialog1: TOpenDialog;
SaveDialog1: TSaveDialog;
PreviewPanel1: TPreviewPanel;
Panel1: TPanel;
Label1: TLabel;
Label2: TLabel;
Label4: TLabel;
Label6: TLabel;
Label7: TLabel;
Label8: TLabel;
Label10: TLabel;
SpinEdit1: TSpinEdit;
SpinEdit2: TSpinEdit;
ExtTrackBar1: TExtTrackBar;
ExtTrackBar2: TExtTrackBar;
ComboBox1: TComboBox;
CheckBox1: TCheckBox;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure ButtonLoadClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure PreviewPanel1OrgPosChanged(Sender: TObject);
procedure SpinEdit1Change(Sender: TObject);
procedure ExtTrackBar1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure ExtTrackBar1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure ExtTrackBar1Change(Sender: TObject);
procedure RedTrackMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure RedTrackMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure Edit1KeyPress(Sender: TObject; var Key: Char);
procedure Button3Click(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure ButtonSaveClick(Sender: TObject);
private
SwapMode:TSwapPlanes;
procedure LigCont;
procedure HSLProcessing;
{ Private-Deklarationen }
public
ResultBitmap:TBitmap;
Gamma:Double;

nOldPositon: array [0..10] of integer; //用作Ctrl+Z的旧位置记录
{ Public-Deklarationen }
end;

var
HSLchangeForm1:THSLchangeForm1;
implementation

uses photomain;


{$R *.DFM}



procedure THSLchangeForm1.FormCreate(Sender: TObject);
var k,i:Integer;
begin
DecimalSeparator:='.';
SpinEdit1.Value:=ExttrackBar1.Position;
SpinEdit2.Value:=ExttrackBar2.Position;
Gamma:=1;

for i:=1 to 255 do
begin
k:=IntGamma(Gamma,i);
end;

ResultBitmap:=tBitmap.Create;
end;


procedure THSLchangeForm1.Button1Click(Sender: TObject);
begin
FormMain.SaveCurrentBitmap(PreviewPanel1.SourceBitmap, FormMain.sCurrentFile , 'HSL调整');
FormMain.MainMenu1.Items.Items[1].Items[0].Caption :='撤消 HSL调整';
FormMain.MenuProcess;

HSLProcessing;
close;
end;

procedure THSLchangeForm1.ButtonLoadClick(Sender: TObject);
Var
FileExt: string[4];
begin
if OpenDialog1.Execute then { Display Open dialog box }
begin
ShowMessage(OpenDialog1.Filename);
ExtTrackBar1.Position :=StrToInt(htwIniReadString(OpenDialog1.Filename,'AHU','H','0'));
ExtTrackBar2.Position :=StrToInt(htwIniReadString(OpenDialog1.Filename,'AHU','S','0'));
end;
end;


procedure THSLchangeForm1.FormDestroy(Sender: TObject);
begin
DecimalSeparator:=',';
ResultBitmap.Free;
end;

procedure THSLchangeForm1.PreviewPanel1OrgPosChanged(Sender: TObject);
begin
HSLProcessing;
end;





procedure THSLchangeForm1.SpinEdit1Change(Sender: TObject);
begin
if (Sender as TSpinEdit).Text='' then (Sender as TSpinEdit).Text:='0';
ExtTrackbar1.Position:=Spinedit1.Value;
ExtTrackbar2.Position:=Spinedit2.Value;
HSLProcessing;
end;

procedure THSLchangeForm1.ExtTrackBar1MouseUp(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if (Sender as TExtTrackBar).Position<>(Sender as TExtTrackBar).Tag then
begin
// (Sender as TExtTrackBar).Tag:=(Sender as TExtTrackBar).Position;
HSLProcessing
end;
end;

procedure THSLchangeForm1.ExtTrackBar1MouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
ExtTrackBar1.Tag :=ExtTrackBar1.Position;
ExtTrackBar2.Tag :=ExtTrackBar2.Position;
(Sender as TExtTrackBar).Position := (x-10) * (((Sender as TExtTrackBar).Max-(Sender as TExtTrackBar).Min)) div (Sender as TExtTrackBar).width * 116 div 100+(Sender as TExtTrackBar).min;

end;

procedure THSLchangeForm1.ExtTrackBar1Change(Sender: TObject);
begin
SpinEdit1.Value:=ExtTrackBar1.Position;
SpinEdit2.Value:=ExtTrackBar2.Position;
end;


procedure THSLchangeForm1.RedTrackMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
(Sender as TExtTrackBar).Tag:=(Sender as TExtTrackBar).Position;
end;

procedure THSLchangeForm1.RedTrackMouseUp(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if (Sender as TExtTrackBar).Position<>(Sender as TExtTrackBar).Tag then
begin
(Sender as TExtTrackBar).Tag:=(Sender as TExtTrackBar).Position;
end;
end;


procedure THSLchangeForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
if Key =#13 then
begin
end;
end;


procedure THSLchangeForm1.LigCont;
var TempBitmap:TBitmap;
begin
TempBitmap:=TBitmap.Create;
try

EffectContrast(PreviewPanel1.OriginalImage.Picture.Bitmap,PreviewPanel1.PreviewImage.Picture.Bitmap,
ExtTrackBar2.Position,true,true,true,nil);
TempBitmap.Assign(PreviewPanel1.PreviewImage.Picture.Bitmap);
EffectLightness(TempBitmap,PreviewPanel1.PreviewImage.Picture.Bitmap,
ExtTrackBar1.Position,true,true,true,nil);
PreviewPanel1.PreviewImage.Repaint;

finally
TempBitmap.Free;
end;
end;

procedure THSLchangeForm1.Button3Click(Sender: TObject);
begin
// EffectColorize(PreviewPanel1.SourceBitmap,ResultBitmap,
// ExtTrackBar1.Position,ExtTrackBar2.Position,ExtTrackBar3.Position,nil);
FormMain.image1.Picture.bitmap.Assign(PreviewPanel1.SourceBitmap);
close;
end;

procedure THSLchangeForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if key =VK_ESCAPE then close;
if ssCtrl in Shift then
begin
if (key =ord('0')) then
ComboBox1.Text := ComboBox1.Items[0];
if (key =VK_NUMPAD1) or (key =ord('1')) then
ComboBox1.Text := ComboBox1.Items[1];
if (key =VK_NUMPAD2) or (key =ord('2')) then
ComboBox1.Text := ComboBox1.Items[2];
if (key =VK_NUMPAD3) or (key =ord('3')) then
ComboBox1.Text := ComboBox1.Items[3];
if (key =VK_NUMPAD4) or (key =ord('4')) then
ComboBox1.Text := ComboBox1.Items[4];
if (key =VK_NUMPAD5) or (key =ord('5')) then
ComboBox1.Text := ComboBox1.Items[5];
if (key =VK_NUMPAD6) or (key =ord('6')) then
ComboBox1.Text := ComboBox1.Items[6];
if (key =ord('Z')) then
begin
nOldPositon[1]:= ExtTrackBar1.Position;
nOldPositon[2]:= ExtTrackBar2.Position;

ExtTrackBar1.Position :=ExtTrackBar1.Tag;
ExtTrackBar2.Position :=ExtTrackBar2.Tag;

ExtTrackBar1.Tag := nOldPositon[1];
ExtTrackBar2.Tag := nOldPositon[2];

end;


end;

// if ssCtrl in Shift then
// begin
// end;


end;

procedure THSLchangeForm1.ButtonSaveClick(Sender: TObject);
Var
FileExt: string[4];
// DelphiIni: TIniFile;
begin
if SaveDialog1.Execute then { Display Open dialog box }
begin
htwIniWriteString(SaveDialog1.Filename,'AHU','H', IntToStr(ExtTrackBar1.Position));
htwIniWriteString(SaveDialog1.Filename,'AHU','S', IntToStr(ExtTrackBar2.Position));
ShowMessage('当前设置已经保存在文件 '+SaveDialog1.Filename+' 中了。');
end;
// WritePrivateProfileString(
// 'AHU', // []中标题的名字
// 'S', // 要写入"="号前的字符串
// IntToStr(ExtTrackBar1.Position), //要写入的数据
// 'c:/forwin95/win.ini' // 调用的文件名
//);

end;


procedure THSLchangeForm1.HSLProcessing();
var
tb:TBitmap;
begin
// EffectColorize(PreviewPanel1.OriginalImage.Picture.Bitmap,PreviewPanel1.PreviewImage.Picture.Bitmap,
// ExtTrackBar1.Position,ExtTrackBar2.Position,ExtTrackBar3.Position,nil);
// PreviewPanel1.PreviewImage.Repaint;

Screen.Cursor:=crHourGlass;
try
if not PreviewPanel1.SourceBitmap.Empty then
begin
EffectColorize(PreviewPanel1.SourceBitmap,ResultBitmap,
ExtTrackBar1.Position,ExtTrackBar2.Position,0,nil);
// PreviewPanel1.SourceBitmap.Assign(ResultBitmap);
if CheckBox1.Checked =true then
FormMain.image1.Picture.bitmap.Assign(ResultBitmap);
end
finally
Screen.Cursor:=crDefault;
end;

end;

end.

 
下面是窗体文件, 文件名为hslchange1.dfm

object HSLchangeForm1: THSLchangeForm1
Left = 242
Top = 262
AutoScroll = False
Caption = '色调/饱和度调整'
ClientHeight = 133
ClientWidth = 391
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object ButtonSave: TButton
Left = 334
Top = 104
Width = 51
Height = 25
Caption = '保存'
TabOrder = 0
OnClick = ButtonSaveClick
end
object ButtonLoad: TButton
Left = 334
Top = 71
Width = 51
Height = 25
Caption = '载入'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -15
Font.Name = 'MS Sans Serif'
Font.Style = []
ParentFont = False
TabOrder = 1
OnClick = ButtonLoadClick
end
object Button3: TButton
Left = 336
Top = 39
Width = 49
Height = 25
Caption = '取消'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -15
Font.Name = 'MS Sans Serif'
Font.Style = []
ModalResult = 1
ParentFont = False
TabOrder = 3
OnClick = Button3Click
end
object Button1: TButton
Left = 336
Top = 6
Width = 49
Height = 25
Caption = '确定'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -15
Font.Name = 'MS Sans Serif'
Font.Style = []
ParentFont = False
TabOrder = 4
OnClick = Button1Click
end
object PreviewPanel1: TPreviewPanel
Left = 24
Top = 184
Width = 309
Height = 151
AutoSize = True
BevelInner = bvRaised
BevelOuter = bvLowered
Font.Charset = GB2312_CHARSET
Font.Color = clWindowText
Font.Height = -14
Font.Name = 'MS Sans Serif'
Font.Style = []
ParentFont = False
TabOrder = 2
Orientation = ppHorizontal
end
object Panel1: TPanel
Left = 8
Top = 2
Width = 321
Height = 127
TabOrder = 5
object Label1: TLabel
Left = 8
Top = 56
Width = 26
Height = 16
Caption = '色调'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -15
Font.Name = 'MS Sans Serif'
Font.Style = []
ParentFont = False
end
object Label2: TLabel
Left = 8
Top = 95
Width = 39
Height = 16
Caption = '饱和度'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -15
Font.Name = 'MS Sans Serif'
Font.Style = []
ParentFont = False
end
object Label4: TLabel
Left = 52
Top = 52
Width = 21
Height = 13
Caption = '-180'
end
object Label6: TLabel
Left = 224
Top = 52
Width = 18
Height = 13
Caption = '180'
end
object Label7: TLabel
Left = 52
Top = 92
Width = 21
Height = 13
Caption = '-255'
end
object Label8: TLabel
Left = 224
Top = 92
Width = 18
Height = 13
Caption = '255'
end
object Label10: TLabel
Left = 8
Top = 22
Width = 26
Height = 16
Caption = '编辑'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -15
Font.Name = 'MS Sans Serif'
Font.Style = []
ParentFont = False
end
object SpinEdit1: TSpinEdit
Left = 256
Top = 48
Width = 57
Height = 22
MaxValue = 180
MinValue = -180
TabOrder = 0
Value = 0
OnChange = SpinEdit1Change
end
object SpinEdit2: TSpinEdit
Left = 256
Top = 88
Width = 57
Height = 22
MaxValue = 255
MinValue = -255
TabOrder = 1
Value = 0
OnChange = SpinEdit1Change
end
object ExtTrackBar1: TExtTrackBar
Left = 80
Top = 51
Width = 145
Height = 25
Max = 180
Min = -180
Orientation = trHorizontal
Frequency = 10
Position = 0
SelEnd = 0
SelStart = 0
TabOrder = 2
ThumbLength = 15
TickMarks = tmBottomRight
TickStyle = tsAuto
OnChange = ExtTrackBar1Change
OnMouseDown = ExtTrackBar1MouseDown
OnMouseUp = ExtTrackBar1MouseUp
end
object ExtTrackBar2: TExtTrackBar
Left = 80
Top = 89
Width = 145
Height = 25
Max = 255
Min = -255
Orientation = trHorizontal
Frequency = 15
Position = 0
SelEnd = 0
SelStart = 0
TabOrder = 3
ThumbLength = 15
TickMarks = tmBottomRight
TickStyle = tsAuto
OnChange = ExtTrackBar1Change
OnMouseDown = ExtTrackBar1MouseDown
OnMouseUp = ExtTrackBar1MouseUp
end
object ComboBox1: TComboBox
Left = 56
Top = 16
Width = 137
Height = 21
ItemHeight = 13
TabOrder = 4
Text = '全图 Ctrl+0'
Items.Strings = (
'全图 Ctrl+0'
'红色 Ctrl+1'
'黄色 Ctrl+2'
'绿色 Ctrl+3'
'青色 Ctrl+4'
'蓝色 Ctrl+5'
'洋红 Ctrl+6'
'')
end
object CheckBox1: TCheckBox
Left = 240
Top = 16
Width = 73
Height = 17
Caption = '预览'
Checked = True
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -15
Font.Name = 'MS Sans Serif'
Font.Style = []
ParentFont = False
State = cbChecked
TabOrder = 5
end
end
object OpenDialog1: TOpenDialog
DefaultExt = '*.AHU'
Filter = '色调饱和度设置(*.AHU)|*.AHU'
Left = 280
Top = 65528
end
object SaveDialog1: TSaveDialog
DefaultExt = '*.AHU'
Filter = '色调饱和度设置(*.AHU)|*.AHU'
Left = 296
Top = 65528
end
end
 
UNIT hhx_effects;


INTERFACE

USES Windows, Graphics,classes,sysutils{,forms},comctrls,hhx_basedef,math;

TYPE

{definiton used by EffectSwapChannels}
TSwapPlanes= (swapNone,swapRedBlue,swapRedGreen,swapBlueGreen);

{definition used by EffectArithmetic}
TGraphicArithmetic=(gaNone,gaAdd,gaSubstract,gaMultiply,gaDivide,gaDarkest,
gaLightest,gaDifference,gaBinaryOr,gaBinaryAnd,gaAverage);

{Static filters}
TStaticFilterType=(sfMedian,sfMin,sfMax);

{Filter types}
TFilterType=(ftStatic,ftLinear,ftMultiPass);

{Matrix size (3x3, 5x5, 7x7}
TMatrixSize = (mxDefault,mx3,mx5,mx7);

{TEffectMatrix used by EffectLinearFilterRGB}
TEffectMatrix = array[0..6,0..6] of Integer;
pEffectMatrix = ^TEffectMatrix;

{TGraphicFilter used by EffectLinearFilterRGB /EffectStaticFilterRGB}
TGraphicFilter = packed record
FilterType: TFilterType;
MatrixSize: TMatrixSize; // Size: mx3=3x3, mx5=5x5, mx7=7x7
Matrix: TEffectMatrix; // Every pixel of source would be calculated by the matrix
Divisor: Integer; // The result of calculation would be divided by divisor (>0!)
Bias: Integer; // Finally bias value would be added
FilterName: Array [0..128] of Char;
end;
pGraphicFilter=^TGraphicFilter;

{TMultiPassGraphicFilter used by EffectMultiPassFilter}
TMultiPassGraphicFilter=packed record
FilterType:TFilterType;
Filters:array [1..4] of pGraphicFilter;
Functions:array [1..3] of TGraphicArithmetic;
FilterName:Array [0..100] of Char;
end;
pMultiPassGraphicFilter=^TMultiPassGraphicFilter;

type TDirection = (TopToBtm, BtmToTop, LftToRgt,RgtToLft,TopLftToBtmRgt, BtmLftToTopRgt, All);

TEffectCallBack = procedure (const Min,Max,Pos:Integer);

procedure EffectAddNoise(SrcBitmap,DestBitmap:TBitmap;Value:Integer;MonoNoise:Boolean;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectAntiAlias(SrcBitmap,DestBitmap:TBitmap;R,G,B:Boolean;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectArithmetic(SrcBitmapOne,SrcBitmapTwo,DestBitmap:TBitmap;Arithmetic:TGraphicArithmetic;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectBlend(SrcBitmapOne,SrcBitmapTwo,DestBitmap:TBitmap;Multiplicator,MaxBlendValue:Integer;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectCircleAround(SrcBitmap,DestBitmap:TBitmap;Value:Integer;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectColorize(SrcBitmap,DestBitmap:TBitmap;Hue,Saturation,Lightness:Integer;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectContrast(SrcBitmap,DestBitmap:TBitmap;Value:Integer;R,G,B:Boolean;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectEllipse(SrcBitmap,DestBitmap:TBitmap;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectFishEye(SrcBitmap,DestBitmap:TBitmap;Value:Extended;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectGamma(SrcBitmap,DestBitmap:TBitmap;Gamma:Double;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectGreyScale(SrcBitmap,DestBitmap:TBitmap;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectIncDecRGB(SrcBitmap,DestBitmap:TBitmap;dR,dG,dB:Integer;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectLightness(SrcBitmap,DestBitmap:TBitmap;Value:Integer;R,G,B:Boolean;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectMosaic(SrcBitmap,DestBitmap:TBitmap;Width,Height:Integer;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectNegative(SrcBitmap,DestBitmap:TBitmap;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectPosterize(SrcBitmap,DestBitmap:TBitmap;BitsPerChannel:Integer;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectSinus(SrcBitmap,DestBitmap:TBitmap;SinusAmpVert,VertDelta,SinusAmpHorz,HorzDelta,VertStart,HorzStart:Integer;ChngVertAtAnyCol:Boolean;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectSolarize(SrcBitmap,DestBitmap:TBitmap;Threshold:Integer;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectSpray(SrcBitmap,DestBitmap:TBitmap;Value:Integer;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectStretch(SrcBitmap,DestBitmap:TBitmap;Low,High:Integer;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectSwapChannels(SrcBitmap,DestBitmap:TBitmap;WhichPlanes:TSwapPlanes;const EffectCallBack:TEffectCallBack);stdcall;

{Filter functions: For universal processing use EffectFilter..the class looks for itself which filter will be used}
procedure EffectFilter(SrcBitmap,DestBitmap:TBitmap;Filter:TGraphicFilter;Size:TMatrixSize;ColorSpace:TColorSpace;Channel1,Channel2,Channel3:Boolean;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectStaticFilter(SrcBitmap,DestBitmap:TBitmap;StaticFilterType:TStaticFilterType;Diameter:Integer;R,G,B:Boolean;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectMultiPass(SrcBitmap,DestBitmap:TBitmap;MultiPassFilter:TMultiPassGraphicFilter;Passes:Integer;ColorSpace:TColorSpace;Channel1,Channel2,Channel3:Boolean;const EffectCallBack:TEffectCallBack);stdcall;
procedure EffectLinearFilter(SrcBitmap,DestBitmap:TBitmap;Filter:TGraphicFilter;ColorSpace:TColorSpace;Channel1,Channel2,Channel3:Boolean;const EffectCallBack:TEffectCallBack);stdcall;

procedure SaveLinearFilterToFile(FileName:PChar;const Filter:TGraphicFilter);stdcall;
procedure LoadLinearFilterFromFile(FileName:PChar;var Filter:TGraphicFilter);stdcall;
procedure SaveMultipassFilterToFile(FileName:PChar;const Filter:TMultiPassGraphicFilter);stdcall;
procedure LoadMultiPassFilterFromFile(FileName:PChar;var Filter:TMultiPassGraphicFilter);stdcall;



{Default Filter (NULL-Filter}
CONST mxZero:TGraphicFilter
=(FilterType:ftLinear;
MatrixSize:mx3;
Matrix:
(( 0, 0, 0, 0, 0, 0, 0),
( 0, 0, 0, 0, 0, 0, 0),
( 0, 0, 0, 0, 0, 0, 0),
( 0, 0, 0, 1, 0, 0, 0),
( 0, 0, 0, 0, 0, 0, 0),
( 0, 0, 0, 0, 0, 0, 0),
( 0, 0, 0, 0, 0, 0, 0));
Divisor:1;
Bias:0;
FilterName:'空值 (线性处理)');
// FilterName:'Null (Linear)');

{Default static filters}
mxMedian:TGraphicFilter
=(FilterType:ftStatic;
FilterName:'中值滤波 (静态)');
// FilterName:'Median (Static)');
mxMaximum:TGraphicFilter
=(FilterType:ftStatic;
FilterName:'最大值滤波 (静态)');
// FilterName:'Maximum (Static)');
mxMinimum:TGraphicFilter
=(FilterType:ftStatic;
FilterName:'最小值滤波 (静态)');
// FilterName:'Minimum (Static)');

IMPLEMENTATION

// -----------------------------------------------------------------------------
//
// Blending two Bitmaps
//
// Parameter:
// SrcBitmapOne : 1st Bitmap
// SrcBitmapOne : 2nd Bitmap
// DestBitmap : Result
// Multiplicator,
// MaxBlendValue : The mixing of the two pictures...
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------
procedure EffectBlend(SrcBitmapOne,SrcBitmapTwo,DestBitmap:TBitmap;Multiplicator,MaxBlendValue:Integer;const EffectCallBack:TEffectCallBack);stdcall;
var
bmpTwo :TBitmap;
r, n, l :Integer;
SrcRow1,SrcRow2,DestRow :pRGBArray;
begin
bmptwo:=TBitmap.Create;
try
BMPTwo.PixelFormat:=pf24Bit;
SetBitmapsEql(SrcBitmapOne,DestBitmap);
BmpTwo.Width:=SrcBitmapOne.Width;
BmpTwo.Height:=SrcBitmapOne.Height;
if SrcBitmapTwo.Empty = TRUE then
bmpTwo.Canvas.FillRect(Rect(0,0,SrcBitmapOne.Width,SrcBitmapOne.Height))
else
begin
bmpTwo.Canvas.StretchDraw(Rect(0,0,SrcBitmapOne.Width,SrcBitmapOne.Height),SrcBitmapTwo);
end;

if MaxBlendValue < 1 then raise EGraphicEffects.Create('BlendRate must be between 0 AND 256');

r:=Multiplicator;
for n := 0 to SrcBitmapOne.Height - 1 do
begin

if Assigned(EffectCallBack) then EffectCallBack(0,100,Round((n/SrcBitmapOne.Height)*100));

SrcRow1 := SrcBitmapOne.Scanline[n];
SrcRow2 := bmpTwo.Scanline[n];
DestRow := DestBitmap.Scanline[n];
for l := 0 to SrcBitmapOne.Width - 1 do WITH DestRow[l] do
begin
rgbtRed:=SrcRow1[l].rgbtRed+MulDiv(r, (SrcRow2[l].rgbtRed-SrcRow1[l].rgbtRed), MaxBlendValue);
rgbtGreen:=SrcRow1[l].rgbtGreen+MulDiv(r, (SrcRow2[l].rgbtGreen-SrcRow1[l].rgbtGreen), MaxBlendValue);
rgbtBlue:= SrcRow1[l].rgbtBlue+MulDiv(r, (SrcRow2[l].rgbtBlue-SrcRow1[l].rgbtBlue), MaxBlendValue);
end;
end;
finally
bmpTwo.Free;
end;
end;

// -----------------------------------------------------------------------------
//
// Arithmetic between two Bitmaps
//
// Parameter:
// SrcBitmapOne : 1st Bitmap
// SrcBitmapOne : 2nd Bitmap
// DestBitmap : Result
// Arithmetic : Arithmetic type
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------
procedure EffectArithmetic(SrcBitmapOne,SrcBitmapTwo,DestBitmap:TBitmap;Arithmetic:TGraphicArithmetic;const EffectCallBack:TEffectCallBack);stdcall;
var
bmpTwo :TBitmap;
Row, Col :Integer;
SrcRow1,SrcRow2,DestRow :pRGBArray;
begin
bmptwo:=TBitmap.Create;
try
SetBitmapsEql(SrcBitmapOne,DestBitmap);
BmpTwo.Width:=SrcBitmapOne.Width;
BmpTwo.Height:=SrcBitmapOne.Height;
BmpTwo.PixelFormat:=pf24Bit;
if SrcBitmapTwo.Empty = TRUE then bmpTwo.Canvas.FillRect(Rect(0,0,SrcBitmapOne.Width,SrcBitmapOne.Height))
ELSE bmpTwo.Canvas.StretchDraw(Rect(0,0,SrcBitmapOne.Width,SrcBitmapOne.Height),SrcBitmapTwo);
for Row := 0 to SrcBitmapOne.Height - 1 do
begin

if Assigned(EffectCallBack) then EffectCallBack(0,100,Round((Row/SrcBitmapOne.Height)*100));

SrcRow1 := SrcBitmapOne.Scanline[Row];
SrcRow2 := bmpTwo.Scanline[Row];
DestRow := DestBitmap.Scanline[Row];
for Col := 0 to SrcBitmapOne.Width - 1 do
begin
WITH DestRow[Col] do
CASE Arithmetic of
gaNone:begin
rgbtRed:=SrcRow1[Col].rgbtRed;
rgbtGreen:=SrcRow1[Col].rgbtGreen;
rgbtBlue:=SrcRow1[Col].rgbtBlue;
end;
gaAdd:begin
rgbtRed:=TrimInt(0,255,SrcRow1[Col].rgbtRed+SrcRow2[Col].rgbtRed);
rgbtGreen:=TrimInt(0,255,SrcRow1[Col].rgbtGreen+SrcRow2[Col].rgbtGreen);
rgbtBlue:=TrimInt(0,255,SrcRow1[Col].rgbtBlue+SrcRow2[Col].rgbtBlue);
end;
gaSubstract:begin
rgbtRed:=TrimInt(0,255,SrcRow1[Col].rgbtRed-SrcRow2[Col].rgbtRed);
rgbtGreen:=TrimInt(0,255,SrcRow1[Col].rgbtGreen-SrcRow2[Col].rgbtGreen);
rgbtBlue:=TrimInt(0,255,SrcRow1[Col].rgbtBlue-SrcRow2[Col].rgbtBlue);
end;
gaMultiply:begin
rgbtRed:=TrimInt(0,255,SrcRow1[Col].rgbtRed*SrcRow2[Col].rgbtRed);
rgbtGreen:=TrimInt(0,255,SrcRow1[Col].rgbtGreen*SrcRow2[Col].rgbtGreen);
rgbtBlue:=TrimInt(0,255,SrcRow1[Col].rgbtBlue*SrcRow2[Col].rgbtBlue);
end;
gaDivide:begin
if SrcRow2[Col].rgbtRed<>0 then rgbtRed:=TrimInt(0,255,SrcRow1[Col].rgbtRed DIV SrcRow2[Col].rgbtRed);
if SrcRow2[Col].rgbtGreen<>0 then rgbtGreen:=TrimInt(0,255,SrcRow1[Col].rgbtGreen DIV SrcRow2[Col].rgbtGreen);
if SrcRow2[Col].rgbtBlue<>0 then rgbtBlue:=TrimInt(0,255,SrcRow1[Col].rgbtBlue DIV SrcRow2[Col].rgbtBlue);
end;
gaDifference:begin
rgbtRed:=Abs(SrcRow1[Col].rgbtRed-SrcRow2[Col].rgbtRed);
rgbtGreen:=Abs(SrcRow1[Col].rgbtGreen-SrcRow2[Col].rgbtGreen);
rgbtBlue:=Abs(SrcRow1[Col].rgbtBlue-SrcRow2[Col].rgbtBlue);
end;
gaDarkest:begin
rgbtRed:=MinInt2(SrcRow1[Col].rgbtRed,SrcRow2[Col].rgbtRed);
rgbtGreen:=MinInt2(SrcRow1[Col].rgbtGreen,SrcRow2[Col].rgbtGreen);
rgbtBlue:=MinInt2(SrcRow1[Col].rgbtBlue,SrcRow2[Col].rgbtBlue);
end;
gaLightest:begin
rgbtRed:=MaxInt2(SrcRow1[Col].rgbtRed,SrcRow2[Col].rgbtRed);
rgbtGreen:=MaxInt2(SrcRow1[Col].rgbtGreen,SrcRow2[Col].rgbtGreen);
rgbtBlue:=MaxInt2(SrcRow1[Col].rgbtBlue,SrcRow2[Col].rgbtBlue);
end;
gaAverage:begin
rgbtRed:=TrimInt(0,255,(SrcRow1[Col].rgbtRed+SrcRow2[Col].rgbtRed)DIV 2);
rgbtGreen:=TrimInt(0,255,(SrcRow1[Col].rgbtGreen+SrcRow2[Col].rgbtGreen)DIV 2);
rgbtBlue:=TrimInt(0,255,(SrcRow1[Col].rgbtBlue+SrcRow2[Col].rgbtBlue)DIV 2);
end;
gaBinaryOr:begin
rgbtRed:=TrimInt(0,255,SrcRow1[Col].rgbtRed OR SrcRow2[Col].rgbtRed);
rgbtGreen:=TrimInt(0,255,SrcRow1[Col].rgbtGreen OR SrcRow2[Col].rgbtGreen);
rgbtBlue:=TrimInt(0,255,SrcRow1[Col].rgbtBlue OR SrcRow2[Col].rgbtBlue);
end;
gaBinaryAnd:begin
rgbtRed:=TrimInt(0,255,SrcRow1[Col].rgbtRed AND SrcRow2[Col].rgbtRed);
rgbtGreen:=TrimInt(0,255,SrcRow1[Col].rgbtGreen AND SrcRow2[Col].rgbtGreen);
rgbtBlue:=TrimInt(0,255,SrcRow1[Col].rgbtBlue AND SrcRow2[Col].rgbtBlue);
end;
end;
end;
end;
finally
bmpTwo.Free;
end;
end;

// -----------------------------------------------------------------------------
//
// Swap colorchannels
//
// Parameter:
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
// Which planes : see TSwapPlanes (at the top of this unit)
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------
procedure EffectSwapChannels(SrcBitmap,DestBitmap:TBitmap;WhichPlanes:TSwapPlanes;const EffectCallBack:TEffectCallBack);stdcall;
var Row,Col :Integer;
SrcRow,DestRow :pRGBArray;
begin
SetBitmapsEql(SrcBitmap,DestBitmap);
for Row:=0 to DestBitmap.Height-1 do
begin

if Assigned(EffectCallBack) then EffectCallBack(0,100,Round((Row/SrcBitmap.Height)*100));

SrcRow:=SrcBitmap.ScanLine[Row];
DestRow:=DestBitmap.ScanLine[Row];
for Col:=0 to DestBitmap.Width-1 do
begin
WITH DestRow[Col] do
CASE WhichPlanes of
swapRedGreen:begin
rgbtBlue:=SrcRow[Col].rgbtBlue;
rgbtGreen:=SrcRow[Col].rgbtRed;
rgbtRed:=SrcRow[Col].rgbtBlue;
end;
swapRedBlue:begin
rgbtBlue:=SrcRow[Col].rgbtRed;
rgbtGreen:=SrcRow[Col].rgbtGreen;
rgbtRed:=SrcRow[Col].rgbtBlue;
end;
swapBlueGreen:begin
rgbtBlue:=SrcRow[Col].rgbtGreen;
rgbtGreen:=SrcRow[Col].rgbtBlue;
rgbtRed:=SrcRow[Col].rgbtRed;
end;
end;
end;
end;
end;

// -----------------------------------------------------------------------------
//
// Greyscale Bitmap
//
// Parameter:
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------
procedure EffectGreyScale(SrcBitmap,DestBitmap:TBitmap;const EffectCallBack:TEffectCallBack);stdcall;
var Row,Col :Integer;
SrcRow,DestRow :pRGBArray;
begin
SetBitmapsEql(SrcBitmap,DestBitmap);
for Row:=0 to DestBitmap.Height-1 do
begin

if Assigned(EffectCallBack) then EffectCallBack(0,100,Round((Row/SrcBitmap.Height)*100));

SrcRow:=SrcBitmap.ScanLine[Row];
DestRow:=DestBitmap.ScanLine[Row];
for Col:=0 to DestBitmap.Width-1 do WITH DestRow[Col] do
begin
rgbtBlue:=RgbLightness(SrcRow[Col]);
rgbtGreen:=RgbLightness(SrcRow[Col]);
rgbtRed:=RgbLightness(SrcRow[Col]);
end;
end;
end;

// -----------------------------------------------------------------------------
//
// Posterize Bitmap
//
// Parameter:
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
// BitsPerChannel : How many Bits per channel
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------
procedure EffectPosterize(SrcBitmap,DestBitmap:TBitmap;BitsPerChannel:Integer;const EffectCallBack:TEffectCallBack);stdcall;
var Row,Col :Integer;
SrcRow,DestRow :pRGBArray;
Mask :Byte;
begin
if not BitsPerChannel in [1..8] then exit;
SetBitmapsEql(SrcBitmap,DestBitmap);
mask:=$FF;
CASE BitsPerChannel of
7:Mask:=$fe;
6:Mask:=$fc;
5:Mask:=$f8;
4:Mask:=$f0;
3:Mask:=$e0;
2:Mask:=$c0;
1:Mask:=$80;
end;
for Row:=0 to DestBitmap.Height-1 do
begin

if Assigned(EffectCallBack) then EffectCallBack(0,100,Round((Row/SrcBitmap.Height)*100));

SrcRow:=SrcBitmap.ScanLine[Row];
DestRow:=DestBitmap.ScanLine[Row];
for Col:=0 to DestBitmap.Width-1 do WITH DestRow[Col] do
begin
rgbtBlue:=(SrcRow[Col].rgbtBlue AND Mask);
rgbtGreen:=(SrcRow[Col].rgbtGreen AND Mask);
rgbtRed:=(SrcRow[Col].rgbtRed AND Mask);
end;
end;
end;

// -----------------------------------------------------------------------------
//
// Solarize Bitmap
//
// Parameter:
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
// Threshold : Process only pixels above this value
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------
procedure EffectSolarize(SrcBitmap,DestBitmap:TBitmap;Threshold:Integer;const EffectCallBack:TEffectCallBack);stdcall;
var Row,Col :Integer;
SrcRow,DestRow :pRGBArray;
begin
SetBitmapsEql(SrcBitmap,DestBitmap);
for Row:=0 to DestBitmap.Height-1 do
begin

if Assigned(EffectCallBack) then EffectCallBack(0,100,Round((Row/SrcBitmap.Height)*100));

SrcRow:=SrcBitmap.ScanLine[Row];
DestRow:=DestBitmap.ScanLine[Row];
for Col:=0 to DestBitmap.Width-1 do
begin
if SrcRow[Col].rgbtBlue>=Threshold then DestRow[Col].rgbtBlue:=not SrcRow[Col].rgbtBlue
else DestRow[Col].rgbtBlue:=SrcRow[Col].rgbtBlue;
if SrcRow[Col].rgbtGreen>=Threshold then DestRow[Col].rgbtGreen:=not SrcRow[Col].rgbtGreen
else DestRow[Col].rgbtGreen:=SrcRow[Col].rgbtGreen;
if SrcRow[Col].rgbtRed>=Threshold then DestRow[Col].rgbtRed:=not SrcRow[Col].rgbtRed
else DestRow[Col].rgbtRed:=SrcRow[Col].rgbtRed;

end;
end;
end;

// -----------------------------------------------------------------------------
//
// Negative Bitmap
//
// Parameter: (Remark: this is just the same as Solarize, just a threshold of zero)
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------
procedure EffectNegative(SrcBitmap,DestBitmap:TBitmap;const EffectCallBack:TEffectCallBack);stdcall;
begin
EffectSolarize(SrcBitmap,DestBitmap,0,EffectCallBack);
end;

// -----------------------------------------------------------------------------
//
// Lighten/Darken Bitmap
//
// Parameter:
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
// Value : Value to be added/substracted (-255..0..255)
// R,G,B : Apply filter to R,G,B Planes (Boolean)
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------
procedure EffectLightness(SrcBitmap,DestBitmap:TBitmap;Value:Integer;R,G,B:Boolean;const EffectCallBack:TEffectCallBack);stdcall;
var
Row,Col :Integer;
TargetRow :pRGBArray;
SourceRows :PPRows;
begin
GetMem(SourceRows, SrcBitmap.Height * SizeOf(pRGBArray));
try
SrcBitmap.PixelFormat:=pf24Bit;
DestBitmap.PixelFormat:=pf24Bit;
DestBitmap.Width:=SrcBitmap.Width;
DestBitmap.Height:=SrcBitmap.Height;

for Row:= 0 to SrcBitmap.Height - 1 do
SourceRows[Row]:=SrcBitmap.Scanline[Row];

for Row := 0 to SrcBitmap.Height - 1 do
begin

if Assigned(EffectCallBack) then EffectCallBack(0,100,Round((Row/SrcBitmap.Height)*100));

TargetRow := DestBitmap.Scanline[Row];
for Col := 0 to SrcBitmap.Width - 1 do
begin
if R then TargetRow[Col].rgbtRed :=TrimReal(0,255,(SourceRows[Row][Col].rgbtRed)+Value)ELSE TargetRow[Col].rgbtRed :=SourceRows[Row][Col].rgbtRed;
if G then TargetRow[Col].rgbtGreen :=TrimReal(0,255,(SourceRows[Row][Col].rgbtGreen)+Value) ELSE TargetRow[Col].rgbtGreen :=SourceRows[Row][Col].rgbtGreen;
if B then TargetRow[Col].rgbtBlue :=TrimReal(0,255,(SourceRows[Row][Col].rgbtBlue)+Value)ELSE TargetRow[Col].rgbtBlue :=SourceRows[Row][Col].rgbtBlue;
end;
end;
finally
FreeMem(SourceRows);
end;
end;

// -----------------------------------------------------------------------------
//
// Change Contrast
//
// Parameter:
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
// Value : Value to be added/substracted (-255..0..255)
// R,G,B : Apply filter to R,G,B Planes (Boolean)
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------
procedure EffectContrast(SrcBitmap,DestBitmap:TBitmap;Value:Integer;R,G,B:Boolean;const EffectCallBack:TEffectCallBack);stdcall;
var
i,Row,Col :Integer;
TargetRow :pRGBArray;
SourceRows :PPRows;
ColArray :Array [0..256] of Byte;
begin
for i:=0 to 126 do
begin
ColArray:=TrimInt(0,255,i-((Abs(128-i)*Value)div 256));
end;
for i:=127 to 255 do
begin
ColArray:=TrimInt(0,255,i+((Abs(128-i)*Value)div 256));
end;

GetMem(SourceRows, SrcBitmap.Height * SizeOf(pRGBArray));
try
SrcBitmap.PixelFormat:=pf24Bit;
DestBitmap.PixelFormat:=pf24Bit;
DestBitmap.Width:=SrcBitmap.Width;
DestBitmap.Height:=SrcBitmap.Height;

for Row:= 0 to SrcBitmap.Height - 1 do
SourceRows[Row]:=SrcBitmap.Scanline[Row];

for Row := 0 to SrcBitmap.Height - 1 do
begin

if Assigned(EffectCallBack) then EffectCallBack(0,100,Round((Row/SrcBitmap.Height)*100));

TargetRow := DestBitmap.Scanline[Row];
for Col := 0 to SrcBitmap.Width - 1 do
begin
if R then TargetRow[Col].rgbtRed :=ColArray[SourceRows[Row][Col].rgbtRed] else TargetRow[Col].rgbtRed:= SourceRows[Row][Col].rgbtRed;
if G then TargetRow[Col].rgbtGreen :=ColArray[SourceRows[Row][Col].rgbtGreen] else TargetRow[Col].rgbtGreen:= SourceRows[Row][Col].rgbtGreen;
if B then TargetRow[Col].rgbtBlue :=ColArray[SourceRows[Row][Col].rgbtBlue] else TargetRow[Col].rgbtBlue:= SourceRows[Row][Col].rgbtBlue;
end;
end;
finally
FreeMem(SourceRows);
end;
end;

// -----------------------------------------------------------------------------
//
// Colorize Bitmap
//
// Parameter:
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
// Hue : Value to be added to Hue (0..359)
// Saturation : Value to be added to Saturation (0..255)
// Value : Value to be added to Value (0..255)
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------
procedure EffectColorize(SrcBitmap,DestBitmap:TBitmap;Hue,Saturation,Lightness:Integer;const EffectCallBack:TEffectCallBack);stdcall;
var
Row,Col :Integer;
TargetRow :pRGBArray;
hsl :THSLTriple;
SourceRows :PPRows;
begin
GetMem(SourceRows, SrcBitmap.Height * SizeOf(pRGBArray));
try
SrcBitmap.PixelFormat:=pf24Bit;
DestBitmap.PixelFormat:=pf24Bit;
DestBitmap.Width:=SrcBitmap.Width;
DestBitmap.Height:=SrcBitmap.Height;

for Row:= 0 to SrcBitmap.Height - 1 do
SourceRows[Row]:=SrcBitmap.Scanline[Row];

for Row := 0 to SrcBitmap.Height - 1 do
begin

if Assigned(EffectCallBack) then EffectCallBack(0,100,Round((Row/SrcBitmap.Height)*100));

TargetRow := DestBitmap.Scanline[Row];
for Col := 0 to SrcBitmap.Width - 1 do
begin
RGBtoHSL(SourceRows[Row][Col],hSl);
hsl.hslHue:=hsl.hslHue+Hue;
hsl.hslSaturation:=hsl.hslSaturation+Saturation;
hsl.hslLightness:=hsl.hslLightness+Lightness;
hsl.hslHue:=LoopInt(0,359,hsl.hslHue);
hsl.hslSaturation:=TrimInt(0,255,hsl.hslSaturation);
hsl.hslLightness:=TrimInt(0,255,hsl.hslLightness);
HSLToRGB(hsl,TargetRow[Col]);
end;
end;
finally
FreeMem(SourceRows);
end;
end;

// -----------------------------------------------------------------------------
//
// IncDecRGB Bitmap
//
// Parameter:
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
// dR : Value to be added to Red (0..255)
// dG : Value to be added to Green (0..255)
// dB : Value to be added to Blue (0..255)
// EffectCallBack : CallBack for user interface
//
// (c) September 1998 by htw, amoser@amoser.de
// -----------------------------------------------------------------------------
procedure EffectIncDecRGB(SrcBitmap,DestBitmap:TBitmap;dR,dG,dB:Integer;const EffectCallBack:TEffectCallBack);stdcall;
var
Row,Col :Integer;
TargetRow :pRGBArray;
SourceRows :PPRows;
begin
GetMem(SourceRows, SrcBitmap.Height * SizeOf(pRGBArray));
try
SrcBitmap.PixelFormat:=pf24Bit;
DestBitmap.PixelFormat:=pf24Bit;
DestBitmap.Width:=SrcBitmap.Width;
DestBitmap.Height:=SrcBitmap.Height;

for Row:= 0 to SrcBitmap.Height - 1 do
SourceRows[Row]:=SrcBitmap.Scanline[Row];

for Row := 0 to SrcBitmap.Height - 1 do
begin

if Assigned(EffectCallBack) then EffectCallBack(0,100,Round((Row/SrcBitmap.Height)*100));

TargetRow := DestBitmap.Scanline[Row];
for Col := 0 to SrcBitmap.Width - 1 do
begin
TargetRow[Col].rgbtRed :=TrimReal(0,255,(SourceRows[Row][Col].rgbtRed)+dR);
TargetRow[Col].rgbtGreen :=TrimReal(0,255,(SourceRows[Row][Col].rgbtGreen)+dG);
TargetRow[Col].rgbtBlue :=TrimReal(0,255,(SourceRows[Row][Col].rgbtBlue)+dB);
end;
end;
finally
FreeMem(SourceRows);
end;
end;

// -----------------------------------------------------------------------------
//
// Apply filter to Bitmap
// (just an interface for the 3 following procedures)
//
// Parameter:
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
// Size : Optional:Size of the calculated area 1..7 (neighbours of the pixel)
// if set to 0, the default size for the filter will be used
// (normally for linear 3x3, for static filters a diameter of 3)
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------
procedure EffectFilter(SrcBitmap,DestBitmap:TBitmap;Filter:TGraphicFilter;Size:TMatrixSize;ColorSpace:TColorSpace;Channel1,Channel2,Channel3:Boolean;const EffectCallBack:TEffectCallBack);stdcall;
var d:Integer;
sft:TStaticFilterType;
begin
CASE Filter.FilterType of
ftLinear:begin
if Size > mxDefault then
Filter.MatrixSize:=Size;
EffectLinearFilter(SrcBitmap,DestBitmap,Filter,ColorSpace,Channel1,Channel2,Channel3,EffectCallBack);
end;
ftStatic:begin
d:=3;
CASE Size of
mx3:d:=3;
mx5:d:=5;
mx7:d:=7;
end;
if Filter.FilterName='Minimum (Static)' then sft:=sfMin ELSE if
Filter.FilterName='Maximum (Static)' then sft:=sfMax ELSE sft:=sfMedian;
EffectStaticFilter(SrcBitmap,DestBitmap,sft,d,Channel1,Channel2,Channel3,EffectCallBack);
end
end;
end;

// -----------------------------------------------------------------------------
//
// Apply Median static filter to Bitmap
//
// Parameter:
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
// Diameter : Size of the calculated area 1..7 (neighbours of the pixel)
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------
procedure EffectStaticFilter(SrcBitmap,DestBitmap:TBitmap;StaticFilterType:TStaticFilterType;Diameter:Integer;R,G,B:Boolean;const EffectCallBack:TEffectCallBack);stdcall;
var
row,col :Integer;
k,i :Integer;
Medium,Min,Max :Integer;
m,mx,my :Integer;
HorzLine,VertLine :Integer;
MaxCol,MaxRow :Integer;
MinCol,MinRow :Integer;
SourceRows :PPRows;
TargetRow :pRGBArray;
lLightness :array[0..6,0..6] of Integer;
BorderDiffX,BorderDiffY :Integer;

begin
if Diameter in [3,5,7] then
begin
Diameter:=Diameter DIV 2;
SetBitmapsEql(SrcBitmap,DestBitmap);
GetMem(SourceRows, SrcBitmap.Height * SizeOf(pRGBArray));
try
for Row:= 0 to SrcBitmap.Height - 1 do SourceRows[Row]:=SrcBitmap.Scanline[Row];
for Row := 0 to SrcBitmap.Height - 1 do
begin

if Assigned(EffectCallBack) then EffectCallBack(0,100,Round((Row/SrcBitmap.Height)*100));

SourceRows[Row]:=SrcBitmap.Scanline[Row];
TargetRow := DestBitmap.Scanline[Row];
for Col := 0 to SrcBitmap.Width - 1 do
begin
MinRow:=-Diameter;
MaxRow:=Diameter;
MinCol:=-Diameter;
Maxcol:=Diameter;
for HorzLine:=MinRow to MaxRow do
begin
{To handle the borders of a Bitmap i use the variable BorderDiff.
In case of a borderpixel the pixelarray will be mirrored to the "illegal" coordinates}
if ((Row<= Diameter) AND (Horzline<0)) OR((Row>= SrcBitmap.Height-1-Diameter) AND (Horzline>0))
then BorderDiffY:=-1 ELSE BorderDiffY:=1;
for VertLine:=MinCol to MaxCol do
begin
if ((Col<= Diameter) AND (Vertline<0)) OR ((Col>= SrcBitmap.Width-1-Diameter) AND (Vertline>0))
then BorderDiffX:=-1 ELSE
BorderDiffX:=1;
lLightness[Horzline+3,Vertline+3]:=RGBLightness(SourceRows[Row+(HorzLine*BorderDiffY)][Col+(VertLine*BorderDiffX)]);
end;
end;
mx:=0;my:=0;
Min:=0;Max:=255;Medium:=lLightness[3,3];
for k:=MinRow to MaxRow do
begin
for i:=MinCol to MaxCol do
begin
m:=lLightness[k+3][i+3];
CASE StaticFilterType of
sfMedian:begin
if (m>Min) AND (m<Max) then
begin
Medium:=m;
mx:=k;my:=i;
end ELSE if (m>Min) AND (m<Medium) then Min:=m ELSE
if (m<Max) AND (m>Medium) then Max:=m;
end;
sfMax: begin
if m>Min then begin
Min:=m;
mx:=k;my:=i;
end;
end;
sfMin: begin
if m<Max then begin
Max:=m;
mx:=k;my:=i;
end;
end;
end;
end;
end;
if ((Row<= Diameter) AND (mx<0)) OR((Row>= SrcBitmap.Height-1-Diameter) AND (mx>0))
then BorderDiffY:=-1 ELSE BorderDiffY:=1;
if ((Col<= Diameter) AND (my<0)) OR ((Col>= SrcBitmap.Width-1-Diameter) AND (my>0))
then BorderDiffX:=-1 ELSE
BorderDiffX:=1;

if b then TargetRow[Col].rgbtBlue :=SourceRows[Row+(mx*BorderDiffY)][Col+(my*BorderDiffX)].rgbtBlue;
if r then TargetRow[Col].rgbtRed :=SourceRows[Row+(mx*BorderDiffY)][Col+(my*BorderDiffX)].rgbtRed;
if g then TargetRow[Col].rgbtGreen :=SourceRows[Row+(mx*BorderDiffY)][Col+(my*BorderDiffX)].rgbtGreen;
end;
end;
finally
FreeMem(SourceRows);
end;
end;
end;

// -----------------------------------------------------------------------------
//
// Apply linear filter to Bitmap (RGB,HSV,HSL)
//
// Parameter:
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
// Filter : Filter (see TGraphicFilter at the top of this unit)
// ColorSpace : Which colorspase should be used (csRGB,csHSV,csHSL)
// Channel 1-3 : Apply filter to Channel 1 -3
// csRGB R:Channel1 G:Channel 2 B: Channel 3
// csHSV H:Channel1 S:Channel 2 V: Channel 3
// csHSL H:Channel1 S:Channel 2 L: Channel 3
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------
procedure EffectLinearFilter(SrcBitmap,DestBitmap:TBitmap;Filter:TGraphicFilter;ColorSpace:TColorSpace;Channel1,Channel2,Channel3:Boolean;const EffectCallBack:TEffectCallBack);stdcall;
var
row,col :Integer;
i,mxCount :Integer;
HorzLine,VertLine :Integer;
MaxCol,MaxRow :Integer;
MinCol,MinRow :Integer;
TargetRow :pRGBArray;
Val1,Val2,Val3 :array[0..6] of Integer;
Val1Sum,Val2Sum,Val3Sum :Integer;
h1,s1,vl1 :Integer;
hsv :THSVTriple;
hsl :THSLTriple;
SourceRows :PPRows;
BorderDiffX,BorderDiffY :Integer;
begin
if (@Filter=nil) OR (Filter.Divisor=0) OR (Filter.FilterType<>ftLinear) then
begin
DestBitmap.Assign(SrcBitmap);
Exit;
end;
mxCount:=1;
CASE Filter.MatrixSize of
mx3:mxCount:=1;
mx5:mxCount:=2;
mx7:mxCount:=3;
end;
SetBitmapsEql(SrcBitmap,DestBitmap);
GetMem(SourceRows, SrcBitmap.Height * SizeOf(pRGBArray));
try
for Row:= 0 to SrcBitmap.Height - 1 do SourceRows[Row]:=SrcBitmap.Scanline[Row];
for Row := 0 to SrcBitmap.Height - 1 do
begin

if Assigned(EffectCallBack) then EffectCallBack(0,100,Round((Row/SrcBitmap.Height)*100));

TargetRow := DestBitmap.Scanline[Row];
for Col := 0 to SrcBitmap.Width - 1 do
begin
Val1Sum:=0;
Val2Sum:=0;
Val3Sum:=0;
MinRow:=-mxCount;
MaxRow:=mxCount;
MinCol:=-mxCount;
Maxcol:=mxCount;
for i:= MinRow to MaxRow do
begin
if Channel1 then Val1[i+3]:=0;
if Channel2 then Val2[i+3]:=0;
if Channel3 then Val3[i+3]:=0;
end;
for HorzLine:=MinRow to MaxRow do
begin
{to handle the borders of a Bitmap i use the variable BorderDiff.
In case of a borderpixel the pixelarray will be mirrored to the "illegal" coordinates}
if ((Row<= mxCount) AND (Horzline<0)) OR((Row>= SrcBitmap.Height-1-mxCount) AND (Horzline>0))
then BorderDiffY:=-1 ELSE BorderDiffY:=1;
for VertLine:=MinCol to MaxCol do
begin
if ((Col<= mxCount) AND (Vertline<0)) OR ((Col>= SrcBitmap.Width-1-mxCount) AND (Vertline>0))
then BorderDiffX:=-1 ELSE
BorderDiffX:=1;
CASE ColorSpace of
csRGB:begin
if Channel1 then Val1[HorzLine+3]:=Val1[HorzLine+3]+(SourceRows[Row+HorzLine*BorderDiffY][Col+VertLine*BorderDiffX].rgbtRed*Filter.Matrix[HorzLine+3,VertLine+3]);
if Channel2 then Val2[HorzLine+3]:=Val2[HorzLine+3]+(SourceRows[Row+HorzLine*BorderDiffY][Col+VertLine*BorderDiffX].rgbtGreen*Filter.Matrix[HorzLine+3,VertLine+3]);
if Channel3 then Val3[Horzline+3]:=Val3[HorzLine+3]+(SourceRows[Row+HorzLine*BorderDiffY][Col+VertLine*BorderDiffX].rgbtBlue*Filter.Matrix[HorzLine+3,VertLine+3]);
end;
csHSV,csHSL:begin
case ColorSpace of
csHSV:begin
RGBtoHSV(SourceRows[Row+HorzLine*BorderDiffY][Col+VertLine*BorderDiffX],hsv);
if Channel1 then Val1[HorzLine+3]:=Val1[HorzLine+3]+(hsv.hsvHue*Filter.Matrix[HorzLine+3,VertLine+3])
ELSE Val1[HorzLine+3]:=hsv.hsvHue;
if Channel2 then Val2[HorzLine+3]:=Val2[HorzLine+3]+(hsv.hsvSaturation*Filter.Matrix[HorzLine+3,VertLine+3])
ELSE Val2[HorzLine+3]:=hsv.hsvSaturation;
if Channel3 then Val3[Horzline+3]:=Val3[HorzLine+3]+(hsv.hsvValue*Filter.Matrix[HorzLine+3,VertLine+3])
ELSE Val3[HorzLine+3]:=hsv.hsvValue;
end;
csHSL:begin
RGBtoHSL(SourceRows[Row+HorzLine*BorderDiffY][Col+VertLine*BorderDiffX],hsl);
if Channel1 then Val1[HorzLine+3]:=Val1[HorzLine+3]+(hsl.hslHue*Filter.Matrix[HorzLine+3,VertLine+3])
ELSE Val1[HorzLine+3]:=hsl.hslHue;
if Channel2 then Val2[HorzLine+3]:=Val2[HorzLine+3]+(hsl.hslSaturation*Filter.Matrix[HorzLine+3,VertLine+3])
ELSE Val2[HorzLine+3]:=hsl.hslSaturation;
if Channel3 then Val3[Horzline+3]:=Val3[HorzLine+3]+(hsl.hslLightness*Filter.Matrix[HorzLine+3,VertLine+3])
ELSE Val3[HorzLine+3]:=hsl.hslLightness;
end;
end;
end;
end;
end;
end;
for i:=MinRow to MaxRow do
begin
if Channel1 then Val1Sum:=Val1Sum+Val1[i+3] else Val1Sum:=Val1[3];
if Channel2 then Val2Sum:=Val2Sum+Val2[i+3] else Val2Sum:=Val2[3];
if Channel3 then Val3Sum:=Val3Sum+Val3[i+3] else Val3Sum:=Val3[3];
end;
CASE ColorSpace of
csRGB:begin
if Channel1 then TargetRow[Col].rgbtRed := TrimReal(0,255,(Val1Sum*(1/Filter.Divisor))+Filter.Bias)
ELSE TargetRow[Col].rgbtRed:=SourceRows[Row][Col].rgbtRed;
if Channel2 then TargetRow[Col].rgbtGreen :=TrimReal(0,255,(Val2Sum*(1/Filter.Divisor))+Filter.Bias)
ELSE TargetRow[Col].rgbtGreen:=SourceRows[Row][Col].rgbtGreen;
if Channel3 then TargetRow[Col].rgbtBlue :=TrimReal(0,255,(Val3Sum*(1/Filter.Divisor))+Filter.Bias)
ELSE TargetRow[Col].rgbtBlue:=SourceRows[Row][Col].rgbtBlue;
end;
csHSV:begin
HSV.hsvHue:= LoopReal(0,359,((1/Filter.Divisor)*Val1Sum)+Filter.Bias);
HSV.hsvSaturation:= TrimReal(0,255,((1/Filter.Divisor)*Val2Sum)+Filter.Bias);
HSV.hsvValue:= TrimReal(0,255,((1/Filter.Divisor)*Val3Sum)+Filter.Bias);
HSVToRGB(HSV,TargetRow[Col]);
end;
csHSL:begin
HSL.hslHue:= LoopReal(0,359,((1/Filter.Divisor)*Val1Sum)+Filter.Bias);
HSL.hslSaturation:= TrimReal(0,255,((1/Filter.Divisor)*Val2Sum)+Filter.Bias);
HSL.hslLightness:= TrimReal(0,255,((1/Filter.Divisor)*Val3Sum)+Filter.Bias);
HSLToRGB(HSL,TargetRow[Col]);
end;
end;
end;
end;
finally
FreeMem(SourceRows);
end;
end;



// -----------------------------------------------------------------------------
//
// Apply multi-pass filter to Bitmap (RGB-Model)
//
// Parameter:
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
// MultiPassFilter : MultiPassFilter (see TMultiPassGraphicFilter at the top of this unit)
// R,G,B : Apply filter to R,G,B Planes (Boolean)
// Passes : Nr of filters to be applied
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------
procedure EffectMultiPass(SrcBitmap,DestBitmap:TBitmap;MultiPassFilter:TMultiPassGraphicFilter;Passes:Integer;ColorSpace:TColorSpace;Channel1,Channel2,Channel3:Boolean;const EffectCallBack:TEffectCallBack);stdcall;
var Bmp1,Bmp2,Bmp3:TBitmap;

procedure ClearBitmap(Bitmap:TBitmap);
begin
Bitmap.Canvas.Brush.Color:=clWhite;
Bitmap.Canvas.FillRect(Rect(0,0,Bitmap.Width,Bitmap.Height));
end;

begin
if not (passes in [1..4]) OR (MultiPassFilter.FilterType<>ftMultiPass) then exit;
SetBitmapsEql(SrcBitmap,DestBitmap);
Bmp1:=TBitmap.Create;
Bmp2:=TBitmap.Create;
Bmp3:=TBitmap.Create;
Bmp1.PixelFormat:=pf24Bit;
Bmp2.PixelFormat:=pf24Bit;
Bmp3.PixelFormat:=pf24Bit;
Bmp1.Assign(SrcBitmap);
try
if Passes>=2 then
begin
EffectFilter(Bmp1,Bmp2,MultiPassFilter.Filters[1]^,mxDefault,ColorSpace,Channel1,Channel2,Channel3,EffectCallBack);
EffectFilter(Bmp2,Bmp3,MultiPassFilter.Filters[2]^,mxDefault,ColorSpace,Channel1,Channel2,Channel3,EffectCallBack);
ClearBitmap(Bmp1);
if MultiPassFilter.Functions[1]<>gaNone then EffectArithmetic(bmp2,bmp3,bmp1,MultiPassFilter.Functions[1],EffectCallBack)
ELSE Bmp1.Assign(Bmp3);
ClearBitmap(Bmp2);
ClearBitmap(Bmp3);
if Passes>=3 then
begin
EffectFilter(Bmp1,Bmp2,MultiPassFilter.Filters[3]^,mxDefault,ColorSpace,Channel1,Channel2,Channel3,EffectCallBack);
if MultiPassFilter.Functions[2]<>gaNone then
begin
EffectArithmetic(bmp1,bmp2,bmp3,MultiPassFilter.Functions[2],EffectCallBack);
ClearBitmap(Bmp1);
Bmp1.Assign(Bmp3);
end ELSE
begin
ClearBitmap(Bmp1);
Bmp1.Assign(Bmp2);
end;
ClearBitmap(Bmp3);
ClearBitmap(Bmp2);
if Passes = 4 then
begin
EffectFilter(Bmp1,Bmp2,MultiPassFilter.Filters[4]^,mxDefault,ColorSpace,Channel1,Channel2,Channel3,EffectCallBack);
if MultiPassFilter.Functions[3]<>gaNone then
begin
EffectArithmetic(bmp1,bmp2,bmp3,MultiPassFilter.Functions[3],EffectCallBack);
ClearBitmap(Bmp1);
Bmp1.Assign(Bmp3);
end ELSE
begin
ClearBitmap(Bmp1);
Bmp1.Assign(Bmp2);
end;
ClearBitmap(Bmp3);
ClearBitmap(Bmp2);
end;
end;
end;
DestBitmap.Assign(Bmp1);
finally
Bmp1.Free;
Bmp2.Free;
Bmp3.Free;
end;
end;

// -----------------------------------------------------------------------------
//
// Sinus effect
//
// Parameter:
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
// SinusAmpVer : Amplitude vertical
// VertDelta : Delta vertical
// SinusAmpHorz : Amplitude horizontal
// HorzDleta : Delta horizontal
// VertStart : Vertical start
// VertStart : Vertical start
// R,G,B : Apply antialias to R,G,B Planes (Boolean)
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------

procedure EffectSinus(SrcBitmap,DestBitmap:TBitmap;SinusAmpVert,VertDelta,SinusAmpHorz,HorzDelta,
VertStart,HorzStart:Integer;
ChngVertAtAnyCol:Boolean;const EffectCallBack:TEffectCallBack);stdcall;
var
Row,Col,s,ds,t,dt :Integer;
TargetRow :pRGBArray;
SourceRows :PPRows;
begin
GetMem(SourceRows, SrcBitmap.Height * SizeOf(pRGBArray));
try
dt:=HorzStart;

if VertDelta=0 then VertDelta:=1;
if HorzDelta=0 then HorzDelta:=1;

SrcBitmap.PixelFormat:=pf24Bit;
DestBitmap.PixelFormat:=pf24Bit;
DestBitmap.Width:=SrcBitmap.Width;
DestBitmap.Height:=SrcBitmap.Height;

for Row:= 0 to SrcBitmap.Height - 1 do
SourceRows[Row]:=SrcBitmap.Scanline[Row];

for Row := 0 to SrcBitmap.Height - 1 do
begin
if not ChngVertAtAnyCol then if dt/HorzDelta<360 then inc(dt,1) else dt:=0;

if Assigned(EffectCallBack) then EffectCallBack(0,100,Round((Row/SrcBitmap.Height)*100));

TargetRow := DestBitmap.Scanline[Row];
ds:=VertStart;
for Col := 0 to SrcBitmap.Width - 1 do
begin
s:=Round(SinusAmpVert*Sin(ds / VertDelta));
if ds/VertDelta<360 then inc(ds,1) else ds:=0;
if ChngVertAtAnyCol then if dt/HorzDelta<360 then inc(dt,1) else dt:=0;
if (Row+s<0) or (Row+s> SrcBitmap.Height - 1 )then s:=0;
t:=Round(SinusAmpHorz*Sin(dt / HorzDelta));
if (Col+t<0) or (Col+t> SrcBitmap.Width - 1 )then t:=0;
TargetRow[Col] := SourceRows[Row+S][Col+T];
end;
end;
finally
FreeMem(SourceRows);
end;
end;

// -----------------------------------------------------------------------------
//
// Spray effect
//
// Parameter:
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
// Value : Intensity
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------

procedure EffectSpray(SrcBitmap,DestBitmap:TBitmap;Value:Integer;const EffectCallBack:TEffectCallBack);stdcall;
var
TargetRow :pRGBArray;
SourceRows :PPRows;
Row,Col,fCol,fRow,f :Integer;
begin

GetMem(SourceRows, SrcBitmap.Height * SizeOf(pRGBArray));
for Row:= 0 to SrcBitmap.Height - 1 do SourceRows[Row]:=SrcBitmap.Scanline[Row];
try
SetBitmapsEql(SrcBitmap,DestBitmap);
for Row:=0 to DestBitmap.Height-1 do
begin

if Assigned(EffectCallBack) then EffectCallBack(0,100,Round((Row/SrcBitmap.Height)*100));

TargetRow:=DestBitmap.ScanLine[Row];
for Col:=0 to DestBitmap.Width-1 do begin
f:=Random(Value);
fCol:=Col+f-Random(f*2);
fRow:=Row+f-Random(f*2);
if(fCOl>-1)and(fCol<SrcBitmap.Width-1)and(fRow>-1)and(fRow<SrcBitmap.Height-1)then
begin
TargetRow[Col].rgbtRed:=SourceRows[fRow][fCol].rgbtRed;
TargetRow[Col].rgbtBlue:=SourceRows[fRow][fCol].rgbtBlue;
TargetRow[Col].rgbtGreen:=SourceRows[fRow][fCol].rgbtGreen;
end;
end;
end;
finally
FreeMem(SourceRows);
end;
end;

// -----------------------------------------------------------------------------
//
// Add noise
//
// Parameter:
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
// MonoNoise : Should noisevalue calculated for each plane or not
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------

procedure EffectAddNoise(SrcBitmap,DestBitmap:TBitmap;Value:Integer;MonoNoise:Boolean;const EffectCallBack:TEffectCallBack);stdcall;
var
TargetRow :pRGBArray;
SourceRow :pRGBArray;
Row,Col,f :Integer;
function Calculate(V:Integer):Integer;
begin
result:=Random(Value)-(Value shr 1);
end;
begin
SetBitmapsEql(SrcBitmap,DestBitmap);
for Row:=0 to DestBitmap.Height-1 do
begin

if Assigned(EffectCallBack) then EffectCallBack(0,100,Round((Row/TBitmap(@SrcBitmap).Height)*100));

TargetRow:=DestBitmap.ScanLine[Row];
SourceRow:=SrcBitmap.ScanLine[Row];
for Col:=0 to DestBitmap.Width-1 do begin
f:=Calculate(Value);
TargetRow[Col].rgbtRed:=TrimInt(0,255,SourceRow[Col].rgbtRed+f);
if (not MonoNoise) then f:=Calculate(Value);
TargetRow[Col].rgbtBlue:=TrimInt(0,255,SourceRow[Col].rgbtBlue+f);
if (not MonoNoise) then f:=Calculate(Value);
TargetRow[Col].rgbtGreen:=TrimInt(0,255,SourceRow[Col].rgbtGreen+f);
end;
end;
end;

// -----------------------------------------------------------------------------
//
// Anti-aliasing
//
// Parameter:
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
// R,G,B : Apply antialias to R,G,B Planes (Boolean)
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------

procedure EffectAntiAlias(SrcBitmap,DestBitmap:TBitmap;R,G,B:Boolean;const EffectCallBack:TEffectCallBack);stdcall;
var
SourceRows :PPRows;
TargetRow :pRGBArray;
nbhood :array[0..3] of TRGBTriple;
rr,gg,bb :Integer;
Row,Col,i :Integer;
begin

SetBitmapsEql(SrcBitmap,DestBitmap);
GetMem(SourceRows, SrcBitmap.Height * SizeOf(pRGBArray));
try
for Row:= 0 to SrcBitmap.Height - 1 do SourceRows[Row]:=SrcBitmap.Scanline[Row];
for Row := 0 to SrcBitmap.Height - 1 do
begin

if Assigned(EffectCallBack) then EffectCallBack(0,100,Round((row/SrcBitmap.Height)*100));

TargetRow:=DestBitmap.Scanline[Row];
for Col := 0 to SrcBitmap.Width - 1 do
begin
if Col>0 then nbhood[0]:=SourceRows[Row][Col-1] else nbhood[0]:=SourceRows[Row][Col+1];
if Col<SrcBitmap.Width-1 then nbhood[1]:=SourceRows[Row][Col+1] else nbhood[1]:=SourceRows[Row][Col-1];
if Row>0 then nbhood[2]:=SourceRows[Row-1][Col] else nbhood[2]:=SourceRows[Row+1][Col];
if Row<SrcBitmap.Height-1 then nbhood[3]:=SourceRows[Row+1][Col] else nbhood[3]:=SourceRows[Row-1][Col];
rr:=0;gg:=0;bb:=0;
for i:=0 to 3 do
begin
rr:=rr+nbhood.rgbtRed;
gg:=gg+nbhood.rgbtGreen;
bb:=bb+nbhood.rgbtBlue;
end;
rr:=rr div 4;
gg:=gg div 4;
bb:=bb div 4;

if b then TargetRow[Col].rgbtBlue := bb;
if r then TargetRow[Col].rgbtRed := rr;
if g then TargetRow[Col].rgbtGreen := gg;
end;
end;
finally
FreeMem(SourceRows);
end;
end;

procedure LoadLinearFilterFromFile(FileName:PChar;var Filter:TGraphicFilter);stdcall;
var Stream:TFileStream;
header: Array [0..5] of Char;
begin
Stream:=TFileStream.Create(FileNAme,fmOpenRead);
try
ZeroMemory(PChar(@header),sizeof(header));
Stream.Read(header,4);
if header<>'fxlf' then raise EGraphicEffects.Create('Not a valid linear filter file') else
begin
Stream.Read(Filter,SizeOf(TGraphicFilter));

end;
finally
Stream.Free;
end;
end;

procedure SaveLinearFilterToFile(FileName:PChar;const Filter:TGraphicFilter);stdcall;
var stream:TFileStream;
const c ='Created by pView (c) 1999 by A.Moser';
begin
Stream:=TFileStream.Create(FileNAme,fmCreate);
try
Stream.Write('fxlf',4);
Stream.Write(Filter,SizeOf(TGraphicFilter));
Stream.Write(c,Length(c));
finally
Stream.Free;
end;

end;

procedure LoadMultiPassFilterFromFile(FileName:PChar;var Filter:TMultiPassGraphicFilter);stdcall;
var Stream:TFileStream;
header: Array [0..5] of Char;
begin
Stream:=TFileStream.Create(FileNAme,fmOpenRead);
try
ZeroMemory(PChar(@header),sizeof(header));
Stream.Read(header,4);
if header<>'fxmf' then raise EGraphicEffects.Create('Not a valid linear filter file') else
begin
Stream.Read(Filter,SizeOf(TMultiPassGraphicFilter));

end;
finally
Stream.Free;
end;
end;

procedure SaveMultipassFilterToFile(FileName:PChar;const Filter:TMultiPassGraphicFilter);stdcall;
var stream:TFileStream;
const c ='Created by pView (c) 1999 by A.Moser';
begin
Stream:=TFileStream.Create(FileNAme,fmCreate);
try
Stream.Write('fxmf',4);
Stream.Write(Filter,SizeOf(TMultiPassGraphicFilter));
Stream.Write(c,Length(c));
finally
Stream.Free;
end;

end;

// -----------------------------------------------------------------------------
//
// Stretch
//
// Parameter:
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
//
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------

procedure EffectStretch(SrcBitmap,DestBitmap:TBitmap;Low,High:Integer;const EffectCallBack:TEffectCallBack);stdcall;
var
SourceRows :PPRows;
TargetRow :pRGBArray;
Row,Col :Integer;
begin
SetBitmapsEql(SrcBitmap,DestBitmap);
GetMem(SourceRows, SrcBitmap.Height * SizeOf(pRGBArray));
try
for Row:= 0 to SrcBitmap.Height - 1 do SourceRows[Row]:=SrcBitmap.Scanline[Row];
for Row := 0 to SrcBitmap.Height - 1 do
begin
TargetRow:=DestBitmap.Scanline[Row];

if Assigned(EffectCallBack) then EffectCallBack(0,100,Round((row/SrcBitmap.Height)*100));
for Col := 0 to SrcBitmap.Width - 1 do
begin

if RGBIntensity(SourceRows[Row][Col])<=Low then
begin
TargetRow[Col].rgbtBlue := 0;
TargetRow[Col].rgbtRed := 0;
TargetRow[Col].rgbtGreen := 0;
end
else
if RGBIntensity(SourceRows[Row][Col])>=High then
begin
TargetRow[Col].rgbtBlue := 255;
TargetRow[Col].rgbtRed := 255;
TargetRow[Col].rgbtGreen := 255;
end
else
TargetRow[Col]:=SourceRows[Row][Col];
end;
end;
finally
FreeMem(SourceRows);
end;
end;


procedure EffectGamma(SrcBitmap,DestBitmap:TBitmap;Gamma:Double;const EffectCallBack:TEffectCallBack);stdcall;
// -----------------------------------------------------------------------------
//
// Gamma correction
//
// Parameter:
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
// Value : Gamma value
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------
var
SourceRows :PPRows;
TargetRow :pRGBArray;
Row,Col,i :Integer;
GammaArray :Array [0..255] of Byte;
begin
for i:=0 to 255 do
GammaArray:=IntGamma(Gamma,i);

SetBitmapsEql(SrcBitmap,DestBitmap);
GetMem(SourceRows, SrcBitmap.Height * SizeOf(pRGBArray));
try
for Row:= 0 to SrcBitmap.Height - 1 do SourceRows[Row]:=SrcBitmap.Scanline[Row];
for Row := 0 to SrcBitmap.Height - 1 do
begin
TargetRow:=DestBitmap.Scanline[Row];

if Assigned(EffectCallBack) then EffectCallBack(0,100,Round((row/SrcBitmap.Height)*100));
for Col := 0 to SrcBitmap.Width - 1 do
begin
TargetRow[Col].rgbtBlue := GammaArray[SourceRows[Row][Col].rgbtBlue];
TargetRow[Col].rgbtGreen := GammaArray[SourceRows[Row][Col].rgbtGreen];
TargetRow[Col].rgbtRed := GammaArray[SourceRows[Row][Col].rgbtRed];
end;
end;
finally
FreeMem(SourceRows);
end;
end;



procedure EffectEllipse(SrcBitmap,DestBitmap:TBitmap;const EffectCallBack:TEffectCallBack);stdcall;
// -----------------------------------------------------------------------------
//
// Ellipse
//
// Parameter:
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------
var
x, y, x1, y1:Integer;
fx, fy, xmid, ymid, ar:double;
SourceRows :PPRows;
TargetRow :pRGBArray;
Row :Integer;

function ComputePixel(x, y:double;var x1,y1:double):Integer; //float &amp;x1, float &amp;y1)
var
r, nn : double;
begin

if (x=0) and (y=0) then
begin
x1 := x;
y1 := y;
result:=1;

end
else
begin

nn := sqrt(x*x*0.5 + y*y);
if (abs(x) > abs(y)) then r:=abs(nn/x) else r:=abs(nn/y);

x1 := (r*x);
y1 := (r*y);

result:= 1;
end;
end;
begin

xmid := SrcBitmap.Width /2.0;
ymid := SrcBitmap.Height/2.0;
ar := SrcBitmap.Height/SrcBitmap.Width ;

SetBitmapsEql(SrcBitmap,DestBitmap);
GetMem(SourceRows, SrcBitmap.Height * SizeOf(pRGBArray));
try
for Row:= 0 to SrcBitmap.Height - 1 do SourceRows[Row]:=SrcBitmap.Scanline[Row];
Row:=0;
for y:=0 to DestBitmap.Height -1 do
begin
if Assigned(EffectCallBack) then EffectCallBack(0,100,Round((row/SrcBitmap.Height)*100));
TargetRow:=DestBitmap.Scanline[y];
for x:=0 to DestBitmap.Width-1 do
begin
ComputePixel(ar*(x-xmid), y-ymid, fx, fy);
x1 := Round(xmid+fx/ar);
y1 := Round(ymid+fy);
if (y1>0) and (y1< SrcBitmap.Height-1) and
(x1>0) and (x1< SrcBitmap.Width-1) then
TargetRow[x]:=SourceRows[y1][x1]
else
begin
TargetRow[x].rgbtBlue := 0;
TargetRow[x].rgbtGreen := 0;
TargetRow[x].rgbtRed := 0;
end;


end;
end;
finally
FreeMem(SourceRows);
end;
end;


procedure EffectMosaic(SrcBitmap,DestBitmap:TBitmap;Width,Height:Integer;const EffectCallBack:TEffectCallBack);stdcall;
// -----------------------------------------------------------------------------
//
// Mosaic effect
//
// Parameter:
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
// Width,Height : Dimension of blocks
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------
var
SourceRows :PPRows;
TargetRow :pRGBArray;
Row,Col :Integer;
source_row,source_col:Integer;
inc_x,inc_y,
half_x, half_y: Integer;
begin
if(Width<1) or (Height<1) then Exit;

half_x:=(Width shr 1)+(Width and 1);
half_y:=(Height shr 1)+(Height and 1);

SetBitmapsEql(SrcBitmap,DestBitmap);
GetMem(SourceRows, SrcBitmap.Height * SizeOf(pRGBArray));
try
for Row:= 0 to SrcBitmap.Height - 1 do SourceRows[Row]:=SrcBitmap.Scanline[Row];


source_Row:=half_y;
inc_y:=0;
for Row:=0 to DestBitmap.Height-1 do
begin
if Assigned(EffectCallBack) then EffectCallBack(0,100,Round((row/SrcBitmap.Height)*100));
source_Col:=half_x;
inc_x:=0;
TargetRow:=DestBitmap.Scanline[Row];
for Col:=0 to DestBitmap.Width-1 do
begin
TargetRow[Col]:=SourceRows[source_row][source_col];
inc(inc_x);
if inc_x>=Width then
begin
source_col:=source_col+Width;
if source_col>SrcBitmap.Width-1 then source_col:=SrcBitmap.Width-1;
inc_x:=0;
end;
end;

// increment the position in source_bitmap;
inc(inc_y);
if inc_y>=Height then
begin
source_row:=source_row+Height;
if source_row>SrcBitmap.Height-1 then source_row:=SrcBitmap.Height-1;
inc_y:=0;
end;
end;


finally
FreeMem(SourceRows);
end;
end;

procedure EffectCircleAround(SrcBitmap,DestBitmap:TBitmap;Value:Integer;const EffectCallBack:TEffectCallBack);stdcall;
// -----------------------------------------------------------------------------
//
// Picture distortion
//
// Parameter:
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
// Value : Amount
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------
var
SourceRows :PPRows;
TargetRow :pRGBArray;
Row,Col :Integer;
half_x, half_y: Integer;
max_x,max_y:Double;
dx,dy,r:Double;
dsx,dsy:Double;
sx,sy:Integer;
theta:double;
begin

half_x:=SrcBitmap.Width div 2;
half_y:=SrcBitmap.Height div 2;
dx:=SrcBitmap.Width-1;
dy:=SrcBitmap.Height-1;
r:=sqrt(dx*dx + dy*dy);
if r>=SrcBitmap.Width then max_x:=SrcBitmap.Width-1 else max_x:=r;
if r>=SrcBitmap.Height then max_y:=SrcBitmap.Height-1 else max_y:=r;

SetBitmapsEql(SrcBitmap,DestBitmap);
GetMem(SourceRows, SrcBitmap.Height * SizeOf(pRGBArray));
try
for Row:= 0 to SrcBitmap.Height - 1 do SourceRows[Row]:=SrcBitmap.Scanline[Row];

for Row:=0 to Round(max_y) do
begin
if Assigned(EffectCallBack) then EffectCallBack(0,100,Round((row/SrcBitmap.Height)*100));
TargetRow:=DestBitmap.Scanline[Row];
for Col:=0 to Round(max_x) do
begin
dx:=Col-half_x;
dy:=Row-half_y;
r:=sqrt(dx*dx+dy*dy);
if r=0 then
begin
dsx:=0;
dsy:=0;
end else
begin
theta:=xArcTan2(dx,dy)-r/Value - (-(Pi / 2));
dsx:=r* cos(theta);
dsy:=r* sin(theta);
end;
dsx:=dsx+ half_x;
dsy:=dsy+ half_y;
sx:=Trunc(dsx);
sy:=Trunc(dsy);
if sy>=SrcBitmap.Height then sy:= SrcBitmap.Height-1;
if sx>=SrcBitmap.Width then sx:= SrcBitmap.Width-1;
if sx<0 then sx:=0;
if sy<0 then sy:=0;
TargetRow[Col]:=SourceRows[sy][sx];
end;
end;


finally
FreeMem(SourceRows);
end;
end;

procedure EffectFishEye(SrcBitmap,DestBitmap:TBitmap;Value:Extended;const EffectCallBack:TEffectCallBack);stdcall;
// -----------------------------------------------------------------------------
//
// FishEye effect
//
// Parameter:
// SrcBitmap : Bitmap to be processed
// DestBitmap : Result
// Value : Amount
// EffectCallBack : CallBack for user interface
//
//
// -----------------------------------------------------------------------------
var
SourceRows :PPRows;
TargetRow :pRGBArray;
Row,Col :Integer;
half_x, half_y: Integer;
dx,dy,radius1,radius2,radiusMax:Double;
dsx,dsy:Double;
sx,sy:Integer;
begin

half_x:=SrcBitmap.Width div 2;
half_y:=SrcBitmap.Height div 2;
radiusMax:= SrcBitmap.Width * Value;
SetBitmapsEql(SrcBitmap,DestBitmap);

GetMem(SourceRows, SrcBitmap.Height * SizeOf(pRGBArray));
try
for Row:= 0 to SrcBitmap.Height - 1 do SourceRows[Row]:=SrcBitmap.Scanline[Row];

for Row:=0 to DestBitmap.Height-1 do
begin
if Assigned(EffectCallBack) then EffectCallBack(0,100,Round((row/SrcBitmap.Height)*100));
TargetRow:=DestBitmap.Scanline[Row];
for Col:=0 to DestBitmap.Width-1 do
begin
dx:=Col-half_x;
dy:=Row-half_y;
radius1:=sqrt(dx*dx+dy*dy);
if radius1=0 then
begin
dsx:=0;
dsy:=0;
end else
begin
if radius1=radiusMax then radius1:=radius1+1;
radius2:=radiusMax / 2 *(1/(1-radius1/radiusMax)-1);
dsx:=dx*radius2 /radius1 +half_x;
dsy:=dy*radius2 /radius1 +half_y;
end;
sx:=Trunc(dsx);
sy:=Trunc(dsy);
// if sy>=SrcBitmap.Height then sy:= SrcBitmap.Height-1;
// if sx>=SrcBitmap.Width then sx:= SrcBitmap.Width-1;
// if sx<0 then sx:=0;
// if sy<0 then sy:=0;
if (sy>=SrcBitmap.Height) or
(sx>=SrcBitmap.Width) or
(sx<0) or
(sy<0) then
begin
TargetRow[Col].rgbtBlue:=0;
TargetRow[Col].rgbtGreen:=0;
TargetRow[Col].rgbtRed:=0;
end
else
TargetRow[Col]:=SourceRows[sy][sx];
end;
end;
finally
FreeMem(SourceRows);
end;
end;

end.
 
procedure HLS_TO_RGB(H, L, S: Integer; var R, G, B: Integer);
//360,100,100-->255,255,255
var
Sat, Lum: Double;
begin
R := 0;
G := 0;
B := 0;
if (H < 360) and (H >= 0) and (S <= 100) and (S >= 0) and (L <= 100) and (L
>= 0) then
begin
if H <= 60 then
begin
R := 255;
G := Round((255 / 60) * H);
B := 0;
end
else if H <= 120 then
begin
R := Round(255 - (255 / 60) * (H - 60));
G := 255;
B := 0;
end
else if H <= 180 then
begin
R := 0;
G := 255;
B := Round((255 / 60) * (H - 120));
end
else if H <= 240 then
begin
R := 0;
G := Round(255 - (255 / 60) * (H - 180));
B := 255;
end
else if H <= 300 then
begin
R := Round((255 / 60) * (H - 240));
G := 0;
B := 255;
end
else if H < 360 then
begin
R := 255;
G := 0;
B := Round(255 - (255 / 60) * (H - 300));
end;

Sat := Abs((S - 100) / 100);
R := Round(R - ((R - 128) * Sat));
G := Round(G - ((G - 128) * Sat));
B := Round(B - ((B - 128) * Sat));

Lum := (L - 50) / 50;
if Lum > 0 then
begin
R := Round(R + ((255 - R) * Lum));
G := Round(G + ((255 - G) * Lum));
B := Round(B + ((255 - B) * Lum));
end
else if Lum < 0 then
begin
R := Round(R + (R * Lum));
G := Round(G + (G * Lum));
B := Round(B + (B * Lum));
end;
end;
end;

procedure RGB_TO_HLS(R, G, B: Integer; var H, L, S: Integer);
//255,255,255-->360,100,100
var
Delta: Double;
CMax, CMin: Double;
Red, Green, Blue, Hue, Sat, Lum: Double;
begin
Red := R / 255;
Green := G / 255;
Blue := B / 255;
CMax := Max(Red, Max(Green, Blue));
CMin := Min(Red, Min(Green, Blue));
Lum := (CMax + CMin) / 2;
if CMax = CMin then
begin
Sat := 0;
Hue := 0;
end
else
begin
if Lum < 0.5 then
Sat := (CMax - CMin) / (CMax + CMin)
else
Sat := (CMax - CMin) / (2 - CMax - CMin);
delta := CMax - CMin;
if Red = CMax then
Hue := (Green - Blue) / Delta
else if Green = CMax then
Hue := 2 + (Blue - Red) / Delta
else
Hue := 4.0 + (Red - Green) / Delta;
Hue := Hue / 6;
if Hue < 0 then
Hue := Hue + 1;
end;
H := Round(Hue * 360);
S := Round(Sat * 100);
L := Round(Lum * 100);
end;

procedure HandleBitmapLightness(BMP: TBitmap); //亮度递减
var
i, j: Integer;
ScanL1, ScanL2: PByteArray;
R, G, B, H, L, S: Integer;
begin
if BMP.Height mod 2 = 1 then
begin
for i := 1 to Trunc(BMP.Height / 2) do
begin
ScanL1 := BMP.ScanLine[Trunc(BMP.Height / 2) - i];
ScanL2 := BMP.ScanLine[Trunc(BMP.Height / 2) + i];
for j := 0 to BMP.Width - 1 do
begin
RGB_TO_HLS(ScanL1[3 * j + 2], ScanL1[3 * j + 1], ScanL1[3 * j], H,
L, S);
L := Round((Trunc(BMP.Height / 2) + 1 - i) * L / (Trunc(BMP.Height /
2) + 1));
HLS_TO_RGB(H, L, S, R, G, B);
ScanL1[3 * j + 2] := R;
ScanL1[3 * j + 1] := G;
ScanL1[3 * j] := B;

RGB_TO_HLS(ScanL2[3 * j + 2], ScanL2[3 * j + 1], ScanL2[3 * j], H,
L, S);
L := Round((Trunc(BMP.Height / 2) + 1 - i) * L / (Trunc(BMP.Height /
2) + 1));
HLS_TO_RGB(H, L, S, R, G, B);
ScanL2[3 * j + 2] := R;
ScanL2[3 * j + 1] := G;
ScanL2[3 * j] := B;
end;
end;
end
else
begin
for i := 1 to Trunc(BMP.Height / 2) - 1 do
begin
ScanL1 := BMP.ScanLine[Trunc(BMP.Height / 2) - 1 - i];
ScanL2 := BMP.ScanLine[Trunc(BMP.Height / 2) + i];
for j := 0 to BMP.Width - 1 do
begin
RGB_TO_HLS(ScanL1[3 * j + 2], ScanL1[3 * j + 1], ScanL1[3 * j], H,
L, S);
L := Round((Trunc(BMP.Height / 2) - i) * L / Trunc(BMP.Height / 2));
HLS_TO_RGB(H, L, S, R, G, B);
ScanL1[3 * j + 2] := R;
ScanL1[3 * j + 1] := G;
ScanL1[3 * j] := B;

RGB_TO_HLS(ScanL2[3 * j + 2], ScanL2[3 * j + 1], ScanL2[3 * j], H,
L, S);
L := Round((Trunc(BMP.Height / 2) - i) * L / Trunc(BMP.Height / 2));
HLS_TO_RGB(H, L, S, R, G, B);
ScanL2[3 * j + 2] := R;
ScanL2[3 * j + 1] := G;
ScanL2[3 * j] := B;
end;
end;
end;
end;
 
看来只能用hhx_effects了,想要达到photoshop的效果还有一段距离,谢谢各位了
 
多人接受答案了。
 
后退
顶部