2

シンプルな Delphi ペイントで元に戻す操作とやり直し操作を実行できるようにする必要があります。そこで、履歴を保存するためのコンテナーを作成することにしました (完全な履歴ではなく、以前のビットマップ ファイルの一部のみ)。

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.

私のプログラムでは、そのように使用しています。

履歴に保存:

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;

そして取り消しとやり直し。

    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;

しかし、奇妙なことが起こります - 私が押してUndoも何も変わらないのです。そして、私が押すと、Redoのように機能しUndoます。

ちなみに、redo と undo の履歴を異なる長さで宣言すると、このようになります。

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

そして、その中で何が起こるかは、picHistory6ではなく配列の長さのように見えます.12です! したがって、これら 2 つのオブジェクトは 1 つの同じ配列を使用していると思います。なぜそれが起こり、どのようにそれを正しくするのですか?

4

1 に答える 1

5

myHistory クラスの 2 つのインスタンスは、同じグローバル データを共有します。データ宣言をクラスに移動して、グローバルではなくインスタンスごとのデータにする必要があります。

これを試して:

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 に答える