local function Set(list) local set={} for _,l in ipairs(list) do set[l]=true end return set end local Refresh=Set{"CtrlLeft","CtrlRight","CtrlUp","CtrlDown","CtrlClear","CtrlNum4","CtrlNum6","CtrlNum2","CtrlNum8"} local Mouse1=Set{"MsWheelDown","MsWheelUp"} local Mouse2=Set{"MsM1Click"} local Mouse3=Set{"MsLClick"} local BGColor=function() return 0xff000000 end local F=far.Flags local K=far.Colors local ffi=require("ffi") local C=ffi.C local function safe_cdef(def) pcall(ffi.cdef,def) end safe_cdef([[ typedef enum GpStatus { Ok = 0, GenericError = 1, InvalidParameter = 2, OutOfMemory = 3, ObjectBusy = 4, InsufficientBuffer = 5, NotImplemented = 6, Win32Error = 7, WrongState = 8, Aborted = 9, FileNotFound = 10, ValueOverflow = 11, AccessDenied = 12, UnknownImageFormat = 13, FontFamilyNotFound = 14, FontStyleNotFound = 15, NotTrueTypeFont = 16, UnsupportedGdiplusVersion = 17, GdiplusNotInitialized = 18, PropertyNotFound = 19, PropertyNotSupported = 20, ProfileNotFound = 21 } GpStatus; ]]) safe_cdef([[ typedef struct GdiplusStartupInput { uint32_t GdiplusVersion; void* DebugEventCallback; int SuppressBackgroundThread; int SuppressExternalCodecs; } GdiplusStartupInput; ]]) safe_cdef([[ typedef struct GdiplusStartupOutput { void* NotificationHook; void* NotificationUnhook; } GdiplusStartupOutput; ]]) safe_cdef([[ typedef struct { unsigned long Data1; unsigned short Data2; unsigned short Data3; unsigned char Data4[8]; } GUID; ]]) safe_cdef([[ typedef struct PropertyItem { unsigned long id; unsigned long length; unsigned short type; void* value; } PropertyItem; ]]) ffi.cdef[[ GpStatus GdiplusStartup(void**,const GdiplusStartupInput*,GdiplusStartupOutput*); void GdiplusShutdown(void*); GpStatus GdipLoadImageFromFile(const wchar_t*,void** GpImage); GpStatus GdipCreateFromHDC(void* HDC,void** GpGraphics); GpStatus GdipDrawImageRectI(void* GpGraphics,void* GpImage,int,int,int,int); GpStatus GdipGetImageWidth(void* GpImage,unsigned int*); GpStatus GdipGetImageHeight(void* GpImage,unsigned int*); GpStatus GdipDeleteGraphics(void* GpGraphics); GpStatus GdipDisposeImage(void* GpImage); GpStatus GdipImageGetFrameDimensionsCount(void* GpImage,unsigned int*); GpStatus GdipImageGetFrameDimensionsList(void* GpImage,GUID*,unsigned int); GpStatus GdipImageGetFrameCount(void* GpImage,const GUID*,unsigned int*); GpStatus GdipImageSelectActiveFrame(void* GpImage,const GUID*,unsigned int); GpStatus GdipFillRectangleI(void* GpGraphics,void* GpBrush,int,int,int,int); GpStatus GdipCreateSolidFill(unsigned long,void** GpSolidFill); GpStatus GdipDeleteBrush(void* GpBrush); GpStatus GdipCreateBitmapFromScan0(int,int,int,int,unsigned char*,void** GpBitmap); GpStatus GdipGetImageGraphicsContext(void* GpImage,void** GpGraphics); GpStatus GdipGetPropertyItemSize(void* GpImage,unsigned long,unsigned int*); GpStatus GdipGetPropertyItem(void* GpImage,unsigned long,unsigned int,PropertyItem*); ]] safe_cdef([[ typedef struct _COORD { short X; short Y; } COORD; ]]) safe_cdef([[ typedef struct _SMALL_RECT { short Left; short Top; short Right; short Bottom; } SMALL_RECT; ]]) safe_cdef([[ typedef struct _CONSOLE_SCREEN_BUFFER_INFO { COORD dwSize; COORD dwCursorPosition; unsigned short wAttributes; SMALL_RECT srWindow; COORD dwMaximumWindowSize; } CONSOLE_SCREEN_BUFFER_INFO; ]]) safe_cdef([[ typedef struct _CONSOLE_SCREEN_BUFFER_INFOEX { unsigned long cbSize; COORD dwSize; COORD dwCursorPosition; unsigned short wAttributes; SMALL_RECT srWindow; COORD dwMaximumWindowSize; unsigned short wPopupAttributes; int bFullscreenSupported; unsigned long ColorTable[16]; } CONSOLE_SCREEN_BUFFER_INFOEX; ]]) safe_cdef([[ typedef struct _CONSOLE_FONT_INFO { unsigned long nFont; COORD dwFontSize; } CONSOLE_FONT_INFO; ]]) ffi.cdef[[ void* GetDC(void* HWND); int ReleaseDC(void* HWND,void* HDC); int32_t GetConsoleScreenBufferInfo(void* hConsoleOutput,CONSOLE_SCREEN_BUFFER_INFO* lpConsoleScreenBufferInfo); int32_t GetConsoleScreenBufferInfoEx(void* hConsoleOutput,CONSOLE_SCREEN_BUFFER_INFOEX* lpConsoleScreenBufferInfoEx); int32_t GetCurrentConsoleFont(void* hConsoleOutput,int32_t bMaximumWindow,CONSOLE_FONT_INFO* lpConsoleCurrentFont); COORD GetConsoleFontSize(void* hConsoleOutput,unsigned long nFont); void* GetStdHandle(uint32_t nStdHandle); ]] local gdiplus=ffi.load("gdiplus") local handle=ffi.new("void*[1]") local startup=ffi.new("GdiplusStartupInput") startup.GdiplusVersion=1 gdiplus.GdiplusStartup(handle,startup,ffi.NULL) local Symbol=0 local function ToWChar(str) str=win.Utf8ToUtf16(str) local result=ffi.new("wchar_t[?]",#str/2+1) ffi.copy(result,str) return result end local function LongPath(path) local type=path:match([[^\\(.?.?)]]) return type and (([[?\]]==type or [[.\]]==type) and path or [[\\?\UNC]]..path:sub(2)) or [[\\?\]]..path end local function BGR2RGB(color) return bit64.bor(bit64.band(bit64.rshift(color,16),0xff),bit64.band(color,0xff00ff00),bit64.band(bit64.lshift(color,16),0xff0000)) end local function PointInRect(point,rect) return point.MousePositionX>=rect.left and point.MousePositionX<=rect.right and point.MousePositionY>=rect.top and point.MousePositionY<=rect.bottom end local function InitBGColor() local color=far.AdvControl(F.ACTL_GETCOLOR,K.COL_PANELTEXT) local bgcolor if bit64.band(color.Flags,F.FCF_BG_4BIT)~=0 then local info=ffi.new("CONSOLE_SCREEN_BUFFER_INFOEX") info.cbSize=ffi.sizeof(info) C.GetConsoleScreenBufferInfoEx(C.GetStdHandle(-11),info) bgcolor=bit64.bor(BGR2RGB(info.ColorTable[bit64.band(color.BackgroundColor,0xf)]),0xff000000) else bgcolor=BGR2RGB(color.BackgroundColor) end BGColor=function() return bgcolor end end local function InitImage(filename) local wnd=far.AdvControl(F.ACTL_GETFARHWND) local dc=C.GetDC(wnd) local image=ffi.new("void*[1]") local status=gdiplus.GdipLoadImageFromFile(ToWChar(LongPath(filename)),image) if status==0 then local graphics=ffi.new("void*[1]") gdiplus.GdipCreateFromHDC(dc,graphics) local width=ffi.new("unsigned int[1]") gdiplus.GdipGetImageWidth(image[0],width) local height=ffi.new("unsigned int[1]") gdiplus.GdipGetImageHeight(image[0],height) local count=ffi.new("unsigned int[1]") gdiplus.GdipImageGetFrameDimensionsCount(image[0],count) local dimensionIDs=ffi.new("GUID[?]",count[0]) gdiplus.GdipImageGetFrameDimensionsList(image[0],dimensionIDs,count[0]) local frames=ffi.new("unsigned int[1]") gdiplus.GdipImageGetFrameCount(image[0],dimensionIDs,frames) local delaysize=ffi.new("unsigned int[1]") gdiplus.GdipGetPropertyItemSize(image[0],0x5100,delaysize) local delay={} if delaysize[0]>0 then local delayraw_buffer=ffi.new("char[?]",delaysize[0]) local delayraw=ffi.cast("PropertyItem*",delayraw_buffer) gdiplus.GdipGetPropertyItem(image[0],0x5100,delaysize[0],delayraw) for ii=1,frames[0] do local value=ffi.cast("long*",delayraw.value)[ii-1] table.insert(delay,value>0 and value or 1) end end local brush=ffi.new("void*[1]") gdiplus.GdipCreateSolidFill(BGColor(),brush) local memimage=ffi.new("void*[1]") gdiplus.GdipCreateBitmapFromScan0(width[0],height[0],0,0x26200a,ffi.NULL,memimage) local memgraphics=ffi.new("void*[1]") gdiplus.GdipGetImageGraphicsContext(memimage[0],memgraphics) return {wnd=wnd,dc=dc,image=image,graphics=graphics,brush=brush,width=width[0],height=height[0],frames=frames[0],delay=delay,memory={image=memimage,graphics=memgraphics}} end return false end local function DeleteImage(params) gdiplus.GdipDisposeImage(params.image.image[0]) gdiplus.GdipDeleteGraphics(params.image.graphics[0]) gdiplus.GdipDeleteBrush(params.image.brush[0]) gdiplus.GdipDeleteGraphics(params.image.memory.graphics[0]) gdiplus.GdipDisposeImage(params.image.memory.image[0]) C.ReleaseDC(params.image.wnd,params.image.dc) end local function InitArea(params) local info=ffi.new("CONSOLE_SCREEN_BUFFER_INFO") local font=ffi.new("CONSOLE_FONT_INFO") local handle=C.GetStdHandle(-11) C.GetConsoleScreenBufferInfo(handle,info) if C.GetCurrentConsoleFont(handle,false,font) then font.dwFontSize=C.GetConsoleFontSize(handle,font.nFont) end --xp workaround local dx=font.dwFontSize.X local dy=font.dwFontSize.Y local DCRect={} DCRect.left=math.floor(dx*(params.DrawRect.left-info.srWindow.Left)) DCRect.right=math.floor(dx*(params.DrawRect.right+1-info.srWindow.Left)) DCRect.top=math.floor(dy*(params.DrawRect.top)) DCRect.bottom=math.floor(dy*(params.DrawRect.bottom+1)) return DCRect end local function RangingPic(params) local asp_dst=(params.DCRect.right-params.DCRect.left)/(params.DCRect.bottom-params.DCRect.top) local asp_src=params.image.width/params.image.height local dst_w,dst_h if asp_dst1 then local function ShowAnimation() if params.timer and not params.timer.Closed then params.timer.Enabled=false end UpdateImage(params) end params.timer=far.Timer(1000000,ShowAnimation) params.timer.Enabled=false params.image.frame=0 params.image.guid=win.Uuid("6aedbd6d-3fb5-418a-83a6-7f45229dc872") end elseif msg==F.DN_CTLCOLORDLGITEM then if param1==1 then local colors= { far.AdvControl(F.ACTL_GETCOLOR,params.CurPanel and K.COL_PANELSELECTEDTITLE or K.COL_PANELTITLE), far.AdvControl(F.ACTL_GETCOLOR,K.COL_PANELTITLE), far.AdvControl(F.ACTL_GETCOLOR,K.COL_PANELTEXT) } return colors end elseif msg==F.DN_DRAWDLGITEM then params.Redraw=true elseif msg==F.DN_DRAWDIALOGDONE then if params.Redraw then params.Redraw=false far.Text(0,0,0,nil) UpdateImage(params) end elseif msg==F.DN_INPUT then if param2.EventType==F.FOCUS_EVENT and param2.SetFocus then UpdateImage(params) elseif param2.EventType==F.MOUSE_EVENT then return not CloseDialog(function(key) return Mouse2[key] or Mouse3[key] and not PointInRect(param2,params.PanelRect) end) end elseif msg==F.DN_CONTROLINPUT then if param1==-1 then return true end return CloseDialog(function(key) return ((param2.EventType==F.KEY_EVENT or param2.EventType==F.FARMACRO_KEY_EVENT) and param2.KeyDown or param2.EventType==F.MOUSE_EVENT and Mouse1[key]) end) elseif msg==F.DN_RESIZECONSOLE then CloseDialog(nil,"CtrlR") elseif msg==F.DN_DRAGGED then return false elseif msg==F.DN_CLOSE then CloseTimer() end end params.DrawRect={} params.DrawRect.left=pinfo.PanelRect.left+1 params.DrawRect.top=pinfo.PanelRect.top+1 params.DrawRect.right=pinfo.PanelRect.right-1 params.DrawRect.bottom=pinfo.PanelRect.bottom-1 params.DCRect=InitArea(params) params.RangedRect=RangingPic(params) params.PanelRect=pinfo.PanelRect local dialog=far.DialogInit(win.Uuid("BFC62A3A-1ED5-4590-AF86-A582F6237E6F"),pinfo.PanelRect.left,pinfo.PanelRect.top,pinfo.PanelRect.right,pinfo.PanelRect.bottom,nil,items,F.FDLG_NODRAWSHADOW,DlgProc) far.DialogRun(dialog) far.DialogFree(dialog) DeleteImage(params) end end end Event { group="ViewerEvent"; action=function(id,event,param) if event==F.VE_READ then local xpanel=0 if far.AdvControl(F.ACTL_GETWINDOWTYPE).Type==F.WTYPE_PANELS then if panel.GetPanelInfo(nil,0).PanelType==F.PTYPE_INFOPANEL then return end local type=panel.GetPanelInfo(nil,1).PanelType if type==F.PTYPE_INFOPANEL then return end if type==F.PTYPE_QVIEWPANEL then xpanel=1 end for ii=0,1 do panel.RedrawPanel(nil,ii) end end pcall(InitBGColor) ShowImage(xpanel) end end } Event { group="ExitFAR"; action=function() gdiplus.GdiplusShutdown(handle[0]) end }