Download CommonLua/X/XImage.lua from sirnii/myspace: direct link, hf CLI and curl.
- Browser
- Download file 19 kB
-
https://huggingface.co/spaces/sirnii/myspace/resolve/main/CommonLua/X/XImage.lua
- Command line
-
hf download hf://spaces/sirnii/myspace/CommonLua/X/XImage.lua
-
curl -L -o XImage.lua https://huggingface.co/spaces/sirnii/myspace/resolve/main/CommonLua/X/XImage.lua
19 kB
| local UIL = UIL | |
| DefineClass.XImage = { | |
| __parents = { "XControl" }, | |
| properties = { | |
| { category = "Image", id = "Image", editor = "ui_image", default = "", }, | |
| { category = "Image", id = "ImageFit", name = "Fit", editor = "choice", default = "none", items = {"none", "width", "height", "smallest", "largest", "stretch", "stretch-x", "stretch-y", "scale-down"}, invalidate = "measure", }, | |
| { category = "Image", id = "Rows", editor = "number", default = 1, }, | |
| { category = "Image", id = "Columns", editor = "number", default = 1, }, | |
| { category = "Image", id = "Row", editor = "number", default = 1, }, | |
| { category = "Image", id = "Column", editor = "number", default = 1, }, | |
| { category = "Image", id = "ImageRect", name = "Custom rect", editor = "rect", default = box(0, 0, 0, 0), help = "Overrides the columns/rows and allows defining a custom rect from the image", }, | |
| { category = "Image", id = "ImageScale", name = "Scale", editor = "point2d", default = point(1000, 1000), help = "Used when the image is not resized (ImageFit equals 'none')", invalidate = true, lock_ratio = true, }, | |
| { category = "Image", id = "ImageColor", name = "Image color", editor = "color", default = RGB(255, 255, 255), invalidate = true, }, | |
| { category = "Image", id = "DisabledImageColor", name = "Disabled image color", editor = "color", default = RGBA(255, 255, 255, 160), invalidate = true, }, | |
| { category = "Image", id = "Desaturation", editor = "number", default = 0, min = 0, max = 255, slider = true, invalidate = true, }, | |
| { category = "Image", id = "DisabledDesaturation", editor = "number", default = 255, min = 0, max = 255, slider = true, invalidate = true, }, | |
| { category = "Image", id = "Angle", editor = "number", default = 0, min = 0, max = 360*60 - 1, slider = true, scale = "deg", invalidate = true, }, | |
| { category = "Image", id = "AdditiveMode", editor = "bool", default = false, invalidate = true, }, | |
| { category = "Image", id = "FlipX", editor = "bool", default = false, invalidate = true, }, | |
| { category = "Image", id = "FlipY", editor = "bool", default = false, invalidate = true, }, | |
| { category = "Image", id = "EffectPixels", editor = "number", default = 0, invalidate = true, min = 0, max = 32, slider = true, }, | |
| { category = "Image", id = "EffectColor", editor = "color", default = RGB(255, 255, 255), invalidate = true }, | |
| { category = "Image", id = "EffectType", editor = "choice", default = "none", items = {"none", "glow", "outline" }, invalidate = true }, | |
| { category = "Image", id = "BaseColorMap", editor = "bool", default = false, help = "Use to display the base color map of a material in the UI", invalidate = true, }, | |
| { category = "Image", id = "FrameEdgeColor", editor = "color", default = RGBA(0,0,0,0), invalidate = true, }, | |
| { category = "Image", id = "FrameLeft", editor = "number", default = 0, invalidate = true, }, | |
| { category = "Image", id = "FrameTop", editor = "number", default = 0, invalidate = true, }, | |
| { category = "Image", id = "FrameRight", editor = "number", default = 0, invalidate = true, }, | |
| { category = "Image", id = "FrameBottom", editor = "number", default = 0, invalidate = true, }, | |
| { category = "Animation", id = "Animate", editor = "bool", default = false, invalidate = true, }, | |
| { category = "Animation", id = "FPS", editor = "number", default = 10, invalidate = true, }, | |
| { category = "Animation", id = "AnimFlags", name = "Flags", editor = "set", default = set("looping"), items = { "looping", "ping-pong", "inverse", "back to start" }, }, | |
| { category = "Animation", id = "AnimDuration", name = "Duration", editor = "number", default = 0 }, | |
| }, | |
| HandleMouse = false, | |
| animation = false, -- cached animation transition table | |
| src_rect = false, | |
| image_id = const.InvalidResourceID, | |
| image_obj = false, | |
| } | |
| function XImage:Init() | |
| self:UpdateModifiers() | |
| self:SetImage(self.Image, true) | |
| end | |
| function XImage:Done() | |
| if self.image_obj ~= false then | |
| self.image_obj:ReleaseRef() | |
| self.image_obj = false | |
| end | |
| end | |
| function XImage:SetImage(image, force) | |
| if self.Image == (image or "") and not force then return end | |
| self.Image = image or nil | |
| self:DeleteThread("LoadImage") | |
| if (self.Image or "") == "" then | |
| return | |
| end | |
| if self.image_obj ~= false then | |
| self.image_obj:ReleaseRef() | |
| self.image_obj = false | |
| end | |
| self.image_id = ResourceManager.GetResourceID(self.Image) | |
| if self.image_id == const.InvalidResourceID then | |
| printf("once", "Could not load image %s!", self.Image or "") | |
| return | |
| end | |
| self.image_obj = ResourceManager.GetResource(self.image_id) | |
| if self.image_obj then | |
| local old_rect = self.src_rect | |
| self.src_rect = false | |
| if self:CalcSrcRect() ~= old_rect then | |
| self:InvalidateMeasure() | |
| end | |
| self:Invalidate() | |
| else | |
| self:CreateThread("LoadImage", function(self) | |
| self.image_obj = AsyncGetResource(self.image_id) | |
| local old_rect = self.src_rect | |
| self.src_rect = false | |
| if self:CalcSrcRect() ~= old_rect then | |
| self:InvalidateMeasure() | |
| end | |
| self:Invalidate() | |
| end, self) | |
| end | |
| end | |
| function XImage:SetRows(rows) | |
| if self.Rows == rows then return end | |
| self.Rows = rows | |
| self.src_rect = false | |
| self:InvalidateMeasure() | |
| self:Invalidate() | |
| end | |
| function XImage:SetColumns(columns) | |
| if self.Columns == columns then return end | |
| self.Columns = columns | |
| self.src_rect = false | |
| self:InvalidateMeasure() | |
| self:Invalidate() | |
| end | |
| function XImage:SetRow(row) | |
| if self.Row == row then return end | |
| self.Row = row | |
| self.src_rect = false | |
| self:Invalidate() | |
| end | |
| function XImage:SetColumn(column) | |
| if self.Column == column then return end | |
| self.Column = column | |
| self.src_rect = false | |
| self:Invalidate() | |
| end | |
| local anim_flags_set = { | |
| ["looping"] = const.intfLooping, | |
| ["ping-pong"] = const.intfPingPong, | |
| ["inverse"] = const.intfInverse, | |
| ["back to start"] = const.intfBackToStart, | |
| } | |
| function AnimFlagsSetToInt(st) | |
| local flags = 0 | |
| for k,v in pairs(anim_flags_set) do | |
| if st[k] then | |
| flags = flags | v | |
| end | |
| end | |
| return flags | |
| end | |
| function XImage:SetAnimate(b) | |
| if self.Animate == b then return end | |
| self.Animate = b | |
| if b then | |
| self.animation = { | |
| modifier_type = const.modInterpolation, | |
| type = const.intAnimate, | |
| start = GetPreciseTicks(), | |
| fps = self.FPS, -- if nonzero, this will set duration based on the number of frames | |
| columns = self.Columns, | |
| rows = self.Rows, | |
| flags = AnimFlagsSetToInt(self.AnimFlags), | |
| duration = 1000, | |
| easing = "Linear", | |
| } | |
| else | |
| self.animation = false | |
| end | |
| self:Invalidate() | |
| end | |
| function XImage:SetAnimFlags(f) | |
| if self.AnimFlags == f then return end | |
| self.AnimFlags = f | |
| if self.animation then | |
| self.animation.flags = AnimFlagsSetToInt(f) | |
| end | |
| self:Invalidate() | |
| end | |
| function XImage:SetFPS(fps) | |
| if self.FPS == fps then return end | |
| self.FPS = fps | |
| if self.animation then | |
| self.animation.fps = self.FPS | |
| if self.FPS > 0 then | |
| self.animation.duration = 1000 -- will be ignored anyway, set to preserve existing behavior | |
| end | |
| end | |
| self:Invalidate() | |
| end | |
| function XImage:SetAnimDuration(duration) | |
| if self.AnimDuration == duration then return end | |
| self.AnimDuration = duration | |
| if self.animation then | |
| self.animation.duration = self.AnimDuration | |
| if self.AnimDuration > 0 then | |
| self.animation.fps = 0 -- if FPS is nonzero, Duration is ignored and calculated from FPS and number of frames in animation | |
| end | |
| end | |
| self:Invalidate() | |
| end | |
| function XImage:SetImageRect(rect) | |
| if self.ImageRect == rect then return end | |
| self.ImageRect = rect | |
| self.src_rect = false | |
| self:Invalidate() | |
| end | |
| function XImage:CalcSrcRect() | |
| local rect = self.src_rect | |
| if not rect then | |
| rect = self.ImageRect | |
| if rect:IsEmpty() then | |
| if self.image_id ~= const.InvalidResourceID then | |
| local w, h = ResourceManager.GetMetadataTextureSizeXY(self.image_id) | |
| w = w / self.Columns | |
| h = h / self.Rows | |
| local column = Clamp(self.Column, 1, self.Columns) - 1 | |
| local row = Clamp(self.Row, 1, self.Rows) - 1 | |
| rect = sizebox(w * column, h * row, w, h) | |
| else | |
| rect = box(0, 0, 0, 0) | |
| end | |
| end | |
| self.src_rect = rect | |
| end | |
| return rect | |
| end | |
| function XImage:CalcDesaturation() | |
| return self:GetEnabled() and self.Desaturation or self.DisabledDesaturation | |
| end | |
| local function FitImage(max_width, max_height, width, height, fit) | |
| if width == 0 or height == 0 then return 0, 0 end | |
| if fit == "smallest" or fit == "largest" then | |
| local image_is_wider = width * max_height >= max_width * height | |
| fit = image_is_wider == (fit == "smallest") and "width" or "height" | |
| end | |
| if fit == "width" then | |
| return max_width, MulDivRound(height, max_width, width) | |
| elseif fit == "height" then | |
| return MulDivRound(width, max_height, height), max_height | |
| elseif fit == "stretch" then | |
| return max_width, max_height | |
| elseif fit == "stretch-x" then | |
| return max_width, height | |
| elseif fit == "stretch-y" then | |
| return width, max_height | |
| elseif fit == "scale-down" then | |
| local scale = Min(1000, Min(MulDivRound(max_width, 1000, width), MulDivRound(max_height, 1000, height))) | |
| return MulDivRound(width, scale, 1000), MulDivRound(height, scale, 1000) | |
| else -- fit == "none" | |
| return width, height | |
| end | |
| end | |
| function XImage:Measure(preferred_width, preferred_height) | |
| if self.ImageFit == "stretch" then | |
| return XControl.Measure(self, preferred_width, preferred_height) | |
| end | |
| local image_width, image_height = ScaleXY(self.scale, ScaleXY(self.ImageScale, self:CalcSrcRect():sizexyz())) | |
| if self.Angle == 90*60 or self.Angle == 270*60 then | |
| image_width, image_height = image_height, image_width | |
| end | |
| image_width, image_height = FitImage(preferred_width, preferred_height, image_width, image_height, self.ImageFit) | |
| local width, height = XControl.Measure(self, preferred_width, preferred_height) | |
| local fit = self.ImageFit | |
| if fit ~= "stretch" and fit ~= "stretch-x" then | |
| width = Max(image_width, width) | |
| end | |
| if fit ~= "stretch" and fit ~= "stretch-y" then | |
| height = Max(image_height, height) | |
| end | |
| return width, height | |
| end | |
| function XImage:UpdateModifiers() | |
| self:RemoveModifiers(const.modShader) | |
| if self.BaseColorMap then | |
| self:AddShaderModifier({ | |
| modifier_type = const.modShader, | |
| shader_flags = const.modIgnoreAlpha, | |
| }) | |
| end | |
| end | |
| function XImage:SetBaseColorMap(value) | |
| if self.BaseColorMap ~= value then | |
| self.BaseColorMap = value | |
| self:UpdateModifiers() | |
| self:Invalidate() | |
| end | |
| end | |
| function XImage:DrawContent() | |
| if self.Image == "" then return end | |
| if self.AdditiveMode then | |
| UIL.SetBlendMode("blendAdditive") | |
| end | |
| local src = self:CalcSrcRect() | |
| local width, height = ScaleXY(self.scale, ScaleXY(self.ImageScale, src:sizexyz())) | |
| local b = self.content_box | |
| width, height = FitImage(b:sizex(), b:sizey(), width, height, self.ImageFit) | |
| local color = self:GetEnabled() and self.ImageColor or self.DisabledImageColor | |
| if self.Animate then | |
| local old_top = UIL.PushModifier(self.animation) | |
| UIL.DrawXImage(self.Image, | |
| b, width, height, box(0, 0, src:maxx() * self.Columns, src:maxy() * self.Rows), | |
| color, color, color, color, | |
| self:CalcDesaturation(), self.Angle, self.FlipX, self.FlipY, | |
| self.EffectType, self.EffectPixels, self.EffectColor, self.UseClipBox, | |
| self.FrameEdgeColor, self.FrameLeft, self.FrameTop, self.FrameRight, self.FrameBottom) | |
| UIL.ModifiersSetTop(old_top) | |
| else | |
| UIL.DrawXImage(self.Image, | |
| b, width, height, src, | |
| color, color, color, color, | |
| self:CalcDesaturation(), self.Angle, self.FlipX, self.FlipY, | |
| self.EffectType, self.EffectPixels, self.EffectColor, self.UseClipBox, | |
| self.FrameEdgeColor, self.FrameLeft, self.FrameTop, self.FrameRight, self.FrameBottom) | |
| end | |
| if self.AdditiveMode then | |
| UIL.SetBlendMode("blendNormal") | |
| end | |
| end | |
| ----- XEmbedIcon | |
| DefineClass.XEmbedIcon = { | |
| __parents = { "XWindow" }, | |
| properties = { | |
| { category = "Icon", id = "Icon", editor = "ui_image", default = "", }, | |
| { category = "Icon", id = "IconDock", editor = "choice", default = false, items = {false, "left", "right", "top", "bottom", "box", "ignore"}, invalidate = "layout", }, | |
| { category = "Icon", id = "IconRows", editor = "number", default = 1, }, | |
| { category = "Icon", id = "IconRow", editor = "number", default = 1, }, | |
| { category = "Icon", id = "IconColumns", editor = "number", default = 1, }, | |
| { category = "Icon", id = "IconColumn", editor = "number", default = 1, }, | |
| { category = "Icon", id = "IconScale", name = "Icon scale", editor = "point2d", default = point(1000, 1000), help = "Used when the image is not resized (ImageFit equals 'none')", lock_ratio = true, }, | |
| { category = "Icon", id = "IconColor", name = "Icon color", editor = "color", default = RGB(255, 255, 255), }, | |
| { category = "Icon", id = "DisabledIconColor", name = "Disabled icon color", editor = "color", default = RGBA(255, 255, 255, 128), }, | |
| { category = "Icon", id = "IconDesaturation", name = "Icon desaturation", editor = "number", default = 0, min = 0, max = 255, slider = true, }, | |
| { category = "Icon", id = "IconDisabledDesaturation", name = "Disabled icon desaturation", editor = "number", default = 255, min = 0, max = 255, slider = true, }, | |
| { category = "Icon", id = "IconFlipX", editor = "bool", default = false, invalidate = true, }, | |
| { category = "Icon", id = "IconFlipY", editor = "bool", default = false, invalidate = true, }, | |
| }, | |
| } | |
| function XEmbedIcon:Init(parent, context) | |
| local icon = XImage:new({ | |
| Id = "idIcon", | |
| HAlign = "center", | |
| VAlign = "center", | |
| }, self, context) | |
| self:SetIcon(self.Icon) | |
| icon:SetRows(self.IconRows) | |
| icon:SetRow(self.IconRow) | |
| icon:SetColumns(self.IconColumns) | |
| icon:SetColumn(self.IconColumn) | |
| icon:SetImageScale(self.IconScale) | |
| icon:SetImageColor(self.IconColor) | |
| icon:SetDisabledImageColor(self.DisabledIconColor) | |
| icon:SetDisabledDesaturation(self.IconDisabledDesaturation) | |
| icon:SetDesaturation(self.IconDesaturation) | |
| icon:SetImageFit("scale-down") | |
| end | |
| LinkPropertyToChild(XEmbedIcon, "Icon", "idIcon", "Image") | |
| LinkPropertyToChild(XEmbedIcon, "IconDock", "idIcon", "Dock") | |
| LinkPropertyToChild(XEmbedIcon, "IconRows", "idIcon", "Rows") | |
| LinkPropertyToChild(XEmbedIcon, "IconRow", "idIcon", "Row") | |
| LinkPropertyToChild(XEmbedIcon, "IconColumns", "idIcon", "Columns") | |
| LinkPropertyToChild(XEmbedIcon, "IconColumn", "idIcon", "Column") | |
| LinkPropertyToChild(XEmbedIcon, "IconScale", "idIcon", "ImageScale") | |
| LinkPropertyToChild(XEmbedIcon, "IconColor", "idIcon", "ImageColor") | |
| LinkPropertyToChild(XEmbedIcon, "DisabledIconColor", "idIcon", "DisabledImageColor") | |
| LinkPropertyToChild(XEmbedIcon, "IconDesaturation", "idIcon", "Desaturation") | |
| LinkPropertyToChild(XEmbedIcon, "IconDisabledDesaturation", "idIcon", "DisabledDesaturation") | |
| LinkPropertyToChild(XEmbedIcon, "IconFlipX", "idIcon", "FlipX") | |
| LinkPropertyToChild(XEmbedIcon, "IconFlipY", "idIcon", "FlipY") | |
| function XEmbedIcon:SetIcon(icon) | |
| if self.idIcon:GetImage() == icon then return end | |
| self.idIcon:SetImage(icon) | |
| self.Icon = icon | |
| self.idIcon:SetDock(icon == "" and "ignore" or self.IconDock) | |
| self.idIcon:SetVisible(icon ~= "") | |
| end | |
| ----- Image reloading helper | |
| function FindXImagesAndReload(path, ximage_list) | |
| if not CanYield() then | |
| CreateRealTimeThread(FindXImagesAndReload, path) | |
| return | |
| end | |
| ximage_list = ximage_list or GetChildrenOfKind(terminal.desktop, "XImage") | |
| local compare_path = NormalizeGamePath(ConvertToOSPath(path) or path) | |
| local dir, name = SplitPath(compare_path) | |
| local list = table.map(ximage_list, function(v) return v:GetImage() end) | |
| local images = table.filter(ximage_list, function(idx, ximage) | |
| local image_path = ximage:GetImage() | |
| local image_os_path = NormalizeGamePath(ConvertToOSPath(image_path) or image_path) | |
| local imagedir, imagename, __ = SplitPath(image_os_path) | |
| return dir == imagedir and name == imagename | |
| end) | |
| for _, ximage in pairs(images) do | |
| ximage:SetImage("") | |
| end | |
| UIL.UnloadImage(path) | |
| UIL.Invalidate() | |
| while not UIL.IsImageUnloaded(path) do | |
| WaitMsg("OnRender") | |
| end | |
| UIL.RequestImage(path) | |
| UIL.Invalidate() | |
| for _, ximage in pairs(images) do | |
| ximage:SetImage(path) | |
| end | |
| end | |
| function EnableUIBlur(value) | |
| hr.UILBlurTextureScale = value and 500 or 0 | |
| end | |
| DefineClass.XBlurRect = { | |
| __parents = { "XWindow" }, | |
| properties = { | |
| { category = "Blur", id = "TintColor", name = "Tint Color", editor = "color", default = RGB(180, 180, 180), }, | |
| { category = "Blur", id = "BlurRadius", name = "Blur Radius", editor = "number", default = 150, min = 10, max = 300, slider = true, }, | |
| { category = "Blur", id = "Mask", name = "Blur Mask", editor = "ui_image", default = "", image_preview_size = 100, }, | |
| { category = "Blur", id = "FrameLeft", editor = "number", default = 0, invalidate = true, }, | |
| { category = "Blur", id = "FrameTop", editor = "number", default = 0, invalidate = true, }, | |
| { category = "Blur", id = "FrameRight", editor = "number", default = 0, invalidate = true, }, | |
| { category = "Blur", id = "FrameBottom", editor = "number", default = 0, invalidate = true, }, | |
| { category = "Blur", id = "Desaturation", editor = "number", default = 0, min = 0, max = 255, slider = true, invalidate = true, }, | |
| }, | |
| image_id = const.InvalidResourceID, | |
| image_obj = false, | |
| cached_image_rect = box(0, 0, 1, 1), | |
| } | |
| function XBlurRect:Init() | |
| self:SetMask(self.Mask, true) | |
| end | |
| function XBlurRect:Done() | |
| if self.image_obj ~= false then | |
| self.image_obj:ReleaseRef() | |
| self.image_obj = false | |
| end | |
| end | |
| function XBlurRect:SetMask(image, force) | |
| if self.Mask == (image or "") and not force then return end | |
| self.Mask = image or nil | |
| self:DeleteThread("LoadImage") | |
| if (self.Mask or "") == "" then | |
| return | |
| end | |
| if self.image_obj ~= false then | |
| self.image_obj:ReleaseRef() | |
| self.image_obj = false | |
| end | |
| self.image_id = ResourceManager.GetResourceID(self.Mask) | |
| if self.image_id == const.InvalidResourceID then | |
| printf("once", "Could not load image %s!", self.Mask or "") | |
| return | |
| end | |
| self.image_obj = ResourceManager.GetResource(self.image_id) | |
| if self.image_obj then | |
| self.cached_image_rect = box(0, 0, self.image_obj:GetWidth(), self.image_obj:GetHeight()) | |
| self:Invalidate() | |
| else | |
| self:CreateThread("LoadImage", function(self) | |
| self.image_obj = AsyncGetResource(self.image_id) | |
| if self.image_obj then | |
| self.cached_image_rect = box(0, 0, self.image_obj:GetWidth(), self.image_obj:GetHeight()) | |
| self:Invalidate() | |
| end | |
| end, self) | |
| end | |
| end | |
| function XBlurRect:DrawBackground() | |
| local desaturation = UIL.GetDesaturation() | |
| UIL.SetDesaturation(self.Desaturation) | |
| if hr.UILBlurTextureScale > 0 then | |
| UIL.DrawBackBufferRect(self.content_box, self.cached_image_rect, MulDivRound(self.BlurRadius, self.scale:x(), 1000), self.TintColor, self.Mask or "", | |
| self.scale, self.FrameLeft, self.FrameTop, self.FrameRight, self.FrameBottom) | |
| else | |
| local new_color = MulDivRound(point(GetRGB(self.TintColor)), 80, 100) | |
| self.Background = RGBA(0, 0, 0, Min(new_color:xyz())) | |
| XWindow.DrawBackground(self) | |
| end | |
| UIL.SetDesaturation(desaturation) | |
| end | |
| function TestXEdgeFadingImage() | |
| local parent = XWindow:new({ | |
| }, terminal.desktop) | |
| XImage:new({ Image = "UI/SplashScreen", HAlign = "center", VAlign = "center", }, parent) | |
| end |