PHOTOSHOP等图形图象处理软件,其中有一个魔术棒的功能,当选取某一点后,相同颜色的区域将被一条流动“蚂蚁线”包围。请问如何实现这一效果,具体的算法如何?谢谢!

解决方案 »

  1.   

    實現的算法, 以當前坐標, 不斷的在x , y 方向增加 or 減少, 判斷比較像素的顏色!不知高效的算法又是如何?
      

  2.   

    种子填充,很基础的算法,搞图形图像软件,这么基础的东西没理由不好好掌握~~~流动效果试试LineDDA吧
      

  3.   

    这是我以前一个截屏软件的部分代码,里面有矩形蚂蚁线的实现部分,可以参考一下,但是魔术棒功能是需要path转换来做的,魔术棒代码不方便贴,你自己研究吧!unit AreaForm;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, ExtCtrls;type
      TAreaFrm = class(TForm)
        Image1: TImage;
        PaintBox1: TPaintBox;
        Timer1: TTimer;
        Panel1: TPanel;
        PaintBox2: TPaintBox;
        procedure PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
          Shift: TShiftState; X, Y: Integer);
        procedure PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
          Shift: TShiftState; X, Y: Integer);
        procedure PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
          Y: Integer);
        procedure FormCreate(Sender: TObject);
        procedure PaintBox1Paint(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
        procedure FormKeyDown(Sender: TObject; var Key: Word;
          Shift: TShiftState);
        procedure Timer1Timer(Sender: TObject);
      private
        FClickCounter: Integer;
        FSelectRect: TRect;
        FPoint: TPoint;
        procedure DrawSelectionRect;
        procedure RemoveTheRect;
        procedure DrawTheRect;
        Procedure WMEraseBkGnd(var Msg: TWMEraseBkGnd); message WM_ERASEBKGND;
        { Private declarations }
      public
        Bmp: TBitmap;
        { Public declarations }
      end;var
      AreaFrm: TAreaFrm;
      Counter: Byte;
      CounterStart: Byte;
      Looper: LongInt;const
      CM_AREACLIP_DONE = WM_USER + 1000;implementationuses Types;{$R *.dfm}procedure MovingDots(X, Y: Integer; TheCanvas: TCanvas); stdcall;
    begin
      Inc(Looper);
      Counter := Counter shl 1;             // Shift the bit left one
      if Counter = 0 then
        Counter := 1;                       // If it shifts off left, reset it
      if (Counter and 224) > 0 then         // Are any of the left 3 bits set?
        TheCanvas.Pixels[X, Y] := clBlack   // Erase the pixel
      else
        TheCanvas.Pixels[X, Y] := clWhite;  // Draw the pixel
    end;function NormalizeRect(R: TRect): TRect;
    begin
      // This routine normalizes a rectangle. It makes sure that the Left,Top
      // coords are always above and to the left of the Bottom,Right coords.
      with R do
        if Left > Right then
          if Top > Bottom then
            Result := Rect(Right, Bottom, Left, Top)
          else
            Result := Rect(Right, Top, Left, Bottom)
        else if Top > Bottom then
          Result := Rect(Left, Bottom, Right, Top)
        else
          Result := Rect(Left, Top, Right, Bottom);
    end;procedure TAreaFrm.PaintBox1MouseDown(Sender: TObject;
      Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    var
      Temp: Integer;
    begin
      if FClickCounter = 0 then
      begin
        if IsRectEmpty(FSelectRect) then
        begin
          FSelectRect.Left := X;
          FSelectRect.Right := X;
          FSelectRect.Top := Y;
          FSelectRect.Bottom := Y;
          //DrawSelectionRect;
          Inc(FClickCounter);
        end
        else
        begin
          {DrawSelectionRect;
          // 把选择框矩形置为空
          FSelectRect := Rect(0, 0, 0, 0);
          // 重画Image的画布
          Invalidate;}
        end;
      end
      else if FClickCounter > 0 then
      begin
        if FSelectRect.Left > FSelectRect.Right then
        begin
          Temp := FSelectRect.Left;
          FSelectRect.Left := FSelectRect.Right;
          FSelectRect.Right := Temp;
        end;
        if FSelectRect.Top > FSelectRect.Bottom then
        begin
          Temp := FSelectRect.Top;
          FSelectRect.Top := FSelectRect.Bottom;
          FSelectRect.Bottom := Temp;
        end;
        //DrawSelectionRect;
        DrawTheRect;    // 开始拷屏到Bmp
        Bmp.Width := FSelectRect.Right - FSelectRect.Left;
        Bmp.Height := FSelectRect.Bottom - FSelectRect.Top;
        BitBlt(Bmp.Canvas.Handle, 0, 0, Bmp.Width, Bmp.Height,
          Image1.Canvas.Handle, FSelectRect.Left, FSelectRect.Top, SRCCOPY);    // 把选择框矩形置为空
        FSelectRect := Rect(0, 0, 0, 0);
        // 将计数器清0
        Dec(FClickCounter);    ReleaseCapture;    // 这是第二次点击所以要把矩形内部的图像拷到Bmp中,并通知主窗体可以接受Bmp了
        SendMessage(Application.MainForm.Handle, CM_AREACLIP_DONE, 0, 0);
      end;
    end;procedure TAreaFrm.PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    //var
      //Temp: Integer;
    begin
      {if IsDown then
      begin
        if FSelectRect.Left > FSelectRect.Right then
        begin
          Temp := FSelectRect.Left;
          FSelectRect.Left := FSelectRect.Right;
          FSelectRect.Right := Temp;
        end;
        if FSelectRect.Top > FSelectRect.Bottom then
        begin
          Temp := FSelectRect.Top;
          FSelectRect.Top := FSelectRect.Bottom;
          FSelectRect.Bottom := Temp;
        end;
        IsDown := False;
      end;}
    end;procedure TAreaFrm.PaintBox1MouseMove(Sender: TObject; Shift: TShiftState;
      X, Y: Integer);
    begin
      {if FClickCounter > 0 then
        if (X <> FSelectRect.Right) or (Y <> FSelectRect.Bottom) then
        begin
          // 画两次,第一次消除前一个矩形,后一次画新矩形
          DrawSelectionRect;
          FSelectRect.Right := X;
          FSelectRect.Bottom := Y;
          DrawSelectionRect;
        end;}  if FClickCounter > 0 then
      begin
        if (X <> FSelectRect.Right) or (Y <> FSelectRect.Bottom) then
        begin
          // Erase any existing rectangle
          RemoveTheRect;
          FSelectRect.Right := X;
          FSelectRect.Bottom := Y;
          DrawTheRect;
        end;
      end;  StretchBlt(PaintBox2.Canvas.Handle, 0, 0,
        PaintBox2.Width, PaintBox2.Height,
        PaintBox1.Canvas.Handle, X - 10, Y - 10, 20, 20, SRCCOPY);
    end;procedure TAreaFrm.DrawSelectionRect;
    begin
      // 绘制橡皮筋矩形
      // 也可以采用其它GDI绘图函数来画,都一样,只是要记得
      // 在原位置再画一遍,擦除掉原来的矩形!
      PaintBox1.Canvas.Polygon([Point(FSelectRect.Left, FSelectRect.Top),
        Point(FSelectRect.Right, FSelectRect.Top),
          Point(FSelectRect.Right, FSelectRect.Bottom),
          Point(FSelectRect.Left, FSelectRect.Bottom)]);
    end;procedure TAreaFrm.FormCreate(Sender: TObject);
    begin
      // 鼠标按下的计数器 0 = 没有按过 1 = 已经按过一次
      FClickCounter := 0;  Bmp := TBitmap.Create;
      Bmp.PixelFormat := pf24bit;  DoubleBuffered := True;
      //PaintBox1.Canvas.Pen.Style := psDash;
      {PaintBox1.Canvas.Pen.Style := psSolid;
      PaintBox1.Canvas.Pen.Width := 1;
      PaintBox1.Canvas.Pen.Color := clWhite;
      PaintBox1.Canvas.Pen.Mode := pmXor;}  // Setup Animated Rubberband
      with PaintBox1 do
      begin
        Canvas.Pen.Color := clWhite;
        Canvas.Brush.Color := clBlack;
      end;
      CounterStart := 128;
      Timer1.Interval := 300;
      Timer1.Enabled := True;
      Looper := 0;
    end;
      

  4.   

    接上贴!CSDN连这么短的帖子都不能贴全!??procedure TAreaFrm.PaintBox1Paint(Sender: TObject);
    begin
      // to do
    end;procedure TAreaFrm.FormDestroy(Sender: TObject);
    begin
      Bmp.Free;
    end;procedure TAreaFrm.FormKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    var
      Temp: Integer;
    begin
      if FSelectRect.Left <= 0 then
        FSelectRect.Left := 0;
      if FSelectRect.Top <= 0 then
        FSelectRect.Top := 0;
      if FSelectRect.Right >= Screen.Width then
        FSelectRect.Right := Screen.Width;
      if FSelectRect.Bottom >= Screen.Height then
        FSelectRect.Bottom := Screen.Height;  if FSelectRect.Left > FSelectRect.Right then
      begin
        Temp := FSelectRect.Left;
        FSelectRect.Left := FSelectRect.Right;
        FSelectRect.Right := Temp;
      end;
      if FSelectRect.Top > FSelectRect.Bottom then
      begin
        Temp := FSelectRect.Top;
        FSelectRect.Top := FSelectRect.Bottom;
        FSelectRect.Bottom := Temp;
      end;
      //DrawSelectionRect;
      DrawTheRect;  case Key of
        VK_ESCAPE:
          begin
            //MessageBeep(0);
            FSelectRect := Rect(0, 0, 0, 0);
            Dec(FClickCounter);
            ReleaseCapture;        Hide;
            Close;
            Application.Restore;
          end;
        VK_RETURN:
          begin
            if FSelectRect.Left > FSelectRect.Right then
            begin
              Temp := FSelectRect.Left;
              FSelectRect.Left := FSelectRect.Right;
              FSelectRect.Right := Temp;
            end;
            if FSelectRect.Top > FSelectRect.Bottom then
            begin
              Temp := FSelectRect.Top;
              FSelectRect.Top := FSelectRect.Bottom;
              FSelectRect.Bottom := Temp;
            end;
            //DrawSelectionRect;
            DrawTheRect;        // 开始拷屏到Bmp
            Bmp.Width := FSelectRect.Right - FSelectRect.Left;
            Bmp.Height := FSelectRect.Bottom - FSelectRect.Top;
            BitBlt(Bmp.Canvas.Handle, 0, 0, Bmp.Width, Bmp.Height,
              Image1.Canvas.Handle, FSelectRect.Left, FSelectRect.Top, SRCCOPY);        // 把选择框矩形置为空
            FSelectRect := Rect(0, 0, 0, 0);
            // 将计数器清0
            Dec(FClickCounter);        ReleaseCapture;        // 这是第二次点击所以要把矩形内部的图像拷到Bmp中,并通知主窗体可以接受Bmp了
            SendMessage(Application.MainForm.Handle, CM_AREACLIP_DONE, 0, 0);
          end;
        VK_DOWN:
          begin
            if ssCtrl in Shift then
            begin
              RemoveTheRect;
              FSelectRect.Top := FSelectRect.Top + 1;
              FSelectRect.Bottom := FSelectRect.Bottom + 1;
              DrawTheRect;
              SetCursorPos(FSelectRect.Right, FSelectRect.Bottom);
            end
            else
            begin
              GetCursorPos(FPoint);
              Inc(FPoint.Y);
              SetCursorPos(FPoint.X, FPoint.Y);
            end;
          end;
        VK_UP:
          begin
            if ssCtrl in Shift then
            begin
              RemoveTheRect;
              Dec(FSelectRect.Top);
              Dec(FSelectRect.Bottom);
              DrawTheRect;
              SetCursorPos(FSelectRect.Right, FSelectRect.Bottom);
            end
            else
            begin
              GetCursorPos(FPoint);
              Dec(FPoint.Y);
              SetCursorPos(FPoint.X, FPoint.Y);
            end;
          end;
        VK_LEFT:
          begin
            if ssCtrl in Shift then
            begin
              RemoveTheRect;
              Dec(FSelectRect.Left);
              Dec(FSelectRect.Right);
              DrawTheRect;
              SetCursorPos(FSelectRect.Right, FSelectRect.Bottom);
            end
            else
            begin
              GetCursorPos(FPoint);
              Dec(FPoint.X);
              SetCursorPos(FPoint.X, FPoint.Y);
            end;
          end;
        VK_RIGHT:
          begin
            if ssCtrl in Shift then
            begin
              RemoveTheRect;
              Inc(FSelectRect.Left);
              Inc(FSelectRect.Right);
              DrawTheRect;
              SetCursorPos(FSelectRect.Right, FSelectRect.Bottom);
            end
            else
            begin
              GetCursorPos(FPoint);
              Inc(FPoint.X);
              SetCursorPos(FPoint.X, FPoint.Y);
            end;
          end;
      end;
    end;procedure TAreaFrm.WMEraseBkGnd(var Msg: TWMEraseBkGnd);
    begin
      Msg.Result := 1;
    end;procedure TAreaFrm.DrawTheRect;
    var
      X1, Y1, X2, Y2: Integer;
    begin
      // Determines starting pixel color of Rect
      Counter := CounterStart;
      // Use LineDDA to draw each of the 4 edges of the rectangle
      with FSelectRect do
      begin
        X1 := Left;
        Y1 := Top;
        X2 := Right;
        Y2 := Bottom;
      end;  LineDDA(X1, Y1, X2, Y1, @MovingDots, LongInt(PaintBox1.Canvas));
      LineDDA(X2, Y1, X2, Y2, @MovingDots, LongInt(PaintBox1.Canvas));
      LineDDA(X2, Y2, X1, Y2, @MovingDots, LongInt(PaintBox1.Canvas));
      LineDDA(X1, Y2, X1, Y1, @MovingDots, LongInt(PaintBox1.Canvas));
    end;procedure TAreaFrm.RemoveTheRect;
    var
      R: TRect;
    begin
      //R := NormalizeRect(Rect(X1, Y1, X2, Y2)); // Rectangle might be flipped
      R := NormalizeRect(FSelectRect);
      InflateRect(R, 1, 1);                 // Make the rectangle 1 pixel larger
      InvalidateRect(Handle, @R, True);     // Mark the area as invalid
      InflateRect(R, -2, -2);               // Now shrink the rectangle 2 pixels
      ValidateRect(Handle, @R);             // And validate this new rectangle.
      // This leaves a 2 pixel band all the way around
      // the rectangle that will be erased & redrawn
      UpdateWindow(Handle);
    end;procedure TAreaFrm.Timer1Timer(Sender: TObject);
    begin
      CounterStart := CounterStart shr 2;   // Shl 1 will move rect slower
      if CounterStart = 0 then
        CounterStart := 128;                // If bit is lost, reset it
      DrawTheRect;                          // Draw the rectangle
    end;end.
      

  5.   

    蚂蚁线部分来自Apprehend, 感谢William Miller,请遵守他的协议,谢谢!Apprehend 2001
    Copyright (C) 1990-2001 William Miller--- LICENSE ---This notice may not be removed from or altered in any source distribution.You are free to use Apprehend 2001 in compiled form for any purpose. Apprehend 2001 in source code or object form (including but not limited to .PAS,
    .DCU, .OBJ), in whole or in part, modified or unmodified, may not be
    redistributed for profit or as part of another commercial or shareware
    software package without express written permission from me.This software is provided 'as-is', without any express or implied warranty.
    In no event shall the author be held liable for any damages arising from the
    use of this software.
    William Miller
    [email protected]
    http://www.software.adirondack/ny.us