{
     Apophysis Copyright (C) 2001-2004 Mark Townsend
     Apophysis Copyright (C) 2005-2006 Ronald Hordijk, Piotr Borys, Peter Sdobnov
     Apophysis Copyright (C) 2007-2008 Piotr Borys, Peter Sdobnov
     
     Apophysis "3D hack" Copyright (C) 2007-2008 Peter Sdobnov
     Apophysis "7X" Copyright (C) 2009-2010 Georg Kiehne
     Apophysis AV "Phoenix Edition" Copyright (C) 2021-2022 Alice V. Koryagina

     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 2 of the License, or
     (at your option) any later version.

     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.

     You should have received a copy of the GNU General Public License
     along with this program; if not, write to the Free Software
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
}
unit FormRender;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ComCtrls, Math, Buttons, Registry, ExtCtrls, MMSystem, 
  System.Win.TaskbarCore, Vcl.Taskbar, Vcl.Samples.Spin, // AV
  ControlPoint, cmap, RenderThread, RenderingInterface,
  ShellAPI, ActiveX, ComObj;

const
	{$ifdef T500}
	rendersAV = 'rendersAV_250.flame';
	{$else}
	rendersAV = 'rendersAV.flame';
	{$endif}

type
  TWin32Version = (wvUnknown, wvWin95, wvWin98, wvWinNT, wvWin2000, wvWinXP,
                        wvWinVista, wvWin7, wvWinFutureFromOuterSpace);
	
type
  TRenderForm = class(TForm)
    btnRender: TButton;
    btnCancel: TButton;
    SaveDialog: TSaveDialog;
    btnPause: TButton;
    StatusBar: TStatusBar;
    PageCtrl: TPageControl;
    TabSettings: TTabSheet;
    TabOutput: TTabSheet;
    GroupBox5: TGroupBox;
    btnSavePreset: TSpeedButton;
    btnDeletePreset: TSpeedButton;
    cmbPreset: TComboBox;
    GroupBox2: TGroupBox;
    cbWidth: TComboBox;
    cbHeight: TComboBox;
    GroupBox3: TGroupBox;
    txtOversample: TEdit;
    txtFilterRadius: TEdit;
    udOversample: TUpDown;
    txtDensity: TComboBox;
    GroupBox4: TGroupBox;
    lblApproxMem: TLabel;
    lblPhysical: TLabel;
    lblMaxbits: TLabel;
    Label9: TLabel;
    cbMaxMemory: TComboBox;
    chkLimitMem: TCheckBox;
    Output: TMemo;
    lblMemory: TLabel;
    btnBrowse: TSpeedButton;
    txtFilename: TEdit;
    GroupBox1: TGroupBox;
    chkSave: TCheckBox;
    GroupBox6: TGroupBox;
    chkPostProcess: TCheckBox;
    chkShutdown: TCheckBox;
    btnGoTo: TSpeedButton;
    pnlWidth: TPanel;
    pnlHeight: TPanel;
    pnlDensity: TPanel;
    pnlFilter: TPanel;
    pnlOversample: TPanel;
    pnlLimit: TPanel;
    pnlTarget: TPanel;
    btnDonate: TButton;
    btnSaveLog: TButton;
    chkBinary: TCheckBox;
    ProgressBar2: TProgressBar;
    PBMem: TProgressBar;
    chkSaveIncompleteRenders: TCheckBox;
    lblCPUCores: TLabel;
    cbAspectRatio: TComboBox;
    chkEmbedFlame: TCheckBox;
    chkPlaySound: TCheckBox;
    btnSnapshot: TButton;
    ProgressTaskbar: TTaskbar;
    sbFilterRadius: TSpinButton;
    lblRatio: TPanel;
    procedure btnSaveLogClick(Sender: TObject);
    procedure btnDonateClick(Sender: TObject);
    procedure cbMaxMemoryChange(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure btnRenderClick(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure txtWidthChange(Sender: TObject);
    procedure txtHeightChange(Sender: TObject);
    procedure txtOversampleChange(Sender: TObject);
    procedure txtFilenameChange(Sender: TObject);
    procedure btnCancelClick(Sender: TObject);
    procedure txtDensityChange(Sender: TObject);
    procedure txtFilterRadiusExit(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure btnPauseClick(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure btnSavePresetClick(Sender: TObject);
    procedure btnBrowseClick(Sender: TObject);
    procedure btnDeletePresetClick(Sender: TObject);
    procedure cmbPresetChange(Sender: TObject);
    //procedure chkMaintainClick(Sender: TObject);
    procedure chkSaveIncompleteRendersClick(Sender: TObject);
    procedure btnGoToClick(Sender: TObject);
    procedure cbAspectRatioChange(Sender: TObject);
    procedure chkPlaySoundClick(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure btnSnapshotClick(Sender: TObject);
    procedure NumFieldKeyPress(Sender: TObject; var Key: Char);
    procedure sbFilterRadiusUpClick(Sender: TObject);
    procedure sbFilterRadiusDownClick(Sender: TObject);
    procedure txtDensityExit(Sender: TObject);
    procedure cbWidthExit(Sender: TObject);
    procedure cbHeightExit(Sender: TObject);
    procedure ProgressTaskbarThumbButtonClick(Sender: TObject;
      AButtonID: Integer);
  private
    StartTime, EndTime, oldElapsed, edt: TDateTime;
    oldProg: double;
    PhysicalMemory, ApproxMemory, TotalPhysicalMemory: int64;
    ApproxSamples: int64;

    procedure DoPostProcess;

    procedure HandleThreadCompletion(var Message: TMessage);
      message WM_THREAD_COMPLETE;
    procedure HandleThreadTermination(var Message: TMessage);
      message WM_THREAD_TERMINATE;
    procedure ListPresets;
    function WindowsExit(RebootParam: Longword = EWX_POWEROFF or EWX_FORCE): Boolean;
    procedure Save(const str: string);
    function IsLimitingMemory(): boolean;
    procedure OnProgress(prog: double);
    procedure ShowMemoryStatus;
  public
    Renderer: TRenderThread;

    //ColorMap: TColorMap;
    cp: TControlPoint;
    Filename: string;
    ImageWidth, ImageHeight, Oversample: Integer;
    Sample_Density, Filter_Radius: double;
    MaxMemory: integer;
    bRenderAll: boolean;

    RenderFlameFile: string; // AV
    FlameNames: array of string; // AV

    procedure ResetControls;
  end;

function GetCpuCount: integer;  // AV: this is used in Options
function GetWinVersion: TWin32Version; // AV: moved from Main

var
  RenderForm: TRenderForm;
  Ratio: double;

implementation

uses
  Main, Global, SavePreset, formPostProcess, ImageMaker, Tracer, Translation;

{$R *.DFM}

function TRenderForm.IsLimitingMemory():boolean;
begin
  Result := (cbMaxMemory.ItemIndex > 0);
end;

procedure TRenderForm.ResetControls;
begin
  txtFilename.Enabled := true;
  btnBrowse.Enabled := true;
  cbWidth.Enabled := true;
  cbHeight.Enabled := true;
  txtDensity.Enabled := true;
  txtFilterRadius.enabled := true;
  txtOversample.Enabled := true;
  //chkLimitMem.Enabled := true;
  cbMaxMemory.enabled := true;
  chkPostProcess.Enabled := not IsLimitingMemory;
  chkSaveIncompleteRenders.Enabled := not IsLimitingMemory;
  btnRender.Enabled := true;
  cmbPreset.enabled := true;
  btnSaveLog.Enabled := false;
  chkSave.enabled := true;
  chkPostProcess.enabled := true;
  chkShutdown.enabled := true;
  btnSavePreset.enabled := true;
  btnDeletePreset.enabled := true;
  btnCancel.Caption := TextByKey('common-close');
  btnPause.enabled := false;
  //chkMaintain.Enabled := true;
  cbAspectRatio.Enabled := true;  // AV
  btnSnapshot.Visible := false;  // AV

  ProgressBar2.Position := 0;
  ProgressTaskBar.ProgressValue := 0;  // AV
  ProgressTaskBar.ProgressState := TTaskBarProgressState.None;  // AV

  pnlWidth.Enabled := true;
  pnlHeight.Enabled := true;
  pnlDensity.Enabled := true;
  pnlFilter.Enabled := true;
  pnlOversample.Enabled := true;
  pnlLimit.Enabled := true;
  pnlTarget.Enabled := true;

  pnlWidth.Font.Color := clWindowText;
  pnlHeight.Font.Color := clWindowText;
  pnlDensity.Font.Color := clWindowText;
  pnlFilter.Font.Color := clWindowText;
  pnlOversample.Font.Color := clWindowText;
  pnlLimit.Font.Color := clWindowText;
  pnlTarget.Font.Color := clWindowText;
  ShowMemoryStatus;
end;

function GetCpuCount: integer;
var
  si: TSystemInfo;
begin;
  GetSystemInfo(si);
  Result := si.dwNumberOfProcessors;
end;

procedure TRenderForm.ShowMemoryStatus;
var
  GlobalMemoryInfo: TMemoryStatus; // holds the global memory status information
begin
  GlobalMemoryInfo.dwLength := SizeOf(GlobalMemoryInfo);
  GlobalMemoryStatus(GlobalMemoryInfo);
  PhysicalMemory := GlobalMemoryInfo.dwAvailPhys div 1048576;
  TotalPhysicalMemory := GlobalMemoryInfo.dwTotalPhys div 1048576;
  //TotalPhysicalMemory := TotalPhysicalMemory * 9 div 10; // assume that OS will take 10% of RAM ;)

  {$ifdef CPUX86}
  ApproxMemory := int64(ImageHeight) * int64(ImageWidth) * sqr(Oversample) * 16 div 1048576;
  {$else}
  ApproxMemory := int64(ImageHeight) * int64(ImageWidth) * sqr(Oversample) * 32 div 1048576;
  {$endif}

  lblMemory.Caption := Format(TextByKey('render-resourceusage-infotext'), [ApproxMemory, PhysicalMemory]);
  PBMem.Position := round(100 * (ApproxMemory / PhysicalMemory));

  if ApproxMemory > PhysicalMemory then  // AV: adapted for GUI styles
  begin
     // AV: exclude the style for the font to set its color properly
    lblMemory.StyleElements := [seClient, seBorder];
    lblMemory.Font.Color := clRed;
  end
  else begin
    lblMemory.StyleElements := [seClient, seBorder, seFont];
    lblMemory.Font.Color := clWindowText;
  end;

  if NrTreads > GetCpuCount then NrTreads := GetCpuCount; // AV: only actual Nrs
 {
    lblCpuCores.Font.Color := clRed
  else
    lblCpuCores.Font.Color := clWindowText;
  }

   lblCPUCores.Caption := Format(TextByKey('render-resourceusage-infotext2'), [NrTreads, GetCpuCount]);
  //btnRender.Enabled := (ApproxMemory <= PhysicalMemory) or (cbMaxMemory.ItemIndex > 0);

  if  ApproxMemory > 0 then
    lblMaxbits.caption := format('%2.3f', [8 + log2(
      sample_density * sqr(power(2, cp.zoom)) * int64(ImageHeight) * int64(ImageWidth) / sqr(oversample)
    )]);
end;

procedure Trace2(const str: string);
begin
  if TraceLevel >= 2 then
    RenderForm.Output.Lines.Add('. . ' + str);
end;

procedure TRenderForm.Save(const str:string);
begin
  Renderer.SaveImage(FileName);
end;

procedure TRenderForm.sbFilterRadiusDownClick(Sender: TObject);  // AV
var n: double;
begin
    try
      n := StrToFloat(txtFilterRadius.Text);
      n := RoundTo(n - 0.05, -3);
      if (n > 0) then
        txtFilterRadius.Text := FloatToStr(n);
    except
      raise Exception.Create(TextByKey('render-status-invalidfilterradius'));
    end;
end;

procedure TRenderForm.sbFilterRadiusUpClick(Sender: TObject);  // AV
var n: double;
begin
    try
      n := StrToFloat(txtFilterRadius.Text);
      txtFilterRadius.Text := Format('%.3g', [n + 0.05]);
    except
      raise Exception.Create(TextByKey('render-status-invalidfilterradius'));
    end;
end;

procedure TRenderForm.HandleThreadCompletion(var Message: TMessage);
var
  tryAgain: boolean;
begin
  Trace2(MsgComplete + IntToStr(message.LParam));
  if not assigned(Renderer) then begin
    Trace2(MsgNotAssigned);
    exit;
  end;
  if Renderer.ThreadID <> message.LParam then begin
    Trace2(MsgAnotherRunning);
    exit;
  end;

  EndTime := Now;

  repeat
    tryAgain := false;
    try
      Save(FileName);
      if ShowRenderImage then // AV
        if FileExists(FileName) then
          ShellExecute(Application.handle, PChar('open'),
            PChar(ExtractFileName(FileName)), nil, PChar(renderPath),
            SW_SHOWNORMAL);
    except
      on e: Exception do begin
        Output.Lines.Add(TimeToStr(Now) + ' : ' + TextByKey('render-status-saveerror-log'));
        tryAgain := (Application.MessageBox(PChar(TextByKey('render-status-saveerror-message1') + #13#10 + e.Message +
          #13#10 + TextByKey('render-status-saveerror-message2')), ApophysisSVN, MB_RETRYCANCEL or MB_ICONERROR) = IDRETRY);
        // AV: displaying status in red
        ProgressTaskBar.ProgressState := TTaskBarProgressState.Error;
      end;
    end;
  until tryAgain = false;

  if PlaySoundOnRenderComplete then
    if RenderCompleteSoundFile <> '' then
      sndPlaySound(PChar(RenderCompleteSoundFile), SND_FILENAME or SND_NOSTOP or SND_ASYNC)
    else
      sndPlaySound(pchar(SND_ALIAS_SYSTEMASTERISK), SND_ALIAS_ID or SND_NOSTOP or SND_ASYNC);

  PageCtrl.TabIndex := 1;
  if ShowRenderStats then
    Renderer.ShowBigStats
  else
    Renderer.ShowSmallStats;
  Output.Lines.Add('  ' + TextByKey('render-status-totaltime') + TimeToString(EndTime - StartTime));
  Output.Lines.Add('');

  ProgressTaskBar.ProgressState := TTaskBarProgressState.None;  // AV

  if not IsLimitingMemory and chkPostProcess.checked then
    DoPostProcess;

  Renderer.Free;
  Renderer := nil;
  if not bRenderAll then ResetControls;

  btnSaveLog.Enabled := true;

  if chkShutdown.Checked and not bRenderAll then
    WindowsExit;
end;

procedure TRenderForm.HandleThreadTermination(var Message: TMessage);
begin
  Trace2(MsgTerminated + IntToStr(message.LParam));
  if not assigned(Renderer) then begin
    Trace2(MsgNotAssigned);
    exit;
  end;
  if Renderer.ThreadID <> message.LParam then begin
    Trace2(MsgAnotherRunning);
    exit;
  end;
  {
  // AV: temporary commented out since undestroyed TBaseRenderer objects
  // from Renderer.GetRenderer method cause regular large memory leaks!

  if Renderer.GetRenderer.Hibernated then
    Output.Lines.Add(TimeToStr(Now) + ' : ' + TextByKey('render-status-renderhibernated'))
  else }
  Output.Lines.Add(TimeToStr(Now) + ' : ' + TextByKey('render-status-renderterminated'));

  Output.Lines.Add('');

  ProgressTaskBar.ProgressState := TTaskBarProgressState.None;  // AV

  sndPlaySound(pchar(SND_ALIAS_SYSTEMEXCLAMATION), SND_ALIAS_ID or SND_NOSTOP or SND_ASYNC);

  PageCtrl.TabIndex := 0;  // AV: moved from btnCancelClick

  Renderer.Free;
  Renderer := nil;
  ResetControls;

  btnSaveLog.Enabled := true;
end;

procedure TRenderForm.OnProgress(prog: double);
var
  Elapsed, Remaining, dt: TDateTime;
begin
  Elapsed := Now - StartTime;
  dt := Elapsed - oldElapsed;
  if (prog = 1.0) then begin
    StatusBar.Panels[0].text := Format(TextByKey('render-status-elapsed') + ': %2.2d:%2.2d:%2.2d.%2.2d',
      [Trunc(Elapsed * 24),
       Trunc(Elapsed * 24 * 60) mod 60,
       Trunc(Elapsed * 24 * 60 * 60) mod 60,
       Trunc(Elapsed * 24 * 60 * 60 * 100) mod 100]);
    StatusBar.Panels[1].text := TextByKey('render-status-remaining') + ': 00:00:00.00';
    exit;
  end;

  //if (dt < 1/24/60/60/10) then exit;
  if (dt < 1/24/60/60) then exit; // PB: too much time consuming... was every 1/10th seconds!
  oldElapsed := Elapsed;

  prog := (Renderer.Slice + Prog) / Renderer.NrSlices;
  if ShowProgress then begin
    ProgressBar2.Position := round(100 * prog);
    // AV: to display the progress on the taskbar
    //ProgressTaskBar.ProgressState := TTaskBarProgressState.Normal;  // AV
    ProgressTaskBar.ProgressValue := ProgressBar2.Position;  // AV
  end;

  StatusBar.Panels[0].text := Format(TextByKey('render-status-elapsed') + ': %2.2d:%2.2d:%2.2d.%2.2d',
    [Trunc(Elapsed * 24),
     Trunc(Elapsed * 24 * 60) mod 60,
     Trunc(Elapsed * 24 * 60 * 60) mod 60,
     Trunc(Elapsed * 24 * 60 * 60 * 100) mod 100]);

  edt := edt + dt;
  if (edt > 1/24/60/60/2) and (prog > 0) then
  begin
    Remaining := (1 - prog) * edt / (prog - oldProg);
    edt := 0;
    oldProg := prog;

    StatusBar.Panels[1].text := Format(TextByKey('render-status-remaining') + ': %2.2d:%2.2d:%2.2d.%2.2d',
      [Trunc(Remaining * 24),
       Trunc(Remaining * 24 * 60) mod 60,
       Trunc(Remaining * 24 * 60 * 60) mod 60,
       Trunc(Remaining * 24 * 60 * 60 * 100) mod 100]);
  end;
  StatusBar.Panels[2].text := Format(TextByKey('render-status-slicestatus'), [(Renderer.Slice + 1), (Renderer.nrSlices)]);

  Application.ProcessMessages;
end;

procedure TRenderForm.ProgressTaskbarThumbButtonClick(Sender: TObject;
  AButtonID: Integer);
begin
  if AButtonID = 0 then
  begin
    if Assigned(Renderer) and (not bRenderAll) then begin
      if Renderer.Suspended = false then
      begin
        Renderer.Suspend;
        btnSnapshotClick(Sender);
        Renderer.Resume;
      end else
        btnSnapshotClick(Sender);
    end;
  end
  else
    btnCancelClick(Sender);
end;

procedure TRenderForm.FormCreate(Sender: TObject);
begin
{$ifdef CPUX64}
  cbMaxMemory.Items.Add('2048');
  cbMaxMemory.Items.Add('3072');
  cbMaxMemory.Items.Add('4096');
{$endif}

  pnlWidth.Caption := TextByKey('common-width');
	pnlHeight.Caption := TextByKey('common-height');
	GroupBox2.Caption := TextByKey('common-size');
	cbAspectRatio.Items[0] := TextByKey('adjustment-tab-size-custom'); // AV
	cbAspectRatio.Items[1] := TextByKey('common-keepaspect');  // AV
	lblRatio.Caption :=  TextByKey('adjustment-tab-size-ratio'); // AV
	pnlTarget.Caption := TextByKey('common-destination');
	btnBrowse.Hint := TextByKey('common-browse');
	GroupBox3.Caption := TextByKey('common-quality');
	pnlFilter.Caption := TextByKey('common-filterradius');
	pnlDensity.Caption := TextByKey('common-density');
	pnlOversample.Caption := TextByKey('common-oversample');
	btnRender.Caption := TextByKey('common-start');
	btnPause.Caption := TextByKey('common-pause');
	btnCancel.Caption := TextByKey('common-close');
	self.Caption := TextByKey('render-title');
  self.Hint := self.Caption; // AV: hack
	TabSettings.Caption := TextByKey('render-tab-settings-title');
	TabOutput.Caption := TextByKey('render-tab-output-title');
	btnGoTo.Hint := TextByKey('render-common-gotofolder');
	GroupBox4.Caption := TextByKey('render-resourceusage-title');
	pnlLimit.Caption := TextByKey('render-resourceusage-limit');
	chkSave.Caption := TextByKey('render-output-saveparams1');
  chkEmbedFlame.Caption := TextByKey('render-output-saveparams2');
	GroupBox6.Caption := TextByKey('render-completion-title');
	chkPostProcess.Caption := TextByKey('render-completion-postprocess');
	chkShutdown.Caption := TextByKey('render-completion-shutdown');
  chkPlaySound.Caption := TextByKey('render-completion-playsound');
	chkSaveIncompleteRenders.Caption := TextByKey('render-completion-saveincomplete');
  cbMaxMemory.Items[0] := TextByKey('render-resourceusage-nolimit') ;
  Groupbox1.Caption := TextByKey('render-output-title');
  Groupbox5.Caption := TextByKey('render-presets-title');
  btnSavePreset.Hint := TextByKey('render-presets-save');
  btnDeletePreset.Hint := TextByKey('render-presets-delete');
  btnDonate.Caption := TextByKey('common-donate');
  btnDonate.Hint := TextByKey('common-donatehint');
  btnSnapshot.Caption := TextByKey('render-status-dosnapshot');  // AV
  btnSnapshot.Hint := TextByKey('render-status-dosnapshothint');
  ProgressTaskbar.TaskbarButtons[1].Hint := TextByKey('render-status-stop');
  ProgressTaskbar.TaskbarButtons[0].Hint := TextByKey('render-status-showimage');

  // AV: fixed the filter order and moved this here since it never changes
  SaveDialog.Filter := Format('%s|*.bmp;*.dib|%s|*.png|%s|*.jpg;*.jpeg|%s|*.bmp;*.dib;*.jpg;*.jpeg;*.png',
        [TextByKey('common-filter-bitmap'), TextByKey('common-filter-png'),
         TextByKey('common-filter-jpeg'), TextByKey('common-filter-allimages')]);

  cp := TControlPoint.Create;
  cbMaxMemory.ItemIndex := 0;
  cbAspectRatio.ItemIndex := 1; // AV

  MainForm.Buttons.GetBitmap(2, btnSavePreset.Glyph);
  // MainForm.Buttons.GetBitmap(9, btnDeletePreset.Glyph);
  bRenderAll := false;
  ListPresets;
end;

procedure TRenderForm.FormDestroy(Sender: TObject);
begin
  if assigned(Renderer) then begin
    Renderer.Terminate;
    Renderer.WaitFor;
    Renderer.Free;
  end;
  cp.free;
end;

procedure TRenderForm.NumFieldKeyPress(Sender: TObject; var Key: Char); // AV
var cs: TSysCharSet;
begin
 cs := ['0'..'9', #8];
 if Sender = txtFilterRadius then
 begin
    if (Key = ',') then Key := '.';
    include(cs, '.');
 end;
 if not CharInSet(Key, cs) then Key:= #0;
end;

procedure TRenderForm.btnRenderClick(Sender: TObject);
var
  iCurrFlame: integer;
  t, ext: string;
  lim: integer;
  ilm: boolean;
{  sl: TStringList;
  tryAgain: boolean;
  cancel: boolean;
  result: integer; }
begin
  // overwrite target with 0b file
  // this to test writability in output directory
  {sl := TStringList.Create;
  sl.Text := '';
  repeat
    tryAgain := false;
    cancel := false;
    try
      sl.SaveToFile(txtFileName.Text);
    except
      on e: Exception do begin
        Output.Lines.Add(TimeToStr(Now) + ' : ' + TextByKey('render-status-saveerror-log'));
        result := (Application.MessageBox(PChar(TextByKey('render-status-saveerror-message1') + #13#10 + e.Message +
          #13#10 + TextByKey('render-status-saveerror-message2')), ApophysisSVN, MB_RETRYCANCEL or MB_ICONERROR));
        tryAgain := (result = IDRETRY);
        cancel := (result = IDCANCEL);
      end;
    end;
  until (tryAgain = false) or (cancel = true);
  sl.Destroy; }

  //if (cancel) then Exit;
  Output.Text := '';

  ImageWidth := StrToInt(cbWidth.text);
  ImageHeight := StrToInt(cbHeight.text);

  ilm := IsLimitingMemory;
  if (IsLimitingMemory) then begin
    lim := StrToInt(cbMaxMemory.text);
    MaxMemory := lim;
  end
  else lim := PhysicalMemory + 1;

  if not ilm then begin
    if (ApproxMemory > {Total}PhysicalMemory) then
    begin
      if IDYES <> Application.MessageBox(PChar(TextByKey('render-status-notenoughmemory1')),
        ApophysisSVN, MB_ICONWARNING or MB_YESNO) then exit;
    end;
  end
  else if (PhysicalMemory < lim) and (Approxmemory > PhysicalMemory) then begin
    if IDYES <> Application.MessageBox(PChar(TextByKey('render-status-notenoughmemory2')),
      ApophysisSVN, MB_ICONWARNING or MB_YESNO) then exit;
  end;

  t := txtFilename.Text;
  if t = '' then begin
    Application.MessageBox(PChar(TextByKey('render-status-nofilename')), ApophysisSVN, 48);
    exit;
  end;

  if FileExists(t) then
    if Application.MessageBox(PChar(Format(TextByKey('render-status-fileexists-message1'),
      [t]) + #13#10 + TextByKey('render-status-fileexists-message2')),
      ApophysisSVN, 52) = ID_NO then exit;
  if not DirectoryExists(ExtractFileDir(t)) then
    raise Exception.Create(TextByKey('render-status-pathdoesnotexist')); // AV

  {Check for invalid values }
  { AV: all MessageBoxes replaced by raised Exceptions for speed }
  if (sample_density <= 0) then
    raise Exception.Create(TextByKey('render-status-invaliddensity')); // AV

  if filter_radius <= 0 then
    raise Exception.Create(TextByKey('render-status-invalidfilterradius')); // AV
  {
  // AV: how is it possible, if (udOversample.Min = 1) and (txtOversample.ReadOnly = true)?
  if Oversample < 1 then
    raise Exception.Create(TextByKey('render-status-invalidoversample'));
  }
  if ImageWidth < 1 then
    raise Exception.Create(TextByKey('render-status-invalidwidth')); // AV

  if ImageHeight < 1 then
    raise Exception.Create(TextByKey('render-status-invalidheight')); // AV

  if (ilm) then
    if lim * 1024*1024 < ImageWidth * (int64(ImageHeight) * 4 + oversample) then begin
      // Must be enough memory to hold the final image (RGBA)
      if IDYES <> Application.MessageBox(PChar(TextByKey('render-status-maxmemorytoosmall')), 
		ApophysisSVN, MB_ICONERROR or MB_YESNO) then exit;
    end;

  // AV: activate the taskbar progress
  ProgressTaskBar.ProgressState := TTaskBarProgressState.Normal;  // AV
  ProgressTaskBar.ProgressValue := 0; // AV

  txtFilename.Enabled := false;
  btnBrowse.Enabled := false;
  cbWidth.Enabled := False;
  cbHeight.Enabled := false;
  txtDensity.Enabled := false;
  txtFilterRadius.enabled := false;
  txtOversample.Enabled := false;
  //chkLimitMem.Enabled := true;
  cbMaxMemory.Enabled := false;
  cmbPreset.enabled := false;
  chkSave.enabled := false;
  chkPostProcess.enabled := false;
  chkShutdown.enabled := false;
  btnSavePreset.enabled := false;
  btnDeletePreset.enabled := false;
  btnRender.Enabled := false;
  btnSaveLog.Enabled := false;
  btnPause.enabled := true;
  btnCancel.Caption := TextByKey('common-cancel');
  //chkMaintain.Enabled := false;
  cbAspectRatio.Enabled := False;
  StartTime := Now;

  pnlWidth.Enabled := false;
  pnlHeight.Enabled := false;
  pnlDensity.Enabled := false;
  pnlFilter.Enabled := false;
  pnlOversample.Enabled := false;
  pnlLimit.Enabled := false;
  pnlTarget.Enabled := false;

  pnlWidth.Font.Color := clGrayText;
  pnlHeight.Font.Color := clGrayText;
  pnlDensity.Font.Color := clGrayText;
  pnlFilter.Font.Color := clGrayText;
  pnlOversample.Font.Color := clGrayText;
  pnlLimit.Font.Color := clGrayText;
  pnlTarget.Font.Color := clGrayText;

  PageCtrl.TabIndex := 1;

  if Output.Lines.Count >= 1000 then Output.Lines.Clear;

  if bRenderAll then
  begin
    renderPath := ExtractFilePath(FileName); // AV
    ext := ExtractFileExt(FileName); // AV

    if Assigned(Renderer) then begin
      Output.Lines.Add(TimeToStr(Now) + TextByKey('render-status-shuttingdownrender'));
      Renderer.Terminate;
      Renderer.WaitFor;
      Renderer.Free;
      Renderer := nil;
    end;

    for iCurrFlame := 0 to High(FlameNames) {MainForm.ListView1.Items.Count-1} do
    begin
      {
      MainForm.ListView1.ItemIndex := iCurrFlame;
      cp.Free;
      cp := TControlPoint.Create;
      cp.Copy(MainCP);
      cp.cmap := maincp.cmap;
      zoom := maincp.zoom;
      Center[0] := MainForm.center[0];
      Center[1] := MainForm.center[1];
      }
      t := LoadXMLFlameText(RenderFlameFile, FlameNames[iCurrFlame]); // AV
	    if t = '' then continue; // AV: user may delete an item from opened file
      MainForm.ParseXML(cp, t, true); // the same was done via ListView - but slower

      FileName := renderPath + cp.name + ext;
      Output.Lines.Add('--- ' + Format(TextByKey('render-status-log-title'), [ExtractFileName(FileName)]) + ' ---');
      Output.Lines.Add('  ' + Format(TextByKey('render-status-log-size'), [ImageWidth, ImageHeight]));
      Output.Lines.Add('  ' + Format(TextByKey('render-status-log-quality'), [sample_density]));
      Output.Lines.Add('  ' + Format(TextByKey('render-status-log-oversampling'), [oversample, filter_radius]));

      {$ifdef CPUX86}
      Output.Lines.Add('  ' + Format(TextByKey('render-status-log-bufferdepth'), ['32-bit float']));
      {$else}
      Output.Lines.Add('  ' + Format(TextByKey('render-status-log-bufferdepth'), ['64-bit float']));
      {$endif}
      
      if (ilm) then
        Output.Lines.Add('  ' + Format(TextByKey('render-status-log-memorylimit'), [MaxMemory]))
      else
        if (UpperCase(ext) = '.PNG') and
           (ImageWidth * ImageHeight >= 20000000) then
        begin
          Output.Lines.Add(TextByKey('render-status-log-largepng-message1'));
          Output.Lines.Add(TextByKey('render-status-log-largepng-message2'));
          Output.Lines.Add(TextByKey('render-status-log-largepng-message3'));
        end;

      if not Assigned(Renderer) then
      begin
        // disable screensaver
        SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, 0, nil, 0);

        cp.sample_density := Sample_density;
        cp.spatial_oversample := Oversample;
        cp.spatial_filter_radius := Filter_Radius;
        cp.AdjustScale(ImageWidth, ImageHeight);
        cp.Transparency := (PNGTransparency <> 0) and (UpperCase(ext) = '.PNG');
        // renderPath := ExtractFilePath(Filename);
        if chkSave.checked then begin  // AV: added check for duplicates
          t := ExtractFileName(FileName);
          if XMLEntryExists(t, renderPath + rendersAV) then
            t := t + FormatDateTime(' (MM-dd-yyyy hh-mm-ss)', Now);
          MainForm.SaveXMLFlame(cp, t, renderPath + rendersAV);
        end;

        oldProg := 0;
        oldElapsed := 0;
        edt := 0;
        ApproxSamples := Round(sample_density * sqr(power(2, cp.zoom)) *
                    int64(ImageHeight) * int64(ImageWidth) / sqr(oversample) );

        ProgressTaskBar.ProgressState := TTaskBarProgressState.Normal;  // AV
       try

        if not bRenderAll then exit;
        if iCurrFlame = High(FlameNames){MainForm.ListView1.Items.Count-1} then
          bRenderAll := false;

        Renderer := TRenderThread.Create;
        assert(Renderer <> nil);
{
        if chkThreadPriority.Checked then
          Renderer.SetPriority(tpLower)
        else
          Renderer.SetPriority(tpNormal);
}
        //Renderer.ExportBuffer := chkBinary.Checked;

        if (ilm) then
          Renderer.MaxMem := lim; //StrToInt(cbMaxMemory.text);
        Renderer.OnProgress := OnProgress;
        Renderer.TargetHandle := self.Handle;
        Renderer.SetCP(cp);

        if chkEmbedFlame.checked then
          Renderer.EmbedText(Trim(FlameToXML(cp))); // AV

        Renderer.Priority := tpLower;  // tpNormal;
        Renderer.NrThreads := NrTreads;
        Renderer.Output := Output.Lines;
        Renderer.Resume;

        if bRenderAll then Renderer.WaitFor;
        while Renderer <> nil do Application.ProcessMessages; // wait for HandleThreadCompletion

       except
        Output.Lines.Add(TimeToStr(Now) + ' : ' + TextByKey('render-status-rendererror-log'));
       end;
      end;
    end;

    if not Assigned(Renderer) then SetLength(FlameNames, 0);  // AV: free memory
  end  // end RenderAll
  else begin
    Output.Lines.Add('--- ' + Format(TextByKey('render-status-log-title'), [ExtractFileName(FileName)]) + '" ---');
    Output.Lines.Add('  ' + Format(TextByKey('render-status-log-size'), [ImageWidth, ImageHeight]));
    Output.Lines.Add('  ' + Format(TextByKey('render-status-log-quality'), [sample_density]));
    Output.Lines.Add('  ' + Format(TextByKey('render-status-log-oversampling'), [oversample, filter_radius]));

    {$ifdef CPUX86}
    Output.Lines.Add('  ' + Format(TextByKey('render-status-log-bufferdepth'), ['32-bit float']));
    {$else}
    Output.Lines.Add('  ' + Format(TextByKey('render-status-log-bufferdepth'), ['64-bit float']));
    {$endif}
      
    if (ilm) then
      Output.Lines.Add('  ' + Format(TextByKey('render-status-log-memorylimit'), [lim]))
    else
      if (UpperCase(ExtractFileExt(FileName)) = '.PNG') and
        (ImageWidth * ImageHeight >= 20000000) then
      begin
        Output.Lines.Add(TextByKey('render-status-log-largepng-message1'));
        Output.Lines.Add(TextByKey('render-status-log-largepng-message2'));
        Output.Lines.Add(TextByKey('render-status-log-largepng-message3'));
      end;

    if Assigned(Renderer) then begin
      Output.Lines.Add(TimeToStr(Now) + TextByKey('render-status-shuttingdownrender'));
      Renderer.Terminate;
      Renderer.WaitFor;
      Renderer.Free;
      Renderer := nil;
    end;

    if not Assigned(Renderer) then
    begin
      // disable screensaver
      SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, 0, nil, 0);

      cp.sample_density := Sample_density;
      cp.spatial_oversample := Oversample;
      cp.spatial_filter_radius := Filter_Radius;
      cp.AdjustScale(ImageWidth, ImageHeight);
      cp.Transparency := (PNGTransparency <> 0) and (UpperCase(ExtractFileExt(FileName)) = '.PNG');
      renderPath := ExtractFilePath(Filename);

      if chkSave.checked then begin  // AV: added check for duplicates
        t := ExtractFileName(FileName);
        if XMLEntryExists(t, renderPath + rendersAV) then
          t := t + FormatDateTime(' (MM-dd-yyyy hh-mm-ss)', Now);
        MainForm.SaveXMLFlame(cp, t, renderPath + rendersAV);
      end;

      oldProg := 0;
      oldElapsed := 0;
      edt := 0;
      ApproxSamples := Round(sample_density * sqr(power(2, cp.zoom)) *
                     int64(ImageHeight) * int64(ImageWidth) / sqr(oversample) );

     try

      Renderer := TRenderThread.Create;
      assert(Renderer <> nil);

      if (ilm) then
        Renderer.MaxMem := lim; //StrToInt(cbMaxMemory.text);
      //Renderer.ExportBuffer := chkBinary.Checked;
      Renderer.OnProgress := OnProgress;
      Renderer.TargetHandle := self.Handle;
      Renderer.SetCP(cp);
      if chkEmbedFlame.checked then
          Renderer.EmbedText(Trim(FlameToXML(cp))); // AV
      {
      if chkThreadPriority.Checked then
        Renderer.SetPriority(tpLower)
      else
        Renderer.SetPriority(tpNormal);
      }
      Renderer.Priority := tpLower;
      Renderer.NrThreads := NrTreads;

      Renderer.Output := Output.Lines;
      Renderer.Resume;

     except
      Output.Lines.Add(TimeToStr(Now) + ' : ' + TextByKey('render-status-rendererror-log'));
      Application.MessageBox(PChar(TextByKey('render-status-rendererror-message')), ApophysisSVN, 48);
     end;
    end;
  end;
  // enable screensaver
  SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, 1, nil, 0);
end;

procedure TRenderForm.FormShow(Sender: TObject);
var
  Registry: TRegistry;
begin
  { Read position from registry }
  Registry := TRegistry.Create;
  try
    Registry.RootKey := HKEY_CURRENT_USER;
    if Registry.OpenKey('Software\' + APP_NAME + '\Forms\Render', False) then
    begin
      if Registry.ValueExists('Left') then
        self.Left := Registry.ReadInteger('Left');
      if Registry.ValueExists('Top') then
        self.Top := Registry.ReadInteger('Top');
    end;
    Registry.CloseKey;
  finally
    Registry.Free;
  end;

  SaveDialog.FileName := Filename;
  case renderFileFormat of
    1: txtFilename.Text := ChangeFileExt(SaveDialog.Filename, '.bmp');
    2: txtFilename.Text := ChangeFileExt(SaveDialog.Filename, '.png');
    3: txtFilename.Text := ChangeFileExt(SaveDialog.Filename, '.jpg');
  end;
  chkEmbedFlame.Enabled := (renderFileFormat = 2); // AV
  chkEmbedFlame.Checked := (chkEmbedFlame.Enabled and EmbedFlame);  // AV
  chkSave.Checked := SaveInFlame;  // AV
  if (renderOversample < 1) or (renderOversample > 4) then  
	  renderOversample := 2; // AV: prevent checks in future
  txtOversample.Text := IntToStr(renderOversample);
  Filter_Radius := max(renderFilterRadius, 0.1); // AV: fixed!
  txtFilterRadius.Text := FloatToStr(RoundTo(Filter_Radius, -3)); // AV
  // AV: fixed 'Floating point division by zero' bug
  sample_density := max(renderDensity, 10);
  txtDensity.Text := FloatToStr(sample_density);

  // AV: fixed '"" is not a valid integer' bug  
  if bRenderAll then begin  
	  ImageWidth := max(renderWidth, 100);  // AV
	  ImageHeight := max(renderHeight, 100);  // AV
  end 
  else begin
	  ImageWidth := cp.Width;  // AV
	  ImageHeight := cp.Height;  // AV 
  end;
  Ratio := ImageWidth / ImageHeight;
  cbWidth.Text := IntToStr(ImageWidth);  // AV
  cbHeight.Text := IntToStr(ImageHeight);  // AV

  ShowMemoryStatus;
  chkSaveIncompleteRenders.Checked := SaveIncompleteRenders;
  chkPlaySound.Checked := PlaySoundOnRenderComplete;  // AV
end;

procedure TRenderForm.txtWidthChange(Sender: TObject);
begin
  try
    ImageWidth := StrToInt(cbWidth.Text);
    if (cbAspectRatio.ItemIndex > 0) and cbWidth.Focused then
    begin
      ImageHeight := Round(ImageWidth / ratio);
      cbHeight.Text := IntToStr(ImageHeight)
    end;
  except
  end;
  ShowMemoryStatus;
end;

procedure TRenderForm.txtHeightChange(Sender: TObject);
begin
  try
    ImageHeight := StrToInt(cbHeight.Text);
    if (cbAspectRatio.ItemIndex > 0) and cbHeight.Focused then
    begin
      ImageWidth := Round(ImageHeight * ratio);
      cbWidth.Text := IntToStr(ImageWidth)
    end;
  except
  end;
  ShowMemoryStatus;
end;

procedure TRenderForm.txtOversampleChange(Sender: TObject);
var
  o: integer;
begin
  try
    o := StrToInt(txtOversample.Text);
  except
    txtOversample.Text := IntToStr(Oversample);
    exit;
  end;
  {  
  // AV: this text field is read-only, moved all checks to FormShow
  if o > udOversample.Max then
  begin
    o := udOversample.Max;
    txtOversample.Text := IntToStr(o);
  end
  else if o < udOversample.Min then
  begin
    o := udOversample.Min;
    txtOversample.Text := IntToStr(o);
  end;
  }
  Oversample := o;
  ShowMemoryStatus;
end;

procedure TRenderForm.txtFilenameChange(Sender: TObject);
var
  ext : string;
begin
  filename := txtFilename.text;
  ext := LowerCase(ExtractFileExt(filename));
  chkEmbedFlame.Enabled := (ext = '.png'); // AV
  chkEmbedFlame.Checked := (chkEmbedFlame.Enabled and EmbedFlame);
end;

procedure TRenderForm.btnCancelClick(Sender: TObject);
begin
  if Assigned(Renderer) or bRenderAll then
  begin
    if Assigned(Renderer) then
      if Renderer.Suspended then begin
        Renderer.Resume;
        btnPause.caption := TextByKey('common-pause');
      end;

    if ConfirmStopRender then begin
      if Application.MessageBox(PChar(TextByKey('render-status-confirmstop')),
        ApophysisSVN, 36) = ID_NO then exit;
    end;

    bRenderAll := false;
    if Assigned(Renderer) then
    begin
      if SaveIncompleteRenders and (not IsLimitingMemory) then
        Renderer.BreakRender
      else
        Renderer.Terminate;
      Renderer.WaitFor; // ?
    end;

	  ProgressTaskBar.ProgressValue := 0;  // AV
    ProgressTaskBar.ProgressState := TTaskBarProgressState.None;  // AV

  end else
    Close;
end;

procedure TRenderForm.txtDensityChange(Sender: TObject);
begin
  Sample_Density := StrToFloatDef(txtDensity.Text, renderDensity);  // AV
  if Sample_Density > 0 then ShowMemoryStatus;
end;

procedure TRenderForm.txtDensityExit(Sender: TObject); // AV
begin
  Sample_Density := StrToFloatDef(txtDensity.Text, renderDensity);
  txtDensity.Text := FloatToStr(Sample_Density);
end;

procedure TRenderForm.txtFilterRadiusExit(Sender: TObject);
begin
  // AV: we don't need try - except here
  Filter_Radius := StrToFloatDef(txtFilterRadius.Text, renderFilterRadius);
  txtFilterRadius.Text := FloatToStr(Filter_Radius); // AV
end;

procedure TRenderForm.FormActivate(Sender: TObject);
begin
  chkPlaySound.Checked := PlaySoundOnRenderComplete;  // AV
  ShowMemoryStatus; //lblCPUCores.Refresh;
end;

procedure TRenderForm.FormClose(Sender: TObject; var Action: TCloseAction);
var
  Ext: string;
  Registry: TRegistry;
begin
  Ext := ExtractFileExt(txtFileName.Text);
  if Ext = '.bmp' then renderFileFormat := 1;
  if Ext = '.png' then renderFileFormat := 2;
  if (Ext = '.jpg') or (Ext = '.jpeg') then renderFileFormat := 3;
  renderFilterRadius := Filter_Radius;
  renderWidth := ImageWidth;
  renderHeight := ImageHeight;
  renderDensity := Sample_density;
  renderOversample := Oversample;

  EmbedFlame := chkEmbedFlame.Checked;  // AV
  SaveInFlame := chkSave.Checked;  // AV

  ProgressTaskBar.ProgressState := TTaskBarProgressState.None;  // AV
  if not Assigned(Renderer) then SetLength(FlameNames, 0);  // AV: free memory

  { Write position to registry }
  Registry := TRegistry.Create;
  try
    Registry.RootKey := HKEY_CURRENT_USER;
    if Registry.OpenKey('\Software\' + APP_NAME + '\Forms\Render', True) then
    begin
      Registry.WriteInteger('Top', Top);
      Registry.WriteInteger('Left', Left);
    end;
  finally
    Registry.Free;
  end;
end;

procedure TRenderForm.btnPauseClick(Sender: TObject);
begin
  if Assigned(Renderer) then
    if Renderer.Suspended = false then begin
      Renderer.Suspend;
      btnPause.caption := TextByKey('common-resume');
      btnSnapshot.Visible := true;  // AV
      // AV: displaying progress in yellow
      ProgressTaskBar.ProgressState := TTaskBarProgressState.Paused;
    end else begin
      Renderer.Resume;
      btnPause.caption := TextByKey('common-pause');
      btnSnapshot.Visible := false;  // AV
      // AV: restore the taskbar progress
      ProgressTaskBar.ProgressState := TTaskBarProgressState.Normal;
    end;
end;

procedure TRenderForm.FormCloseQuery(Sender: TObject;
  var CanClose: Boolean);
begin
  if Assigned(Renderer) then
    if Application.MessageBox(PChar(TextByKey('render-status-confirmstop')),
      ApophysisSVN, 36) = ID_NO then CanClose := False
    else
    begin
      if Assigned(Renderer) then
      begin
        // AV: prevent Apo perpetual hanging when render is paused
        if Renderer.Suspended then Renderer.Resume; // AV
        Renderer.Terminate;
        Renderer.WaitFor;
        Renderer.Free;  // AV
        Renderer := nil;   // AV
        ProgressTaskBar.ProgressState := TTaskBarProgressState.None; // AV
      end;
    end;
end;

procedure TRenderForm.btnSavePresetClick(Sender: TObject);
var
  IFile: TextFile;
  Title, Filename: string;
begin
  SavePresetForm.txtPresetName.Text := cmbPreset.Text;
  if SavePresetForm.ShowModal = mrOK then
  begin
    Title := Trim(SavePresetForm.txtPresetName.Text);
    Filename := AppPath + 'Render presets.av';
    try
      AssignFile(IFile, FileName);
      if FileExists(FileName) then
      begin
        if EntryExists(Title, FileName) then DeleteEntry(Title, FileName);
        Append(IFile);
      end
      else
        ReWrite(IFile);
      WriteLn(IFile, Title + ' {');
      WriteLn(IFile, Trim(cbWidth.text));
      WriteLn(IFile, Trim(cbHeight.text));
      WriteLn(IFile, Trim(txtDensity.text));
      WriteLn(IFile, Trim(txtFilterRadius.text));
      WriteLn(IFile, Trim(txtOversample.text));
      WriteLn(IFile, ExtractFileExt(txtFileName.Text));
      if (not IsLimitingMemory) then
        WriteLn(IFile, 'true')
      else
        WriteLn(IFile, 'false');
      WriteLn(IFile, IntToStr(cbMaxMemory.ItemIndex));
      WriteLn(IFile, '}');
      WriteLn(IFile, '');
      CloseFile(IFile);
    except on EInOutError do
      begin
        Application.MessageBox(PChar(TextByKey('render-prestatus-saveerror-preset')),
            ApophysisSVN, 16);
        Exit;
      end;
    end;
    ListPresets;
    cmbPreset.ItemIndex := cmbPreset.Items.count - 1;
  end;
end;

procedure TRenderForm.btnSnapshotClick(Sender: TObject);
var
  tryAgain: boolean;
begin
  if Assigned(Renderer) then
    if Renderer.Suspended = true then
    begin
      repeat
        tryAgain := false;
        try
          Renderer.DoSnapshot; // AV: to save mid-render images
          Renderer.SaveImage(FileName);

          // AV: show saved image if needed
          if ShowRenderImage or (Sender = ProgressTaskbar.TaskBarButtons[0]) then
            if FileExists(FileName) then
              ShellExecute(Application.handle, PChar('open'),
                PChar(ExtractFileName(FileName)), nil, PChar(renderPath),
                SW_SHOWNORMAL);
        except
          on e: Exception do begin
            Output.Lines.Add(TimeToStr(Now) + ' : ' + TextByKey('render-status-saveerror-log'));
            tryAgain := (Application.MessageBox(PChar(TextByKey('render-status-saveerror-message1') + #13#10 + e.Message +
              #13#10 + TextByKey('render-status-saveerror-message1')), 'Error', MB_RETRYCANCEL or MB_ICONERROR) = IDRETRY);
            // AV: displaying status in red
            ProgressTaskBar.ProgressState := TTaskBarProgressState.Error;
          end;
        end;
      until tryAgain = false;
    end;
end;

procedure TRenderForm.btnBrowseClick(Sender: TObject);
var
  fn, ext:  string;
begin
  SaveDialog.Filename := Filename;
  case renderFileFormat of
    1: SaveDialog.DefaultExt := 'bmp';
    2: SaveDialog.DefaultExt := 'png';
    3: SaveDialog.DefaultExt := 'jpg';
  end;
  SaveDialog.FilterIndex := renderFileFormat;
  if SaveDialog.Execute then
  begin
    fn := SaveDialog.FileName;
    ext := LowerCase(ExtractFileExt(fn));
    if (ext = '.jpg') or (ext = '.jpeg')then
      renderFileFormat := 3
    else if (ext = '.png') then
      renderFileFormat := 2
    else // if (ext = '.bmp') or (ext = '.dib')  then
      renderFileFormat := 1;
    txtFileName.Text := ChangeFileExt(fn, ext);
    renderPath := ExtractFilePath(fn);
  end;
end;

procedure TRenderForm.ListPresets;
{ List identifiers in file }
var
  i, p: integer;
  Title: string;
  FStrings: TStringList;
begin
  FStrings := TStringList.Create;
  try
    if fileExists(AppPath + 'Render presets.av') then begin
      FStrings.LoadFromFile(AppPath + 'Render presets.av');
      cmbPreset.Clear;
      if (Pos('{', FStrings.Text) <> 0) then begin
        for i := 0 to FStrings.Count - 1 do begin
          p := Pos('{', FStrings[i]);
          if (p <> 0) then  begin
            Title := Trim(Copy(FStrings[i], 1, p - 1));
            if Title <> '' then begin
              cmbPreset.Items.add(Copy(FStrings[i], 1, p - 1));
            end;
          end;
        end;
      end;
    end;
  finally
    FStrings.Free;
  end;
end;

procedure TRenderForm.btnDeletePresetClick(Sender: TObject);
var
  Title, Filename: string;
begin
  Title := Trim(cmbPreset.Text);
  if Title = '' then exit;
  Filename := AppPath + 'Render presets.av';
  if EntryExists(Title, FileName) then DeleteEntry(Title, FileName);
  ListPresets;
end;

procedure TRenderForm.cmbPresetChange(Sender: TObject);
var
  chk: shortint; // AV
  i, j: integer;
  FStrings: TStringList;
  Title, Filename: string;
begin
  Title := Trim(cmbPreset.Text);
  Filename := AppPath + 'Render presets.av';  // AV: added file extension
  if Title = '' then exit;
  if EntryExists(Title, FileName) then
  begin
  // Load preset
    FStrings := TStringList.Create;
    try
      FStrings.LoadFromFile(Filename);
      for i := 0 to FStrings.Count - 1 do
        if Pos(LowerCase(Title) + ' {', Lowercase(FStrings[i])) <> 0 then
        begin
          chk := cbAspectRatio.ItemIndex;  // AV
          cbAspectRatio.ItemIndex := 0;
          j := i + 1;
          cbWidth.Text := FStrings[j];
          inc(j);
          cbHeight.text := FStrings[j];
          cbAspectRatio.ItemIndex := chk;
          inc(j);
          txtDensity.text := FStrings[j];
          inc(j);
          txtFilterRadius.text := FStrings[j];
          inc(j);
          txtOversample.text := FStrings[j];
          inc(j);
          txtFileName.Text := ChangeFileExt(txtFileName.Text, FStrings[j]);
          inc(j, 2);
		  // AV: avoiding conflicts with 32- and 64-bit versions
          cbMaxMemory.ItemIndex := min(StrToInt(FStrings[j]), cbMaxMemory.Items.Count-1); 
          break;
        end;
    finally
      FStrings.Free;
    end
  end;
  ImageWidth := StrToInt(cbWidth.Text);
  ImageHeight := StrToInt(cbHeight.Text);
  Sample_Density := StrToFloat(txtDensity.Text);
  ShowMemoryStatus;
end;

procedure TRenderForm.DoPostProcess;
begin
  frmPostProcess.cp := cp;
  frmPostProcess.SetRenderer(Renderer.GetRenderer);
  frmPostProcess.SetControlPoint(CP);
  frmPostProcess.SetImageName(FileName);
  frmPostProcess.Show;
end;

function GetWinVersion: TWin32Version;
{ Returns current version of a host Win32 platform }
var
    wMinor, wMajor: integer;
begin
  Result := wvUnknown;
  wMinor := Win32MinorVersion; // AV
  wMajor := Win32MajorVersion; // AV
  if Win32Platform = VER_PLATFORM_WIN32_WINDOWS then
    if (wMajor > 4) or ((wMajor = 4) and (wMinor > 0)) then
      Result := wvWin98
    else
      Result := wvWin95
  else
    if wMajor <= 4 then
      Result := wvWinNT
    else if wMajor = 5 then
	  begin  // AV
      if wMinor = 0 then
        Result := wvWin2000
      else if wMinor >= 1 then
        Result := wvWinXP
	  end
    else if wMajor = 6 then
	  begin  // AV
      if wMinor = 0 then
        Result := wvWinVista
      else if wMinor >= 1 then
        Result := wvWin7
	  end
    else if wMajor >= 7 then
      Result := wvWinFutureFromOuterSpace;
end;

function TRenderForm.WindowsExit(RebootParam: Longword = EWX_POWEROFF or EWX_FORCE): Boolean;
var
   TTokenHd: THandle;
   TTokenPvg: TTokenPrivileges;
   cbtpPrevious: DWORD;
   rTTokenPvg: TTokenPrivileges;
   pcbtpPreviousRequired: DWORD;
   tpResult: Boolean;
   WinVersion: TWin32Version; // AV
const
   SE_SHUTDOWN_NAME = 'SeShutdownPrivilege';
begin
  WinVersion := GetWinVersion; // AV: we don't need to calc it many times
  if ((WinVersion = wvWinNT) or
      (WinVersion = wvWin2000) or
      (WinVersion = wvWinXP) or
      (WinVersion = wvWinVista) or
      (WinVersion = wvWin7) or
      (WinVersion = wvWinFutureFromOuterSpace)) then
  begin
    tpResult := OpenProcessToken(GetCurrentProcess(),
      TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY,
      TTokenHd);
    if tpResult then
    begin
      tpResult := LookupPrivilegeValue(nil,
                                       SE_SHUTDOWN_NAME,
                                       TTokenPvg.Privileges[0].Luid);
      TTokenPvg.PrivilegeCount := 1;
      TTokenPvg.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
      cbtpPrevious := SizeOf(rTTokenPvg);
      pcbtpPreviousRequired := 0;
      if tpResult then
        Windows.AdjustTokenPrivileges(TTokenHd,
                                      False,
                                      TTokenPvg,
                                      cbtpPrevious,
                                      rTTokenPvg,
                                      pcbtpPreviousRequired);
    end;
  end;
  Result := ExitWindowsEx(RebootParam, 0);
end;

procedure TRenderForm.chkPlaySoundClick(Sender: TObject);
begin
  PlaySoundOnRenderComplete := chkPlaySound.Checked;
end;

procedure TRenderForm.chkSaveIncompleteRendersClick(Sender: TObject);
begin
  SaveIncompleteRenders := chkSaveIncompleteRenders.Checked;
end;

procedure TRenderForm.btnGoToClick(Sender: TObject);
var
  path: string;
begin
  path := ExtractFilePath(txtFilename.Text);
  if (path <> '') then WinShellExecute('open', path);
end;


procedure TRenderForm.cbAspectRatioChange(Sender: TObject);
begin
   case cbAspectRatio.ItemIndex of
   1:  Ratio := ImageWidth / ImageHeight;  // AV: replaced "Keep aspect ratio"
   2:  Ratio := 1.5;
   3:  Ratio := 4 / 3;
   4:  Ratio := 1.25;
   5:  Ratio := 16 / 9;
   6:  Ratio := 1.6;
   7:  Ratio := 21 / 9;
  end;
end;

procedure TRenderForm.cbHeightExit(Sender: TObject);  // AV
begin
  ImageHeight := StrToIntDef(cbHeight.Text, renderHeight);
  cbHeight.Text := IntToStr(ImageHeight);
end;

procedure TRenderForm.cbWidthExit(Sender: TObject);  // AV
begin
  ImageWidth := StrToIntDef(cbWidth.Text, renderWidth);
  cbWidth.Text := IntToStr(ImageWidth);
end;

procedure TRenderForm.cbMaxMemoryChange(Sender: TObject);
begin
  //cbMaxMemory.enabled := IsLimitingMemory;
  chkPostProcess.Enabled := not IsLimitingMemory;
  chkSaveIncompleteRenders.Enabled := not IsLimitingMemory;
  //btnRender.Enabled := (ApproxMemory <= PhysicalMemory) or (cbMaxMemory.ItemIndex > 0);
end;

procedure TRenderForm.btnDonateClick(Sender: TObject);
begin
  WinShellExecute('open', 'https://www.donationalerts.com/r/fract_alice');
end;

procedure TRenderForm.btnSaveLogClick(Sender: TObject);
var 
	fn: string; 
	sl: TStringList;
begin
  if OpenSaveFileDialog(RenderForm, '.log',
    Format('Render-Log (*.txt;*.log)|*.txt;*.log|%s|*.*', [TextByKey('common-filter-allfiles')]),
    SaveDialog.InitialDir, TextByKey('common-browse'), fn, false, true, false, false)
  then begin
    sl := TStringList.Create;
    sl.Text := Output.Text;
    sl.SaveToFile(fn);
    sl.Destroy;
  end;
end;

end.

