7860 lines
239 KiB
ObjectPascal
7860 lines
239 KiB
ObjectPascal
{
|
||
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.
|