2

I need to be able to make Undo and Redo operatons in my simple delphi paint. So I decided to make some container to save history (not full history, only few previous bitmap files).

unit HistoryQueue;

interface

uses
  Graphics;

type
myHistory = class
  constructor Create(Size:Integer);
  public
    procedure Push(Bmp:TBitmap);
    function Pop():TBitmap;
    procedure Clean();
    procedure Offset();
    function isEmpty():boolean;
    function isFull():boolean;
    function getLast():TBitmap;
  protected

end;

var
    historyQueueArray: array of TBitmap;
    historyIndex, hSize:Integer;
implementation

procedure myHistory.Push(Bmp:TBitmap);
var tbmp:TBitmap;
begin
  if(not isFull) then begin
      Inc(historyIndex);
      historyQueueArray[historyIndex]:=TBitmap.Create;
      historyQueueArray[historyIndex].Assign(bmp);
  end else begin
      Offset();
      historyQueueArray[historyIndex]:=TBitmap.Create;
      historyQueueArray[historyIndex].Assign(bmp);
  end;

end;

procedure myHistory.Clean;
var i:Integer;
begin
{  for i:=0 to hSize do begin
    historyQueueArray[i].Free;
    historyQueueArray[i].Destroy;
  end;        }

end;

constructor myHistory.Create(Size:Integer);
begin
  hSize:=Size;
  SetLength(historyQueueArray, hSize);
  historyIndex:=-1;
end;

function myHistory.isEmpty: boolean;
begin
  Result:=(historyIndex = -1);
end;

function myHistory.isFull: boolean;
begin
  Result:=(historyIndex = hSize);
end;

procedure myHistory.Offset;
var i:integer;
begin
  //historyQueueArray[0]:=nil;
  for i:=0 to hSize-1 do begin
    historyQueueArray[i]:=TBitmap.Create;
    historyQueueArray[i].Assign(historyQueueArray[i+1]);
  end;
end;

function myHistory.Pop: TBitmap;
var
  popBmp:TBitmap;
begin
  popBmp:= TBitmap.Create;
  popBmp.Assign(historyQueueArray[historyIndex]);
  Dec(historyIndex);
  Result:=popBmp;
end;

function myHistory.getLast: TBitmap;
var
  tBmp:TBitmap;
begin
  tBmp:= TBitmap.Create;
  tBmp.Assign(historyQueueArray[historyIndex]);
  Result:=tBmp;
end;

end.

In my program I use it like that.

Saving in history:

procedure TMainForm.FormCreate(Sender: TObject);
begin
 {...}
  picHistory:=myHistory.Create(10);   //FOR UNDO
  tempHistory:=myHistory.Create(10); //FOR REDO
end;

    //if mouse is up - that mean we finish to draw something on canvas, so we gonna save what we drew

    procedure TMainForm.imgMainMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    var bmp:TBitmap;
    begin
      mouseIsDown:=false;
      bmp:=TBitmap.Create;
      try
        bmp.Assign(imgMain.Picture.Bitmap);
        picHistory.Push(bmp);
      finally
        bmp.Free;
      end;

    end;

And making undo and redo.

    procedure TMainForm.btnUndoClick(Sender: TObject);
    var redBmp:TBitmap;

    begin
      if(not picHistory.isEmpty) then begin //if we draw something before
        //prepare to save what've done into history for redo
        redBmp:=TBitmap.Create;
        redBmp.Assign(picHistory.getLast);
       //showing what were done with image before on screen
        MainForm.imgMain.Canvas.Draw(0,0, picHistory.Pop);
        //but in case we want to be able get back our last changes we save it into redo history 
        tempHistory.Push(redBmp);
        redBmp.Free;
      end;

    end;

  {...}

    procedure TMainForm.btnRedoClick(Sender: TObject);

    begin
    //if there were something into history for redo then show int on canvas
      if(not tempHistory.isEmpty) then
        MainForm.imgMain.Canvas.Draw(0,0, tempHistory.Pop);
    end;

But there are strang thing happens - what I push on Undo nothing changes. And when I push on Redo - it works like Undo.

And by the way when I declare history for redo and undo with different lenght like that

procedure TMainForm.FormCreate(Sender: TObject);
begin
 {...}
  picHistory:=myHistory.Create(6);   //FOR UNDO
  tempHistory:=myHistory.Create(12); //FOR REDO
end;

And then whatch by steps what happens in picHistory it seems like lenght of it's array not 6, it's 12! So I think those two objects use one same array! Why does it happens and how to make it right?

4

1 回答 1

5

您的 myHistory 类的两个实例共享相同的全局数据。您必须将数据声明移动到类中,以使它们成为每个实例数据而不是全局数据。

尝试这个:

type
myHistory = class
  private
    historyQueueArray: array of TBitmap;  //Now they are class members instead of global
    historyIndex, hSize:Integer;
  public
    constructor Create(Size:Integer);
    procedure Push(Bmp:TBitmap);
    function Pop():TBitmap;
    procedure Clean();
    procedure Offset();
    function isEmpty():boolean;
    function isFull():boolean;
    function getLast():TBitmap;
  protected
  end;
于 2013-10-20T12:49:58.667 回答