{
   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_Draw;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, UfrmEffectBase, gr32, GR32_Layers, Menus, UUndoManage, StdCtrls,
  ExtCtrls, Spin, Buttons, G32_Interface, OpenDrawShape, ColorDialogAdv, im_FillFlood,
  UnViewColorBox, ComCtrls, UBrowserListView;

type
  Tselecttool = (stnone, stfree, stline, strect, stellipse, stroundrect, starrow,
    steraser, stselcolor, stairbrush, stfillflood);

  Tdrawtype = (dtnone, dtrectangle, dtellipse);

  PBalloonRec = ^TBalloonRec;
  TBalloonRec = record
    idx:integer;
    filename:string;
    metafile:TMetafile;
    drawcaption:boolean;
    caption:TStringList;
    font:tfont;
    fontoutline:boolean;
    fontoutline_color:tcolor;
    fontoutline_width:integer;
    antialias:boolean;
    drawtype:Tdrawtype;
    drawcolor:tcolor;
    drawwidth:integer;
    drawfill:boolean;
    drawfillcolor:tcolor;
    location:TFloatRect;
    topmargin,leftmargin:integer;
  end;

  TfrmEffect_Draw = class(TfrmEffectBase)
    tmrBrush: TTimer;
    pMlayer: TPopupMenu;
    mnu_layerflatten: TMenuItem;
    N7: TMenuItem;
    N8: TMenuItem;
    N5: TMenuItem;
    N1: TMenuItem;
    N2: TMenuItem;
    N3: TMenuItem;
    N9: TMenuItem;
    N10: TMenuItem;
    N11: TMenuItem;
    N12: TMenuItem;
    N6: TMenuItem;
    mnu_layerdelete: TMenuItem;
    FontDialog1: TFontDialog;
    Timer1: TTimer;
    Splitter1: TSplitter;
    Panel5: TPanel;
    Panel6: TPanel;
    ScaleCombo: TComboBox;
    PageControl1: TPageControl;
    TabSheet1: TTabSheet;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    RadioGroup1: TRadioGroup;
    SpinEdit1: TSpinEdit;
    ComboBox1: TComboBox;
    ColorBox1: TnViewColorBox;
    Button1: TButton;
    ColorBox2: TnViewColorBox;
    CheckBox2: TCheckBox;
    CheckBox4: TCheckBox;
    CheckBox3: TCheckBox;
    Panel4: TPanel;
    Label6: TLabel;
    Label7: TLabel;
    SpinEdit2: TSpinEdit;
    SpinEdit3: TSpinEdit;
    Button2: TButton;
    TabSheet2: TTabSheet;
    Label8: TLabel;
    Label9: TLabel;
    Label10: TLabel;
    Label11: TLabel;
    Memo1: TMemo;
    Button3: TButton;
    cfontcolor: TnViewColorBox;
    cfontsize: TSpinEdit;
    cuseoutline: TCheckBox;
    coutlinecolor: TnViewColorBox;
    coutlinedepth: TSpinEdit;
    cuseantialias: TCheckBox;
    TabSheet3: TTabSheet;
    ListView1: TListView;
    Panel3: TPanel;
    Label4: TLabel;
    Label5: TLabel;
    SpinEdit4: TSpinEdit;
    Label12: TLabel;
    SpinEdit5: TSpinEdit;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormShortCut(var Msg: TWMKey; var Handled: Boolean);
    procedure RadioGroup1Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure ComboBox1Change(Sender: TObject);
    procedure Label2Click(Sender: TObject);
    procedure tmrBrushTimer(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure ListView1DblClick(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure cfontcolorChange(Sender: TObject);
    procedure cuseoutlineClick(Sender: TObject);
    procedure coutlinecolorChange(Sender: TObject);
    procedure coutlinedepthChange(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure mnu_layerflattenClick(Sender: TObject);
    procedure N7Click(Sender: TObject);
    procedure N1Click(Sender: TObject);
    procedure N10Click(Sender: TObject);
    procedure N11Click(Sender: TObject);
    procedure N6Click(Sender: TObject);
    procedure mnu_layerdeleteClick(Sender: TObject);
    procedure pMlayerPopup(Sender: TObject);
    procedure N8Click(Sender: TObject);
    procedure Splitter1Moved(Sender: TObject);
    procedure SpinEdit4Change(Sender: TObject);
  private
    { Private declarations }
    LastDot,PrevDot:Tpoint;
    Drawing:boolean;
    UndoManage1:TUndoManage;
    undoadded:boolean;
    selecttool:Tselecttool;
    Antialiased:boolean;

    lastbitmap:tbitmap32;
    preparedundo:boolean;

    BrowserListView1:TBrowserListView;
    FSelection: TPositionedLayer;
    RBLayer: TRubberbandLayer;
    flattening: boolean;
    defaulttext:string;
    blockapply:boolean;

    procedure proc_undoadd();
    procedure prepare_undoadd();
    function convertabsolutepos(x,y:integer):TPoint;
    procedure imagepreview1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer; Layer: TCustomLayer); virtual;
    procedure imagepreview1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer; Layer: TCustomLayer); virtual;
    procedure imagepreview1MouseMove(Sender: TObject; Shift: TShiftState;
      X, Y: Integer; Layer: TCustomLayer);
    procedure imagepreview1OnMouseLeave(Sender: TObject);

    procedure LoadBalloon;
    procedure SetSelection(Value: TPositionedLayer);
    function CreatePositionedLayer: TPositionedLayer;
    procedure PaintSimpleDrawingHandler(Sender: TObject; Buffer: TBitmap32);
    procedure LayerMouseDown(Sender: TObject; Buttons: TMouseButton;Shift: TShiftState; X, Y: Integer);

    procedure ApplyText;
    procedure WriteAntiAlias(PBalloonRec1:PBalloonRec; Canvas: TCanvas; X, Y: Integer; Text: String);
    function move_selectlayer(key,pitch:integer):boolean;
    procedure setlayerloaction();
  public
    { Public declarations }
    property Selection: TPositionedLayer read FSelection write SetSelection;
  end;

var
  frmEffect_Draw: TfrmEffect_Draw;

implementation
uses Uconfig, UfrmMain, Ufunction, UfrmEffect_Draw2;
var
  BalloonList:TList;
  BalloonCount,PosCount:integer;
  copy_BalloonRec:PBalloonRec;

const
  BalloonSize = 40;

{$R *.dfm}

function BalloonList_get(idx:integer):PBalloonRec;
var i:integer;
begin
  result:=nil;
  for i:=0 to BalloonList.Count-1 do
    if PBalloonRec(BalloonList.Items[i]).idx=idx then begin
      result:=PBalloonRec(BalloonList.Items[i]);
      break;
    end;
end;

procedure BalloonList_del(idx:integer);
var
  i:integer;
  PBalloonRec1:PBalloonRec;
begin
  for i:=0 to BalloonList.Count-1 do
    if PBalloonRec(BalloonList.Items[i]).idx=idx then begin
      PBalloonRec1:=PBalloonRec(BalloonList.Items[i]);
      freeandnil(PBalloonRec1.metafile);
      freeandnil(PBalloonRec1.font);
      freeandnil(PBalloonRec1.caption);
      dispose(PBalloonRec1);
      BalloonList.Delete(i);
      break;
    end;
end;

procedure BalloonList_clear;
var
  i:integer;
  PBalloonRec1:PBalloonRec;
begin
  for i:=BalloonList.Count-1 downto 0 do begin
    PBalloonRec1:=PBalloonRec(BalloonList.Items[i]);
    freeandnil(PBalloonRec1.metafile);
    freeandnil(PBalloonRec1.font);    
    freeandnil(PBalloonRec1.caption);
    dispose(PBalloonRec1);
  end;
  BalloonList.Clear;
end;

procedure TfrmEffect_Draw.FormCreate(Sender: TObject);
begin
  inherited;
  PanelPreView.Align:=alClient;
  imagepreview1.ScrollBars.Size:=0;
  imagepreview1.PopupMenu:=pMlayer;

  self.BorderStyle:=bsSizeable;
  self.BorderIcons:=[biSystemMenu,biMaximize];
  if config.effect_width>=0 then self.Width:=config.effect_width
  else self.Width:=750;
  if config.effect_height>=0 then self.Height:=config.effect_height
  else self.Height:=500;
  if config.effect_max then WindowState:=wsMaximized;

  BalloonList:=TList.Create;
  BalloonCount:=0;
  PosCount:=0;
  flattening:=false;

  BrowserListView1:=TBrowserListView.Create(ListView1,BalloonSize);
  BrowserListView1.TextOutStyle:=tosNoAll;
  BrowserListView1.showhint:=false;
  BrowserListView1.Setthumbspace(22);
  LoadBalloon;

  imagepreview1.OnMouseDown:=imagepreview1MouseDown;
  imagepreview1.OnMouseUp:=imagepreview1MouseUp;
  imagepreview1.OnMouseMove:=imagepreview1MouseMove;
  imagepreview1.OnMouseLeave:=imagepreview1OnMouseLeave;

  UndoManage1:=TUndoManage.Create(-1,'Draw');
  UndoManage1.maxundocount:=config.c_undomaxcount;

  lastbitmap:=tbitmap32.Create;
end;

procedure TfrmEffect_Draw.FormDestroy(Sender: TObject);
begin
  inherited;
  UndoManage1.Free;

  BalloonList_clear;
  BalloonList.Free;
  BrowserListView1.Free;

  config.setvaluebyinteger('scaleindex',ScaleCombo.ItemIndex);
  config.setvaluebyinteger('draw_penwidth',self.SpinEdit1.Value);
  config.setvaluebyinteger('draw_pencolor',self.ColorBox1.Selected);

  config.setvaluebyinteger('draw_selecttool',RadioGroup1.ItemIndex);
  config.setvaluebyinteger('draw_filluse',integer(self.CheckBox2.Checked));
  config.setvaluebyinteger('draw_fillcolor',self.ColorBox2.Selected);
  config.setvaluebyinteger('draw_antialias',integer(self.CheckBox3.Checked));

  config.setvaluebyinteger('draw_penuse',integer(self.CheckBox4.Checked));
//  config.setvaluebyinteger('draw_selpenwidth',self.ComboBox1.ItemIndex);

  config.setvaluebyinteger('draw_tolerance',self.SpinEdit2.Value);
  config.setvaluebyinteger('draw_trans',self.SpinEdit3.Value);

  //balloon
  config.setvaluebyinteger('scaleindex',ScaleCombo.ItemIndex);
  config.setvaluebystring('ballon_fontname',FontDialog1.Font.Name);
  config.setvaluebyinteger('ballon_fontsize',self.cfontsize.Value);
  config.setvaluebyinteger('ballon_fontcolor',self.cfontcolor.Selected);
  config.setvaluebyinteger('ballon_fontbold',integer(fsbold in FontDialog1.Font.Style));
  config.setvaluebyinteger('ballon_fontitalic',integer(fsItalic in FontDialog1.Font.Style));
  config.setvaluebyinteger('ballon_fontunderline',integer(fsUnderline in FontDialog1.Font.Style));
  config.setvaluebyinteger('ballon_fontstrikeOut',integer(fsStrikeOut in FontDialog1.Font.Style));

  if self.cuseoutline.Checked then
    config.setvaluebyinteger('ballon_outline',1)
  else
    config.setvaluebyinteger('ballon_outline',0);
  config.setvaluebyinteger('ballon_outlinecolor',self.coutlinecolor.Selected);
  config.setvaluebyinteger('ballon_outlinewidth',self.coutlinedepth.Value);
  if self.cuseantialias.Checked then
    config.setvaluebyinteger('ballon_antialias',1)
  else
    config.setvaluebyinteger('ballon_antialias',0);

  config.setvaluebystring('ballon_text',memo1.Text);

  config.setvaluebyinteger('draw_lastpageindex',PageControl1.ActivePageIndex);
  config.setvaluebyinteger('draw_pagewidth',panel5.Width);
  config.setvaluebyinteger('ballon_topmargin',self.SpinEdit4.Value);
  config.setvaluebyinteger('ballon_leftmargin',self.SpinEdit5.Value);


  config.effect_max:=WindowState=wsMaximized;
  if WindowState=wsnormal then begin
    config.effect_width:=self.Width;
    config.effect_height:=self.Height;
  end;
  lastbitmap.Free;
end;

procedure TfrmEffect_Draw.FormShow(Sender: TObject);
var
  i:integer;
begin
  inherited;
  self.setScaleCombo(ScaleCombo);

  //colorbox select fix, first create colobox!
  PageControl1.ActivePageIndex:=0;
  PageControl1.ActivePageIndex:=1;

  i:=config.getvaluebyinteger('draw_lastpageindex',0);
  if (i<0) or (i>PageControl1.PageCount-1) then i:=0;
  self.PageControl1.ActivePageIndex:=i;
  panel5.Width:=config.getvaluebyinteger('draw_pagewidth',193);

  self.SpinEdit1.Value:=config.getvaluebyinteger('draw_penwidth',3);
  self.ColorBox1.Selected:=config.getvaluebyinteger('draw_pencolor',clred);

  RadioGroup1.ItemIndex:=config.getvaluebyinteger('draw_selecttool',0);
  RadioGroup1Click(nil);
  self.CheckBox2.Checked:=boolean(config.getvaluebyinteger('draw_filluse',0));
  self.ColorBox2.Selected:=config.getvaluebyinteger('draw_fillcolor',clwhite);
  self.CheckBox3.Checked:=boolean(config.getvaluebyinteger('draw_antialias',1));

  self.CheckBox4.Checked:=boolean(config.getvaluebyinteger('draw_penuse',1));
//  self.ComboBox1.ItemIndex:=config.getvaluebyinteger('draw_selpenwidth',0);

  SpinEdit2.Value:=config.getvaluebyinteger('draw_tolerance',15);
  SpinEdit3.Value:=config.getvaluebyinteger('draw_trans',0);

  //balloon
  FontDialog1.Font.Name:=config.getvaluebystring('ballon_fontname','굴림');
  self.cfontcolor.Selected:=config.getvaluebyinteger('ballon_fontcolor',clblack);
  self.cfontsize.Value:=config.getvaluebyinteger('ballon_fontsize',10);

  if config.getvaluebyinteger('ballon_fontbold',0)=1 then
    FontDialog1.Font.Style:=FontDialog1.Font.Style+[fsbold];
  if config.getvaluebyinteger('ballon_fontitalic',0)=1 then
    FontDialog1.Font.Style:=FontDialog1.Font.Style+[fsItalic];
  if config.getvaluebyinteger('ballon_fontunderline',0)=1 then
    FontDialog1.Font.Style:=FontDialog1.Font.Style+[fsUnderline];
  if config.getvaluebyinteger('ballon_fontstrikeOut',0)=1 then
    FontDialog1.Font.Style:=FontDialog1.Font.Style+[fsStrikeOut];

  self.cuseoutline.Checked:=config.getvaluebyinteger('ballon_outline',0)=1;
  self.coutlinedepth.Value:=config.getvaluebyinteger('ballon_outlinewidth',3);
  self.coutlinecolor.Selected:=config.getvaluebyinteger('ballon_outlinecolor',0);
  self.cuseantialias.Checked:=config.getvaluebyinteger('ballon_antialias',0)=1;

  self.SpinEdit4.Value:=config.getvaluebyinteger('ballon_topmargin',8);
  self.SpinEdit5.Value:=config.getvaluebyinteger('ballon_leftmargin',8);

  defaulttext:=#13#10+'여기에 원하는 글을 입력하세요!'+#13#10+'감사합니다 ^^';
  memo1.Text:=config.getvaluebystring('ballon_text',defaulttext);

  labeldisplay1.Left:=10;
  labeldisplay2.Left:=10;
end;

function TfrmEffect_Draw.convertabsolutepos(x,y:integer):TPoint;
var
  np1:tpoint;
begin
  np1.X:=x;
  np1.y:=y;
  result:=imagepreview1.ControlToBitmap(np1);
end;

procedure TfrmEffect_Draw.proc_undoadd();
var
  UndoData1:TUndoData;
  bitmap:tbitmap;
begin
  screen.Cursor:=crHourglass;
  bitmap:=tbitmap.Create;
  try
    preparedundo:=false;
    lastbitmap.AssignTo24(bitmap);
    UndoData1.bitmap:=bitmap;
    UndoData1.targetrect:=rect(0,0,bitmap.Width,bitmap.Height);
    UndoData1.name:='Draw';
    UndoData1.resized:=true;
    UndoManage1.addundo(bitmap,UndoData1,false);
  finally
    bitmap.Free;
    screen.Cursor:=crdefault;
  end;
end;

procedure TfrmEffect_Draw.prepare_undoadd();
begin
  lastbitmap.Assign(imagepreview1.Bitmap);
  preparedundo:=true;
end;

procedure TfrmEffect_Draw.imagepreview1OnMouseLeave(Sender: TObject);
begin
  imagepreview1.Cursor:=crDefault;
end;

procedure TfrmEffect_Draw.imagepreview1MouseMove(Sender: TObject;
  Shift: TShiftState; X, Y: Integer; Layer: TCustomLayer);
var
  np2:tpoint;
  rgb:TColor;
  scrollpitch:integer;
  i:integer;
  r1:trect;
  flag:boolean;

  procedure restorelastbitmap();
  var
    r:trect;
  begin
     if LastDot.X>PrevDot.X then begin
       r.Left:=PrevDot.X;
       r.Right:=LastDot.X;
     end else begin
       r.Left:=LastDot.X;
       r.Right:=PrevDot.X;
     end;
     if LastDot.Y>PrevDot.Y then begin
       r.Top:=PrevDot.Y;
       r.Bottom:=LastDot.Y;
     end else begin
       r.Top:=LastDot.Y;
       r.Bottom:=PrevDot.Y;
     end;
     if selecttool=starrow then
       windows.InflateRect(r, (imagepreview1.Bitmap.Canvas.Pen.Width*2)+20, (imagepreview1.Bitmap.Canvas.Pen.Width*2)+20)
     else
       windows.InflateRect(r, imagepreview1.Bitmap.Canvas.Pen.Width, imagepreview1.Bitmap.Canvas.Pen.Width);
     if r.Left<0 then r.Left:=0;
     if r.Right>imagepreview1.Bitmap.Width then r.Right:=imagepreview1.Bitmap.Width;
     if r.Top<0 then r.Top:=0;
     if r.Bottom>imagepreview1.Bitmap.Height then r.Bottom:=imagepreview1.Bitmap.Height;

     imagepreview1.Bitmap.Draw(r,r,lastbitmap);
  end;
begin
  np2:=convertabsolutepos(x,y);

  flag:=false;
  for i:=0 to imagepreview1.Layers.Count-1 do begin
   if (imagepreview1.Layers[i] is TPositionedLayer) and (imagepreview1.Layers[i]<>self.RBLayer) then begin
     r1:=MakeRect(TPositionedLayer(imagepreview1.Layers[i]).Location);
     if windows.PtInRect(r1,np2) then begin
       imagepreview1.Cursor:=crdefault;
       flag:=true;
       break;
     end;
   end;
  end;
  
  if flag=false then
   case selecttool of
     stfree: imagepreview1.Cursor:=1;
     stselcolor: imagepreview1.Cursor:=9;
     steraser: imagepreview1.Cursor:=3;
     stline, strect, stellipse, stroundrect, starrow: imagepreview1.Cursor:=4;
     stairbrush: imagepreview1.Cursor:=2;
     stfillflood: imagepreview1.Cursor:=10;
     else
       imagepreview1.Cursor:=crdefault;
   end;

  if Drawing and (selecttool<>stfree) and  (selecttool<>steraser) and (selecttool<>stairbrush) then
    labeldisplay1.Caption:=format('좌표 x:%d,y:%d -> x:%d,y:%d',[LastDot.X, LastDot.Y, np2.X, np2.Y])
  else
    labeldisplay1.Caption:=format('좌표 x:%d,y:%d',[np2.X, np2.Y]);
  setlayerloaction();

  if (selecttool=stselcolor) or (selecttool=stfillflood) then begin
    if (np2.X>=0) and (np2.Y>=0) and
      (np2.X<imagepreview1.Bitmap.Width) and
        (np2.Y<imagepreview1.Bitmap.Height) then begin
      rgb:=WinColor(imagepreview1.Bitmap.PixelS[np2.X,np2.Y]);
      panel3.Color:=rgb;
      label4.Caption:=format('R:%d G:%d B:%d',[(rgb) and $FF,
                                  (rgb shr  8) and $FF,
                                  (rgb shr 16) and $FF]);
    end;
    exit;
  end;

  if Drawing=false then exit;

  if undoadded=false then begin
    undoadded:=true;
    lastbitmap.Assign(imagepreview1.Bitmap);
    preparedundo:=true;
  end;

  scrollpitch:=30;
  if (x<0) then imagepreview1.Scroll(-scrollpitch,0);
  if (x>imagepreview1.Width) then imagepreview1.Scroll(scrollpitch,0);
  if (y<0) then imagepreview1.Scroll(0,-scrollpitch);
  if (y>imagepreview1.Height) then imagepreview1.Scroll(0,scrollpitch);

  case selecttool of
    stline,strect,stellipse,stroundrect,starrow:begin
      if random(2)<>0 then exit;
    end;
  end;

  case selecttool of
    stfree,steraser:begin
//      open_Line(imagepreview1.Bitmap, LastDot, np2, 255, Antialiased);
      imagepreview1.Bitmap.Canvas.MoveTo(LastDot.X, LastDot.Y);
      imagepreview1.Bitmap.Canvas.LineTo(np2.x+1, np2.y+1);
      LastDot:=Point(np2.x,np2.y);
    end;
    stline:begin
      restorelastbitmap();
      open_Line(imagepreview1.Bitmap, LastDot, np2, 255, Antialiased);
//      imagepreview1.Bitmap.Canvas.MoveTo(LastDot.X, LastDot.Y);
//      imagepreview1.Bitmap.Canvas.LineTo(np2.x,np2.y);
      PrevDot:=Point(np2.x,np2.y);
    end;

    strect:begin
      restorelastbitmap();
      open_Rectangle(imagepreview1.Bitmap, LastDot, np2, Antialiased, 0, nil);
//      imagepreview1.Bitmap.Canvas.Rectangle(LastDot.X, LastDot.Y, np2.x, np2.y);
      PrevDot:=Point(np2.x,np2.y);
    end;
    stellipse:begin
      restorelastbitmap();
      open_Ellipse(imagepreview1.Bitmap, LastDot, np2, Antialiased, 0, nil);
//      imagepreview1.Bitmap.Canvas.Ellipse(LastDot.X, LastDot.Y, np2.x, np2.y);
      PrevDot:=Point(np2.x,np2.y);
    end;

    stroundrect:begin
      restorelastbitmap();
      open_RoundRectangle(imagepreview1.Bitmap, LastDot, np2, Antialiased, 0, nil);
      PrevDot:=Point(np2.x,np2.y);
    end;

    starrow:begin
      restorelastbitmap();
      open_Arrow(imagepreview1.Bitmap, LastDot, np2, 255, Antialiased);
      PrevDot:=Point(np2.x,np2.y);
    end;

    stairbrush:begin
      PrevDot:=Point(np2.x,np2.y);
    end;

  end;
end;

procedure TfrmEffect_Draw.imagepreview1MouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer;
  Layer: TCustomLayer);
var
  np2:tpoint;
  bit:tbitmap;
begin
  if Button<>mbleft then begin
    Drawing:=false;
    exit;
  end;

  np2:=convertabsolutepos(x,y);
  if (np2.X>=0) and (np2.Y>=0) and (np2.X<imagepreview1.Bitmap.Width) and
        (np2.Y<imagepreview1.Bitmap.Height) then
  else
    exit;

  if selecttool=stnone then begin
    Drawing:=false;
    exit;
  end;

  if selecttool=stselcolor then begin
    self.ColorBox1.Selected:=panel3.Color;
    self.ColorBox1.Invalidate;
    exit;
  end;

  if selecttool=stfillflood then begin
    undoadded:=true;
    prepare_undoadd();

    bit:=tbitmap.create;
    screen.Cursor:=crHourglass;
    try
      bit.PixelFormat := pf24Bit;
      bit.Width := imagepreview1.Bitmap.Width;
      bit.Height := imagepreview1.Bitmap.Height;
      imagepreview1.Bitmap.DrawTo(bit.Canvas.Handle, 0, 0);
      fill(bit, np2.X, np2.Y, ColorToRGB(self.ColorBox1.Selected), bit.canvas.pixels[np2.X,np2.Y], SpinEdit2.Value/100,
         0, 0.1, SpinEdit3.Value/10);
      imagepreview1.Bitmap.Assign(bit);
    finally
      bit.free;
      screen.Cursor:=crdefault;
    end;
    exit;
  end;

  undoadded:=false;
  Drawing:=true;
  Antialiased:=CheckBox3.Checked;

  imagepreview1.Bitmap.Canvas.Pen.Width:=self.SpinEdit1.Value;
  imagepreview1.Bitmap.Canvas.Pen.Color:=self.ColorBox1.Selected;
  imagepreview1.Bitmap.Canvas.Pen.Style:=psSolid;

  LastDot:=Point(np2.x,np2.y);

  case selecttool of
    stfree:begin
      imagepreview1.Bitmap.Canvas.MoveTo(np2.x,np2.y);
    end;
    stline,starrow:begin
      PrevDot:=Point(np2.x,np2.y);
    end;
    strect,stellipse,stroundrect:begin
      if self.CheckBox2.Checked then begin
        imagepreview1.Bitmap.Canvas.Brush.Style:=bsSolid;
        imagepreview1.Bitmap.Canvas.Brush.Color:=self.ColorBox2.Selected;
      end else begin
        imagepreview1.Bitmap.Canvas.Brush.Style:=bsclear;
      end;
      if self.CheckBox4.Checked=false then
       imagepreview1.Bitmap.Canvas.Pen.Style:=psClear;
    end;
    steraser:begin
      imagepreview1.Bitmap.Canvas.Pen.Color:=self.ColorBox2.Selected;
      imagepreview1.Bitmap.Canvas.MoveTo(np2.x,np2.y);
    end;
    stairbrush:begin
      undoadded:=true;
      prepare_undoadd();
      PrevDot:=Point(np2.x,np2.y);
      tmrBrush.OnTimer(Sender);
      tmrBrush.Enabled := True;
    end;
  end;
end;

procedure TfrmEffect_Draw.imagepreview1MouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer;
  Layer: TCustomLayer);
begin
  Drawing:=false;
  case selecttool of
    stairbrush:begin
      tmrBrush.Enabled:=false;
    end;
  end;
  if preparedundo then
    proc_undoadd;
end;

procedure TfrmEffect_Draw.FormShortCut(var Msg: TWMKey;
  var Handled: Boolean);
begin
  inherited FormShortCut(msg, handled);
  if handled then exit;

  if imagepreview1.Focused=false then begin
    Handled:=false;
    exit;
  end;
  if (GetKeyState(VK_CONTROL)<0) then begin
    Handled:=move_selectlayer(msg.CharCode,config.c_ctrlmovepitch);
  end else if (GetKeyState(VK_SHIFT)<0) then begin
    Handled:=move_selectlayer(msg.CharCode,config.c_shiftmovepitch);
  end else
    Handled:=false;
end;

procedure TfrmEffect_Draw.RadioGroup1Click(Sender: TObject);
begin
  selecttool:=Tselecttool(RadioGroup1.ItemIndex);
  ColorBox1.Enabled:=selecttool<>stnone;
  spinedit1.Enabled:=selecttool<>stnone;
  ComboBox1.Enabled:=selecttool<>stnone;
  label2.Enabled:=selecttool<>stnone;
  label3.Enabled:=selecttool<>stnone;
  Button1.Enabled:=selecttool<>stnone;
  
  case selecttool of
    stnone,stfree,stairbrush:begin
      CheckBox2.Enabled:=false;
      CheckBox3.Enabled:=false;
      CheckBox4.Enabled:=false;
      ColorBox2.Enabled:=false;
      panel3.Visible:=false;
      label4.Visible:=false;
      panel4.Visible:=false;
    end;
    stline,starrow:begin
      CheckBox2.Enabled:=false;
      CheckBox3.Enabled:=true;
      CheckBox4.Enabled:=false;
      ColorBox2.Enabled:=false;
      panel3.Visible:=false;
      label4.Visible:=false;
      panel4.Visible:=false;
    end;
    stselcolor,stfillflood:begin
      CheckBox2.Enabled:=false;
      CheckBox3.Enabled:=false;
      CheckBox4.Enabled:=false;
      ColorBox2.Enabled:=false;
      panel3.Visible:=true;
      label4.Visible:=true;
      panel4.Visible:=selecttool=stfillflood;

      spinedit1.Enabled:=false;
      ComboBox1.Enabled:=false;
    end;
    strect,stellipse,stroundrect:begin
      CheckBox2.Enabled:=true;
      CheckBox3.Enabled:=true;
      CheckBox4.Enabled:=true;
      ColorBox2.Enabled:=true;
      panel3.Visible:=false;
      label4.Visible:=false;
      panel4.Visible:=false;
    end;
    steraser:begin
      CheckBox2.Enabled:=false;
      CheckBox3.Enabled:=false;
      CheckBox4.Enabled:=false;
      ColorBox2.Enabled:=true;
      panel3.Visible:=false;
      label4.Visible:=false;
      panel4.Visible:=false;

      ColorBox1.Enabled:=false;
    end;

  end;

end;

procedure TfrmEffect_Draw.Button1Click(Sender: TObject);
var
  color1,color2:tcolor;
begin
  color1:=self.ColorBox1.Selected;
  color2:=self.ColorBox2.Selected;
  ColorBox1.Selected:=color2;
  ColorBox2.Selected:=color1;
  ColorBox1.Invalidate;
  ColorBox2.Invalidate;
end;

procedure TfrmEffect_Draw.ComboBox1Change(Sender: TObject);
begin
  case ComboBox1.ItemIndex of
    1:self.SpinEdit1.Value:=2;
    2:self.SpinEdit1.Value:=5;
    3:self.SpinEdit1.Value:=12;
    4:self.SpinEdit1.Value:=22;
  end;
end;

procedure TfrmEffect_Draw.Label2Click(Sender: TObject);
var
  pt:tpoint;
begin
  if gColorDialogAdv=nil then
    gColorDialogAdv:=TColorDialogAdv.Create(nil);

  pt:=(Sender as Tlabel).ClientToScreen(point(0,0));

  with gColorDialogAdv do begin
    TheForm.Left:=pt.X;
    if TheForm.Left<0 then TheForm.Left:=0;
    if TheForm.Left+TheForm.Width>screen.WorkAreaWidth then TheForm.Left:=screen.WorkAreaWidth-TheForm.Width;
    TheForm.Top:=pt.Y+(Sender as Tlabel).Height+3;
    if TheForm.Top<0 then TheForm.Top:=0;
    if TheForm.Top+TheForm.Height>screen.WorkAreaHeight then TheForm.Top:=screen.WorkAreaHeight-TheForm.Height;
    TheForm.Caption:='색깔선택';
  end;

  if (Sender as Tlabel).Tag=0 then
    gColorDialogAdv.Color:=self.ColorBox1.Selected
  else
    gColorDialogAdv.Color:=self.ColorBox2.Selected;
  if gColorDialogAdv.Execute then begin
    if (Sender as Tlabel).Tag=0 then begin
      self.ColorBox1.Selected:=gColorDialogAdv.Color;
      ColorBox1.Invalidate;
    end else begin
      self.ColorBox2.Selected:=gColorDialogAdv.Color;
      ColorBox2.Invalidate;
    end;
  end;
end;

procedure TfrmEffect_Draw.tmrBrushTimer(Sender: TObject);
var
  i: Integer;
  theta: integer;
  r: integer;
  Pt: TPoint;
begin
  for i := 1 to 200 do begin
    r := Random(self.SpinEdit1.Value+10);
    theta := Random(360);

    Pt := PrevDot;
    Pt.X := Pt.X + Round((r * Cos(theta * 3.141592 / 180)));
    Pt.Y := Pt.Y + Round((r * Sin(theta * 3.141592 / 180)));

    self.imagepreview1.Bitmap.SetPixelTS(Pt.X, Pt.Y, Color32(self.ColorBox1.Selected));
  end;
  imagepreview1.Changed;
end;

procedure TfrmEffect_Draw.Button2Click(Sender: TObject);
begin
  SpinEdit2.Value:=15;
  SpinEdit3.Value:=0;
end;

///////////////////////////////////////////////////////
////////////////balloon////////////////////////////////
///////////////////////////////////////////////////////
procedure TfrmEffect_Draw.LoadBalloon;
var
  SR: TSearchRec;
  Metafile:TMetafile;
  bitmap:TBitmap;
  stream:TMemoryStream;
  directory:string;
begin
Metafile:=TMetafile.Create;
stream:=TMemoryStream.Create;
bitmap:=TBitmap.Create;
bitmap.PixelFormat:=pf24bit;
bitmap.Width:=BalloonSize;
bitmap.Height:=BalloonSize;
directory:=GetAppDirectory+'balloon\';
try
  if FindFirst(directory+'image*.wmf',faAnyFile,SR) = 0 then begin
   repeat
     if SR.Attr <> faDirectory then begin
       Metafile.LoadFromFile(directory + SR.Name);
       Metafile.Width:=BalloonSize;
       Metafile.Height:=BalloonSize;
       bitmap.Canvas.Draw(0,0,Metafile);
       bitmap.SaveToStream(stream);
       BrowserListView1.BrowserStreamBlock(directory + SR.Name,stream);
       bitmap.Canvas.FillRect(rect(0,0,bitmap.Width,bitmap.Height));
       stream.Clear;
     end;
   until FindNext(SR) <> 0;
  end;
finally
  Metafile.Free;
  stream.Free;
  bitmap.Free;
end;
end;

function TfrmEffect_Draw.CreatePositionedLayer: TPositionedLayer;
var
  P,P2: TPoint;
  size,offset:integer;
  r:tfloatrect;
begin
  // get coordinates of the center of viewport
  with imagepreview1.GetViewportRect do
   P := imagepreview1.ControlToBitmap(Point((Right + Left) div 2, (Top + Bottom) div 2));
  with imagepreview1.GetViewportRect do
   P2 := imagepreview1.ControlToBitmap(Point(Right,Bottom));
  if p2.X>imagepreview1.Bitmap.Width then p2.X:=imagepreview1.Bitmap.Width;
  if p2.Y>imagepreview1.Bitmap.Height then p2.Y:=imagepreview1.Bitmap.Height;

  Result := TPositionedLayer.Create(imagepreview1.Layers);
  offset:=(PosCount*10);
  inc(PosCount);

  size:=160;

  r.Left:=P.X-(size div 2)+offset;
  r.Right:=r.Left+size;
  r.Top:=P.Y-(size div 2)+offset;
  r.Bottom:=r.Top+size;
  if (r.Left>p2.X-(size div 4)) or (r.Top>p2.Y-(size div 4)) then begin
    PosCount:=0;
    r.Left:=P.X-(size div 2)+offset;
    r.Right:=r.Left+size;
    r.Top:=P.Y-(size div 2)+offset;
    r.Bottom:=r.Top+size;
  end;
  if r.Left<0 then begin
    r.Left:=0; r.Right:=size;
  end;
  if r.Top<0 then begin
    r.Top:=0; r.Bottom:=size;
  end;

  Result.Location:=r;

  Result.Scaled := true;
  Result.MouseEvents := True;

  Result.OnMouseDown:=LayerMouseDown;
end;

procedure TfrmEffect_Draw.ListView1DblClick(Sender: TObject);
var
  L: TPositionedLayer;
  PBalloonRec1:PBalloonRec;
  PThumbImage1:PThumbImage;
begin
  if ListView1.Selected=nil then exit;
  PThumbImage1:=ListView1.Selected.Data;

  New(PBalloonRec1);
  inc(BalloonCount);
  PBalloonRec1.idx:=BalloonCount;
  PBalloonRec1.filename:=PThumbImage1.name;
  PBalloonRec1.Metafile:=TMetafile.Create;
  PBalloonRec1.Metafile.LoadFromFile(PBalloonRec1.filename);
  PBalloonRec1.caption:=TStringList.Create;
  PBalloonRec1.caption.Assign(memo1.Lines);
  PBalloonRec1.font:=tfont.Create;
  PBalloonRec1.font.Assign(FontDialog1.Font);
  PBalloonRec1.fontoutline:=self.cuseoutline.Checked;
  PBalloonRec1.fontoutline_color:=self.coutlinecolor.Selected;
  PBalloonRec1.fontoutline_width:=self.coutlinedepth.Value;
  PBalloonRec1.antialias:=self.cuseantialias.Checked;
  PBalloonRec1.drawtype:=dtnone;
  PBalloonRec1.drawcaption:=true;
  PBalloonRec1.topmargin:=self.SpinEdit4.Value;
  PBalloonRec1.leftmargin:=self.SpinEdit5.Value;

  if trim(PBalloonRec1.caption.Text)='' then PBalloonRec1.caption.Text:=defaulttext;
//  PBalloonRec1.caption.Add('');
//  PBalloonRec1.caption.Add('');
//  PBalloonRec1.caption.Add('여기에 원하는 글을 입력하세요!');
//  PBalloonRec1.caption.Add('감사합니다 ^^');

  BalloonList.Add(PBalloonRec1);

  L := CreatePositionedLayer;
  L.OnPaint := PaintSimpleDrawingHandler;
  L.Tag := BalloonCount;
  Selection := L;
  imagepreview1.SetFocus;
end;

procedure TfrmEffect_Draw.WriteAntiAlias(PBalloonRec1:PBalloonRec; Canvas: TCanvas; X, Y: Integer; Text: String);
const
  Sampling = 2;
var
  Width, Height: Integer;
  VirtualCanvas: TBitmap;
  RenderCanvas: TBitmap;
  DrawCanvas: TCanvas;

  x2,y2: Integer;
  i0, i1, o0: pbytearray;
  oldcolor:tcolor;
begin
  Width := Canvas.TextWidth(Text) + 20;
  Height := Canvas.TextHeight(Text);

  VirtualCanvas := TBitmap.Create;
  RenderCanvas := TBitmap.Create;
  try
    VirtualCanvas.Width := Width * Sampling;
    VirtualCanvas.Height := Height * Sampling;
    VirtualCanvas.PixelFormat := pf24bit;

    RenderCanvas.Width := Width;
    RenderCanvas.Height := Height;
    RenderCanvas.PixelFormat := pf24bit;

    RenderCanvas.Canvas.CopyRect(Rect(0,0, Width, Height), Canvas, Rect(X, Y, X + Width, Y + Height));

    DrawCanvas := VirtualCanvas.Canvas;
    DrawCanvas.StretchDraw(Rect(0,0, Width *2, Height *2), RenderCanvas);

    DrawCanvas.Brush.Style:=bsClear;
    DrawCanvas.Font.Color := Canvas.Font.Color;
    DrawCanvas.Font.Name := Canvas.Font.Name;
    DrawCanvas.Font.Size := Canvas.Font.Size * Sampling;
    DrawCanvas.Font.Style := Canvas.Font.Style;

    if PBalloonRec1.fontoutline then begin
      oldcolor:=DrawCanvas.Font.Color;
      DrawCanvas.Font.Color:=PBalloonRec1.fontoutline_color;
      DrawCanvas.TextOut(-Sampling+PBalloonRec1.fontoutline_width, 0+PBalloonRec1.fontoutline_width, Text);
      DrawCanvas.Font.Color:=oldcolor;
    end;

    DrawCanvas.TextOut(-Sampling, 0, Text);

    for y2 := 0 to Height-1 do
    begin
      i0 := VirtualCanvas.ScanLine[(y2 * 2)];
      i1 := VirtualCanvas.Scanline[(y2 * 2) + 1];
      o0 := RenderCanvas.Scanline[y2];

      for x2 := 0 to Width-1 do
      begin
        o0[x2 * 3] := (i0[x2 * 2 * 3] + i1[x2 * 2 * 3] + i0[(x2 * 2 * 3) + 3] + i1[(x2 * 2 * 3) + 3]) div 4;
        o0[(x2 * 3) + 1] := (i0[(x2 * 2 * 3) + 1] + i1[(x2 * 2 * 3) + 1] + i0[(x2 * 2 * 3) + 4] + i1[(x2 * 2 * 3) + 4]) div 4;
        o0[(x2 * 3) + 2] := (i0[(x2 * 2 * 3) + 2] + i1[(x2 * 2 * 3) + 2] + i0[(x2 * 2 * 3) + 5] + i1[(x2 * 2 * 3) + 5]) div 4;
      end;
    end;
    Canvas.CopyRect(Rect(X, Y, X + Width, Y + Height), RenderCanvas.Canvas, Rect(0,0, Width, Height));

  finally
    VirtualCanvas.Free;
    RenderCanvas.Free;
  end;
end;

procedure TfrmEffect_Draw.PaintSimpleDrawingHandler(Sender: TObject; Buffer: TBitmap32);
var
  srcrect,dstrect,aRect,brect:TRect;
  bitmap32:TBitmap32;
  w,h,w1,h1:integer;
  x,y:integer;
  PBalloonRec1:PBalloonRec;

  i,th:integer;
  s:string;
  oldcolor:tcolor;
begin
  PBalloonRec1:=BalloonList_get(TPositionedLayer(Sender).Tag);

  dstrect:=MakeRect(TPositionedLayer(Sender).GetAdjustedLocation);

  w1:=dstrect.Right-dstrect.Left;
  h1:=dstrect.Bottom-dstrect.Top;
  if assigned(PBalloonRec1.metafile) then begin
    PBalloonRec1.metafile.Width:=w1; //round(w1*(100/150));
    PBalloonRec1.metafile.Height:=h1; //round(h1*(100/150));
  end;

  IntersectRect(aRect, dstrect, MakeRect(0, 0, Buffer.Width -1, Buffer.Height -1));
  w:=aRect.Right-aRect.Left;
  h:=aRect.Bottom-aRect.Top;
  if (w=0) or (h=0) then exit;

  bitmap32:=TBitmap32.Create;
  try
    bitmap32.SetSize(w,h);
    Buffer.DrawTo(bitmap32,rect(0,0,w,h),aRect);

    x:=0; y:=0;
    if aRect.Left>dstrect.Left then x:=dstrect.Left-aRect.Left;
    if aRect.Top>dstrect.Top then y:=dstrect.Top-aRect.Top;

    if assigned(PBalloonRec1.metafile) then
      bitmap32.Canvas.Draw(x,y,PBalloonRec1.metafile);

    with bitmap32.Canvas do begin
      Font.Assign(PBalloonRec1.Font);

      if flattening=false then
        Font.Height:=trunc(Font.Height*imagepreview1.Scale);
      th:=TextHeight('gh')+2;

      if assigned(PBalloonRec1.metafile)=false then begin
        if PBalloonRec1.drawfill then begin
          Brush.Style:=bsSolid;
          bitmap32.Canvas.Brush.Color:=PBalloonRec1.drawfillcolor;
        end else
          Brush.Style:=bsClear;

        if flattening=false then
          bitmap32.Canvas.Pen.Width:=trunc(PBalloonRec1.drawwidth*imagepreview1.Scale)
        else
          bitmap32.Canvas.Pen.Width:=PBalloonRec1.drawwidth;
        brect:=rect(x,y,w1+x,h1+y);
        windows.InflateRect(brect,
            -1*bitmap32.Canvas.Pen.Width,
            -1*bitmap32.Canvas.Pen.Width);

        bitmap32.Canvas.Pen.Color:=PBalloonRec1.drawcolor;

        case PBalloonRec1.drawtype of
          dtrectangle:begin
            open_Rectangle(bitmap32, point(brect.Left,brect.Top), point(brect.Right,brect.Bottom), true, 0, nil);
//            bitmap32.Canvas.Rectangle(brect);
          end;
          dtellipse:begin
            open_Ellipse(bitmap32, point(brect.Left,brect.Top), point(brect.Right,brect.Bottom), true, 0, nil);
//            bitmap32.Canvas.Ellipse(brect);
          end;
        end;
      end;

      if PBalloonRec1.drawcaption then begin
        Brush.Style:=bsClear;
        for i:=0 to PBalloonRec1.caption.Count-1 do begin
         s:=PBalloonRec1.caption.Strings[i];
         if PBalloonRec1.antialias then begin
           WriteAntiAlias(PBalloonRec1,bitmap32.Canvas,x+PBalloonRec1.leftmargin,y+PBalloonRec1.topmargin+(i*th),s)
         end else begin
           if PBalloonRec1.fontoutline then begin
             oldcolor:=font.Color;
             font.Color:=PBalloonRec1.fontoutline_color;
             TextOut(x+PBalloonRec1.leftmargin+PBalloonRec1.fontoutline_width,y+PBalloonRec1.topmargin+PBalloonRec1.fontoutline_width+(i*th),s);
             font.Color:=oldcolor;
           end;
           TextOut(x+PBalloonRec1.leftmargin,y+PBalloonRec1.topmargin+(i*th),s);
         end;
        end;
      end;

    end;

    Buffer.Draw(aRect,rect(0,0,w,h), bitmap32);
  finally
    bitmap32.Free;
  end;

end;

procedure TfrmEffect_Draw.LayerMouseDown(Sender: TObject; Buttons: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Sender=nil then exit;
  Selection := TPositionedLayer(Sender);
  Selection.Index := imagepreview1.Layers.Count - 2;
  if (Buttons = mbLeft) then begin
    mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
    mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
  end else if (Buttons = mbRight) then begin
    mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
    mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0);
  end;
end;

procedure TfrmEffect_Draw.SetSelection(Value: TPositionedLayer);
var
  PBalloonRec1:PBalloonRec;
begin
  if Value <> FSelection then
  begin
    if RBLayer <> nil then
    begin
      RBLayer.ChildLayer := nil;
      RBLayer.LayerOptions := LOB_NO_UPDATE;
      imagepreview1.Invalidate;
    end;

    FSelection := Value;
    if Value <> nil then
    begin
      if RBLayer = nil then
      begin
        RBLayer := TRubberBandLayer.Create(imagepreview1.Layers);
        RBLayer.MinHeight := 1;
        RBLayer.MinWidth := 1;
      end
      else RBLayer.BringToFront;
      RBLayer.ChildLayer := Value;
      RBLayer.LayerOptions := LOB_VISIBLE or LOB_MOUSE_EVENTS or LOB_NO_UPDATE;
      if RBLayer.HandleFrame<>clBlack32 then begin
        RBLayer.HandleFrame:=clBlack32;
        RBLayer.HandleFill:=clWhite32;
        RBLayer.Update;
      end;
    end;

    //insert info
    if Selection=nil then PBalloonRec1:=nil
    else PBalloonRec1:=BalloonList_get(TPositionedLayer(Selection).Tag);
    if PBalloonRec1<>nil then begin
      blockapply:=true;
      try
        memo1.Lines.Assign(PBalloonRec1.caption);
        FontDialog1.Font.Assign(PBalloonRec1.font);
        self.cfontsize.Value:=FontDialog1.Font.Size;
        self.cfontcolor.Selected:=FontDialog1.Font.Color;
        self.cuseoutline.Checked:=PBalloonRec1.fontoutline;
        self.coutlinecolor.Selected:=PBalloonRec1.fontoutline_color;
        self.coutlinedepth.Value:=PBalloonRec1.fontoutline_width;
        self.cuseantialias.Checked:=PBalloonRec1.antialias;
        self.SpinEdit4.Value:=PBalloonRec1.topmargin;
        self.SpinEdit5.Value:=PBalloonRec1.leftmargin;
      finally
        blockapply:=false;
      end;
    end;

  end;
end;

procedure TfrmEffect_Draw.ApplyText;
var
  PBalloonRec1:PBalloonRec;
begin
  if Selection=nil then exit;
  PBalloonRec1:=BalloonList_get(TPositionedLayer(Selection).Tag);
  PBalloonRec1.caption.Assign(memo1.Lines);
  PBalloonRec1.font.Assign(FontDialog1.Font);
  PBalloonRec1.fontoutline:=self.cuseoutline.Checked;
  PBalloonRec1.fontoutline_color:=self.coutlinecolor.Selected;
  PBalloonRec1.fontoutline_width:=self.coutlinedepth.Value;
  PBalloonRec1.antialias:=self.cuseantialias.Checked;
  PBalloonRec1.topmargin:=self.SpinEdit4.Value;
  PBalloonRec1.leftmargin:=self.SpinEdit5.Value;

  imagepreview1.Invalidate;
end;

function TfrmEffect_Draw.move_selectlayer(key,pitch:integer):boolean;
var
  r:Tfloatrect;
begin
  result:=true;
      if (RBLayer<>nil) then begin
        r:=RBLayer.Location;
        case key of
          vk_left:begin
            r.Left:=r.Left-pitch;
            r.Right:=r.Right-pitch;
          end;
          VK_RIGHT:begin
            r.Left:=r.Left+pitch;
            r.Right:=r.Right+pitch;
          end;
          VK_UP:begin
            r.Top:=r.Top-pitch;
            r.Bottom:=r.Bottom-pitch;
          end;
          VK_DOWN:begin
            r.Top:=r.Top+pitch;
            r.Bottom:=r.Bottom+pitch;
          end;
          else
            result:=false;
        end;
        if result then begin
          RBLayer.Location:=r;
          setlayerloaction();
        end;
      end else
        result:=false;
end;

procedure TfrmEffect_Draw.Button3Click(Sender: TObject);
begin
  FontDialog1.Font.Color:=self.cfontcolor.Selected;
  FontDialog1.Font.Size:=self.cfontsize.Value;
  if FontDialog1.Execute then begin
    blockapply:=true;
    try
      self.cfontcolor.Selected:=FontDialog1.Font.Color;
      self.cfontsize.Value:=FontDialog1.Font.Size;
    finally
      blockapply:=false;
    end;
    ApplyText;
  end;
end;

procedure TfrmEffect_Draw.cfontcolorChange(Sender: TObject);
begin
  if blockapply=false then begin
    FontDialog1.Font.Color:=self.cfontcolor.Selected;
    FontDialog1.Font.Size:=self.cfontsize.Value;
    ApplyText;
  end;
end;

procedure TfrmEffect_Draw.cuseoutlineClick(Sender: TObject);
begin
  if blockapply=false then
    ApplyText;
end;

procedure TfrmEffect_Draw.coutlinecolorChange(Sender: TObject);
begin
  if blockapply=false then
    ApplyText;
end;

procedure TfrmEffect_Draw.coutlinedepthChange(Sender: TObject);
begin
  if blockapply=false then
    ApplyText;
end;

procedure TfrmEffect_Draw.setlayerloaction();
var
  r1:trect;
  i:integer;
begin
  if Selection<>nil then begin
    r1:=makerect(TPositionedLayer(Selection).Location);
    labeldisplay2.Caption:=format('레이어 x:%d,y:%d -> x:%d,y:%d (%d,%d)',[
      r1.Left,r1.Top,r1.Right,r1.Bottom,r1.Right-r1.Left,r1.Bottom-r1.Top]);
  end else begin
    labeldisplay2.Caption:='';
  end;
  i:=labeldisplay1.Left+labeldisplay1.Width+20;
  if i>labeldisplay2.Left then
    labeldisplay2.Left:=i;
end;

procedure TfrmEffect_Draw.Timer1Timer(Sender: TObject);
var
  PBalloonRec1:PBalloonRec;
begin
  if Selection=nil then exit;
  PBalloonRec1:=BalloonList_get(TPositionedLayer(Selection).Tag);
  if PBalloonRec1.drawcaption then begin
    if memo1.Text<>PBalloonRec1.caption.Text then begin
      PBalloonRec1.caption.Assign(memo1.Lines);
      imagepreview1.Invalidate;
    end;
  end;
end;

procedure TfrmEffect_Draw.mnu_layerflattenClick(Sender: TObject);
var
  destrect:TRect;
  bitmap:Tbitmap;
  bitmap32:TBitmap32;
  W,H:Integer;
  ALayer: TPositionedLayer;

  i:integer;
  PBalloonRec1:PBalloonRec;
  UndoData1:TUndoData;
begin
  if Selection=nil then exit;

  PBalloonRec1:=BalloonList_get(Selection.Tag);
  if PBalloonRec1.drawcaption and (imagepreview1.Scale<>1) then
    frmmain.showballoon('알림','100% 이외의 비율에서는 글자가 부정확하게 들어갈수 있습니다.',self.ScaleCombo,mtwarning);

  destrect:=MakeRect(TPositionedLayer(Selection).Location);

  for i:=0 to imagepreview1.Layers.Count-1 do
    if (imagepreview1.Layers[i]<>Selection) then begin
      imagepreview1.Layers[i].Visible:=false;
    end;

  W:=imagepreview1.Bitmap.Width;
  H:=imagepreview1.Bitmap.Height;

  if destrect.Left<0 then destrect.Left:=0;
  if destrect.Right>W then destrect.Right:=W;
  if destrect.Top<0 then destrect.Top:=0;
  if destrect.Bottom>H then destrect.Bottom:=H;

  ALayer:=Selection;

  //add undo
  screen.Cursor:=crHourglass;
  bitmap:=tbitmap.Create;
  try
    imagepreview1.Bitmap.AssignTo24(bitmap);
    UndoData1.bitmap:=bitmap;
    UndoData1.targetrect:=rect(0,0,bitmap.Width,bitmap.Height);
    UndoData1.name:='Draw';
    UndoData1.resized:=true;
    UndoManage1.addundo(bitmap,UndoData1,false);
  finally
    bitmap.Free;
    screen.Cursor:=crdefault;
  end;

  flattening:=true;
  bitmap32:=TBitmap32.Create;
  try
    bitmap32.SetSize(W, H);
    imagepreview1.PaintTo(bitmap32, Rect(0, 0, W, H));
    Selection:=nil;
    imagepreview1.Bitmap.Draw(destrect,destrect,bitmap32);
  finally
    flattening:=false;
    bitmap32.Free;
  end;

  for i:=0 to imagepreview1.Layers.Count-1 do
    imagepreview1.Layers[i].Visible:=true;

  Selection:=ALayer;
  mnu_layerdeleteClick(nil);
end;

procedure TfrmEffect_Draw.N7Click(Sender: TObject);
var
  bitmap32:TBitmap32;
  bitmap:TBitmap;
  W,H:Integer;
  i,j:integer;
  PBalloonRec1:PBalloonRec;
  flag:boolean;
  UndoData1:TUndoData;
begin
  if BalloonList.Count=0 then exit;

  if MessageDlg('모두 이미지에 적용하시겠습니까?',  mtConfirmation, [mbYes, mbNo], 0) <> mrYes then
    exit;

  flag:=false;
  for i:=0 to imagepreview1.Layers.Count-1 do
    if imagepreview1.Layers[i]<>RBLayer then begin
      PBalloonRec1:=BalloonList_get(imagepreview1.Layers[i].Tag);
      if PBalloonRec1.drawcaption then begin
        flag:=true;
        break;
      end;
    end;

  if flag and (imagepreview1.Scale<>1) then
    frmmain.showballoon('알림','100% 이외의 비율에서는 글자가 부정확하게 들어갈수 있습니다.',self.ScaleCombo,mtwarning);

  //add undo
  screen.Cursor:=crHourglass;
  bitmap:=tbitmap.Create;
  try
    imagepreview1.Bitmap.AssignTo24(bitmap);
    UndoData1.bitmap:=bitmap;
    UndoData1.targetrect:=rect(0,0,bitmap.Width,bitmap.Height);
    UndoData1.name:='Draw';
    UndoData1.resized:=true;
    UndoManage1.addundo(bitmap,UndoData1);
  finally
    bitmap.Free;
    screen.Cursor:=crdefault;
  end;

  Selection:=nil;
  W:=imagepreview1.Bitmap.Width;
  H:=imagepreview1.Bitmap.Height;

  flattening:=true;
  bitmap32:=TBitmap32.Create;
  try
    bitmap32.SetSize(W, H);
    imagepreview1.PaintTo(bitmap32, Rect(0, 0, W, H));
    imagepreview1.Bitmap.Assign(bitmap32);
  finally
    flattening:=false;
    bitmap32.Free;
  end;

  for i:=imagepreview1.Layers.Count-1 downto 0 do begin
    if imagepreview1.Layers[i]<>RBLayer then
      imagepreview1.Layers[i].Free;
  end;
  BalloonList_clear;
end;

procedure TfrmEffect_Draw.N1Click(Sender: TObject);
var
  L: TPositionedLayer;
  PBalloonRec1:PBalloonRec;
  r:tfloatrect;
begin
  New(PBalloonRec1);
  inc(BalloonCount);
  PBalloonRec1.idx:=BalloonCount;
  PBalloonRec1.filename:='';
  PBalloonRec1.Metafile:=nil;
  PBalloonRec1.caption:=TStringList.Create;
  PBalloonRec1.caption.Assign(memo1.Lines);
  PBalloonRec1.font:=tfont.Create;
  PBalloonRec1.font.Assign(FontDialog1.Font);
  PBalloonRec1.fontoutline:=self.cuseoutline.Checked;
  PBalloonRec1.fontoutline_color:=self.coutlinecolor.Selected;
  PBalloonRec1.fontoutline_width:=self.coutlinedepth.Value;
  PBalloonRec1.antialias:=self.cuseantialias.Checked;
  PBalloonRec1.drawtype:=Tdrawtype((sender as tmenuitem).Tag);
  if PBalloonRec1.drawtype=dtnone then begin
    PBalloonRec1.drawcaption:=true;
    PBalloonRec1.drawfill:=false;
  end else begin
    PBalloonRec1.drawcaption:=boolean(config.getvaluebyinteger('ballon_drawcaption',0));
    PBalloonRec1.drawfill:=boolean(config.getvaluebyinteger('ballon_drawfill',0));
  end;
  PBalloonRec1.drawcolor:=config.getvaluebyinteger('ballon_drawcolor',clblack);
  PBalloonRec1.drawwidth:=config.getvaluebyinteger('ballon_drawwidth',3);
  PBalloonRec1.drawfillcolor:=config.getvaluebyinteger('ballon_drawfillcolor',clwhite);
  PBalloonRec1.topmargin:=SpinEdit4.Value;
  PBalloonRec1.leftmargin:=SpinEdit5.Value;

  if trim(PBalloonRec1.caption.Text)='' then PBalloonRec1.caption.Text:=defaulttext;
//  PBalloonRec1.caption.Add('여기에 원하는 글을 입력하세요!');
//  PBalloonRec1.caption.Add('감사합니다 ^^');

  BalloonList.Add(PBalloonRec1);

  L := CreatePositionedLayer;
  r:=L.Location;
  r.Bottom:=r.Top+((r.Bottom-r.Top) / 2);
  L.Location:=r;
  L.OnPaint := PaintSimpleDrawingHandler;
  L.Tag := BalloonCount;
  Selection := L;
  imagepreview1.SetFocus;
end;

procedure TfrmEffect_Draw.N10Click(Sender: TObject);
var
  PBalloonRec1:PBalloonRec;
begin
  if Selection=nil then exit;
  PBalloonRec1:=BalloonList_get(Selection.Tag);
  if assigned(copy_BalloonRec) then begin
    freeandnil(copy_BalloonRec.metafile);
    freeandnil(copy_BalloonRec.font);
    freeandnil(copy_BalloonRec.caption);
    dispose(copy_BalloonRec);
  end;
  new(copy_BalloonRec);
  copy_BalloonRec^:=PBalloonRec1^;

  if PBalloonRec1.metafile=nil then
    copy_BalloonRec.Metafile:=nil
  else begin
    copy_BalloonRec.Metafile:=TMetafile.Create;
    copy_BalloonRec.Metafile.Assign(PBalloonRec1.metafile);
  end;
  copy_BalloonRec.caption:=TStringList.Create;
  copy_BalloonRec.caption.Assign(PBalloonRec1.caption);
  copy_BalloonRec.font:=tfont.Create;
  copy_BalloonRec.font.Assign(PBalloonRec1.font);
  copy_BalloonRec.location:=Selection.Location;
end;

procedure TfrmEffect_Draw.N11Click(Sender: TObject);
var
  L: TPositionedLayer;
  PBalloonRec1:PBalloonRec;
  r:tfloatrect;
  P,P2: TPoint;
begin
  New(PBalloonRec1);
  PBalloonRec1^:=copy_BalloonRec^;

  inc(BalloonCount);
  PBalloonRec1.idx:=BalloonCount;
  if copy_BalloonRec.metafile=nil then
    PBalloonRec1.metafile:=nil
  else begin
    PBalloonRec1.Metafile:=TMetafile.Create;
    PBalloonRec1.Metafile.Assign(copy_BalloonRec.metafile);
  end;
  PBalloonRec1.caption:=TStringList.Create;
  PBalloonRec1.caption.Assign(copy_BalloonRec.caption);
  PBalloonRec1.font:=tfont.Create;
  PBalloonRec1.font.Assign(copy_BalloonRec.font);

  BalloonList.Add(PBalloonRec1);

  L := TPositionedLayer.Create(imagepreview1.Layers);

  r:=PBalloonRec1.location;
  with imagepreview1.GetViewportRect do
   P := imagepreview1.ControlToBitmap(Point(Left,Top));
  with imagepreview1.GetViewportRect do
   P2 := imagepreview1.ControlToBitmap(Point(Right,Bottom));

  if r.Left<p.X then r.Left:=p.X;
  if r.Top<p.Y then r.Top:=p.Y;
  if r.Left>p2.X then r.Left:=p.X;
  if r.Top>p2.Y then r.Top:=p.Y;

  r.Right:=r.Left+(PBalloonRec1.location.Right-PBalloonRec1.location.Left);
  r.Bottom:=r.Top+(PBalloonRec1.location.Bottom-PBalloonRec1.location.Top);

  L.Location:=r;
  L.Scaled := true;
  L.MouseEvents := True;
  L.OnMouseDown:=LayerMouseDown;
  L.OnPaint := PaintSimpleDrawingHandler;
  L.Tag := BalloonCount;
  Selection := L;

  RBLayer.HandleFrame:=color32(clred);
  RBLayer.HandleFill:=color32(clred);
  RBLayer.Update;

  imagepreview1.SetFocus;
end;

procedure TfrmEffect_Draw.N6Click(Sender: TObject);
var
  PBalloonRec1,PBalloonRec2:PBalloonRec;
  dstrect:trect;
  pt:tpoint;
begin
  if Selection=nil then exit;
  PBalloonRec1:=BalloonList_get(Selection.Tag);
  if PBalloonRec1.metafile<>nil then exit;

  new(PBalloonRec2);
  frmEffect_Draw2:=TfrmEffect_Draw2.Create(self);
  try
    PBalloonRec2^:=PBalloonRec1^;
    frmEffect_Draw2.PBalloonRec1:=PBalloonRec1;
    frmEffect_Draw2.frmEffect_Balloon:=self;

    dstrect:=MakeRect(Selection.GetAdjustedLocation);
    pt:=imagepreview1.ClientToScreen(point(0,0));

    pt.X:=pt.X+dstrect.Left;
    pt.Y:=pt.Y+dstrect.Bottom;

    if pt.X<screen.WorkAreaRect.Left then pt.X:=screen.WorkAreaRect.Left;
    if pt.Y<screen.WorkAreaRect.Top then pt.Y:=screen.WorkAreaRect.Top;
    if (pt.X+frmEffect_Draw2.Width)>screen.WorkAreaRect.Right then
      pt.X:=screen.WorkAreaRect.Right-frmEffect_Draw2.Width;
    if (pt.Y+frmEffect_Draw2.Height)>screen.WorkAreaRect.Bottom then
      pt.Y:=screen.WorkAreaRect.Bottom-frmEffect_Draw2.Height;

    frmEffect_Draw2.Left:=pt.X;
    frmEffect_Draw2.Top:=pt.Y;

    if frmEffect_Draw2.ShowModal=mrok then begin
      config.setvaluebyinteger('ballon_drawcaption',integer(PBalloonRec1.drawcaption));
      config.setvaluebyinteger('ballon_drawcolor',PBalloonRec1.drawcolor);
      config.setvaluebyinteger('ballon_drawwidth',PBalloonRec1.drawwidth);
      config.setvaluebyinteger('ballon_drawfill',integer(PBalloonRec1.drawfill));
      config.setvaluebyinteger('ballon_drawfillcolor',PBalloonRec1.drawfillcolor);
    end else begin
      PBalloonRec1^:=PBalloonRec2^;
      imagepreview1.Invalidate;
    end;
  finally
    frmEffect_Draw2.Free;
    dispose(PBalloonRec2);
  end;
end;

procedure TfrmEffect_Draw.mnu_layerdeleteClick(Sender: TObject);
begin
  if Selection=nil then exit;
  BalloonList_del(TPositionedLayer(Selection).Tag);
  TPositionedLayer(Selection).Free;
  Selection:=nil;
end;

procedure TfrmEffect_Draw.pMlayerPopup(Sender: TObject);
var
  PBalloonRec1:PBalloonRec;
begin
  mnu_layerflatten.Enabled:=Selection<>nil;
  mnu_layerdelete.Enabled:=Selection<>nil;
  N6.Enabled:=Selection<>nil;
  if N6.Enabled then begin
    PBalloonRec1:=BalloonList_get(Selection.Tag);
    N6.Enabled:=PBalloonRec1.metafile=nil;
  end;
  N7.Enabled:=BalloonList.Count>0;
  n8.Enabled:=UndoManage1.getcount>0;
  n10.Enabled:=Selection<>nil;
  n11.Enabled:=copy_BalloonRec<>nil;
end;

procedure TfrmEffect_Draw.N8Click(Sender: TObject);
var
  bitmap:TBitmap;
  idx:integer;
begin
  idx:=UndoManage1.getcount-1;
  if idx<0 then exit;
  screen.Cursor:=crHourglass;
  bitmap:=TBitmap.Create;
  try
    if UndoManage1.getundo(bitmap,idx+1) then begin
      imagepreview1.Bitmap.Assign(bitmap);
    end;
  finally
    bitmap.Free;
    screen.Cursor:=crdefault;
  end;
end;

procedure TfrmEffect_Draw.Splitter1Moved(Sender: TObject);
begin
  ListView1.Arrange(arAlignTop);
end;

procedure TfrmEffect_Draw.SpinEdit4Change(Sender: TObject);
begin
  if blockapply=false then
    ApplyText;
end;

end.
