unit frmSaveSnapshotsUnit;

{$mode delphi}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  ExtCtrls, math, LuaCanvas, FPImage, FPCanvas, FPImgCanv, FPReadPNG, FPWritePNG;

resourcestring
  rsSSAreYouSureYouWishToThrowAwayTheseSnapshots = 'Are you sure you wish to throw away these snapshots?';

type

  { TfrmSaveSnapshots }

  TfrmSaveSnapshots = class(TForm)
    btnSave: TButton;
    btnDone: TButton;
    btnCombinedSelect: TButton;
    Label1: TLabel;
    lblDeselectAll: TLabel;
    lblSelectAll: TLabel;
    PaintBox1: TPaintBox;
    Panel1: TPanel;
    Panel2: TPanel;
    SaveDialog1: TSaveDialog;
    ScrollBar1: TScrollBar;
    procedure btnCancelClick(Sender: TObject);
    procedure btnSaveClick(Sender: TObject);
    procedure btnDoneClick(Sender: TObject);
    procedure btnCombinedSelectClick(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: boolean);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure lblDeselectAllClick(Sender: TObject);
    procedure lblSelectAllClick(Sender: TObject);
    procedure PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure PaintBox1Paint(Sender: TObject);
    procedure PaintBox1Resize(Sender: TObject);
    procedure Panel2Resize(Sender: TObject);
    procedure ScrollBar1Change(Sender: TObject);
  private
    { private declarations }
    snapshots: array of record
      filename: string;
      pic: TBitmap;
      selected: boolean;
      xpos: integer;
      width: integer;
    end;
    fsaved: tstringlist;

    loaded: integer;
    procedure combinedselect(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: integer);
    procedure loadSnapshot(index: integer);
  public
    { public declarations }
    procedure initialize(path: string; max: integer);
    property saved: TStringlist read fsaved;
  end;

var
  frmSaveSnapshots: TfrmSaveSnapshots;

implementation

{$R *.lfm}

{ TfrmSaveSnapshots }

procedure TfrmSaveSnapshots.loadSnapshot(index: integer);
var
  s: Tfilestream;
  picturesize: integer;
  error: string;
  i: integer;
  fpi: TFPMemoryImage;
  fpr: TFPReaderPNG;
  fpw: TFPWriterPNG;


  c: TFPCustomCanvas;

  format: integer;


begin
  try



    if loaded>64 then //time to cleanup
    begin
      for i:=index-16 downto 0 do  //allow a few in front
        if snapshots[i].pic<>nil then
        begin
          freeandnil(snapshots[i].pic);
          dec(loaded);
        end;

      for i:=index+48 to length(snapshots)-1 do
        if snapshots[i].pic<>nil then
        begin
          freeandnil(snapshots[i].pic);
          dec(loaded);
        end;

    end;

    if snapshots[index].pic=nil then
    begin
      s:=tfilestream.Create(snapshots[index].filename, fmOpenRead);
      try
        s.position:=4; //to the format type
        s.readbuffer(format, sizeof(format));
        s.ReadBuffer(picturesize, sizeof(picturesize));

        if format=0 then
        begin
          //bmp
          snapshots[index].pic:=tbitmap.Create;
          snapshots[index].pic.LoadFromStream(s, picturesize);
        end
        else
        if format=3 then //png
        begin

          fpi:=TFPMemoryImage.Create(0,0);
          fpr:=TFPReaderPNG.create;
          fpi.LoadFromStream(s, fpr);


          c:=TFPImageCanvas.create(fpi);

          snapshots[index].pic:=tbitmap.Create;
          snapshots[index].pic.Width:=fpi.Width;
          snapshots[index].pic.Height:=fpi.Height;
          TFPCustomCanvas(snapshots[index].pic.Canvas).CopyRect(0,0, c, rect(0,0,fpi.width, fpi.height));

          c.free;
          fpr.free;
          fpi.free;
        end;

      finally
        s.free;
      end;

      inc(loaded);
    end;

  except
    //this is called from onpaint. Exceptions in onpaint crash apps
{    on e: exception do
    begin
      error:=e.Message;
      ShowMessage(error);
    end;}
  end;
end;

procedure TfrmSaveSnapshots.initialize(path: string; max: integer);
var i: integer;
begin
  try
    scrollbar1.position:=0;
    scrollbar1.Max:=max-1;
    setlength(snapshots, max);

    for i:=0 to max-1 do
    begin
      snapshots[i].filename:=path+'snapshot'+inttostr(i+1)+'.ce3dsnapshot';
      snapshots[i].pic:=nil;
      snapshots[i].selected:=false;
    end;
  except
    setlength(snapshots, 0);
  end;
end;

procedure TfrmSaveSnapshots.Panel2Resize(Sender: TObject);
begin
  btnsave.left:=panel2.Width div 2-btnsave.width div 2;
  btnDone.left:=panel2.Width div 2-btnDone.width div 2;
end;

procedure TfrmSaveSnapshots.ScrollBar1Change(Sender: TObject);
begin
  PaintBox1.Repaint;
end;



procedure TfrmSaveSnapshots.FormCreate(Sender: TObject);
begin
  DoubleBuffered:=true;
  Panel1.DoubleBuffered:=true;
  fsaved:=TStringList.create;
end;

procedure TfrmSaveSnapshots.FormDestroy(Sender: TObject);
var i: integer;
begin
  if fsaved<>nil then
    freeandnil(fsaved);

  for i:=0 to length(snapshots)-1 do
  begin
    if snapshots[i].pic<>nil then
      FreeAndNil(snapshots[i].pic);
  end;
end;

procedure TfrmSaveSnapshots.FormShow(Sender: TObject);
begin
  lblSelectAll.Font.height:=GetFontData(font.handle).height;
  lblDeselectAll.Font.height:=GetFontData(font.handle).height;
end;

procedure TfrmSaveSnapshots.lblDeselectAllClick(Sender: TObject);
var i: integer;
begin
  for i:=0 to length(snapshots)-1 do
    snapshots[i].selected:=false;

  paintbox1.Repaint;
end;

procedure TfrmSaveSnapshots.btnCancelClick(Sender: TObject);
begin

end;

procedure TfrmSaveSnapshots.btnSaveClick(Sender: TObject);
var i: integer;
  v: string;
  j: integer;

  ext: string;
  f: string;


  fn: string;
begin



  if savedialog1.execute then
  begin
    //copy all selected snapshots to this folder

    ext:=ExtractFileExt(savedialog1.filename);
    f:=ExtractFileNameWithoutExt(savedialog1.filename);



    j:=1;
    for i:=0 to length(snapshots)-1 do
      if snapshots[i].selected then
      begin
        if j=1 then
          fn:=f+ext
        else
          fn:=f+inttostr(j)+ext;

        CopyFile(snapshots[i].filename, fn, true);
        fsaved.Add(fn);
        inc(j);

      end;

    lblDeselectAll.OnClick(lblSelectAll);

  end;

end;

procedure TfrmSaveSnapshots.btnDoneClick(Sender: TObject);
var i,j: integer;
begin
  j:=0;
  for i:=0 to length(snapshots)-1 do
    if snapshots[i].selected then
      inc(j);

  if j>0 then //something is selected
    btnSave.click;

  close;
end;


procedure TfrmSaveSnapshots.combinedselect(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: integer);
var img: timage;
  sx, sy: integer;
  i: integer;


begin
  img:=TImage(sender);
  sx:=trunc((img.Picture.Width/img.Width)*x);
  sy:=trunc((img.Picture.Height/img.Height)*y);

  //find snapshots that have a pixel not $ff00ff at sx,sy

  for i:=0 to length(snapshots)-1 do
  begin
    loadSnapshot(i);

    if snapshots[i].pic.canvas.Pixels[sx,sy]<>$ff00ff then
      snapshots[i].selected:=true;
  end;


  TCustomForm(img.Parent).close;

  PaintBox1.repaint;
end;

procedure TfrmSaveSnapshots.btnCombinedSelectClick(Sender: TObject);
var
  i: integer;
  b: TBitmap;

  b2: tbitmap;
  f: TCustomForm;

  img: timage;
begin
  //create a "combined" view the user can use to select which pixels to pick

  loadSnapshot(0);

  b:=tbitmap.create;
  b.Width:=snapshots[0].pic.Width;
  b.Height:=snapshots[0].pic.Height;




  for i:=0 to length(snapshots)-1 do
  begin
    loadSnapshot(i);
    //for some reason I first need to convert it to a BMP before transparancy take an effect

    b2:=tbitmap.create;
    b2.width:=snapshots[i].pic.width;
    b2.Height:=snapshots[i].pic.Height;

    b2.Canvas.Draw(0,0,snapshots[i].pic);


    b2.TransparentColor:=$ff00ff;
    b2.Transparent:=true;

    b.Canvas.Draw(0,0,b2);

    b2.free;
  end;


  f:=TCustomForm.create(self);
  img:=TImage.create(f);
  img.Picture.Bitmap:=b;
  img.parent:=f;
  img.align:=alClient;
  img.Stretch:=true;
  img.OnMouseDown:=combinedselect;

  f.ClientWidth:=b.width;
  f.ClientHeight:=b.Height;
  f.BorderIcons:=[biSystemMenu];
  f.position:=poScreenCenter;
  f.showmodal;

  img.free;
  f.free;
  b.free;


end;

procedure TfrmSaveSnapshots.FormCloseQuery(Sender: TObject;
  var CanClose: boolean);
begin
  if (saved.count>0) or (MessageDlg(rsSSAreYouSureYouWishToThrowAwayTheseSnapshots, mtConfirmation, [mbyes, mbno], 0)=mryes) then
    CanClose:=true
  else
    canclose:=false;
end;

procedure TfrmSaveSnapshots.lblSelectAllClick(Sender: TObject);
var i: integer;
begin
  for i:=0 to length(snapshots)-1 do
    snapshots[i].selected:=true;

  paintbox1.Repaint;
end;

procedure TfrmSaveSnapshots.PaintBox1MouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var i: integer;
begin
  for i:=scrollbar1.Position to length(snapshots)-1 do
    if InRange(x, snapshots[i].xpos, snapshots[i].xpos+snapshots[i].width) then
    begin
      snapshots[i].selected:=not snapshots[i].selected;
      break;
    end;

  PaintBox1.repaint;
end;

procedure TfrmSaveSnapshots.PaintBox1Paint(Sender: TObject);
var i,j: integer;
  startpos: integer;
  xpos: integer;

  aspectratio: single;
  currentw: integer;

  h: integer;
begin
  //load and draw the pictures
  paintbox1.Canvas.Clear;

  startpos:=scrollbar1.Position;

  if startpos>=length(snapshots) then
    exit;

  xpos:=0;

  //
  if snapshots[startpos].pic=nil then
    loadSnapshot(startpos);




  h:=paintbox1.Height;




  for i:=startpos to length(snapshots)-1 do
  begin
    if snapshots[i].pic=nil then
      loadSnapshot(i);

    if snapshots[i].pic=nil then continue;

    aspectratio:=snapshots[i].pic.Width/snapshots[i].pic.Height;



    currentw:=ceil(h*aspectratio);



    paintbox1.Canvas.CopyRect(rect(xpos, 0, xpos+currentw, h), snapshots[i].pic.Canvas, rect(0,0,snapshots[i].pic.width, snapshots[i].pic.height));

    snapshots[i].xpos:=xpos;
    snapshots[i].width:=currentw;

    if snapshots[i].selected then
    begin
      paintbox1.Canvas.Pen.Width:=3;
      paintbox1.canvas.pen.Color:=clAqua;
      paintbox1.canvas.Brush.Style:=bsClear;
      paintbox1.Canvas.Rectangle(rect(xpos, 0, xpos+currentw, h));
      paintbox1.canvas.Brush.Style:=bsSolid;

    end;

    inc(xpos, currentw+1);
    if xpos>paintbox1.Width then
      exit; //done
  end;

end;

procedure TfrmSaveSnapshots.PaintBox1Resize(Sender: TObject);
begin


end;

end.

