{Copyright Francesco Savastano 2001 : This source is free , but you
 cannot distribute it , if you want others have it , let them know
 to come to my web site : http://digilander.iol.it/gensavas/francosava/
If you put this algorithm in your free or commercial software i'd like my name in your thanx list .
 also check out my photoretouch program at : http://web.tiscalinet.it/new_world_software/ }

 {Description :unit to implement the famous magic wand algorithm : an other secret is revealed !
 the use of this unit is shown in the attached delphi sample program }
 unit magwand;



interface


uses
  Windows,SysUtils, Classes, Graphics,math;

type
tbool2darray=array of array of boolean;
TPointArray= array of TPoint;
procedure magicwand(var bitmap1:tbitmap;a,b:integer;oldcolor:tcolor;tolerance:real;var mask:tbool2darray);
procedure exec(var bitmap1:tbitmap;xf,yf:integer;var nf:integer;oldcolor:tcolor;tolerance:real;var mask:tbool2darray);
procedure showmagicwand(var bitmap1:tbitmap;var mask:tbool2darray);
Procedure MakePath(var mask:tbool2darray;var Points:TPointArray;var closed:Boolean);
Procedure BoundMask(var mask:tbool2darray;var maskX:tbool2darray);


implementation
type
  trgbtriplerow=array[0..30000]of trgbtriple;
  prgbtriplerow=^trgbtriplerow;

var
  fillx:array[0..80000]of integer;
  filly:array[0..80000]of integer;


  {magic wand algorithm same as seed fill algorithm}
procedure magicwand;
var
  nf:integer;
  i,ir:integer;
  xf,yf:integer;
begin
nf := 1;
ir := 1;
fillx[nf]:= a;
filly[nf]:= b;
exec(bitmap1,a,b,nf,oldcolor,tolerance,mask);
while nf>ir do
begin
ir := ir+1;
xf := fillx[ir];
yf := filly[ir];
exec(bitmap1,xf,yf,nf,oldcolor,tolerance,mask);
if (nf>75000)  then
begin
for i := 1 to nf-ir do
begin
fillx[i] := fillx[ir+i];
filly[i] := filly[ir+i];
end;
nf := nf-ir ;
ir := 0;
end;
end;
end;


procedure exec;
var
hh,ll,ss,h1,l1,s1,aa,bb,cc:real;
c1,c2,c3,pixr,pixg,pixb,oldr,oldg,oldb,newr,newg,newb:byte;
pix:tcolorref;
jj:byte;
xr,yr:longint;
gray:real;
pp:prgbtriplerow;
begin
oldr:=getrvalue(oldcolor);
oldg:=getgvalue(oldcolor);
oldb:=getbvalue(oldcolor);

for jj:=1 to 4 do
begin
if jj=1 then
begin
xr:=xf+1;
yr:=yf;
end;
if jj=2 then
begin
xr:=xf-1;
yr:=yf;
end;
if jj=3 then
begin
xr:=xf;
yr:=yf+1;
end;
if jj=4 then
begin
xr:=xf;
yr:=yf-1;
end;
if ((xr< bitmap1.width)and(jj=1))or((xr>=0)and(jj=2))
or ((yr< bitmap1.height)and(jj=3))or((yr>=0)and(jj=4)) then
begin
pp:=bitmap1.scanline[yr];
pix:=rgb(pp[xr].rgbtred,pp[xr].rgbtgreen,pp[xr].rgbtblue);
pixr:=getrvalue(pix);
pixg:=getgvalue(pix);
pixb:=getbvalue(pix);
if (not mask[xr,yr])and(abs(pixr-oldr)<=tolerance*150)and(abs(pixg-oldg)<=tolerance*150)and(abs(pixb-oldb)<=tolerance*150)then
begin
mask[xr,yr]:=true;
nf := nf+1;
fillx[nf]:= xr;
filly[nf]:= yr;
end;
end;
end;
end;

{This is only one of possible ways to show the result of the magic wand selection (stored in the array "mask" ) onto a bitmap ,
sorry but the selection will not be animated like you see in some photoretouch softwares ,
but i don't think this is very difficult to do !}

procedure showmagicwand;
var
i,j:integer;
test:boolean;
pp:prgbtriplerow;
begin
  for j:=1 to bitmap1.height-2 do
  begin
    pp:=bitmap1.scanline[j];
    for i:=1 to bitmap1.width-2 do
    begin
      if mask[i,j]then
      begin
        test:= mask[i-1,j] and mask[i+1,j] and mask[i,j-1] and mask[i,j+1] ;
        if not test then
        begin
          {simply invert the colors of the boundary pixels }
          with pp[i] do
          begin
            rgbtred:=255-rgbtred;
            rgbtgreen:=255-rgbtgreen;
            rgbtblue:=255-rgbtblue;
          end;
        end;
      end;
    end;
  end;
end;

Procedure BoundMask(var mask:tbool2darray;var maskX:tbool2darray);
var
i,j:integer;
lenH,lenW: Integer;
test: Boolean;
Si,sJ,ci,cj: Integer;
first:Boolean;
done: Boolean;
pCnt:Integer;
begin
  lenW := Length(mask);
  lenH := Length(mask[0]);
  SetLength(maskX,LenW);
  for i := 0 to LenW-1 do SetLength(maskX[i],LenH);
  for j := 0 to Lenh-1 do
    for i := 0 to LenW-1 do
      maskX[i,j] := false;
  first := True;
  for j:=1 to lenH-2 do
  begin
    //pp:=bitmap1.scanline[j];
    for i:=1 to lenW-2 do
    begin
      if mask[i,j]then
      begin
        test:= mask[i-1,j] and mask[i+1,j] and mask[i,j-1] and mask[i,j+1] ;
        if not test then
        begin
          {simply invert the colors of the boundary pixels }
          maskX[i,j] := True;
          if first then begin
            si := i;
            sj := j;
            first := false;
          end;
        end;
      end;
    end;
  end;
end;

Procedure MakePath(var mask:tbool2darray;var Points:TPointArray; var closed: Boolean);
var
i,j:integer;
lenH,lenW: Integer;
MaskX:tbool2darray;
test: Boolean;
Si,sJ,ci,cj: Integer;
first:Boolean;
done: Boolean;
pCnt:Integer;
begin
  Closed := false;
  si := -1;
  sj := -1;

  lenW := Length(mask);
  lenH := Length(mask[0]);
  SetLength(maskX,LenW);
  SetLength(Points,0);
  for i := 0 to LenW-1 do SetLength(maskX[i],LenH);
  for j := 0 to Lenh-1 do
    for i := 0 to LenW-1 do
      maskX[i,j] := false;
  first := True;
  for j:=1 to lenH-2 do
  begin
    //pp:=bitmap1.scanline[j];
    for i:=1 to lenW-2 do
    begin
      if mask[i,j]then
      begin
        test:= mask[i-1,j] and mask[i+1,j] and mask[i,j-1] and mask[i,j+1] ;
        if not test then
        begin
          {simply invert the colors of the boundary pixels }
          maskX[i,j] := True;
          if first then begin
            si := i;
            sj := j;
            first := false;
          end;
        end;
      end;
    end;
  end;
  if (si = -1) or (sj = -1) then exit;
  ci := si;
  cj := sj;
  SetLength(Points,1);
  Points[0] := Point(si,sj);
  pCnt := 1;
  done := false;
  repeat
    if (ci < LenW-1) and maskX[ci+1,cj] then begin
      ci := ci+1;
    end else if (cj > 0) and maskX[ci,cj-1] then begin
      cj := cj-1;
    end else if (cj < LenH-1) and maskX[ci,cj+1] then begin
      cj := cj+1;
    end else if (ci > 0) and maskX[ci-1,cj] then begin
      ci := ci-1;
    end else if (ci > 0) and (cj < LenH-1) and maskX[ci-1,cj+1] then begin
      ci := ci-1;
      cj := cj+1;
    end else if (cj < LenH-1) and (ci < LenW-1) and maskX[ci+1,cj+1] then begin
      ci := ci+1;
      cj := cj+1;
    end else if (ci > 0) and (cj > 0) and maskX[ci-1,cj-1] then begin
      ci := ci-1;
      cj := cj-1;
    end else if (ci < LenW-1) and (cj >0) and maskX[ci+1,cj-1] then begin
      ci := ci+1;
      cj := cj-1;
    end else begin
      done := True;
    end;
    if not done then begin
      pCnt := pCnt+1;
      SetLength(Points,pCnt);
      Points[pCnt-1] := Point(ci,cj);
      maskx[ci,cj] := false;
    end;
    if done then begin
      if (abs(ci-si) < 2) and (abs(cj-sj) < 2) then closed := True;
    end;
  until done;

end;



end.
 