{

   This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This program 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 General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
}
unit UfrmEffect_Special;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, UfrmEffectBase, ComCtrls, ExtCtrls,
  GR32_Image, GR32_Layers, GR32, StdCtrls, Spin;

type
  Tspeffecttype = (spnone, spgrayscale, spinvert, spposasi, spbright, spbrightblur, spfilm,
   spsharpen, spredeye, spsepia);

  TfrmEffect_Special = class(TfrmEffectBase)
    Panel1: TPanel;
    TreeView1: TTreeView;
    effbar0: TTrackBar;
    Edit18: TSpinEdit;
    effbar1: TTrackBar;
    effbar2: TTrackBar;
    Edit16: TSpinEdit;
    Edit17: TSpinEdit;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure TreeView1Click(Sender: TObject);
    procedure effbar0Change(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Edit18Change(Sender: TObject);
  private
    { Private declarations }
    speffecttype:Tspeffecttype;
    EffMatrix : array [0..6,0..6] of integer;
    divisor:integer;

    rdbar1,rdbar2,rdbar3:integer;
    special_film_v1,special_sharpen_v1,special_redeye_v1,
    special_sepia_v1:integer;
    value_arr:array[0..2] of integer;

    procedure filltreeview;
    procedure apply_effect();
    procedure UpdateConvolution;
    function getmatrixsize():integer;
    procedure ok_select; override;
    procedure imageprocess(var src:tbitmap);
    procedure set_controlvisible(v1,v2,v3,v4,v5,v6:integer);
    procedure set_controlvalue(min1,max1,v1, min2,max2,v2, min3,max3,v3:integer);
    procedure get_defaultvalue(effecttype:Tspeffecttype);
    procedure proc_redeye;
    procedure proc_sepia;
    procedure proc_posasi;
  public
    { Public declarations }
  end;

var
  frmEffect_Special: TfrmEffect_Special;

implementation
uses Ufrmmain, Uconfig, GR32_Filters, im_Effects, im_basic, im_Convolution, Ufunction;

{$R *.dfm}

procedure TfrmEffect_Special.FormCreate(Sender: TObject);
begin
  inherited;
  self.Width:=750;

  PanelPreView.Width:=250;
  PanelPreView.Align:=alLeft;
  panel1.Align:=alclient;

  get_defaultvalue(spfilm);
  special_film_v1:=config.getvaluebyinteger('special_film_v1',value_arr[0]);
  get_defaultvalue(spsharpen);
  special_sharpen_v1:=config.getvaluebyinteger('special_sharpen2_v1',value_arr[0]);
  get_defaultvalue(spredeye);
  special_redeye_v1:=config.getvaluebyinteger('special_redeye_v1',value_arr[0]);
  get_defaultvalue(spsepia);
  special_sepia_v1:=config.getvaluebyinteger('special_sepia_v1',value_arr[0]);

  speffecttype:=spnone;
  filltreeview;
end;

procedure TfrmEffect_Special.FormShow(Sender: TObject);
begin
  make_thumbimage;
  imagepreview1.Bitmap.Assign(thumb_bitmap32);

  applyselect.Visible:=not frmmain.is_layerselected;
end;

procedure TfrmEffect_Special.FormDestroy(Sender: TObject);
begin
  inherited;

  config.setvaluebyinteger('special_film_v1',special_film_v1);
  config.setvaluebyinteger('special_sharpen2_v1',special_sharpen_v1);
  config.setvaluebyinteger('special_redeye_v1',special_redeye_v1);
  config.setvaluebyinteger('special_sepia_v1',special_sepia_v1);
end;

procedure TfrmEffect_Special.ok_select;
begin
  if applyselect.ItemIndex=0 then begin
    Screen.Cursor:=crHourglass;
    try
      imagepreview1.Bitmap.Assign(org_bitmap32);
      apply_effect;
    finally
      screen.Cursor:=crdefault;
    end;
    exit;
  end;

  frmmain.apply_listimages(applyselect.ItemIndex=1,imageprocess,etspecial,self.Caption);
  self.ModalResult:=mrcancel;
end;

procedure TfrmEffect_Special.imageprocess(var src:tbitmap);
begin
  imagepreview1.Bitmap.Assign(src);
  apply_effect;
  imagepreview1.Bitmap.AssignTo24(src);
end;

procedure TfrmEffect_Special.filltreeview;
  procedure add(title:string;speffecttype:Tspeffecttype);
  var
    treenode:Ttreenode;
  begin
    treenode:=TreeView1.Items.AddChild(nil,title);
    treenode.Data:=Pinteger(ord(speffecttype));
  end;
begin
  add('회색톤으로',spgrayscale);
  add('네거티브',spinvert);
  add('이미지 보정,최적화',spposasi);
  add('밝고 환하게',spbright);
  add('밝고 환하게(흐릿하게)',spbrightblur);
  add('필름입자',spfilm);
  add('선명하게',spsharpen);
  add('붉은눈,적목 제거(영역선택필요)',spredeye);
  add('갈색톤으로',spsepia);
end;

procedure TfrmEffect_Special.TreeView1Click(Sender: TObject);
var
  v:integer;
  effecttype:Tspeffecttype;
begin
  if TreeView1.Selected=nil then exit;
  effecttype:=Tspeffecttype(integer(TreeView1.Selected.Data));

  case effecttype of
    spfilm: set_controlvisible(1,0,0,1,0,0);
    spsharpen: set_controlvisible(1,0,0,1,0,0);
    spsepia: set_controlvisible(1,0,0,1,0,0);
    spredeye:
      if frmmain.is_layerselected=false then begin
        MessageDlg('적목제거는 원본에서 제거할 영역을 미리 선택하셔야 합니다.',mtError, [mbOk], 0);
        exit;
      end else
        set_controlvisible(1,0,0,1,0,0);
    else
      set_controlvisible(0,0,0,0,0,0);
  end;

  speffecttype:=effecttype;

  case speffecttype of
    spfilm: set_controlvalue(0,1000,special_film_v1,0,0,0,0,0,0);
    spsharpen: set_controlvalue(1,20,special_sharpen_v1,0,0,0,0,0,0); //-20..-1
    spredeye: set_controlvalue(0,100,special_redeye_v1,0,0,0,0,0,0);
    spsepia: set_controlvalue(1,100,special_sepia_v1,0,0,0,0,0,0);
    else
      imagepreview1.bitmap.Assign(thumb_bitmap32);
      apply_effect;
  end;

  imagepreview1.bitmap.changed;
end;

procedure TfrmEffect_Special.apply_effect();
var
  dest:tbitmap32;
  i,j,k,m:integer;
  hm:tconvolutionkernel;
begin
dest:=tbitmap32.Create;
try
  if speffecttype=spgrayscale then begin
    ColorToGrayscale(dest,imagepreview1.bitmap);
    imagepreview1.bitmap.Assign(dest);
  end else if speffecttype=spinvert then begin
    Invert(dest,imagepreview1.bitmap);
    imagepreview1.bitmap.Assign(dest);
  end else if speffecttype=spbright then begin
    im_basic.contrast(imagepreview1.bitmap,(7/100));
    im_basic.brightness(imagepreview1.bitmap,(7/100));
    //shapen
    rdbar1:=13;
    hm:=(KERNEL_ARRAY[13]);
    k:=0;
    for i:=0 to 6 do
     for j:=0 to 6 do begin
       effmatrix[j,i]:=hm[k];
       inc(k);
    end;
    m:=rdbar1-21;
    effmatrix[3,3]:=(m*(-1))+8;
    divisor:=m*(-1);
    UpdateConvolution;

  end else if speffecttype=spbrightblur then begin
    im_basic.contrast(imagepreview1.bitmap,(7/100));
    im_basic.brightness(imagepreview1.bitmap,(7/100));
    //shapen
    rdbar1:=13;
    hm:=(KERNEL_ARRAY[13]);
    k:=0;
    for i:=0 to 6 do
     for j:=0 to 6 do begin
       effmatrix[j,i]:=hm[k];
       inc(k);
    end;
    m:=rdbar1-21;
    effmatrix[3,3]:=(m*(-1))+8;
    divisor:=m*(-1);
    UpdateConvolution;
    //blur
    hm:=(KERNEL_ARRAY[9]);
    k:=0;
    for i:=0 to 6 do
     for j:=0 to 6 do begin
       effmatrix[j,i]:=hm[k];
       inc(k);
    end;
    divisor:=hm[49];
    UpdateConvolution;

  end else if speffecttype=spfilm then begin
    Effect_AddMonoNoise(imagepreview1.bitmap,rdbar1);
    special_film_v1:=rdbar1;

  end else if speffecttype=spsharpen then begin
    //shapen
    if rdbar1=0 then exit;
    hm:=(KERNEL_ARRAY[13]);
    k:=0;
    for i:=0 to 6 do
     for j:=0 to 6 do begin
       effmatrix[j,i]:=hm[k];
       inc(k);
    end;
    m:=rdbar1-21;
    effmatrix[3,3]:=(m*(-1))+8;
    divisor:=m*(-1);
    UpdateConvolution;
    special_sharpen_v1:=rdbar1;

  end else if speffecttype=spredeye then begin
    proc_redeye;
    special_redeye_v1:=rdbar1;

  end else if speffecttype=spsepia then begin
    proc_sepia;
    special_sepia_v1:=rdbar1;
  end else if speffecttype=spposasi then begin
    proc_posasi;
  end;
finally
  dest.Free;
end;
end;

procedure TfrmEffect_Special.proc_posasi;
var
  Bmp2,Bmp3,Bmp4,Bmp5:tbitmap32;
  R3,G3,B3, R2,G2,B2, R4: byte;

  p2,p3,p4:Pcolor32array;
  SS:Pcolor32;
  x,y:integer;
  r,g,b: byte;
const
  // the array with all the filters...
  EDGEVERYSTRONG: array [0..0] of TConvolutionKernel = (
     ( 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0,
       0, 0, 4, 4, 4, 0, 0,
       0, 0, 4,-33,4, 0, 0,
       0, 0, 4, 4, 4, 0, 0,
       0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0,1) {Edges Very Strong}
       );
begin
Bmp5:=tbitmap32.Create;
Bmp3:=tbitmap32.Create;
Bmp4:=tbitmap32.Create;
try
  Bmp5.Assign(imagepreview1.bitmap);
  Bmp3.Assign(bmp5);

  applyconvolution(Bmp5,EDGEVERYSTRONG[0],7);
  Effect_GaussianBlur(Bmp5, 1);
  bmp4.Assign(bmp5);
  bmp2:=imagepreview1.bitmap;

  Effect_GaussianBlur(Bmp2, 2);

  SS := @bmp2.Bits[0];

  for y := 0 to bmp2.height-1 do begin
    p2:=bmp2.scanline[y];
    p3:=bmp3.scanline[y];
    p4:=bmp4.scanline[y];
    for x := 0 to bmp2.width-1 do begin
      B3:=(p3[x] and $FF);
      G3:=(p3[x] shr 8) and $FF;
      R3:=(p3[x] shr 16) and $FF; // current image
      B2:=(p2[x] and $FF);
      G2:=(p2[x] shr 8) and $FF;
      R2:=(p2[x] shr 16) and $FF; // blurred image
      R4:=(p4[x] shr 16) and $FF; // mask
      if R4 > 0 then
      begin
        r   := (R2+R3*3) div 4;   // edges
        g := (G2+G3*3) div 4;
        b  := (B2+B3*3) div 4;
      end
      else
      begin
        r  := (R2*7+R3) div 8;    // blur areas
        g := (G2*7+G3) div 8;
        b := (B2*7+B3) div 8;
      end;
      SS^ := $FF000000 + r shl 16 + g shl 8 + b;
      inc(SS);

    end;
   end;

finally
  Bmp5.Free;
  bmp3.Free;
  bmp4.Free;
end;
end;

procedure TfrmEffect_Special.proc_sepia;
var
  dest:tbitmap32;
  depth:integer;

  p0:Pcolor32array;
  x,y:integer;
  r,g,b: byte;
  rr,gg,bb: byte;
  SS:Pcolor32;
begin
  depth:=rdbar1;
  dest:=imagepreview1.bitmap;

  SS := @dest.Bits[0];

  for y:=0 to dest.Height-1 do begin
    p0:=dest.scanline[y];
    for x:=0 to dest.Width-1 do begin
      b:=(p0[x] and $FF);
      g:=(p0[x] shr 8) and $FF;
      r:=(p0[x] shr 16) and $FF;

      //grayscale
      r:=(r+g+b) div 3;
      g:=r;
      b:=r;
      SS^ := $FF000000 + r shl 16 + g shl 8 + b;

      //sepia
      b:=(SS^ and $FF);
      g:=(SS^ shr 8) and $FF;
      r:=(SS^ shr 16) and $FF;
      rr:=r+(depth*2);
      gg:=g+depth;
      bb:=b;
      if rr <= ((depth*2)-1) then
        rr:=255;
      if gg <= (depth-1) then
        gg:=255;
      SS^ := $FF000000 + rr shl 16 + gg shl 8 + bb;

      inc(SS);
    end;
  end;

end;

procedure TfrmEffect_Special.proc_redeye;
var
  dest:tbitmap32;

  rbrite,gbrite,bbrite:extended;
  threshold:integer;

  p0:Pcolor32array;
  x,y:integer;
  r,g,b: byte;
  SS:Pcolor32;
begin
  threshold:=rdbar1;
  dest:=imagepreview1.bitmap;

  SS := @dest.Bits[0];

  for y:=0 to dest.Height-1 do begin
    p0:=dest.scanline[y];
    for x:=0 to dest.Width-1 do begin
      b:=(p0[x] and $FF);
      g:=(p0[x] shr 8) and $FF;
      r:=(p0[x] shr 16) and $FF;

      rbrite:=r * 0.5133333;
	  	gbrite:=g;
		  bbrite:=b * 0.1933333;
  		if (rbrite >= gbrite-threshold) and
           (rbrite >= bbrite-threshold) then begin
        rbrite:=(gbrite + bbrite) / 2;
  		  r:=round(rbrite / 0.51333333);
        SS^ := $FF000000 + r shl 16 + g shl 8 + b;
      end;
      inc(SS);
    end;
  end;
end;

procedure TfrmEffect_Special.UpdateConvolution;
var
  ray:array [0..49] of integer;
  EffMatrix2 : array [0..6,0..6] of integer;
  i,j,k:integer;
  mx:integer;
begin
  mx:=getmatrixsize();
  k:=0;
  for i:=0 to 49 do ray[i]:=0;

 case mx of
 3: begin
    for i:=2 to 6 do for j:=2 to 6 do
    effmatrix2[i-2,j-2]:=effmatrix[i,j];
      for i:=0 to 2 do for j:=0 to 2 do
      begin
       ray[k]:=effmatrix2[j,i];
       inc(k);
      end;
     ray[49]:=divisor;
    end;
 5: begin
    for i:=1 to 6 do for j:=1 to 6 do
    effmatrix2[i-1,j-1]:=effmatrix[i,j];
      for i:=0 to 4 do for j:=0 to 4 do
      begin
       ray[k]:=effmatrix2[j,i];
       inc(k);
      end;
     ray[49]:=divisor;
    end;
  7: begin
      for i:=0 to 6 do for j:=0 to 6 do
      begin
       ray[k]:=effmatrix[j,i];
       inc(k);
      end;
      ray[49]:=divisor;
     end;
   end;

  applyconvolution(imagepreview1.bitmap,ray,mx);
end;

function TfrmEffect_Special.getmatrixsize():integer;
var i:integer;
begin
result:=7;
 for i:=0 to 6 do if effmatrix[i,0]<>0 then exit;
 for i:=0 to 6 do if effmatrix[i,6]<>0 then exit;
 for i:=0 to 6 do if effmatrix[0,i]<>0 then exit;
 for i:=0 to 6 do if effmatrix[6,i]<>0 then exit;
result:=5;
 for i:=1 to 5 do if effmatrix[i,1]<>0 then exit;
 for i:=1 to 5 do if effmatrix[i,5]<>0 then exit;
 for i:=1 to 5 do if effmatrix[1,i]<>0 then exit;
 for i:=1 to 5 do if effmatrix[5,i]<>0 then exit;
result:=3;
end;

procedure TfrmEffect_Special.effbar0Change(Sender: TObject);
begin
  rdbar1:=effbar0.Position;
  rdbar2:=effbar1.Position;
  rdbar3:=effbar2.Position;
  edit18.text:=inttostr(rdbar1);
  edit17.text:=inttostr(rdbar2);
  edit16.text:=inttostr(rdbar3);

  imagepreview1.bitmap.Assign(thumb_bitmap32);
  apply_effect;
end;

procedure TfrmEffect_Special.set_controlvisible(v1,v2,v3,v4,v5,v6:integer);
begin
  effbar0.Visible:=v1=1;
  effbar1.Visible:=v2=1;
  effbar2.Visible:=v3=1;
  Edit18.Visible:=v4=1;
  Edit17.Visible:=v5=1;
  Edit16.Visible:=v6=1;
  Button1.Visible:=effbar0.Visible;
end;

procedure TfrmEffect_Special.set_controlvalue(min1,max1,v1, min2,max2,v2, min3,max3,v3:integer);
begin
  effbar0.Min:=min1;
  effbar0.Max:=max1;
  effbar0.Position:=v1;
  effbar0.Frequency:=(max1-min1)*5 div 180;
  if effbar0.Frequency<=0 then effbar0.Frequency:=1;

  effbar1.Min:=min2;
  effbar1.Max:=max2;
  effbar1.Position:=v2;
  effbar1.Frequency:=(max2-min2)*5 div 180;
  if effbar1.Frequency<=0 then effbar1.Frequency:=1;

  effbar2.Min:=min3;
  effbar2.Max:=max3;
  effbar2.Position:=v3;
  effbar2.Frequency:=(max3-min3)*5 div 180;
  if effbar2.Frequency<=0 then effbar2.Frequency:=1;

end;

procedure TfrmEffect_Special.get_defaultvalue(effecttype:Tspeffecttype);
begin
  case effecttype of
    spfilm: value_arr[0]:=128;
    spsharpen: value_arr[0]:=13; //-8=13-21
    spredeye: value_arr[0]:=0;
    spsepia: value_arr[0]:=20;
  end;
end;

procedure TfrmEffect_Special.Button1Click(Sender: TObject);
begin
  get_defaultvalue(speffecttype);
  case speffecttype of
    spfilm, spsharpen, spredeye, spsepia:
      effbar0.Position:=value_arr[0];
  end;
end;

procedure TfrmEffect_Special.Edit18Change(Sender: TObject);
begin
  if isvalidinteger(edit18.Text) then effbar0.Position:=round(edit18.Value);
  if isvalidinteger(edit17.Text) then effbar1.Position:=round(edit17.Value);
  if isvalidinteger(edit16.Text) then effbar2.Position:=round(edit16.Value);
end;

end.
