diff --git a/src/Image.h b/src/Image.h --- a/src/Image.h +++ b/src/Image.h @@ -1,688 +1,688 @@ -/*hdr -** FILE: Image.h -** AUTHOR: Matthew Allen -** DATE: 1/8/97 -** DESCRIPTION: Image paint application -** -** Copyright (C) 1997-1998 Matthew Allen -** fret@memecode.com -*/ - -#include - -#include "lgi/common/Lgi.h" -#include "lgi/common/DragAndDrop.h" -#include "lgi/common/ToolTabBar.h" -#include "lgi/common/DocApp.h" -#include "lgi/common/Icc.h" -#include "lgi/common/OptionsFile.h" -#include "lgi/common/Path.h" -#include "lgi/common/ZoomView.h" -#include "lgi/common/Box.h" -#include "lgi/common/Scripting.h" +/*hdr +** FILE: Image.h +** AUTHOR: Matthew Allen +** DATE: 1/8/97 +** DESCRIPTION: Image paint application +** +** Copyright (C) 1997-1998 Matthew Allen +** fret@memecode.com +*/ + +#include + +#include "lgi/common/Lgi.h" +#include "lgi/common/DragAndDrop.h" +#include "lgi/common/ToolTabBar.h" +#include "lgi/common/DocApp.h" +#include "lgi/common/Icc.h" +#include "lgi/common/OptionsFile.h" +#include "lgi/common/Path.h" +#include "lgi/common/ZoomView.h" +#include "lgi/common/Box.h" +#include "lgi/common/Scripting.h" #include "lgi/common/HashDom.h" - -#include "../src/common/Coding/ScriptingPriv.h" - -#include "resdefs.h" -extern char AppName[]; - -//////////////////////////////////////////////////////////////////////////////////////////// -// Defines -#define OPT_ErrorMsg "ErrorMsg" -#define OPT_Cancel "Cancel" -#define OPT_ToolsOpen "ToolOpen" -#define OPT_BackgroundColour "BgCol" -#define OPT_Lang "CurLang" - -// Default tool button sizes -#define TBS_X 19 -#define TBS_Y 18 - -// Ver -#define IMAGE_VER "1.11" - -// window messages -#define IDM_EDITPALETTE 108 -#define IDM_NEWWINDOW 109 - -#define IDM_FULLSCREEN 130 -#define IDM_FITTOIMAGE 131 -#define IDM_INFO 132 -#define IDM_TOGGLEPALETTE 133 -#define IDM_VIEWPALETTE 134 -#define IDM_TRANSFORM 135 -#define IDM_DIST 136 -#define IDM_PROFILE 137 - -#define IDM_CHANGE_CD 140 -#define IDM_LOAD_PAL 143 -#define IDM_SAVE_PAL 144 -#define IDM_EDIT_PAL 145 -#define IDM_RGB_PAL 146 -#define IDM_BGR_PAL 147 - -#define IDM_1BIT 150 -#define IDM_4BIT 151 -#define IDM_8BIT 152 -#define IDM_24BIT 153 - -#define IDM_SCAN_NONE 160 -#define IDM_SCAN_BYTE 161 -#define IDM_SCAN_SHORT 162 -#define IDM_SCAN_DWORD 163 - -#define IDM_Y_NORMAL 170 -#define IDM_Y_FLIP 171 - -#define IDM_FREEMEM 192 - -#define IDM_TEXTEDIT 200 -#define IDM_CUT 203 // not used -#define IDM_FIND 206 -#define IDM_GLYPHS 207 -#define IDM_NEWFONTEDITOR 208 -#define IDM_IMPORT 209 -#define IDM_EXPORT 209 -#define IDM_LOAD_FONT 210 -#define IDM_PROPERTIES 211 -#define IDM_ZOOM_X 212 -#define IDM_ZOOM_Y 213 -#define IDM_VIEW_X 214 -#define IDM_VIEW_Y 215 - -// Image messages -enum ImageEvents -{ - IDM_SCALE_BASE = M_USER + 2000, - IDM_LANG_BASE = M_USER + 3000, - IDM_SCRIPTS_BASE = M_USER + 4000, - M_OUTPUT = M_USER + 5000, - M_LOAD, -}; - -/////////////////////////////////////// -// misc -#define DEFAULT_X 500 -#define DEFAULT_Y 400 - -#define STATUS_NORMAL 0 -#define STATUS_COLOURINFO 1 -#define STATUS_POSINFO 2 -#define STATUS_DOCINFO 3 -#define STATUS_MAX 4 - -#define RECENT_FILES 10 - -#define VIEW_PULSE_RATE 100 - -// classes -#define IDM_OV_DRIVE 100 -#define IDM_OV_START 200 -#define IDM_OV_END 299 -#define IDM_OV_CLOSE 300 -#define IDM_OV_EXIT 301 - -#define OV_SELECTED 0x0001 -#define OV_CURSOR 0x0002 - -// Events -#define COL_CTRL_SET (M_USER + 600) -#define COL_CTRL_GET (M_USER + 601) -#define COL_CTRL_GET_BITS (M_USER + 603) -#define COL_CTRL_SET_PAL (M_USER + 604) -#define COL_CTRL_GET_PAL (M_USER + 605) - -#define GR_MIN_SPACER 1 -#define GR_SPACER_WID 5 - -#define GREY_SQUARE_SHIFT 5 - -// Tool defines -#define IDM_TOOL_EVENT 2000 -#define IDM_TOOL_SET_WORKSPACE (IDM_TOOL_EVENT + 0) -#define IEV_SET_FORE (IDM_TOOL_EVENT + 1) -#define IEV_SET_BACK (IDM_TOOL_EVENT + 2) -#define IEV_RETRACT (IDM_TOOL_EVENT + 3) -#define IEV_UPDATE_DC (IDM_TOOL_EVENT + 4) -#define IEV_SET_OP (IDM_TOOL_EVENT + 5) - -#define TOOL_PRIMITIVES 0 -#define TOOL_BRUSH (TOOL_PRIMITIVES + 0) -#define TOOL_SELECT_BRUSH (TOOL_PRIMITIVES + 1) -#define TOOL_EYE_DROPPER (TOOL_PRIMITIVES + 2) -#define TOOL_ZOOM (TOOL_PRIMITIVES + 3) -#define TOOL_TEXT (TOOL_PRIMITIVES + 4) -#define TOOL_FLOOD (TOOL_PRIMITIVES + 5) -#define TOOL_FREEHAND (TOOL_PRIMITIVES + 6) -#define TOOL_LINE (TOOL_PRIMITIVES + 7) -#define TOOL_ELLIPSE (TOOL_PRIMITIVES + 8) -#define TOOL_RECTANGLE (TOOL_PRIMITIVES + 9) -#define TOOL_POLYGON (TOOL_PRIMITIVES + 10) -#define TOOL_ERASE (TOOL_PRIMITIVES + 11) -#define TOOLSET_MAX (TOOL_PRIMITIVES + 12) - -// #define TOOL_AIRBRUSH (TOOL_PRIMITIVES + 5) - -#define TOOL_OPTIONS (TOOL_PRIMITIVES + 14) -#define TOOL_GUIDELINES (TOOL_OPTIONS + 0) -#define TOOL_FILL (TOOL_OPTIONS + 1) -#define TOOL_GRID (TOOL_OPTIONS + 2) -#define TOOL_GRIDINC (TOOL_OPTIONS + 3) -#define TOOL_TRANSPARENT_PASTE (TOOL_OPTIONS + 4) -#define TOOL_DRAW_ALPHA (TOOL_OPTIONS + 5) - -#define TOOL_OTHER (TOOL_OPTIONS + 6) -#define TOOL_UNDO (TOOL_OTHER + 0) -#define TOOL_REDO (TOOL_OTHER + 1) -#define TOOL_PALETTE (TOOL_OTHER + 2) -#define TOOL_MAX (TOOL_OTHER + 3) - -#define IMGT_NONE 0 -#define IMGT_LINE 1 -#define IMGT_RECTANGLE 2 -#define IMGT_CIRCLE 3 -#define IMGT_SQUARE 4 -#define IMGT_ELLIPSE 5 - -// This controls the speed of scrolling when the mouse is -// being dragged outside the bounds of the current window. -#define IMAGE_SELECTION_MS 100 - -//////////////////////////////////////////////////////////////////////////////////////////// -#define IDC_TOOLS 800 - -//////////////////////////////////////////////////////////////////////////////////////////// -// Options -#define OPT_DisplayGrid "DspGrid" -#define OPT_GridSize "GridSize" -#define OPT_DisplayTile "DspTile" -#define OPT_TileType "TileType" -#define OPT_TileX "Tx" -#define OPT_TileY "Ty" - -//////////////////////////////////////////////////////////////////////////////////////////// -// Other includes -#include "lgi/common/GdcTools.h" -#include "ImageCommon.h" - -//////////////////////////////////////////////////////////////////////////////////////////// -// Classes -class ImageApp; -class ImgView; -class ImgDocument; -class ImgTool; -class LColourCtrl; -class ImgWnd; - -//////////////////////////////////////////////////////////////////////////////////// -// UI Headers -#include "UiPaletteView.h" -#include "lgi/common/ProgressStatusPane.h" - -class ImgMemDC : public LMemDC -{ -public: - ImgMemDC(int x = 0, int y = 0, LColourSpace cs = CsNone) : - LMemDC(x, y, cs) {} - ImgMemDC(LSurface *pDC) : - LMemDC(pDC) {} - ~ImgMemDC(); - - bool GetVariant(const char *Name, LVariant &Value, const char *Array = 0); - bool SetVariant(const char *Name, LVariant &Value, const char *Array = 0); -}; - -// Generic Document -class ImgDocument : public LBase -{ -protected: - LAutoPtr pDC; - LAutoPtr ColourProfile; - LAutoPtr F; - LArray Views; - Progress *Meter; - bool Dirty; - int ZoomViewIdx; - -public: - LXmlTag Props; - - ImgDocument(ImgMemDC *new_dc = NULL); - ~ImgDocument(); - - ImgMemDC *GetDC() { return pDC; } - ImgMemDC *ReleaseDC() { return pDC.Release(); } - void SetDC(LAutoPtr pDC); - - void AddView(ImgView *v); - void RemoveView(ImgView *v); - - void Update(LRect *Where = NULL); - int GetZoom(); - void SetZoom(int z); - - LIccProfile *GetColourProfile() { return ColourProfile; } - void SetColourProfile(LAutoPtr Cp) { ColourProfile = Cp; } - - bool IsDirty() { return Dirty; } - const char *Name(); - bool Name(const char *p); - Progress *GetProgress() { return Meter; } - void SetProgress(Progress *Prg) { Meter = Prg; } - void VerifyExtension(LString &FileName, const char *Type); - - void Save(LView *Parent, const char *Name, const char *Type, LDom *Dom, std::function Callback); - void ContinueSave(LString File, std::function Callback); - bool Load(LView *Parent, const char *Name, const char *Type = NULL); -}; - -struct ImgKey : public LKey -{ - LPointF pt; - ImgView *view; - - ImgKey(LKey &k, ImgView *v) - { - ((LKey&)*this) = k; - view = v; - } -}; - -struct ImgMouse : public LMouse -{ - LPointF pt; - LPoint ViewPt; - ImgView *view; - - ImgMouse(LMouse &m, ImgView *v) - { - ((LMouse&)*this) = m; - ViewPt.x = m.x; - ViewPt.y = m.y; - view = v; - } - - ImgMouse(const ImgMouse &m) : LMouse(m) - { - pt.x = m.pt.x; - pt.y = m.pt.y; - ViewPt = m.ViewPt; - view = m.view; - } -}; - -// Virtual base class for image view events and settings -class ImageViewTarget : public LZoomViewCallback -{ -public: - virtual ~ImageViewTarget() {} - - virtual void UserKey(ImgKey &k) = 0; - virtual void UserMouseClick(ImgMouse &m) = 0; - virtual void UserMouseEnter(ImgMouse &m) = 0; - virtual void UserMouseExit(ImgMouse &m) = 0; - virtual void UserMouseMove(ImgMouse &m) = 0; - virtual ImgTool *GetCurrentTool() = 0; -}; - -// Bitmap View Type -class ImgView : public LZoomView -{ -protected: - ImageViewTarget *App; - LMouse Last; - - int MouseFlags; - int ScrollX, ScrollY; - -public: - ImgView(ImageViewTarget *p) : LZoomView(p) - { - App = p; - ScrollX = 0; - ScrollY = 0; - } - ~ImgView() - { - } - - void OnMouseMove(LMouse &m); - void OnMouseClick(LMouse &m); - void OnMouseEnter(LMouse &m); - void OnMouseExit(LMouse &m); - bool OnKey(LKey &k); - void OnPulse(); - ImageViewTarget *GetApp() { return App; } -}; - -class ImgEvent -{ - // Image palette info - LPalette *Palette; - - // Image delta info - LAutoPtr Data; - int Length; - -public: - ImgEvent(LSurface *From, LSurface *To, LRect &Bounds); - virtual ~ImgEvent(); - - bool Apply(LSurface *pDC); - uint Sizeof(); -}; - -// The main window -#ifdef MainWnd -#undef MainWnd -#endif -#define MainWnd ((ImgWnd*)LAppInst->AppWnd) - -class ImgWnd : public LDocApp, public LDom, public ImageViewTarget -{ -protected: - class ImgWndPrivate *d = NULL; - - // Doc - ImgDocument Doc; - - // Views ------------------------------------- - LBox *Splitter = NULL; - // contains... - ImgView *Zoom = NULL; - ImgView *Normal = NULL; - //-------------------------------------------- - LStatusBar *Status = NULL; - // contains... - LStatusPane *StatusInfo[STATUS_MAX]; - LProgressStatusPane *Meter = NULL; - //-------------------------------------------- - LSubMenu *File = NULL; - LSubMenu *RecentFilesMenu = NULL; - LSubMenu *Edit = NULL; - LSubMenu *Image = NULL; - LSubMenu *BrushMenu = NULL; - LSubMenu *ToolsMenu = NULL; - LSubMenu *Adjust = NULL; - LSubMenu *Macro = NULL; - LSubMenu *Help = NULL; - //-------------------------------------------- - LToolBar *Commands = NULL; - // contains... - LCommand ImageProperties; - LCommand FileOpen; - LCommand FileSave; - // Separator - LCommand Cut; - LCommand Copy; - LCommand Paste; - // Separator - LCommand OptGuideLines; - LCommand OptFillPrimitives; - LCommand OptShowGrid; - LCommand GridSizeInc; - LCommand OptTransparentPaste; - // Separator - LCommand EnableUndo; - LCommand DiscardUndo; - LCommand Undo; - LCommand Redo; - //-------------------------------------------- - LPaletteUI *Palette = NULL; - //-------------------------------------------- - class ImgTools *Toolbar = NULL; - // contains... - LCommand Cmd[TOOL_MAX]; - //-------------------------------------------- - // Tools - int CurrentTool = 0; - ImgTool *Current = NULL; - ImgTool *Tools[TOOL_MAX] = {}; - //-------------------------------------------- - // Options and Brush - int Operator = 0; - bool UseFore = true; - bool FillPrim = true; - bool ShowGrid = true; - int AlphaLevel = 255; - LAutoPtr BrushMap; - - void PostPasteBrush(LSurface *pBrush); - - // Stuff - int OpLevel = 0; - bool QuitWhenDone = false; - ImgView *ClickedView = NULL; - - bool GetVariant(const char *Name, LVariant &Value, const char *Array = 0); - bool SetVariant(const char *Name, LVariant &Value, const char *Array = 0); - - // Temporary variables - LPoint MouseStart; - LPoint LastOffset; - - // Undo - bool UndoEnabled = true; - int UndoPos = 0; - int CaptureUndoLevel; // if 0 { capture the undo info } - List UndoQueue; - LSurface *LastFrame = NULL; - LRect LastUpdate; - - // UI - void SetupUi(); - void OnDcChange(); - void OnUndoQueueChange(); - void PushUndoEvent(ImgEvent *u); - void EmptyUndoQueue(); - void DrawOnAlpha(); - void UpdateBackgroundMenu(); - -public: - ImgWnd(); - ~ImgWnd(); - - // Stuff - void StartOp(); - void EndOp(); - bool OnRequestClose(bool OsShuttingDown); - ImgTool *GetCurrentTool() { return Current; } - void RunScript(LViewI *Parent, char *Script, const char *FileName, bool Debug); - bool OpenHelp(const char *File = 0); - void UpdateScriptsMenu(); - - // Properties - int Op(); - void Op(int o); - LSurface *Brush(); - void Brush(LAutoPtr Ptr); - LSurface *GetDoc(); - void SetDoc(LSurface *s); - Progress *GetProgress() { return Doc.GetProgress(); } - - // Colour - ImgCol Fore(); - void Fore(ImgCol c); - ImgCol Back(); - void Back(ImgCol c); - bool GetColour(ImgCol &c, bool Fore = true); - COLOUR GetColour32(bool Fore = true); - void DrawBackground(LZoomView *View, LSurface *pDC, LPoint Offset, LRect *Src); - void DrawForeground(LZoomView *View, LSurface *Dst, LPoint Offset, LRect *Src); - - bool TransparentPaste(); - void TransparentPaste(bool b); - bool UseForeColour(); - void UseForeColour(bool b); - int Alpha(); - void Alpha(int i); - bool FillPrimitives(); - void FillPrimitives(bool b); - bool CrossHairs(); - void CrossHairs(bool b); - - #define DefOption(_type, _name, _opt, _def) \ - _type _name() { LVariant v = _def; GetOptions()->GetValue(_opt, v); return v.CastInt32(); } \ - void _name(_type i) { LVariant v; GetOptions()->SetValue(_opt, v = i); OnSettingChange(); } - - DefOption(int, DisplayGrid, OPT_DisplayGrid, true); - DefOption(int, GridSize, OPT_GridSize, 8); - DefOption(int, DisplayTile, OPT_DisplayTile, true); - DefOption(int, TileType, OPT_TileType, 0); - DefOption(int, TileX, OPT_TileX, 16); - DefOption(int, TileY, OPT_TileY, 16); - - // --------------------------------------------------------------------- - // Methods - void SetStatusText(const char *Text, int Pane = 0); - void ZoomCenter(int x, int y); - void ZoomTo(LRect r); - - // An action is a logical group of things that the user - // would expect to get undone together - virtual void BeforeAction(); - - // BeforeUpdate is before the tool modifies the DC - // selects in all the required colours and paint options - virtual void BeforeUpdate(LRect *a = NULL, bool PaletteChange = false); - - // Update is after the tool has modified the DC - virtual void Update(LRect *a = NULL, bool Now = false); - - // After all related updates have taken place - virtual void AfterAction(); - - // --------------------------------------------------------------------- - // Tool Events - void UserKey(ImgKey &k); - void UserMouseClick(ImgMouse &m); - void UserMouseEnter(ImgMouse &m); - void UserMouseExit(ImgMouse &m); - void UserMouseMove(ImgMouse &m); - - // --------------------------------------------------------------------- - // Methods - LView *GetClickedView() { return ClickedView; } - ImgMemDC *GetDC(); - LAutoPtr ReleaseDC(); - bool SetDC(LAutoPtr pDC); - void AttachTool(int i = -1); - void DetachTool(); - bool SerializeFile(LAutoPtr &pDC, const char *FileName, bool Write); - void OffsetImage(int x, int y); - - LDom *GetUserDom(); - void SetUserDom(LDom *b); - - bool Empty(); - bool OpenFile(const char *FileName, bool Ro); - void SaveFile(const char *FileName, std::function Callback); - bool SerializeOptions(LOptionsFile *Options, bool Write); - void GetFileTypes(LFileSelect *Dlg, bool Write); - - // --------------------------------------------------------------------- - // Window - int OnNotify(LViewI *Ctrl, LNotification n); - LMessage::Result OnEvent(LMessage *Msg); - int OnCommand(int Cmd, int Event, OsView Handle); - void OnReceiveFiles(LArray &Files); - void OnCreate(); - bool OnMouseWheel(double Lines); - void OnSettingChange(); - bool OnKey(LKey &k); -}; - -/// The image <-> script glue class -class ImageScriptContext : public LScriptContext -{ - ImgWnd *App; - LBrush *Brush; - LHashTbl,LPath*> Paths; - LHashTbl,LSurface*> Bitmaps; - LViewI *Parent; - LFont *Fnt; - LScriptEngine *Engine; + +#include "../src/common/Coding/ScriptingPriv.h" + +#include "resdefs.h" +extern char AppName[]; + +//////////////////////////////////////////////////////////////////////////////////////////// +// Defines +#define OPT_ErrorMsg "ErrorMsg" +#define OPT_Cancel "Cancel" +#define OPT_ToolsOpen "ToolOpen" +#define OPT_BackgroundColour "BgCol" +#define OPT_Lang "CurLang" + +// Default tool button sizes +#define TBS_X 19 +#define TBS_Y 18 + +// Ver +#define IMAGE_VER "1.11" + +// window messages +#define IDM_EDITPALETTE 108 +#define IDM_NEWWINDOW 109 + +#define IDM_FULLSCREEN 130 +#define IDM_FITTOIMAGE 131 +#define IDM_INFO 132 +#define IDM_TOGGLEPALETTE 133 +#define IDM_VIEWPALETTE 134 +#define IDM_TRANSFORM 135 +#define IDM_DIST 136 +#define IDM_PROFILE 137 + +#define IDM_CHANGE_CD 140 +#define IDM_LOAD_PAL 143 +#define IDM_SAVE_PAL 144 +#define IDM_EDIT_PAL 145 +#define IDM_RGB_PAL 146 +#define IDM_BGR_PAL 147 + +#define IDM_1BIT 150 +#define IDM_4BIT 151 +#define IDM_8BIT 152 +#define IDM_24BIT 153 + +#define IDM_SCAN_NONE 160 +#define IDM_SCAN_BYTE 161 +#define IDM_SCAN_SHORT 162 +#define IDM_SCAN_DWORD 163 + +#define IDM_Y_NORMAL 170 +#define IDM_Y_FLIP 171 + +#define IDM_FREEMEM 192 + +#define IDM_TEXTEDIT 200 +#define IDM_CUT 203 // not used +#define IDM_FIND 206 +#define IDM_GLYPHS 207 +#define IDM_NEWFONTEDITOR 208 +#define IDM_IMPORT 209 +#define IDM_EXPORT 209 +#define IDM_LOAD_FONT 210 +#define IDM_PROPERTIES 211 +#define IDM_ZOOM_X 212 +#define IDM_ZOOM_Y 213 +#define IDM_VIEW_X 214 +#define IDM_VIEW_Y 215 + +// Image messages +enum ImageEvents +{ + IDM_SCALE_BASE = M_USER + 2000, + IDM_LANG_BASE = M_USER + 3000, + IDM_SCRIPTS_BASE = M_USER + 4000, + M_OUTPUT = M_USER + 5000, + M_LOAD, +}; + +/////////////////////////////////////// +// misc +#define DEFAULT_X 500 +#define DEFAULT_Y 400 + +#define STATUS_NORMAL 0 +#define STATUS_COLOURINFO 1 +#define STATUS_POSINFO 2 +#define STATUS_DOCINFO 3 +#define STATUS_MAX 4 + +#define RECENT_FILES 10 + +#define VIEW_PULSE_RATE 100 + +// classes +#define IDM_OV_DRIVE 100 +#define IDM_OV_START 200 +#define IDM_OV_END 299 +#define IDM_OV_CLOSE 300 +#define IDM_OV_EXIT 301 + +#define OV_SELECTED 0x0001 +#define OV_CURSOR 0x0002 + +// Events +#define COL_CTRL_SET (M_USER + 600) +#define COL_CTRL_GET (M_USER + 601) +#define COL_CTRL_GET_BITS (M_USER + 603) +#define COL_CTRL_SET_PAL (M_USER + 604) +#define COL_CTRL_GET_PAL (M_USER + 605) + +#define GR_MIN_SPACER 1 +#define GR_SPACER_WID 5 + +#define GREY_SQUARE_SHIFT 5 + +// Tool defines +#define IDM_TOOL_EVENT 2000 +#define IDM_TOOL_SET_WORKSPACE (IDM_TOOL_EVENT + 0) +#define IEV_SET_FORE (IDM_TOOL_EVENT + 1) +#define IEV_SET_BACK (IDM_TOOL_EVENT + 2) +#define IEV_RETRACT (IDM_TOOL_EVENT + 3) +#define IEV_UPDATE_DC (IDM_TOOL_EVENT + 4) +#define IEV_SET_OP (IDM_TOOL_EVENT + 5) + +#define TOOL_PRIMITIVES 0 +#define TOOL_BRUSH (TOOL_PRIMITIVES + 0) +#define TOOL_SELECT_BRUSH (TOOL_PRIMITIVES + 1) +#define TOOL_EYE_DROPPER (TOOL_PRIMITIVES + 2) +#define TOOL_ZOOM (TOOL_PRIMITIVES + 3) +#define TOOL_TEXT (TOOL_PRIMITIVES + 4) +#define TOOL_FLOOD (TOOL_PRIMITIVES + 5) +#define TOOL_FREEHAND (TOOL_PRIMITIVES + 6) +#define TOOL_LINE (TOOL_PRIMITIVES + 7) +#define TOOL_ELLIPSE (TOOL_PRIMITIVES + 8) +#define TOOL_RECTANGLE (TOOL_PRIMITIVES + 9) +#define TOOL_POLYGON (TOOL_PRIMITIVES + 10) +#define TOOL_ERASE (TOOL_PRIMITIVES + 11) +#define TOOLSET_MAX (TOOL_PRIMITIVES + 12) + +// #define TOOL_AIRBRUSH (TOOL_PRIMITIVES + 5) + +#define TOOL_OPTIONS (TOOL_PRIMITIVES + 14) +#define TOOL_GUIDELINES (TOOL_OPTIONS + 0) +#define TOOL_FILL (TOOL_OPTIONS + 1) +#define TOOL_GRID (TOOL_OPTIONS + 2) +#define TOOL_GRIDINC (TOOL_OPTIONS + 3) +#define TOOL_TRANSPARENT_PASTE (TOOL_OPTIONS + 4) +#define TOOL_DRAW_ALPHA (TOOL_OPTIONS + 5) + +#define TOOL_OTHER (TOOL_OPTIONS + 6) +#define TOOL_UNDO (TOOL_OTHER + 0) +#define TOOL_REDO (TOOL_OTHER + 1) +#define TOOL_PALETTE (TOOL_OTHER + 2) +#define TOOL_MAX (TOOL_OTHER + 3) + +#define IMGT_NONE 0 +#define IMGT_LINE 1 +#define IMGT_RECTANGLE 2 +#define IMGT_CIRCLE 3 +#define IMGT_SQUARE 4 +#define IMGT_ELLIPSE 5 + +// This controls the speed of scrolling when the mouse is +// being dragged outside the bounds of the current window. +#define IMAGE_SELECTION_MS 100 + +//////////////////////////////////////////////////////////////////////////////////////////// +#define IDC_TOOLS 800 + +//////////////////////////////////////////////////////////////////////////////////////////// +// Options +#define OPT_DisplayGrid "DspGrid" +#define OPT_GridSize "GridSize" +#define OPT_DisplayTile "DspTile" +#define OPT_TileType "TileType" +#define OPT_TileX "Tx" +#define OPT_TileY "Ty" + +//////////////////////////////////////////////////////////////////////////////////////////// +// Other includes +#include "lgi/common/GdcTools.h" +#include "ImageCommon.h" + +//////////////////////////////////////////////////////////////////////////////////////////// +// Classes +class ImageApp; +class ImgView; +class ImgDocument; +class ImgTool; +class LColourCtrl; +class ImgWnd; + +//////////////////////////////////////////////////////////////////////////////////// +// UI Headers +#include "UiPaletteView.h" +#include "lgi/common/ProgressStatusPane.h" + +class ImgMemDC : public LMemDC +{ +public: + ImgMemDC(int x = 0, int y = 0, LColourSpace cs = CsNone) : + LMemDC(x, y, cs) {} + ImgMemDC(LSurface *pDC) : + LMemDC(pDC) {} + ~ImgMemDC(); + + bool GetVariant(const char *Name, LVariant &Value, const char *Array = 0); + bool SetVariant(const char *Name, LVariant &Value, const char *Array = 0); +}; + +// Generic Document +class ImgDocument : public LBase +{ +protected: + LAutoPtr pDC; + LAutoPtr ColourProfile; + LAutoPtr F; + LArray Views; + Progress *Meter; + bool Dirty; + int ZoomViewIdx; + +public: + LXmlTag Props; + + ImgDocument(ImgMemDC *new_dc = NULL); + ~ImgDocument(); + + ImgMemDC *GetDC() { return pDC; } + ImgMemDC *ReleaseDC() { return pDC.Release(); } + void SetDC(LAutoPtr pDC); + + void AddView(ImgView *v); + void RemoveView(ImgView *v); + + void Update(LRect *Where = NULL); + int GetZoom(); + void SetZoom(int z); + + LIccProfile *GetColourProfile() { return ColourProfile; } + void SetColourProfile(LAutoPtr Cp) { ColourProfile = Cp; } + + bool IsDirty() { return Dirty; } + const char *Name(); + bool Name(const char *p); + Progress *GetProgress() { return Meter; } + void SetProgress(Progress *Prg) { Meter = Prg; } + void VerifyExtension(LString &FileName, const char *Type); + + void Save(LView *Parent, const char *Name, const char *Type, LDom *Dom, std::function Callback); + void ContinueSave(LString File, std::function Callback); + bool Load(LView *Parent, const char *Name, const char *Type = NULL); +}; + +struct ImgKey : public LKey +{ + LPointF pt; + ImgView *view; + + ImgKey(LKey &k, ImgView *v) + { + ((LKey&)*this) = k; + view = v; + } +}; + +struct ImgMouse : public LMouse +{ + LPointF pt; + LPoint ViewPt; + ImgView *view; + + ImgMouse(LMouse &m, ImgView *v) + { + ((LMouse&)*this) = m; + ViewPt.x = m.x; + ViewPt.y = m.y; + view = v; + } + + ImgMouse(const ImgMouse &m) : LMouse(m) + { + pt.x = m.pt.x; + pt.y = m.pt.y; + ViewPt = m.ViewPt; + view = m.view; + } +}; + +// Virtual base class for image view events and settings +class ImageViewTarget : public LZoomViewCallback +{ +public: + virtual ~ImageViewTarget() {} + + virtual void UserKey(ImgKey &k) = 0; + virtual void UserMouseClick(ImgMouse &m) = 0; + virtual void UserMouseEnter(ImgMouse &m) = 0; + virtual void UserMouseExit(ImgMouse &m) = 0; + virtual void UserMouseMove(ImgMouse &m) = 0; + virtual ImgTool *GetCurrentTool() = 0; +}; + +// Bitmap View Type +class ImgView : public LZoomView +{ +protected: + ImageViewTarget *App; + LMouse Last; + + int MouseFlags; + int ScrollX, ScrollY; + +public: + ImgView(ImageViewTarget *p) : LZoomView(p) + { + App = p; + ScrollX = 0; + ScrollY = 0; + } + ~ImgView() + { + } + + void OnMouseMove(LMouse &m); + void OnMouseClick(LMouse &m); + void OnMouseEnter(LMouse &m); + void OnMouseExit(LMouse &m); + bool OnKey(LKey &k); + void OnPulse(); + ImageViewTarget *GetApp() { return App; } +}; + +class ImgEvent +{ + // Image palette info + LPalette *Palette; + + // Image delta info + LAutoPtr Data; + int Length; + +public: + ImgEvent(LSurface *From, LSurface *To, LRect &Bounds); + virtual ~ImgEvent(); + + bool Apply(LSurface *pDC); + uint Sizeof(); +}; + +// The main window +#ifdef MainWnd +#undef MainWnd +#endif +#define MainWnd ((ImgWnd*)LAppInst->AppWnd) + +class ImgWnd : public LDocApp, public LDom, public ImageViewTarget +{ +protected: + class ImgWndPrivate *d = NULL; + + // Doc + ImgDocument Doc; + + // Views ------------------------------------- + LBox *Splitter = NULL; + // contains... + ImgView *Zoom = NULL; + ImgView *Normal = NULL; + //-------------------------------------------- + LStatusBar *Status = NULL; + // contains... + LStatusPane *StatusInfo[STATUS_MAX]; + LProgressStatusPane *Meter = NULL; + //-------------------------------------------- + LSubMenu *File = NULL; + LSubMenu *RecentFilesMenu = NULL; + LSubMenu *Edit = NULL; + LSubMenu *Image = NULL; + LSubMenu *BrushMenu = NULL; + LSubMenu *ToolsMenu = NULL; + LSubMenu *Adjust = NULL; + LSubMenu *Macro = NULL; + LSubMenu *Help = NULL; + //-------------------------------------------- + LToolBar *Commands = NULL; + // contains... + LCommand ImageProperties; + LCommand FileOpen; + LCommand FileSave; + // Separator + LCommand Cut; + LCommand Copy; + LCommand Paste; + // Separator + LCommand OptGuideLines; + LCommand OptFillPrimitives; + LCommand OptShowGrid; + LCommand GridSizeInc; + LCommand OptTransparentPaste; + // Separator + LCommand EnableUndo; + LCommand DiscardUndo; + LCommand Undo; + LCommand Redo; + //-------------------------------------------- + LPaletteUI *Palette = NULL; + //-------------------------------------------- + class ImgTools *Toolbar = NULL; + // contains... + LCommand Cmd[TOOL_MAX]; + //-------------------------------------------- + // Tools + int CurrentTool = 0; + ImgTool *Current = NULL; + ImgTool *Tools[TOOL_MAX] = {}; + //-------------------------------------------- + // Options and Brush + int Operator = 0; + bool UseFore = true; + bool FillPrim = true; + bool ShowGrid = true; + int AlphaLevel = 255; + LAutoPtr BrushMap; + + void PostPasteBrush(LSurface *pBrush); + + // Stuff + int OpLevel = 0; + bool QuitWhenDone = false; + ImgView *ClickedView = NULL; + + bool GetVariant(const char *Name, LVariant &Value, const char *Array = 0); + bool SetVariant(const char *Name, LVariant &Value, const char *Array = 0); + + // Temporary variables + LPoint MouseStart; + LPoint LastOffset; + + // Undo + bool UndoEnabled = true; + int UndoPos = 0; + int CaptureUndoLevel; // if 0 { capture the undo info } + List UndoQueue; + LSurface *LastFrame = NULL; + LRect LastUpdate; + + // UI + void SetupUi(); + void OnDcChange(); + void OnUndoQueueChange(); + void PushUndoEvent(ImgEvent *u); + void EmptyUndoQueue(); + void DrawOnAlpha(); + void UpdateBackgroundMenu(); + +public: + ImgWnd(); + ~ImgWnd(); + + // Stuff + void StartOp(); + void EndOp(); + bool OnRequestClose(bool OsShuttingDown); + ImgTool *GetCurrentTool() { return Current; } + void RunScript(LViewI *Parent, char *Script, const char *FileName, bool Debug); + bool OpenHelp(const char *File = 0); + void UpdateScriptsMenu(); + + // Properties + int Op(); + void Op(int o); + LSurface *Brush(); + void Brush(LAutoPtr Ptr); + LSurface *GetDoc(); + void SetDoc(LSurface *s); + Progress *GetProgress() { return Doc.GetProgress(); } + + // Colour + ImgCol Fore(); + void Fore(ImgCol c); + ImgCol Back(); + void Back(ImgCol c); + bool GetColour(ImgCol &c, bool Fore = true); + COLOUR GetColour32(bool Fore = true); + void DrawBackground(LZoomView *View, LSurface *pDC, LPoint Offset, LRect *Src); + void DrawForeground(LZoomView *View, LSurface *Dst, LPoint Offset, LRect *Src); + + bool TransparentPaste(); + void TransparentPaste(bool b); + bool UseForeColour(); + void UseForeColour(bool b); + int Alpha(); + void Alpha(int i); + bool FillPrimitives(); + void FillPrimitives(bool b); + bool CrossHairs(); + void CrossHairs(bool b); + + #define DefOption(_type, _name, _opt, _def) \ + _type _name() { LVariant v = _def; GetOptions()->GetValue(_opt, v); return v.CastInt32(); } \ + void _name(_type i) { LVariant v; GetOptions()->SetValue(_opt, v = i); OnSettingChange(); } + + DefOption(int, DisplayGrid, OPT_DisplayGrid, true); + DefOption(int, GridSize, OPT_GridSize, 8); + DefOption(int, DisplayTile, OPT_DisplayTile, true); + DefOption(int, TileType, OPT_TileType, 0); + DefOption(int, TileX, OPT_TileX, 16); + DefOption(int, TileY, OPT_TileY, 16); + + // --------------------------------------------------------------------- + // Methods + void SetStatusText(const char *Text, int Pane = 0); + void ZoomCenter(int x, int y); + void ZoomTo(LRect r); + + // An action is a logical group of things that the user + // would expect to get undone together + virtual void BeforeAction(); + + // BeforeUpdate is before the tool modifies the DC + // selects in all the required colours and paint options + virtual void BeforeUpdate(LRect *a = NULL, bool PaletteChange = false); + + // Update is after the tool has modified the DC + virtual void Update(LRect *a = NULL, bool Now = false); + + // After all related updates have taken place + virtual void AfterAction(); + + // --------------------------------------------------------------------- + // Tool Events + void UserKey(ImgKey &k); + void UserMouseClick(ImgMouse &m); + void UserMouseEnter(ImgMouse &m); + void UserMouseExit(ImgMouse &m); + void UserMouseMove(ImgMouse &m); + + // --------------------------------------------------------------------- + // Methods + LView *GetClickedView() { return ClickedView; } + ImgMemDC *GetDC(); + LAutoPtr ReleaseDC(); + bool SetDC(LAutoPtr pDC); + void AttachTool(int i = -1); + void DetachTool(); + bool SerializeFile(LAutoPtr &pDC, const char *FileName, bool Write); + void OffsetImage(int x, int y); + + LDom *GetUserDom(); + void SetUserDom(LDom *b); + + bool Empty(); + void OpenFile(const char *FileName, bool Ro, std::function Callback); + void SaveFile(const char *FileName, std::function Callback); + bool SerializeOptions(LOptionsFile *Options, bool Write); + void GetFileTypes(LFileSelect *Dlg, bool Write); + + // --------------------------------------------------------------------- + // Window + int OnNotify(LViewI *Ctrl, LNotification n); + LMessage::Result OnEvent(LMessage *Msg); + int OnCommand(int Cmd, int Event, OsView Handle); + void OnReceiveFiles(LArray &Files); + void OnCreate(); + bool OnMouseWheel(double Lines); + void OnSettingChange(); + bool OnKey(LKey &k); +}; + +/// The image <-> script glue class +class ImageScriptContext : public LScriptContext +{ + ImgWnd *App; + LBrush *Brush; + LHashTbl,LPath*> Paths; + LHashTbl,LSurface*> Bitmaps; + LViewI *Parent; + LFont *Fnt; + LScriptEngine *Engine; LHashDom Dom; - - LString GetIncludeFile(const char *FileName) override; - LPath *GetPath(char *name); - void SetEngine(LScriptEngine *Eng); - -public: - ImageScriptContext(ImgWnd *app, LViewI *parent); - ~ImageScriptContext(); - - LHostFunc *GetCommands(); - bool Create(LScriptArguments &Args); - bool GetDocument(LScriptArguments &Args); - bool SetDocument(LScriptArguments &Args); - bool Load(LScriptArguments &Args); - bool Save(LScriptArguments &Args); - bool Rgb(LScriptArguments &Args); - bool Rgba(LScriptArguments &Args); - bool LinearGradient(LScriptArguments &Args); - bool RadialGradient(LScriptArguments &Args); - bool AddStop(LScriptArguments &Args); - bool NewPath(LScriptArguments &Args); - bool Rect(LScriptArguments &Args); - bool RoundRect(LScriptArguments &Args); - bool Circle(LScriptArguments &Args); - bool MoveTo(LScriptArguments &Args); - bool LineTo(LScriptArguments &Args); - bool QuadTo(LScriptArguments &Args); - bool CubicTo(LScriptArguments &Args); - bool FillPath(LScriptArguments &Args); - bool ErasePath(LScriptArguments &Args); - bool PathWidth(LScriptArguments &Args); - bool PathHeight(LScriptArguments &Args); - bool MsgBox(LScriptArguments &Args); - bool Font(LScriptArguments &Args); - bool Text(LScriptArguments &Args); - bool FontAscent(LScriptArguments &Args); - bool FontHeight(LScriptArguments &Args); - bool Blur(LScriptArguments &Args); - bool SetFillRule(LScriptArguments &Args); - bool SetDepth(LScriptArguments &Args); - bool Resample(LScriptArguments &Args); - bool Crop(LScriptArguments &Args); - bool Difference(LScriptArguments &Args); - bool Normalize(LScriptArguments &Args); - bool Blt(LScriptArguments &Args); -}; - -// UI -#include "ColourCtrl.h" -#include "ImageProperties.h" -#include "GridDlg.h" -#include "ColourDlg.h" -#include "OperatorDlg.h" -#include "TextDlg.h" -#include "ReduceDlg.h" - -#include "lgi/common/Panel.h" -class ImgTools : public LPanel -{ - ImgWnd *App; - int ToolsY; - -public: - ImgTools(ImgWnd *app, int x, int y); - - void OnCreate(); - void OnPaint(LSurface *pDC); -}; - -extern void ImageCompare(ImgWnd *App, const char *FileA = NULL, const char *FileB = NULL); -extern bool GaussianBlur(LSurface *Dest, LSurface *Source, double Radius, Progress *Prog = 0); -extern bool IsPreMulAlpha(LSurface *pDC); -extern void PreToPostMulAlpha(LSurface *pDC); -extern void PostToPreMulAlpha(LSurface *pDC); -extern void CreateScriptWindow(class ImgWnd *App, const char *ScriptFile = 0); -extern void DoLevels(ImgWnd *App, std::function Callback); -extern void HslSplit(ImgWnd *App); -extern void OpenHueGraph(ImgWnd *App, LSurface *pDC); -extern bool IsHighDpi(); - -// Tool stuff -#include "ImageTool.h" + + LString GetIncludeFile(const char *FileName) override; + LPath *GetPath(char *name); + void SetEngine(LScriptEngine *Eng); + +public: + ImageScriptContext(ImgWnd *app, LViewI *parent); + ~ImageScriptContext(); + + LHostFunc *GetCommands(); + bool Create(LScriptArguments &Args); + bool GetDocument(LScriptArguments &Args); + bool SetDocument(LScriptArguments &Args); + bool Load(LScriptArguments &Args); + bool Save(LScriptArguments &Args); + bool Rgb(LScriptArguments &Args); + bool Rgba(LScriptArguments &Args); + bool LinearGradient(LScriptArguments &Args); + bool RadialGradient(LScriptArguments &Args); + bool AddStop(LScriptArguments &Args); + bool NewPath(LScriptArguments &Args); + bool Rect(LScriptArguments &Args); + bool RoundRect(LScriptArguments &Args); + bool Circle(LScriptArguments &Args); + bool MoveTo(LScriptArguments &Args); + bool LineTo(LScriptArguments &Args); + bool QuadTo(LScriptArguments &Args); + bool CubicTo(LScriptArguments &Args); + bool FillPath(LScriptArguments &Args); + bool ErasePath(LScriptArguments &Args); + bool PathWidth(LScriptArguments &Args); + bool PathHeight(LScriptArguments &Args); + bool MsgBox(LScriptArguments &Args); + bool Font(LScriptArguments &Args); + bool Text(LScriptArguments &Args); + bool FontAscent(LScriptArguments &Args); + bool FontHeight(LScriptArguments &Args); + bool Blur(LScriptArguments &Args); + bool SetFillRule(LScriptArguments &Args); + bool SetDepth(LScriptArguments &Args); + bool Resample(LScriptArguments &Args); + bool Crop(LScriptArguments &Args); + bool Difference(LScriptArguments &Args); + bool Normalize(LScriptArguments &Args); + bool Blt(LScriptArguments &Args); +}; + +// UI +#include "ColourCtrl.h" +#include "ImageProperties.h" +#include "GridDlg.h" +#include "ColourDlg.h" +#include "OperatorDlg.h" +#include "TextDlg.h" +#include "ReduceDlg.h" + +#include "lgi/common/Panel.h" +class ImgTools : public LPanel +{ + ImgWnd *App; + int ToolsY; + +public: + ImgTools(ImgWnd *app, int x, int y); + + void OnCreate(); + void OnPaint(LSurface *pDC); +}; + +extern void ImageCompare(ImgWnd *App, const char *FileA = NULL, const char *FileB = NULL); +extern bool GaussianBlur(LSurface *Dest, LSurface *Source, double Radius, Progress *Prog = 0); +extern bool IsPreMulAlpha(LSurface *pDC); +extern void PreToPostMulAlpha(LSurface *pDC); +extern void PostToPreMulAlpha(LSurface *pDC); +extern void CreateScriptWindow(class ImgWnd *App, const char *ScriptFile = 0); +extern void DoLevels(ImgWnd *App, std::function Callback); +extern void HslSplit(ImgWnd *App); +extern void OpenHueGraph(ImgWnd *App, LSurface *pDC); +extern bool IsHighDpi(); + +// Tool stuff +#include "ImageTool.h" diff --git a/src/ImageApp.cpp b/src/ImageApp.cpp --- a/src/ImageApp.cpp +++ b/src/ImageApp.cpp @@ -1,4949 +1,4951 @@ /* ** FILE: ImageApp.cpp ** AUTHOR: Matthew Allen ** DATE: 9/6/98 ** DESCRIPTION: Image ** ** Copyright (C) 1998-1999, Matthew Allen ** fret@memecode.com */ #include #include "Image.h" #include "lgi/common/Token.h" #include "lgi/common/About.h" #include "lgi/common/ScrollBar.h" #include "lgi/common/HashDom.h" #include "lgi/common/Jpeg.h" #include "lgi/common/ClipBoard.h" #include "lgi/common/List.h" #include "lgi/common/DisplayString.h" #include "lgi/common/Palette.h" #include "lgi/common/TableLayout.h" #include "lgi/common/LgiRes.h" #include "lgi/common/Menu.h" #include "TransparentDlg.h" #include "resource.h" #include "IccUi.h" char AppName[] = "i.Mage"; char HelpFile[] = "index.html"; char OptionsFile[] = "image.r"; double ScalingFactors[] = { 0.15, 0.25, 0.37, 0.5, 0.75, 1.25, 1.5, 2.0, 3.0, 0 }; ///////////////////////////////////////////////////////////////////// class LJpegOptions : public LDialog { LScrollBar *Scroll = NULL; GdcJpeg *Jpeg = NULL; LSurface *pDC = NULL; void Update() { char Temp[256]; LMakePath(Temp, sizeof(Temp), LGetSystemPath(LSP_TEMP), "~jpeg.tmp"); LFile f; if (f.Open(Temp, O_WRITE)) { if (Jpeg->_Write(&f, pDC, (int)Quality, SubSample, LPoint(0, 0))) { int64 Size = f.GetSize(); if (Size >= 0) { char s[256]; LFormatSize(s, sizeof(s), Size); SetCtrlName(IDC_FILE_SIZE, s); } f.Close(); FileDev->Delete(Temp, false); } } } public: int64 Quality = 50; LPoint Dpi; GdcJpeg::SubSampleMode SubSample = GdcJpeg::Sample_2x2_1x1_1x1; LJpegOptions(GdcJpeg *jpeg, LSurface *pdc) { Jpeg = jpeg; pDC = pdc; Dpi.x = Dpi.y = 600; if (LoadFromResource(IDD_JPEG)) { if (GetViewById(IDC_QUALITY, Scroll)) { Scroll->SetPage(5); Scroll->SetLimits(0, 100); Scroll->Value(Quality); } SetCtrlValue(IDC_TABLE, SubSample); SetCtrlValue(IDC_X, Dpi.x); SetCtrlValue(IDC_Y, Dpi.y); } LVariant Parent; if (Jpeg->Props->GetValue(LGI_FILTER_PARENT_WND, Parent) && Parent.Type == GV_GVIEW) { SetParent(Parent.Value.View); MoveSameScreen(Parent.Value.View); } else { MoveToCenter(); } } void OnCreate() { Update(); } int OnNotify(LViewI *c, LNotification n) { switch (c->GetId()) { case IDC_QUALITY: { Quality = c->Value(); Update(); break; } case IDC_SUB1: case IDC_SUB2: case IDC_SUB3: case IDC_SUB4: { SubSample = (GdcJpeg::SubSampleMode)GetCtrlValue(IDC_TABLE); Update(); break; } case IDOK: { Quality = Scroll ? Scroll->Value() : -1; SubSample = (GdcJpeg::SubSampleMode)GetCtrlValue(IDC_TABLE); Dpi.x = (int)GetCtrlValue(IDC_X); Dpi.y = (int)GetCtrlValue(IDC_Y); // fall thru } case IDCANCEL: { EndModal(c->GetId()); break; } } return 0; } }; ///////////////////////////////////////////////////////////////////////////////////// bool IsPreMulAlpha(LSurface *pDC) { if (!pDC) return false; switch (pDC->GetColourSpace()) { default: LAssert(0); break; case System32BitColourSpace: { for (int y=0; yY(); y++) { System32BitPixel *p = (System32BitPixel*)((*pDC)[y]); System32BitPixel *e = p + pDC->X(); while (p < e) { if (p->r > p->a || p->g > p->a || p->b > p->a) return false; p++; } } break; } } return true; } void PreToPostMulAlpha(LSurface *pDC) { if (!pDC) return; switch (pDC->GetColourSpace()) { default: LAssert(0); break; case System32BitColourSpace: { for (int y=0; yY(); y++) { System32BitPixel *p = (System32BitPixel*)((*pDC)[y]); System32BitPixel *e = p + pDC->X(); while (p < e) { if (p->a) { p->r = (p->r * 255) / p->a; p->g = (p->g * 255) / p->a; p->b = (p->b * 255) / p->a; } p++; } } break; } case System24BitColourSpace: { if (pDC->AlphaDC()) { for (int y=0; yY(); y++) { System24BitPixel *p = (System24BitPixel*)((*pDC)[y]); uint8_t *a = (uint8_t*)((*pDC->AlphaDC())[y]); System24BitPixel *e = p + pDC->X(); while (p < e) { if (*a) { p->r = (p->r * 255) / *a; p->g = (p->g * 255) / *a; p->b = (p->b * 255) / *a; } p++; a++; } } } break; } } } void PostToPreMulAlpha(LSurface *pDC) { if (!pDC) return; switch (pDC->GetColourSpace()) { default: LAssert(0); break; case System32BitColourSpace: { for (int y=0; yY(); y++) { System32BitPixel *p = (System32BitPixel*)((*pDC)[y]); System32BitPixel *e = p + pDC->X(); while (p < e) { p->r = (p->r * p->a) / 255; p->g = (p->g * p->a) / 255; p->b = (p->b * p->a) / 255; p++; } } break; } case System24BitColourSpace: { if (pDC->AlphaDC()) { for (int y=0; yY(); y++) { System24BitPixel *p = (System24BitPixel*)((*pDC)[y]); uint8_t *a = (uint8_t*)((*pDC->AlphaDC())[y]); System24BitPixel *e = p + pDC->X(); while (p < e) { p->r = (p->r * *a) / 255; p->g = (p->g * *a) / 255; p->b = (p->b * *a) / 255; p++; a++; } } } break; } } } int IcoCmp(LRect *a, LRect *b) { if (a->y2 < b->y1 || a->y1 > b->y2) { // Not overlapping Y, sort by Y return a->y1 - b->y1; } else { // Overlapping Y, sort by X return a->x1 - b->x1; } } bool CompactIcons(ImgWnd *App) { LSurface *pDC = App->GetDC(); int Tx = App->TileX(); int Ty = App->TileY(); if (!pDC) return false; LMemDC Done; Done.Create(pDC->X(), pDC->Y(), CsIndex8); Done.Colour(0); Done.Rectangle(); Done.Colour(255); bool Status = true; COLOUR k = pDC->Get(0,0); LArray Icons; int MaxX = 0, MaxY = 0; int FirstOversize = -1; for (int y=0; yY(); y++) { for (int x=0; xX(); x++) { if (Done.Get(x, y)) continue; if (pDC->Get(x, y) == k) continue; LRect &r = Icons.New(); r.x1 = r.x2 = x; r.y1 = r.y2 = y; while (true) { LRect a = r; int i; // Top for (i = a.x1 - 1; a.y1 > 0 && i <= a.x2 + 1; i++) { if (k != pDC->Get(i, a.y1 - 1) || k != pDC->Get(i, a.y1 - 2)) { a.y1--; break; } } // Bottom for (i = a.x1 - 1; a.y2 < (pDC->Y() - 1) && i <= a.x2 + 1; i++) { if (k != pDC->Get(i, a.y2 + 1) || k != pDC->Get(i, a.y2 + 2)) { a.y2++; break; } } // Left for (i = a.y1 - 1; a.x1 > 0 && i <= a.y2 + 1; i++) { if (k != pDC->Get(a.x1 - 1, i) || k != pDC->Get(a.x1 - 2, i)) { a.x1--; break; } } // Right for (i = a.y1 - 1; a.x2 < (pDC->X() - 1) && i <= a.y2 + 1; i++) { if (k != pDC->Get(a.x2 + 1, i) || k != pDC->Get(a.x2 + 2, i)) { a.x2++; break; } } if (a == r) break; r = a; } MaxX = MAX(MaxX, r.X()); MaxY = MAX(MaxY, r.Y()); Done.Rectangle(&r); if ((r.X() > Tx || r.Y() > Ty) && FirstOversize < 0) { FirstOversize = Icons.Length()-1; } } } if (Icons.Length() == 0) { LgiMsg(App, "No icons found.", AppName); } else if (LgiMsg(App, "Found %i icons, do you want to compact them into a %ix%i grid? (Max size: %ix%i, Oversize: %s)", AppName, MB_YESNO, Icons.Length(), Tx, Ty, MaxX, MaxY, FirstOversize >= 0 ? Icons[FirstOversize].GetStr() : "None") == IDYES) { Icons.Sort(IcoCmp); App->BeforeAction(); App->BeforeUpdate(); int Px = 0; int Py = 0; for (unsigned i=0; iGetColourSpace()); Tmp.Blt(0, 0, pDC, &r); pDC->Colour(k); pDC->Rectangle(&r); pDC->Blt(Px, Py, &Tmp); Px += Tx; if (Px + Tx >= pDC->X()) { Px = 0; Py += Ty; } // App->Invalidate(); // LYield(); #else pDC->Colour(Rgb24(255, 0, 0), 24); pDC->Box(&r); #endif } App->Update(); App->AfterAction(); Status = true; } return Status; } ////////////////////////////////////////////////////////////////////////////// LColourSpace AllCs[] = { // CsIndex1, // CsIndex4, CsIndex8, // CsAlpha8, // CsArgb15, CsRgb15, // CsAbgr15, CsBgr15, CsRgb16, CsBgr16, CsRgb24, CsBgr24, CsRgbx32, CsBgrx32, CsXrgb32, CsXbgr32, CsRgba32, CsBgra32, CsArgb32, CsAbgr32, CsBgr48, CsRgb48, CsBgra64, CsRgba64, CsAbgr64, CsArgb64, CsNone, }; int x = 16, y = 16; int GetAlphaIndex(LColourSpace Cs) { for (int i=0; i<4; i++) { int Comp = (Cs >> ((4 - 1 - i) * 8)) & 0xff; LComponentType Type = (LComponentType) (uint8_t)(Comp >> 4); if (Type == CtAlpha) { return i; } } return -1; } void RgbaMark(LSurface *d) { #if 0 uint8 *p = (*d)[0]; int bytes = d->GetBits() >> 3; if (p) { if (bytes == 3 || bytes == 4) { LColourSpace Cs = d->GetColourSpace(); int AlphaIdx = GetAlphaIndex(Cs); for (int i=0; i 4) { int words = bytes >> 1; for (int i=0; iGetColourSpace() == CsIndex8) { LPalette *p = new LPalette; p->CreateCube(); d->Palette(p); } bool HasAlpha = GetAlphaIndex(d->GetColourSpace()) >= 0; for (int x=0; xX(); x++) { int n = x * 255 / d->X(); if (HasAlpha && Op == GDC_ALPHA) d->Colour(Rgba32(255, 0, 0, n), 32); else d->Colour(Rgb24(255, 255-n, 255-n), 24); d->VLine(x, 0, d->Y()-1); } RgbaMark(d); } void Blue(LSurface *d) { if (d->GetColourSpace() == CsIndex8) { LPalette *p = new LPalette; p->CreateCube(); d->Palette(p); } for (int y=0; yY(); y++) { int n = y * 255 / d->Y(); d->Colour(Rgb24(255-n, 255-n, 255), 24); d->HLine(0, d->X()-1, y); } RgbaMark(d); } LAutoPtr CreateTest(int Op, LColourSpace in, LColourSpace out) { LAutoPtr Src, Dst; if (Src.Reset(new LMemDC) && Src->Create(x, y, in, LSurface::SurfaceRequireExactCs)) { Red(Src, Op); } if (Src.Get() && Dst.Reset(new LMemDC) && Dst->Create(x, y, out, LSurface::SurfaceRequireExactCs)) { LSurface *d = Dst; LSurface *s = Src; Blue(d); d->Op(Op); #if 1 d->Blt(0, 0, s); #endif } return Dst; } class ImgItem : public LListItem { int Op; LColourSpace In, Out; LDisplayString Txt; LAutoPtr Img; bool Debug; public: ImgItem(int op, LColourSpace in, LColourSpace out, const char *txt) : Txt(LSysFont, txt) { Op = op; In = in; Out = out; Debug = false; Img = CreateTest(op, in, out); } void OnMeasure(LPoint *Info) { Info->x = (Img ? Img->X() : 0) + (LTableLayout::CellSpacing << 1) + Txt.X(); Info->y = MAX(Img ? Img->Y() : 0, Txt.Y()); } void OnPaint(LItem::ItemPaintCtx &Ctx) { Ctx.pDC->Colour(Ctx.Back); Ctx.pDC->Rectangle(&Ctx); int Cx = Ctx.x1; if (Img) { Ctx.pDC->Blt(Ctx.x1, Ctx.y1, Img); Cx += Img->X() + LTableLayout::CellSpacing; } Txt.GetFont()->Colour(Ctx.Fore, Ctx.Back); Txt.GetFont()->Transparent(true); Txt.Draw(Ctx.pDC, Cx, Ctx.y1 + ((Ctx.Y()-Txt.Y())>>1)); } void OnMouseClick(LMouse &m) { if (m.Double()) { Debug = true; Img = CreateTest(Op, In, Out); Update(); } } }; class CsTestThread : public LThread { LList *Lst; bool Loop; public: CsTestThread(LList *lst) : LThread("CsTestThread") { Lst = lst; Loop = true; Run(); } ~CsTestThread() { Loop = false; while (!IsExited()) LSleep(1); } int Main() { int Ops[] = {GDC_SET, GDC_ALPHA}; for (int o = 0; o < CountOf(Ops); o++) { List a; for (int in_cs = 0; AllCs[in_cs]; in_cs++) { for (int out_cs = 0; Loop && AllCs[out_cs]; out_cs++) { LColourSpace InCs = AllCs[in_cs]; LColourSpace OutCs = AllCs[out_cs]; char Txt[256]; sprintf_s(Txt, sizeof(Txt), "%i:%s->%s", Ops[o], LColourSpaceToString(InCs), LColourSpaceToString(OutCs)); a.Insert(new ImgItem(Ops[o], InCs, OutCs, Txt)); } } Lst->Insert(a); } return 0; } }; class ColourTest : public LWindow { LList *Lst; LAutoPtr Thread; public: ColourTest() { LRect r(0, 0, (int) (GdcD->X()*0.8), (int) (GdcD->Y()*0.8)); SetPos(r); MoveToCenter(); if (Attach(0)) { AddView(Lst = new LList(100, 0, 0, 100, 100)); Lst->SetMode(LListColumns); Lst->AddColumn("Test", 200); Lst->SetPourLargest(true); AttachChildren(); Visible(true); Thread.Reset(new CsTestThread(Lst)); } } }; ////////////////////////////////////////////////////////////////////////////// extern void RegisterFileTypes(LView *Parent); void EnumAvailableFormats(LFileSelect *Select, int Cap) { int ExtLength = 0; LString::Array Ext; LArray Formats; for (int i=0; true; i++) { auto f = LFilterFactory::NewAt(i); if (f) { if (f->GetCapabilites() & Cap) { LVariant e; if (f->GetValue("Extension", e)) { auto t = e.LStr().SplitDelimit(","); for (auto s: t) { ExtLength += s.Length() + 3; Ext.Add(s); } } Formats.Add(f.Release()); } } else { break; } } char *ExtStr = NewStr("", ExtLength+1); if (ExtStr) { int Ch = 0; for (auto e: Ext) { Ch += sprintf_s(ExtStr+Ch, ExtLength-Ch, "%s*.%s", (*ExtStr) ? ";" : "", e.Get()); } Select->Type("Graphics", ExtStr, 0); DeleteArray(ExtStr); } for (auto f: Formats) { char ExtStr[256] = ""; LVariant e; LVariant n = "Format"; f->GetValue("Type", n); if (f->GetValue("Extension", e)) { LToken t(e.Str(), ","); int Ch = 0; for (unsigned n=0; nType(n.Str(), ExtStr, 1); } Select->Type("All files", LGI_ALL_FILES, 0); Formats.DeleteObjects(); } ////////////////////////////////////////////////////////////////////////////// /* void ImgView::SetScrollPos(int x, int y) { LLayout::SetScrollPos(x, y); ScrollX = (int) (((HScroll) ? HScroll->Value() : 0) * GetBlockSize()); ScrollY = (int) (((VScroll) ? VScroll->Value() : 0) * GetBlockSize()); } */ ////////////////////////////////////////////////////////////////////////////// enum BackgroundType { BgBlack, BgGrey, BgWhite, }; class ImgWndPrivate : public LVmCallback { public: ImgWnd *App; SystemFunctions SysContext; ImageScriptContext Context; BackgroundType Background = BgGrey; bool CmdLineDone = false; LDom *UserDom = NULL; LHashDom DefaultDom; LArray ToolScripts; LMouse LastMousePos; ImgWndPrivate(ImgWnd *app) : Context(app, app) { App = app; } LVmDebugger *AttachVm(LVirtualMachine *Vm, LCompiledCode *Code, const char *Assembly) { LAutoPtr CopiedVm(new LVirtualMachine(Vm)); LAutoPtr CopiedCode(new LCompiledCode(*Code)); return new LVmDebuggerWnd(App, this, CopiedVm, CopiedCode, Assembly); } bool CompileScript(LAutoPtr &Output, const char *FileName, const char *Source) { LCompiler c; return c.Compile(Output, &SysContext, &Context, FileName, Source, NULL); } bool CallCallback(LVirtualMachine &Vm, LString CallbackName, LScriptArguments &Args) { LAssert(!"Impl me."); return false; } }; #ifdef WIN32 LDocApp::LIcon Icon = IDI_APP; #else LDocApp::LIcon Icon = "image-icon.png"; #endif ImgWnd::ImgWnd() : LDocApp(AppName, Icon) { LAppInst->AppWnd = this; d = new ImgWndPrivate(this); // Tool stuff ZeroObj(StatusInfo); CurrentTool = 0; Current = 0; ZeroObj(Tools); #ifdef MAC LgiGetResObj(false, "image"); #endif #ifdef WINDOWS #else SetIcon("icon64.png"); #endif // Startup UI if (_Create()) { LVariant v; GetOptions()->GetValue(OPT_BackgroundColour, v); d->Background = (BackgroundType)v.CastInt32(); #if 0 LAutoPtr t(new ImgMemDC(90, 100, 32)); t->Colour(LColour(255, 0, 0)); t->Rectangle(0, 0, 30, 100); t->Colour(LColour(0, 255, 0)); t->Rectangle(31, 0, 60, 100); t->Colour(LColour(0, 0, 255)); t->Rectangle(61, 0, 90, 100); SetDC(t); AttachTool(); #endif } else { LgiMsg(this, LLoadString(IDS_ERROR_UI), AppName); LExitApp(); } } ImgWnd::~ImgWnd() { if (GetDirty()) { LAssert(!"Document needs to be clean by this point. We can't wait for a callback here."); } Doc.RemoveView(Zoom); Doc.RemoveView(Normal); DetachTool(); LAppInst->AppWnd = 0; if (Toolbar) { LVariant v; GetOptions()->SetValue(OPT_ToolsOpen, v = Toolbar->Open()); } DeleteObj(Zoom); DeleteObj(Normal); DeleteObj(Palette); UndoQueue.DeleteObjects(); DeleteObj(LastFrame); LAutoPtr Null; SetDC(Null); for (int i=0; iGetValue(OPT_Lang, LangId)) { // Set the language to load... LAppInst->SetConfig("language", LangId.Str()); } #ifdef MAC SetWindow(this); #else DropTarget(true); #endif SetupUi(); AttachTool(); } bool ImgWnd::SerializeOptions(LOptionsFile *Options, bool Write) { if (Write) { LVariant v; Options->SetValue("EnabledUndo", v = EnableUndo.Value()); Options->SetValue("FillObjects", v = FillPrimitives()); Options->SetValue("TransparentPaste", v = TransparentPaste()); if (Palette) { LPalette *Pal = Palette->GetPalette(); ImgCol c; // Fore Col Palette->GetFore(&c); if (c.Bits <= 8) { c.c = CBit(24, c.c, c.Bits, Pal); c.Bits = 24; } Options->SetValue("ForeColour", v = (int)c.c); Options->SetValue("ForeColourBits", v = c.Bits); // Back Col Palette->GetBack(&c); if (c.Bits <= 8) { c.c = CBit(24, c.c, c.Bits, Pal); c.Bits = 24; } Options->SetValue("BackColour", v = (int)c.c); Options->SetValue("BackColourBits", v = c.Bits); } Options->SetValue("Operator", v = Op()); Options->SetValue("Alpha", v = Alpha()); if (Splitter) { Options->SetValue("SplitterPos", v = Splitter->Value()); } } else { LVariant i; if (Options->GetValue("Operator", i)) { Op(i.CastInt32()); } if (Options->GetValue("Alpha", i)) { Alpha(i.CastInt32()); } if (GetOptions()->GetValue("FillObjects", i)) { FillPrim = i.CastBool(); } if (GetOptions()->GetValue("TransparentPaste", i)) { TransparentPaste(i.CastBool()); } if (GetOptions()->GetValue("Alpha", i)) { Alpha(i.CastInt32()); } if (GetOptions()->GetValue("EnabledUndo", i)) { UndoEnabled = i.CastBool(); if (!UndoEnabled) { CaptureUndoLevel++; } } } return true; } void ImgWnd::AttachTool(int i) { DetachTool(); if (!Tools) return; LMouse m; Splitter->GetMouse(m); LView *Over = 0; if (Normal && Normal->GetPos().Overlap(m.x, m.y)) { Over = Normal; } else if (Zoom && Zoom->GetPos().Overlap(m.x, m.y)) { Over = Zoom; } if (Current) { if (Over) { Over->GetMouse(m); Over->OnMouseExit(m); } Current->Detach(); Current = NULL; } if (i >= TOOL_BRUSH && i < TOOL_MAX) { Current = Tools[i]; } else { Current = Tools[CurrentTool]; i = CurrentTool; } if (Current) { CurrentTool = i; Current->AttachTo(this); if (Over) { Over->GetMouse(m); Over->OnMouseEnter(m); } } if (Cmd[CurrentTool].ToolButton) { Cmd[CurrentTool].ToolButton->Value(true); } } void ImgWnd::DetachTool() { if (Current) { Current->OnDetach(); Current = NULL; } } bool IsHighDpi() { static auto Dpi = LScreenDpi(); #if 0 return true; // Testing... #else return Dpi.x >= 110; #endif } void ImgWnd::SetupUi() { RecentFilesMenu = NULL; if (!LgiGetResObj(true)) { return; } Tools[TOOL_BRUSH] = new ToolBrush(GetOptions()); Tools[TOOL_SELECT_BRUSH] = new ToolSelectBrush(GetOptions()); Tools[TOOL_EYE_DROPPER] = new ToolEyeDropper(GetOptions()); Tools[TOOL_ZOOM] = new ToolZoom(GetOptions()); Tools[TOOL_TEXT] = new ToolText(GetOptions()); Tools[TOOL_FLOOD] = new ToolFlood(GetOptions()); Tools[TOOL_FREEHAND] = new ToolFreehand(GetOptions()); Tools[TOOL_LINE] = new ToolLine(GetOptions()); Tools[TOOL_ELLIPSE] = new ToolEllipse(GetOptions()); Tools[TOOL_RECTANGLE] = new ToolRectangle(GetOptions()); Tools[TOOL_POLYGON] = new ToolPolygon(GetOptions()); Tools[TOOL_ERASE] = new ToolErase(GetOptions()); if (_LoadMenu("IDM_MENU")) { Menu->RemoveItem(0); auto i = Menu->FindItem(IDM_RECENT_NONE); if (i) { Set(i->GetParent()); } i = Menu->FindItem(IDM_SCALE_NONE); if (i) { auto ScaleMenu = i->GetParent(); if (ScaleMenu) { ScaleMenu->Empty(); int n=0; for (double *Scale=ScalingFactors; *Scale > 0.01; Scale++) { char s[256]; sprintf_s(s, sizeof(s), "%i%%", (int)(*Scale*100)); ScaleMenu->AppendItem(s, IDM_SCALE_BASE+n++, true); } } } EnableUndo.MenuItem = Menu->FindItem(IDM_ENABLE_UNDO); i = Menu->FindItem(IDM_NO_LANG); if (i) { auto Langs = i->GetParent(); if (Langs) { LResources *r = LgiGetResObj(); if (r) { LArray *l = r->GetLanguages(); if (l) { Langs->Empty(); for (int i=0; iLength(); i++) { LLanguage *k = LFindLang((*l)[i]); if (k) { Langs->AppendItem(k->Name, IDM_LANG_BASE + i, true); } } } } } } if ((i = Menu->FindItem(IDM_SCRIPTING))) { if ((ToolsMenu = i->GetParent())) { ToolsMenu->AppendSeparator(); UpdateScriptsMenu(); } } } if (IsHighDpi()) Commands = LgiLoadToolbar(this, "commands40.png", 40, 40); else Commands = LgiLoadToolbar(this, "commands20.png", TBS_X, TBS_Y); if (Commands) { Commands->Attach(this); enum AppIcons { ICON_NEW = 0, ICON_OPEN, ICON_SAVE, ICON_CUT, ICON_COPY, ICON_PASTE, ICON_GUIDELINES, ICON_FILL, ICON_GRID, ICON_GRIDINC, ICON_TRANSPARENT, ICON_ENABLEUNDO, ICON_UNDO, ICON_REDO, ICON_FLIPX, ICON_FLIPY, ICON_ROTCLOCK, ICON_ROTANTICLOCK, ICON_SCALE, ICON_MAX, }; ImageProperties.ToolButton = Commands->AppendButton(LLoadString(IDS_NEW), IDM_NEW, TBT_PUSH, true, ICON_NEW); FileOpen.ToolButton = Commands->AppendButton(LLoadString(IDS_OPEN), IDM_OPEN, TBT_PUSH, true, ICON_OPEN); FileSave.ToolButton = Commands->AppendButton(LLoadString(IDS_SAVE), IDM_SAVE, TBT_PUSH, true, ICON_SAVE); Commands->AppendSeparator(); Cut.ToolButton = Commands->AppendButton(LLoadString(IDS_CUT), IDM_CUT, TBT_PUSH, false, ICON_CUT); Copy.ToolButton = Commands->AppendButton(LLoadString(IDS_COPY), IDM_COPY_BRUSH, TBT_PUSH, true, ICON_COPY); Paste.ToolButton = Commands->AppendButton(LLoadString(IDS_PASTE), IDM_PASTE_BRUSH, TBT_PUSH, true, ICON_PASTE); Commands->AppendSeparator(); // OptGuideLines.ToolButton = Commands->AppendButton(LLoadString(IDS_GUIDELINES), TOOL_GUIDELINES, TBT_TOGGLE, false); OptFillPrimitives.ToolButton = Commands->AppendButton(LLoadString(IDS_FILLOBJECTS), TOOL_FILL, TBT_TOGGLE, true, ICON_FILL); OptShowGrid.ToolButton = Commands->AppendButton(LLoadString(IDS_GRID), TOOL_GRID, TBT_TOGGLE, true, ICON_GRID); OptShowGrid.ToolButton->SetNeedsRightClick(true); GridSizeInc.ToolButton = Commands->AppendButton(LLoadString(IDS_GRIDINCREMENT), TOOL_GRIDINC, TBT_PUSH, true, ICON_GRIDINC); GridSizeInc.ToolButton->SetNeedsRightClick(true); OptTransparentPaste.ToolButton = Commands->AppendButton(LLoadString(IDS_USETRANSPARENTPASTE), TOOL_TRANSPARENT_PASTE, TBT_TOGGLE, true, ICON_TRANSPARENT); // Commands->AppendButton(LLoadString(IDS_DRAWONALPHA), TOOL_DRAW_ALPHA, TBT_TOGGLE); Commands->AppendSeparator(); EnableUndo.ToolButton = Commands->AppendButton(LLoadString(IDS_ENABLEUNDO), IDM_ENABLE_UNDO, TBT_TOGGLE, true, ICON_ENABLEUNDO); Undo.ToolButton = Commands->AppendButton(LLoadString(IDS_UNDO), IDM_UNDO, TBT_PUSH, false, ICON_UNDO); Redo.ToolButton = Commands->AppendButton(LLoadString(IDS_REDO), IDM_REDO, TBT_PUSH, false, ICON_REDO); Commands->AppendSeparator(); Commands->AppendButton(LLoadString(IDS_FLIPX), IDM_FLIP_X, TBT_PUSH, true, ICON_FLIPX); Commands->AppendButton(LLoadString(IDS_FLIPY), IDM_FLIP_Y, TBT_PUSH, true, ICON_FLIPY); Commands->AppendButton(LLoadString(IDS_ROTATECLOCKWISE), IDM_ROTATE_90, TBT_PUSH, true, ICON_ROTCLOCK); Commands->AppendButton(LLoadString(IDS_ROTATEANTICLOCKWISE), IDM_ROTATE_270, TBT_PUSH, true, ICON_ROTANTICLOCK); Commands->AppendSeparator(); int n=0; for (double *Scale=ScalingFactors; *Scale > 0.01; Scale++, n++) { char s[256]; sprintf_s(s, sizeof(s), LLoadString(IDS_SCALE), (int)(*Scale*100)); Commands->AppendButton(s, IDM_SCALE_BASE + n, TBT_PUSH, true, ICON_SCALE + n); } OptFillPrimitives.Value(FillPrim); OptShowGrid.Value(ShowGrid); EnableUndo.Value(UndoEnabled); Commands->Customizable(GetOptions(), "Toolbar"); } Status = new LStatusBar; if (Status) { Status->Attach(this); StatusInfo[0] = Status->AppendPane("", 2); Status->AppendPane(Meter = new LProgressStatusPane); Doc.SetProgress(Meter); #define CreateStatusPane(i, width) \ { \ auto &p = StatusInfo[i]; \ if (p = Status->AppendPane("", width)) \ p->Sunken(true); \ } CreateStatusPane(STATUS_COLOURINFO, 400); CreateStatusPane(STATUS_POSINFO, 150); CreateStatusPane(STATUS_DOCINFO, 100); } PourAll(); auto Dpi = LScreenDpi(); int ToolX = IsHighDpi() ? 350 : 200; int ToolY = 0; Toolbar = new ImgTools(this, ToolX + 20, Commands ? Commands->Y() : 26); if (Toolbar) { Toolbar->Attach(this); LToolTabBar *t = new LToolTabBar(IDC_TOOLS); if (t) { t->SetNotify(this); t->Raised(false); t->HasBorder(false); t->Visible(false); t->IsVertical(true); auto ImgName = IsHighDpi() ? "tools40.png" : "tools20.png"; auto FileName = LFindFile(ImgName); if (FileName && t->SetBitmap(FileName, IsHighDpi() ? 40 : TBS_X, IsHighDpi() ? 40 : TBS_Y)) { for (int i=TOOL_BRUSH; iAppendControl(Cmd[i].ToolButton); } LRect r = Toolbar->GetClient(); r.Inset(1, 1); r.x1 += 16; r.x2 += ToolX + 21; r.y2 = r.y1 + (IsHighDpi() ? 500 : 270) - 1; ToolY += r.Y(); LRegion c(r); t->Pour(c); t->Attach(Toolbar); t->SetPos(r); LVariant ToolsOpen = false; GetOptions()->GetValue(OPT_ToolsOpen, ToolsOpen); Toolbar->Open(ToolsOpen.CastBool()); } else { DeleteObj(t); } } } Palette = new LPaletteUI(this); if (Palette) { LRect r(16, ToolY + 8, ToolX, ToolY + 220); Palette->SetPos(r); Palette->SetId(TOOL_PALETTE); Palette->Attach(Toolbar); Palette->Raised(false); { LRegion c(r); Palette->Pour(c); } LVariant col, bits; if (GetOptions()->GetValue("ForeColour", col) && GetOptions()->GetValue("ForeColourBits", bits)) { ImgCol c; c.c = col.CastInt32(); c.Bits = bits.CastInt32(); Palette->SetFore(c); } if (GetOptions()->GetValue("BackColour", col) && GetOptions()->GetValue("BackColourBits", bits)) { ImgCol c; c.c = col.CastInt32(); c.Bits = bits.CastInt32(); Palette->SetBack(c); } } Splitter = new LBox; if (Splitter) { Splitter->GetCss(true)->Padding("5px"); Splitter->GetCss()->Border("1px Outset"); /* Splitter->AddView(Zoom = new ImgView(this), false); if (Zoom) { Doc.AddView(Zoom); Zoom->SetNotify(this); } */ Splitter->AddView(Normal = new ImgView(this), false); if (Normal) { Normal->GetCss(true)->Border("1px Inset"); Doc.AddView(Normal); Normal->SetNotify(this); } /* LVariant i; if (GetOptions()->GetValue("SplitterPos", i)) { Splitter->Value(i.CastInt32()); } */ Splitter->Attach(this); } DropTarget(true); OptShowGrid.Value(DisplayGrid() != 0); PourAll(); Visible(true); } void ImgWnd::UpdateScriptsMenu() { char p[MAX_PATH_LEN]; if (ToolsMenu && LGetSystemPath(LSP_APP_INSTALL, p, sizeof(p))) { LMakePath(p, sizeof(p), p, "Scripts"); d->ToolScripts.Length(0); LMenuItem *it; while ( ToolsMenu->Length() && (it = ToolsMenu->ItemAt(ToolsMenu->Length()-1))) { if (it->Separator() || it->Id() == IDM_SCRIPTING) break; it->Remove(); DeleteObj(it); } LDirectory dir; for (int b = dir.First(p); b; b = dir.Next()) { if (!dir.IsDir()) { char *e = LGetExtension(dir.GetName()); if (e && !_stricmp(e, "script")) { dir.Path(p, sizeof(p)); ToolsMenu->AppendItem(dir.GetName(), IDM_SCRIPTS_BASE+d->ToolScripts.Length(), true); d->ToolScripts.New().Reset(NewStr(p)); } } } if (d->ToolScripts.Length() == 0) ToolsMenu->AppendItem("(no scripts)", -1, false); } } void ImgWnd::PostPasteBrush(LSurface *pBrush) { if (Doc.GetDC()) { LPalette *Pal = Doc.GetDC()->Palette(); if (Doc.GetDC()->GetBits() <= 8 && pBrush->GetBits() <= 8) { RemapDC(pBrush, Pal); } else if (Doc.GetDC()->GetBits() != pBrush->GetBits()) { LReduceBitDepth(pBrush, Doc.GetDC()->GetBits(), Pal); } } if (OptTransparentPaste.Value()) { LSurface *pMask = pBrush->AlphaDC(); if (!pMask) { pBrush->HasAlpha(true); pMask = pBrush->AlphaDC(); } if (pMask) { uint Masks[] = { 0, 0x000000FF, 0x0000FFFF, 0x00FFFFFF, 0xFFFFFFFF}; uint Mask = Masks[(pBrush->GetBits() + 7) / 8]; ImgCol c = Back(); pMask->Colour(0xff); pMask->Rectangle(); pMask->Colour(0x00); for (int y=0; yY(); y++) { for (int x=0; xX(); x++) { if ((pBrush->Get(x, y) & Mask) == (c.c & Mask)) { pMask->Set(x, y); } } } } } } bool ImgWnd::OpenHelp(const char *File) { auto Exe = LGetExePath(); bool Done = false; const char *Sub[] = { "./Help", "./Code/Help", "../Help", "../Code/Help", 0 }; for (int i = 0; Sub[i]; i++) { char p[300]; LMakePath(p, sizeof(p), Exe, Sub[i]); LMakePath(p, sizeof(p), p, File ? File : (char*)"index.html"); char *Hash = strrchr(p, '#'); if (Hash) *Hash = 0; if (LFileExists(p)) { if (Hash) *Hash = '#'; LExecute(p, NULL, Exe); Done = true; break; } } if (!Done) { LgiMsg(this, LLoadString(IDS_ERROR_NO_HELP), AppName, MB_OK); return false; } return true; } LMessage::Result ImgWnd::OnEvent(LMessage *Msg) { switch (Msg->Msg()) { case M_DESCRIBE: { char *Text = (char*) Msg->B(); if (Text) { SetStatusText(Text, STATUS_NORMAL); } break; } } return LDocApp::OnEvent(Msg); } bool ImgWnd::OnKey(LKey &k) { switch (k.vkey) { case LK_ESCAPE: { if (Current) Current->Key(k); return true; } } return false; } bool ImgWnd::OnMouseWheel(double Lines) { LMouse m; GetMouse(m); auto v = WindowFromPoint(m.x, m.y); if (v) { ImgView *view = dynamic_cast(v); if (view) { view->OnMouseWheel(Lines); } } return true; } void ImgWnd::DrawOnAlpha() { if (!Doc.GetDC()) return; DetachTool(); if (!Doc.GetDC()->HasAlpha()) { Doc.GetDC()->HasAlpha(true); } bool Doa = Doc.GetDC()->DrawOnAlpha(); Doc.GetDC()->DrawOnAlpha(!Doa); Update(); LAutoPtr Null; Brush(Null); AttachTool(); } class ColourItem : public LListItem { LColour Col; public: ColourItem(char *Name, LColour &c) { Col = c; SetText(Name, 1); char s[256]; sprintf_s(s, sizeof(s), "%02X %02X %02X", Col.r(), Col.g(), Col.b()); SetText(s, 2); } void OnPaintColumn(LItem::ItemPaintCtx &Ctx, int i, LItemColumn *c) { if (i == 0) { Ctx.pDC->Colour(Col); Ctx.pDC->Rectangle(&Ctx); } else { LListItem::OnPaintColumn(Ctx, i, c); } } }; class ShowSysColours : public LWindow { LList *Lst; public: ShowSysColours() { struct Colour { char *Name; int Def; } Cols[] = { #ifdef WIN32 {"CTLCOLOR_MSGBOX", CTLCOLOR_MSGBOX}, {"CTLCOLOR_EDIT", CTLCOLOR_EDIT}, {"CTLCOLOR_LISTBOX", CTLCOLOR_LISTBOX}, {"CTLCOLOR_BTN", CTLCOLOR_BTN}, {"CTLCOLOR_DLG", CTLCOLOR_DLG}, {"CTLCOLOR_SCROLLBAR", CTLCOLOR_SCROLLBAR}, {"CTLCOLOR_STATIC", CTLCOLOR_STATIC}, {"CTLCOLOR_MAX", CTLCOLOR_MAX}, {"COLOR_SCROLLBAR", COLOR_SCROLLBAR}, {"COLOR_BACKGROUND", COLOR_BACKGROUND}, {"COLOR_ACTIVECAPTION", COLOR_ACTIVECAPTION}, {"COLOR_INACTIVECAPTION", COLOR_INACTIVECAPTION}, {"COLOR_MENU", COLOR_MENU}, {"COLOR_WINDOW", COLOR_WINDOW}, {"COLOR_WINDOWFRAME", COLOR_WINDOWFRAME}, {"COLOR_MENUTEXT", COLOR_MENUTEXT}, {"COLOR_WINDOWTEXT", COLOR_WINDOWTEXT}, {"COLOR_CAPTIONTEXT", COLOR_CAPTIONTEXT}, {"COLOR_ACTIVEBORDER", COLOR_ACTIVEBORDER}, {"COLOR_INACTIVEBORDER", COLOR_INACTIVEBORDER}, {"COLOR_APPWORKSPACE", COLOR_APPWORKSPACE}, {"COLOR_HIGHLIGHT", COLOR_HIGHLIGHT}, {"COLOR_HIGHLIGHTTEXT", COLOR_HIGHLIGHTTEXT}, {"COLOR_BTNFACE", COLOR_BTNFACE}, {"COLOR_BTNSHADOW", COLOR_BTNSHADOW}, {"COLOR_GRAYTEXT", COLOR_GRAYTEXT}, {"COLOR_BTNTEXT", COLOR_BTNTEXT}, {"COLOR_INACTIVECAPTIONTEXT", COLOR_INACTIVECAPTIONTEXT}, {"COLOR_BTNHIGHLIGHT", COLOR_BTNHIGHLIGHT}, {"COLOR_3DDKSHADOW", COLOR_3DDKSHADOW}, {"COLOR_3DLIGHT", COLOR_3DLIGHT}, {"COLOR_INFOTEXT", COLOR_INFOTEXT}, {"COLOR_INFOBK", COLOR_INFOBK}, {"COLOR_HOTLIGHT", COLOR_HOTLIGHT}, {"COLOR_GRADIENTACTIVECAPTION", COLOR_GRADIENTACTIVECAPTION}, {"COLOR_GRADIENTINACTIVECAPTION", COLOR_GRADIENTINACTIVECAPTION}, {"COLOR_MENUHILIGHT", COLOR_MENUHILIGHT}, {"COLOR_MENUBAR", COLOR_MENUBAR}, #endif {0, 0} }; AddView(Lst = new LList(100, 0, 0, 100, 100)); Lst->SetPourLargest(true); Lst->AddColumn("Colour", 100); Lst->AddColumn("Name", 300); Lst->AddColumn("Hex", 150); Lst->Sunken(false); LRect r(0, 0, 1000, 900); SetPos(r); MoveToCenter(); if (Attach(0)) { for (Colour *c = Cols; c->Name; c++) { #ifdef WIN32 LColour col(GetSysColor(c->Def), 24); Lst->Insert(new ColourItem(c->Name, col)); #endif } Visible(true); } } }; void ImgWnd::UpdateBackgroundMenu() { auto m = GetMenu(); auto mi = m->FindItem(IDM_BG_BLACK); if (mi) mi->Checked(d->Background == BgBlack); mi = m->FindItem(IDM_BG_GREY); if (mi) mi->Checked(d->Background == BgGrey); mi = m->FindItem(IDM_BG_WHITE); if (mi) mi->Checked(d->Background == BgWhite); LVariant v; GetOptions()->SetValue(OPT_BackgroundColour, v = (int)d->Background); } int ImgWnd::OnCommand(int Cmd, int Event, OsView Handle) { switch (Cmd) { case IDM_SYS_COLOURS: { new ShowSysColours(); break; } case IDM_MAKE_OPAQUE: { ImgMemDC *pDC = GetDC(); if (pDC) { if (pDC->GetColourSpace() == System32BitColourSpace) { for (int y=0; yY(); y++) { System32BitPixel *p = (System32BitPixel*)(*pDC)[y]; System32BitPixel *e = p + pDC->X(); while (p < e) { p->a = 255; p++; } } Update(); } else if (pDC->HasAlpha()) { pDC->HasAlpha(false); } } break; } case IDM_SCRIPTING: { CreateScriptWindow(this); break; } case IDM_COMPARE_IMAGES: { ImageCompare(this, GetCurFile()); break; } case IDM_COLOURPROF_VIEW: { if (Doc.GetColourProfile()) { new LIccProfileUi(Doc.GetColourProfile()); } else { LgiMsg(this, LLoadString(IDS_NO_COL_PROF), AppName); } break; } case IDM_COLOURPROF_LOAD: { if (!Doc.GetColourProfile()) { LAutoPtr c(new LIccProfile); Doc.SetColourProfile(c); } if (Doc.GetColourProfile()) { auto s = new LFileSelect; s->Parent(this); s->Type("ICC Profile", "*.icc"); s->Open([this](auto s, auto ok) { if (ok) Doc.GetColourProfile()->Open(s->Name()); delete s; }); } break; } case IDM_COLOURPROF_SAVE: { if (Doc.GetColourProfile()) { auto s = new LFileSelect; s->Parent(this); s->Type("ICC Profile", "*.icc"); s->Save([this](auto s, auto ok) { if (ok) Doc.GetColourProfile()->Save(s->Name()); delete s; }); } else { LgiMsg(this, LLoadString(IDS_NO_COL_PROF), AppName); } break; } case IDM_BG_BLACK: { d->Background = BgBlack; UpdateBackgroundMenu(); Update(); break; } case IDM_BG_GREY: { d->Background = BgGrey; UpdateBackgroundMenu(); Update(); break; } case IDM_BG_WHITE: { d->Background = BgWhite; UpdateBackgroundMenu(); Update(); break; } case IDM_GRID_SETTINGS: { auto Dlg = new GridDlg(this); Dlg->DoModal(NULL); break; } case IDM_FLATTEN: { LSurface *pDC = GetDC(); if (pDC) { ImgMemDC Temp; if (Temp.Create(pDC->X(), pDC->Y(), pDC->GetColourSpace())) { if (pDC->Palette()) { Temp.Palette(new LPalette(pDC->Palette())); } LRect r(0, 0, pDC->X()-1, pDC->Y()-1); LPoint off(0, 0); DrawBackground(NULL, &Temp, off, &r); Temp.Op(GDC_ALPHA); Temp.Blt(0, 0, pDC); Temp.Op(GDC_SET); pDC->HasAlpha(false); pDC->Op(GDC_SET); pDC->Blt(0, 0, &Temp); Update(); } } break; } case IDM_TEST_COLOUR: { new ColourTest; break; } case 5000: case 5001: case 5002: { LSurface *pDC = GetDC(); if (pDC) { DetachTool(); int Min = MIN(pDC->X(), pDC->Y()); LAutoPtr Crop(new ImgMemDC(Min, Min, pDC->GetColourSpace())); LRect a(0, 0, Min, Min); if (Crop) { switch (Cmd) { case 5001: { if (pDC->X() > pDC->Y()) { a.Offset((pDC->X() - Min) / 2, 0); } else { a.Offset(0, (pDC->Y() - Min) / 2); } break; } case 5002: { if (pDC->X() > pDC->Y()) { a.Offset(pDC->X() - Min, 0); } else { a.Offset(0, pDC->Y() - Min); } break; } } Crop->Blt(0, 0, pDC, &a); LAutoPtr pNew(new ImgMemDC(76, 76, Crop->GetColourSpace())); if (pDC && pNew && ResampleDC(pNew, Crop, 0, Doc.GetProgress())) { SetDC(pNew); } } Update(); AttachTool(); } break; } case IDM_DISCARD_UNDO: { EmptyUndoQueue(); break; } case IDM_ENABLE_UNDO: { if (!Handle) { // This is for the menu item. EnableUndo.Value(!EnableUndo.Value()); } if (EnableUndo.Value()) { CaptureUndoLevel = 0; UndoEnabled = true; } else { CaptureUndoLevel = 1; UndoEnabled = false; } break; } case TOOL_BRUSH: case TOOL_SELECT_BRUSH: case TOOL_EYE_DROPPER: case TOOL_ZOOM: case TOOL_TEXT: // case TOOL_AIRBRUSH: case TOOL_FLOOD: case TOOL_FREEHAND: case TOOL_LINE: case TOOL_ELLIPSE: case TOOL_RECTANGLE: case TOOL_POLYGON: case TOOL_ERASE: { AttachTool(Cmd); break; } case TOOL_DRAW_ALPHA: { DrawOnAlpha(); break; } case IDM_DRAWONALPHA: { DrawOnAlpha(); break; } case IDM_UNDO: case IDM_REDO: { bool Redo = Cmd == IDM_REDO; CaptureUndoLevel++; if (UndoQueue.Length() > 0 && Doc.GetDC()) { if ( (Redo && UndoPos < UndoQueue.Length() - 1) || (!Redo && UndoPos >= 0)) { if (Redo) UndoPos += 1; ImgEvent *u = UndoQueue.ItemAt(limit(UndoPos, 0, UndoQueue.Length()-1)); if (u) { u->Apply(Doc.GetDC()); if (!Redo) UndoPos += -1; Update(); LPalette *Pal = Doc.GetDC()->Palette(); if (Pal) { Palette->SetPalette(Pal); } OnUndoQueueChange(); } } } CaptureUndoLevel--; break; } case IDM_NEW: { EmptyUndoQueue(); CaptureUndoLevel++; DetachTool(); auto Dlg = new ImagePropertiesDlg(this); Dlg->DoModal([this, Dlg](auto dialog, auto code) { LAutoPtr dlg(dialog); if (!code) return; LAutoPtr pDC = ReleaseDC(); if (pDC && Dlg->KeepData) { // do any resizing if (Dlg->ScaleOption == IMGPROP_SCALE_RESAMPLE) { if (Dlg->x != pDC->X() || Dlg->y != pDC->Y()) { // change res LAutoPtr pNew(new ImgMemDC(Dlg->x, Dlg->y, pDC->GetColourSpace())); if (pNew) { if (pDC->Palette()) pNew->Palette(new LPalette( pDC->Palette() )); ResampleDC(pNew, pDC, 0, Meter); if (Meter) { Meter->Value(0); } pDC = pNew; } } } else { LAutoPtr pNew(new ImgMemDC(Dlg->x, Dlg->y, pDC->GetColourSpace())); if (pNew) { LAssert(pDC); if (!pDC) return; // blank pNew->Colour(Dlg->Background); pNew->Rectangle(); // copy data int Nx = pNew->X() - pDC->X(); int Ny = pNew->Y() - pDC->Y(); switch (Dlg->AlignType) { default: case IDC_LEFT_TOP: { pNew->Blt(0, 0, pDC); break; } case IDC_CENTER_TOP: { pNew->Blt(Nx/2, 0, pDC); break; } case IDC_RIGHT_TOP: { pNew->Blt(Nx, 0, pDC); break; } case IDC_LEFT_CENTER: { pNew->Blt(0, Ny/2, pDC); break; } case IDC_CENTERED: { pNew->Blt(Nx/2, Ny/2, pDC); break; } case IDC_RIGHT_CENTER: { pNew->Blt(Nx, Ny/2, pDC); break; } case IDC_LEFT_BOTTOM: { pNew->Blt(0, Ny, pDC); break; } case IDC_CENTER_BOTTOM: { pNew->Blt(Nx/2, Ny, pDC); break; } case IDC_RIGHT_BOTTOM: { pNew->Blt(Nx, Ny, pDC); break; } } pDC = pNew; } } // do any bit conversion if (pDC && pDC->GetBits() != Dlg->Bits) { if (pDC->GetBits() > 8 && Dlg->Bits <= 8) { // reduce colour depth from // true colour to paletted auto Reduce = new LReduceDlg(this); Reduce->DoModal([this, Dlg, Reduce, pDC](auto dlg, auto code) { if (code) { if (Reduce->PalType >= 0) LReduceBitDepth(pDC, Dlg->Bits, Reduce->Palette, Reduce); else LAssert(!"What now?"); } delete dlg; }); } else { LAutoPtr pNew(new ImgMemDC(Dlg->x, Dlg->y, LBitsToColourSpace(Dlg->Bits))); if (pNew) { if (pDC->GetBits() > 24) { if (!Dlg->Background.IsValid()) { LRect r(0, 0, pNew->X()-1, pNew->Y()-1); LPoint off(0, 0); DrawBackground(NULL, pNew, off, &r); } else { pNew->Colour(Dlg->Background); pNew->Rectangle(); } pNew->Op(GDC_ALPHA); } pNew->Blt(0, 0, pDC); pDC = pNew; } } } } else { LAutoPtr Dc(new ImgMemDC(Dlg->x, Dlg->y, LBitsToColourSpace(Dlg->Bits))); if (Dc) { // blank Dc->Colour(Dlg->Background); Dc->Rectangle(); SetDC(Dc); } } if (pDC) { if (pDC->GetBits() <= 8) { LPalette *p = 0; LPalette *OldPal = pDC->Palette(); if (OldPal) { if (Dlg->Palette) { if (Dlg->RemapPalette && *Dlg->Palette != *OldPal) { // Remap to the new palette uchar Lut[256]; for (int i=0; iPalette->MatchRgb(Rgb24(Rgb->r, Rgb->g, Rgb->b)); } else { Lut[i] = 0; } } for (int y=0; yY(); y++) { uchar *Start = (*pDC)[y]; if (Start) { uchar *End = Start + pDC->X(); while (Start < End) { //GdcRGB *Old = (*OldPal)[*Start]; //GdcRGB *New = (*Dlg.Palette)[Lut[*Start]]; *Start = Lut[*Start]; Start++; } } } p = Dlg->Palette; Dlg->Palette = 0; } } } else { if (Dlg->KeepData && pDC && pDC->Palette()) { p = new LPalette(pDC->Palette()); } else if (Dlg->Palette) { p = new LPalette(Dlg->Palette); } else { p = new LPalette; if (p && p->SetSize()) { GdcRGB *rgb = (*p)[0]; rgb->r = rgb->g = rgb->b = 0; rgb++; for (int i=1; i<256; i++, rgb++) { rgb->r = rgb->g = rgb->b = 0xFF; } } } } if (p) { pDC->Palette(p, true); } } SetDC(pDC); } if (!Dlg->KeepData) { SetCurFile(0); } }); AttachTool(); CaptureUndoLevel--; break; } case IDM_EXIT: { LCloseApp(); break; } case IDM_OFFSET: { if (GetDC()) { auto Dlg = new LInput(this, "0,0", LLoadString(IDS_ENTEROFFSET), LLoadString(IDS_OFFSETDOCUMENT)); Dlg->DoModal([this,Dlg](auto dlg, auto code) { if (code == IDOK) { LString::Array a = Dlg->GetStr().SplitDelimit(", "); if (a.Length() == 2) OffsetImage(a[0].Int(), a[1].Int()); else LgiMsg(this, "Not in the format '##,##'", "Error"); } delete dlg; }); } break; } case IDM_OFFSET_HALF: { if (GetDC()) { OffsetImage(GetDC()->X()/2, GetDC()->Y()/2); } break; } case IDM_OFFSET_BACK: { if (LastOffset.x != -1 && LastOffset.y != -1) { OffsetImage(-LastOffset.x, -LastOffset.y); } break; } case IDM_BRIGHT_CONT: { extern void DoBrightnessContrast(ImgWnd *Parent); DoBrightnessContrast(this); break; } case IDM_ADJUST_LEVELS: { DoLevels(this, NULL); break; } case IDM_GAUSSIAN_BLUR: { extern void DoGaussianBlur(ImgWnd *Parent); DoGaussianBlur(this); break; } case IDM_HUE_GRAPH: { OpenHueGraph(this, Doc.GetDC()); break; } case IDM_COPY: { LSurface *pDC = GetDC(); if (pDC) { DetachTool(); LClipBoard Clip(this); Clip.Bitmap(pDC, true); AttachTool(); } break; } case IDM_PASTE: { if (GetDC()) { // Paste as the brush, as we already have a document LClipBoard Clip(this); LSurface *pDC = Clip.Bitmap(); if (pDC) { BrushMap.Reset(pDC); PostPasteBrush(BrushMap); AttachTool(TOOL_BRUSH); } } else { // Paste as the document LClipBoard Clip(this); Clip.Bitmap([this](auto bmp, auto msg) { if (bmp) { LAutoPtr MemDC(new ImgMemDC(bmp)); SetDC(MemDC); } else if (msg) { LgiTrace("%s:%i - clipboard err: %s\n", _FL, msg.Get()); } }); } break; } case IDM_COPY_CODE: { LSurface *pDC = GetDC(); if (pDC) { DetachTool(); LClipBoard Clip(this); Clip.Empty(); char Str[1024] = ""; int Ch = 0; LStringPipe Buf; int Len = ((pDC->X() * pDC->GetBits()) + 31) / 32; for (int y=0; yY(); y++) { ulong *Ptr = (ulong*) (*pDC)[y]; if (Ptr) { for (int i=0; i= 800) { Buf.Push(Str); Buf.Push("\r\n"); Str[0] = 0; Ch = 0; } } } } if (strlen(Str) > 0) { Buf.Push(Str); Buf.Push("\r\n"); Str[0] = 0; } char *Final = Buf.NewStr(); if (Final) { Clip.Text(Final); DeleteArray(Final); } AttachTool(); } break; } case IDM_PASTE_CODE: { LClipBoard c(this); LAutoString Txt(c.Text()); if (Txt) { LArray a; LToken Lines(Txt, "\r\n"); unsigned MaxX = 0; for (unsigned y=0; y Bmp(new ImgMemDC); int Height = (a.Length() + (MaxX - 1)) / MaxX; if (Bmp->Create(MaxX, Height, System32BitColourSpace)) { Bmp->Colour(0); Bmp->Rectangle(); for (unsigned i=0; iColour ( LColour ( (c >> 16) & 0xff, (c >> 8) & 0xff, (c) & 0xff ) ); Bmp->Set ( i % MaxX, i / MaxX ); } DetachTool(); if (!GetDC()) { SetDC(Bmp); } else { LAutoPtr Tmp; Tmp.Reset(Bmp.Release()); Brush(Tmp); } Update(); AttachTool(); } } break; } case IDM_COPY_BRUSH: { LSurface *pBrush = Brush(); if (pBrush) { LSurface *pDC = GetDC(); if (pDC && pDC->Palette()) { LPalette *Pal = new LPalette(pDC->Palette()); if (Pal) { pBrush->Palette(Pal); } } LClipBoard Clip(this); Clip.Empty(); if (!Clip.Bitmap(pBrush)) LgiMsg(this, "Failed to paste bitmap to clipboard.", AppName); } break; } case IDM_PASTE_BRUSH: { LClipBoard Clip(this); Clip.Bitmap([this](auto pDC, auto Msg) { if (pDC) { BrushMap = pDC; PostPasteBrush(BrushMap); AttachTool(TOOL_BRUSH); } else { LgiMsg(this, Msg, AppName); } }); break; } case IDM_BRUSH_TO_DOC: { LSurface *pDC = GetDC(); LSurface *pBrush = Brush(); if (pDC && pBrush) { DetachTool(); if (pDC->Create(pBrush->X(), pBrush->Y(), pBrush->GetColourSpace())) { if (pBrush->Palette()) { pDC->Palette(new LPalette(pBrush->Palette())); } if (pBrush->HasAlpha()) { pDC->HasAlpha(true); pDC->AlphaDC()->Blt(0, 0, pBrush->AlphaDC()); } pDC->Blt(0, 0, pBrush); LAutoPtr n; Brush(n); } OnDcChange(); Update(); AttachTool(); } break; } case IDM_OPEN_BRUSH: { auto Select = new LFileSelect; Select->Parent(this); EnumAvailableFormats(Select, FILTER_CAP_READ); Select->Open([this](auto s, auto ok) { if (ok) { if (SerializeFile(BrushMap, s->Name(), false)) { PostPasteBrush(BrushMap); AttachTool(TOOL_BRUSH); } else LgiMsg(this, "Failed to open file '%s'", AppName, MB_OK, s->Name()); } delete s; }); break; } case IDM_SAVE_BRUSH: { if (BrushMap) { auto Select = new LFileSelect; Select->Parent(this); EnumAvailableFormats(Select, FILTER_CAP_WRITE); Select->Save([this](auto s, auto ok) { if (ok) SerializeFile(BrushMap, s->Name(), true); delete s; }); } break; } case IDM_REGISTER_TYPES: { RegisterFileTypes(this); break; } case IDM_INVERT: { DetachTool(); if (InvertDC(Doc.GetDC())) { Update(); } AttachTool(); break; } case IDM_GREY: { DetachTool(); LAutoPtr NewDC(new ImgMemDC); if (NewDC) { if (GreyScaleDC(NewDC, Doc.GetDC())) { Doc.SetDC(NewDC); OnDcChange(); Update(); } } AttachTool(); break; } case IDM_ROTATE_90: { if (GetDC()) { LAutoPtr pDC = ReleaseDC(); RotateDC(pDC, 90); if (pDC->AlphaDC()) { RotateDC(pDC->AlphaDC(), 90); } SetDC(pDC); } break; } case IDM_ROTATE_180: { if (GetDC()) { LAutoPtr pDC = ReleaseDC(); RotateDC(pDC, 180); if (pDC->AlphaDC()) RotateDC(pDC->AlphaDC(), 180); SetDC(pDC); } break; } case IDM_ROTATE_270: { LSurface *pDC = GetDC(); if (pDC) { LAutoPtr pDC = ReleaseDC(); RotateDC(pDC, 270); if (pDC->AlphaDC()) RotateDC(pDC->AlphaDC(), 270); SetDC(pDC); } break; } /* case IDM_ROTATE_FREE: { DetachTool(); if (InvertDC(Doc.pDC)) { Update(); } AttachTool(); break; } */ case IDM_FLIP_X: { DetachTool(); LSurface *pDC = Doc.GetDC(); if (pDC) { FlipXDC(pDC); pDC = pDC->AlphaDC(); if (pDC) { FlipXDC(pDC); } Update(); } AttachTool(); break; } case IDM_FLIP_Y: { DetachTool(); LSurface *pDC = Doc.GetDC(); if (pDC) { FlipYDC(pDC); pDC = pDC->AlphaDC(); if (pDC) { FlipYDC(pDC); } Update(); } AttachTool(); break; } case IDM_ROTATE_BRUSH_90: { LSurface *pDC = Brush(); if (pDC) { DetachTool(); RotateDC(pDC, 90); AttachTool(); } break; } case IDM_ROTATE_BRUSH_180: { LSurface *pDC = Brush(); if (pDC) { DetachTool(); RotateDC(pDC, 180); AttachTool(); } break; } case IDM_ROTATE_BRUSH_270: { LSurface *pDC = Brush(); if (pDC) { DetachTool(); RotateDC(pDC, 270); AttachTool(); } break; } case IDM_FLIP_BRUSH_X: { DetachTool(); LSurface *pDC = Brush(); if (pDC) { FlipXDC(pDC); pDC = pDC->AlphaDC(); if (pDC) { FlipXDC(pDC); } Update(); } AttachTool(); break; } case IDM_FLIP_BRUSH_Y: { DetachTool(); LSurface *pDC = Brush(); if (pDC) { FlipYDC(pDC); pDC = pDC->AlphaDC(); if (pDC) { FlipYDC(pDC); } Update(); } AttachTool(); break; } case IDM_HELP: { OpenHelp(); break; } case IDM_ABOUT: { LAbout Dlg( this, AppName, IMAGE_VER, LLoadString(IDS_ABOUT), "icon128.png", "http://www.memecode.com/image.php", "fret@memecode.com"); break; } default: { if (Cmd >= IDM_LANG_BASE && Cmd < IDM_LANG_BASE + 50) { int i = Cmd - IDM_LANG_BASE; LResources *r = LgiGetResObj(); if (r) { LArray *l = r->GetLanguages(); if (l) { LVariant v; GetOptions()->SetValue(OPT_Lang, v = (*l)[i]); LgiMsg(this, LLoadString(IDS_RESTART), AppName); } } } else if (Cmd >= IDM_SCALE_BASE && Cmd < IDM_SCALE_BASE + CountOf(ScalingFactors)) { double Scale = ScalingFactors[Cmd-IDM_SCALE_BASE]; if (Scale > 0.001) { LSurface *pDC = GetDC(); if (pDC) { DetachTool(); LAutoPtr pNew(new ImgMemDC); if (pDC && pNew && pNew->Create((int) ((double)pDC->X() * Scale), (int) ((double)pDC->Y() * Scale), pDC->GetColourSpace()) && ResampleDC(pNew, pDC, 0, Doc.GetProgress())) { SetDC(pNew); } Update(); AttachTool(); Doc.GetProgress()->Value(0); } } } else if (Cmd >= IDM_SCRIPTS_BASE && Cmd < IDM_SCRIPTS_BASE + (int)d->ToolScripts.Length()) { char *Script = d->ToolScripts[Cmd - IDM_SCRIPTS_BASE]; if (Script) CreateScriptWindow(this, Script); } break; } } return LDocApp::OnCommand(Cmd, Event, Handle); } int ImgWnd::OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case TOOL_PALETTE: { LSurface *pDC = GetDC(); if (Palette && pDC) { LPalette *Dest = pDC->Palette(); LPalette *Src = Palette->GetPalette(); if (Dest && Src) { // Save snapshot of image BeforeAction(); // Set document to new palette Dest->Set(Src); Update(); // Create undo information AfterAction(); } } break; } case TOOL_BRUSH: { int Id = Ctrl->GetId(); if (Tools[Id]) Tools[Id]->OnProperties(n.Type); if (n.IsMouseEvent() && n.GetMouseEvent().Right()) { auto Dlg = new OperatorDlg(this); Dlg->DoModal(NULL); } break; } case TOOL_TEXT: case TOOL_SELECT_BRUSH: case TOOL_EYE_DROPPER: case TOOL_ZOOM: // case TOOL_AIRBRUSH: case TOOL_FLOOD: case TOOL_FREEHAND: case TOOL_LINE: case TOOL_ELLIPSE: case TOOL_RECTANGLE: case TOOL_POLYGON: case TOOL_ERASE: { int Id = Ctrl->GetId(); if (Tools[Id]) Tools[Id]->OnProperties(n.Type); break; } case TOOL_GRID: { if (n.IsMouseEvent()) { auto m = n.GetMouseEvent(); if (m.Left()) { DisplayGrid(OptShowGrid.Value()); } else if (m.Right()) { auto Dlg = new GridDlg(this); Dlg->DoModal(NULL); } } break; } case TOOL_GRIDINC: { bool Right = false; if (n.IsMouseEvent()) { auto m = n.GetMouseEvent(); Right = m.Right(); if (!m.Down()) break; } int Inc = Right ? -1 : 1; auto Cur = Doc.GetZoom(); auto New = Cur + Inc; Doc.SetZoom(New); GridSize(New); break; } case IDC_TOOLS: { if (n.Type == LNotifyValueChanged) { } break; } } return 0; } void ImgWnd::OnSettingChange() { Doc.Update(); OptShowGrid.Value(DisplayGrid() != 0); } ImgMemDC *ImgWnd::GetDC() { return Doc.GetDC(); } LAutoPtr ImgWnd::ReleaseDC() { DetachTool(); LAutoPtr dc(Doc.ReleaseDC()); return dc; } bool ImgWnd::SetDC(LAutoPtr pDC) { DetachTool(); if (Palette) { LPalette *Pal = Palette->GetPalette(); if (Pal) { // we might lose our palette here... go true colour ImgCol c; Palette->GetFore(&c); if (c.Bits <= 8) { c.c = CBit(24, c.c, c.Bits, Pal); c.Bits = 24; Palette->SetFore(c); } Palette->GetBack(&c); if (c.Bits <= 8) { c.c = CBit(24, c.c, c.Bits, Pal); c.Bits = 24; Palette->SetBack(c); } } } if (Doc.GetDC() != pDC) { SetDirty(pDC != NULL, [this, dc = pDC.Release()](auto ok) { LAutoPtr pDC(dc); if (ok) Doc.SetDC(pDC); Update(0, true); OnDcChange(); AttachTool(); }); return false; } Update(0, true); OnDcChange(); AttachTool(); return true; } void ImgWnd::EmptyUndoQueue() { for (auto e: UndoQueue) { UndoQueue.Delete(e); DeleteObj(e); } OnUndoQueueChange(); } void ImgWnd::PushUndoEvent(ImgEvent *u) { if (u) { // delete any undo objects ahead of us for (int i=UndoPos + 1; i < UndoQueue.Length(); i++) { ImgEvent *e = UndoQueue.ItemAt(i); if (e) { UndoQueue.Delete(e); DeleteObj(e); } } // Insert us on the end UndoQueue.Insert(u); UndoPos = UndoQueue.IndexOf(u); OnUndoQueueChange(); } } void ImgWnd::OnUndoQueueChange() { int Items = UndoQueue.Length(); Undo.Enabled(Items > 0 && UndoPos >= 0); Redo.Enabled(Items > 0 && UndoPos < Items - 1); uint Size = 0; for (auto e: UndoQueue) { Size += e->Sizeof(); } char Str[256]; int ch = sprintf_s(Str, sizeof(Str), "%s", LLoadString(IDS_UNDOQUEUE)); LFormatSize(Str+ch, sizeof(Str)-ch, Size); SetStatusText(Str); } void ImgWnd::OnDcChange() { LString Str; if (Doc.GetDC()) Str.Printf(LLoadString(IDS_IMAGE_FORMAT), Doc.GetDC()->X(), Doc.GetDC()->Y(), Doc.GetDC()->GetBits()); if (Palette) Palette->SetPalette((Doc.GetDC())?Doc.GetDC()->Palette():0); SetStatusText(Str, STATUS_DOCINFO); } void ImgWnd::OnReceiveFiles(LArray &Files) { bool Exit = false; auto Name = Files.Length() ? Files[0] : NULL; const char *Ext; if (Files.Length() == 2) { ImageCompare(this, Files[0], Files[1]); return; } else if (!Name) { return; } else if ((Ext = LGetExtension(Name)) && !_stricmp(Ext, "script")) { // Image script being launched with the command line... CreateScriptWindow(this, Name); } else { // Normal file drop LDocApp::OnReceiveFiles(Files); if (Doc.GetDC() && !d->CmdLineDone) { d->CmdLineDone = true; char s[512]; if (LAppInst->GetOption("resize", s)) { int x = 0; int y = 0; bool Resize = true; LToken t(s, ","); for (unsigned i=0; iX() * atoi(Val) / 100; } else { x = atoi(Val); } } } else if (_stricmp(Var, "y") == 0) { if (Val) { if (strchr(Val, '%')) { y = Doc.GetDC()->Y() * atoi(Val) / 100; } else { y = atoi(Val); } } } else if (_stricmp(Var, "crop") == 0) { Resize = false; } else if (_stricmp(Var, "resize") == 0) { Resize = true; } } if (!x) { if (y) { double Ratio = (double) y / Doc.GetDC()->Y(); x = (int) (Ratio * Doc.GetDC()->X()); } } else if (!y) { if (x) { double Ratio = (double) x / Doc.GetDC()->X(); y = (int) (Ratio * Doc.GetDC()->Y()); } } if (x && y) { LAutoPtr pNew (new ImgMemDC); if (pNew && pNew->Create(x, y, Doc.GetDC()->GetColourSpace()) && ResampleDC(pNew, Doc.GetDC(), 0)) { SetDC(pNew); } else { LgiTrace("%s:%i - 'resize' cmd line failed.\n", __FILE__, __LINE__); } } else { LgiTrace("%s:%i - You must specify at least one size to use the 'resize' cmd line.\n", __FILE__, __LINE__); } } if (LAppInst->GetOption("reduce", s)) { // LReduceOptions } if (LAppInst->GetOption("write", s)) { // Write the output file SaveFile(s, NULL); } if (LAppInst->GetOption("exit")) { Exit = true; } } } for (unsigned i=1; i 0); // endop called before startop OpLevel--; if (OpLevel && QuitWhenDone) { LCloseApp(); } } LSurface *ImgWnd::GetDoc() { return Doc.GetDC(); } void ImgWnd::SetDoc(LSurface *s) { LAutoPtr p(new ImgMemDC(s)); SetDC(p); } LSurface *ImgWnd::Brush() { return BrushMap; } void ImgWnd::Brush(LAutoPtr b) { BrushMap = b; } void ImgWnd::SetStatusText(const char *Text, int Pane) { if (Pane >= 0 && Pane < STATUS_MAX && StatusInfo[Pane]) { StatusInfo[Pane]->Name(Text); } } COLOUR ImgWnd::GetColour32(bool Fore) { ImgCol c; if (GetColour(c, Fore)) { COLOUR c24 = CBit(24, c.c, c.Bits, Palette->GetPalette()); return Rgba32(R24(c24), G24(c24), B24(c24), c.a); } return 0; } bool ImgWnd::GetColour(ImgCol &c, bool Fore) { if (!Palette) return false; if (Fore) Palette->GetFore(&c); else Palette->GetBack(&c); c.a = Op() == GDC_ALPHA ? Alpha() : 255; return true; } ImgCol ImgWnd::Fore() { ImgCol c; if (Palette) { Palette->GetFore(&c); } return c; } void ImgWnd::Fore(ImgCol c) { if (Palette) { Palette->SetFore(c); } } ImgCol ImgWnd::Back() { ImgCol c; // int DcBits = (Doc.GetDC()) ? Doc.GetDC()->GetBits() : 24; // return CBit(DcBits, c.c, c.Bits, Palette->GetPalette()); if (Palette) { Palette->GetBack(&c); } return c; } void ImgWnd::Back(ImgCol c) { if (Palette) { Palette->SetBack(c); } } bool ImgWnd::TransparentPaste() { return OptTransparentPaste.Value(); } void ImgWnd::TransparentPaste(bool b) { OptTransparentPaste.Value(b); } bool ImgWnd::UseForeColour() { return UseFore; } void ImgWnd::UseForeColour(bool b) { UseFore = b; } int ImgWnd::Alpha() { return AlphaLevel; } void ImgWnd::Alpha(int i) { AlphaLevel = i; } bool ImgWnd::FillPrimitives() { return FillPrim = OptFillPrimitives.Value(); } void ImgWnd::FillPrimitives(bool b) { FillPrim = b; OptFillPrimitives.Value(b); } bool ImgWnd::CrossHairs() { return OptGuideLines.Value(); } void ImgWnd::CrossHairs(bool b) { OptGuideLines.Value(b); } void ImgWnd::BeforeAction() { if (CaptureUndoLevel != 0) return; // save document state to create undo information with if (!LastFrame) { LastFrame = new ImgMemDC; } if (LastFrame && Doc.GetDC()) { // create the frame buffer if not the right size if (LastFrame->X() != Doc.GetDC()->X() || LastFrame->Y() != Doc.GetDC()->Y() || LastFrame->GetBits() != Doc.GetDC()->GetBits()) { LastFrame->Create(Doc.GetDC()->X(), Doc.GetDC()->Y(), Doc.GetDC()->GetColourSpace()); LastFrame->HasAlpha(Doc.GetDC()->AlphaDC() != 0); } // save the current info // LSurface *In = Doc.GetDC(); LPalette *p = Doc.GetDC()->Palette(); if (p) { LastFrame->Palette(new LPalette(p)); } LastUpdate.ZOff(-1, -1); } } void ImgWnd::BeforeUpdate(LRect *a, bool PaletteChange) { auto pDC = Doc.GetDC(); if (!pDC) return; // save undo information if (LastFrame) { if (a) { if (LastUpdate.Valid()) { // adding to the updated region... LRect u(LastUpdate); u.Union(a); LRegion c(u); c.Subtract(&LastUpdate); for (LRect *r=c.First(); r; r=c.Next()) { LastFrame->Blt(r->x1, r->y1, pDC, r); if (LastFrame->AlphaDC() && pDC->AlphaDC()) { LastFrame->AlphaDC()->Blt(r->x1, r->y1, pDC->AlphaDC(), r); } } LastUpdate = u; } else { // setting the update region... LastUpdate = *a; LastFrame->Blt(a->x1, a->y1, pDC, a); if (LastFrame->AlphaDC() && pDC->AlphaDC()) { LastFrame->AlphaDC()->Blt(a->x1, a->y1, pDC->AlphaDC(), a); } } } else { // don't know where to update so grab everything... LastUpdate.ZOff(pDC->X()-1, pDC->Y()-1); LastFrame->Blt(0, 0, pDC); if (LastFrame->AlphaDC() && pDC->AlphaDC()) { LastFrame->AlphaDC()->Blt(0, 0, pDC->AlphaDC()); } } } // setup for drawing... ImgCol Cur = (UseFore) ? Fore() : Back(); pDC->Colour(CBit(pDC->GetBits(), Cur.c, Cur.Bits, Palette->GetPalette())); pDC->Op(Operator); if (pDC->AlphaDC()) { pDC->AlphaDC()->Colour(Cur.a); } LApplicator *pApp = pDC->Applicator(); if (pApp) { if (Operator == GDC_ALPHA) { pApp->SetVar(GAPP_ALPHA_A, AlphaLevel); pApp->SetVar(GAPP_ALPHA_PAL, (NativeInt)pDC->Palette()); } LVariant Angle; if (GetOptions()->GetValue(OPT_LinearAngle, Angle)) { pApp->SetVar(GAPP_ANGLE, Angle.CastInt32()); } pApp->SetVar(GAPP_BOUNDS, (NativeInt)a); pApp->SetVar(GAPP_BACKGROUND, (UseFore) ? Back().c : Fore().c); } } void ImgWnd::AfterAction() { if (Doc.GetDC()) { // save change history into undo queue if (UndoEnabled && CaptureUndoLevel == 0 && LastFrame) { PushUndoEvent(new ImgEvent(LastFrame, Doc.GetDC(), LastUpdate)); } // set changed flag SetDirty(true, NULL); } } void ImgWnd::Update(LRect *Up, bool Now) { if (Zoom) { Zoom->Update(Up); #ifdef WIN32 if (Now) UpdateWindow(Zoom->Handle()); #endif } if (Normal) { Normal->Update(Up); #ifdef WIN32 if (Now) UpdateWindow(Normal->Handle()); #endif } } void ImgView::OnMouseMove(LMouse &m) { ImgMouse im(m, this); Convert(im.pt, m.x, m.y); im.x = (int)im.pt.x; im.y = (int)im.pt.y; App->UserMouseMove(im); } void ImgView::OnMouseClick(LMouse &m) { ImgMouse im(m, this); Convert(im.pt, m.x, m.y); im.x = (int)im.pt.x; im.y = (int)im.pt.y; Capture(m.Down()); App->UserMouseClick(im); } void ImgView::OnMouseEnter(LMouse &m) { ImgMouse im(m, this); Convert(im.pt, m.x, m.y); im.x = (int)im.pt.x; im.y = (int)im.pt.y; App->UserMouseEnter(im); if (IsCapturing()) SetPulse(); } void ImgView::OnMouseExit(LMouse &m) { ImgMouse im(m, this); Convert(im.pt, m.x, m.y); im.x = (int)im.pt.x; im.y = (int)im.pt.y; App->UserMouseExit(im); if (IsCapturing()) SetPulse(150); } bool ImgView::OnKey(LKey &k) { ImgKey ik(k, this); App->UserKey(ik); return false; } void ImgView::OnPulse() { LMouse m; if (!GetMouse(m)) return; ImgMouse im(m, this); Convert(im.pt, m.x, m.y); im.x = (int)im.pt.x; im.y = (int)im.pt.y; ScrollToPoint(LPoint(im.x, im.y)); App->UserMouseMove(im); } void ImgWnd::UserKey(ImgKey &k) { if (Current) Current->Key(k); } void ImgWnd::UserMouseClick(ImgMouse &m) { char Str[64]; ClickedView = m.view; if (m.Down()) { d->LastMousePos = m; MouseStart = m; sprintf_s(Str, sizeof(Str), LLoadString(IDS_MOUSEMOVE), m.x, m.y, abs(m.x-MouseStart.x)+1, abs(m.y-MouseStart.y)+1); } else { sprintf_s(Str, sizeof(Str), LLoadString(IDS_MOUSEPOS), m.x, m.y); SetPulse(); } SetStatusText(Str, STATUS_POSINFO); if (Current) Current->MouseClick(m); } void ImgWnd::UserMouseEnter(ImgMouse &m) { if (Current) Current->MouseEnter(m); } void ImgWnd::UserMouseExit(ImgMouse &m) { if (Current) Current->MouseExit(m); } void ImgWnd::UserMouseMove(ImgMouse &m) { if (d->LastMousePos == m) return; char Str[256] = ""; d->LastMousePos = m; if (Current) Current->MouseMove(m); bool Tile = DisplayTile() != false; int Tx = TileX(); int Ty = TileY(); int TileMx = m.x / Tx; int TileMy = m.y / Ty; int TileWidth = GetDoc() ? (GetDoc()->X() + (Tx - 1)) / Tx : 1; int TilePos = (TileWidth * TileMy) + TileMx; if (m.Down()) { const char *Fmt = LLoadString(Tile ? IDS_MOUSEMOVE_GUIDE : IDS_MOUSEMOVE); if (Fmt) sprintf_s(Str, sizeof(Str), Fmt, m.x, m.y, abs(m.x-MouseStart.x)+1, abs(m.y-MouseStart.y)+1, TileMx, TileMy, TilePos); } else { const char *Fmt = LLoadString(Tile ? IDS_MOUSEPOS_GUIDE : IDS_MOUSEPOS); if (Fmt) sprintf_s(Str, sizeof(Str), Fmt, m.x, m.y, TileMx, TileMy, TilePos); } SetStatusText(Str, STATUS_POSINFO); auto pDC = Doc.GetDC(); if (!pDC) return; auto c = pDC->Get(m.x, m.y); switch (pDC->GetBits()) { case 8: { auto Pal = pDC->Palette(); auto p = Pal ? (*Pal)[c] : NULL; if (p) { sprintf_s(Str, sizeof(Str), LLoadString(IDS_I8_FORMAT), c, p->r, p->g, p->b, p->r, p->g, p->b); } else { sprintf_s(Str, sizeof(Str), "%i", c); } break; } default: { #define toDbl(i) ((double)(i)/255.0) LColour col(c, pDC->GetBits()); sprintf_s(Str, sizeof(Str), "int:%i,%i,%i,%i hex:%2.2x,%2.2x,%2.2x,%2.2x dbl:%.3f,%.3f,%.3f,%.3f", col.r(), col.g(), col.b(), col.a(), col.r(), col.g(), col.b(), col.a(), toDbl(col.r()), toDbl(col.g()), toDbl(col.b()), toDbl(col.a())); break; } } SetStatusText(Str, STATUS_COLOURINFO); } void ImgWnd::ZoomCenter(int x, int y) { if (!Zoom) return; LRect Pos = Zoom->GetPos(); x = MAX(x - (Pos.X() / GridSize() / 2), 0); y = MAX(y - (Pos.Y() / GridSize() / 2), 0); Zoom->SetScrollPos(x, y); Zoom->Update(); #ifdef WIN32 UpdateWindow(Zoom->Handle()); #endif } void ImgWnd::ZoomTo(LRect r) { LSurface *pDC = Doc.GetDC(); if (!Normal || !pDC) return; LZoomView::ViewportInfo vpi; double Sx = (double) r.X() / pDC->X(); double Sy = (double) r.Y() / pDC->Y(); double S = MAX(Sx, Sy); if (S < 1.0) { double F = 1.0 / S; F = ceil(F); vpi.Zoom = (int)F - 1; vpi.Sx = r.x1; vpi.Sy = r.y1; Normal->SetViewport(vpi); } } bool ImgWnd::Empty() { if (GetDC()) { LAutoPtr Ptr; return SetDC(Ptr); } else { LCloseApp(); } return true; } void ImgWnd::GetFileTypes(LFileSelect *Dlg, bool Write) { EnumAvailableFormats(Dlg, Write ? FILTER_CAP_WRITE : FILTER_CAP_READ); } -bool ImgWnd::OpenFile(const char *FileName, bool Ro) +void ImgWnd::OpenFile(const char *FileName, bool Ro, std::function Callback) { bool Status = false; if (FileName) { DetachTool(); StartOp(); if (!Doc.Load(this, FileName)) { char *ErrorMsg = Doc.Props.GetAttr(LGI_FILTER_ERROR); LgiMsg( this, LLoadString(IDS_ERROR_LOADING_IMAGE), AppName, MB_OK, FileName, ErrorMsg ? ErrorMsg : (char*)""); } else { Status = true; EmptyUndoQueue(); char *Info = Doc.Props.GetAttr(LGI_FILTER_INFO); if (Info && StatusInfo[0]) { StatusInfo[0]->Name(Info); } } EndOp(); bool Undo = UndoEnabled; UndoEnabled = false; OnDcChange(); Update(); AttachTool(); UndoEnabled = Undo; } - return Status; + + if (Callback) + Callback(Status); } LDom *ImgWnd::GetUserDom() { return d->UserDom; } void ImgWnd::SetUserDom(LDom *b) { d->UserDom = b; } void ImgWnd::SaveFile(const char *FileName, std::function Callback) { if (!FileName) { if (Callback) Callback(NULL, false); return; } if (!Doc.GetDC()) { if (Callback) Callback(NULL, false); return; } DetachTool(); StartOp(); d->DefaultDom.Empty(); Doc.Save(this, FileName, 0, d->UserDom ? d->UserDom : &d->DefaultDom, [this, Callback](auto fn, auto ok) { // We do all this regardless of the status: EndOp(); OnDcChange(); Update(); AttachTool(); if (!ok) { int Cancel = Doc.Props.GetAsInt(OPT_Cancel); if (Cancel < 1) { char *ErrorMsg = Doc.Props.GetAttr(OPT_ErrorMsg); LgiMsg( this, LLoadString(IDS_ERROR_SAVING_IMAGE), AppName, MB_OK, ErrorMsg?ErrorMsg:(char*)""); } } if (Callback) Callback(fn, ok); }); } bool ImgWnd::SerializeFile(LAutoPtr &pDC, const char *FileName, bool Write) { bool Status = false; auto f = LFilterFactory::New(FileName, Write ? FILTER_CAP_WRITE : FILTER_CAP_READ, 0); if (f) { if (!Write) { pDC.Reset(new ImgMemDC); } if (pDC) { LHashDom Hd; LVariant v; f->Props = &Hd; f->SetProgress(Meter); f->Props->SetValue("Parent", v = (void*)this); f->Props->SetValue("Fore", v = (int)Fore().c); f->Props->SetValue("Back", v = (int)Back().c); LFile File; if (File.Open(FileName, (Write) ? O_WRITE : O_READ)) { if ((Write) ? f->WriteImage(&File, pDC) : f->ReadImage(pDC, &File)) { Status = true; } else { pDC.Reset(); } } } } return Status; } void ImgWnd::OffsetImage(int x, int y) { LSurface *pDC = GetDC(); if (!pDC) return; DetachTool(); LMemDC *pTemp = new ImgMemDC; if (pTemp && pTemp->Create(pDC->X(), pDC->Y(), pDC->GetColourSpace())) { int Sx = ((x > 0) ? -1 : 1) * pDC->X(); int Sy = ((y > 0) ? -1 : 1) * pDC->Y(); pTemp->Blt(0, 0, pDC); pDC->Blt(x, y, pTemp); pDC->Blt(x+Sx, y, pTemp); pDC->Blt(x, y+Sy, pTemp); pDC->Blt(x+Sx, y+Sy, pTemp); DeleteObj(pTemp); LastOffset.x = x; LastOffset.y = y; Update(); } AttachTool(); } void ImgWnd::DrawBackground(LZoomView *View, LSurface *pDC, LPoint Offset, LRect *Rc) { if (!pDC) return; LRect All = pDC->Bounds(); if (!Rc) Rc = &All; switch (d->Background) { case BgBlack: { pDC->Colour(L_BLACK); pDC->Rectangle(Rc); break; } default: case BgGrey: { LSystemColour c[2] = { L_MED, L_HIGH }; LRect RcOff = *Rc; RcOff.Offset(Offset.x, Offset.y); for (int y = RcOff.y1 >> GREY_SQUARE_SHIFT; y <= RcOff.y2 >> GREY_SQUARE_SHIFT; y++) { for (int x = RcOff.x1 >> GREY_SQUARE_SHIFT; x <= RcOff.x2 >> GREY_SQUARE_SHIFT; x++) { auto col = c[(x&1)^(y&1)]; pDC->Colour(col); int dx = (x << GREY_SQUARE_SHIFT) - Offset.x; int dy = (y << GREY_SQUARE_SHIFT) - Offset.y; LRect r; r.ZOff((1 << GREY_SQUARE_SHIFT) - 1, (1 << GREY_SQUARE_SHIFT) - 1); r.Offset(dx, dy); pDC->Rectangle(&r); } } break; } case BgWhite: { pDC->Colour(L_WHITE); pDC->Rectangle(Rc); break; } } } void ImgWnd::DrawForeground(LZoomView *View, LSurface *Dst, LPoint Offset, LRect *Src) { if (Current) Current->DrawForeground(View, Dst, Offset, Src); } bool ImgWnd::GetVariant(const char *Name, LVariant &Value, const char *Array) { if (!Name) return false; if (_stricmp(Name, "doc") == 0) { Value = Doc.GetDC(); return true; } return false; } bool ImgWnd::SetVariant(const char *Name, LVariant &Value, const char *Array) { return false; } void ImgWnd::RunScript(LViewI *Parent, char *Script, const char *FileName, bool Debug) { if (!Script) return; LAutoPtr Code(new LCompiledCode); LVariant v; Code->Set("App", v = (LDom*)this); Code->Set("Parent", v = Parent->GetGView()); if (Debug) { LCompiler c; if (c.Compile(Code, &d->SysContext, &d->Context, FileName, Script, NULL)) { LVirtualMachine *Vm = new LVirtualMachine(d); if (Vm) { LVmDebugger *Dbg = Vm->OpenDebugger(Code); if (Dbg) { Dbg->SetCode(Code); LVmDebuggerWnd *DbgWnd = dynamic_cast(Dbg); Vm->Execute(Dbg->GetCode(), 0, DbgWnd ? DbgWnd->GetLog() : NULL, false, NULL); } } } } else { LStringPipe Console; LScriptEngine Engine(Parent, &d->Context, d); Engine.SetConsole(&Console); Engine.GetConsole()->Print("Compiling script...\n"); if (Engine.Compile(Code, &d->Context, Script, FileName)) { Engine.GetConsole()->Print("Starting script...\n"); Engine.Run(Code); Engine.GetConsole()->Print("Script ended.\n"); } Parent->PostEvent(M_OUTPUT, (LMessage::Param)Console.NewStr()); Update(); } DeleteArray(Script); } ////////////////////////////////////////////////////////////////////////////// ImgMemDC::~ImgMemDC() { } bool ImgMemDC::GetVariant(const char *Name, LVariant &Value, const char *Array) { if (!Name) return false; if (_stricmp(Name, "x") == 0) { Value = X(); return true; } else if (_stricmp(Name, "y") == 0) { Value = Y(); return true; } else if (_stricmp(Name, "bits") == 0) { Value = GetBits(); return true; } return false; } bool ImgMemDC::SetVariant(const char *Name, LVariant &Value, const char *Array) { return false; } ////////////////////////////////////////////////////////////////////////////// ImgDocument::ImgDocument(ImgMemDC *new_dc) { pDC.Reset(new_dc); Dirty = false; Meter = 0; ZoomViewIdx = 0; } ImgDocument::~ImgDocument() { } const char *ImgDocument::Name() { return LBase::Name(); } bool ImgDocument::Name(const char *p) { return LBase::Name(p); } void ImgDocument::VerifyExtension(LString &FileName, const char *Type) { if (!FileName || !Type) return; // Extract extension auto Dir = strrchr(FileName, DIR_CHAR); auto Ext = strrchr(Dir ? Dir : FileName.Get(), '.'); if (!Ext) { FileName += LString(".") + Type; } else { FileName = FileName(0, Ext - FileName.Get()) + "." + Type; } } int ImgDocument::GetZoom() { return Views.IdxCheck(ZoomViewIdx) ? Views[ZoomViewIdx]->GetZoom() : 0; } void ImgDocument::SetZoom(int z) { if (Views.IdxCheck(ZoomViewIdx)) Views[ZoomViewIdx]->SetZoom(z); } void ImgDocument::Update(LRect *Where) { for (int i=0; iUpdate(Where); } } void ImgDocument::AddView(ImgView *v) { if (!Views.HasItem(v)) { v->SetSurface(pDC, false); Views.Add(v); } } void ImgDocument::RemoveView(ImgView *v) { if (Views.HasItem(v)) { v->SetSurface(NULL, false); Views.Delete(v); } } void ImgDocument::SetDC(LAutoPtr pdc) { if (pDC) { for (int i=0; iSetSurface(NULL, false); } pDC = pdc; if (pDC) { for (int i=0; iSetSurface(pDC, false); } } void ImgDocument::Save(LView *Parent, const char *FileName, const char *Type, LDom *Dom, std::function Callback) { if (!FileName || !pDC || !Dom) { LAssert(!"Invalid Param."); if (Callback) Callback(FileName, false); return; } LString File = FileName; VerifyExtension(File, Type); F = LFilterFactory::New(Type?Type:File.Get(), FILTER_CAP_WRITE, 0); if (!F) { Props.SetAttr(OPT_ErrorMsg, LLoadString(IDS_ERROR_NOFILTER)); if (Callback) Callback(FileName, false); return; } LVariant v; F->Props = Dom; Dom->SetValue(LGI_FILTER_PARENT_WND, v = Parent); switch (F->GetFormat()) { case LFilter::FmtJpeg: { if (!Dom->GetValue(LGI_FILTER_QUALITY, v)) { auto j = dynamic_cast(F.Get()); if (!j) { LAssert(!"Wrong obj."); return; } auto Dlg = new LJpegOptions(j, pDC); Dlg->DoModal([this, Dlg, Callback, File](auto dlg, auto code) { if (code == IDOK) { LVariant v; F->Props->SetValue(LGI_FILTER_QUALITY, v = Dlg->Quality); F->Props->SetValue(LGI_FILTER_SUBSAMPLE, v = Dlg->SubSample); F->Props->SetValue(LGI_FILTER_DPI_X, v = Dlg->Dpi.x); F->Props->SetValue(LGI_FILTER_DPI_Y, v = Dlg->Dpi.y); ContinueSave(File, Callback); } delete dlg; }); // The LJpegOptions callback will continue the save operation... return; } break; } case LFilter::FmtPng: { #ifdef FILTER_UI LVariant Transparent; if (!Dom->GetValue(LGI_FILTER_TRANSPARENT, Transparent) || Transparent.IsNull()) { // put up a dialog to ask about transparent colour auto Dlg = new LTransparentDlg(Parent, &Transparent); Dlg->DoModal([this, Dlg, Dom, File, Callback](auto dlg, auto code) { LVariant v; if (code) { ContinueSave(File, Callback); } else { Dom->SetValue("Cancel", v = 1); if (Callback) Callback(File, false); } delete dlg; }); // The LTransparentDlg callback will continue the save operation... return; } #else #warning "FILTER_UI not defined." #endif break; } case LFilter::FmtGif: { // Prep background gif channel LSurface *pA = pDC->AlphaDC(); if (pA) { ImgCol b = MainWnd->Back(); // Find a suitable background index int BackIdx = b.c; if (b.Bits != 8 || b.a != 0xff) { bool Used[256]; ZeroObj(Used); for (int y=0; yY(); y++) { uchar *p = (*pDC)[y]; uchar *pe = p + pDC->X(); uchar *a = (*pA)[y]; while (p < pe) { if (*a) Used[*p] = true; p++; a++; } } for (int i=0; i<256; i++) { if (!Used[i]) { BackIdx = i; break; } } } // Pass the background index to the filter F->Props->SetValue(LGI_FILTER_BACKGROUND, v = BackIdx); // Set all the transparent pixels to the background index pDC->Colour(BackIdx); for (int y=0; yY(); y++) { for (int x=0; xX(); x++) { if (!pA->Get(x, y)) pDC->Set(x, y); } } } break; } } ContinueSave(File, Callback); } void ImgDocument::ContinueSave(LString File, std::function Callback) { LFile Out; if (!Out.Open(File, O_WRITE)) { Props.SetAttr(OPT_ErrorMsg, LLoadString(IDS_ERROR_OPEN_WRITING)); if (Callback) Callback(File, false); return; } F->SetProgress(Meter); Out.SetSize(0); LVariant v; if (!F->Props->GetValue(LGI_FILTER_FOREGROUND, v)) F->Props->SetValue(LGI_FILTER_FOREGROUND, v = (int)MainWnd->Fore().c); if (!F->Props->GetValue(LGI_FILTER_BACKGROUND, v)) F->Props->SetValue(LGI_FILTER_BACKGROUND, v = (int)MainWnd->Back().c); LSurface *ColourConverted = NULL; if (ColourProfile) { LStringPipe p; if (ColourProfile->Save(&p)) { int Len = (int)p.GetSize(); char *Data = new char[Len]; if (Data) { p.Read(Data, Len); v.SetBinary(Len, Data); F->Props->SetValue(LGI_FILTER_COLOUR_PROF, v); DeleteArray(Data); // Create a colour converted copy of the image for saving // The in memory representation is always in sRGB because // thats what we can display. ColourConverted = new ImgMemDC(pDC); if (ColourConverted) { LIccProfile sRGB; if (sRGB.CreateNamed("sRGB")) { ColourProfile->Convert(ColourConverted, pDC, &sRGB); } } } } } auto Status = F->WriteImage(&Out, ColourConverted ? ColourConverted : pDC); DeleteObj(ColourConverted); if (Status == LFilter::IoSuccess) { Dirty = false; } else { LVariant v; F->Props->GetValue(OPT_Cancel, v); if (v.CastInt32()) { Props.SetAttr(OPT_Cancel, 1); } else { if (F->Props->GetValue(OPT_ErrorMsg, v)) { Props.SetAttr(OPT_ErrorMsg, v.Str()); } } } if (Meter) Meter->Value(0); Name(File); F.Reset(); if (Callback) Callback(File, Status == LFilter::IoSuccess); } bool ImgDocument::Load(LView *Parent, const char *FileName, const char *Type) { LFilter::IoStatus Status = LFilter::IoUnsupportedFormat; auto F = LFilterFactory::New(FileName, FILTER_CAP_READ, 0); if (F) { ColourProfile.Reset(); LFile In; if (In.Open(FileName, O_READ)) { LVariant v; LHashDom Hd; F->Props = &Hd; F->SetProgress(Meter); F->Props->SetValue(LGI_FILTER_PARENT_WND, v = static_cast(MainWnd)); if (!F->Props->GetValue(LGI_FILTER_FOREGROUND, v)) F->Props->SetValue(LGI_FILTER_FOREGROUND, v = (int)MainWnd->Fore().c); if (!F->Props->GetValue(LGI_FILTER_BACKGROUND, v)) F->Props->SetValue(LGI_FILTER_BACKGROUND, v = (int)MainWnd->Back().c); LAutoPtr MemDC(new ImgMemDC); if (MemDC) { Status = F->ReadImage(MemDC, &In); if (Status == LFilter::IoSuccess) { Dirty = false; // Set the palette if (MemDC->GetBits() <= 8 && !MemDC->Palette()) { LPalette *Pal = new LPalette; if (Pal) { int Colours = 1 << MemDC->GetBits(); Pal->SetSize(Colours); for (int i=0; ir = Rgb->g = Rgb->b = i * 255 / (Colours - 1); } } MemDC->Palette(Pal, true); } } // Setup the colour profile. LVariant v; if (F->Props->GetValue(LGI_FILTER_COLOUR_PROF, v)) { ColourProfile.Reset(new LIccProfile); if (ColourProfile) { LMemStream p(v.Value.Binary.Data, v.Value.Binary.Length); if (ColourProfile->Open(&p)) { // Convert into sRGB for editing / display LIccProfile sRGB; if (sRGB.CreateNamed("sRGB")) { sRGB.Convert(MemDC, MemDC, ColourProfile); } } } } if (F->Props->GetValue(LGI_FILTER_INFO, v)) { Props.SetAttr(LGI_FILTER_INFO, v.Str()); } SetDC(MemDC); } else { LVariant v; if (F->Props->GetValue(LGI_FILTER_ERROR, v)) { Props.SetAttr(LGI_FILTER_ERROR, v.Str()); } LAutoPtr Empty; SetDC(Empty); } } if (Meter) Meter->Value(0); Name(FileName); } else { Props.SetAttr(OPT_ErrorMsg, LLoadString(IDS_ERROR_OPEN_READING)); } } else { Props.SetAttr(OPT_ErrorMsg, LLoadString(IDS_ERROR_NOFILTER)); } return Status == LFilter::IoSuccess; } //////////////////////////////////////////////////////////////////////////////// typedef int EventInt; ImgEvent::ImgEvent(LSurface *From, LSurface *To, LRect &Bounds) { Length = 0; Palette = 0; if (From && To && To->GetBits() == From->GetBits()) { // Check for differences in the palette LPalette *FromPal = From->Palette(); LPalette *ToPal = To->Palette(); if (FromPal && ToPal) { bool Different = false; for (int i=0; iGetSize(); i++) { GdcRGB *f = (*FromPal)[i]; GdcRGB *t = (*ToPal)[i]; if (f && t) { if (f->r != t->r || f->g != t->g || f->b != t->b) { Different = true; break; } } } if (FromPal->GetSize() != ToPal->GetSize() || Different) { // Save the old palette Palette = new LPalette(FromPal); } } // Check for differences in the bits LMemQueue Pipe; Bounds.x2 = MIN(Bounds.x2, From->X()-1); Bounds.x2 = MIN(Bounds.x2, To->X()-1); Bounds.y2 = MIN(Bounds.y2, From->Y()-1); Bounds.y2 = MIN(Bounds.y2, To->Y()-1); int Bpp = From->GetBits() >> 3; EventInt n; // step through image(s) for (int y=0; yY(); y++) { if (y>=Bounds.y1 && y<=Bounds.y2) { for (int x=Bounds.x1; x<=Bounds.x2;) { // search for start of dissimilar pixels for (; From->Get(x, y)==To->Get(x, y) && x<=Bounds.x2; x++) ; if (x<=Bounds.x2) { int Start = MAX(x, 0); for (; From->Get(x, y)!=To->Get(x, y) && x<=Bounds.x2; x++); int Length = (x - Start) * Bpp; // push block onto pipe if (Length > 0) { // Position/Length n = Start; Pipe.Write((uchar*)&n, sizeof(n)); n = Length; Pipe.Write((uchar*)&n, sizeof(n)); // Pixels uchar *Pixels = (*From)[y]; if (Pixels) { Pipe.Write(Pixels + (Start * Bpp), Length); } } } } } n = -1; // EOL Pipe.Write((uchar*)&n, sizeof(n)); } n = -2; // EOF Pipe.Write((uchar*)&n, sizeof(n)); // save out result Length = (int)Pipe.GetSize(); if (Data.Reset(new uchar[Length])) { Pipe.Read(Data, Length); } } } ImgEvent::~ImgEvent() { DeleteObj(Palette); } bool ImgEvent::Apply(LSurface *pDC) { if (pDC) { // Palette LPalette *Pal = pDC->Palette(); if (Palette && Pal) { LPalette *Buffer = new LPalette(Pal); if (Buffer) { Pal->Set(Palette); Palette->Set(Buffer); DeleteObj(Buffer); } } // Image diff if (Data) { int y = 0; int Bpp = pDC->GetBits() >> 3; EventInt *d = (EventInt*) Data.Get(); LAutoPtr Buffer(new uchar[pDC->X() * Bpp]); if (Buffer) { while (y < pDC->Y()) { if (*d >= 0) { EventInt x = *d++; EventInt Length = *d++; uchar *Pixels = (*pDC)[y]; if (Pixels) { Pixels += (x * Bpp); memcpy(Buffer, d, Length); memcpy(d, Pixels, Length); memcpy(Pixels, Buffer, Length); } d = (EventInt*) (((char*)d) + Length); } else if (*d == -1) { // EOL y++; d++; } else { // EOF break; } } } } } return false; } uint ImgEvent::Sizeof() { uint Size = (Data) ? Length : 0; if (Palette) { Size += sizeof(*Palette) + (sizeof(GdcRGB) * Palette->GetSize()); } return Size; } ////////////////////////////////////////////////////////////////////// ImgTools::ImgTools(ImgWnd *app, int x, int y) : LPanel("Tools", x, false) { App = app; Alignment(GV_EDGE_LEFT); SetClosedSize(y); } void ImgTools::OnCreate() { LPanel::OnCreate(); } void ImgTools::OnPaint(LSurface *pDC) { LPanel::OnPaint(pDC); } ///////////////////////////////////////////////////////////////////////// int LgiMain(OsAppArguments &AppArgs) { #ifdef _DEBUG const char *Build = "Debug"; #else const char *Build = "Release"; #endif printf("i.Mage v%s %s\n", IMAGE_VER, Build); LApp a(AppArgs, "i.Mage"); if (a.IsOk()) { printf("Screen: %ix%i@%i\n", GdcD->X(), GdcD->Y(), GdcD->GetBits()); if ((a.AppWnd = new ImgWnd)) a.Run(); } return 0; }