Apophysis-AV/Forms/Main.pas

7860 lines
239 KiB
ObjectPascal
Raw Permalink Blame History

{
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.
}
//{$D-,L-,O+,Q-,R-,Y-,S-}
unit Main;
//{$define VAR_STR}
interface
uses
Windows, Forms, Dialogs, Menus, Controls, ComCtrls,
StdCtrls, Classes, Messages, ExtCtrls, ImgList, System.ImageList,
Vcl.Imaging.Jpeg, SyncObjs, SysUtils, Graphics, Math, Vcl.ToolWin,
ExtDlgs, AppEvnts, ShellAPI, Registry,
Global, Xform, XFormMan, ControlPoint, CMap,
RenderThread, RenderingCommon, RenderingInterface,
LibXmlParser, LibXmlComps, Vcl.Imaging.PngImage,
StrUtils, LoadTracker, CommandLine, Translation,
RegularExpressionsCore, RegexHelper, Vcl.Themes, Vcl.Styles; // AV
const
mbHeight = 30; // AV: height (in items) of all styled submenus
{$ifdef CPUX86}
randFilename = 'ApophysisAV.rand';
undoFilename = 'ApophysisAV.undo';
ApophysisSVN = 'Apophysis AV (32 bit)'; // AV: the caption for all dialogs
{$else}
randFilename = 'ApophysisAV_64.rand'; // AV
undoFilename = 'ApophysisAV_64.undo'; // AV
ApophysisSVN = 'Apophysis AV (64 bit)';
{$endif}
templateFilename = 'ApophysisAV.temp';
//templatePath = '\templates';
// AV: hmm, we have a global variable with the same name...
// scriptPath = '\scripts';
type
TMouseMoveState = (msUsual, msZoomWindow, msZoomOutWindow, msZoomWindowMove,
msZoomOutWindowMove, msDrag, msDragMove, msRotate,
msRotateMove, msPitchYaw, msHeight);
type
TThumbnailThread = class(TThread)
private
FlameItems: TListItems;
class var
FPreviewDensity: double; // AV
FThumbnailSize : integer; // AV: added F to avoid of name conflicts
public
constructor Create;
procedure Execute; override;
destructor Destroy; override;
end;
TMainForm = class(TForm)
Buttons: TImageList;
MainMenu: TMainMenu;
MainFile: TMenuItem;
mnuSaveUPR: TMenuItem;
N1: TMenuItem;
mnuRandomBatch: TMenuItem;
FileExitSep: TMenuItem;
mnuExit: TMenuItem;
MainEdit: TMenuItem;
mnuCopyUPR: TMenuItem;
mnuEditor: TMenuItem;
mnuRandom: TMenuItem;
mnuNormalWeights: TMenuItem;
mnuEqualize: TMenuItem;
mnuRWeights: TMenuItem;
mnuOptions: TMenuItem;
MainHelp: TMenuItem;
mnuHelpTopics: TMenuItem;
OpenDialog: TOpenDialog;
ListPopUp: TPopupMenu;
mnuItemDelete: TMenuItem;
mnuListRename: TMenuItem;
DisplayPopup: TPopupMenu;
mnuPopFullscreen: TMenuItem;
RedrawTimer: TTimer;
mnuVar: TMenuItem;
mnuVRandom: TMenuItem;
N3: TMenuItem;
mnuOpen: TMenuItem;
mnuSaveAs: TMenuItem;
N8: TMenuItem;
mnuGrad: TMenuItem;
mnuSmoothGradient: TMenuItem;
mnuView: TMenuItem;
mnuToolbar: TMenuItem;
mnuStatusBar: TMenuItem;
BackPanel: TPanel;
mnuFileContents: TMenuItem;
mnuUndo: TMenuItem;
mnuRedo: TMenuItem;
N5: TMenuItem;
SaveDialog: TSaveDialog;
MainFlame: TMenuItem;
N11: TMenuItem;
mnuAbout: TMenuItem;
mnuFullScreen: TMenuItem;
mnuRender: TMenuItem;
mnuMutate: TMenuItem;
mnuAdjust: TMenuItem;
mnuOpenGradient: TMenuItem;
mnuResetLocation: TMenuItem;
N4: TMenuItem;
N14: TMenuItem;
mnuSaveUndo: TMenuItem;
N2: TMenuItem;
mnuPopResetLocation: TMenuItem;
N6: TMenuItem;
mnuPopUndo: TMenuItem;
N16: TMenuItem;
mnuPopRedo: TMenuItem;
mnuCalculateColors: TMenuItem;
mnuRandomizeColorValues: TMenuItem;
N18: TMenuItem;
N19: TMenuItem;
mnuScript: TMenuItem;
mnuRun: TMenuItem;
mnuEditScript: TMenuItem;
N15: TMenuItem;
mnuStop: TMenuItem;
mnuOpenScript: TMenuItem;
mnuImportGimp: TMenuItem;
N9: TMenuItem;
N10: TMenuItem;
mnuManageFavorites: TMenuItem;
mnuImageSize: TMenuItem;
N13: TMenuItem;
ApplicationEvents: TApplicationEvents;
mnuPaste: TMenuItem;
mnuCopy: TMenuItem;
N20: TMenuItem;
mnuExportFLame: TMenuItem;
mnuFlamepdf: TMenuItem;
mnuSaveAllAs: TMenuItem;
View1: TMenuItem;
mnuRenderAll: TMenuItem;
mnuBuiltinVars: TMenuItem;
mnuPluginVars: TMenuItem;
UsedThumbnails: TImageList;
Splitter: TSplitter;
ListBackPanel: TPanel;
ListView1: TListView;
cbMain: TCoolBar;
ToolBar: TToolBar;
btNew: TToolButton;
btnOpen: TToolButton;
btnSave: TToolButton;
ToolButton10: TToolButton;
btnRender: TToolButton;
tbRenderAll: TToolButton;
ToolButton9: TToolButton;
btnViewList: TToolButton;
btnViewIcons: TToolButton;
ToolButton2: TToolButton;
btnUndo: TToolButton;
btnRedo: TToolButton;
ToolButton1: TToolButton;
btnReset: TToolButton;
btnFullScreen: TToolButton;
ToolButton3: TToolButton;
tbQualityBox: TComboBoxEx;
New1: TMenuItem;
ColorDialog: TColorDialog;
mnuResetUI: TMenuItem;
ToolButton4: TToolButton;
tbEditor: TToolButton;
tbAdjust: TToolButton;
tbPalette: TToolButton;
tbMutate: TToolButton;
tbImageSize: TToolButton;
tbMessages: TToolButton;
tbOptions: TToolButton;
ToolButton15: TToolButton;
tbShowAlpha: TToolButton;
ToolButton16: TToolButton;
tbEditScript: TToolButton;
btnRunScript: TToolButton;
btnStopScript: TToolButton;
ToolButton18: TToolButton;
tbDrag: TToolButton;
tbRotate: TToolButton;
tbZoomIn: TToolButton;
tbZoomOut: TToolButton;
AutoSaveTimer: TTimer;
Restorelastautosave1: TMenuItem;
tbGuides: TToolButton;
mnuTurnFlameToScript: TMenuItem;
N12: TMenuItem;
mnuReportFlame: TMenuItem;
mnuMessages: TMenuItem;
BottomDock: TPanel;
StatusBar: TStatusBar;
Image: TImage;
pnlLSPFrame: TPanel;
LoadSaveProgress: TProgressBar;
mnuExportChaotica: TMenuItem;
mnuResumeRender: TMenuItem;
mnuManual: TMenuItem;
tbCurves: TToolButton;
mnuCurves: TMenuItem;
N17: TMenuItem;
mnuTrace: TMenuItem;
CalculateWeights: TMenuItem;
FavouriteScripts1: TMenuItem;
Directory1: TMenuItem;
Randomizecolorspeed1: TMenuItem;
Calculatecolorspeed1: TMenuItem;
Changecolordistribution1: TMenuItem;
Changeweightdistribution1: TMenuItem;
ResetColorSpeed: TMenuItem;
mnuApoStyle: TMenuItem; // AV
N7: TMenuItem;
N22: TMenuItem;
AddSymmetry: TMenuItem;
BilateralSym: TMenuItem;
RotationalSym: TMenuItem;
DihedralSym: TMenuItem;
rot2: TMenuItem;
rot3: TMenuItem;
rot4: TMenuItem;
rot5: TMenuItem;
rot6: TMenuItem;
rot8: TMenuItem;
dih2: TMenuItem;
dih3: TMenuItem;
dih4: TMenuItem;
dih5: TMenuItem;
dih6: TMenuItem;
dih8: TMenuItem;
AddTile: TMenuItem; // AV
Square1: TMenuItem;
Rhombic1: TMenuItem;
Hexagonal1: TMenuItem;
ImportFromPNG: TMenuItem;
ToolButton23: TToolButton;
mnuScreenShot: TMenuItem;
N23: TMenuItem;
rot7: TMenuItem;
dih7: TMenuItem;
AddTemplate: TMenuItem;
N21: TMenuItem;
ResetColorValues: TMenuItem;
mnuExportBitmap: TMenuItem;
N24: TMenuItem;
mnuUnflatten: TMenuItem; // AV
mnuFlatten: TMenuItem;
N25: TMenuItem;
SortFlames: TMenuItem;
N26: TMenuItem;
mnuLowQuality: TMenuItem;
mnuMediumQuality: TMenuItem;
mnuHighQuality: TMenuItem;
mnuRefreshThumb: TMenuItem;
EnumerateFlames: TMenuItem;
DownloadPlugins: TMenuItem;
N27: TMenuItem;
mnuRefreshAllThumbs: TMenuItem;
mnuAnimator: TMenuItem;
tbAnimate: TToolButton; // AV
procedure mnuManualClick(Sender: TObject);
procedure mnuReportFlameClick(Sender: TObject);
procedure mnuTurnFlameToScriptClick(Sender: TObject);
procedure tbzoomoutwindowClick(Sender: TObject);
procedure mnuExitClick(Sender: TObject);
procedure mnuSaveUPRClick(Sender: TObject);
procedure ListViewChange(Sender: TObject; Item: TListItem;
Change: TItemChange);
procedure FormCreate(Sender: TObject);
procedure mnuRandomClick(Sender: TObject);
procedure mnuEqualizeClick(Sender: TObject);
procedure mnuEditorClick(Sender: TObject);
procedure mnuRWeightsClick(Sender: TObject);
procedure mnuRandomBatchClick(Sender: TObject);
procedure FormKeyPress(Sender: TObject; var Key: Char);
procedure FormKeyUpDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure mnuOptionsClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure mnuHelpTopicsClick(Sender: TObject);
procedure mnuNormalWeightsClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure mnuCopyUPRClick(Sender: TObject);
procedure mnuItemDeleteClick(Sender: TObject);
procedure ListViewEdited(Sender: TObject; Item: TListItem;
var S: string);
procedure mnuListRenameClick(Sender: TObject);
procedure BackPanelResize(Sender: TObject);
procedure RedrawTimerTimer(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure ShowStyledWindows(Sender: TObject);
procedure mnuVRandomClick(Sender: TObject);
procedure mnuSaveAsClick(Sender: TObject);
procedure mnuOpenClick(Sender: TObject);
procedure mnuGradClick(Sender: TObject);
procedure mnuSmoothGradientClick(Sender: TObject);
procedure mnuToolbarClick(Sender: TObject);
procedure mnuStatusBarClick(Sender: TObject);
procedure mnuFileContentsClick(Sender: TObject);
procedure mnuUndoClick(Sender: TObject);
procedure mnuRedoClick(Sender: TObject);
procedure Undo;
procedure Redo;
procedure mnuSaveUndoClick(Sender: TObject); // AV: restored and works
procedure mnuExportBitmapClick(Sender: TObject); // AV: to fast save params in PNG
procedure mnuFullScreenClick(Sender: TObject);
procedure mnuRenderClick(Sender: TObject);
procedure mnuMutateClick(Sender: TObject);
procedure mnuAdjustClick(Sender: TObject);
procedure mnuResetLocationClick(Sender: TObject);
procedure mnuAboutClick(Sender: TObject);
procedure mnuOpenGradientClick(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure FormActivate(Sender: TObject);
procedure FormDeactivate(Sender: TObject);
procedure mnuCalculateColorsClick(Sender: TObject);
procedure mnuRandomizeColorValuesClick(Sender: TObject);
procedure mnuEditScriptClick(Sender: TObject);
procedure mnuRunClick(Sender: TObject);
procedure mnuOpenScriptClick(Sender: TObject);
procedure mnuStopClick(Sender: TObject);
// procedure mnuImportGimpClick(Sender: TObject); // AV: rudiment from Apo 2.02
procedure mnuManageFavoritesClick(Sender: TObject);
procedure mnuImageSizeClick(Sender: TObject);
procedure ApplicationEventsActivate(Sender: TObject);
procedure mnuPasteClick(Sender: TObject);
procedure mnuCopyClick(Sender: TObject);
procedure mnuExportFlameClick(Sender: TObject);
procedure mnuExportChaoticaClick(Sender: TObject);
procedure ListXmlScannerStartTag(Sender: TObject; TagName: string;
Attributes: TAttrList);
procedure XmlScannerComment(Sender: TObject; Comment: string); // AV
procedure XMLScannerStartTag(Sender: TObject; TagName: string;
Attributes: TAttrList);
procedure XMLScannerEmptyTag(Sender: TObject; TagName: string;
Attributes: TAttrList);
procedure mnuFlamepdfClick(Sender: TObject);
procedure ImageMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure ImageMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure ImageMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure tbzoomwindowClick(Sender: TObject);
procedure tbDragClick(Sender: TObject);
procedure tbRotateClick(Sender: TObject);
procedure mnuSaveAllAsClick(Sender: TObject);
procedure tbQualityBoxKeyPress(Sender: TObject; var Key: Char);
procedure tbQualityBoxSet(Sender: TObject);
procedure ImageDblClick(Sender: TObject);
procedure tbShowAlphaClick(Sender: TObject);
procedure tbShowTraceClick(Sender: TObject);
procedure XmlScannerContent(Sender: TObject; Content: String);
procedure mnuRenderAllClick(Sender: TObject);
procedure btnViewIconsClick(Sender: TObject);
procedure btnViewListClick(Sender: TObject);
procedure XmlScannerEndTag(Sender: TObject; TagName: String);
procedure tbMessagesClick(Sender: TObject);
procedure btNewClick(Sender: TObject);
procedure ToolBarResize(Sender: TObject);
procedure mnuResetUIClick(Sender: TObject);
procedure AutoSaveTimerTimer(Sender: TObject);
procedure Restorelastautosave1Click(Sender: TObject);
procedure tbGuidesClick(Sender: TObject);
procedure tbCurvesClick(Sender: TObject);
procedure mnuTraceClick(Sender: TObject);
procedure CalculateWeightsClick(Sender: TObject);
procedure Randomizecolorspeed1Click(Sender: TObject);
procedure Calculatecolorspeed1Click(Sender: TObject);
procedure ResetColorSpeedClick(Sender: TObject);
procedure AddSymmetryClick(Sender: TObject); //AV
procedure AddTileClick(Sender: TObject); // AV
procedure ImportFromPNGClick(Sender: TObject); // AV
procedure mnuScreenShotClick(Sender: TObject); // AV
procedure ExtSysMenu(var Msg: TMessage); message WM_SysCommand; // AV
procedure AddTemplateClick(Sender: TObject);
procedure ResetColorValuesClick(Sender: TObject);
procedure mnuUnflattenClick(Sender: TObject);
procedure mnuFlattenClick(Sender: TObject);
procedure SortFlamesClick(Sender: TObject);
procedure ListViewColumnClick(Sender: TObject; Column: TListColumn);
procedure mnuThumbnailQualityClick(Sender: TObject);
procedure ListPopUpPopup(Sender: TObject);
procedure mnuRefreshThumbClick(Sender: TObject);
procedure EnumerateFlamesClick(Sender: TObject);
procedure ListViewDblClick(Sender: TObject);
procedure DownloadPluginsClick(Sender: TObject);
procedure mnuAnimatorClick(Sender: TObject);
procedure ListViewSelectItem(Sender: TObject; Item: TListItem;
Selected: Boolean);
private
SubstSource: TStringList;
SubstTarget: TStringList;
Renderer: TRenderThread;
FMouseMoveState: TMouseMoveState;
FSelectRect, FClickRect: TRect;
DrawSelection: boolean;
FRotateAngle: double;
FClickAngle: double;
FViewImage: TPngObject;
FViewPos, FViewOldPos: TSPoint;
FViewScale: double;
// FClickPitch, FNewPitch: double;
// FClickYaw, FNewYaw: double;
FShiftState: TShiftState;
// For parsing:
FinalXformLoaded: boolean;
ActiveXformSet: integer;
XMLPaletteFormat: string;
XMLPaletteCount: integer;
camDragMode, camDragged, camMM: boolean;
camDragPos, camDragOld: TPoint;
camDragValueX, camDragValueY: double;
oldApo: boolean; // AV: to check relict variations
defKB: HKL; // AV: for non-English users :)
procedure CreateSubstMap;
procedure InsertStrings;
procedure DrawImageView;
procedure DrawZoomWindow;
procedure DrawRotatelines(Angle: double);
// procedure DrawPitchYawLines(YawAngle: double; PitchAngle:double);
procedure SetAutoSaveTimer; // AV
procedure RunThumbnailThread; inline;
procedure FillVariantMenu;
procedure VariantMenuClick(Sender: TObject);
procedure FavoriteClick(Sender: TObject);
procedure ScriptItemClick(Sender: TObject);
procedure StopScripter; // AV
// AV: for Apo GUI themes
procedure CreateStyleList;
procedure StyleItemClick(Sender: TObject);
procedure HandleThreadCompletion(var Message: TMessage);
message WM_THREAD_COMPLETE;
procedure HandleThreadTermination(var Message: TMessage);
message WM_THREAD_TERMINATE;
public
{ Public declarations }
UndoIndex, UndoMax: integer;
Center: array[0..1] of double;
StartTime: TDateTime;
ParseLoadingBatch : boolean;
SurpressHandleMissingPlugins : boolean;
VarMenus: array of TMenuItem;
ListXmlScanner : TEasyXmlScanner;
XmlScanner : TXmlScanner;
function ReadWithSubst(Attributes: TAttrList; attrname: string): string;
// AV: added 3-rd parameter to be able to discard multiple updates
procedure LoadXMLFlame(filename, name: string; upd: boolean = true);
procedure DisableFavorites;
procedure EnableFavorites;
procedure ParseXML(var cp1: TControlPoint; const params: string; const ignoreErrors : boolean);
function SaveFlame(cp1: TControlPoint; title, filename: string): boolean;
function SaveXMLFlame(const cp1: TControlPoint; title, filename: string): boolean;
procedure DisplayHint(Sender: TObject);
procedure OnProgress(prog: double);
procedure ResizeImage;
// procedure DrawPreview;
procedure DrawFlame;
procedure UpdateUndo;
procedure LoadUndoFlame(index: integer; filename: string);
procedure SmoothPalette;
procedure Smoothize(const oldpal: TColorMap; const a, b: byte);
procedure RandomizeCP(var cp1: TControlPoint; alg: integer = 0);
function UPRString(cp1: TControlPoint; Entry: string): string;
function SaveGradient(Gradient, Title, FileName: string): boolean;
function GradientFromPalette(const pal: TColorMap; const title: string): string;
procedure StopThread;
procedure UpdateWindows;
procedure ResetLocation;
procedure RandomBatch;
procedure GetScripts;
function ApplicationOnHelp(Command: Word; Data: Integer; var CallHelp: Boolean): Boolean;
function SystemErrorMessage: string;
function SystemErrorMessage2(errno: cardinal): string;
// function RetrieveXML(cp : TControlPoint): string; // AV: we can call it directly
procedure ApplyThemedColors;
// AV: for reading / writing embedded parameters
procedure PasteFlameXML(flameXML: string);
procedure ImportThumbnailPNG(Filename: string);
function LoadXMLFlameTextPNG(FileName: string): string;
// AV: for updating the list with flame previews
procedure SetThumbnailProperties;
procedure RefreshThumbnail;
procedure UpdateThumbnails;
procedure AddFlameToList(const title: string = '');
end;
procedure ListXML(FileName: string; sel: integer; selname: string = ''); // AV
procedure ListIFS(FileName: string; sel: integer); // AV: for loading Undo flame files
function FlameToXML(const cp1: TControlPoint; exporting: boolean = false; title: string = ''): string; // AV: make global
function LoadXMLFlameText(filename, name: string) : string;
function FindFlameXML(const FlameStr: string; const Title: string) : Integer; // AV
procedure FlameFromUndo(cp: TControlPoint; const FlameName: string; const ParamFile: string); // AV
function EntryExists(En, Fl: string): boolean;
function XMLEntryExists(title, filename: string): boolean;
function DeleteEntry(Entry, FileName: string): boolean;
function CleanIdentifier(ident: string): string;
function CleanUPRTitle(ident: string): string;
function GradientString(c: TColorMap): string;
procedure RotateCMapHue(var cp: TControlPoint); // AV
function FlameInClipboard: boolean; // AV
function RemoveExt(filename: string): string; // AV
function WinShellExecute(const Operation, AssociatedFile: string): Boolean; // AV
// AV: for making window screenshots
procedure GetFormScreenShot(const AFileName: string);
procedure SaveScreenShot(const AFormName: string);
var
MainForm: TMainForm;
pname, ptime: string;
// pversion: string;
nxform: integer;
MainCp: TControlPoint;
ParseCp: TControlPoint;
MemCp: TControlPoint;
ThumbnailSize: integer;
GeneratingThumbs: boolean; // AV
AppVersionString: string;
implementation
uses
ClipBrd, Editor, Options, Settings, Template, MissingPlugin, Chaotica,
FullScreen, FormRender, Mutate, Adjust, Browser, Save, About, CmapData,
ScriptForm, FormFavorites, FormExport, RndFlame, Tracer, Types, SplashForm,
Animate;
const
TbBreakWidth = 810; // AV
{$R *.DFM}
procedure AssignBitmapProperly(var Bitmap: TBitmap; Source: TBitmap);
begin
Bitmap.Dormant;
Bitmap.FreeImage;
Bitmap.Width := 0;
Bitmap.Assign(Source);
end;
procedure FreeBitmapProperly(var Bitmap: TBitmap);
begin
try
Bitmap.Dormant;
Bitmap.FreeImage;
finally
Bitmap.Free;
end;
end;
{//////////////// Screenshot utils ////////////////////////}
procedure GetFormScreenShot(const AFileName: string); // AV
var
ScreenShot: TBitmap;
WindowRect: TRect;
begin
ScreenShot := TBitmap.Create;
try
ScreenShot.PixelFormat := pf32bit;
try
WindowRect := Screen.ActiveForm.BoundsRect;
ScreenShot.Width := WindowRect.Width;
ScreenShot.Height := WindowRect.Height;
BitBlt(ScreenShot.Canvas.Handle, 0, 0, WindowRect.Width, WindowRect.Height,
GetWindowDC(Screen.ActiveForm.Handle), 0, 0, SRCCOPY);
except
ScreenShot := nil;
end;
if ScreenShot <> nil then
ScreenShot.SaveToFile(AFileName);
finally
ScreenShot.Free;
end;
end;
procedure SaveScreenShot(const AFormName: string); // AV
var
s: string;
begin
if not DirectoryExists(ScreenShotPath) then
begin
CreateDir(AppPath + 'ScreenShots\');
ScreenShotPath := AppPath + 'ScreenShots\';
end;
s := ScreenShotPath + AFormName + FormatDateTime(' (MM-dd-yyyy hh-mm-ss)', Now) + '.bmp';
try
GetFormScreenShot(s);
Application.MessageBox(PChar(Format(TextByKey('common-screenshot-saved'),
[ExtractFileName(s), ExtractFilePath(s)])),
ApophysisSVN, MB_ICONINFORMATION);
except
Application.MessageBox(PChar(TextByKey('common-screenshot-error')),
ApophysisSVN, MB_ICONERROR);
end;
end;
{//////////////////////////////////////////////////////////////////////////////}
function FlameInClipboard: boolean;
var
flamestr: string;
isstart, isend: integer;
begin
{ returns true if a flame in clipboard - can be tricked }
result := false;
if Clipboard.HasFormat(CF_TEXT) then
begin
flamestr := Clipboard.AsText;
isstart := Pos('<flame', flamestr);
isend := Pos('</flame>', flamestr);
if (isstart > 0) and (isend > 0) and (isstart < isend) then
Result := true;
end;
end;
{ ************************************* Help ********************************* }
procedure ShowHelp(Pt: TPoint; ContextId: Integer);
//var
//Popup: THHPopup;
begin
(* -X- context help not longer supported
FillChar(Popup, SizeOf(Popup), 0);
Popup.cbStruct := SizeOf(Popup);
Popup.hinst := 0;
Popup.idString := ContextId;
Popup.pszText := nil;
GetCursorPos(Pt);
Popup.pt := Pt;
Popup.clrForeGround := TColorRef(-1);
Popup.clrBackground := TColorRef(-1);
Popup.rcMargins := Rect(-1, -1, -1, -1);
Popup.pszFont := '';
HtmlHelp(0, PChar(AppPath + 'Apophysis7x.chm::/Popups.txt'), HH_DISPLAY_TEXT_POPUP, DWORD(@Popup));
*)
end;
procedure TMainForm.ExtSysMenu(var Msg: TMessage);
begin
if Msg.WParam = $C0 then mnuScreenShot.Click;
inherited;
end;
procedure TMainForm.InsertStrings;
begin
mnuCopy.Caption := TextByKey('common-copy');
mnuPaste.Caption := TextByKey('common-paste');
mnuCopy.Hint := TextByKey('main-menu-edit-copy');
mnuPaste.Hint := TextByKey('main-menu-edit-paste');
mnuItemDelete.Caption := TextByKey('common-delete');
mnuListRename.Caption := TextByKey('common-rename');
mnuItemDelete.Hint := TextByKey('main-menu-deletehint'); // AV
mnuListRename.Hint := TextByKey('main-menu-renamehint'); // AV
mnuRefreshThumb.Caption := TextByKey('main-menu-updatethumb');
mnuRefreshAllThumbs.Caption := TextByKey('main-menu-updateallthumbs');
mnuLowQuality.Caption := TextByKey('common-lowquality'); // AV
mnuMediumQuality.Caption := TextByKey('common-mediumquality'); // AV
mnuHighQuality.Caption := TextByKey('common-highquality'); // AV
mnuUndo.Caption := TextByKey('common-undo');
mnuPopUndo.Caption := TextByKey('common-undo');
btnUndo.Hint := TextByKey('main-toolbar-undo');
mnuUndo.Hint := TextByKey('main-toolbar-undo');
mnuRedo.Caption := TextByKey('common-redo');
mnuPopRedo.Caption := TextByKey('common-redo');
btnRedo.Hint := TextByKey('main-toolbar-redo');
mnuRedo.Hint := TextByKey('main-toolbar-redo');
MainFile.Caption := TextByKey('main-menu-file-title');
New1.Caption := TextByKey('main-menu-file-new');
New1.Hint := TextByKey('main-toolbar-new');
mnuTrace.Caption := TextByKey('main-menu-options-tracelog');
btNew.Hint := TextByKey('main-toolbar-new');
mnuOpen.Caption := TextByKey('main-menu-file-open');
btnOpen.Hint := TextByKey('main-toolbar-open');
mnuOpen.Hint := TextByKey('main-toolbar-open');
ImportFromPNG.Caption := TextByKey('main-menu-file-loadpng');
ImportFromPNG.Hint := TextByKey('main-menu-file-loadpnghint');
mnuScreenShot.Caption := TextByKey('main-menu-screenshot');
RestoreLastAutosave1.Caption := TextByKey('main-menu-file-restoreautosave');
RestoreLastAutosave1.Hint := TextByKey('main-menu-file-autosavehint');
mnuSaveAs.Caption := TextByKey('main-menu-file-saveparams');
mnuSaveAs.Hint := TextByKey('main-toolbar-saveparams');
btnSave.Hint := TextByKey('main-toolbar-saveparams');
mnuSaveAllAs.Caption := TextByKey('main-menu-file-saveallparams');
mnuSaveAllAs.Hint := TextByKey('main-menu-file-saveallhint');
mnuSmoothGradient.Caption := TextByKey('main-menu-file-smoothpalette');
mnuSmoothGradient.Hint := TextByKey('main-menu-file-smoothpalettehint');
mnuOpenGradient.Caption := TextByKey('main-menu-file-gradientbrowser');
mnuOpenGradient.Hint := TextByKey('main-menu-file-gradientbrowserhint');
mnuSaveUPR.Caption := TextByKey('main-menu-file-exportupr');
mnuExportFlame.Caption := TextByKey('main-menu-file-exportflame');
mnuExportChaotica.Caption := TextByKey('main-menu-file-exportchaotica');
mnuRandomBatch.Caption := TextByKey('main-menu-file-randombatch');
mnuExit.Caption := TextByKey('main-menu-file-exit');
mnuExit.Hint := TextByKey('main-menu-file-exithint');
mnuSaveUPR.Hint := TextByKey('main-menu-file-exportuprhint');
mnuExportFlame.Hint := TextByKey('main-menu-file-exportflamehint');
mnuExportChaotica.Hint := TextByKey('main-menu-file-exportchaoticahint');
mnuExportBitmap.Caption := TextByKey('main-menu-saveimage'); // AV
mnuRandomBatch.Hint := TextByKey('main-menu-file-randombatchhint');
MainEdit.Caption := TextByKey('main-menu-edit-title');
mnuSaveUndo.Caption := TextByKey('main-menu-edit-saveundo');
mnuCopyUPR.Caption := TextByKey('main-menu-edit-copyasupr');
mnuCopyUPR.Hint := TextByKey('main-menu-edit-copyuprhint');
View1.Caption := TextByKey('main-menu-view-title');
mnuFullScreen.Caption := TextByKey('main-menu-view-fullscreen');
mnuPopFullscreen.Caption := TextByKey('main-menu-view-fullscreen');
btnFullScreen.Hint := TextByKey('main-toolbar-fullscreen');
mnuFullScreen.Hint := TextByKey('main-toolbar-fullscreen');
mnuEditor.Caption := TextByKey('main-menu-view-editor');
mnuEditor.Hint := TextByKey('main-toolbar-editor');
tbEditor.Hint := TextByKey('main-toolbar-editor');
mnuAdjust.Caption := TextByKey('main-menu-view-adjustment');
mnuAdjust.Hint := TextByKey('main-toolbar-adjustment');
tbAdjust.Hint := TextByKey('main-toolbar-adjustment');
mnuGrad.Caption := TextByKey('main-menu-view-gradient');
mnuGrad.Hint := TextByKey('main-toolbar-gradient');
tbPalette.Hint := TextByKey('main-toolbar-gradient');
mnuMutate.Hint := TextByKey('main-toolbar-mutation');
mnuMutate.Caption := TextByKey('main-menu-view-mutation');
tbMutate.Hint := TextByKey('main-toolbar-mutation');
mnuImageSize.Caption := TextByKey('main-menu-view-imagesize');
mnuImageSize.Hint := TextByKey('main-toolbar-imagesize');
tbImageSize.Hint := TextByKey('main-toolbar-imagesize');
mnuMessages.Caption := TextByKey('main-menu-view-messages');
mnuMessages.Hint := TextByKey('main-toolbar-messages');
tbMessages.Hint := TextByKey('main-toolbar-messages');
tbCurves.Hint := TextByKey('main-toolbar-curves');
mnuCurves.Hint := TextByKey('main-toolbar-curves');
mnuCurves.Caption := TextByKey('main-menu-view-curves');
tbAnimate.Hint := TextByKey('main-toolbar-animator');
mnuAnimator.Caption := TextByKey('main-menu-view-animator'); // AV
mnuAnimator.Hint := GetLongHint(tbAnimate.Hint); // AV
MainFlame.Caption := TextByKey('main-menu-flame-title');
mnuResetLocation.Caption := TextByKey('main-menu-flame-reset');
mnuPopResetLocation.Caption := TextByKey('main-menu-flame-reset');
mnuResetLocation.Hint := TextByKey('main-toolbar-reset');
btnReset.Hint := TextByKey('main-toolbar-reset');
mnuRandom.Caption := TextByKey('main-menu-flame-randomize');
mnuRandom.Hint := TextByKey('main-menu-flame-randomizehint');
mnuRWeights.Caption := TextByKey('main-menu-flame-randomweights');
mnuRWeights.Hint := TextByKey('main-menu-flame-randomweightshint');
mnuEqualize.Caption := TextByKey('main-menu-flame-equalweights');
mnuEqualize.Hint := TextByKey('main-menu-flame-equalweightshint');
CalculateWeights.Caption := TextByKey('main-menu-flame-calculateweights');
CalculateWeights.Hint := TextByKey('main-menu-flame-calculateweightshint');
mnuNormalWeights.Caption := TextByKey('main-menu-flame-normweights');
mnuNormalWeights.Hint := TextByKey('main-menu-flame-normweightshint');
mnuCalculateColors.Caption := TextByKey('main-menu-flame-calculatecolors');
mnuRandomizeColorValues.Caption := TextByKey('main-menu-flame-randomizecolors');
Calculatecolorspeed1.Caption := TextByKey('main-menu-flame-calculatecolorspeed');
Randomizecolorspeed1.Caption := TextByKey('main-menu-flame-randomizecolorspeed');
ResetColorSpeed.Caption := TextByKey('main-menu-flame-resetcolorspeed');
ResetColorValues.Caption := TextByKey('main-menu-flame-resetcolors');
ResetColorValues.Hint := TextByKey('main-menu-flame-resetcolorshint');
mnuCalculateColors.Hint := TextByKey('main-menu-flame-calccolorshint');
mnuRandomizeColorValues.Hint := TextByKey('main-menu-flame-randcolorshint');
Calculatecolorspeed1.Hint := TextByKey('main-menu-flame-calccolorspeedhint');
Randomizecolorspeed1.Hint := TextByKey('main-menu-flame-randcolorspeedhint');
Resetcolorspeed.Hint := TextByKey('main-menu-flame-resetcolorspeedhint');
mnuFlatten.Caption := TextByKey('main-menu-flame-flatten');
mnuFlatten.Hint := TextByKey('main-menu-flame-flattenhint');
mnuUnflatten.Caption := TextByKey('main-menu-flame-unflatten');
mnuUnflatten.Hint := TextByKey('main-menu-flame-unflattenhint');
mnuRender.Caption := TextByKey('main-menu-flame-rendertodisk');
mnuRender.Hint := TextByKey('main-toolbar-render');
btnRender.Hint := TextByKey('main-toolbar-render');
mnuRenderAll.Caption := TextByKey('main-menu-flame-renderallflames');
mnuRenderAll.Hint := TextByKey('main-toolbar-renderall');
tbRenderAll.Hint := TextByKey('main-toolbar-renderall');
mnuReportFlame.Caption := TextByKey('main-menu-flame-generatereport');
mnuReportFlame.Hint := TextByKey('main-menu-flame-reporthint');
AddTemplate.Caption := TextByKey('main-menu-flame-template');
AddTemplate.Hint := TextByKey('main-menu-flame-templatehint');
mnuVar.Caption := TextByKey('main-menu-variation-title');
mnuVRandom.Caption := TextByKey('main-menu-variation-random');
mnuBuiltinVars.Caption := TextByKey('main-menu-variation-builtin');
mnuPluginVars.Caption := TextByKey('main-menu-variation-plugins');
mnuVRandom.Hint := TextByKey('main-menu-variation-randomhint');
mnuBuiltinVars.Hint := TextByKey('main-menu-variation-builtinhint');
mnuPluginVars.Hint := TextByKey('main-menu-variation-pluginshint');
mnuScript.Caption := TextByKey('main-menu-script-title');
mnuRun.Caption := TextByKey('main-menu-script-run');
mnuRun.Hint := TextByKey('main-toolbar-runscript');
btnRunScript.Hint := TextByKey('main-toolbar-runscript');
mnuStop.Caption := TextByKey('main-menu-script-stop');
mnuStop.Hint := TextByKey('main-toolbar-stopscript');
btnStopScript.Hint := TextByKey('main-toolbar-stopscript');
mnuOpenScript.Caption := TextByKey('main-menu-script-open');
mnuOpenScript.Hint := TextByKey('main-menu-script-openhint');
mnuEditScript.Caption := TextByKey('main-menu-script-edit');
mnuEditScript.Hint := TextByKey('main-toolbar-editscript');
tbEditScript.Hint := TextByKey('main-toolbar-editscript');
mnuManageFavorites.Caption := TextByKey('main-menu-script-managefaves');
mnuTurnFlameToScript.Caption := TextByKey('main-menu-script-flametoscript');
mnuManageFavorites.Hint := TextByKey('main-menu-script-managefaveshint');
mnuTurnFlameToScript.Hint := TextByKey('main-menu-script-flametoscripthint');
FavouriteScripts1.Caption := TextByKey('favscripts-title');
FavouriteScripts1.Hint := TextByKey('favscripts-hint');
mnuView.Caption := TextByKey('main-menu-options-title');
Directory1.Caption := IfThen(FavouriteScripts1.Enabled,
TextByKey('main-menu-script-more'), TextByKey('main-menu-script-directory'));
Directory1.Hint := TextByKey('main-menu-script-directoryhint');
mnuToolbar.Caption := TextByKey('main-menu-options-togglemaintoolbar');
mnuStatusBar.Caption := TextByKey('main-menu-options-togglestatusbar');
mnuFileContents.Caption := TextByKey('main-menu-options-togglefilelist');
mnuResetUI.Caption := TextByKey('main-menu-options-resetfilelistwidth');
mnuResetUI.Hint := TextByKey('main-menu-options-resetwidthhint');
SortFlames.Caption := TextByKey('main-menu-options-sortflames');
EnumerateFlames.Caption := TextByKey('main-menu-options-enumflames');
mnuTrace.Hint := TextByKey('main-menu-options-traceloghint');
mnuOptions.Caption := TextByKey('main-menu-options-showoptions');
mnuOptions.Hint := TextByKey('main-toolbar-options');
tbOptions.Hint := TextByKey('main-toolbar-options');
MainHelp.Caption := TextByKey('main-menu-help-title');
mnuHelpTopics.Caption := TextByKey('main-menu-help-contents');
mnuFlamePDF.Caption := TextByKey('main-menu-help-aboutalgorithm');
mnuAbout.Caption := TextByKey('main-menu-help-aboutapophysis');
mnuHelpTopics.Hint := TextByKey('main-menu-help-contentshint');
mnuFlamePDF.Hint := TextByKey('main-menu-help-aboutalgorithmhint');
mnuAbout.Hint := TextByKey('main-menu-help-aboutapophysishint');
btnViewList.Hint := TextByKey('main-toolbar-listviewmode-classic');
btnViewIcons.Hint := TextByKey('main-toolbar-listviewmode-icons');
tbShowAlpha.Hint := TextByKey('main-toolbar-togglealpha');
tbGuides.Hint := TextByKey('main-toolbar-toggleguides');
tbQualityBox.Hint := TextByKey('main-toolbar-quality');
tbDrag.Hint := TextByKey('main-toolbar-modemove');
tbRotate.Hint := TextByKey('main-toolbar-moderotate');
tbZoomIn.Hint := TextByKey('main-toolbar-modezoomin');
tbZoomOut.Hint := TextByKey('main-toolbar-modezoomout');
ListView1.Columns[0].Caption := TextByKey('save-name');
mnuResumeRender.Caption := TextByKey('main-menu-flame-resumeunfinished');
mnuApoStyle.Caption := TextByKey('main-menu-options-apouistyle');
mnuApoStyle.Hint := TextByKey('main-menu-options-apouistylehint');
mnuManual.Caption := TextByKey('main-menu-help-ifstheory');
mnuManual.Hint := TextByKey('main-menu-help-ifstheoryhint'); // AV
DownloadPlugins.Caption := TextByKey('main-menu-help-pluginlink'); // AV
Changeweightdistribution1.Caption := TextByKey('main-menu-flame-changeweights');
Changecolordistribution1.Caption := TextByKey('main-menu-flame-changecolors');
Hexagonal1.Caption := TextByKey('main-menu-flame-hextile');
Rhombic1.Caption := TextByKey('main-menu-flame-rhombustile');
Square1.Caption := TextByKey('main-menu-flame-squaretile');
AddTile.Caption := TextByKey('main-menu-flame-addtile');
AddSymmetry.Caption := TextByKey('main-menu-flame-addsymmetry');
BilateralSym.Caption := TextByKey('options-tab-random-type-bilateral');
RotationalSym.Caption := TextByKey('options-tab-random-type-rotational') + TextByKey('main-menu-flame-symorder');
DihedralSym.Caption := TextByKey('options-tab-random-type-dihedral') + TextByKey('main-menu-flame-symorder');
end;
function TMainForm.ApplicationOnHelp(Command: Word; Data: Integer; var CallHelp: Boolean): Boolean;
var
Pos: TPoint;
begin
Pos.x := 0;
Pos.y := 0;
CallHelp := False;
Result := True;
case Command of
HELP_SETPOPUP_POS: Pos := SmallPointToPoint(TSmallPoint(Data));
HELP_CONTEXTPOPUP: ShowHelp(Pos, Data);
else Result := False;
end;
end;
procedure TMainForm.ApplyThemedColors; // AV
var
AStyle: TCustomStyleServices;
MenuC1, MenuC2: TColor;
mb: TMenuBreak;
i: smallint;
begin
AStyle := TStyleManager.ActiveStyle;
CurrentStyle := AStyle.Name;
BrightColor := AStyle.GetSystemColor(clHighlight);
WinColor := AStyle.GetSystemColor(clWindow);
TextColor := AStyle.GetSystemColor(clWindowText);
MidColor := MiddleColor(BrightColor, WinColor);
AStyle.GetElementColor(AStyle.GetElementDetails(tmPopupItemNormal),
ecTextColor, MenuC1);
AStyle.GetElementColor(AStyle.GetElementDetails(tmMenuBarItemNormal),
ecTextColor, MenuC2);
IsLightMenu := (MenuC1 > $00BEBEBE);
if IsLightMenu then
mnuEditor.ImageIndex := 75
else
mnuEditor.ImageIndex := 19;
if CurrentStyle <> 'Obsidian' then
IsLightMenu := IsLightMenu or (MenuC2 > $00BEBEBE);
if IsLightMenu then
tbEditor.ImageIndex := 75
else
tbEditor.ImageIndex := 19;
IsDarkTheme := (CurrentStyle = 'TabletDark') or (CurrentStyle = 'Auric')
or (CurrentStyle = 'Cobalt XEMedia') or (CurrentStyle = 'Onyx Blue')
or (CurrentStyle = 'Ruby Graphite') or (CurrentStyle = 'Golden Graphite');
if (CurrentStyle = 'Windows') then mb := mbNone
else mb := mbBreak;
i := 0;
while i < mnuBuiltinVars.Count do
begin
mnuBuiltinVars[i].Break := mb;
inc(i, mbHeight);
end;
i := 0;
while i < mnuPluginVars.Count do
begin
mnuPluginVars[i].Break := mb;
inc(i, mbHeight);
end;
end;
{ **************************************************************************** }
procedure TMainForm.StopThread;
begin
RedrawTimer.Enabled := False;
if Assigned(Renderer) then begin
assert(Renderer.Suspended = false);
Renderer.Terminate;
Renderer.WaitFor;
end;
end;
(*
// AV: how old are they? Maybe since Apo 1.0? :)
procedure EqualizeVars(const x: integer);
var
i: integer;
begin
for i := 0 to Transforms - 1 do
MainCp.xform[x].SetVariation(i, 1.0 / NRVAR);
end;
procedure NormalVars(const x: integer);
var
i: integer;
td: double;
begin
td := 0.0;
for i := 0 to 6 do
td := td + Maincp.xform[x].GetVariation(i);
if (td < 0.001) then
EqualizeVars(x)
else
for i := 0 to 6 do
MainCp.xform[x].SetVariation(i, MainCp.xform[x].GetVariation(i) / td);
end;
*)
(*
// AV: commented out since we have the same methods in RndFlame unit!
procedure RandomVariation(cp: TControlPoint);
{ Randomise variation parameters }
var
a, b, i, j: integer;
begin
inc(MainSeed);
RandSeed := MainSeed;
for i := 0 to cp.NumXForms - 1 do
begin
for j := 0 to NRVAR - 1 do
cp.xform[i].SetVariation(j, 0);
repeat
a := random(NRVAR);
until Variations[a];
repeat
b := random(NRVAR);
until Variations[b];
if (a = b) then
begin
cp.xform[i].SetVariation(a, 1);
end
else
begin
cp.xform[i].SetVariation(a, random);
cp.xform[i].SetVariation(b, 1 - cp.xform[i].GetVariation(a));
end;
end;
end;
procedure SetVariation(cp: TControlPoint);
{ Set the current Variation }
var
i, j: integer;
begin
if Variation = vRandom then
begin
RandomVariation(cp);
end
else
for i := 0 to cp.NumXForms - 1 do
begin
for j := 0 to NRVAR - 1 do
cp.xform[i].SetVariation(j, 0);
cp.xform[i].SetVariation(integer(Variation), 1);
end;
end;
*)
function FindFlameXML(const FlameStr: string; const Title: string) : Integer;
var
i: integer;
FlameStart: string;
begin
Result := 0;
FlameStart := '<flame ';
if Title <> '' then
FlameStart := '<flame name="' + Title + '"';
if FlameStr <> '' then
begin
i := Pos(FlameStart, Lowercase(FlameStr));
while i > 0 do
if PosEx('</flame', Lowercase(FlameStr), i+1) > i then
begin
Result := i;
break;
end;
end;
end;
///////////////////////////////////////////////////////////////////////////////
procedure TMainForm.RandomizeColorSpeed1Click(Sender: TObject);
var
i: smallint;
begin
inc(MainSeed);
RandSeed := MainSeed;
StopThread;
UpdateUndo;
for i := 0 to Transforms - 1 do
maincp.xform[i].symmetry := 2 * random - 1;
RedrawTimer.Enabled := True;
UpdateWindows;
end;
procedure TMainForm.RandomizeCP(var cp1: TControlPoint; alg: integer = 0);
var
sourceCP: TControlPoint;
begin
if assigned(MainCP) then
sourceCP := MainCP.Clone
else
SourceCP := nil;
if assigned(cp1) then begin
cp1.Free;
cp1 := nil;
end;
cp1 := RandomFlame(sourceCP, alg);
if assigned(sourceCP) then
sourceCP.Free;
end;
function TMainForm.GradientFromPalette(const pal: TColorMap; const title: string): string;
var
c, i, j: integer;
strings: TStringList;
begin
strings := TStringList.Create;
try
strings.add('gradient:');
strings.add(' title="' + CleanUPRTitle(title) + '" smooth=no');
for i := 0 to 255 do
begin
j := round(i * (399 / 255));
c := pal[i][2] shl 16 + pal[i][1] shl 8 + pal[i][0];
strings.Add(' index=' + IntToStr(j) + ' color=' + intToStr(c));
end;
result := strings.text;
finally
strings.free;
end;
end;
procedure TMainForm.OnProgress(prog: double);
var
Elapsed, Remaining: TDateTime;
IntProg: Integer;
begin
IntProg := (round(prog * 100));
LoadSaveProgress.Position := IntProg;
if (IntProg = 100) then LoadSaveProgress.Position := 0;
Elapsed := Now - StartTime;
StatusBar.Panels[1].Text := Format(TextByKey('render-status-elapsed') + ' %2.2d:%2.2d:%2.2d.%2.2d',
[Trunc(Elapsed * 24),
Trunc((Elapsed * 24 - Trunc(Elapsed * 24)) * 60),
Trunc((Elapsed * 24 * 60 - Trunc(Elapsed * 24 * 60)) * 60),
Trunc((Elapsed * 24 * 60 * 60 - Trunc(Elapsed * 24 * 60 * 60)) * 100)]);
if prog > 0 then
Remaining := Elapsed/prog - Elapsed
else
Remaining := 0;
StatusBar.Panels[2].Text := Format(TextByKey('render-status-remaining') + ' %2.2d:%2.2d:%2.2d.%2.2d',
[Trunc(Remaining * 24),
Trunc((Remaining * 24 - Trunc(Remaining * 24)) * 60),
Trunc((Remaining * 24 * 60 - Trunc(Remaining * 24 * 60)) * 60),
Trunc((Remaining * 24 * 60 * 60 - Trunc(Remaining * 24 * 60 * 60)) * 100)]);
StatusBar.Panels[3].Text := MainCp.name;
Application.ProcessMessages;
end;
procedure TMainForm.UpdateUndo;
begin
MainCp.FillUsedPlugins;
SaveFlame(MainCp, Format('%.4d-', [UndoIndex]) + MainCp.name,
AppPath + undoFilename);
Inc(UndoIndex);
UndoMax := UndoIndex; //Inc(UndoMax);
mnuSaveUndo.Enabled := true;
mnuUndo.Enabled := True;
mnuPopUndo.Enabled := True;
mnuRedo.Enabled := false;
mnuPopRedo.Enabled := false;
btnUndo.enabled := true;
btnRedo.Enabled := false;
EditForm.mnuUndo.Enabled := True;
EditForm.mnuRedo.Enabled := false;
EditForm.tbUndo.enabled := true;
EditForm.tbRedo.enabled := false;
AdjustForm.btnUndo.enabled := true;
AdjustForm.btnRedo.enabled := false;
end;
(*
function GradientEntries(gFilename: string): string;
var
i, p: integer;
Title: string;
FileStrings: TStringList;
NewStrings: TStringList;
begin
FileStrings := TStringList.Create;
NewStrings := TStringList.Create;
NewStrings.Text := '';
FileStrings.LoadFromFile(gFilename);
try
if (Pos('{', FileStrings.Text) <> 0) then
begin
for i := 0 to FileStrings.Count - 1 do
begin
p := Pos('{', FileStrings[i]);
if (p <> 0) then
begin
Title := Trim(Copy(FileStrings[i], 1, p - 1));
if (Title <> '') and (LowerCase(Title) <> 'comment') then
begin { Otherwise bad format }
NewStrings.Add(Title);
end;
end;
end;
GradientEntries := NewStrings.Text;
end;
finally
FileStrings.Free;
NewStrings.Free;
end;
end;
function GradTitle(str: string): string;
var
p: integer;
begin
p := pos('{', str);
GradTitle := Trim(copy(str, 1, p - 1));
end;
*)
{ ********************************* File ************************************* }
function EntryExists(En, Fl: string): boolean;
{ Searches for existing identifier in parameter files }
var
FStrings: TStringList;
i: integer;
begin
Result := False;
if FileExists(Fl) then
begin
FStrings := TStringList.Create;
try
FStrings.LoadFromFile(Fl);
for i := 0 to FStrings.Count - 1 do
if Pos(LowerCase(En) + ' {', Lowercase(FStrings[i])) <> 0 then
Result := True;
finally
FStrings.Free;
end
end
else
Result := False;
end;
function CleanXMLName(ident: string): string;
var
i: integer;
begin
for i := 1 to Length(ident) do
begin
if ident[i] = '*' then
ident[i] := '_'
else if ident[i] = '"' then
ident[i] := #39;
end;
Result := ident;
end;
function CleanIdentifier(ident: string): string;
{ Strips unwanted characters from an identifier}
var
i: integer;
begin
for i := 1 to Length(ident) do
if (ident[i] = #32) or (ident[i] = '}') or (ident[i] = '{') then
ident[i] := '_';
Result := ident;
end;
function CleanUPRTitle(ident: string): string;
{ Strips braces but leave spaces }
var
i: integer;
begin
for i := 1 to Length(ident) do
if (ident[i] = '}') or (ident[i] = '{') then
ident[i] := '_';
Result := ident;
end;
function DeleteEntry(Entry, FileName: string): boolean;
{ Deletes an entry from a multi-entry file }
var
Strings: TStringList;
p, i: integer;
begin
Result := True;
Strings := TStringList.Create;
try
i := 0;
Strings.LoadFromFile(FileName);
while Pos(Entry + ' ', Trim(Strings[i])) <> 1 do
begin
inc(i);
end;
repeat
p := Pos('}', Strings[i]);
Strings.Delete(i);
until p <> 0;
if (i < Strings.Count) and (Trim(Strings[i]) = '') then Strings.Delete(i);
Strings.SaveToFile(FileName);
finally
Strings.Free;
end;
end;
function SaveUPR(Entry, FileName: string): boolean;
{ Saves UF parameter to end of file }
var
UPRFile: TextFile;
begin
Result := True;
try
AssignFile(UPRFile, FileName);
if FileExists(FileName) then
begin
if EntryExists(Entry, FileName) then DeleteEntry(Entry, FileName);
Append(UPRFile);
end
else
ReWrite(UPRFile);
WriteLn(UPRFile, MainForm.UPRString(MainCp, Entry));
CloseFile(UPRFile);
except on EInOutError do
begin
Result := False;
raise Exception.CreateFmt(TextByKey('common-genericsavefailure'), [FileName]);
end;
end;
end;
(* // AV: outdated, for affine coefs only
function IFSToString(cp: TControlPoint; Title: string): string;
{ Creates a string containing a formated IFS parameter set }
var
i: integer;
a, b, c, d, e, f, p: double;
Strings: TStringList;
begin
Strings := TStringList.Create;
try
Strings.Add(CleanIdentifier(Title) + ' {');
for i := 0 to Transforms - 1 do
begin
a := cp.xform[i].c[0][0];
b := cp.xform[i].c[0][1];
c := cp.xform[i].c[1][0];
d := cp.xform[i].c[1][1];
e := cp.xform[i].c[2][0];
f := cp.xform[i].c[2][1];
p := cp.xform[i].density;
Strings.Add(Format('%.6g %.6g %.6g %.6g %.6g %.6g %.6g',
[a, b, c, d, e, f, p]));
end;
Strings.Add('}');
IFSToString := Strings.Text;
finally
Strings.Free;
end;
end;
*)
procedure RotateCMapHue(var cp: TControlPoint); // AV
var
i: byte;
h, s, v: real;
hue: double;
begin
hue := cp.hue_rotation;
if (hue > 0) and (hue < 1) then // has visual effect
for i := 0 to 255 do
begin
RGBToHSV(cp.cmap[i][0], cp.cmap[i][1], cp.cmap[i][2], h, s, v);
h := Round(360 + h + (hue * 360)) mod 360;
HSVToRGB(h, s, v, cp.cmap[i][0], cp.cmap[i][1], cp.cmap[i][2]);
end;
end;
function TMainForm.SaveFlame(cp1: TControlPoint; title, filename: string): boolean;
{ Saves Flame parameters to end of file }
var
IFile: TextFile;
sl: TStringList;
i: integer;
begin
Result := True;
try
AssignFile(IFile, filename);
if FileExists(filename) then
begin
if EntryExists(title, filename) then DeleteEntry(title, fileName);
Append(IFile);
end
else ReWrite(IFile);
sl := TStringList.Create;
try
cp1.SaveToStringList(sl);
WriteLn(IFile, title + ' {');
write(IFile, sl.Text);
WriteLn(IFile, 'palette:');
for i := 0 to 255 do
begin
WriteLn(IFile, IntToStr(cp1.cmap[i][0]) + ' ' +
IntToStr(cp1.cmap[i][1]) + ' ' +
IntToStr(cp1.cmap[i][2]))
end;
WriteLn(IFile, ' }');
finally
sl.free
end;
WriteLn(IFile, ' ');
CloseFile(IFile);
except on EInOutError do
begin
Result := False;
raise Exception.CreateFmt(TextByKey('common-genericsavefailure'), [FileName]);
end;
end;
end;
function ColorToXmlCompact(cp1: TControlPoint): string;
var
i: integer;
begin
Result := ' <palette count="256" format="RGB">';
for i := 0 to 255 do begin
if ((i and 7) = 0) then Result := Result + #13#10 + ' ';
Result := Result + IntToHex(cp1.cmap[i, 0],2)
+ IntToHex(cp1.cmap[i, 1],2)
+ IntToHex(cp1.cmap[i, 2],2);
end;
Result := Result + #13#10 + ' </palette>';
end;
function ColorToXml(cp1: TControlPoint): string;
var
i: integer;
begin
Result := '';
for i := 0 to 255 do begin
Result := Result + ' <color index="' + IntToStr(i) +
'" rgb="' + IntToStr(cp1.cmap[i, 0]) + ' ' +
IntToStr(cp1.cmap[i, 1]) + ' ' +
IntToStr(cp1.cmap[i, 2]) + '"/>' + #13#10;
end;
end;
//************ AV: working with embedded PNG-parameters ***********************//
procedure TMainForm.ImportFromPNGClick(Sender: TObject);
begin
OpenDialog.Title := TextByKey('common-open-apoimage');
OpenDialog.Filter := TextByKey('common-filter-png') + ' |*.png';
OpenDialog.InitialDir := ParamFolder;
OpenDialog.FileName := '';
if OpenDialog.Execute then
ImportThumbnailPNG(OpenDialog.FileName);
end;
procedure TMainForm.ImportThumbnailPNG(FileName: string);
var
flameXML: string;
begin
flameXML := MainForm.LoadXMLFlameTextPNG(FileName);
if flameXML <> '' then
begin
try
PasteFlameXML(flameXML);
except
Application.MessageBox(PChar(Format(TextByKey('common-openpngerror1'),
[ExtractFileName(FileName)])), ApophysisSVN, MB_ICONWARNING or MB_OK);
end;
end;
end;
function TMainForm.LoadXMLFlameTextPNG(FileName: string): string;
var
PngObject: TPNGObject;
ChunkList: TPngList;
TextChunk: TChunkTEXT;
flameXML: string;
begin
Result := '';
PngObject := TPngObject.Create;
try
PngObject.LoadFromFile(FileName);
ChunkList := PngObject.Chunks;
if ChunkList <> nil then
begin
TextChunk := ChunkList.FindChunk(TChunkTEXT) as TChunkTEXT;
// iterate through text chunks until 'ApoFlame' keyword is found
while TextChunk <> nil do
begin
if TextChunk.Keyword = 'ApoFlame' then
begin
flameXML := string(TextChunk.Text);
if FindFlameXML(flameXML, '') > 0 then
begin
PngObject.Free; // AV: free the memory if search is succeed
Exit(flameXML); // AV: XML-flame is found
end;
break;
end else
ChunkList.RemoveChunk(TextChunk); // AV: text is not an XML-flame
end;
// AV: XML-parameters are not found
Application.MessageBox(PChar(Format(TextByKey('common-openpngerror2'),
[ExtractFileName(FileName)])), ApophysisSVN, MB_ICONWARNING or MB_OK);
end;
except // AV: error in reading parameters
Application.MessageBox(PChar(Format(TextByKey('common-openpngerror3'),
[ExtractFileName(FileName)])), ApophysisSVN, MB_ICONWARNING or MB_OK);
end;
PngObject.Free; // AV: free the memory if search is failed
end;
//*************************************************************************//
(*
function GetThumbnailBase64(const cp1: TControlPoint) : string;
var
st: TMemoryStream;
tempcp : TControlPoint;
render : TRenderer;
buffer : array of byte;
base64 : string;
size : integer;
bmp : TJPegImage;
w, h, r: double;
begin
w := cp1.Width;
h := cp1.Height;
r := w / h;
if (w < h) then
begin
w := r * ThumbnailSize;
h := ThumbnailSize;
end else if (w > h) then
begin
h := ThumbnailSize / r;
w := ThumbnailSize;
end else
begin
w := ThumbnailSize;
h := ThumbnailSize;
end;
render := TRenderer.Create;
tempcp := TControlPoint.create;
tempcp.Copy(cp1);
tempcp.AdjustScale(round(w), round(h));
// tempcp.Width := round(w);
// tempcp.Height := round(h);
tempcp.spatial_oversample := defOversample;
tempcp.spatial_filter_radius := defFilterRadius;
tempcp.sample_density := 10;
render.SetCP(tempcp);
render.Render;
st := TMemoryStream.Create;
bmp := TJpegImage.Create;
bmp.Assign(render.GetImage);
bmp.SaveToStream(st);
size := st.Size;
SetLength(buffer, size);
st.Seek(0, soBeginning);
st.ReadBuffer(buffer[0], length(buffer));
base64 := B64Encode(TBinArray(buffer), length(buffer));
tempcp.Free;
render.Free;
st.Free;
bmp.Free;
result := base64;
end;
*)
// AV: added default parameter values to get rid of duplicated code
function FlameToXML(const cp1: TControlPoint; exporting: boolean = false; title: string = ''): string;
var
t, i: integer;
FileList: TStringList;
x, y: double;
parameters: string;
curves, str, cpName: string;
begin
FileList := TStringList.create;
x := cp1.center[0];
y := cp1.center[1];
if title = '' then // AV
cpName := CleanXMLName(cp1.name)
else
cpName := CleanXMLName(title);
try
parameters := 'version="' + AppVersionString + '" ';
if cp1.time <> 0 then
parameters := parameters + format('time="%g" ', [cp1.time]);
parameters := parameters +
'size="' + IntToStr(cp1.width) + ' ' + IntToStr(cp1.height) +
format('" center="%g %g" ', [x, y]) +
format('scale="%g" ', [cp1.pixels_per_unit]);
if cp1.FAngle <> 0 then
parameters := parameters + format('angle="%g" ', [cp1.FAngle]) + // !?!?!?
format('rotate="%g" ', [-180 * cp1.FAngle/Pi]);
if cp1.zoom <> 0 then
parameters := parameters + format('zoom="%g" ', [cp1.zoom]);
// 3d
if cp1.cameraPitch <> 0 then
parameters := parameters + format('cam_pitch="%g" ', [cp1.cameraPitch]);
if cp1.cameraYaw <> 0 then
parameters := parameters + format('cam_yaw="%g" ', [cp1.cameraYaw]);
if cp1.cameraRoll <> 0 then // AV
parameters := parameters + format('cam_roll="%g" ', [cp1.cameraRoll]);
if cp1.cameraPersp <> 0 then
parameters := parameters + format('cam_perspective="%g" ', [cp1.cameraPersp]);
if cp1.cameraZpos <> 0 then
parameters := parameters + format('cam_zpos="%g" ', [cp1.cameraZpos]);
if cp1.cameraDOF <> 0 then
parameters := parameters + format('cam_dof="%g" ', [cp1.cameraDOF]);
//
parameters := parameters + format(
'oversample="%d" filter="%g" quality="%g" ',
[cp1.spatial_oversample,
cp1.spatial_filter_radius,
cp1.sample_density]
);
if cp1.nbatches <> 1 then parameters := parameters + 'batches="' + IntToStr(cp1.nbatches) + '" ';
if cp1.hue_rotation <> 1 then parameters := parameters + format('hue="%g" ', [cp1.hue_rotation]); // AV
parameters := parameters +
format('background="%g %g %g" ', [cp1.background[0] / 255, cp1.background[1] / 255, cp1.background[2] / 255]) +
format('brightness="%g" ', [cp1.brightness]) +
format('gamma="%g" ', [cp1.gamma]);
if cp1.contrast <> 1 then // AV
parameters := parameters + format('contrast="%g" ', [cp1.contrast]);
if cp1.vibrancy <> 1 then
parameters := parameters + format('vibrancy="%g" ', [cp1.vibrancy]);
if cp1.gamma_threshold <> 0 then
parameters := parameters + format('gamma_threshold="%g" ', [cp1.gamma_threshold]);
if cp1.soloXform >= 0 then
parameters := parameters + format('soloxform="%d" ', [cp1.soloXform]);
parameters := parameters +
format('estimator_radius="%g" ', [cp1.estimator]) +
format('estimator_minimum="%g" ', [cp1.estimator_min]) +
format('estimator_curve="%g" ', [cp1.estimator_curve]);
if exporting then parameters := parameters +
format('temporal_samples="%d" ', [cp1.jitters]);
if (cp1.enable_de) then
parameters := parameters + ('enable_de="1" ')
else parameters := parameters + ('enable_de="0" ');
str := '';
for i := 0 to cp1.used_plugins.Count-1 do begin
str := str + cp1.used_plugins[i];
if (i = cp1.used_plugins.Count-1) then break;
str := str + ' ';
end;
parameters := parameters + format('plugins="%s" new_linear="1" ', [str]);
for i := 0 to 3 do
begin
curves := curves + FloatToStr(cp1.curvePoints[i][0].x) + ' ';
curves := curves + FloatToStr(cp1.curvePoints[i][0].y) + ' ';
curves := curves + FloatToStr(cp1.curveWeights[i][0]) + ' ';
curves := curves + FloatToStr(cp1.curvePoints[i][1].x) + ' ';
curves := curves + FloatToStr(cp1.curvePoints[i][1].y) + ' ';
curves := curves + FloatToStr(cp1.curveWeights[i][1]) + ' ';
curves := curves + FloatToStr(cp1.curvePoints[i][2].x) + ' ';
curves := curves + FloatToStr(cp1.curvePoints[i][2].y) + ' ';
curves := curves + FloatToStr(cp1.curveWeights[i][2]) + ' ';
curves := curves + FloatToStr(cp1.curvePoints[i][3].x) + ' ';
curves := curves + FloatToStr(cp1.curvePoints[i][3].y) + ' ';
curves := curves + FloatToStr(cp1.curveWeights[i][3]) + ' ';
end;
curves := trim(curves);
parameters := parameters + format('curves="%s" ', [curves]);
FileList.Add('<flame name="' + cpName + '" ' + parameters + '>');
if cp1.comment <> '' then FileList.Add('<!--' + cp1.comment + '-->'); // AV
{ Write transform parameters }
t := cp1.NumXForms;
for i := 0 to t - 1 do
FileList.Add(cp1.xform[i].ToXMLString);
if cp1.HasFinalXForm then
begin
// 'enabled' flag disabled in this release
FileList.Add(cp1.xform[t].FinalToXMLString(cp1.finalXformEnabled));
end;
(*
// AV: too bugged... and not extremely useful thing
if (embedthumb and EmbedThumbnails) then begin
xdata := GetThumbnailBase64(cp1);
buf := '';
for i := 1 to length(xdata) do begin
buf := buf + xdata[i];
if (length(buf) = 150) then begin
FileList.Add(' <xdata content="' + buf + '" />');
buf := '';
end;
end;
if (Length(buf) > 0) then FileList.Add(' <xdata content="' + buf + '" />');
end;
*)
{ Write palette data }
if exporting or OldPaletteFormat then
FileList.Add(ColorToXml(cp1))
else
FileList.Add(ColorToXmlCompact(cp1));
FileList.Add('</flame>');
Result := FileList.text;
finally
FileList.Free;
end;
end;
function RemoveExt(filename: string): string;
var
ext: string;
p: integer;
begin
filename := ExtractFileName(filename);
ext := ExtractFileExt(filename);
p := Pos(ext, filename);
Result := Copy(filename, 1, p - 1); // AV: 1 <-- 0
end;
function XMLEntryExists(title, filename: string): boolean;
var
FileList: TStringList;
begin
Result := false;
if FileExists(filename) then
begin
FileList := TStringList.Create;
try
FileList.LoadFromFile(filename);
if pos('<flame name="' + title + '"', FileList.Text) <> 0 then Result := true;
finally
FileList.Free;
end
end else
Result := false;
end;
procedure DeleteXMLEntry(title, filename: string);
var
Strings: TStringList;
p, i: integer;
begin
Strings := TStringList.Create;
try
i := 0;
Strings.LoadFromFile(FileName);
{ AV: fixed a bug with data corruption when the name of file or transform
is the same as the flame name! Was: 'name="'}
while Pos('<flame name="' + title + '"', Trim(Strings[i])) = 0 do // AV
inc(i);
p := 0;
while p = 0 do
begin
p := Pos('</flame>', Strings[i]);
Strings.Delete(i);
end;
Strings.SaveToFile(FileName);
finally
Strings.Free;
end;
end;
function TMainForm.SaveXMLFlame(const cp1: TControlPoint; title, filename: string): boolean;
{ Saves Flame parameters to end of file }
var
Tag: string;
FileList: TStringList;
i, p: integer;
bakname: string;
begin
Tag := RemoveExt(filename);
Result := True;
try
if FileExists(filename) then
begin
bakname := ChangeFileExt(filename, '.bak');
if FileExists(bakname) then DeleteFile(bakname);
RenameFile(filename, bakname);
FileList := TStringList.create;
try
FileList.LoadFromFile(bakname);
if Pos('<flame name="' + title + '"', FileList.Text) <> 0 then
begin
i := 0;
while Pos('<flame name="' + title + '"', Trim(FileList[i])) = 0 do
inc(i);
p := 0;
while p = 0 do
begin
p := Pos('</flame>', FileList[i]);
FileList.Delete(i);
end;
end;
// fix first line
if (FileList.Count > 0) then begin
//FileList[0] := '<flames name="' + Tag + '">';
// AV: fix fixed :-) Apo 2.09 uses capital F in this tag
if (pos('<flames name', LowerCase(FileList[0])) <> 0) then
FileList[0] := '<flames name="' + Tag + '">'
else // single-flame support
FileList.Insert(0, '<flames name="' + Tag + '">');
end
else // AV: if the existing file is empty
FileList.Add('<flames name="' + Tag + '">'); // AV
if FileList.Count > 2 then
begin
// AV fix last line :-)
if (pos('</flames>', FileList[FileList.Count - 1]) = 0) then
FileList.Add('</flames>');
if pos('<flame ', FileList.text) <> 0 then
repeat
FileList.Delete(FileList.Count - 1);
until (Pos('</flame>', FileList[FileList.count - 1]) <> 0)
else
repeat // AV: now condition will be true anyway
FileList.Delete(FileList.Count - 1);
until (Pos('</flames>', FileList[FileList.count - 1]) <> 0) or
(Pos('<' + Tag + '>', FileList[FileList.count - 1]) <> 0);
end else if (FileList.count > 1) then // AV
FileList.Delete(FileList.Count - 1);
FileList.Add(Trim(FlameToXML(cp1, false, title)));
FileList.Add('</flames>');
FileList.SaveToFile(filename);
finally
if FileExists(bakname) and not FileExists(filename) then
RenameFile(bakname, filename);
FileList.Free;
end;
end
else
begin
// New file ... easy
FileList := TStringList.Create;
FileList.Add('<flames name="' + Tag + '">');
FileList.Add(FlameToXML(cp1, false, title));
FileList.Add('</flames>');
FileList.SaveToFile(filename, TEncoding.UTF8);
FileList.Free;
end;
except // AV: fixed multi-updating of the flame
Result := False; // AV: first assign the value, then exit
raise Exception.CreateFmt(TextByKey('common-genericsavefailure'), [FileName]);
end;
end;
function TMainForm.SaveGradient(Gradient, Title, FileName: string): boolean;
{ Saves gradient parameters to end of file }
var
IFile: TextFile;
begin
Result := True;
try
AssignFile(IFile, FileName);
if FileExists(FileName) then
begin
if EntryExists(Title, FileName) then DeleteEntry(Title, FileName);
Append(IFile);
end
else
ReWrite(IFile);
Write(IFile, Gradient);
WriteLn(IFile, ' ');
CloseFile(IFile);
except on EInOutError do
begin
Result := False;
raise Exception.CreateFmt(TextByKey('common-genericsavefailure'), [FileName]); // AV
end;
end;
end;
function RenameIFS(OldIdent: string; var NewIdent: string): boolean;
{ Renames an IFS parameter set in a file }
var
Strings: TStringList;
p, i: integer;
s: string;
begin
Result := True;
NewIdent := CleanIdentifier(NewIdent);
Strings := TStringList.Create;
try
try
i := 0;
Strings.LoadFromFile(OpenFile);
if Pos(OldIdent + ' ', Trim(Strings.Text)) <> 0 then
begin
while Pos(OldIdent + ' ', Trim(Strings[i])) <> 1 do
begin
inc(i);
end;
p := Pos('{', Strings[i]);
s := Copy(Strings[i], p, Length(Strings[i]) - p + 1);
Strings[i] := NewIdent + ' ' + s;
Strings.SaveToFile(OpenFile);
end
else
Result := False;
except on Exception do Result := False;
end;
finally
Strings.Free;
end;
end;
function RenameXML(OldIdent: string; var NewIdent: string): boolean;
{ Renames an XML parameter set in a file }
var
Strings: TStringList;
i: integer;
bakname: string;
begin
Result := True;
Strings := TStringList.Create;
try
try
i := 0;
Strings.LoadFromFile(OpenFile);
if Pos('name="' + OldIdent + '"', Strings.Text) <> 0 then
begin
while Pos('name="' + OldIdent + '"', Strings[i]) = 0 do
begin
inc(i);
end;
Strings[i] := StringReplace(Strings[i], OldIdent, NewIdent, []);
bakname := ChangeFileExt(OpenFile, '.bak');
if FileExists(bakname) then DeleteFile(bakname);
RenameFile(OpenFile, bakname);
Strings.SaveToFile(OpenFile);
end
else
Result := False;
except on Exception do
Result := False;
end;
finally
Strings.Free;
end;
end;
procedure ListIFS(FileName: string; sel: integer);
{ AV: List identifiers in Undo file }
var
i, p: integer;
Title: string;
ListItem: TListItem;
FStrings: TStringList;
begin
MainForm.ParseLoadingBatch := true;
FStrings := TStringList.Create;
FStrings.LoadFromFile(FileName);
try
MainForm.ListView1.Items.BeginUpdate;
MainForm.ListView1.Items.Clear;
if (Pos('{', FStrings.Text) <> 0) then
begin
for i := 0 to FStrings.Count - 1 do
begin
p := Pos('{', FStrings[i]);
// AV: why do we use 2-nd condition? A rudiment from 3D-hack?
if (p <> 0) {and (Pos('(3D)', FStrings[i]) = 0)} then
begin
Title := Trim(Copy(FStrings[i], 1, p - 1));
if Title <> '' then
begin { Otherwise bad format }
if ((i mod 5) = 0) then
MainForm.LoadSaveProgress.Position :=
round(100 * i / FStrings.Count); // AV
ListItem := MainForm.ListView1.Items.Add;
ListItem.Caption := Title;
// AV: hack - remember the creation order in an unused field
ListItem.OverlayIndex := MainForm.ListView1.Items.Count;
end;
end;
end;
end;
MainForm.LoadSaveProgress.Position := 0; // AV
if ClassicListMode then // AV: thumbs are useless
GeneratingThumbs := False
else // AV: added thumbnails support for Undo list
MainForm.RunThumbnailThread;
with MainForm.ListView1 do
if Items.Count > 0 then // AV
case sel of
0: Selected := Items[Items.Count - 1];
1: Selected := Items[0];
end;
finally
MainForm.ListView1.Items.EndUpdate;
FStrings.Free;
end;
MainForm.ParseLoadingBatch := false; // AV
if AnimateForm.Visible then AnimateForm.UpdateControls; // AV
end;
(*
procedure ListFlames(FileName: string; sel: integer);
{ List identifiers in file }
var
i, p: integer;
Title: string;
ListItem: TListItem;
FStrings: TStringList;
begin
FStrings := TStringList.Create;
FStrings.LoadFromFile(FileName);
try
MainForm.ListView1.Items.BeginUpdate;
MainForm.ListView1.Items.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 { Otherwise bad format }
ListItem := MainForm.ListView1.Items.Add;
Listitem.Caption := Trim(Copy(FStrings[i], 1, p - 1));
end;
end;
end;
end;
MainForm.ListView1.Items.EndUpdate;
if sel = 1 then MainForm.ListView1.Selected := MainForm.ListView1.Items[0];
finally
FStrings.Free;
end;
end;
*)
{ ****************************** Display ************************************ }
procedure Trace1(const str: string);
begin
if TraceLevel >= 1 then
TraceForm.MainTrace.Lines.Add('. ' + str);
end;
procedure Trace2(const str: string);
begin
if TraceLevel >= 2 then
TraceForm.MainTrace.Lines.Add('. . ' + str);
end;
procedure TMainForm.HandleThreadCompletion(var Message: TMessage);
var
oldscale: double;
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;
Image.Cursor := crDefault;
if assigned(FViewImage) then begin
oldscale := FViewImage.Width / Image.Width;
FViewImage.Free;
end
else oldscale := FViewScale;
FViewImage := Renderer.GetTransparentImage;
if (FViewImage <> nil) and (FViewImage.Width > 0) then begin
FViewScale := FViewImage.Width / Image.Width;
FViewPos.X := FViewScale/oldscale * (FViewPos.X - FViewOldPos.X);
FViewPos.Y := FViewScale/oldscale * (FViewPos.Y - FViewOldPos.Y);
DrawImageView;
{
case FMouseMoveState of
msZoomWindowMove: FMouseMoveState := msZoomWindow;
msZoomOutWindowMove: FMouseMoveState := msZoomOutWindow;
// msDragMove: FMouseMoveState := msDrag;
msRotateMove: FMouseMoveState := msRotate;
end;
}
if FMouseMoveState in [msZoomWindowMove, msZoomOutWindowMove, msRotateMove] then
DrawSelection := false;
Trace1(TimeToStr(Now) + ' : Render complete');
Renderer.ShowSmallStats;
end
else Trace2('WARNING: No image rendered!');
Renderer.WaitFor;
Trace2('Destroying RenderThread #' + IntToStr(Renderer.ThreadID));
Renderer.Free;
Renderer := nil;
Trace1('');
end;
procedure TMainForm.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;
Image.Cursor := crDefault;
Trace2(' Render aborted');
Trace2('Destroying RenderThread #' + IntToStr(Renderer.ThreadID));
Renderer.Free;
Renderer := nil;
Trace1('');
end;
(*
procedure TMainForm.DrawPreview;
var
cp : TControlPoint;
Render : TRenderer;
BM: TBitmap;
begin
Render := TRenderer.Create;
bm := TBitmap.Create;
cp := MainCP.Clone;
cp.sample_density := 1;
cp.spatial_oversample := 1;
cp.spatial_filter_radius := 1;
//Render.NrThreads := NrTreads;
Render.SetCP(cp);
Render.Render;
BM.Assign(Render.GetImage);
Image.Picture.Graphic := bm;
end;
*)
procedure TMainForm.DrawFlame;
const
{$ifdef CPUX86}
bs = 16;
{$else}
bs = 32;
{$endif}
var
GlobalMemoryInfo: TMemoryStatus; // holds the global memory status information
RenderCP: TControlPoint;
Mem, ApproxMem: cardinal;
begin
RedrawTimer.Enabled := False;
if Assigned(Renderer) then begin
assert(Renderer.Suspended = false);
Trace2('Killing previous RenderThread #' + inttostr(Renderer.ThreadID));
Renderer.Terminate;
Renderer.WaitFor;
Trace2('Destroying RenderThread #' + IntToStr(Renderer.ThreadID));
Renderer.Free;
Renderer := nil;
end;
if not Assigned(Renderer) then
begin
if EditForm.Visible and ((MainCP.Width / MainCP.Height) <> (EditForm.cp.Width / EditForm.cp.Height))
then EditForm.UpdateDisplay(true); // preview only?
if AdjustForm.Visible then AdjustForm.UpdateDisplay(true); // preview only!
RenderCP := MainCP.Clone;
RenderCp.AdjustScale(Image.width, Image.height);
RenderCP.sample_density := defSampleDensity;
// oversample and filter are just slowing us down here...
RenderCP.spatial_oversample := 1; // defOversample;
RenderCP.spatial_filter_radius := 0.001; {?} //defFilterRadius;
RenderCP.Transparency := true; // always generate transparency here
GlobalMemoryInfo.dwLength := SizeOf(GlobalMemoryInfo);
GlobalMemoryStatus(GlobalMemoryInfo);
Mem := GlobalMemoryInfo.dwAvailPhys;
// if Output.Lines.Count >= 1000 then Output.Lines.Clear;
Trace1('--- Previewing "' + RenderCP.name + '" ---');
Trace1(Format(' Available memory: %f Mb', [Mem / (1024*1024)]));
ApproxMem := int64(RenderCp.Width) * int64(RenderCp.Height) {* sqr(Oversample)}
* (bs + 4 + 4); // +4 for temp image(s)...?
assert(MainPreviewScale <> 0);
if ApproxMem * sqr(MainPreviewScale) < Mem then begin
if ExtendMainPreview then begin
RenderCP.sample_density := RenderCP.sample_density / sqr(MainPreviewScale);
RenderCP.Width := round(RenderCp.Width * MainPreviewScale);
RenderCP.Height := round(RenderCp.Height * MainPreviewScale);
end;
end
else Trace1('WARNING: Not enough memory for extended preview!');
if ApproxMem > Mem then
Trace1('OUTRAGEOUS: Not enough memory even for normal preview! :-(');
Trace1(Format(' Size: %dx%d, Quality: %f',
[RenderCP.Width, RenderCP.Height, RenderCP.sample_density]));
FViewOldPos.x := FViewPos.x;
FViewOldPos.y := FViewPos.y;
StartTime := Now;
try
Renderer := TRenderThread.Create;
Renderer.TargetHandle := MainForm.Handle;
if TraceLevel > 0 then Renderer.Output := TraceForm.MainTrace.Lines;
Renderer.OnProgress := OnProgress;
Renderer.SetCP(RenderCP);
// Renderer.NrThreads := NrTreads;
Trace2('Starting RenderThread #' + inttostr(Renderer.ThreadID));
Renderer.Resume;
Image.Cursor := crAppStart;
except
Trace1('ERROR: Cannot start renderer!');
end;
RenderCP.Free;
end;
end;
procedure TMainForm.RandomBatch;
{ Write a series of random flames to a file }
var
i: integer;
F: TextFile;
b, RandFile: string;
begin
b := IntToStr(BatchSize);
inc(MainSeed);
RandSeed := MainSeed;
RandFile := AppPath + randFilename;
try
AssignFile(F, RandFile);
OpenFile := RandFile;
ReWrite(F);
WriteLn(F, '<flames name="Random Batch">'); // AV: fixed '<random_batch>');
for i := 0 to BatchSize - 1 do
begin
inc(RandomIndex);
Statusbar.SimpleText := Format(TextByKey('main-status-batchgenerate'), [(i+1), b]);
RandSeed := MainSeed;
if randGradient = 0 then cmap_index := random(NRCMAPS);
inc(MainSeed);
RandSeed := MainSeed;
RandomizeCP(MainCp);
MainCp.CalcBoundbox;
MainCp.name := RandomPrefix + RandomDate + '-' + IntToStr(RandomIndex);
Write(F, FlameToXML(MainCp));
end;
Write(F, '</flames>'); // AV: fixed '</random_batch>');
CloseFile(F);
except
on EInOutError do
Application.MessageBox(PChar(TextByKey('main-status-batcherror')),
ApophysisSVN, 16);
end;
MainCp.name := '';
end;
{ ******************************** Menu ************************************ }
function LoadXMLFlameText(filename, name: string) : string;
var
i, p: integer;
FileStrings: TStringList;
ParamStrings: TStringList;
Tokens: TStringList;
time: integer;
begin
time := -1;
FileStrings := TStringList.Create;
ParamStrings := TStringList.Create;
Result := '';
if pos('*untitled', name) <> 0 then
begin
Tokens := TStringList.Create;
GetTokens(name, tokens);
time := StrToInt(tokens[1]);
Tokens.free;
end;
try
{ if UpperCase(ExtractFileExt(filename)) = '.PNG' then
FileStrings.Text := MainForm.LoadXMLFlameTextPNG(filename)
else }
FileStrings.LoadFromFile(filename);
for i := 0 to FileStrings.Count - 1 do
begin
pname := '';
ptime := '';
p := Pos('<flame ', LowerCase(FileStrings[i]));
if (p <> 0) then
begin
MainForm.ListXMLScanner.LoadFromBuffer(PAnsiChar(Utf8String(FileStrings[i])));
MainForm.ListXMLScanner.Execute;
if pname <> '' then
begin
if (Trim(pname) = Trim(name)) then
begin
ParamStrings.Add(FileStrings[i]);
Break;
end;
end
else
begin
if ptime <> '' then
begin
if StrToInt(ptime) = time then
begin
ParamStrings.Add(FileStrings[i]);
Break;
end;
end;
end;
end;
end;
repeat
inc(i);
ParamStrings.Add(FileStrings[i]);
until pos('</flame>', Lowercase(FileStrings[i])) <> 0;
Result := ParamStrings.Text;
finally
FileStrings.free;
ParamStrings.free;
end;
end;
function ScanVariations(name:string):boolean;
var
i,count: integer;
vname: string;
begin
count := NrVar;
for i:=0 to count - 1 do
begin
vname := VarNames(i);
if (vname = name) then
begin
Result := true;
exit;
end;
end;
for i := 0 to MainForm.SubstSource.Count - 1 do
begin
vname := MainForm.SubstSource[i];
if (vname = name) then
begin
Result := true;
exit;
end;
end;
Result := false;
end;
function ScanVariables(name:string):boolean;
var
i, count: integer;
begin
count := GetNrVariableNames;
for i :=0 to count - 1 do
begin
if (GetVariableNameAt(i) = name) then
begin
Result := true;
exit;
end;
end;
for i := 0 to MainForm.SubstSource.Count - 1 do
begin
if (MainForm.SubstSource[i] = name) then
begin
Result := true;
exit;
end;
end;
Result := false;
end;
procedure TMainForm.mnuOpenClick(Sender: TObject);
var
fn: string;
begin
StopScripter; // AV
OpenDialog.Filter := TextByKey('common-filter-flamefiles') + '|*.flame;*.xml|'
+ TextByKey('common-filter-templatefiles') + ' |*.template;*.temp|'
+ TextByKey('common-filter-undofiles') + '|*.undo;*.apo|'
+ TextByKey('common-filter-allfiles') + '|*.*';
OpenDialog.InitialDir := ParamFolder;
OpenDialog.FileName := '';
OpenDialog.Title := ''; // AV
// AV: turn back classic dialog since OpenSaveFileDialog looks ugly then themed
if OpenDialog.Execute then
begin
fn := OpenDialog.FileName; // AV
LastOpenFile := fn;
Maincp.name := '';
ParamFolder := ExtractFilePath(fn);
OpenFile := fn;
if APP_BUILD = '' then
MainForm.Caption := AppVersionString + ' - ' + OpenFile
else
MainForm.Caption := AppVersionString + ' ' + APP_BUILD + ' - ' + OpenFile;
fn := UpperCase(ExtractFileExt(fn));
if (fn = '.UNDO') or (fn = '.APO') then
begin
OpenFileType := ftApo; // AV
ListIFS(OpenDialog.FileName, 1);
end
else begin
OpenFileType := ftXML;
ListXML(OpenDialog.FileName, 1);
end;
end;
end;
(*
procedure TMainForm.mnuNextClick(Sender: TObject);
begin
with ListView1 do
if Items.Count <> 0 then
Selected := Items[(Selected.Index + 1) mod Items.Count];
end;
procedure TMainForm.mnuPreviousClick(Sender: TObject);
var
i: integer;
begin
with ListView1 do
if Items.Count <> 0 then
begin
i := Selected.Index - 1;
if i < 0 then i := Items.Count - 1;
Selected := Items[i];
end;
end;
*)
procedure TMainForm.mnuListRenameClick(Sender: TObject);
begin
if ListView1.Selected <> nil then
ListView1.Items[ListView1.Selected.Index].EditCaption;
end;
procedure TMainForm.mnuCopyUPRClick(Sender: TObject);
begin
Clipboard.SetTextBuf(PChar(UPRString(MainCp, Maincp.name)));
end;
procedure TMainForm.mnuItemDeleteClick(Sender: TObject);
var
c: boolean;
begin
if ListView1.SelCount <> 0 then
begin
if ConfirmDelete then
begin
if (UndoIndex <> 0) then // AV: if the flame is not saved in the list
c := Application.MessageBox(
PChar(Format(TextByKey('common-confirmdelete'), [ListView1.Selected.Caption])
+ #32 + TextByKey('common-deletecurrent')), ApophysisSVN, 36) = IDYES
else
c := Application.MessageBox(
PChar(Format(TextByKey('common-confirmdelete'), [ListView1.Selected.Caption])),
ApophysisSVN, 36) = IDYES
end
else
c := True;
if c then
if ListView1.Focused and (ListView1.SelCount <> 0) then
begin
Application.ProcessMessages;
if OpenFileType = ftXML then
DeleteXMLEntry(ListView1.Selected.Caption, OpenFile)
else
DeleteEntry(ListView1.Selected.Caption, OpenFile);
{
// AV: do not change the sequence in order to display all icons properly
if (ListView1.Selected.Index >= 0) and (ListView1.Selected.Index < UsedThumbnails.Count)
and (not ClassicListMode) then
UsedThumbnails.Delete(ListView1.Selected.Index);
}
ListView1.Items.Delete(ListView1.Selected.Index);
Application.ProcessMessages;
ListView1.Selected := ListView1.ItemFocused;
// AV: re-adjust the displayed numbers...
if EnumerateFlames.Checked then EnumerateFlamesClick(EnumerateFlames);
if AnimateForm.Visible then AnimateForm.UpdateControls; // AV
(*
// AV: I set ListView1.IconOptions.AutoArrange := True;
// for auto-updating the flame list without redrawing the thumbs.
// An alternative (but slow) way to do the same thing:
if ListView1.Items.Count > 0 then // refresh the list
begin
i := ListView1.ItemFocused.Index; // AV
if OpenFileType = ftXML then
UpdateThumbnails // AV
else
ListIFS(OpenFile, 2); // AV: for undo files
// AV: now scroll to the nearest item
i := min(i, ListView1.Items.Count - 1);
ListView1.Selected := ListView1.Items[i];
ListView1.Items[i].MakeVisible(true);
end;
*)
end;
end;
end;
procedure TMainForm.mnuOptionsClick(Sender: TObject);
var isSmallThumb: boolean;
begin
isSmallThumb := UseSmallThumbnails; // AV
// AV: update flame ONLY if settings were changed
if OptionsForm.ShowModal = mrOK then
begin
StopThread; // --Z--
RedrawTimer.Enabled := True;
tbQualityBox.Text := FloatToStr(defSampleDensity);
tbShowAlpha.Down := ShowTransparency;
if (isSmallThumb <> UseSmallThumbnails) then // update the thumbs
begin
SetThumbnailProperties; // AV
UpdateThumbnails; // AV
end;
if EnumerateFlames.Checked then EnumerateFlamesClick(EnumerateFlames); // AV
SetAutoSaveTimer; // AV: to enable autosave without restarting Apophysis
if ConfirmResetUndo then
ListView1.OnSelectItem := ListViewSelectItem
else
ListView1.OnSelectItem := nil;
DrawImageView;
UpdateWindows;
end;
end;
procedure TMainForm.mnuRefreshThumbClick(Sender: TObject);
begin
if (ListView1.Selected = nil) or ParseLoadingBatch then exit;
RefreshThumbnail; // current only
end;
procedure TMainForm.mnuNormalWeightsClick(Sender: TObject);
begin
StopThread;
UpdateUndo;
// TODO: ...something <-- AV: something's done :)
MainCp.NormalizeProbabilities;
RedrawTimer.Enabled := True;
UpdateWindows;
end;
procedure TMainForm.mnuRWeightsClick(Sender: TObject);
var i: smallint;
begin
StopThread;
UpdateUndo;
inc(MainSeed);
RandSeed := MainSeed;
for i := 0 to Transforms - 1 do
maincp.xform[i].density := random;
RedrawTimer.Enabled := True;
UpdateWindows;
end;
procedure TMainForm.mnuRandomBatchClick(Sender: TObject);
begin
// StopScripter;
inc(MainSeed);
RandSeed := MainSeed;
RandomBatch;
OpenFile := AppPath + randFilename;
OpenFileType := ftXML;
MainForm.Caption := AppVersionString + ' - ' + TextByKey('main-common-randombatch');
ListXML(OpenFile, 1);
ListView1.SetFocus; // AV
if batchsize = 1 then DrawFlame;
end;
function GradientString(c: TColorMap): string;
var
strings: TStringList;
i, j, cl: integer;
begin
strings := TStringList.Create;
for i := 0 to 255 do
begin
j := round(i * (399 / 255));
cl := (c[i][2] shl 16) + (c[i][1] shl 8) + (c[i][0]);
strings.Add(' index=' + IntToStr(j) + ' color=' + intToStr(cl));
end;
Result := Strings.Text;
strings.Free;
end;
function TMainForm.UPRString(cp1: TControlPoint; Entry: string): string;
{ Returns a string containing an Ultra Fractal parameter set for copying
or saving to file }
var
IterDensity, m, i, j: integer;
scale, a, b, c, d, e, f, p, v: double;
GradStrings, Strings: TStringList;
rept, cby, smap, sol: string;
uprcenter: array[0..1] of double; // camera center
Backcolor: longint;
xf_str: string;
begin
cp1.Prepare;
uprcenter[0] := cp1.Center[0];
uprcenter[1] := cp1.Center[1];
cp1.Width := UPRWidth;
cp1.Height := UPRHeight;
scale := power(2, cp1.zoom) * CalcUPRMagn(cp1);
cp1.center[0] := uprCenter[0];
cp1.center[1] := uprCenter[1];
smap := 'no';
sol := 'no';
rept := '';
cby := 'Hit Frequency';
Strings := TStringList.Create;
GradStrings := TStringList.Create;
try
Strings.Add(CleanIdentifier(Entry) + ' {');
Strings.Add('fractal:');
Strings.Add(' title="' + CleanUPRTitle(Entry) +
'" width=' + IntToStr(UPRWidth) + ' height=' + IntToStr(UPRHeight) + ' layers=1');
Strings.Add('layer:');
Strings.Add(' method=linear caption="Background" opacity=100 mergemode=normal');
Strings.Add('mapping:');
Strings.Add(' center=' + floatToStr(cp1.center[0]) + '/' + floatToStr(-cp1.center[1]) +
' magn=' + FloatToStr(scale));
Strings.Add('formula:');
Strings.Add(' maxiter=1 filename="' + UPRFormulaFile + '" entry="' + UPRFormulaIdent + '"');
Strings.Add('inside:');
Strings.Add(' transfer=none');
Strings.Add('outside:');
Strings.Add(' transfer=linear repeat=no ' + 'filename="' + UPRColoringFile + '" entry="'
+ UPRColoringIdent + '"');
if (UPRAdjustDensity) and (scale > 1) then
IterDensity := Trunc(UPRSampleDensity * scale * scale)
else
IterDensity := UPRSampleDensity;
Strings.Add(' p_iter_density=' + IntToStr(IterDensity) + ' p_spat_filt_rad=' +
Format('%.3g', [UPRFilterRadius]) + ' p_oversample=' + IntToStr(UPROversample));
backcolor := 255 shl 24 + cp1.background[0] shl 16 + cp1.background[1] shl 8 + cp1.background[2];
Strings.Add(' p_bk_color=' + IntToStr(Backcolor) + ' p_contrast=' + FloatToStr(cp1.Contrast) +
' p_brightness=' + FloatToStr(cp1.Brightness) + ' p_gamma=' + FloatToStr(cp1.Gamma));
Strings.Add(' p_white_level=200 p_xforms=' + inttostr(Transforms));
for m := 0 to Transforms do
begin
a := cp1.xform[m].c[0][0];
c := cp1.xform[m].c[0][1];
b := cp1.xform[m].c[1][0];
d := cp1.xform[m].c[1][1];
e := cp1.xform[m].c[2][0];
f := cp1.xform[m].c[2][1];
p := cp1.xform[m].Density;
if m < Transforms then xf_str := 'p_xf' + inttostr(m)
else begin
if cp1.HasFinalXForm = false then break;
xf_str := 'p_finalxf';
end;
Strings.Add(' ' + xf_str + '_p=' + Format('%.6g ', [p]));
Strings.Add(' ' + xf_str + '_c=' + floatTostr(cp1.xform[m].color));
Strings.Add(' ' + xf_str + '_sym=' + floatTostr(cp1.xform[m].symmetry));
Strings.Add(' ' + xf_str + '_cfa=' + Format('%.6g ', [a]) +
xf_str + '_cfb=' + Format('%.6g ', [b]) +
xf_str + '_cfc=' + Format('%.6g ', [c]) +
xf_str + '_cfd=' + Format('%.6g ', [d]));
Strings.Add(' ' + xf_str + '_cfe=' + Format('%.6g ', [e]) +
' ' + xf_str + '_cff=' + Format('%.6g ', [f]));
for i := 0 to NRVAR-1 do
if cp1.xform[m].GetVariation(i) <> 0 then begin
Strings.Add(' ' + xf_str + '_var_' + VarNames(i) + '=' +
floatToStr(cp1.xform[m].GetVariation(i)));
for j:= 0 to GetNrVariableNames - 1 do begin
{$ifndef VAR_STR}
cp1.xform[m].GetVariable(GetVariableNameAt(j), v);
Strings.Add(' ' + xf_str + '_par_' + GetVariableNameAt(j) + '=' + floatToStr(v));
{$else}
Strings.Add(' ' + xf_str + '_par_' +
GetVariableNameAt(j) + '=' + cp1.xform[m].GetVariableStr(GetVariableNameAt(j)));
{$endif}
end;
end;
end;
Strings.Add('gradient:');
Strings.Add(GradientString(cp1.cmap));
Strings.Add('}');
UPRString := Strings.Text;
finally
GradStrings.Free;
Strings.Free;
end;
end;
procedure TMainForm.mnuRandomClick(Sender: TObject);
begin
StopThread;
UpdateUndo;
inc(MainSeed);
RandomizeCP(MainCp);
inc(RandomIndex);
MainCp.name := RandomPrefix + RandomDate + '-' +
IntToStr(RandomIndex);
Transforms := MainCp.TrianglesFromCP(MainTriangles);
if AdjustForm.visible then AdjustForm.UpdateDisplay;
StatusBar.Panels[3].text := maincp.name;
ResetLocation;
RedrawTimer.Enabled := true;
UpdateWindows;
end;
procedure TMainForm.mnuEqualizeClick(Sender: TObject);
var i: smallint;
begin
StopThread;
UpdateUndo;
RedrawTimer.Enabled := True;
for i := 0 to Transforms - 1 do
maincp.xform[i].density := 0.5;
UpdateWindows;
end;
procedure TMainForm.mnuEditorClick(Sender: TObject);
begin
EditForm.Show;
end;
procedure TMainForm.mnuExitClick(Sender: TObject);
begin
Close;
end;
procedure TMainForm.mnuSaveUPRClick(Sender: TObject);
{ Write a UPR to a file }
begin
SaveForm.SaveType := stExportUPR;
SaveForm.Filename := UPRPath;
SaveForm.Title := maincp.name;
if SaveForm.ShowModal = mrOK then
begin
UPRPath := SaveForm.FileName;
SaveUPR(SaveForm.Title, SaveForm.Filename);
end;
end;
procedure FlameFromUndo(cp: TControlPoint; const FlameName: string; const ParamFile: string);
{ AV: common method for loading internal-formatted flames }
var
FStrings, IFSStrings, EntryStrings, Tokens: TStringList;
SavedPal: Boolean;
i, j: integer;
floatcolor: double;
s: string;
Palette: TColorMap;
begin
SavedPal := false;
FStrings := TStringList.Create;
IFSStrings := TStringList.Create;
Tokens := TStringList.Create;
EntryStrings := TStringList.Create;
try
FStrings.LoadFromFile(ParamFile);
for i := 0 to FStrings.count - 1 do
if Pos(FlameName + ' {', Trim(FStrings[i])) = 1 then
break;
IFSStrings.Add(FStrings[i]);
repeat
inc(i);
IFSStrings.Add(FStrings[i]);
until Pos('}', FStrings[i]) <> 0;
for i := 0 to FStrings.count - 1 do
begin
if Pos(FlameName + ' {', Trim(FStrings[i])) = 1 then
break;
end;
inc(i);
while (Pos('}', FStrings[i]) = 0) and (Pos('palette:', FStrings[i]) = 0) do
begin
EntryStrings.Add(FStrings[i]);
inc(i);
end;
if Pos('palette:', FStrings[i]) = 1 then
begin
SavedPal := True;
inc(i);
for j := 0 to 255 do begin
s := FStrings[i];
GetTokens(s, tokens);
floatcolor := StrToFloat(Tokens[0]);
Palette[j][0] := round(floatcolor);
floatcolor := StrToFloat(Tokens[1]);
Palette[j][1] := round(floatcolor);
floatcolor := StrToFloat(Tokens[2]);
Palette[j][2] := round(floatcolor);
inc(i);
end;
end;
cp.ParseString(EntryStrings.Text);
if SavedPal then cp.cmap := Palette;
cp.name := FlameName;
finally
IFSStrings.Free;
FStrings.Free;
Tokens.free;
EntryStrings.free;
end;
end;
procedure TMainForm.mnuSaveAsClick(Sender: TObject);
{ Save fractal parameters to a file }
var
saved: boolean; // AV
ext: string;
begin
SaveForm.SaveType := stSaveParameters;
SaveForm.Filename := SavePath;
SaveForm.Title := maincp.name;
SaveForm.Comment := maincp.comment; // AV
if SaveForm.ShowModal = mrOK then
begin
maincp.name := SaveForm.Title;
SavePath := SaveForm.Filename;
maincp.comment := SaveForm.Comment;
ext := LowerCase(ExtractFileExt(SavePath));
if ext = '' then
SavePath := SavePath + '.flame';
if (ext = '.undo') or (ext = '.apo') then
saved := SaveFlame(maincp, maincp.name, SavePath) // AV
else
saved := SaveXMLFlame(maincp, maincp.name, SavePath);
StatusBar.Panels[3].Text := maincp.name;
if (SavePath = OpenFile) and saved then // AV: added status check
// AV: fixed re-saving error with OpenDialog.FileName!
AddFlameToList; // AV: show the new item
end;
end;
procedure TMainForm.mnuSaveAllAsClick(Sender: TObject);
{ Save all parameters to a file }
var
i, current: integer;
currentXML : string;
cp: TControlPoint;
begin
SaveForm.SaveType := stSaveAllParameters;
SaveForm.Filename := SavePath;
if SaveForm.ShowModal = mrOK then
begin
SavePath := SaveForm.Filename;
if ExtractFileExt(SavePath) = '' then
SavePath := SavePath + '.flame';
// AV: added support for saving all Undo flames as XML
if OpenFileType = ftApo then
begin
cp := TControlPoint.Create;
try
for i := 0 to ListView1.Items.Count-1 do
begin
cp.Clear;
FlameFromUndo(cp, ListView1.Items[i].Caption, OpenFile);
SaveXMLFlame(cp, cp.name, SavePath);
LoadSaveProgress.Position := round(100 * i /(ListView1.Items.Count - 1));
end;
finally
LoadSaveProgress.Position := 0; // AV
cp.Free;
end;
exit;
end;
current := ListView1.ItemIndex; // AV: hmm, what if ListView1.Selected = nil?
currentXML := Trim(FlameToXML(Maincp));
for i := 0 to ListView1.Items.Count-1 do
begin
// -X- what if there are unsaved changes at the current CP?
// AV: this only can be if UndoIndex <> 0
if (i = current) and (UndoIndex <> 0) then begin
ParseXML(maincp, currentXML, true); // AV: fixed - was PChar instead String
SaveXMLFlame(maincp, maincp.name, SavePath);
end else begin
// AV: cancel useless multiple preview updated
LoadXMLFlame(OpenFile, ListView1.Items[i].Caption, false);
SaveXMLFlame(maincp, maincp.name, SavePath);
end;
LoadSaveProgress.Position :=
round(100 * i / (ListView1.Items.Count - 1)); // AV: display progress
end;
LoadSaveProgress.Position := 0; // AV
// AV: we don't need to do this because it resets the Undo history!
{
ListXML(SavePath, 2);
if (current < 0) then current := 0;
ListView1.Selected := ListView1.Items[current];
LoadXMLFlame(SavePath, ListView1.Selected.caption);
}
end;
end;
procedure TMainForm.DisplayHint(Sender: TObject);
var
T: TComponent;
begin
T := MainForm.FindComponent('StatusBar');
if T <> nil then
if Application.Hint = '' then
begin
TStatusBar(T).SimpleText := '';
TStatusBar(T).SimplePanel := False;
TStatusBar(T).Refresh;
end
else
begin
// AV: fixed - someone forgot to change this property
TStatusBar(T).SimplePanel := True;
TStatusBar(T).SimpleText := Application.Hint;
end;
end;
procedure TMainForm.DownloadPluginsClick(Sender: TObject);
begin
AboutForm.lblPluginsClick(Sender);
end;
procedure TMainForm.StopScripter;
begin
try
with ScriptEditor do begin
if btnPause.Down then btnPause.Click;
Stopped := True;
end;
except
// Beep;
end;
end;
procedure TMainForm.mnuScreenShotClick(Sender: TObject);
begin
SaveScreenShot('Apophysis Main Window');
end;
{ ********************************* Form ************************************ }
procedure TMainForm.FavoriteClick(Sender: TObject);
var
i: integer;
s: string;
begin
i := TMenuItem(Sender).Tag;
Script := favorites[i];
if FileExists(Script) then
begin
ScriptEditor.Editor.Lines.LoadFromFile(Script);
s := ExtractFileName(Script);
s := Copy(s, 0, length(s) - Length(ExtractFileExt(s)));
mnuRun.Caption := Format(TextByKey('main-menu-script-run2'), [s]);
btnRunScript.Hint := Format(TextByKey('main-menu-script-run2'), [s]);
ScriptEditor.Caption := s;
ScriptEditor.RunScript;
end else
TMenuItem(Sender).Enabled := False;
end;
procedure TMainForm.ScriptItemClick(Sender: TObject);
var
s: string;
begin
s := AppPath + 'Scripts\' + TMenuItem(Sender).Caption;
// AV: fixed Apo7X bug that didn't recognize the new extension
if TMenuItem(Sender).Tag = 1 then
s := s + '.aposcript'
else
s := s + '.asc';
if FileExists(s) then
begin
Script := s;
ScriptEditor.Editor.Lines.LoadFromFile(Script);
s := ExtractFileName(Script);
s := RemoveExt(s);
mnuRun.Caption := Format(TextByKey('main-menu-script-run2'), [s]);
btnRunScript.Hint := Format(TextByKey('main-menu-script-run2'), [s]);
ScriptEditor.Caption := s;
ScriptEditor.RunScript;
end else // if the script was removed or renamed
TMenuItem(Sender).Visible := False;
end;
procedure TMainForm.GetScripts;
var
NewItem, MenuItem: TMenuItem;
searchResult: TSearchRec;
i: integer;
s, path: string;
sl: TStringList;
begin
sl := TStringList.Create;
if FileExists(AppPath + scriptFavsFilename) then
begin
Favorites.LoadFromFile(AppPath + scriptFavsFilename);
if Trim(Favorites.Text) <> '' then
begin
if Favorites.count <> 0 then
begin
FavouriteScripts1.Enabled := True;
FavouriteScripts1.Clear; // AV: refresh the menu everytime it updates
for i := 0 to Favorites.Count - 1 do
begin
if FileExists(Favorites[i]) then
begin
NewItem := TMenuItem.Create(FavouriteScripts1); // (Self);
if i < 12 then
NewItem.ShortCut := TextToShortCut('Ctrl+F' + IntToStr(i + 1));
NewItem.Tag := i;
s := ExtractFileName(Favorites[i]);
sl.Add(s);
s := RemoveExt(s);
MenuItem := Directory1.Find(s); // check the default folder
if (MenuItem <> nil) then
begin
path := LowerCase(ExtractFilePath(Favorites[i]));
if (path = LowerCase(AppPath + 'scripts\')) then
MenuItem.Free;
end;
NewItem.Caption := s;
NewItem.Hint := Format(TextByKey('main-menu-script-run3'), [s]);
NewItem.OnClick := FavoriteClick;
//OnClick := FavoriteClick; // AV: MainForm.OnClick - why?!
FavouriteScripts1.Add(NewItem);
end;
end;
end;
Directory1.Caption := TextByKey('main-menu-script-more');
end
else begin // disable unused items
FavouriteScripts1.Enabled := False;
Directory1.Caption := TextByKey('main-menu-script-directory');
end;
end;
// Try to find regular files matching *.asc in the scripts dir
path := AppPath + 'Scripts\*.asc';
if FindFirst(path, faAnyFile, searchResult) = 0 then
begin
Directory1.Enabled := True;
repeat
NewItem := TMenuItem.Create(Directory1); // (Self);
s := searchResult.Name;
if (sl.IndexOf(s) < 0) then
begin
s := RemoveExt(s);
NewItem.AutoHotkeys := maManual; // AV: to prevent underlined letters
NewItem.Caption := s;
NewItem.Hint := Format(TextByKey('main-menu-script-run3'), [s]);
NewItem.OnClick := ScriptItemClick;
if (Directory1.Find(s) = nil) then Directory1.Add(NewItem);
end;
until (FindNext(searchResult) <> 0);
FindClose(searchResult);
end;
// AV: the same procedure for new extensions
path := AppPath + 'Scripts\*.aposcript';
if FindFirst(path, faAnyFile, searchResult) = 0 then
begin
Directory1.Enabled := True;
repeat
NewItem := TMenuItem.Create(Directory1); // (Self);
s := searchResult.Name;
if (sl.IndexOf(s) < 0) then
begin
s := RemoveExt(s);
NewItem.AutoHotkeys := maManual; // AV: to prevent underlined letters
NewItem.Caption := s;
NewItem.Tag := 1; // AV: to identify scripts with different extensions
NewItem.Hint := Format(TextByKey('main-menu-script-run3'), [s]);
NewItem.OnClick := ScriptItemClick;
if (Directory1.Find(s) = nil) then Directory1.Add(NewItem);
end;
until (FindNext(searchResult) <> 0);
FindClose(searchResult);
end;
if (Directory1.Count = 0) then Directory1.Enabled := False; // AV
sl.Free;
i := 0;
while i < FavouriteScripts1.Count do
begin
FavouriteScripts1[i].Break := mbBreak;
inc(i, mbHeight);
end;
i := 0;
while i < Directory1.Count do
begin
Directory1[i].Break := mbBreak;
inc(i, mbHeight);
end;
end;
procedure TMainForm.FormCreate(Sender: TObject);
var
dte: string;
Registry: TRegistry;
apoUI: string;
Layouts: array[0..7] of THandle;
lnum, i: byte;
ExtSM: HMenu;
extStyle: TSearchRec;
begin
AppVersionString := APP_NAME + ' ' + APP_VERSION;
SubstSource := TStringList.Create;
SubstTarget := TStringList.Create;
CreateSubstMap;
ListXmlScanner := TEasyXmlScanner.Create(nil);
XmlScanner := TXmlScanner.Create(nil);
MainForm.ListXmlScanner.Normalize := False;
MainForm.ListXmlScanner.OnStartTag := ListXmlScannerStartTag;
MainForm.XmlScanner.Normalize := False;
MainForm.XmlScanner.OnContent := XmlScannerContent;
MainForm.XmlScanner.OnEmptyTag := XMLScannerEmptyTag;
MainForm.XmlScanner.OnEndTag := XmlScannerEndTag;
MainForm.XmlScanner.OnStartTag := XMLScannerStartTag;
MainForm.XmlScanner.OnComment := XmlScannerComment; // AV
AppPath := ExtractFilePath(Application.ExeName); // AV: moved here
ReadSettings;
//SaveSettings;
LoadLanguage(LanguageFile);
InsertStrings;
AvailableLanguages := TStringList.Create;
AvailableLanguages.Add('');
ListLanguages;
SplashWindow.SetInfo(TextByKey('splash-loadingplugins'));
MissingPluginList := TStringList.Create; // AV
C_SyncDllPlugins; // for Chaotica export
if (NXFORMS > 100) then
AppVersionString := AppVersionString + ' (' + TextByKey('main-common-title-t500') + ')'
else if (NXFORMS < 100) then
AppVersionString := AppVersionString + ' (' + TextByKey('main-common-title-lite') + ')';
SplashWindow.SetInfo(TextByKey('splash-loadingui'));
// AV: prevent reloading of the splash window after style changing
SplashWindow.OnShow := nil;
{ //*************** GUI Style Stuff *****************************// }
// AV: trying to load externals GUI styles
apoUI := AppPath + 'Styles\';
if FindFirst(apoUI + '*.vsf', faAnyFile, extStyle) = 0 then
begin
repeat
if TStyleManager.IsValidStyle(apoUI + extStyle.Name) then
TStyleManager.LoadFromFile(apoUI + extStyle.Name);
until (FindNext(extStyle) <> 0);
FindClose(extStyle);
end;
{ AV: Read Apophysis style name from registry }
Registry := TRegistry.Create;
try
Registry.RootKey := HKEY_CURRENT_USER;
if Registry.OpenKey('\Software\' + APP_NAME + '\Defaults', False) then
if Registry.ValueExists('UIStyle') then begin
apoUI := Registry.ReadString('UIStyle');
TStyleManager.TrySetStyle(apoUI, false);
end;
Registry.CloseKey;
finally
Registry.Free;
end;
CreateStyleList; // create Apo GUI style menu...
ApplyThemedColors; // AV
{ //******************************************************************// }
Screen.Cursors[crEditArrow] := LoadCursor(HInstance, 'ARROW_WHITE');
Screen.Cursors[crEditMove] := LoadCursor(HInstance, 'MOVE_WB');
Screen.Cursors[crEditRotate] := LoadCursor(HInstance, 'ROTATE_WB');
Screen.Cursors[crEditScale] := LoadCursor(HInstance, 'SCALE_WB');
Caption := AppVersionString + APP_BUILD;
mnuExportFLame.Enabled := FileExists(flam3Path);
mnuExportChaotica.Enabled := FileExists(chaoticaPath + '\chaotica.exe');
// AV: hack for creating screenshots of Apo windows
ExtSM := GetSystemMenu(Handle, False);
InsertMenu(ExtSM, UINT(5), MF_ByPosition or MF_Separator, 0, nil);
InsertMenu(ExtSM, UINT(6), MF_ByPosition, $C0, PChar(TextByKey('main-menu-screenshot')));
FMouseMoveState := msDrag;
LimitVibrancy := False;
Favorites := TStringList.Create;
GetScripts;
Randomize;
MainSeed := Random(123456789);
maincp := TControlPoint.Create;
ParseCp := TControlPoint.create;
MemCp := TControlPoint.Create; // AV
OpenFileType := ftXML;
Application.OnHint := DisplayHint;
CanDrawOnResize := False;
SplashWindow.SetInfo(TextByKey('splash-loadingsettings'));
Dte := FormatDateTime('yymmdd', Now);
if Dte <> RandomDate then
RandomIndex := 0;
RandomDate := Dte;
mnuExit.ShortCut := TextToShortCut('Alt+F4');
defKB := Screen.DefaultKbLayout;
if SetEngLayout then // AV: switch to English language if needed
begin
lnum := GetKeyboardLayoutList(High(Layouts) + 1, Layouts);
for i := 0 to lnum-1 do
if (LoWord(Layouts[i]) and $FF) = Lang_English then
begin
ActivateKeyboardLayout(Layouts[i], 0);
PInteger(@Screen.DefaultKbLayout)^ := -1; // AV: hack - to rewrite a read-only value
break;
end;
end;
FillVariantMenu;
tbQualityBox.Text := FloatToStr(defSampleDensity);
tbShowAlpha.Down := ShowTransparency;
DrawSelection := true;
FViewScale := 1;
{ ************ AV: setting flame thumbnails properties *************}
case ThumbPrevQual of
0: begin
TThumbnailThread.FPreviewDensity := prevLowQuality;
mnuLowQuality.Checked := True;
end;
1: begin
TThumbnailThread.FPreviewDensity := prevMediumQuality;
mnuMediumQuality.Checked := True;
end;
2: begin
TThumbnailThread.FPreviewDensity := prevHighQuality;
mnuHighQuality.Checked := True;
end;
else TThumbnailThread.FPreviewDensity := 1; // just in case...
end;
ThumbnailPlaceholder := TBitmap.Create; // AV
SetThumbnailProperties; // AV
// AV: deleted duplicated image lists to reduce memory allocation
ListView1.LargeImages := UsedThumbnails;
// AV: to prevent updating flame list before it's created
GeneratingThumbs := True;
{ *******************************************************************}
if ClassicListMode = true then
btnViewListClick(nil)
else
btnViewIconsClick(nil);
if ConfirmResetUndo = False then ListView1.OnSelectItem := nil; // AV
SaveSettings; // AV: moved back from top to the end
end;
procedure TMainForm.FormShow(Sender: TObject);
var
Registry: TRegistry;
i: integer;
index: integer;
cmdl : TCommandLine;
fn, flameXML : string;
openScript: string;
autoScript: TStringList; // AV
begin
tbGuides.Down := EnableGuides;
{ Read position from registry }
Registry := TRegistry.Create;
try
Registry.RootKey := HKEY_CURRENT_USER;
if Registry.OpenKey('\Software\' + APP_NAME + '\Forms\Main', False) then
begin
if Registry.ValueExists('Left') then
MainForm.Left := Registry.ReadInteger('Left');
if Registry.ValueExists('Top') then
MainForm.Top := Registry.ReadInteger('Top');
if Registry.ValueExists('Width') then
MainForm.Width := Registry.ReadInteger('Width');
if Registry.ValueExists('Height') then
MainForm.Height := Registry.ReadInteger('Height');
if Registry.ValueExists('SortFlames') then // AV
begin
if Registry.ReadBool('SortFlames') then SortFlames.Click;
end;
if Registry.ValueExists('EnumerateFlames') then // AV
EnumerateFlames.Checked := Registry.ReadBool('EnumerateFlames');
end;
Registry.CloseKey;
if Registry.OpenKey('\Software\' + APP_NAME + '\Defaults', False) then
begin // AV
if Registry.ValueExists('RandBackColor') then
RandBackColor := Registry.ReadInteger('RandBackColor')
else RandBackColor := 0; // AV
end;
Registry.CloseKey;
finally
Registry.Free;
end;
SplashWindow.SetInfo(TextByKey('splash-initrenderer'));
Application.ProcessMessages; // AV: added to update the status properly
{ Synchronize menus etc..}
// should be defaults....
UndoIndex := 0;
UndoMax := 0;
index := 1;
ListView1.RowSelect := True;
inc(MainSeed);
RandSeed := MainSeed;
Variation := vRandom;
Maincp.brightness := defBrightness;
maincp.contrast := defContrast; // AV
maincp.gamma := defGamma;
maincp.vibrancy := defVibrancy;
maincp.sample_density := defSampleDensity;
maincp.spatial_oversample := defOversample;
maincp.spatial_filter_radius := defFilterRadius;
maincp.gammaThreshRelative := defGammaThreshold;
if KeepBackGround and (RandBackColor <> 0) then begin // AV
maincp.background[0] := RandBackColor and 255;
maincp.background[1] := RandBackColor shr 8 and 255;
maincp.background[2] := RandBackColor shr 16 and 255;
end;
inc(MainSeed);
RandSeed := MainSeed;
// somehow this doesn't work:
// Image.Width := BackPanel.Width - 2;
// Image.Height := BackPanel.Height - 2;
// so we'll do it 'bad' way ;-)
Image.Align := alNone;
SplashWindow.SetInfo(TextByKey('splash-initcolormap'));
if FileExists(AppPath + 'Gradients\default.map') then
begin
DefaultPalette := GradientBrowser.LoadFractintMap(AppPath + 'Gradients\default.map');
maincp.cmap := DefaultPalette;
end
else
begin
cmap_index := random(NRCMAPS);
GetCMap(cmap_index, 1, maincp.cmap);
DefaultPalette := maincp.cmap;
end;
fn := AppPath + randFilename;
if FileExists(fn) then DeleteFile(fn);
fn := AppPath + ChangeFileExt(randFilename, '.bak'); // AV
if FileExists(fn) then DeleteFile(fn);
cmdl := TCommandLine.Create;
cmdl.Load;
openScript := '';
SplashWindow.SetInfo(TextByKey('splash-initbatch'));
// get filename from command line argument
if ParamCount > 0 then
openFile := ParamStr(1)
else
openFile := defFlameFile;
if ((openFile = '') or (not FileExists(openFile))) and RememberLastOpenFile then begin
openFile := LastOpenFile;
index := LastOpenFileEntry;
end;
// AV: we must precalc the string that is used so often
fn := LowerCase(ExtractFileExt(OpenFile));
if FileExists(openFile) and (not ((fn = '.asc') or (fn = '.aposcript'))) then begin
LastOpenFile := openFile;
LastOpenFileEntry := index;
end;
if (openFile = '') or (not FileExists(openFile)) and
(not ((fn = '.asc') or (fn = '.aposcript'))) then
begin
MainCp.Width := Image.Width;
MainCp.Height := Image.Height;
RandomBatch;
if APP_BUILD = '' then
MainForm.Caption := AppVersionString + ' - ' + TextByKey('main-common-randombatch')
else
MainForm.Caption := AppVersionString + ' ' + APP_BUILD + ' - ' + TextByKey('main-common-randombatch');
OpenFile := AppPath + randFilename;
OpenFileType := ftXML;
ListXML(OpenFile, 1);
if batchsize = 1 then DrawFlame;
end
else
begin
if (fn = '.apo') or (fn = '.undo') then
begin
OpenFileType := ftApo; // AV: we must choose a file type BEFORE updating list view
ListIFS(OpenFile, 1); // ListFlames(OpenFile, 1);
end else
if (fn = '.asc') or (fn = '.aposcript') then
begin
openScript := OpenFile;
RandomBatch;
if APP_BUILD = '' then
MainForm.Caption := AppVersionString + ' - ' + TextByKey('main-common-randombatch')
else
MainForm.Caption := AppVersionString + ' ' + APP_BUILD + ' - ' + TextByKey('main-common-randombatch');
OpenFile := AppPath + randFilename;
OpenFileType := ftXML;
ListXML(OpenFile, 1);
if batchsize = 1 then DrawFlame;
end else begin
OpenFileType := ftXML;
ListXML(OpenFile, 2);
MainForm.ListView1.Selected := MainForm.ListView1.Items[index - 1];
end;
if APP_BUILD = '' then MainForm.Caption := AppVersionString + ' - ' + openFile
else MainForm.Caption := AppVersionString + ' ' + APP_BUILD + ' - ' + openFile;
end;
ListView1.SetFocus; // AV
CanDrawOnResize := True;
Statusbar.Panels[3].Text := maincp.name;
AdjustForm.cmbPalette.Items.clear;
for i := 0 to NRCMAPS -1 do
AdjustForm.cmbPalette.Items.Add(cMapnames[i]);
AdjustForm.cmbPalette.ItemIndex := 0;
// ExportDialog.cmbDepth.ItemIndex := 2; // AV: changed inside ExportForm
// DoNotAskAboutChange := false;
SetAutoSaveTimer; // AV: a code block is replaced by a method
// loading done..now do what is told by cmdline ...
if (cmdl.CreateFromTemplate) then begin
if FileExists(cmdl.TemplateFile) then begin
fn := cmdl.TemplateFile;
flameXML := LoadXMLFlameText(fn, cmdl.TemplateName);
UpdateUndo;
ScriptEditor.Stopped := True;
StopThread;
ParseXML(MainCP, flameXML, false); //InvokeLoadXML(flameXML);
Transforms := MainCp.TrianglesFromCP(MainTriangles);
Statusbar.Panels[3].Text := MainCp.name;
ResizeImage;
RedrawTimer.Enabled := True;
Application.ProcessMessages;
UpdateWindows;
AdjustForm.mnuRandomize.Click;
end;
end;
cmdl.Free; // <-- AV: fixed memory leak
// .. and run autoexec.asc
SplashWindow.SetInfo(TextByKey('splash-execstartupscript'));
if (FileExists(AppPath + 'autoexec.asc')) then
begin
// AV: first we must check that the file is not empty
autoScript := TStringList.Create;
autoScript.LoadFromFile(AppPath + 'autoexec.asc');
if Trim(autoScript.Text) <> '' then // AV
begin
ScriptEditor.LoadRunAndClear(AppPath + 'autoexec.asc');
mnuRun.Caption := TextByKey('main-menu-script-run');
btnRunScript.Hint := TextByKey('main-menu-script-run');
end;
autoScript.Free;
end;
if (openScript <> '') then begin
ScriptEditor.LoadScriptFile(openScript);
ScriptEditor.Show;
end;
if ScriptEditor.Editor.IsEmpty then // AV: is there any code?
begin
mnuStop.Enabled := False;
btnStopScript.Enabled := False;
end;
SplashWindow.Hide;
SplashWindow.Free;
end;
function TMainForm.SystemErrorMessage: string;
var
P: PChar;
begin
if FormatMessage(Format_Message_Allocate_Buffer + Format_Message_From_System,
nil,
GetLastError,
0,
@P,
0,
nil) <> 0 then
begin
Result := P;
LocalFree(Integer(P))
end
else
Result := '';
end;
function TMainForm.SystemErrorMessage2(errno: cardinal): string;
var
P: PChar;
begin
if FormatMessage(Format_Message_Allocate_Buffer + Format_Message_From_System,
nil,
errno,
0,
@P,
0,
nil) <> 0 then
begin
Result := P;
LocalFree(Integer(P))
end
else
Result := '';
end;
procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
var
Registry: TRegistry;
fn: string;
begin
if ConfirmExit and (UndoIndex <> 0) then
if Application.MessageBox(PChar(TextByKey('common-confirmexit')),
ApophysisSVN, MB_ICONWARNING or MB_YESNO) <> IDYES then
begin
Action := caNone;
exit;
end;
AutoSaveTimer.Enabled := False; // AV
StopScripter; // AV: stopping the scripter's animation
// HtmlHelp(0, nil, HH_CLOSE_ALL, 0);
{ To capture secondary window positions }
if EditForm.visible then EditForm.Close;
if AdjustForm.visible then AdjustForm.close;
if GradientBrowser.visible then GradientBrowser.close;
if MutateForm.visible then MutateForm.Close;
if ScriptEditor.visible then ScriptEditor.Close;
{ Stop the render thread }
if assigned(Renderer) then Renderer.Terminate;
if assigned(Renderer) then Renderer.WaitFor;
if RenderForm.Visible then RenderForm.Close;
{ Write position to registry }
Registry := TRegistry.Create;
try
Registry.RootKey := HKEY_CURRENT_USER;
if Registry.OpenKey('\Software\' + APP_NAME + '\Forms\Main', True) then
begin
if MainForm.WindowState <> wsMaximized then begin
Registry.WriteInteger('Top', MainForm.Top);
Registry.WriteInteger('Left', MainForm.Left);
Registry.WriteInteger('Width', MainForm.Width);
Registry.WriteInteger('Height', MainForm.Height);
end;
Registry.WriteBool('SortFlames', SortFlames.Checked); // AV
Registry.WriteBool('EnumerateFlames', EnumerateFlames.Checked); // AV
end;
finally
Registry.Free;
end;
Application.ProcessMessages;
CanDrawOnResize := False;
fn := AppPath + randFilename;
if FileExists(fn) then DeleteFile(fn);
fn := AppPath + ChangeFileExt(randFilename, '.bak'); // AV
if FileExists(fn) then DeleteFile(fn);
fn := AppPath + undoFilename;
if FileExists(fn) then DeleteFile(fn);
fn := APPDATA + export_flame; // AV
if FileExists(fn) then DeleteFile(fn);
if KeepBackGround then // AV
RandBackColor := MainCp.background[2] * 65536 +
MainCp.background[1] * 256 + MainCp.background[0];
// AV: remember the flame position if the list was sorted
if assigned(ListView1.Selected) then
LastOpenFileEntry := ListView1.Selected.Index + 1;
SaveSettings;
end;
procedure TMainForm.FormDestroy(Sender: TObject);
var i: word;
begin
//if assigned(Renderer) then Renderer.Terminate;
//if assigned(Renderer) then Renderer.WaitFor;
if assigned(Renderer) then Renderer.Free;
if assigned(FViewImage) then FViewImage.Free;
ListXmlScanner.Free; // AV: fixed memory leak
XmlScanner.Free; // AV: fixed memory leak
// AV: all memory leaks with cp.used_plugins are fixed
MainCP.free;
ParseCp.free;
MemCp.free; // AV
Favorites.Free;
SubstSource.Free; // AV: fixed memory leak
SubstTarget.Free; // AV: fixed memory leak
MissingPluginList.Free; // AV
if assigned(ThumbnailPlaceholder) then
begin
ThumbnailPlaceholder.Free; // AV: fixed memory leak
ThumbnailPlaceholder := nil;
end;
AvailableLanguages.Free; // AV: fixed memory leak
for i := 0 to length(Translation.language) - 1 do
Translation.language[i].Free; // AV: fixed memory leaks
ActivateKeyboardLayout(defKB, 0); // AV: restore default user's language
end;
procedure TMainForm.FormKeyPress(Sender: TObject; var Key: Char);
var
scale: double;
begin
if Key = #27 then begin
case FMouseMoveState of
msZoomWindowMove:
FMouseMoveState := msZoomWindow;
msZoomOutWindowMove:
FMouseMoveState := msZoomOutWindow;
msDragMove:
begin
FMouseMoveState := msDrag;
scale := FViewScale * Image.Width / FViewImage.Width;
FViewPos.X := FViewPos.X - (FClickRect.Right - FClickRect.Left) / scale;
FViewPos.Y := FViewPos.Y - (FClickRect.Bottom - FClickRect.Top) / scale;
end;
msRotateMove:
FMouseMoveState := msRotate;
end;
DrawImageView;
end;
//ScriptEditor.Stopped := True;
end;
{ ****************************** Misc controls ****************************** }
procedure TMainForm.BackPanelResize(Sender: TObject);
begin
try
StopThread;
if CanDrawOnResize then
reDrawTimer.Enabled := True;
ResizeImage;
DrawImageView;
except
end;
end;
// AV: added the third parameter to prevent multiple updates of the previews
procedure TMainForm.LoadXMLFlame(filename, name: string; upd: boolean = true);
var
i, p: integer;
FileStrings: TStringList;
ParamStrings: TStringList;
Tokens: TStringList;
time: integer;
begin
time := -1;
FileStrings := TStringList.Create;
ParamStrings := TStringList.Create;
if pos('*untitled', name) <> 0 then
begin
Tokens := TStringList.Create;
GetTokens(name, tokens);
time := StrToInt(tokens[1]);
Tokens.free;
end;
try
FileStrings.LoadFromFile(filename);
for i := 0 to FileStrings.Count - 1 do
begin
pname := '';
ptime := '';
p := Pos('<flame ', LowerCase(FileStrings[i]));
if (p <> 0) then
begin
ListXMLScanner.LoadFromBuffer(PAnsiChar(Utf8String(FileStrings[i]))); // AV
ListXMLScanner.Execute;
if pname <> '' then
begin
if (Trim(pname) = Trim(name)) then
begin
ParamStrings.Add(FileStrings[i]);
Break;
end;
end
else
begin
if ptime='' then ptime:='0'; //hack
if StrToInt(ptime) = time then
begin
ParamStrings.Add(FileStrings[i]);
Break;
end;
end;
end;
end;
repeat
inc(i);
ParamStrings.Add(FileStrings[i]);
until pos('</flame>', Lowercase(FileStrings[i])) <> 0;
//ScriptEditor.Stopped := True; // <-- AV: I hate this...
// If script preview isn't visible, it's useless,
// otherwise it loads wrong flame from sripter...
StopThread;
ParseXML(MainCp, ParamStrings.Text, true);
if upd then
begin // AV: to prevent redrawing when saving a batch
mnuSaveUndo.Enabled := false;
mnuUndo.Enabled := False;
mnuPopUndo.Enabled := False;
mnuRedo.enabled := False;
mnuPopRedo.enabled := False;
EditForm.mnuUndo.Enabled := False;
EditForm.mnuRedo.enabled := False;
EditForm.tbUndo.enabled := false;
EditForm.tbRedo.enabled := false;
AdjustForm.btnUndo.enabled := false;
AdjustForm.btnRedo.enabled := false;
btnUndo.Enabled := false;
btnRedo.enabled := false;
Transforms := MainCp.TrianglesFromCP(MainTriangles);
UndoIndex := 0;
UndoMax := 0;
if fileExists(AppPath + undoFilename) then
DeleteFile(AppPath + undoFilename);
Statusbar.Panels[3].Text := Maincp.name;
RedrawTimer.Enabled := True;
Application.ProcessMessages;
EditForm.SelectedTriangle := 0; // (?)
UpdateWindows;
end; // end updates
finally
FileStrings.free;
ParamStrings.free;
end;
end;
procedure TMainForm.ResizeImage;
var
pw, ph: integer;
begin
pw := BackPanel.Width - 2;
ph := BackPanel.Height - 2;
begin
if (MainCP.Width / MainCP.Height) > (pw / ph) then
begin
Image.Width := pw;
Image.Height := round(MainCP.Height / MainCP.Width * pw);
Image.Left := 1;
Image.Top := (ph - Image.Height) div 2;
end
else begin
Image.Height := ph;
Image.Width := round(MainCP.Width / MainCP.Height * ph);
Image.Top := 1;
Image.Left := (pw - Image.Width) div 2;
end;
end;
//MainCP.AdjustScale(Image.Width, Image.Height);
end;
procedure TMainForm.ListViewColumnClick(Sender: TObject; Column: TListColumn);
begin // AV
if Column = ListView1.Columns[0] then
SortFlames.Click // sorting flames alphabetically or chronologically
else // if Column = ListView1.Columns[1] then
EnumerateFlames.Click;
end;
procedure TMainForm.ListPopUpPopup(Sender: TObject); // AV
var
i: byte;
IsSel: boolean;
begin
IsSel := assigned(ListView1.Selected);
mnuListRename.Enabled := IsSel;
mnuItemDelete.Enabled := IsSel;
mnuRefreshThumb.Enabled := IsSel;
if ClassicListMode then
for i := 2 to 8 do ListPopUp.Items[i].Visible := False
else
for i := 2 to 8 do ListPopUp.Items[i].Visible := True;
end;
procedure TMainForm.ListViewDblClick(Sender: TObject);
begin
if not (ClassicListMode or ParseLoadingBatch) then UpdateThumbnails;
end;
procedure TMainForm.ListViewSelectItem(Sender: TObject; Item: TListItem;
Selected: Boolean);
begin
if (UndoIndex <> 0) and (not Selected) then
if Application.MessageBox(PChar(TextByKey('common-confirmselect')),
ApophysisSVN, 36) = IDYES then
mnuSaveUndo.Click; // AV
end;
procedure TMainForm.ListViewChange(Sender: TObject; Item: TListItem;
Change: TItemChange);
var
i: smallint;
begin
if (ListView1.Selected <> nil) and
(Trim(ListView1.Selected.Caption) <> Trim(maincp.name)) then
begin
LastOpenFileEntry := ListView1.Selected.Index + 1;
RedrawTimer.Enabled := False; //?
StopThread;
if OpenFileType = ftXML then
begin
// ParseLoadingBatch := false; // AV: ?
LoadXMLFlame(OpenFile, ListView1.Selected.caption);
AnnoyUser;
end
else // if OpenFileType = ftApo then // AV: Undo flame list
begin
maincp.Clear; // initialize control point for new flame;
// AV: deleted all duplicated code here
FlameFromUndo(maincp, ListView1.Selected.caption, OpenFile);
maincp.sample_density := defSampleDensity;
maincp.spatial_oversample := defOversample;
maincp.spatial_filter_radius := defFilterRadius;
//Transforms := MainCP.NumXForms; // we'll change it later
Center[0] := maincp.Center[0];
Center[1] := maincp.Center[1];
mnuSaveUndo.Enabled := false;
mnuUndo.Enabled := False;
mnuPopUndo.Enabled := False;
mnuRedo.enabled := False;
mnuPopRedo.enabled := False;
EditForm.mnuUndo.Enabled := False;
EditForm.mnuRedo.enabled := False;
EditForm.tbUndo.enabled := false;
EditForm.tbRedo.enabled := false;
AdjustForm.btnUndo.enabled := false;
AdjustForm.btnRedo.enabled := false;
btnUndo.Enabled := false;
btnRedo.enabled := false;
Transforms := MainCp.TrianglesFromCP(MainTriangles);
// Fix Apophysis 1.0 parameters with negative color parameteres!
for i := 0 to Transforms - 1 do
if maincp.xform[i].color < 0 then maincp.xform[i].color := 0;
UndoIndex := 0;
UndoMax := 0;
if fileExists(AppPath + undoFilename) then
DeleteFile(AppPath + undoFilename);
maincp.name := ListView1.Selected.Caption; // AV: fixed Apo7X bug
Statusbar.Panels[3].Text := maincp.name;
RedrawTimer.Enabled := True;
Application.ProcessMessages;
UpdateWindows;
end;
{if ResizeOnLoad then}
ResizeImage;
end;
end;
procedure TMainForm.UpdateWindows;
begin
if AdjustForm.visible then AdjustForm.UpdateDisplay;
if EditForm.visible then EditForm.UpdateDisplay;
if MutateForm.visible then MutateForm.UpdateDisplay;
end;
procedure TMainForm.LoadUndoFlame(index: integer; filename: string);
var
FStrings: TStringList;
IFSStrings: TStringList;
EntryStrings, Tokens: TStringList;
SavedPal: Boolean;
i, j: integer;
s: string;
Palette: TColorMap;
begin
//ScriptEditor.Stopped := True;
FStrings := TStringList.Create;
IFSStrings := TStringList.Create;
Tokens := TStringList.Create;
EntryStrings := TStringList.Create;
try
FStrings.LoadFromFile(filename);
for i := 0 to FStrings.count - 1 do
if Pos(Format('%.4d-', [UndoIndex]), Trim(FStrings[i])) = 1 then
break;
IFSStrings.Add(FStrings[i]);
repeat
inc(i);
IFSStrings.Add(FStrings[i]);
until Pos('}', FStrings[i]) <> 0;
for i := 0 to FStrings.count - 1 do
begin
if Pos(Format('%.4d-', [UndoIndex]), Trim(Lowercase(FStrings[i]))) = 1 then
break;
end;
inc(i);
while (Pos('}', FStrings[i]) = 0) and (Pos('palette:', FStrings[i]) = 0) do
begin
EntryStrings.Add(FStrings[i]);
inc(i);
end;
SavedPal := false;
if Pos('palette:', FStrings[i]) = 1 then
begin
SavedPal := True;
inc(i);
for j := 0 to 255 do begin
s := FStrings[i];
GetTokens(s, tokens);
Palette[j][0] := StrToInt(Tokens[0]);
Palette[j][1] := StrToInt(Tokens[1]);
Palette[j][2] := StrToInt(Tokens[2]);
inc(i);
end;
end;
maincp.Clear;
maincp.zoom := 0;
maincp.center[0] := 0;
maincp.center[0] := 0;
maincp.ParseString(EntryStrings.Text);
maincp.sample_density := defSampleDensity;
Center[0] := maincp.Center[0];
Center[1] := maincp.Center[1];
Transforms := MainCp.TrianglesFromCP(MainTriangles);
// Trim undo index from title
maincp.name := Copy(Fstrings[0], 6, length(Fstrings[0]) - 7);
if SavedPal then maincp.cmap := palette;
if AdjustForm.visible then AdjustForm.UpdateDisplay;
RedrawTimer.Enabled := True;
UpdateWindows;
finally
IFSStrings.Free;
FStrings.Free;
Tokens.free;
EntryStrings.free;
end;
end;
procedure TMainForm.ResetColorSpeedClick(Sender: TObject);
var i: smallint;
begin
StopThread;
UpdateUndo;
for i := 0 to Transforms-1 do
maincp.xform[i].symmetry := 0;
RedrawTimer.Enabled := True;
UpdateWindows;
end;
procedure TMainForm.ResetColorValuesClick(Sender: TObject);
var i: smallint;
begin
StopThread;
UpdateUndo;
for i := 0 to Transforms-1 do
maincp.xform[i].color := 0;
RedrawTimer.Enabled := True;
UpdateWindows;
end;
procedure TMainForm.ResetLocation;
begin
maincp.zoom := 0;
//maincp.FAngle := 0;
//maincp.Width := Image.Width;
//maincp.Height := Image.Height;
maincp.CalcBoundBox;
center[0] := maincp.center[0];
center[1] := maincp.center[1];
end;
procedure TMainForm.ListViewEdited(Sender: TObject; Item: TListItem;
var S: string);
var Discard: boolean;
begin
if (s <> Item.Caption) then
begin
// AV: fixed 'List index out of bounds' bugs
if (s = '') then
begin
MessageBox(Handle, PChar(TextByKey('save-status-notitle')), ApophysisSVN, 48);
Discard := True;
end else if (ListView1.FindCaption(0, s, false, true, false) <> nil) then
begin
MessageBox(Handle, PChar(Format(TextByKey('save-status-alreadyexists3'),
[s])), ApophysisSVN, 48);
Discard := True;
end
else if OpenFileType = ftXML then
Discard := (not RenameXML(Item.Caption, s))
else
Discard := (not RenameIFS(Item.Caption, s));
if Discard then
s := Item.Caption
else begin
MainCp.name := s; // AV: prevent unnecessary flame redrawing
StatusBar.Panels[3].Text := s; // AV
Application.ProcessMessages;
if AnimateForm.Visible then AnimateForm.Close; // TODO
if SortFlames.Checked and EnumerateFlames.Checked then
EnumerateFlamesClick(EnumerateFlames); // hmm
end;
end;
end;
procedure TMainForm.RedrawTimerTimer(Sender: TObject);
{ Draw flame when timer fires. This seems to stop a lot of errors }
begin
if FMouseMoveState in [msZoomWindowMove, msZoomOutWindowMove, msDragMove, msRotateMove] then exit;
RedrawTimer.enabled := False;
DrawFlame;
end;
procedure TMainForm.mnuVRandomClick(Sender: TObject);
begin
mnuVRandom.Checked := True;
// AV: only one variation can be active here
if Variation > vRandom then begin
VarMenus[Variation].Checked := False;
mnuBuiltinVars.Checked := False;
mnuPluginVars.Checked := False;
end;
StopThread;
UpdateUndo;
inc(MainSeed);
RandSeed := MainSeed;
repeat
Variation := vRandom;
SetVariation(maincp);
until not maincp.blowsup(1000);
inc(randomindex);
MainCp.name := RandomPrefix + RandomDate + '-' +
IntToStr(RandomIndex);
ResetLocation;
RedrawTimer.Enabled := True;
UpdateWindows;
end;
procedure TMainForm.mnuGradClick(Sender: TObject);
begin
AdjustForm.UpdateDisplay;
AdjustForm.PageControl.TabIndex:=2;
AdjustForm.Show;
end;
//**************** Smooth Palette *********************//
procedure swapcolor(var clist: array of cardinal; i, j: integer);
var
t: cardinal;
begin
t := clist[j];
clist[j] := clist[i];
clist[i] := t;
end;
function diffcolor(clist: array of cardinal; i, j: integer): cardinal;
var
r1, g1, b1, r2, g2, b2: byte;
begin
r1 := clist[j] and 255;
g1 := clist[j] shr 8 and 255;
b1 := clist[j] shr 16 and 255;
r2 := clist[i] and 255;
g2 := clist[i] shr 8 and 255;
b2 := clist[i] shr 16 and 255;
Result := abs((r1 - r2) * (r1 - r2)) + abs((g1 - g2) * (g1 - g2)) +
abs((b1 - b2) * (b1 - b2));
end;
procedure TMainForm.mnuSmoothGradientClick(Sender: TObject);
begin
SmoothPalette;
end;
procedure TMainForm.SmoothPalette;
{ From Draves' Smooth palette Gimp plug-in }
var
Bitmap: TBitMap;
JPEG: TJPEGImage;
PNG: TPNGImage; // AV
pal: TColorMap;
strings: TStringlist;
ident, FileName: string;
len, len_best, as_is, swapd: cardinal;
cmap_best, original, clist: array[0..255] of cardinal;
{p, total,} j, rand, tryit, i0, i1, x, y, i, iw, ih: integer;
fn: string;
begin
//Total := Trunc(NumTries * TryLength / 100);
//p := 0;
Bitmap := TBitmap.Create;
strings := TStringList.Create;
try
begin
inc(MainSeed);
RandSeed := MainSeed;
OpenDialog.Filter := RenderForm.SaveDialog.Filter; // AV: added precalc
OpenDialog.InitialDir := ImageFolder;
OpenDialog.Title := TextByKey('common-selectimage'); // AV
OpenDialog.FileName := '';
if OpenDialog.Execute then
begin
fn := OpenDialog.FileName; // AV
ImageFolder := ExtractFilePath(fn);
Application.ProcessMessages;
len_best := 0;
ident := UpperCase(ExtractFileExt(fn)); // AV: added precalc
if (ident = '.BMP') or (ident = '.DIB') then
Bitmap.LoadFromFile(fn)
else if (ident = '.JPG') or (ident = '.JPEG') then
begin
JPEG := TJPEGImage.Create;
try
JPEG.LoadFromFile(fn);
with Bitmap do
begin
Width := JPEG.Width;
Height := JPEG.Height;
Canvas.Draw(0, 0, JPEG);
end;
finally
JPEG.Free;
end;
end
else // if (ident = '.PNG') then // <-- AV: added PNG support here
begin
PNG := TPNGImage.Create; // AV
try
PNG.LoadFromFile(fn);
with Bitmap do
begin
Width := PNG.Width;
Height := PNG.Height;
Canvas.Draw(0, 0, PNG);
end;
finally
PNG.Free;
end;
end;
iw := Bitmap.Width;
ih := Bitmap.Height;
for i := 0 to 255 do
begin
{ Pick colors from 256 random pixels in the image }
x := random(iw);
y := random(ih);
clist[i] := Bitmap.canvas.Pixels[x, y];
end;
original := clist;
cmap_best := clist;
for tryit := 1 to NumTries do
begin
clist := original;
// scramble
for i := 0 to 255 do
begin
rand := random(256);
swapcolor(clist, i, rand);
end;
// measure
len := 0;
for i := 0 to 255 do
len := len + diffcolor(clist, i, i + 1);
// improve
for i := 1 to TryLength do
begin
//inc(p);
// StatusBar.SimpleText := Format(TextByKey('main-status-calculatingpalette'), [p div total]);
i0 := 1 + random(254);
i1 := 1 + random(254);
if ((i0 - i1) = 1) then
begin
as_is := diffcolor(clist, i1 - 1, i1) + diffcolor(clist, i0, i0 + 1);
swapd := diffcolor(clist, i1 - 1, i0) + diffcolor(clist, i1, i0 + 1);
end
else if ((i1 - i0) = 1) then
begin
as_is := diffcolor(clist, i0 - 1, i0) + diffcolor(clist, i1, i1 + 1);
swapd := diffcolor(clist, i0 - 1, i1) + diffcolor(clist, i0, i1 + 1);
end
else
begin
as_is := diffcolor(clist, i0, i0 + 1) + diffcolor(clist, i0, i0 - 1) +
diffcolor(clist, i1, i1 + 1) + diffcolor(clist, i1, i1 - 1);
swapd := diffcolor(clist, i1, i0 + 1) + diffcolor(clist, i1, i0 - 1) +
diffcolor(clist, i0, i1 + 1) + diffcolor(clist, i0, i1 - 1);
end;
if (swapd < as_is) then
begin
swapcolor(clist, i0, i1);
len := abs(len + swapd - as_is);
end;
end;
if (tryit = 1) or (len < len_best) then
begin
cmap_best := clist;
len_best := len;
end;
end;
clist := cmap_best;
// clean
for i := 1 to 1024 do
begin
i0 := 1 + random(254);
i1 := i0 + 1;
as_is := diffcolor(clist, i0 - 1, i0) + diffcolor(clist, i1, i1 + 1);
swapd := diffcolor(clist, i0 - 1, i1) + diffcolor(clist, i0, i1 + 1);
if (swapd < as_is) then
begin
swapcolor(clist, i0, i1);
len_best := len_best + swapd - as_is;
end;
end;
{ Convert to TColorMap, Gradient and save }
FileName := lowercase(ExtractFileName(Opendialog.FileName));
ident := CleanIdentifier(FileName);
strings.add(ident + ' {');
strings.add('gradient:');
strings.add(' title="' + CleanUPRTitle(FileName) + '" smooth=no');
for i := 0 to 255 do
begin
pal[i][0] := clist[i] and 255;
pal[i][1] := clist[i] shr 8 and 255;
pal[i][2] := clist[i] shr 16 and 255;
j := round(i * (399 / 255));
strings.Add(' index=' + IntToStr(j) + ' color=' + intToStr(clist[i]));
end;
strings.Add('}');
if not DirectoryExists(ExtractFilePath(defSmoothPaletteFile)) then // AV
begin
CreateDir(AppPath + 'Gradients\');
defSmoothPaletteFile := AppPath + 'Gradients\SmoothPalette.ugr';
end;
SaveGradient(Strings.Text, Ident, defSmoothPaletteFile);
StopThread;
UpdateUndo;
maincp.cmap := Pal;
maincp.cmapindex := -1;
AdjustForm.UpdateDisplay;
if EditForm.Visible then EditForm.UpdateDisplay;
if MutateForm.Visible then MutateForm.UpdateDisplay;
RedrawTimer.enabled := true;
end;
// StatusBar.SimpleText := '';
end;
finally
Bitmap.Free;
strings.Free;
end;
end;
procedure TMainForm.Smoothize(const oldpal: TColorMap; const a, b: byte);
{ AV: this one applies Smooth palette to the current gradient or its part }
var
pal: TColorMap;
len, len_best, as_is, swapd: cardinal;
cmap_best, original, clist: array[0..255] of cardinal;
rand, tryit, i0, i1, i: integer;
begin
try
inc(MainSeed);
RandSeed := MainSeed;
Application.ProcessMessages;
len_best := 0;
for i := 0 to 255 do
clist[i] := OldPal[i, 2] * 65536 + OldPal[i, 1] * 256 + oldpal[i, 0];
original := clist;
cmap_best := clist;
for tryit := 1 to NumTries do
begin
clist := original;
// scramble
for i := a to b do
begin
{ Pick color from randomly selected index of the palette }
rand := a + random(b - a + 1); // random(256);
swapcolor(clist, i, rand);
end;
// measure
len := 0;
for i := a to b do
len := len + diffcolor(clist, i, i + 1);
// improve
for i := 1 to TryLength do
begin
i0 := a + 1 + random(b - a - 1); // 1 + random(254);
i1 := a + 1 + random(b - a - 1); // 1 + random(254);
if ((i0 - i1) = 1) then
begin
as_is := diffcolor(clist, i1 - 1, i1) + diffcolor(clist, i0, i0 + 1);
swapd := diffcolor(clist, i1 - 1, i0) + diffcolor(clist, i1, i0 + 1);
end
else if ((i1 - i0) = 1) then
begin
as_is := diffcolor(clist, i0 - 1, i0) + diffcolor(clist, i1, i1 + 1);
swapd := diffcolor(clist, i0 - 1, i1) + diffcolor(clist, i0, i1 + 1);
end
else
begin
as_is := diffcolor(clist, i0, i0 + 1) + diffcolor(clist, i0, i0 - 1) +
diffcolor(clist, i1, i1 + 1) + diffcolor(clist, i1, i1 - 1);
swapd := diffcolor(clist, i1, i0 + 1) + diffcolor(clist, i1, i0 - 1) +
diffcolor(clist, i0, i1 + 1) + diffcolor(clist, i0, i1 - 1);
end;
if (swapd < as_is) then
begin
swapcolor(clist, i0, i1);
len := abs(len + swapd - as_is);
end;
end;
if (tryit = 1) or (len < len_best) then
begin
cmap_best := clist;
len_best := len;
end;
end;
clist := cmap_best;
// clean
for i := 1 to 1024 do
begin
i0 := a + 1 + random(b - a - 1); // 1 + random(254);
i1 := i0 + 1;
as_is := diffcolor(clist, i0 - 1, i0) + diffcolor(clist, i1, i1 + 1);
swapd := diffcolor(clist, i0 - 1, i1) + diffcolor(clist, i0, i1 + 1);
if (swapd < as_is) then
begin
swapcolor(clist, i0, i1);
len_best := len_best + swapd - as_is;
end;
end;
{ Convert to TColorMap }
for i := 0 to 255 do
begin
pal[i][0] := clist[i] and 255;
pal[i][1] := clist[i] shr 8 and 255;
pal[i][2] := clist[i] shr 16 and 255;
end;
StopThread;
UpdateUndo;
maincp.cmap := Pal;
maincp.cmapindex := -1;
AdjustForm.UpdateDisplay;
if EditForm.Visible then EditForm.UpdateDisplay;
if MutateForm.Visible then MutateForm.UpdateDisplay;
RedrawTimer.enabled := true;
finally
end;
end;
//**********************************************************************//
{ AV: quick sort to switch between alphabetical and chronological flame order }
function ChronoSort(Item1, Item2: TListItem; ParamSort: integer): integer; stdcall;
begin
Result := (Item1.OverlayIndex - Item2.OverlayIndex); // hacky, but fast...
end;
procedure TMainForm.SortFlamesClick(Sender: TObject);
begin
SortFlames.Checked := not SortFlames.Checked;
if SortFlames.Checked then
begin
ListView1.SortType := stText;
// AV: to use Morph scripting method properly
ScriptForm.ScFileList.Sorted := True;
if ListView1.Items.Count > 1 then begin
ListView1.AlphaSort;
if EnumerateFlames.Checked then EnumerateFlamesClick(EnumerateFlames);
end;
end
else begin
ListView1.SortType := stNone;
ScriptForm.ScFileList.Sorted := False;
if ListView1.Items.Count > 1 then begin
ListView1.CustomSort(@ChronoSort, 0);
if EnumerateFlames.Checked then EnumerateFlamesClick(EnumerateFlames);
end;
end;
if AnimateForm.Visible then AnimateForm.UpdateControls;
end;
//**********************************************************************//
procedure TMainForm.mnuThumbnailQualityClick(Sender: TObject); // AV
begin
if TMenuItem(Sender).Checked then exit; // prevent unneseccary updating
TMenuItem(Sender).Checked := True;
case TMenuItem(Sender).Tag of
0: TThumbnailThread.FPreviewDensity := prevMediumQuality;
1: TThumbnailThread.FPreviewDensity := prevMediumQuality;
2: TThumbnailThread.FPreviewDensity := prevHighQuality;
end;
ThumbPrevQual := TMenuItem(Sender).Tag;
// refresh the list of flame previews
if (OpenFile <> '') then
if not ParseLoadingBatch then UpdateThumbnails;
end;
procedure TMainForm.mnuToolbarClick(Sender: TObject);
begin
Toolbar.Visible := not Toolbar.Visible;
mnuToolbar.Checked := Toolbar.visible;
end;
procedure TMainForm.mnuTraceClick(Sender: TObject);
begin
TraceForm.Show;
end;
procedure TMainForm.mnuStatusBarClick(Sender: TObject);
begin
// Statusbar.Visible := not Statusbar.Visible;
// AV: fixed Apo7X bug - someone forget to hide other components...
BottomDock.Visible := not BottomDock.Visible; // AV
mnuStatusbar.Checked := BottomDock.Visible; // Statusbar.visible;
end;
procedure TMainForm.mnuFileContentsClick(Sender: TObject);
begin
ListBackPanel.Visible := not ListBackPanel.Visible;
mnuFileContents.Checked := ListBackPanel.Visible; // ListView1.Visible;
if ListBackPanel.Visible then
Splitter.Width := 4
else
Splitter.Width := 0;
end;
procedure TMainForm.Undo;
begin
if UndoIndex = UndoMax then
SaveFlame(maincp, Format('%.4d-', [UndoIndex]) + maincp.name,
AppPath + undoFilename);
StopThread;
Dec(UndoIndex);
LoadUndoFlame(UndoIndex, AppPath + undoFilename);
mnuRedo.Enabled := True;
mnuPopRedo.Enabled := True;
btnRedo.Enabled := True;
EditForm.mnuRedo.Enabled := True;
EditForm.tbRedo.enabled := true;
AdjustForm.btnRedo.enabled := true;
if UndoIndex = 0 then begin
mnuUndo.Enabled := false;
mnuPopUndo.Enabled := false;
btnUndo.Enabled := false;
EditForm.mnuUndo.Enabled := false;
EditForm.tbUndo.enabled := false;
AdjustForm.btnUndo.enabled := false;
end;
end;
procedure TMainForm.mnuUndoClick(Sender: TObject);
begin
Undo;
StatusBar.Panels[3].Text := maincp.name;
end;
procedure TMainForm.mnuUnflattenClick(Sender: TObject);
var
i, t: smallint;
refresh: boolean;
begin
StopThread;
refresh := False;
if maincp.HasFinalXForm then t := Transforms
else t := Transforms - 1;
for i := 0 to t do
if maincp.xform[i].GetVariation(1) <> 0 then
begin
maincp.xform[i].SetVariation(1, 0);
refresh := True;
end;
if refresh then
begin
UpdateUndo;
RedrawTimer.Enabled := True;
UpdateWindows;
end;
end;
procedure TMainForm.Redo;
begin
StopThread;
Inc(UndoIndex);
assert(UndoIndex <= UndoMax, 'Undo list index out of range!');
LoadUndoFlame(UndoIndex, AppPath + undoFilename);
mnuUndo.Enabled := True;
mnuPopUndo.Enabled := True;
btnUndo.Enabled := True;
EditForm.mnuUndo.Enabled := True;
EditForm.tbUndo.enabled := true;
AdjustForm.btnUndo.enabled := true;
if UndoIndex = UndoMax then begin
mnuRedo.Enabled := false;
mnuPopRedo.Enabled := false;
btnRedo.Enabled := false;
EditForm.mnuRedo.Enabled := false;
EditForm.tbRedo.enabled := false;
AdjustForm.btnRedo.enabled := false;
end;
end;
procedure TMainForm.mnuRedoClick(Sender: TObject);
begin
Redo;
StatusBar.Panels[3].Text := maincp.name;
end;
// AV: added support for fast preview and params saving without rendering
// for absolute beginners :)
procedure TMainForm.mnuExportBitmapClick(Sender: TObject);
var pic: TPNGImage;
begin
SaveDialog.DefaultExt := 'png';
SaveDialog.Filter := Format('%s|*.png', [TextByKey('common-filter-png')]);
SaveDialog.Filename := maincp.name;
if SaveDialog.Execute then
begin
try
pic := TPNGImage.Create;
try
pic.Assign(Image.Picture.Bitmap);
pic.AddtEXt('ApoFlame', AnsiString(Trim(FlameToXML(Maincp))));
pic.SaveToFile(SaveDialog.Filename);
finally
pic.Free;
end;
except
Image.Picture.Bitmap.SaveToFile(ChangeFileExt(SaveDialog.FileName, '.bmp'));
end;
end;
end;
procedure TMainForm.mnuFullScreenClick(Sender: TObject);
begin
// AV: screen size never changed while app works - moved into OnCreate handler
{
FullScreenForm.Width := Screen.Width;
FullScreenForm.Height := Screen.Height;
FullScreenForm.Top := 0;
FullScreenForm.Left := 0;
}
FullScreenForm.ActiveForm := Screen.ActiveForm;
FullScreenForm.cp.Copy(maincp);
FullScreenForm.cp.cmap := maincp.cmap;
FullScreenForm.Calculate := True;
FullScreenForm.Show;
end;
procedure TMainForm.mnuRenderClick(Sender: TObject);
var
Ext: string;
NewRender: Boolean;
begin
NewRender := True;
if Assigned(RenderForm.Renderer) then
if Application.MessageBox(PChar(TextByKey('render-status-confirmstop')), ApophysisSVN, 36) = ID_NO then
NewRender := false;
if NewRender then
begin
if Assigned(RenderForm.Renderer) then RenderForm.Renderer.Terminate;
if Assigned(RenderForm.Renderer) then RenderForm.Renderer.WaitFor; // hmm #1
RenderForm.ResetControls;
RenderForm.PageCtrl.TabIndex := 0;
case renderFileFormat of
1: Ext := '.bmp';
2: Ext := '.png';
3: Ext := '.jpg';
end;
RenderForm.bRenderAll := False;
//RenderForm.caption := 'Render ' + #39 + maincp.name + #39 + ' to Disk';
RenderForm.Caption := RenderForm.Hint; // AV
RenderForm.Filename := RenderPath + maincp.name + Ext;
RenderForm.SaveDialog.FileName := RenderPath + maincp.name + Ext;
RenderForm.txtFilename.Text := ChangeFileExt(RenderForm.SaveDialog.Filename, Ext);
RenderForm.cp.Copy(MainCP);
RenderForm.cp.cmap := maincp.cmap;
if Assigned(RenderForm.Renderer) then RenderForm.Renderer.WaitFor; // hmm #2
end;
RenderForm.Show;
end;
procedure TMainForm.mnuRenderAllClick(Sender: TObject);
var
Ext: string;
NewRender: Boolean;
i: smallint;
begin
NewRender := True;
if Assigned(RenderForm.Renderer) then
if Application.MessageBox(PChar(TextByKey('render-status-confirmstop')), ApophysisSVN, 36) = ID_NO then
NewRender := false;
if NewRender then
begin
if Assigned(RenderForm.Renderer) then RenderForm.Renderer.Terminate;
if Assigned(RenderForm.Renderer) then RenderForm.Renderer.WaitFor; // hmm #1
RenderForm.ResetControls;
RenderForm.PageCtrl.TabIndex := 0;
case renderFileFormat of
1: Ext := '.bmp';
2: Ext := '.png';
3: Ext := '.jpg';
end;
RenderForm.Caption := GetShortHint(tbRenderAll.Hint); // AV
RenderForm.bRenderAll := true;
RenderForm.Filename := RenderPath + maincp.name + Ext;
RenderForm.SaveDialog.FileName := RenderForm.Filename;
RenderForm.txtFilename.Text := ChangeFileExt(RenderForm.SaveDialog.Filename, Ext);
//AV: added support here for any flame-file (not only opened)
RenderForm.RenderFlameFile := OpenFile;
SetLength(RenderForm.FlameNames, ListView1.Items.Count);
for i := 0 to ListView1.Items.Count - 1 do
RenderForm.FlameNames[i] := ListView1.Items[i].Caption;
{
RenderForm.cp.Copy(MainCP);
RenderForm.cp.cmap := maincp.cmap;
RenderForm.zoom := maincp.zoom;
RenderForm.Center[0] := center[0];
RenderForm.Center[1] := center[1];
}
if Assigned(RenderForm.Renderer) then RenderForm.Renderer.WaitFor; // hmm #2
end;
RenderForm.Show;
end;
procedure TMainForm.mnuMutateClick(Sender: TObject);
begin
MutateForm.Show;
MutateForm.UpdateDisplay;
end;
procedure TMainForm.mnuAdjustClick(Sender: TObject);
begin
AdjustForm.UpdateDisplay;
AdjustForm.PageControl.TabIndex := 0;
AdjustForm.Show;
end;
procedure TMainForm.mnuAnimatorClick(Sender: TObject);
begin
StopScripter;
AnimateForm.Show;
end;
procedure TMainForm.mnuResetLocationClick(Sender: TObject);
var
scale: double;
dx, dy, cdx, cdy: double;
sina, cosa: extended;
begin
StopThread; // AV
UpdateUndo;
try // AV
scale := MainCP.pixels_per_unit / MainCP.Width * power(2, MainCP.zoom);
cdx := MainCP.center[0];
cdy := MainCP.center[1];
ResetLocation;
cdx := MainCP.center[0] - cdx;
cdy := MainCP.center[1] - cdy;
Sincos(MainCP.FAngle, sina, cosa);
if IsZero(sina) then begin
dy := cdy*cosa {- cdx*sina};
dx := (cdx {+ dy*sina})/cosa;
end
else begin
dx := cdy*sina + cdx*cosa;
dy := (dx*cosa - cdx)/sina;
end;
FViewPos.x := FViewPos.x - dx * scale * Image.Width;
FViewPos.y := FViewPos.y - dy * scale * Image.Width;
FViewScale := FViewScale * MainCP.pixels_per_unit / MainCP.Width * power(2, MainCP.zoom) / scale;
DrawImageView;
RedrawTimer.enabled := true;
UpdateWindows;
except on EMathError do // AV
Trace2('Cannot calculate the flame scale and bounds...');
end;
end;
procedure TMainForm.mnuAboutClick(Sender: TObject);
begin
AboutForm.ShowModal;
end;
procedure TMainForm.mnuOpenGradientClick(Sender: TObject);
begin
GradientBrowser.Filename := GradientFile;
GradientBrowser.Show;
end;
procedure TMainForm.mnuSaveUndoClick(Sender: TObject);
begin
if FileExists(AppPath + undoFilename) then
begin
SaveDialog.DefaultExt := 'apo';
SaveDialog.Filter := TextByKey('common-filter-undofiles') + '|*undo;*.apo';
SaveDialog.InitialDir := ParamFolder;
SaveDialog.Filename := maincp.name;
if SaveDialog.Execute then
begin
if FileExists(SaveDialog.Filename) then DeleteFile(SaveDialog.Filename);
CopyFile(PChar(AppPath + undoFilename), PChar(SaveDialog.Filename), False);
end;
end;
end;
procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if Assigned(RenderForm.Renderer) then
if Application.MessageBox(PChar(TextByKey('render-status-confirmstop')),
ApophysisSVN, 36) = ID_NO then
CanClose := False;
// AboutToExit := CanClose;
end;
procedure TMainForm.FormActivate(Sender: TObject);
begin
if Assigned(Renderer) then Renderer.Priority := tpNormal;
mnuPaste.Enabled := FlameInClipboard; // AV
end;
procedure TMainForm.FormDeactivate(Sender: TObject);
begin
if Assigned(Renderer) then Renderer.Priority := tpLower;
end;
procedure TMainForm.mnuCalculateColorsClick(Sender: TObject);
var
i: smallint;
begin
StopThread;
UpdateUndo;
if Transforms > 1 then // AV: fixed divide-by-zero bug
for i := 0 to Transforms - 1 do
maincp.xform[i].color := i / (transforms - 1)
else
maincp.xform[0].color := 0; // AV
RedrawTimer.Enabled := True;
UpdateWindows;
end;
procedure TMainForm.mnuRandomizeColorValuesClick(Sender: TObject);
var
i: smallint;
begin
inc(MainSeed);
RandSeed := MainSeed;
StopThread;
UpdateUndo;
for i := 0 to Transforms - 1 do
maincp.xform[i].color := random;
RedrawTimer.Enabled := True;
UpdateWindows;
end;
procedure TMainForm.mnuEditScriptClick(Sender: TObject);
begin
ScriptEditor.Show;
end;
procedure TMainForm.mnuRunClick(Sender: TObject);
begin
if not ScriptEditor.Editor.IsEmpty then // AV: is there any code?
ScriptEditor.RunScript;
end;
procedure TMainForm.mnuOpenScriptClick(Sender: TObject);
begin
ScriptEditor.OpenScript;
end;
procedure TMainForm.mnuStopClick(Sender: TObject);
begin
//ScriptEditor.Stopped := True;
// AV: what if script is paused?
ScriptEditor.btnStop.Click; // AV
end;
{
procedure TMainForm.mnuImportGimpClick(Sender: TObject);
var
flist: tStringList;
begin
flist := TStringList.Create;
OpenDialog.Filter := Format('%s|*.*', [TextByKey('common-filter-allfiles')]);
try
if OpenDialog.Execute then
begin
flist.loadFromFile(OpenDialog.filename);
maincp.clear;
maincp.ParseStringList(flist);
maincp.Width := Image.Width;
maincp.Height := Image.Height;
maincp.zoom := 0;
maincp.CalcBoundBox;
center[0] := maincp.center[0];
center[1] := maincp.center[1];
RedrawTimer.Enabled := True;
Application.ProcessMessages;
Transforms := MainCp.TrianglesFromCP(MainTriangles);
UpdateWindows;
end;
finally
flist.free;
end;
end;
}
procedure TMainForm.mnuManageFavoritesClick(Sender: TObject);
var
MenuItem: TMenuItem;
i: integer;
s: string;
begin
if FavoritesForm.ShowModal = mrOK then
begin
if Favorites.Count <> 0 then
begin
for i := 0 to Favorites.Count - 1 do
begin
s := ExtractFileName(Favorites[i]);
s := Copy(s, 0, length(s) - Length(ExtractFileExt(s)));
MenuItem := FavouriteScripts1.Find(s);
if MenuItem <> nil then
MenuItem.Free;
end;
end;
GetScripts;
end;
end;
procedure TMainForm.DisableFavorites;
var
MenuItem: TMenuItem;
i: integer;
s: string;
begin
for i := 0 to Favorites.Count - 1 do
begin
s := ExtractFileName(Favorites[i]);
s := Copy(s, 0, length(s) - Length(ExtractFileExt(s)));
MenuItem := FavouriteScripts1.Find(s);
if MenuItem <> nil then
MenuItem.Enabled := False;
end;
end;
procedure TMainForm.EnableFavorites;
var
MenuItem: TMenuItem;
i: integer;
s: string;
begin
for i := 0 to Favorites.Count - 1 do
begin
s := ExtractFileName(Favorites[i]);
s := Copy(s, 0, length(s) - Length(ExtractFileExt(s)));
MenuItem := FavouriteScripts1.Find(s);
if MenuItem <> nil then
MenuItem.Enabled := True;
end;
end;
procedure TMainForm.EnumerateFlamesClick(Sender: TObject);
var i: integer;
begin
with MainForm.ListView1.Items do
begin
BeginUpdate;
for i := 0 to Count - 1 do Item[i].SubItems.Clear; // AV: hide the index
if TMenuItem(Sender).Checked then
begin // AV: displaying the index
MainForm.ListView1.Column[1].Caption := ' N ';
if FlameEnumMode = 0 then
for i := 0 to Count - 1 do Item[i].SubItems.Add(IntToStr(i))
else
for i := 0 to Count - 1 do Item[i].SubItems.Add(IntToStr(i+1));
end
else
MainForm.ListView1.Column[1].Caption := '';
EndUpdate;
end;
end;
procedure TMainForm.mnuImageSizeClick(Sender: TObject);
begin
AdjustForm.UpdateDisplay;
AdjustForm.PageControl.TabIndex := 3;
AdjustForm.Show;
end;
procedure TMainForm.AddSymmetryClick(Sender: TObject);
var finTX: TXForm;
begin
if (Transforms + TMenuItem(Sender).Tag > NXForms) then Exit;
StopThread;
UpdateUndo;
finTX := TXForm.Create;
finTX.Assign(MainCp.xform[Transforms]);
MainCp.NormalizeProbabilities;
add_symmetry_to_control_point(MainCp, TMenuItem(Sender).Tag);
Transforms := MainCp.TrianglesFromCP(MainTriangles);
MainCp.xform[Transforms].Assign(finTX);
ResetLocation;
RedrawTimer.Enabled := True;
UpdateWindows;
finTX.Free;
end;
procedure TMainForm.AddTemplateClick(Sender: TObject); // AV
var
tmpdir: string;
begin
tmpdir := AppPath + 'Templates\';
if not DirectoryExists(tmpdir) then
CreateDir(tmpdir);
with SaveForm do
begin
SaveType := stSaveTemplate;
Filename := tmpdir + 'Fractal Templates.template';
Title := maincp.name;
ActiveControl := txtTitle;
if ShowModal = mrOK then
begin
maincp.name := Title;
StatusBar.Panels[3].Text := maincp.name;
if SaveXMLFlame(maincp, maincp.name, Filename) and (FileName = OpenFile) then
AddFlameToList;
end;
end;
end;
procedure TMainForm.AddTileClick(Sender: TObject);
var finTX: TXForm;
begin
if (Transforms + 6 > NXForms) then Exit;
StopThread;
UpdateUndo;
finTX := TXForm.Create;
finTX.Assign(MainCp.xform[Transforms]);
MainCp.NormalizeProbabilities;
tile_control_point(MainCp, TMenuItem(Sender).Tag);
Transforms := MainCp.TrianglesFromCP(MainTriangles);
MainCp.xform[Transforms].Assign(finTX);
ResetLocation;
RedrawTimer.Enabled := True;
UpdateWindows;
finTX.Free;
end;
// AV: make a common event handler for Main and Adjust forms
procedure TMainForm.ApplicationEventsActivate(Sender: TObject);
begin
if GradientInClipboard then
begin
AdjustForm.mnuPaste.enabled := true;
AdjustForm.btnPaste.enabled := true;
mnuPaste.enabled := false;
end
else if FlameInClipboard then
begin
AdjustForm.mnuPaste.enabled := false;
AdjustForm.btnPaste.enabled := false;
if (pos('Memorized XForm Parameters', Clipboard.AsText) > 0) then
mnuPaste.enabled := False // AV: hack
else
mnuPaste.Enabled := true;
end
else
begin
AdjustForm.mnuPaste.enabled := false;
AdjustForm.btnPaste.enabled := false;
mnuPaste.enabled := false;
end;
end;
procedure TMainForm.ParseXML(var cp1: TControlPoint; const params: string; const ignoreErrors : boolean);
var
i: integer;
begin
nxform := 0;
FinalXformLoaded := false;
ActiveXformSet := 0;
XMLPaletteFormat := '';
XMLPaletteCount := 0;
SurpressHandleMissingPlugins := ignoreErrors;
ParseCp.Free; // we're creating this CP from the scratch
ParseCp := TControlPoint.create; // to reset variables properly (randomize)
XMLScanner.LoadFromBuffer(PAnsiChar(Utf8String(params)));
XMLScanner.Execute;
cp1.copy(ParseCp);
if (Parsecp.cmapindex = -2) then
begin
if cp1.cmapindex < NRCMAPS then
GetCMap(cp1.cmapindex, 1, cp1.cmap)
{else
ShowMessage('Palette index too high')};
RotateCMapHue(cp1); // AV
end;
if FinalXformLoaded = false then begin
cp1.xform[nxform].Clear;
cp1.xform[nxform].symmetry := 1;
end;
if nxform < NXFORMS then
for i := nxform to NXFORMS - 1 do
cp1.xform[i].density := 0;
// Check for symmetry parameter
if ParseCp.symmetry <> 0 then
begin
add_symmetry_to_control_point(cp1, ParseCp.symmetry);
cp1.symmetry := 0;
end;
cp1.FillUsedPlugins;
SurpressHandleMissingPlugins := false;
end;
procedure TMainForm.PasteFlameXML(flameXML: string); // AV
begin
if (flameXML <> '') then
begin
UpdateUndo;
// StopScripter;
StopThread;
ParseXML(MainCP, flameXML, false); // AV: fixed - was PChar instead String
AnnoyUser;
Transforms := MainCp.TrianglesFromCP(MainTriangles);
Statusbar.Panels[3].Text := MainCp.name;
if AutoSaveXML then // AV: saving loaded parameters in the current file
begin
while XMLEntryExists(MainCp.name, OpenFile) do
MainCp.name := MainCp.name + ' (new)'; // hmm...
if (OpenFile = AppPath + randfilename) then // random batch will be deleted
SaveXMLFlame(MainCp, MainCp.name, IfThen(DirectoryExists(ExtractFilePath(AutoSavePath)),
ExtractFilePath(AutoSavePath), AppPath) + 'Saved by ApophysisAV.flame'); // :)
// AV: display these changes and scroll to the selected item
if SaveXMLFlame(MainCp, MainCp.name, OpenFile) then
AddFlameToList; // AV: show the new item
end;
ResizeImage;
RedrawTimer.Enabled := True;
Application.ProcessMessages;
UpdateWindows;
end;
end;
procedure TMainForm.mnuPasteClick(Sender: TObject);
begin
//if Clipboard.HasFormat(CF_TEXT) then
if FlameInClipboard then // AV
PasteFlameXML(Clipboard.AsText);
end;
procedure TMainForm.mnuCopyClick(Sender: TObject);
var
txt: string;
i: integer;
begin
txt := Trim(FlameToXML(Maincp));
Clipboard.SetTextBuf(PChar(txt));
mnuPaste.enabled := true;
AdjustForm.mnuPaste.enabled := False;
AdjustForm.btnPaste.enabled := False;
// AV: for pasting multiple transforms into editor
MemCp.Clear;
for i := 0 to Maincp.NumXForms - 1 do //FIXME: skip final transform!
MemCp.xform[i].Assign(Maincp.xform[i]);
EditForm.PasteTransform.Enabled := True;
end;
function WinShellExecute(const Operation, AssociatedFile: string): Boolean;
var
a1: string;
r: Cardinal;
begin
a1 := Operation;
if a1 = '' then
a1 := 'open';
r := ShellExecute(
application.handle,
pchar(a1),
pchar(AssociatedFile),
'',
'',
SW_SHOWNORMAL
);
if (r > 32) then WinShellExecute := true
else WinShellExecute := false;
end;
procedure WinShellOpen(const AssociatedFile: string);
begin
WinShellExecute('open', AssociatedFile);
end;
procedure TMainForm.mnuExportFlameClick(Sender: TObject);
var
FileList: Tstringlist;
Ext: string;
cp1: TControlPoint;
begin
if not FileExists(flam3Path) then
begin
Application.MessageBox(PChar(TextByKey('main-status-noflam3')), ApophysisSVN, 16);
exit;
end;
// AV: we really don't need to waste the memory and create it at startup
ExportDialog := TExportDialog.Create(Application); // AV
case ExportFileFormat of
1: Ext := '.jpg';
2: Ext := '.ppm';
3: Ext := '.png';
end;
FileList := TstringList.Create;
cp1 := TControlPoint.Create;
cp1.copy(Maincp);
ExportDialog.ImageWidth := ExportWidth;
ExportDialog.ImageHeight := ExportHeight;
ExportDialog.Sample_density := ExportDensity;
ExportDialog.Filter_Radius := ExportFilter;
ExportDialog.Oversample := ExportOversample;
try
ExportDialog.Filename := RenderPath + Maincp.name + Ext;
if ExportDialog.ShowModal = mrOK then
begin
Ext := ExtractFileExt(ExportDialog.Filename);
if Ext = '.ppm' then
ExportFileFormat := 2
else if Ext = '.png' then
ExportFileFormat := 3
else // if Ext = '.jpg' then
ExportFileFormat := 1;
Delete(Ext, 1, 1);
{
case ExportFileFormat of
1: Ext := 'jpg';
2: Ext := 'ppm';
3: Ext := 'png';
end;
}
ExportWidth := ExportDialog.ImageWidth;
ExportHeight := ExportDialog.ImageHeight;
ExportDensity := ExportDialog.Sample_density;
ExportFilter := ExportDialog.Filter_Radius;
ExportOversample := ExportDialog.Oversample;
ExportEstimator := ExportDialog.Estimator;
ExportEstimatorMin := ExportDialog.EstimatorMin;
ExportEstimatorCurve := ExportDialog.EstimatorCurve;
ExportGammaTreshold := ExportDialog.GammaTreshold;
// AV: user cannot change the following, anyway
//ExportJitters := ExportDialog.Jitters;
//ExportBatches := ExportDialog.Batches;
cp1.sample_density := ExportDensity;
cp1.spatial_oversample := ExportOversample;
cp1.spatial_filter_radius := ExportFilter;
cp1.nbatches := 1; //ExportBatches;
cp1.jitters := 1; //ExportJitters;
if (cp1.width <> ExportWidth) or (cp1.Height <> ExportHeight) then
cp1.AdjustScale(ExportWidth, ExportHeight);
cp1.estimator := ExportEstimator;
cp1.estimator_min := ExportEstimatorMin;
cp1.estimator_curve := ExportEstimatorCurve;
cp1.gamma_threshold := ExportGammaTreshold;
FileList.Text := FlameToXML(cp1, true);
FileList.SaveToFile(ChangeFileExt(ExportDialog.Filename, '.flame'));
FileList.Clear;
FileList.Add('@echo off');
FileList.Add('set verbose=1');
FileList.Add('set format=' + Ext);
if ExportFileFormat = 1 then
FileList.Add('set jpeg=' + IntToStr(JPEGQuality));
case ExportDialog.cmbDepth.ItemIndex of
0: FileList.Add('set bits=16');
1: FileList.Add('set bits=32');
2: FileList.Add('set bits=33');
3: FileList.Add('set bits=64');
end;
if ExportDialog.udStrips.Position > 1 then
FileList.Add('set nstrips=' + IntToStr(ExportDialog.udStrips.Position));
if (PNGTransparency > 0) then
FileList.Add('set transparency=1')
else
FileList.Add('set transparency=0');
FileList.Add('set out=' + ExportDialog.Filename);
FileList.Add('@echo Rendering "' + ExportDialog.Filename + '"');
FileList.Add('"' + flam3Path + '" < "' + ChangeFileExt(ExportDialog.Filename, '.flame') + '"');
FileList.SaveToFile(ChangeFileExt(ExportDialog.Filename, '.bat'));
if ExportDialog.chkRender.Checked then
begin
SetCurrentDir(ExtractFilePath(ExportDialog.Filename));
WinShellOpen(ChangeFileExt(ExportDialog.Filename, '.bat'));
end;
end;
finally
FileList.Free;
cp1.free;
ExportDialog.Free; // AV: destroying unnecessary form
end;
end;
////////////////////////////////////////////////////////////////////////////////
procedure ParseCompactColors(cp: TControlPoint; count: integer; in_data: string; alpha: boolean = true);
function HexChar(c: Char): Byte;
begin
case c of
'0'..'9': Result := Byte(c) - Byte('0');
'a'..'f': Result := (Byte(c) - Byte('a')) + 10;
'A'..'F': Result := (Byte(c) - Byte('A')) + 10;
else
Result := 0;
end;
end;
var
i, pos, len: integer;
c: char;
data: string;
begin
// diable generating pallete
if Parsecp.cmapindex = -2 then
Parsecp.cmapindex := -1;
Assert(Count = 256, 'only 256 color gradients are supported at the moment');
data := '';
for i := 1 to Length(in_data) do
begin
c := in_data[i];
if CharInSet(c,['0'..'9']+['A'..'F']+['a'..'f']) then data := data + c;
end;
if alpha then len := count * 8
else len := count * 6;
Assert(len = Length(data), 'color-data size mismatch');
for i := 0 to Count-1 do begin
if alpha then pos := i*8 + 2
else pos := i*6;
Parsecp.cmap[i][0] := 16 * HexChar(Data[pos + 1]) + HexChar(Data[pos + 2]);
Parsecp.cmap[i][1] := 16 * HexChar(Data[pos + 3]) + HexChar(Data[pos + 4]);
Parsecp.cmap[i][2] := 16 * HexChar(Data[pos + 5]) + HexChar(Data[pos + 6]);
end;
end;
procedure TMainForm.ListXmlScannerStartTag(Sender: TObject;
TagName: string; Attributes: TAttrList);
begin
if (TagName = 'flame') then begin // AV: fixed
pname := string(Attributes.value('name'));
ptime := string(Attributes.value('time'));
// pversion := string(Attributes.value('version'));
end;
end;
procedure TMainForm.XMLScannerStartTag(Sender: TObject; TagName: string;
Attributes: TAttrList);
var
Tokens: TStringList;
v: string; //TStringType;
ParsePos, i : integer;
begin
Tokens := TStringList.Create;
try
if TagName='xformset' then // unused in this release...
begin
v := string(Attributes.Value('enabled'));
if v <> '' then ParseCP.finalXformEnabled := (StrToInt(v) <> 0)
else ParseCP.finalXformEnabled := true;
inc(activeXformSet);
end
else if TagName='flame' then
begin
BeginParsing;
v := string(Attributes.value('version')); // AV
if (pos('Apophysis 2.0', v) > 0) or (v = '') then
oldApo := true else oldApo := false;
v := string(Attributes.value('name'));
if v <> '' then Parsecp.name := v else Parsecp.name := 'untitled';
v := string(Attributes.Value('time'));
if v <> '' then Parsecp.Time := StrToFloat(v);
v := string(Attributes.value('palette'));
if v <> '' then
Parsecp.cmapindex := StrToInt(v)
else
Parsecp.cmapindex := -1;
v := string(Attributes.value('gradient'));
if v <> '' then
Parsecp.cmapindex := StrToInt(v)
else
Parsecp.cmapindex := -1;
//ParseCP.hue_rotation := 1;
v := string(Attributes.value('hue')); // AV: to animate the palette
if v <> '' then
Parsecp.hue_rotation := StrToFloat(v)
else
ParseCP.hue_rotation := 1;
v := string(Attributes.Value('brightness'));
if v <> '' then Parsecp.Brightness := StrToFloat(v);
v := string(Attributes.Value('gamma'));
if v <> '' then Parsecp.gamma := StrToFloat(v);
v := string(Attributes.Value('contrast')); // AV
if v <> '' then Parsecp.contrast := StrToFloat(v);
v := string(Attributes.Value('vibrancy'));
if v <> '' then Parsecp.vibrancy := StrToFloat(v);
if (LimitVibrancy) and (Parsecp.vibrancy > 1) then Parsecp.vibrancy := 1;
v := string(Attributes.Value('gamma_threshold'));
if v <> '' then Parsecp.gamma_threshold := StrToFloat(v)
else Parsecp.gamma_threshold := 0;
v := string(Attributes.Value('zoom'));
if v <> '' then Parsecp.zoom := StrToFloat(v);
v := string(Attributes.Value('scale'));
if v <> '' then Parsecp.pixels_per_unit := StrToFloat(v);
v := string(Attributes.Value('rotate'));
if v <> '' then Parsecp.FAngle := -PI * StrToFloat(v)/180;
v := string(Attributes.Value('angle'));
if v <> '' then Parsecp.FAngle := StrToFloat(v);
// 3d
v := string(Attributes.Value('cam_pitch'));
if v <> '' then Parsecp.cameraPitch := StrToFloat(v);
v := string(Attributes.Value('cam_yaw'));
if v <> '' then Parsecp.cameraYaw := StrToFloat(v);
v := string(Attributes.Value('cam_roll'));
if v <> '' then Parsecp.cameraRoll := StrToFloat(v);
v := string(Attributes.Value('cam_dist'));
if v <> '' then Parsecp.cameraPersp := 1/StrToFloat(v);
v := string(Attributes.Value('cam_perspective'));
if v <> '' then Parsecp.cameraPersp := StrToFloat(v);
v := string(Attributes.Value('cam_zpos'));
if v <> '' then Parsecp.cameraZpos := StrToFloat(v);
v := string(Attributes.Value('cam_dof'));
if v <> '' then Parsecp.cameraDOF := abs(StrToFloat(v));
//density estimation
v := string(Attributes.Value('estimator_radius'));
if v <> '' then Parsecp.estimator := StrToFloat(v);
v := string(Attributes.Value('estimator_minimum'));
if v <> '' then Parsecp.estimator_min := StrToFloat(v);
v := string(Attributes.Value('estimator_curve'));
if v <> '' then Parsecp.estimator_curve := StrToFloat(v);
v := string(Attributes.Value('enable_de'));
if (v = '1') then Parsecp.enable_de := true;
v := string(Attributes.Value('new_linear'));
if (v = '1') then // AV
Parsecp.noLinearFix := true
else ParseCp.noLinearFix := false;
v := string(Attributes.Value('curves'));
if (v <> '') then begin
GetTokens(v, tokens);
ParsePos := -1;
for i := 0 to 3 do
begin
Inc(ParsePos); ParseCp.curvePoints[i][0].x := StrToFloat(Tokens[ParsePos]);
Inc(ParsePos); ParseCp.curvePoints[i][0].y := StrToFloat(Tokens[ParsePos]);
Inc(ParsePos); ParseCp.curveWeights[i][0] := StrToFloat(Tokens[ParsePos]);
Inc(ParsePos); ParseCp.curvePoints[i][1].x := StrToFloat(Tokens[ParsePos]);
Inc(ParsePos); ParseCp.curvePoints[i][1].y := StrToFloat(Tokens[ParsePos]);
Inc(ParsePos); ParseCp.curveWeights[i][1] := StrToFloat(Tokens[ParsePos]);
Inc(ParsePos); ParseCp.curvePoints[i][2].x := StrToFloat(Tokens[ParsePos]);
Inc(ParsePos); ParseCp.curvePoints[i][2].y := StrToFloat(Tokens[ParsePos]);
Inc(ParsePos); ParseCp.curveWeights[i][2] := StrToFloat(Tokens[ParsePos]);
Inc(ParsePos); ParseCp.curvePoints[i][3].x := StrToFloat(Tokens[ParsePos]);
Inc(ParsePos); ParseCp.curvePoints[i][3].y := StrToFloat(Tokens[ParsePos]);
Inc(ParsePos); ParseCp.curveWeights[i][3] := StrToFloat(Tokens[ParsePos]);
end;
end;
try
v := string(Attributes.Value('center'));
GetTokens(v, tokens);
Parsecp.center[0] := StrToFloat(Tokens[0]);
Parsecp.center[1] := StrToFloat(Tokens[1]);
except
Parsecp.center[0] := 0;
Parsecp.center[1] := 0;
end;
v := string(Attributes.Value('size'));
GetTokens(v, tokens);
Parsecp.width := StrToInt(Tokens[0]);
Parsecp.height := StrToInt(Tokens[1]);
try
v := string(Attributes.Value('background'));
GetTokens(v, tokens);
Parsecp.background[0] := Floor(StrToFloat(Tokens[0]) * 255);
Parsecp.background[1] := Floor(StrToFloat(Tokens[1]) * 255);
Parsecp.background[2] := Floor(StrToFloat(Tokens[2]) * 255);
except
Parsecp.background[0] := 0;
Parsecp.background[1] := 0;
Parsecp.background[2] := 0;
end;
v := string(Attributes.Value('soloxform'));
if v <> '' then Parsecp.soloXform := StrToInt(v);
v := string(Attributes.Value('plugins'));
GetTokens(v, tokens);
if (tokens.Count > 0) then begin
ParseCP.used_plugins.Clear;
for i := 0 to tokens.Count - 1 do
ParseCP.used_plugins.Add(tokens[i]);
end;
(* // AV: commented out since it's useless
v := Attributes.Value('nick');
if Trim(v) = '' then v := SheepNick;
Parsecp.Nick := v;
v := Attributes.Value('url');
if Trim(v) = '' then v := SheepUrl;
Parsecp.URL := v;
*)
end
else if TagName='palette' then
begin
XMLPaletteFormat := string(Attributes.Value('format'));
XMLPaletteCount := StrToIntDef(string(Attributes.Value('count')), 256);
end;
finally
Tokens.free;
end;
end;
function GetComment(str: string): string;
{ AV: Extracts comment from XML-file }
begin
try
Result := Trim(Copy(str, 5, Length(str) - 7));
except
Result := '';
end;
end;
procedure TMainForm.XmlScannerComment(Sender: TObject; Comment: string);
begin
ParseCP.comment := GetComment(Comment);
end;
function flatten_val(Attributes: TAttrList): double;
var
vv: array of double;
vn: array of string;
i: integer;
s: string;
d: boolean;
begin
// AV: invert the behavior since it flatten real 3D figures like bubble
vn := ['crop', 'auger', 'bipolar', 'blur', 'blur_circle', 'blur_pixelize',
'blur_zoom', 'horseshoe', 'diamond', 'disc', 'bent2', 'escher', 'eyefish',
'fan2', 'flux', 'foci', 'log', 'bwraps', 'juliascope', 'julian', 'mobius',
'noise', 'ngon', 'curl', 'rings2', 'scry', 'spherical', 'spiral', 'cropn',
'swirl', 'wedge', 'checks', 'polar', 'polar2', 'linear', 'cross', 'pdj',
'hyperbolic', 'radial_blur', 'elliptic', 'lazysusan', 'post_smartcrop',
'circlecrop', 'rectangles'];
SetLength(vv, length(vn)); // AV
d := false;
for i := 0 to High(vn) do
begin
s := string(Attributes.Value(Utf8String(vn[i])));
if (s <> '') then vv[i] := StrToFloat(s)
else vv[i] := 0;
d := d or (vv[i] <> 0);
end;
// AV: changed 0 to 1 and vice versa
if (d) then Result := 1
else Result := 0;
SetLength(vv, 0);
SetLength(vn, 0);
end;
function linear_val(Attributes: TAttrList): double;
var
vv: array of double;
vn: array of string;
i: integer;
s: string;
begin
SetLength(vv, 2);
vn := ['linear3D', 'linear'];
Result := 0;
for i := 0 to 1 do
begin
s := string(Attributes.Value(Utf8String(vn[i])));
if (s <> '') then vv[i] := StrToFloat(s)
else vv[i] := 0;
Result := Result + vv[i];
end;
SetLength(vv, 0);
SetLength(vn, 0);
end;
procedure TMainForm.XmlScannerContent(Sender: TObject; Content: String);
begin
if XMLPaletteCount <= 0 then
//ShowMessage('ERROR: No colors in palette!');
raise Exception.Create(TextByKey('common-invalidformat') + ': palette'); // AV
if XMLPaletteFormat = 'RGB' then
begin
ParseCompactColors(ParseCP, XMLPaletteCount, Content, false);
end
else if XMLPaletteFormat = 'RGBA' then
begin
ParseCompactColors(ParseCP, XMLPaletteCount, Content);
end
else
raise Exception.Create(TextByKey('common-invalidformat') + ': palette'); // AV
Parsecp.cmapindex := -1;
// AV: restored hue rotation support, useful for animation
RotateCMapHue(Parsecp);
XMLPaletteFormat := '';
XMLPaletteCount := 0;
end;
procedure TMainForm.XMLScannerEmptyTag(Sender: TObject; TagName: string;
Attributes: TAttrList);
var
i, j, k, vindex: integer; // j, k - AV
v, l, s: string; //TStringType;
d, floatcolor, vl, n: double;
Tokens: TStringList;
begin
Tokens := TStringList.Create;
try
if (TagName = 'xform') or (TagName = 'finalxform') then
if {(TagName = 'finalxform') and} (FinalXformLoaded) then
Application.MessageBox(PChar(TextByKey('common-invalidformat')), ApophysisSVN, MB_ICONERROR)
// ShowMessage('ERROR: No xforms allowed after FinalXform!')
else
begin // AV
for i := 0 to Attributes.Count - 1 do begin
if not ScanVariations(string(attributes.Name(i))) and
not ScanVariables(string(attributes.Name(i))) then
CheckAttribute(string(Attributes.Name(i)));
end;
if (TagName = 'finalxform') or (activeXformSet > 0) then FinalXformLoaded := true;
with ParseCP.xform[nXform] do begin
Clear;
v := string(Attributes.Value('weight'));
if (v <> '') and (TagName = 'xform') then density := StrToFloat(v);
if (TagName = 'finalxform') then
begin
v := string(Attributes.Value('enabled'));
if v <> '' then ParseCP.finalXformEnabled := (StrToInt(v) <> 0)
else ParseCP.finalXformEnabled := true;
end;
if activexformset > 0 then density := 0; // tmp...
//**************** AV: checking variation order ***********//
v := string(Attributes.Value('var_order'));
if v <> '' then begin
GetTokens(v, tokens);
k := -1;
for j := 0 to Tokens.Count-1 do
begin
vindex := ifs.IndexOf(Tokens[j]);
if vindex >= 0 then
begin
inc(k);
ifs.Move(vindex, k);
end;
end;
end;
//************************************************************//
v := string(Attributes.Value('color'));
if v <> '' then color := StrToFloat(v);
v := string(Attributes.Value('var_color'));
if v <> '' then pluginColor := StrToFloat(v);
v := string(Attributes.Value('symmetry'));
if v <> '' then symmetry := StrToFloat(v);
v := string(Attributes.Value('coefs'));
if v <> '' then begin
GetTokens(v, tokens);
if Tokens.Count < 6 then
Application.MessageBox(PChar(TextByKey('common-invalidformat') + ': coefs'),
ApophysisSVN, MB_ICONERROR);
c[0][0] := StrToFloat(Tokens[0]);
c[0][1] := StrToFloat(Tokens[1]);
c[1][0] := StrToFloat(Tokens[2]);
c[1][1] := StrToFloat(Tokens[3]);
c[2][0] := StrToFloat(Tokens[4]);
c[2][1] := StrToFloat(Tokens[5]);
end;
v := string(Attributes.Value('post'));
if v <> '' then begin
GetTokens(v, tokens);
if Tokens.Count < 6 then
Application.MessageBox(PChar(TextByKey('common-invalidformat') + ': post'),
ApophysisSVN, MB_ICONERROR);
p[0][0] := StrToFloat(Tokens[0]);
p[0][1] := StrToFloat(Tokens[1]);
p[1][0] := StrToFloat(Tokens[2]);
p[1][1] := StrToFloat(Tokens[3]);
p[2][0] := StrToFloat(Tokens[4]);
p[2][1] := StrToFloat(Tokens[5]);
end;
v := string(Attributes.Value('chaos'));
if v <> '' then begin
GetTokens(v, tokens);
for i := 0 to Tokens.Count-1 do
modWeights[i] := Abs(StrToFloat(Tokens[i]));
end;
//else for i := 0 to NXFORMS-1 do modWeights[i] := 1;
// for 2.09 flames compatibility
v := string(Attributes.Value('opacity'));
if v <> '' then begin
if StrToFloat(v) = 0.0 then begin
transOpacity := 0;
end else begin
transOpacity := StrToFloat(v);
end;
end;
// 7x.9 name tag
v := string(Attributes.Value('name'));
if v <> '' then begin
TransformName := v;
end;
v := string(Attributes.Value('plotmode'));
if v <> '' then begin
if v = 'off' then begin
transOpacity := 0;
end;
end;
// tricky: attempt to convert parameters to 15C+-format if necessary
if ParseCp.noLinearFix then
for i := 0 to 1 do
begin
v := ReadWithSubst(Attributes, varnames(i));
if v <> '' then
SetVariation(i, StrToFloat(v))
else
SetVariation(i, 0);
end
else begin
SetVariation(0, linear_val(Attributes));
if ApplyFlatten then // AV
SetVariation(1, flatten_val(Attributes));
end;
// now parse the rest of the variations...as usual
for i := 2 to NRVAR - 1 do
begin
v := ReadWithSubst(Attributes, varnames(i));
if v <> '' then
SetVariation(i, StrToFloat(v))
else
SetVariation(i, 0);
end;
// and the variables
for i := 0 to GetNrVariableNames - 1 do begin
s := GetVariableNameAt(i);
v := ReadWithSubst(Attributes, s);
if v <> '' then begin
{$ifndef VAR_STR}
d := StrToFloat(v);
SetVariable(s, d);
{$else}
SetVariableStr(s, v);
{$endif}
end;
end;
{***** AV: tryig to convert old Apo 2.0x variations into new ones *****}
if oldApo then begin
// AV: 'perspective' into 'projective'
v := string(Attributes.Value('perspective'));
s := string(Attributes.Value('projective'));
if (v <> '') and (s = '') then // avoid to overwrite
begin
d := StrToFloat(v);
SetVariation(GetVariationIndex('projective'), d);
v := string(Attributes.Value('perspective_dist'));
l := string(Attributes.Value('perspective_angle'));
vl := StrToFloat(v); // dist
d := StrToFloat(l); // angle
n := 0;
SetVariable('pr_A', n);
SetVariable('pr_B1', n);
SetVariable('pr_C1', n);
SetVariable('pr_A2', n);
SetVariable('pr_C2', n);
SetVariable('pr_A1', vl);
SetVariable('pr_C', vl);
n := -sin(d * pi * 0.5);
SetVariable('pr_B', n);
n := vl * cos(d * pi * 0.5);
SetVariable('pr_B2', n);
n := 1;
SetVariable('projective_mode', n);
end
else if (v <> '') and (s <> '') then
begin
MissingPlugin.MissingPluginList.Add('perspective');
MissingPlugin.MissingPluginList.Add('perspective_angle');
MissingPlugin.MissingPluginList.Add('perspective_dist');
end;
v := string(Attributes.Value('rings'));
s := string(Attributes.Value('rings2'));
if (v <> '') and (s = '') then
begin
d := StrToFloat(v);
SetVariation(GetVariationIndex('rings2'), d);
n := c[2][0];
SetVariable('rings2_val', n);
n := 1;
SetVariable('rings2_old', n);
end
else if (v <> '') and (s <> '') then
MissingPlugin.MissingPluginList.Add('rings');
v := string(Attributes.Value('fan'));
s := string(Attributes.Value('fan2'));
if (v <> '') and (s = '') then
begin
d := StrToFloat(v);
SetVariation(GetVariationIndex('fan2'), d);
n := c[2][0];
SetVariable('fan2_x', n);
n := c[2][1];
SetVariable('fan2_y', n);
n := 0; // AV: it is 1 only for 2.09 'fan2'
SetVariable('fan2_old', n);
end
else if (v <> '') and (s <> '') then
MissingPlugin.MissingPluginList.Add('fan');
v := string(Attributes.Value('bent'));
if (v <> '') then
begin
s := string(Attributes.Value('bent2'));
if (s = '') then
begin
d := StrToFloat(v);
SetVariation(GetVariationIndex('bent2'), d);
n := 2;
SetVariable('bent2_x', n);
n := 0.5;
SetVariable('bent2_y', n);
n := 1;
SetVariable('bent2_z', n);
end
else MissingPlugin.MissingPluginList.Add('bent');
end;
v := string(Attributes.Value('waves'));
s := string(Attributes.Value('waves2'));
if (v <> '') and (s = '') then
begin
d := StrToFloat(v);
SetVariation(GetVariationIndex('waves2'), d);
n := c[1][0];
SetVariable('waves2_scalex', n);
n := 1/(sqr(c[2][0]) + 1E-300);
SetVariable('waves2_freqx', n);
n := c[1][1];
SetVariable('waves2_scaley', n);
n := 1/(sqr(c[2][1]) + 1E-300);
SetVariable('waves2_freqy', n);
n := 0;
SetVariable('waves2_scalez', n);
SetVariable('waves2_freqz', n);
end
else if (v <> '') and (s <> '') then
MissingPlugin.MissingPluginList.Add('waves');
v := string(Attributes.Value('popcorn'));
if (v <> '') then
begin
s := string(Attributes.Value('popcorn2'));
if (s = '') then
begin
d := StrToFloat(v);
SetVariation(GetVariationIndex('popcorn2'), d);
n := c[2][0];
SetVariable('popcorn2_x', n);
n := c[2][1];
SetVariable('popcorn2_y', n);
n := 3;
SetVariable('popcorn2_c', n);
end
else MissingPlugin.MissingPluginList.Add('popcorn');
end;
end; // oldApo
// AV: Droste into Escher
v := string(Attributes.Value('droste'));
s := string(Attributes.Value('escher'));
if (v <> '') and (s = '') then
begin
d := StrToFloat(v);
SetVariation(GetVariationIndex('escher'), d);
v := string(Attributes.Value('droste_r1'));
l := string(Attributes.Value('droste_r2'));
try
vl := StrToFloat(v); // r1
d := StrToFloat(l); // r2
if (vl <> d) then
n := 2 * arctan(ln(d / vl) / 2 / pi)
else n := 0;
SetVariable('escher_beta', n);
except
n := 0;
SetVariable('escher_beta', n);
end;
end
else if (v <> '') and (s <> '') then
begin
MissingPlugin.MissingPluginList.Add('droste');
MissingPlugin.MissingPluginList.Add('droste_r1');
MissingPlugin.MissingPluginList.Add('droste_r2');
end;
// Spherical3D into inversion3D
v := string(Attributes.Value('Spherical3D'));
if (v <> '') and (GetVariationIndex('Spherical3D')< 0) then
// if plugin is NOT available
begin
s := string(Attributes.Value('inversion3D'));
if (s = '') then
begin
d := StrToFloat(v);
SetVariation(GetVariationIndex('inversion3D'), d);
n := 1;
SetVariable('inversion3D_radius', n);
n := 0;
SetVariable('inversion3D_x0', n);
SetVariable('inversion3D_y0', n);
SetVariable('inversion3D_z0', n);
end
else MissingPlugin.MissingPluginList.Add('Spherical3D');
end;
// secant into secant2
v := string(Attributes.Value('secant'));
if (v <> '') and (GetVariationIndex('secant') < 0) then
// if plugin is NOT available
begin
s := string(Attributes.Value('secant2'));
if (s = '') then
begin
d := StrToFloat(v);
SetVariation(GetVariationIndex('secant2'), d);
n := 1;
SetVariable('secant2_old', n);
end
else MissingPlugin.MissingPluginList.Add('secant');
end;
// arch into Z_arch
v := string(Attributes.Value('arch'));
if (v <> '') then
begin
s := string(Attributes.Value('Z_arch'));
if (s = '') then
begin
d := StrToFloat(v);
SetVariation(GetVariationIndex('Z_arch'), d);
SetVariable('Z_arch_weight', d);
end
else MissingPlugin.MissingPluginList.Add('arch');
end;
{********************************************************}
// legacy variation/variable notation
v := string(Attributes.Value('var1'));
if v <> '' then
begin
for i := 0 to NRVAR - 1 do
SetVariation(i, 0);
SetVariation(StrToInt(v), 1);
end;
v := string(Attributes.Value('var'));
if v <> '' then
begin
for i := 0 to NRVAR - 1 do
SetVariation(i, 0);
GetTokens(v, tokens);
if Tokens.Count > NRVAR then
Application.MessageBox(PChar(TextByKey('common-invalidformat')),
ApophysisSVN, MB_ICONERROR);
for i := 0 to Tokens.Count - 1 do
SetVariation(i, StrToFloat(Tokens[i]));
end;
end;
// AV: prevent crash with flames containing over 100 xforms
{$ifndef T500}
if nXform < NXFORMS then
{$endif}
Inc(nXform);
end;
if TagName = 'color' then
begin
// disable generating palette
//if Parsecp.cmapindex = -2 then
Parsecp.cmapindex := -1;
i := StrToInt(string(Attributes.value('index')));
v := string(Attributes.value('rgb'));
GetTokens(v, tokens);
floatcolor := StrToFloat(Tokens[0]);
Parsecp.cmap[i][0] := round(floatcolor);
floatcolor := StrToFloat(Tokens[1]);
Parsecp.cmap[i][1] := round(floatcolor);
floatcolor := StrToFloat(Tokens[2]);
Parsecp.cmap[i][2] := round(floatcolor);
end;
if TagName = 'colors' then
begin
ParseCompactcolors(Parsecp, StrToInt(string(Attributes.value('count'))),
string(Attributes.value('data')));
Parsecp.cmapindex := -1;
end;
if TagName = 'symmetry' then
begin
i := StrToInt(string(Attributes.value('kind')));
Parsecp.symmetry := i;
end;
{
if TagName = 'xdata' then
begin
Parsecp.xdata := Parsecp.xdata + string(Attributes.value('content'));
end;
}
finally
Tokens.free;
end;
end;
procedure TMainForm.mnuFlamepdfClick(Sender: TObject);
begin
WinShellOpen('http://www.flam3.com/flame_draves.pdf');
end;
procedure TMainForm.mnuFlattenClick(Sender: TObject);
var
i, j, t: integer;
v: double;
refresh: boolean;
flat: array of integer;
begin
StopThread;
refresh := False;
// AV: using new Delphi's feature for dynamic arrays
flat := [GetVariationIndex('crop'), GetVariationIndex('auger'),
GetVariationIndex('bipolar'), GetVariationIndex('blur'),
GetVariationIndex('blur_circle'), GetVariationIndex('blur_pixelize'),
GetVariationIndex('blur_zoom'), GetVariationIndex('horseshoe'),
GetVariationIndex('diamond'), GetVariationIndex('disc'),
GetVariationIndex('bent2'), GetVariationIndex('escher'),
GetVariationIndex('eyefish'), GetVariationIndex('fan2'),
GetVariationIndex('flux'), GetVariationIndex('foci'),
GetVariationIndex('log'), GetVariationIndex('bwraps'),
GetVariationIndex('juliascope'), GetVariationIndex('julian'),
GetVariationIndex('mobius'), GetVariationIndex('noise'),
GetVariationIndex('ngon'), GetVariationIndex('curl'),
GetVariationIndex('rings2'), GetVariationIndex('scry'),
GetVariationIndex('spherical'), GetVariationIndex('spiral'),
GetVariationIndex('circlecrop'), GetVariationIndex('swirl'),
GetVariationIndex('wedge'), GetVariationIndex('rectangles'),
GetVariationIndex('polar'), GetVariationIndex('polar2'),
GetVariationIndex('linear'), GetVariationIndex('cross'),
GetVariationIndex('pdj'), GetVariationIndex('hyperbolic'),
GetVariationIndex('radial_blur'), GetVariationIndex('elliptic'),
GetVariationIndex('lazysusan'), GetVariationIndex('checks'),
GetVariationIndex('cropn'), GetVariationIndex('post_smartcrop')];
if maincp.HasFinalXForm then t := Transforms
else t := Transforms - 1;
for i := 0 to t do
for j in flat do // AV: iterate only for chosen variation indices
begin
if (j < 0) then continue;
v := maincp.xform[i].GetVariation(j);
if (v <> 0) and (maincp.xform[i].GetVariation(1) = 0) then
begin
maincp.xform[i].SetVariation(1, 1); // apply flatten
refresh := True;
end;
end;
if refresh then
begin
UpdateUndo;
RedrawTimer.Enabled := True;
UpdateWindows;
end;
SetLength(flat, 0);
end;
///////////////////////////////////////////////////////////////////////////////
procedure TMainForm.ImageMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
(*
if button = mbMiddle then begin
//FMouseMoveState := msHeight;
exit;
end else if button = mbRight then begin
//FMouseMoveState := msPitchYaw;
camDragValueY := MainCP.cameraPitch * 180.0 / PI;
camDragValueX := MainCP.cameraYaw * 180.0 / PI;
camDragMode := true;
camDragPos.x := 0;
camDragPos.y := 0;
camDragOld.x := x;
camDragOld.y := y;
camMM := false;
//SetCaptureControl(TControl(Sender));
//Screen.Cursor := crNone;
//GetCursorPos(mousepos); // hmmm
//mousePos := (Sender as TControl).ClientToScreen(Point(x, y));
camDragged := false;
exit;
end;
*)
if button <> mbLeft then exit;
FClickRect.TopLeft := Point(x, y);
FClickRect.BottomRight := FClickRect.TopLeft;
case FMouseMoveState of
msZoomWindow:
begin
FSelectRect.TopLeft := Point(x, y);
FSelectRect.BottomRight := Point(x, y);
DrawZoomWindow;
// if ssAlt in Shift then
// FMouseMoveState := msZoomOutWindowMove
// else
FMouseMoveState := msZoomWindowMove;
end;
msZoomOutWindow:
begin
FSelectRect.TopLeft := Point(x, y);
FSelectRect.BottomRight := Point(x, y);
DrawZoomWindow;
// if ssAlt in Shift then
// FMouseMoveState := msZoomWindowMove
// else
FMouseMoveState := msZoomOutWindowMove;
end;
msDrag:
begin
if not assigned(FViewImage) then exit;
// FSelectRect.TopLeft := Point(x, y);
// FSelectRect.BottomRight := Point(x, y);
FMouseMoveState := msDragMove;
end;
msRotate:
begin
FClickAngle := arctan2(y - Image.Height/2, Image.Width/2 - x);
FRotateAngle := 0;
// FSelectRect.Left := x;
DrawRotateLines(FRotateAngle);
FMouseMoveState := msRotateMove;
end;
end;
end;
//***************************************************************************//
procedure TMainForm.ImageMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
const
snap_angle = 0.261799387799149; // AV: the same as 15*pi/180;
var
dx, dy, cx, cy, sgn: integer;
sc, vx, vy, scale: double;
q : Extended;
begin
{
case FMouseMoveState of
msRotate, msRotateMove:
Image.Cursor := crEditRotate;
msDrag, msDragMove:
Image.Cursor := crEditMove;
else
Image.Cursor := crEditArrow;
end;
}
case FMouseMoveState of
msZoomWindowMove,
msZoomOutWindowMove:
begin
if DrawSelection then DrawZoomWindow;
FClickRect.BottomRight := Point(x, y);
dx := x - FClickRect.TopLeft.X;
dy := y - FClickRect.TopLeft.Y;
if ssShift in Shift then begin
if (dy = 0) or (abs(dx/dy) >= Image.Width/Image.Height) then
dy := Round(dx / Image.Width * Image.Height)
else
dx := Round(dy / Image.Height * Image.Width);
FSelectRect.Left := FClickRect.TopLeft.X - dx;
FSelectRect.Top := FClickRect.TopLeft.Y - dy;
FSelectRect.Right := FClickRect.TopLeft.X + dx;
FSelectRect.Bottom := FClickRect.TopLeft.Y + dy;
end
else if ssCtrl in Shift then begin
FSelectRect.TopLeft := FClickRect.TopLeft;
sgn := IfThen(dy*dx >=0, 1, -1);
if (dy = 0) or (abs(dx/dy) >= Image.Width/Image.Height) then begin
FSelectRect.Right := x;
FSelectRect.Bottom := FClickRect.TopLeft.Y + sgn * Round(dx / Image.Width * Image.Height);
end
else begin
FSelectRect.Right := FClickRect.TopLeft.X + sgn * Round(dy / Image.Height * Image.Width);
FSelectRect.Bottom := y;
end;
end
else begin
sgn := IfThen(dy*dx >=0, 1, -1);
if (dy = 0) or (abs(dx/dy) >= Image.Width/Image.Height) then begin
cy := (y + FClickRect.TopLeft.Y) div 2;
FSelectRect.Left := FClickRect.TopLeft.X;
FSelectRect.Right := x;
FSelectRect.Top := cy - sgn * Round(dx / 2 / Image.Width * Image.Height);
FSelectRect.Bottom := cy + sgn * Round(dx / 2 / Image.Width * Image.Height);
end
else begin
cx := (x + FClickRect.TopLeft.X) div 2;
FSelectRect.Left := cx - sgn * Round(dy / 2 / Image.Height * Image.Width);
FSelectRect.Right := cx + sgn * Round(dy / 2 / Image.Height * Image.Width);
FSelectRect.Top := FClickRect.TopLeft.Y;
FSelectRect.Bottom := y;
end;
end;
DrawZoomWindow;
DrawSelection := true;
end;
msDragMove:
begin
assert(assigned(FviewImage));
assert(FViewScale <> 0);
scale := FViewScale * Image.Width / FViewImage.Width;
FViewPos.X := FViewPos.X + (x - FClickRect.Right) / scale;
FViewPos.Y := FViewPos.Y + (y - FClickRect.Bottom) / scale;
//FClickRect.BottomRight := Point(x, y);
DrawImageView;
end;
{ msPitchYaw:
begin
if camDragMode and ( (x <> camDragOld.x) or (y <> camDragOld.y) ) then
begin
Inc(camDragPos.x, x - camDragOld.x);
Inc(camDragPos.y, y - camDragOld.y);
vx := Round6(camDragValueX + camDragPos.x / 10);
vy := Round6(camDragValueY - camDragPos.y / 10);
MainCP.cameraPitch := vy * PI / 180.0;
MainCP.cameraYaw := vx * PI / 180.0;
vx := Round(vx);
vy := Round(vy);
camDragged := True;
//StatusBar.Panels.Items[1].Text := Format('Pitch: %f<>, Yaw: %f<>', [vx,vy]);
end;
end; }
msRotateMove:
begin
if DrawSelection then DrawRotatelines(FRotateAngle);
FRotateAngle := arctan2(y-Image.Height/2, Image.Width/2-x) - FClickAngle;
if ssShift in Shift then // angle snap
FRotateAngle := Round(FRotateAngle/snap_angle)*snap_angle;
//SelectRect.Left := x;
// pdjpointgen.Rotate(FRotateAngle);
// FRotateAngle := 0;
DrawRotatelines(FRotateAngle);
DrawSelection := true;
{
Image.Refresh;
if AdjustForm.Visible then begin
MainCp.FAngle:=-FRotateAngle;
AdjustForm.UpdateDisplay;
end;
}
end;
end;
FClickRect.BottomRight := Point(x, y);
end;
function ScaleRect(r: TRect; scale: double): TSRect;
begin
Result.Left := r.Left * scale;
Result.Top := r.Top * scale;
Result.Right := r.Right * scale;
Result.Bottom := r.Bottom * scale;
end;
//***************************************************************************//
procedure TMainForm.ImageMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
scale: double;
rs: TSRect;
begin
case FMouseMoveState of
msZoomWindowMove:
begin
DrawZoomWindow;
FMouseMoveState := msZoomWindow;
if (abs(FSelectRect.Left - FSelectRect.Right) < 10) or
(abs(FSelectRect.Top - FSelectRect.Bottom) < 10) then
Exit; // zoom to much or double clicked
StopThread;
UpdateUndo;
MainCp.ZoomtoRect(ScaleRect(FSelectRect, MainCP.Width / Image.Width));
FViewScale := FViewScale * Image.Width / abs(FSelectRect.Right - FSelectRect.Left);
FViewPos.x := FViewPos.x - ((FSelectRect.Right + FSelectRect.Left) - Image.Width)/2;
FViewPos.y := FViewPos.y - ((FSelectRect.Bottom + FSelectRect.Top) - Image.Height)/2;
DrawImageView;
RedrawTimer.Enabled := True;
UpdateWindows;
end;
msZoomOutWindowMove:
begin
DrawZoomWindow;
FMouseMoveState := msZoomOutWindow;
if (abs(FSelectRect.Left - FSelectRect.Right) < 10) or
(abs(FSelectRect.Top - FSelectRect.Bottom) < 10) then
Exit; // zoom to much or double clicked
StopThread;
UpdateUndo;
MainCp.ZoomOuttoRect(ScaleRect(FSelectRect, MainCP.Width / Image.Width));
scale := Image.Width / abs(FSelectRect.Right - FSelectRect.Left);
FViewScale := FViewScale / scale;
FViewPos.x := scale * (FViewPos.x + ((FSelectRect.Right + FSelectRect.Left) - Image.Width)/2);
FViewPos.y := scale * (FViewPos.y + ((FSelectRect.Bottom + FSelectRect.Top) - Image.Height)/2);
DrawImageView;
RedrawTimer.Enabled := True;
UpdateWindows;
end;
msDragMove:
begin
FClickRect.BottomRight := Point(x, y);
FMouseMoveState := msDrag;
if ((x = 0) and (y = 0)) or // double clicked
((FClickRect.left = FClickRect.right) and (FClickRect.top = FClickRect.bottom))
then Exit;
StopThread;
UpdateUndo;
MainCp.MoveRect(ScaleRect(FClickRect, MainCP.Width / Image.Width));
RedrawTimer.Enabled := True;
UpdateWindows;
end;
msRotateMove:
begin
DrawRotatelines(FRotateAngle);
FMouseMoveState := msRotate;
if (FRotateAngle = 0) then Exit; // double clicked
StopThread;
UpdateUndo;
if MainForm_RotationMode = 0 then MainCp.Rotate(FRotateAngle)
else MainCp.Rotate(-FRotateAngle);
if assigned(FViewImage) then begin
FViewImage.Free;
FViewImage := nil;
DrawImageView;
end;
RedrawTimer.Enabled := True;
UpdateWindows;
end;
{ msPitchYaw:
begin
camDragMode := false;
Screen.Cursor := crDefault;
if camDragged then
begin
camDragged := False;
RedrawTimer.Enabled := True;
UpdateWindows;
end;
end; }
end;
end;
//***************************************************************************//
procedure TMainForm.DrawImageView;
var
i, j: integer;
bm: TBitmap;
r: TRect;
scale: double;
const
msg = #54; // 'NO PREVIEW';
var
ok: boolean;
GlobalMemoryInfo: TMemoryStatus; // holds the global memory status information
area: int64;
gridp: integer;
begin
bm := TBitmap.Create;
bm.Width := Image.Width;
bm.Height := Image.Height;
with bm.Canvas do begin
if ShowTransparency then begin
Brush.Color := $F0F0F0;
FillRect(Rect(0, 0, bm.Width, bm.Height));
Brush.Color := $C0C0C0;
for i := 0 to ((bm.Width - 1) shr 3) do begin
for j := 0 to ((bm.Height - 1) shr 3) do begin
if odd(i + j) then
FillRect(Rect(i shl 3, j shl 3, (i+1) shl 3, (j+1) shl 3));
end;
end;
end
else begin
Brush.Color := MainCP.background[0] or (MainCP.background[1] shl 8) or (MainCP.background[2] shl 16);
FillRect(Rect(0, 0, bm.Width, bm.Height));
end;
end;
ok := false;
if assigned(FViewImage) then begin
scale := FViewScale * Image.Width / FViewImage.Width;
r.Left := Image.Width div 2 + round(scale * (FViewPos.X - FViewImage.Width/2));
r.Right := Image.Width div 2 + round(scale * (FViewPos.X + FViewImage.Width/2));
r.Top := Image.Height div 2 + round(scale * (FViewPos.Y - FViewImage.Height/2));
r.Bottom := Image.Height div 2 + round(scale * (FViewPos.Y + FViewImage.Height/2));
GlobalMemoryInfo.dwLength := SizeOf(GlobalMemoryInfo);
GlobalMemoryStatus(GlobalMemoryInfo);
area := abs(r.Right - r.Left) * int64(abs(r.Bottom - r.Top));
if (area * 4 < GlobalMemoryInfo.dwAvailPhys div 2) or
(area <= Screen.Width*Screen.Height*4) then
try
FViewImage.Draw(bm.Canvas, r);
ok := true;
except
end;
// Gridlines for composition (taken from JK mod by Jed Kelsey)
if (EnableGuides) then begin
with bm.Canvas do begin
Pen.Width := 1;
Pen.Color := TColor(LineCenterColor); //$000000; // Center
MoveTo(0, bm.Height shr 1); LineTo(bm.Width, bm.Height shr 1);
MoveTo(bm.Width shr 1, 0); LineTo(bm.Width shr 1, bm.Height);
Pen.Color := TColor(LineThirdsColor); //$C000C0; // Thirds
gridp := Floor(bm.Height/3);
MoveTo(0, gridp); LineTo(bm.Width, gridp);
MoveTo(0, bm.Height-gridp); LineTo(bm.Width, bm.Height-gridp);
gridp := Floor(bm.Width/3);
MoveTo(gridp, 0); LineTo(gridp, bm.Height);
MoveTo(bm.Width-gridp, 0); LineTo(bm.Width-gridp, bm.Height);
Pen.Color := TColor(LineGRColor); //$0000F0; // "Golden Ratio" (per axis)
gridp := Floor(bm.Height * 0.61803399);
MoveTo(0, gridp); LineTo(bm.Width, gridp);
MoveTo(0, bm.Height-gridp); LineTo(bm.Width, bm.Height-gridp);
gridp := Floor(bm.Width * 0.61803399);
MoveTo(gridp, 0); LineTo(gridp, bm.Height);
MoveTo(bm.Width-gridp, 0); LineTo(bm.Width-gridp, bm.Height);
end;
end;
end;
if not ok then
with bm.Canvas do
begin
Font.Name := 'Wingdings'; // 'Arial';
Font.Height := bm.Height div 4;
Font.Color := $808080;
Brush.Style := bsClear;
i := (bm.Width - TextWidth(msg)) div 2;
j := (bm.Height - TextHeight(msg)) div 2;
Font.Color := 0;
TextOut(i+2,j+2, msg);
Font.Color := clWhite; //$808080;
TextOut(i,j, msg);
end;
Image.Picture.Graphic := bm;
//EditForm.PaintBackground;
Image.Refresh;
bm.Free;
end;
//***************************************************************************//
(*
procedure TMainForm.DrawPitchYawLines(YawAngle: double; PitchAngle: double);
var
bkuPen: TPen;
points: array[0..3] of TPoint;
i: integer;
begin
bkuPen := TPen.Create;
bkuPen.Assign(Image.Canvas.Pen);
Image.Canvas.Pen.Mode := pmXor;
Image.Canvas.Pen.Color := clWhite;
Image.Canvas.Pen.Style := psDot; //psDash;
Image.Canvas.Brush.Style := bsClear;
// Image.Canvas.Rectangle(FSelectRect);
points[0].x := 0;
points[0].y := round((Image.Height / 2) * sin(PitchAngle));
points[1].x := Image.Width - 1;
points[1].y := points[0].y;
points[2].x := points[1].x;
points[2].y := round((Image.Height) - ((Image.Height / 2) * sin(PitchAngle)));
points[3].x := points[0].x;
points[3].y := points[2].y;
Image.Canvas.MoveTo(Points[3].x, Points[3].y);
for i := 0 to 3 do begin
Image.Canvas.LineTo(Points[i].x, Points[i].y);
end;
Image.Canvas.Pen.Assign(bkuPen);
bkuPen.Free;
end;
*)
procedure TMainForm.DrawRotateLines(Angle: double);
var
bkuPen: TPen;
points: array[0..3] of TPoint;
i,x,y: integer;
begin
bkuPen := TPen.Create;
bkuPen.Assign(Image.Canvas.Pen);
Image.Canvas.Pen.Mode := pmXor;
Image.Canvas.Pen.Color := clWhite;
Image.Canvas.Pen.Style := psDot; //psDash;
Image.Canvas.Brush.Style := bsClear;
// Image.Canvas.Rectangle(FSelectRect);
points[0].x := (Image.Width div 2)-1;
points[0].y := (Image.Height div 2)-1;
points[1].x := (Image.Width div 2)-1;
points[1].y := -Image.Height div 2;
points[2].x := -Image.Width div 2;
points[2].y := -Image.Height div 2;
points[3].x := -Image.Width div 2;
points[3].y := (Image.Height div 2)-1;
for i := 0 to 3 do begin
x := points[i].x;
y := points[i].y;
points[i].x := round(cos(Angle) * x + sin(Angle) * y) + Image.Width div 2;
points[i].y := round(-sin(Angle) * x + cos(Angle) * y) + Image.Height div 2;
end;
Image.Canvas.MoveTo(Points[3].x, Points[3].y);
for i := 0 to 3 do begin
Image.Canvas.LineTo(Points[i].x, Points[i].y);
end;
Image.Canvas.Pen.Assign(bkuPen);
bkuPen.Free;
end;
//***************************************************************************//
procedure TMainForm.DrawZoomWindow;
const
cornerSize = 32;
var
bkuPen: TPen;
dx, dy, cx, cy: integer;
l, r, t, b: integer;
begin
bkuPen := TPen.Create;
bkuPen.Assign(Image.Canvas.Pen);
with Image.Canvas do begin
Pen.Mode := pmXor;
Pen.Color := clWhite;
Brush.Style := bsClear;
Pen.Style := psDot; //psDash;
if ssShift in FShiftState then
begin
dx := FClickRect.Right - FClickRect.Left;
dy := FClickRect.Bottom - FClickRect.Top;
Rectangle(FClickRect.Left - dx, FClickRect.Top - dy, FClickRect.Right, FClickRect.Bottom);
end
else Rectangle(FClickRect);
dx := FSelectRect.Right - FSelectRect.Left;
if dx >= 0 then begin
l := FSelectRect.Left - 1;
r := FSelectRect.Right;
end
else begin
dx := -dx;
l := FSelectRect.Right - 1;
r := FSelectRect.Left;
end;
dx := min(dx div 2 - 1, cornerSize);
dy := FSelectRect.Bottom - FSelectRect.Top;
if dy >= 0 then begin
t := FSelectRect.Top - 1;
b := FSelectRect.Bottom;
end
else begin
dy := -dy;
t := FSelectRect.Bottom - 1;
b := FSelectRect.Top;
end;
dy := min(dy div 2, cornerSize);
pen.Style := psSolid;
MoveTo(l + dx, t);
LineTo(l, t);
LineTo(l, t + dy);
MoveTo(r - dx, t);
LineTo(r, t);
LineTo(r, t + dy);
MoveTo(r - dx, b);
LineTo(r, b);
LineTo(r, b - dy);
MoveTo(l + dx, b);
LineTo(l, b);
LineTo(l, b - dy);
{
cx := (l + r) div 2;
cy := (t + b) div 2;
MoveTo(cx - dx div 2, cy);
LineTo(cx + dx div 2 + 1, cy);
MoveTo(cx, cy - dy div 2);
LineTo(cx, cy + dy div 2 + 1);
}
Pen.Assign(bkuPen);
end;
bkuPen.Free;
end;
//***************************************************************************//
procedure TMainForm.tbzoomwindowClick(Sender: TObject);
begin
FMouseMoveState := msZoomWindow;
end;
procedure TMainForm.tbzoomoutwindowClick(Sender: TObject);
begin
FMouseMoveState := msZoomOutWindow;
end;
procedure TMainForm.tbDragClick(Sender: TObject);
begin
FMouseMoveState := msDrag;
end;
procedure TMainForm.tbRotateClick(Sender: TObject);
begin
FMouseMoveState := msRotate;
end;
//***************************************************************************//
procedure TMainForm.FillVariantMenu;
var
i, j: smallint;
s: string;
NewMenuItem : TMenuItem;
svars: TStringList;
begin
SetLength(VarMenus, NrVar);
// AV: to prevent underlined letters with GUI themes
mnuBuiltinVars.AutoHotkeys := maManual;
mnuPluginVars.AutoHotkeys := maManual;
svars := TStringList.Create;
svars.Sorted := True;
for i := 0 to NRVAR - 1 do begin
NewMenuItem := TMenuItem.Create(self);
s := varnames(i);
NewMenuItem.Caption := uppercase(s[1]) + copy(s, 2, length(s)-1);
NewMenuItem.OnClick := VariantMenuClick;
NewMenuItem.Enabled := True;
NewMenuItem.Name := 'var' + intTostr(i);
NewMenuItem.Tag := i;
NewMenuItem.GroupIndex := 2;
NewMenuItem.RadioItem := True;
VarMenus[i] := NewMenuItem;
if i < NumBuiltinVars then begin // AV: creating sorted menu
j := svars.Add(NewMenuItem.Caption); // AV: remember the position...
mnuBuiltinVars.Insert(j, NewMenuItem); // ...and put it at the right place
end
else // AV: plugin variations are already sorted
mnuPluginVars.Add(NewMenuItem);
end;
svars.Free;
// AV: exotic GUI styles not always work well :-/
if TStyleManager.ActiveStyle.Name <> 'Windows' then
begin
i := 0;
while i < mnuBuiltinVars.Count do
begin
mnuBuiltinVars[i].Break := mbBreak;
inc(i, mbHeight);
end;
i := 0;
while i < mnuPluginVars.Count do
begin
mnuPluginVars[i].Break := mbBreak;
inc(i, mbHeight);
end;
end;
end;
procedure TMainForm.VariantMenuClick(Sender: TObject);
begin
TMenuItem(Sender).Checked := True;
// AV: only one variation type can be active,
// but Apo allows to check up to 3 menu items, confusing users...
if Variation > vRandom then
VarMenus[Variation].Checked := False
else
mnuVRandom.Checked := False;
if (TMenuItem(Sender).Tag >= NumBuiltinVars) then
begin
mnuBuiltinVars.Checked := False;
mnuPluginVars.Checked := True;
end
else begin
mnuBuiltinVars.Checked := True;
mnuPluginVars.Checked := False;
end;
UpdateUndo;
// AV: changed Variation to integer - no more ugly type-casting here!
Variation := TMenuItem(Sender).Tag;
SetVariation(maincp);
ResetLocation;
RedrawTimer.Enabled := True;
UpdateWindows;
end;
{ /////////////////////////////////////////////////////////////////////////// }
// AV: make it a separate method to be able to call it later
procedure TMainForm.SetAutoSaveTimer;
var mins: shortint;
begin
if (AutoSaveFreq = 0) then mins := 1
else if (AutoSaveFreq = 1) then mins := 2
else if (AutoSaveFreq = 2) then mins := 5
else if (AutoSaveFreq = 3) then mins := 10
else begin
mins := 5;
AutoSaveFreq := 2;
AutoSaveEnabled := false;
end;
AutoSaveTimer.Interval := 60 * 1000 * mins;
AutoSaveTimer.Enabled := AutoSaveEnabled;
end;
// AV: Apo UI Appearance /////////////////////////////
procedure TMainForm.CreateStyleList;
var i: smallint;
s: string;
apostyle : TMenuItem;
begin
for i := 0 to Length(TStyleManager.StyleNames)-1 do
begin
apostyle := TMenuItem.Create(mnuApoStyle);
s := TStyleManager.StyleNames[i];
apostyle.Caption := s;
if (TStyleManager.ActiveStyle.Name = s) then
apostyle.Checked := True;
apostyle.Name := 'style' + IntToStr(i);
apostyle.RadioItem := True;
apostyle.Enabled := True;
apostyle.Tag := i;
apostyle.OnClick := StyleItemClick;
mnuApoStyle.Add(apostyle);
end;
end;
procedure TMainForm.ShowStyledWindows(Sender: TObject);
begin
self.ApplyThemedColors;
ScriptEditor.AdjustScripterColors;
EditForm.RedrawButtons;
AboutForm.SetTitleColor;
end;
procedure TMainForm.StyleItemClick(Sender: TObject);
var
newGUI: string;
Registry: TRegistry;
begin
if not TMenuItem(Sender).Checked then
begin
TMenuItem(Sender).Checked := True;
newGUI := TMenuItem(Sender).Caption;
try
StopThread; // ?
self.OnShow := ShowStyledWindows;
if EditForm.Visible then EditForm.Close;
if AdjustForm.Visible then AdjustForm.Close;
if MutateForm.Visible then MutateForm.Close;
if ScriptEditor.Visible then ScriptEditor.Close;
TStyleManager.TrySetStyle(newGUI, false);
except on EAccessViolation do // hmmm...
MessageBox(0, PChar(TextByKey('options-restartnotice')),
ApophysisSVN, MB_ICONWARNING);
end;
Registry := TRegistry.Create;
try
Registry.RootKey := HKEY_CURRENT_USER;
if Registry.OpenKey('\Software\' + APP_NAME + '\Defaults', True) then
Registry.WriteString('UIStyle', newGUI);
Registry.CloseKey;
finally
Registry.Free;
end;
end;
end;
//--Z--////////////////////////////////////////////////////////////////////////
procedure TMainForm.tbQualityBoxKeyPress(Sender: TObject; var Key: Char);
begin
if (Key = ',') then Key := '.'; // AV
if not CharinSet(Key,['0'..'9', #8, #13, #27, '.']) then Key := #0; // AV
if key = #13 then
begin
tbQualityBoxSet(Sender);
key := #0;
end
else if key = #27 then // AV: Esc
tbQualityBox.Text := FloatToStr(defSampleDensity);
end;
procedure TMainForm.tbQualityBoxSet(Sender: TObject);
var
q: double;
begin
try
q := StrToFloat(tbQualityBox.Text);
except
exit;
end;
defSampleDensity := q;
StopThread;
RedrawTimer.Enabled := True;
UpdateWindows;
end;
///////////////////////////////////////////////////////////////////////////////
procedure TMainForm.ImageDblClick(Sender: TObject);
begin
if FMouseMoveState = msRotateMove then
begin
StopThread;
UpdateUndo;
MainCp.FAngle := 0;
RedrawTimer.Enabled := True;
UpdateWindows;
end
else mnuResetLocationClick(Sender);
end;
///////////////////////////////////////////////////////////////////////////////
procedure TMainForm.tbShowAlphaClick(Sender: TObject);
begin
//tbShowAlpha.Down := not tbShowAlpha.Down;
ShowTransparency := tbShowAlpha.Down;
DrawImageView;
end;
///////////////////////////////////////////////////////////////////////////////
procedure TMainForm.tbShowTraceClick(Sender: TObject);
begin
TraceForm.Show;
end;
///////////////////////////////////////////////////////////////////////////////
procedure TMainForm.FormKeyUpDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
MousePos: TPoint;
begin
if Shift <> FShiftState then begin
if FMouseMoveState in [msZoomWindowMove, msZoomOutWindowMove, msRotateMove, msDragMove] then
begin
// hack: to generate MouseMove event
GetCursorPos(MousePos);
SetCursorPos(MousePos.x, MousePos.y);
end;
if (FMouseMoveState in [msZoomWindowMove, msZoomOutWindowMove]) then
begin
DrawZoomWindow;
FShiftState := Shift;
DrawZoomWindow;
end
else FShiftState := Shift;
end;
end;
procedure TMainForm.btnViewIconsClick(Sender: TObject);
begin
ListView1.ViewStyle := vsIcon;
btnViewList.Down := false;
btnViewIcons.Down := true;
ClassicListMode := false;
// AV: refresh flame images ONLY if they didn't exist
if not GeneratingThumbs then RunThumbnailThread;
// AV: scroll down to the selected flame preview
if MainForm.ListView1.SelCount > 0 then
MainForm.ListView1.Selected.MakeVisible(True);
end;
procedure TMainForm.btnViewListClick(Sender: TObject);
begin
ListView1.ViewStyle := vsReport;
btnViewList.Down := true;
btnViewIcons.Down := false;
ClassicListMode := true;
ListView1.Column[1].Caption := IfThen(EnumerateFlames.Checked,' N ', '');
end;
procedure TMainForm.XmlScannerEndTag(Sender: TObject; TagName: String);
var sb : string;
begin
if (TagName = 'flame') then begin
EndParsing(ParseCP, sb);
MainForm.StatusBar.Panels[0].Text := sb;
end;
end;
procedure TMainForm.tbCurvesClick(Sender: TObject);
begin
AdjustForm.UpdateDisplay;
AdjustForm.PageControl.TabIndex:=4;
AdjustForm.Show;
end;
procedure TMainForm.tbMessagesClick(Sender: TObject);
begin
if (LoadForm.Showing = false) then LoadForm.Show;
end;
procedure TMainForm.btNewClick(Sender: TObject);
var saved: boolean;
begin
StopThread; // AV
if AlwaysCreateBlankFlame then
EditForm.mnuResetAllClick(Sender) // AV
else
if TemplateForm.ShowModal = mrOK then // AV
if AutoSaveXML then
// AV: create a flame from scratch (rather than replace the current) if needed
begin
MainCp.name := MainCp.name + FormatDateTime(' (MM-dd-yyyy hh-mm-ss)', Now);
if (OpenFileType = ftXML) then
saved := SaveXMLFlame(MainCp, MainCp.name, OpenFile)
else
saved := SaveFlame(MainCp, MainCp.name, OpenFile);
if saved then AddFlameToList; // AV: show the new item
end;
end;
procedure TMainForm.ToolBarResize(Sender: TObject);
begin
if (Toolbar.Width <= TbBreakWidth) then
Toolbar.Height := 60 // 26 * 2 + 8
else Toolbar.Height := 26;
end;
{
// AV: exactly the same code exists in the Global module
function Split(const fText: String; const fSep: Char; fTrim: Boolean=false; fQuotes: Boolean=false):TStringList;
var vI: Integer;
vBuffer: String;
vOn: Boolean;
begin
Result := TStringList.Create;
vBuffer:='';
vOn:=true;
for vI:=1 to Length(fText) do
begin
if (fQuotes and(fText[vI]=fSep)and vOn)or(Not(fQuotes) and (fText[vI]=fSep)) then
begin
if fTrim then vBuffer:=Trim(vBuffer);
if vBuffer='' then vBuffer:=fSep; // !!! e.g. split(',**',',')...
if vBuffer[1]=fSep then
vBuffer:=Copy(vBuffer,2,Length(vBuffer));
Result.Add(vBuffer);
vBuffer:='';
end;
if fQuotes then
begin
if fText[vI]='"' then
begin
vOn:=Not(vOn);
Continue;
end;
if (fText[vI]<>fSep)or((fText[vI]=fSep)and(vOn=false)) then
vBuffer:=vBuffer+fText[vI];
end else
if fText[vI]<>fSep then
vBuffer:=vBuffer+fText[vI];
end;
if vBuffer<>'' then
begin
if fTrim then vBuffer:=Trim(vBuffer);
Result.Add(vBuffer);
end;
end;
}
procedure TMainForm.mnuResetUIClick(Sender: TObject);
begin
ListBackPanel.Width := ThumbnailSize + 90;
Splitter.Left := ListBackPanel.Width;
ListView1.Columns[0].Width := ThumbnailSize + 30; // AV
ListView1.Columns[1].Width := 35; // AV
end;
procedure TMainForm.AutoSaveTimerTimer(Sender: TObject);
var
filename, title, bakname : string;
FileListPre: TStringList;
begin
filename := AutoSavePath;
title := CleanXMLName(maincp.name) + FormatDateTime(' (MM-dd-yyyy hh-mm-ss)', Now);
if FileExists(filename) then begin
FileListPre := TStringList.create;
try
FileListPre.LoadFromFile(filename);
if (FileListPre.Count > 100000) then // increased nr of flames
// AV: if user working on auto-saved flame, we must NOT delete it
if (AutoSavePath <> OpenFile) then
begin
bakname := ChangeFileExt(filename, '.tmp');
if FileExists(bakname) then DeleteFile(bakname);
RenameFile(filename, bakname);
end;
finally
FileListPre.Free;
end;
end;
if SaveXMLFlame(maincp, title, filename) then
// AV: added ListView updating
if (FileName = OpenFile) then AddFlameToList(title);
bakname := ChangeFileExt(filename, '.bak');
if FileExists(bakname) then DeleteFile(bakname);
end;
procedure TMainForm.Restorelastautosave1Click(Sender: TObject);
var fn: string;
begin
if (not FileExists(AutoSavePath)) then
raise Exception.Create(TextByKey('main-status-noautosave')); // AV
// StopScripter;
fn := AutoSavePath;
LastOpenFile := fn;
Maincp.name := ''; // AV: ?
ParamFolder := ExtractFilePath(fn);
OpenFile := fn;
if APP_BUILD = '' then
MainForm.Caption := AppVersionString + ' - ' + openFile
else
MainForm.Caption := AppVersionString + ' ' + APP_BUILD + ' - ' + openFile;
OpenFileType := ftXML;
ListXML(fn, 1)
end;
procedure TMainForm.mnuHelpTopicsClick(Sender: TObject);
// var URL, HelpTopic: string;
begin
{
if EditForm.Active then HelpTopic := 'Transform editor.htm'
else if AdjustForm.Active then HelpTopic := 'Adjust window.htm'
else if MutateForm.Active then HelpTopic := 'Mutation window.htm'
else if RenderForm.Active then HelpTopic := 'Render window.htm';
HtmlHelp(0, nil, HH_CLOSE_ALL, 0);
URL := AppPath + Application.HelpFile;
if HelpTopic <> '' then URL := URL + '::\' + HelpTopic;
HtmlHelp(0, PChar(URL), HH_DISPLAY_TOC, 0);
}
if (HelpPath <> '') then
begin
if not (FileExists(HelpPath)) then // AV
MessageBox(self.Handle, PCHAR(TextByKey('common-noparamfile')),
ApophysisSVN, MB_ICONHAND)
else
if (not WinShellExecute('open', HelpPath)) then begin
MessageBox(self.Handle, PCHAR(Format(TextByKey('common-genericopenfailure'),
[HelpPath])), ApophysisSVN, MB_ICONHAND);
end;
end else
MessageBox(self.Handle, PCHAR(TextByKey('main-status-nohelpfile')),
ApophysisSVN, 48);
end;
{
function TMainForm.RetrieveXML(cp : TControlPoint):string;
begin
// AV: commented out since we can call it directly
Result := FlameToXML(cp, false);
end;
}
procedure TMainForm.tbGuidesClick(Sender: TObject);
begin
// tbGuides.Down := not tbGuides.Down;
EnableGuides := tbGuides.Down;
DrawImageView;
end;
(*
function WinExecAndWait32(FileName: string): integer;
var
zAppName: array[0..1024] of Char;
zCurDir: array[0..255] of Char;
WorkDir: string;
StartupInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
r : dword;
begin
StrPCopy(zAppName, FileName);
GetDir(0, WorkDir);
StrPCopy(zCurDir, WorkDir);
FillChar(StartupInfo, Sizeof(StartupInfo), #0);
StartupInfo.cb := Sizeof(StartupInfo);
StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow := 0;
if (not CreateProcess(nil, zAppName, nil, nil, false, CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo, ProcessInfo)) then
Result := -1
else begin
WaitforSingleObject(ProcessInfo.hProcess, INFINITE);
GetExitCodeProcess(ProcessInfo.hProcess, r);
result := r;
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
end;
end;
*)
procedure TMainForm.mnuTurnFlameToScriptClick(Sender: TObject);
var
txt: string;
begin
txt := Trim(FlameToXML(Maincp));
ScriptEditor.ScriptFromFlame(txt);
ScriptEditor.Show;
end;
procedure TMainForm.RunThumbnailThread;
var
thumbs: TThumbnailThread;
begin
thumbs := TThumbnailThread.Create;
thumbs.Start; // AV: Resume method is deprecated here
GeneratingThumbs := True;
end;
constructor TThumbnailThread.Create;
begin
inherited create(True); // AV: don't run the thread immediately
FreeOnTerminate := true; // AV: fixed - someone forgot to free the memory
Trace2('Creating ThumbnailThread #' + IntToStr(self.ThreadID));
FlameItems := MainForm.ListView1.Items; // AV: keep the reference to flame list
end;
destructor TThumbnailThread.Destroy;
begin
// AV: added tracing to fix Apo7X memory leaks
Synchronize(
procedure
begin
Trace2('Destroying ThumbnailThread #' + IntToStr(self.ThreadID));
end);
inherited;
end;
procedure TThumbnailThread.Execute;
var
Renderer : TRenderer;
cp : TControlPoint;
Thumbnail : TBitmap;
flameXML, fCaption : string;
w, h: integer;
r : double;
Fitem: TListItem;
FlameProc: TProc;
begin
inherited;
Renderer := TRenderer.Create;
cp := TControlPoint.Create;
Thumbnail := TBitmap.Create;
try // AV: added try-finally block
// AV: moved outside the loop for speed
Thumbnail.SetSize(FThumbnailSize, FThumbnailSize);
Thumbnail.PixelFormat := pf24bit;
Thumbnail.HandleType := bmDIB;
Thumbnail.Canvas.Lock; // AV: added thread-safe handling
Thumbnail.Canvas.Brush.Color := WinColor; // AV: theme-aware GetSysColor(5);
if (OpenFileType = ftXML) then
FlameProc := procedure
begin
flameXML := LoadXMLFlameText(Openfile, fCaption);
MainForm.ParseXML(cp, flameXML, true);
end
else // added thumbs support for Undo (*.apo) flames
FlameProc := procedure
begin
FlameFromUndo(cp, fCaption, OpenFile);
end;
for Fitem in FlameItems do // hope this is more safety loop
begin
cp.Clear;
fCaption := Fitem.Caption;
FlameProc; // AV
r := cp.Width / cp.Height;
w := ThumbnailSize;
h := w;
if (r < 1) then w := round(r * w)
else if (r > 1) then h := round(h / r);
cp.AdjustScale(w, h);
cp.spatial_oversample := defOversample;
cp.spatial_filter_radius := defFilterRadius;
cp.sample_density := FPreviewDensity; // AV
Renderer.SetCP(cp);
Renderer.Render;
Thumbnail.Canvas.FillRect(Rect(0, 0, FThumbnailSize, FThumbnailSize));
Thumbnail.Canvas.Draw((ThumbnailSize - w) shr 1,
(ThumbnailSize - h) shr 1, Renderer.GetImage);
// AV: added thread synchronization for updating visual components
Synchronize(
procedure
begin
MainForm.UsedThumbnails.Add(Thumbnail, nil);
Fitem.ImageIndex := MainForm.UsedThumbnails.Count - 1;
Trace2('Generating thumbnail for "' + fCaption + '"');
end);
if Terminated then break; // AV
end;
finally
Thumbnail.Canvas.UnLock; // AV: added thread-safe handling
Thumbnail.Free;
Thumbnail := nil;
cp.Free;
Renderer.Free;
end;
end;
procedure ListXML(FileName: string; sel: integer; selname: string = '');
var
FStrings : TStringList;
i, p : integer;
title : string;
item : TListItem;
begin
MainForm.ParseLoadingBatch := true;
FStrings := TStringList.Create;
FStrings.LoadFromFile(FileName);
MainForm.ListView1.Items.BeginUpdate;
try
// AV: moved all the main code inside try-finally block
// because Apo often crashes here
MainForm.ListView1.Items.Clear;
// AV: moved from TThumbnailThread.Execute - seems like it saves a lot of time
MainForm.UsedThumbnails.Clear;
MainForm.UsedThumbnails.Add(ThumbnailPlaceholder, nil);
if (Pos('<flame ', Lowercase(FStrings.Text)) <> 0) then
begin
for i := 0 to FStrings.Count - 1 do
begin
p := Pos('<flame ', LowerCase(FStrings[i]));
if (p <> 0) then
begin
MainForm.ListXMLScanner.LoadFromBuffer(PAnsiChar(Utf8String(FStrings[i])));
MainForm.ListXMLScanner.Execute;
if Trim(pname) = '' then
Title := '*untitled ' + ptime
else
Title := Trim(pname);
if Title <> '' then
begin
if ((i mod 5) = 0) then
MainForm.LoadSaveProgress.Position :=
round(100 * i / FStrings.Count);
item := MainForm.ListView1.Items.Add;
item.Caption := Title;
item.ImageIndex := 0; // AV: now we can load a hourglass icon
// AV: hack - remember the creation order in an unused field
item.OverlayIndex := MainForm.ListView1.Items.Count;
end;
end;
end;
end;
MainForm.LoadSaveProgress.Position := 0;
//MainForm.ListView1.AllocBy := MainForm.ListView1.Items.Count;
if ClassicListMode then // AV: thumbs are useless
GeneratingThumbs := False
else
MainForm.RunThumbnailThread; // AV: wrap it into a separate method
finally
MainForm.ListView1.Items.EndUpdate;
FStrings.Free;
with MainForm.ListView1 do // AV
if Items.Count > 0 then // AV: added a check
case sel of
0: Selected := Items[Items.Count - 1];
1: Selected := Items[0];
2: if (selname <> '') then // AV: show the flame with the specified name
Selected := FindCaption(0, selname, false, true, false);
end;
if MainForm.EnumerateFlames.Checked then // AV: displaying indices
MainForm.EnumerateFlamesClick(MainForm.EnumerateFlames);
end;
MainForm.ParseLoadingBatch := false;
if AnimateForm.Visible then AnimateForm.UpdateControls; // AV
end;
// AV: the fast way to refresh ListView
procedure TMainForm.AddFlameToList(const title: string = '');
var
item: TListItem;
i: integer;
fname: string;
begin
if title = '' then fname := MainCp.name
else fname := title;
ListView1.Items.BeginUpdate;
try
// first check for duplicates
item := ListView1.FindCaption(max(ListView1.ItemIndex, 0), fname,
false, true, true);
if item <> nil then
ListView1.Items.Delete(item.Index);
// AV: temporary prevent all preview updates
ListView1.OnChange := nil;
ListView1.OnSelectItem := nil;
item := ListView1.Items.Add;
item.Caption := fname;
ListView1.Selected := ListView1.Items[item.Index];
UsedThumbnails.Add(ThumbnailPlaceholder, nil); // add dummy hourglass icon
item.ImageIndex := UsedThumbnails.Count - 1;
item.OverlayIndex := ListView1.Items.Count; // remember the creation order
// if some flames were deleted from the list, max index is greater than the last one
for i := 0 to ListView1.Items.Count - 1 do
if ListView1.Items[i].OverlayIndex > item.OverlayIndex then
item.OverlayIndex := ListView1.Items[i].OverlayIndex + 1;
ListView1.Selected := ListView1.Items[item.Index];
ListView1.ItemFocused := ListView1.Selected;
if GeneratingThumbs then RefreshThumbnail;
finally
// AV: restore the default event handlers
ListView1.OnChange := ListViewChange;
if ConfirmResetUndo then
ListView1.OnSelectItem := ListViewSelectItem;
ListView1.Items.EndUpdate;
end;
// refreshing flame indices
if EnumerateFlames.Checked then EnumerateFlamesClick(EnumerateFlames);
// scroll to the new item
if ListView1.Selected <> nil then ListView1.Selected.MakeVisible(true);
if AnimateForm.Visible then AnimateForm.UpdateControls;
end;
procedure TMainForm.RefreshThumbnail;
var
Renderer : TRenderer;
Thumbnail : TBitmap;
r, sd: double;
i, w, h, w_old, h_old: integer;
begin
if not Assigned(ListView1.Selected) then exit;
i := ListView1.Selected.ImageIndex;
if (i >= UsedThumbnails.Count) then exit;
w_old := Maincp.Width;
h_old := Maincp.Height;
r := w_old / h_old;
w := ThumbnailSize;
h := w;
if (r < 1) then w := round(r * w)
else if (r > 1) then h := round(h / r);
sd := Maincp.sample_density;
Maincp.AdjustScale(w, h);
Maincp.spatial_oversample := defOversample;
Maincp.spatial_filter_radius := defFilterRadius;
Maincp.sample_density := TThumbnailThread.FPreviewDensity;
Renderer := TRenderer.Create;
Thumbnail := TBitmap.Create;
try
Renderer.SetCP(Maincp);
Renderer.Render;
Thumbnail.PixelFormat := pf24bit;
Thumbnail.HandleType := bmDIB;
Thumbnail.SetSize(ThumbnailSize, ThumbnailSize);
Thumbnail.Canvas.Brush.Color := WinColor; // theme-aware system color
Thumbnail.Canvas.FillRect(Rect(0, 0, ThumbnailSize, ThumbnailSize));
Thumbnail.Canvas.Draw((ThumbnailSize - w) shr 1,
(ThumbnailSize - h) shr 1, Renderer.GetImage);
try
UsedThumbnails.Replace(i, Thumbnail, nil);
i := ListView1.Selected.Index;
ListView1.Items.Item[i].Update;
Trace2('Updating thumbnail for "' + ListView1.Items[i].Caption + '"');
except
ListView1.Items[i].ImageIndex := 0;
end;
finally
Thumbnail.Free;
Thumbnail := nil;
Renderer.Free;
// restore old settings
Maincp.AdjustScale(w_old, h_old);
Maincp.sample_density := sd;
end;
end;
procedure TMainForm.UpdateThumbnails; // AV: refreshes images only
var
i: integer;
begin
UsedThumbnails.Clear;
UsedThumbnails.Add(ThumbnailPlaceholder, nil);
with ListView1.Items do
begin
BeginUpdate;
for i := 0 to Count - 1 do Item[i].ImageIndex := 0; // hourglass icon
EndUpdate;
end;
RunThumbnailThread;
// hightlight the item if possible
ListView1.Selected := ListView1.ItemFocused;
end;
procedure TMainForm.SetThumbnailProperties; // AV
begin
if UseSmallThumbnails then
ThumbnailSize := 96
else
ThumbnailSize := 128;
UsedThumbnails.Height := ThumbnailSize;
UsedThumbnails.Width := ThumbnailSize;
TThumbnailThread.FThumbnailSize := ThumbnailSize;
LoadThumbnailPlaceholder(ThumbnailSize);
mnuResetUI.Click;
end;
procedure TMainForm.mnuReportFlameClick(Sender: TObject);
var
str: string;
i : integer;
begin
if (not LoadForm.Visible) then LoadForm.Show;
str := MainCP.name + #13#10 + StringOfChar('=', length(MainCP.name)) + #13#10 +
Format(TextByKey('main-report-transformcount'), [MainCp.NumXForms]) + #13#10 +
Format(TextByKey('main-report-finaltransform'), [IfThen(maincp.finalXformEnabled, TextByKey('common-yes'), TextByKey('common-no'))]) + #13#10 +
TextByKey('main-report-usedplugins');
MainCP.FillUsedPlugins;
if MainCp.used_plugins.Count = 0 then begin
LoadForm.Output.Text := LoadForm.Output.Text + #13#10 + str + ' ' + TextByKey('main-report-noplugins') + #13#10;
exit;
end;
for i := 0 to MainCP.used_plugins.Count-1 do
str := str + #13#10 + ' - ' + MainCP.used_plugins[i];
// AV: added 3D and DC status
str := str + #13#10 + TextByKey('main-report-directcoloring') + #32 +
IfThen((pos('dc', str) > 0) or (pos('falloff', str) > 0) or (pos('affine3D', str) > 0),
TextByKey('common-yes'), TextByKey('common-no'));
str := str + #13#10 + TextByKey('main-report-flame3d') + #32 +
IfThen((MainCP.cameraPitch <> 0) or (MainCP.cameraRoll <> 0) or (pos('_rotate_', str) > 0),
TextByKey('common-yes'), TextByKey('common-no'));
if MainCp.Comment <> '' then
str := str + #13#10 + TextByKey('common-comment') + ': '#13#10'"' +
MainCp.Comment + '"';
LoadForm.Output.Text := LoadForm.Output.Text + #13#10 + str + #13#10;
end;
procedure TMainForm.mnuExportChaoticaClick(Sender: TObject);
begin
MainCP.FillUsedPlugins;
C_ExecuteChaotica(FlameToXml(MainCp), MainCp.used_plugins, UseX64IfPossible);
end;
procedure TMainForm.mnuManualClick(Sender: TObject); // AV: Apo7X link is dead...
begin
// AV: first link is for Russian people only
// WinShellOpen('https://books.google.ru/books?id=PbMAAQAAQBAJ&printsec=frontcover&hl=ru#v=onepage&q&f=false');
WinShellOpen('https://www.amazon.com/Fractals-Everywhere-Dover-Books-Mathematics/dp/0486488705');
end;
procedure TMainForm.CalculateColorSpeed1Click(Sender: TObject); // AV
begin
StopThread;
UpdateUndo;
MainCp.CalculateColorSpeed;
RedrawTimer.Enabled := True;
UpdateWindows;
end;
procedure TMainForm.CalculateWeightsClick(Sender: TObject); // AV
begin
StopThread;
UpdateUndo;
MainCp.CalculateWeights;
RedrawTimer.Enabled := True;
UpdateWindows;
end;
procedure TMainForm.CreateSubstMap;
begin
// AV: set backward compatibility since both plugins crash the scripter
SubstSource.Add('epispiral'); SubstTarget.Add('Epispiral');
SubstSource.Add('epispiral_n'); SubstTarget.Add('Epispiral_n');
SubstSource.Add('epispiral_thickness'); SubstTarget.Add('Epispiral_thickness');
SubstSource.Add('epispiral_holes'); SubstTarget.Add('Epispiral_holes');
{ AV: Apo7X has a bug here: when a source variation
is avaliable as a plugin, the application internally sets both versions
(source and substitute). It gives wrong visual results. So I added a checking }
if (GetVariationIndex('cross2') < 0) then begin // only if plugin is not loaded
SubstSource.Add('cross2'); SubstTarget.Add('cross');
end;
if (GetVariationIndex('bwraps2') < 0) then begin
SubstSource.Add('bwraps2'); SubstTarget.Add('bwraps');
SubstSource.Add('bwraps2_cellsize'); SubstTarget.Add('bwraps_cellsize');
SubstSource.Add('bwraps2_space'); SubstTarget.Add('bwraps_space');
SubstSource.Add('bwraps2_gain'); SubstTarget.Add('bwraps_gain');
SubstSource.Add('bwraps2_inner_twist'); SubstTarget.Add('bwraps_inner_twist');
SubstSource.Add('bwraps2_outer_twist'); SubstTarget.Add('bwraps_outer_twist');
end;
if (GetVariationIndex('pre_bwraps2') < 0) then begin
SubstSource.Add('pre_bwraps2'); SubstTarget.Add('pre_bwraps');
SubstSource.Add('pre_bwraps2_cellsize'); SubstTarget.Add('pre_bwraps_cellsize');
SubstSource.Add('pre_bwraps2_space'); SubstTarget.Add('pre_bwraps_space');
SubstSource.Add('pre_bwraps2_gain'); SubstTarget.Add('pre_bwraps_gain');
SubstSource.Add('pre_bwraps2_inner_twist'); SubstTarget.Add('pre_bwraps_inner_twist');
SubstSource.Add('pre_bwraps2_outer_twist'); SubstTarget.Add('pre_bwraps_outer_twist');
end;
if (GetVariationIndex('post_bwraps2') < 0) then begin
SubstSource.Add('post_bwraps2'); SubstTarget.Add('post_bwraps');
SubstSource.Add('post_bwraps2_cellsize'); SubstTarget.Add('post_bwraps_cellsize');
SubstSource.Add('post_bwraps2_space'); SubstTarget.Add('post_bwraps_space');
SubstSource.Add('post_bwraps2_gain'); SubstTarget.Add('post_bwraps_gain');
SubstSource.Add('post_bwraps2_inner_twist'); SubstTarget.Add('post_bwraps_inner_twist');
SubstSource.Add('post_bwraps2_outer_twist'); SubstTarget.Add('post_bwraps_outer_twist');
end;
if (GetVariationIndex('bwraps7') < 0) then begin
SubstSource.Add('bwraps7'); SubstTarget.Add('bwraps');
SubstSource.Add('bwraps7_cellsize'); SubstTarget.Add('bwraps_cellsize');
SubstSource.Add('bwraps7_space'); SubstTarget.Add('bwraps_space');
SubstSource.Add('bwraps7_gain'); SubstTarget.Add('bwraps_gain');
SubstSource.Add('bwraps7_inner_twist'); SubstTarget.Add('bwraps_inner_twist');
SubstSource.Add('bwraps7_outer_twist'); SubstTarget.Add('bwraps_outer_twist');
end;
if (GetVariationIndex('logn') < 0) then begin
SubstSource.Add('logn'); SubstTarget.Add('log');
SubstSource.Add('logn_base'); SubstTarget.Add('log_base');
end;
if (GetVariationIndex('circleblur') < 0) then begin // AV
SubstSource.Add('circleblur'); SubstTarget.Add('blur_circle');
end;
if (GetVariationIndex('circle2') < 0) then begin // AV
SubstSource.Add('circle2'); SubstTarget.Add('blur_circle');
end;
if (GetVariationIndex('boarders') < 0) then begin // AV
SubstSource.Add('boarders'); SubstTarget.Add('boarders2');
end;
if (GetVariationIndex('dc_boarders') < 0) then begin // AV
SubstSource.Add('dc_boarders'); SubstTarget.Add('boarders2');
end;
if (GetVariationIndex('splits3D') < 0) then begin // AV
SubstSource.Add('splits3D'); SubstTarget.Add('splits');
SubstSource.Add('splits3D_x'); SubstTarget.Add('splits_x');
SubstSource.Add('splits3D_y'); SubstTarget.Add('splits_y');
SubstSource.Add('splits3D_z'); SubstTarget.Add('splits_z');
end;
if (GetVariationIndex('blob_fl') < 0) then // AV
begin
SubstSource.Add('blob_fl'); SubstTarget.Add('blob');
SubstSource.Add('blob_fl_high'); SubstTarget.Add('blob_fl_high');
SubstSource.Add('blob_fl_low'); SubstTarget.Add('blob_low');
SubstSource.Add('blob_fl_waves'); SubstTarget.Add('blob_waves');
end;
if (GetVariationIndex('twintrian2') < 0) then begin // AV
SubstSource.Add('twintrian2'); SubstTarget.Add('twintrian');
end;
if (GetVariationIndex('Z_disc2') < 0) then // AV
begin
SubstSource.Add('Z_disc2'); SubstTarget.Add('disc2');
SubstSource.Add('Z_disc2_rot'); SubstTarget.Add('disc2_rot');
SubstSource.Add('Z_disc2_twist'); SubstTarget.Add('disc2_twist');
end;
end;
function TMainForm.ReadWithSubst(Attributes: TAttrList; attrname: string): string;
var i: integer;
v: string; //TStringType;
begin
v := string(Attributes.Value(Utf8String(attrname)));
if (v <> '') then begin
Result := v;
Exit;
end;
for i := 0 to SubstTarget.Count - 1 do begin
if (SubstTarget[i] = attrname) then begin
v := string(Attributes.Value(Utf8String(SubstSource[i])));
if (v <> '') then begin
Result := v;
Exit;
end;
end;
end;
Result := '';
end;
end.