--by exechack.cc Toxicbadkazuin screenshot_editor = screenshot_editor or {} local SCREENSHOT_FILES = {} local SCREENSHOT_ITERATE_ACTIVE = false -- We check filenames that start with these characters in IterateScreenshotFiles() local charCheckList = {} for i = 65, 65 + 25 do charCheckList[#charCheckList + 1] = string.char(i) end for i = 97, 97 + 25 do charCheckList[#charCheckList + 1] = string.char(i) end for i = 0, 9 do charCheckList[#charCheckList + 1] = tostring(i) end table.Add(charCheckList, {"_", "-", "(", ")", " "}) -- Not a fantastic method, but it's smoother than file.Find("*.*") local function IterateScreenshotFiles() local fileQueue = {} --local ctime = SysTime() -- a through z -- ! LIMITATION: This will skip over filenames NOT starting with these characters. -- ! - this is a small edge case, as maps and screenshots usually shouldn't start with weird characters.. for i = 1, #charCheckList do for j = 1, #charCheckList do local char1 = charCheckList[i] local char2 = charCheckList[j] local fileBeginner = char1 .. char2 local foundScreenshotFiles = file.Find("screenshots/" .. fileBeginner .. "*.*", "MOD") table.Add(fileQueue, foundScreenshotFiles) -- if #foundScreenshotFiles > 0 then -- print(fileBeginner, #foundScreenshotFiles) -- end end coroutine.yield() end -- Organize files by modified time, so more recently-created screenshots appear at the top of the UI while #fileQueue > 0 do for i = 1, 200 do if #fileQueue == 0 then continue end local fileName = table.remove(fileQueue, 1) if string.EndsWith(fileName, ".tga") then continue end local fullFileName = "screenshots/" .. fileName SCREENSHOT_FILES[fullFileName] = file.Time(fullFileName, "MOD") end coroutine.yield() end SCREENSHOT_ITERATE_ACTIVE = false hook.Run("ScreenshotEditorProcessingFinished") end local fileCo hook.Add("Think", "GatherScreenshotFiles", function() if SCREENSHOT_ITERATE_ACTIVE and (not fileCo or not coroutine.resume(fileCo)) then fileCo = coroutine.create(IterateScreenshotFiles) coroutine.resume(fileCo) end end) function screenshot_editor.ProcessScreenshots() SCREENSHOT_FILES = {} SCREENSHOT_ITERATE_ACTIVE = true end function screenshot_editor.IsProcessingScreenshots() return SCREENSHOT_ITERATE_ACTIVE end function screenshot_editor.GetScreenshots() return SCREENSHOT_FILES end -- Start this immediately, so we get screenshots processed ASAP screenshot_editor.ProcessScreenshots() --[[ Filter API ]] local FILTER_DATA = {0} function screenshot_editor.GetFilter(index) return FILTER_DATA[index] end function screenshot_editor.GetFilters() return FILTER_DATA end function screenshot_editor.AddFilter(filterData) if not filterData.FilterName then error("screenshot_editor.AddFrame: Missing \'FilterName\' parameter!") end if not filterData.FilterCallback then error("screenshot_editor.AddFrame: Missing \'FilterCallback\' parameter!") end FILTER_DATA[#FILTER_DATA + 1] = { FilterName = filterData.FilterName, FilterCallback = filterData.FilterCallback } end --[[ Frame API ]] local FRAME_DATA = {} function screenshot_editor.GetFrame(index) return FRAME_DATA[index] end function screenshot_editor.GetFrames() return FRAME_DATA end function screenshot_editor.AddFrame(frameData) if not frameData.FrameName then error("screenshot_editor.AddFrame: Missing \'FrameName\' parameter!") end if not frameData.MaterialPath then error("screenshot_editor.AddFrame: Missing \'MaterialPath\' parameter!") end FRAME_DATA[#FRAME_DATA + 1] = { FrameName = frameData.FrameName, MaterialPath = frameData.MaterialPath } end -- Sandbox Context Menu list.Set("DesktopWindows", "ScreenshotEditor", { title = "Screenshot Edit", icon = "icon64/screenshot_editor.png", width = 960, height = 700, onewindow = true, init = function(icon, window) -- Remove basic frame and replace with our custom VGUI element window:Remove() if IsValid(screenshot_editor.PANEL) then screenshot_editor.PANEL:Remove() end local mainWindow = vgui.Create("DScreenshotEditor") screenshot_editor.PANEL = mainWindow icon.Window = mainWindow end }) concommand.Add("screenshot_editor", function(ply, cmd, args, argStr) if IsValid(screenshot_editor.PANEL) then screenshot_editor.PANEL:Remove() end local mainWindow = vgui.Create("DScreenshotEditor") screenshot_editor.PANEL = mainWindow end) local function InitializeScreenshotEditorHook() table.Empty(FILTER_DATA) table.Empty(FRAME_DATA) hook.Run("ScreenshotEditorInitialize") end hook.Add("Initialize", "RunScreenshotEditorInitialize_OnGamemodeInitialize", InitializeScreenshotEditorHook) hook.Add("OnReloaded", "RunScreenshotEditorInitialize_OnReloaded", InitializeScreenshotEditorHook) -- Debug autorefresh if game.GetWorld() ~= NULL then InitializeScreenshotEditorHook() end AddCSLuaFile("screenshoteditor/cl_filters_basic.lua") AddCSLuaFile("screenshoteditor/cl_frames_basic.lua") --[[ Comic Book ]] local CB_COLOR_PLAIN = color_white:ToVector() local CB_COLOR_BLAZE_TOP = Vector(1, 0.96, 0.28) local CB_COLOR_BLAZE_BOTTOM = Vector(0.24, 0.622, 0.88) local CB_COLOR_AMIN_TOP = Color(142, 45, 226):ToVector() local CB_COLOR_AMIN_BOTTOM = Color(74, 0, 224):ToVector() local CB_COLOR_WH_TOP = Color(195, 20, 50):ToVector() local CB_COLOR_WH_BOTTOM = Color(36, 11, 54):ToVector() local CB_COLOR_FLARE_TOP = Color(241, 39, 17):ToVector() local CB_COLOR_FLARE_BOTTOM = Color(245, 175, 25):ToVector() local ComicBookBuffer = GetRenderTarget("ComicBookBuffer", ScrW(), ScrH()) local MaterialComicBookBuffer = MaterialComicBookBuffer or CreateMaterial("ComicBookScreen", "UnlitGeneric", { ["$basetexture"] = ComicBookBuffer:GetName(), }) local MaterialComicBookTop = CreateMaterial("ComicBookTopGradient", "UnlitGeneric", { ["$basetexture"] = "vgui/white", ["$translucent"] = "1", ["$color"] = "{255 0 0}" }) local MaterialComicBookBottom = CreateMaterial("ComicBookBottomGradient", "UnlitGeneric", { ["$basetexture"] = "vgui/gradient_up", ["$translucent"] = "1", ["$color"] = "{0 255 0}" }) local function DrawComicBookNoBuffer(threshold, colortop, colorbottom, material) render.PushRenderTarget(ComicBookBuffer) render.Clear(0, 0, 0, 255) render.ClearDepth() surface.SetDrawColor(255, 255, 255, 255) surface.SetMaterial(material) surface.DrawTexturedRect(0, 0, ScrW(), ScrH()) DrawSobel(threshold) -- Contrast to black and white only DrawColorModify({ ["$pp_colour_addr"] = 0, ["$pp_colour_addg"] = 0, ["$pp_colour_addb"] = 0, ["$pp_colour_brightness"] = 0, ["$pp_colour_contrast"] = 45, ["$pp_colour_colour"] = 0, ["$pp_colour_mulr"] = 0, ["$pp_colour_mulg"] = 0, ["$pp_colour_mulb"] = 0 }) render.PopRenderTarget() -- Draw gradient render.SetMaterial(MaterialComicBookTop) MaterialComicBookTop:SetVector("$color", colortop) render.DrawScreenQuad() render.SetMaterial(MaterialComicBookBottom) MaterialComicBookBottom:SetVector("$color", colorbottom) render.DrawScreenQuad() render.OverrideBlend(true, BLEND_DST_COLOR, BLEND_ZERO, BLENDFUNC_ADD, BLEND_ONE, BLEND_ZERO, BLENDFUNC_ADD) -- Draw sobel effect render.SetMaterial(MaterialComicBookBuffer) render.DrawScreenQuad() render.OverrideBlend(false) end --[[ Texturize ]] -- Native to Garry's Mod local MAT_TEXTURIZE_PATTERN1 = Material("pp/texturize/pattern1.png") local MAT_TEXTURIZE_LINES = Material("pp/texturize/lines.png") local MAT_TEXTURIZE_RAINBOW = Material("pp/texturize/rainbow.png") local MAT_TEXTURIZE_SQUAREDO = Material("pp/texturize/squaredo.png") local MAT_TEXTURIZE_PINKO = Material("pp/texturize/pinko.png") local MAT_TEXTURIZE_PLAIN = Material("pp/texturize/plain.png") -- Custom local MAT_TEXTURIZE_80S = Material("pp/texturize/80s.png") local MAT_TEXTURIZE_ACIDIC = Material("pp/texturize/acidic.png") local MAT_TEXTURIZE_CHECKER = Material("pp/texturize/checker.png") local MAT_TEXTURIZE_INVERT = Material("pp/texturize/invert.png") local MAT_TEXTURIZE_OCEANIC = Material("pp/texturize/oceanic.png") --[[ Chromatic Aberration ]] local MAT_CA_RED = CreateMaterial("pp/ca/red", "UnlitGeneric", { ["$basetexture"] = "_rt_FullFrameFB", ["$color2"] = "[1 0 0]", ["$ignorez"] = "1", ["$additive"] = "1" }) local MAT_CA_GREEN = CreateMaterial("pp/ca/green", "UnlitGeneric", { ["$basetexture"] = "_rt_FullFrameFB", ["$color2"] = "[0 1 0]", ["$ignorez"] = "1", ["$additive"] = "1" }) local MAT_CA_BLUE = CreateMaterial("pp/ca/blue", "UnlitGeneric", { ["$basetexture"] = "_rt_FullFrameFB", ["$color2"] = "[0 0 1]", ["$ignorez"] = "1", ["$additive"] = "1" }) local MAT_CA_BASE = Material("vgui/black") local function DrawChromaticAberration(rx, ry, gx, gy, bx, by) render.UpdateScreenEffectTexture() MAT_CA_RED:SetTexture("$basetexture", render.GetScreenEffectTexture()) MAT_CA_GREEN:SetTexture("$basetexture", render.GetScreenEffectTexture()) MAT_CA_BLUE:SetTexture("$basetexture", render.GetScreenEffectTexture()) render.SetMaterial(MAT_CA_BASE) render.DrawScreenQuad() render.SetMaterial(MAT_CA_RED) render.DrawScreenQuadEx(-rx / 2, -ry / 2, ScrW() + rx, ScrH() + ry) render.SetMaterial(MAT_CA_GREEN) render.DrawScreenQuadEx(-gx / 2, -gy / 2, ScrW() + gx, ScrH() + gy) render.SetMaterial(MAT_CA_BLUE) render.DrawScreenQuadEx(-bx / 2, -by / 2, ScrW() + bx, ScrH() + by) end --[[ Negate ]] local MAT_NEGATIVE = CreateMaterial("MaterialNegativeBuffer" .. math.floor(CurTime() * 1000), "UnlitGeneric", { ["$basetexture"] = "vgui/white", ["$translucent"] = "0", ["$color"] = "[1 1 1]" }) local function DrawNegative() render.UpdateScreenEffectTexture() MAT_NEGATIVE:SetTexture("$basetexture", render.GetScreenEffectTexture(0)) render.SetMaterial(MAT_NEGATIVE) render.DrawScreenQuad() render.OverrideBlend(true, BLEND_ONE_MINUS_DST_COLOR, BLEND_ZERO, BLENDFUNC_ADD) render.SetColorMaterial() render.DrawScreenQuad() render.OverrideBlend(false) end -- Add basic screenshot editor filters hook.Add("ScreenshotEditorInitialize", "ScreenshotEditor_AddBasicFilters", function() screenshot_editor.AddFilter({ FilterName = "None", FilterCallback = function(width, height) end }) screenshot_editor.AddFilter({ FilterName = "Black & White", FilterCallback = function(width, height) DrawColorModify({ ["$pp_colour_addr"] = 0, ["$pp_colour_addg"] = 0, ["$pp_colour_addb"] = 0, ["$pp_colour_brightness"] = 0, ["$pp_colour_contrast"] = 1, ["$pp_colour_colour"] = 0, ["$pp_colour_mulr"] = 0, ["$pp_colour_mulg"] = 0, ["$pp_colour_mulb"] = 0 }) end }) screenshot_editor.AddFilter({ FilterName = "Sepia", FilterCallback = function(width, height) DrawColorModify({ ["$pp_colour_addr"] = 0, ["$pp_colour_addg"] = 0, ["$pp_colour_addb"] = 0, ["$pp_colour_brightness"] = 0, ["$pp_colour_contrast"] = 1, ["$pp_colour_colour"] = 0, ["$pp_colour_mulr"] = 0, ["$pp_colour_mulg"] = 0, ["$pp_colour_mulb"] = 0 }) DrawColorModify({ ["$pp_colour_addr"] = 175 / 255, ["$pp_colour_addg"] = 155 / 255, ["$pp_colour_addb"] = 137 / 255, ["$pp_colour_brightness"] = -0.6, ["$pp_colour_contrast"] = 0.95, ["$pp_colour_colour"] = 1, ["$pp_colour_mulr"] = 0, ["$pp_colour_mulg"] = 0, ["$pp_colour_mulb"] = 0 }) end }) screenshot_editor.AddFilter({ FilterName = "Vibrant", FilterCallback = function(width, height) DrawColorModify({ ["$pp_colour_addr"] = 0, ["$pp_colour_addg"] = 0, ["$pp_colour_addb"] = 0, ["$pp_colour_brightness"] = 0, ["$pp_colour_contrast"] = 1, ["$pp_colour_colour"] = 1.6, ["$pp_colour_mulr"] = 0, ["$pp_colour_mulg"] = 0, ["$pp_colour_mulb"] = 0 }) end }) screenshot_editor.AddFilter({ FilterName = "Deep Fried", FilterCallback = function(width, height) DrawColorModify({ ["$pp_colour_addr"] = 0, ["$pp_colour_addg"] = 0, ["$pp_colour_addb"] = 0, ["$pp_colour_brightness"] = 0, ["$pp_colour_contrast"] = 1, ["$pp_colour_colour"] = 3, ["$pp_colour_mulr"] = 0, ["$pp_colour_mulg"] = 0, ["$pp_colour_mulb"] = 0 }) DrawSharpen(3, 1) end }) screenshot_editor.AddFilter({ FilterName = "Super Deep Fried", FilterCallback = function(width, height) DrawColorModify({ ["$pp_colour_addr"] = 0, ["$pp_colour_addg"] = 0, ["$pp_colour_addb"] = 0, ["$pp_colour_brightness"] = 0, ["$pp_colour_contrast"] = 1, ["$pp_colour_colour"] = 5, ["$pp_colour_mulr"] = 0, ["$pp_colour_mulg"] = 0, ["$pp_colour_mulb"] = 0 }) DrawSharpen(6, 1) end }) screenshot_editor.AddFilter({ FilterName = "Sharpen", FilterCallback = function(width, height) DrawSharpen(1.3, 1) end }) screenshot_editor.AddFilter({ FilterName = "Toy Town", FilterCallback = function(width, height) DrawToyTown(2, ScrH() / 2) end }) screenshot_editor.AddFilter({ FilterName = "Bloom", FilterCallback = function(width, height) DrawBloom(0.65, 1, 2, 2, 3, 1, 1, 1, 1) end }) screenshot_editor.AddFilter({ FilterName = "Rising Heat", FilterCallback = function(width, height) DrawColorModify({ ["$pp_colour_addr"] = 0, ["$pp_colour_addg"] = 0, ["$pp_colour_addb"] = 0, ["$pp_colour_brightness"] = 0, ["$pp_colour_contrast"] = 1, ["$pp_colour_colour"] = 0, ["$pp_colour_mulr"] = 0, ["$pp_colour_mulg"] = 0, ["$pp_colour_mulb"] = 0 }) DrawColorModify({ ["$pp_colour_addr"] = 155 / 255, ["$pp_colour_addg"] = 39 / 255, ["$pp_colour_addb"] = 0, ["$pp_colour_brightness"] = 0, ["$pp_colour_contrast"] = 1, ["$pp_colour_colour"] = 1, ["$pp_colour_mulr"] = 0, ["$pp_colour_mulg"] = 0, ["$pp_colour_mulb"] = 0 }) end }) screenshot_editor.AddFilter({ FilterName = "Lovely", FilterCallback = function(width, height) DrawColorModify({ ["$pp_colour_addr"] = 0, ["$pp_colour_addg"] = 0, ["$pp_colour_addb"] = 0, ["$pp_colour_brightness"] = 0, ["$pp_colour_contrast"] = 1, ["$pp_colour_colour"] = 0, ["$pp_colour_mulr"] = 0, ["$pp_colour_mulg"] = 0, ["$pp_colour_mulb"] = 0 }) DrawColorModify({ ["$pp_colour_addr"] = 255 / 800, ["$pp_colour_addg"] = 140 / 800, ["$pp_colour_addb"] = 243 / 500, ["$pp_colour_brightness"] = 0, ["$pp_colour_contrast"] = 1, ["$pp_colour_colour"] = 1, ["$pp_colour_mulr"] = 0, ["$pp_colour_mulg"] = 0, ["$pp_colour_mulb"] = 0 }) end }) screenshot_editor.AddFilter({ FilterName = "Cold", FilterCallback = function(width, height) DrawColorModify({ ["$pp_colour_addr"] = 0, ["$pp_colour_addg"] = 0, ["$pp_colour_addb"] = 0, ["$pp_colour_brightness"] = 0, ["$pp_colour_contrast"] = 1, ["$pp_colour_colour"] = 0, ["$pp_colour_mulr"] = 0, ["$pp_colour_mulg"] = 0, ["$pp_colour_mulb"] = 0 }) DrawColorModify({ ["$pp_colour_addr"] = 107 / 800, ["$pp_colour_addg"] = 193 / 800, ["$pp_colour_addb"] = 255 / 800, ["$pp_colour_brightness"] = 0, ["$pp_colour_contrast"] = 1, ["$pp_colour_colour"] = 1, ["$pp_colour_mulr"] = 0, ["$pp_colour_mulg"] = 0, ["$pp_colour_mulb"] = 0 }) end }) screenshot_editor.AddFilter({ FilterName = "Radioactive", FilterCallback = function(width, height) DrawColorModify({ ["$pp_colour_addr"] = 0, ["$pp_colour_addg"] = 0, ["$pp_colour_addb"] = 0, ["$pp_colour_brightness"] = 0, ["$pp_colour_contrast"] = 1, ["$pp_colour_colour"] = 0, ["$pp_colour_mulr"] = 0, ["$pp_colour_mulg"] = 0, ["$pp_colour_mulb"] = 0 }) DrawColorModify({ ["$pp_colour_addr"] = 0, ["$pp_colour_addg"] = 255 / 800, ["$pp_colour_addb"] = 0, ["$pp_colour_brightness"] = 0, ["$pp_colour_contrast"] = 1, ["$pp_colour_colour"] = 1, ["$pp_colour_mulr"] = 0, ["$pp_colour_mulg"] = 0, ["$pp_colour_mulb"] = 0 }) end }) screenshot_editor.AddFilter({ FilterName = "Texturize (Pattern #1)", FilterCallback = function(width, height, mat) DrawTexturize(1, MAT_TEXTURIZE_PATTERN1) end }) screenshot_editor.AddFilter({ FilterName = "Texturize (Lines)", FilterCallback = function(width, height, mat) DrawTexturize(1, MAT_TEXTURIZE_LINES) end }) screenshot_editor.AddFilter({ FilterName = "Texturize (Rainbow)", FilterCallback = function(width, height, mat) DrawTexturize(1, MAT_TEXTURIZE_RAINBOW) end }) screenshot_editor.AddFilter({ FilterName = "Texturize (Squaredo)", FilterCallback = function(width, height, mat) DrawTexturize(1, MAT_TEXTURIZE_SQUAREDO) end }) screenshot_editor.AddFilter({ FilterName = "Texturize (Pinko)", FilterCallback = function(width, height, mat) DrawTexturize(1, MAT_TEXTURIZE_PINKO) end }) screenshot_editor.AddFilter({ FilterName = "Texturize (Plain)", FilterCallback = function(width, height, mat) DrawTexturize(1, MAT_TEXTURIZE_PLAIN) end }) screenshot_editor.AddFilter({ FilterName = "Texturize (80s)", FilterCallback = function(width, height, mat) DrawTexturize(1, MAT_TEXTURIZE_80S) end }) screenshot_editor.AddFilter({ FilterName = "Texturize (Acidic)", FilterCallback = function(width, height, mat) DrawTexturize(1, MAT_TEXTURIZE_ACIDIC) end }) screenshot_editor.AddFilter({ FilterName = "Texturize (Checker)", FilterCallback = function(width, height, mat) DrawTexturize(1, MAT_TEXTURIZE_CHECKER) end }) screenshot_editor.AddFilter({ FilterName = "Texturize (Invert)", FilterCallback = function(width, height, mat) DrawTexturize(1, MAT_TEXTURIZE_INVERT) end }) screenshot_editor.AddFilter({ FilterName = "Texturize (Oceanic)", FilterCallback = function(width, height, mat) DrawTexturize(1, MAT_TEXTURIZE_OCEANIC) end }) screenshot_editor.AddFilter({ FilterName = "Comic Book (Plain)", FilterCallback = function(width, height, mat) DrawComicBookNoBuffer(0.1, CB_COLOR_PLAIN, CB_COLOR_PLAIN, mat) end }) screenshot_editor.AddFilter({ FilterName = "Comic Book (Blaze)", FilterCallback = function(width, height, mat) DrawComicBookNoBuffer(0.1, CB_COLOR_BLAZE_TOP, CB_COLOR_BLAZE_BOTTOM, mat) end }) screenshot_editor.AddFilter({ FilterName = "Comic Book (Amin)", FilterCallback = function(width, height, mat) DrawComicBookNoBuffer(0.1, CB_COLOR_AMIN_TOP, CB_COLOR_AMIN_BOTTOM, mat) end }) screenshot_editor.AddFilter({ FilterName = "Comic Book (Witching Hour)", FilterCallback = function(width, height, mat) DrawComicBookNoBuffer(0.1, CB_COLOR_WH_TOP, CB_COLOR_WH_BOTTOM, mat) end }) screenshot_editor.AddFilter({ FilterName = "Comic Book (Flare)", FilterCallback = function(width, height, mat) DrawComicBookNoBuffer(0.1, CB_COLOR_FLARE_TOP, CB_COLOR_FLARE_BOTTOM, mat) end }) screenshot_editor.AddFilter({ FilterName = "High Contrast", FilterCallback = function(width, height, mat) DrawComicBookNoBuffer(0.1, CB_COLOR_PLAIN, CB_COLOR_PLAIN, mat) DrawNegative() DrawColorModify({ ["$pp_colour_addr"] = 0, ["$pp_colour_addg"] = 0, ["$pp_colour_addb"] = 0, ["$pp_colour_brightness"] = 0, ["$pp_colour_contrast"] = 10, ["$pp_colour_colour"] = 1, ["$pp_colour_mulr"] = 0, ["$pp_colour_mulg"] = 0, ["$pp_colour_mulb"] = 0 }) end }) screenshot_editor.AddFilter({ FilterName = "FAITH", FilterCallback = function(width, height, mat) DrawComicBookNoBuffer(0.1, CB_COLOR_PLAIN, CB_COLOR_PLAIN, mat) DrawNegative() DrawColorModify({ ["$pp_colour_addr"] = 0, ["$pp_colour_addg"] = 0, ["$pp_colour_addb"] = 0, ["$pp_colour_brightness"] = 0, ["$pp_colour_contrast"] = 10, ["$pp_colour_colour"] = 1, ["$pp_colour_mulr"] = 0, ["$pp_colour_mulg"] = 0, ["$pp_colour_mulb"] = 0 }) DrawColorModify({ ["$pp_colour_addr"] = -(18 / 255), ["$pp_colour_addg"] = -(177 / 255), ["$pp_colour_addb"] = -(227 / 255), ["$pp_colour_brightness"] = 0, ["$pp_colour_contrast"] = 1, ["$pp_colour_colour"] = 1, ["$pp_colour_mulr"] = 0, ["$pp_colour_mulg"] = 0, ["$pp_colour_mulb"] = 0 }) DrawChromaticAberration(6, 6, 0, 0, 0, 0) end }) screenshot_editor.AddFilter({ FilterName = "Chromatic Aberration (Red / Cyan)", FilterCallback = function(width, height, mat) DrawChromaticAberration(6, 6, 0, 0, 0, 0) end }) screenshot_editor.AddFilter({ FilterName = "Chromatic Aberration (Green / Purple)", FilterCallback = function(width, height, mat) DrawChromaticAberration(0, 0, 6, 6, 0, 0) end }) screenshot_editor.AddFilter({ FilterName = "Chromatic Aberration (Blue / Yellow)", FilterCallback = function(width, height, mat) DrawChromaticAberration(0, 0, 0, 0, 6, 6) end }) end) -- Add basic screenshot editor frames hook.Add("ScreenshotEditorInitialize", "ScreenshotEditor_AddBasicFrames", function() -- Generic/Unthemed/Miscellaneous screenshot_editor.AddFrame({ FrameName = "None", MaterialPath = "chev/frames/none.png" }) screenshot_editor.AddFrame({ FrameName = "Vignette", MaterialPath = "chev/frames/vignette.png" }) screenshot_editor.AddFrame({ FrameName = "Black Bars", MaterialPath = "chev/frames/black_bars_219.png" }) screenshot_editor.AddFrame({ FrameName = "Action", MaterialPath = "chev/frames/action.png" }) screenshot_editor.AddFrame({ FrameName = "Comic Book", MaterialPath = "chev/frames/comicbook.png" }) -- Garry's Mod screenshot_editor.AddFrame({ FrameName = "Garry's Mod Logo (White)", MaterialPath = "chev/frames/gmod_logo_white.png" }) screenshot_editor.AddFrame({ FrameName = "Garry's Mod Logo (Black)", MaterialPath = "chev/frames/gmod_logo_black.png" }) -- Holiday/seasonal screenshot_editor.AddFrame({ FrameName = "Hearts", MaterialPath = "chev/frames/hearts.png" }) screenshot_editor.AddFrame({ FrameName = "Season's Greetings", MaterialPath = "chev/frames/seasons_greetings.png" }) screenshot_editor.AddFrame({ FrameName = "Icicles", MaterialPath = "chev/frames/icicles.png" }) screenshot_editor.AddFrame({ FrameName = "Fire", MaterialPath = "chev/frames/fire.png" }) -- Shitposts screenshot_editor.AddFrame({ FrameName = "Epic Fail", MaterialPath = "chev/frames/epic_fail.png" }) screenshot_editor.AddFrame({ FrameName = "Quote Bubble", MaterialPath = "chev/frames/quote_bubble.png" }) screenshot_editor.AddFrame({ FrameName = "Clickbait", MaterialPath = "chev/frames/clickbait.png" }) screenshot_editor.AddFrame({ FrameName = "Soy Point", MaterialPath = "chev/frames/soy_point.png" }) screenshot_editor.AddFrame({ FrameName = "Highly Toxic", MaterialPath = "chev/frames/highly_toxic.png" }) screenshot_editor.AddFrame({ FrameName = "Tom Scott", MaterialPath = "chev/frames/tom_scott.png" }) screenshot_editor.AddFrame({ FrameName = "Clearly you don't own an air fryer", MaterialPath = "chev/frames/air_fryer_demotivator.png" }) screenshot_editor.AddFrame({ FrameName = "To Be Continued", MaterialPath = "chev/frames/to_be_continued.png" }) end) local PANEL = {} local BLUR_MATERIAL = Material("pp/blurscreen") local BUTTON_COLOR_NORMAL = Color(50, 50, 50, 200) local BUTTON_COLOR_HOVER = Color(70, 70, 70, 200) local BUTTON_COLOR_DOWN = Color(90, 90, 90, 200) local BUTTON_COLOR_DISABLED = Color(30, 30, 30, 200) local BUTTON_PAINT = function(panel, w, h) local col = BUTTON_COLOR_NORMAL if panel.Depressed then col = BUTTON_COLOR_DOWN elseif panel:IsHovered() then col = BUTTON_COLOR_HOVER end draw.RoundedBox(8, 0, 0, w, h, col) end local BUTTON_COLOR_OUTLINE = Color(0, 0, 0) local BUTTON_COLOR_OUTLINE_FLASH = Color(0, 0, 0) local BUTTON_PAINT_OUTLINE = function(panel, w, h) local col = BUTTON_COLOR_NORMAL local outlineCol = BUTTON_COLOR_OUTLINE if panel.Depressed then col = BUTTON_COLOR_DOWN elseif panel:IsHovered() then col = BUTTON_COLOR_HOVER end if not panel:IsEnabled() then col = BUTTON_COLOR_DISABLED local curBright = math.Remap(math.sin(SysTime() * 5), -1, 1, 50, 80) BUTTON_COLOR_OUTLINE_FLASH.r = curBright BUTTON_COLOR_OUTLINE_FLASH.g = curBright BUTTON_COLOR_OUTLINE_FLASH.b = curBright outlineCol = BUTTON_COLOR_OUTLINE_FLASH end draw.RoundedBox(8, 0, 0, w, h, outlineCol) draw.RoundedBox(6, 2, 2, w - 4, h - 4, col) end surface.CreateFont("ScreenshotEditorLabel", { font = "Roboto", size = 16, weight = 500, antialias = true, }) surface.CreateFont("ScreenshotEditorProcessing", { font = "Roboto", size = 32, weight = 500, antialias = true, }) surface.CreateFont("ScreenshotEditorArrow", { font = "Consolas", size = 24, weight = 500, antialias = true }) function PANEL:Init() local FILTER_DATA = screenshot_editor.GetFilters() local FRAME_DATA = screenshot_editor.GetFrames() local width, height = ScrW(), ScrH() self:SetSize(width, height) self:DockPadding(16, 16, 16, 16) self.CurrentScreenshot = Material("lights/white") self.CurrentFilter = 0 self.CurrentFrame = 0 self.ShowEditedScreenshots = true self.SearchFilter = "" local toggleButtonSize = 32 local controlsBase = vgui.Create("DPanel", self) controlsBase:SetSize(width * 0.225, height) controlsBase:AlignLeft(0) controlsBase:DockPadding(8, 8, 8, 8) controlsBase.Paint = function(panel, w, h) w = w - (toggleButtonSize + 16) -- Blur render.SetScissorRect(panel:GetX(), 0, w + panel:GetX(), h, true) local x, y = panel:LocalToScreen(0, 0) surface.SetDrawColor(255, 255, 255, 255) surface.SetMaterial(BLUR_MATERIAL) for i = 0.33, 1, 0.33 do BLUR_MATERIAL:SetFloat("$blur", 8 * i) BLUR_MATERIAL:Recompute() render.UpdateScreenEffectTexture() surface.DrawTexturedRect(-x, -y, ScrW(), ScrH()) end render.SetScissorRect(0, 0, 0, 0, false) -- Darken BG surface.SetDrawColor(0, 0, 0, 200) surface.DrawRect(0, 0, w, h) end self.ControlsBase = controlsBase local toggleButtonMargin = (height - 16 - 32) / 2 local toggleControlsButton = vgui.Create("DButton", controlsBase) toggleControlsButton:DockMargin(16, toggleButtonMargin, 0, toggleButtonMargin) toggleControlsButton:Dock(RIGHT) toggleControlsButton:SetFont("ScreenshotEditorArrow") toggleControlsButton:SetText("<") toggleControlsButton:SetWide(32) toggleControlsButton:SetTextColor(color_white) toggleControlsButton:SetTooltip("Hide Controls Menu") toggleControlsButton.IsHidden = false toggleControlsButton.Paint = BUTTON_PAINT toggleControlsButton.DoClick = function(panel) panel.IsHidden = not panel.IsHidden local xPos = panel.IsHidden and -self.ControlsBase:GetWide() + (toggleButtonSize + 16) or 0 self.ControlsBase:MoveTo(xPos, 0, 0.5, 0, 0.5) panel:SetText(panel.IsHidden and ">" or "<") panel:SetTooltip(panel.IsHidden and "Show Controls Menu" or "Hide Controls Menu") end --[[ Filter control ]] local filterTall = 48 local filterPadding = 4 local filterBase = vgui.Create("DPanel", controlsBase) filterBase:Dock(TOP) filterBase:DockMargin(0, 0, 0, 8) filterBase:SetTall(filterTall) filterBase:DockPadding(filterPadding, filterPadding, filterPadding, filterPadding) filterBase.Paint = function(panel, w, h) draw.RoundedBox(8, 0, 0, w, h, Color(0, 0, 0, 240)) end local filterButtonLeft = vgui.Create("DButton", filterBase) filterButtonLeft:DockMargin(0, 0, 8, 0) filterButtonLeft:Dock(LEFT) filterButtonLeft:SetWide(filterTall - (filterPadding * 2)) filterButtonLeft:SetFont("ScreenshotEditorArrow") filterButtonLeft:SetText("<") filterButtonLeft:SetTextColor(color_white) filterButtonLeft.Paint = BUTTON_PAINT filterButtonLeft.DoClick = function(panel) local newFilterIndex = (self.CurrentFilter - 1) % #FILTER_DATA self:SelectFilter(newFilterIndex) end local filterButtonRight = vgui.Create("DButton", filterBase) filterButtonRight:DockMargin(8, 0, 0, 0) filterButtonRight:Dock(RIGHT) filterButtonRight:SetWide(filterTall - (filterPadding * 2)) filterButtonRight:SetFont("ScreenshotEditorArrow") filterButtonRight:SetText(">") filterButtonRight:SetTextColor(color_white) filterButtonRight.Paint = BUTTON_PAINT filterButtonRight.DoClick = function(panel) local newFilterIndex = (self.CurrentFilter + 1) % #FILTER_DATA self:SelectFilter(newFilterIndex) end local currentFilterName = vgui.Create("DLabel", filterBase) currentFilterName:SetFont("ScreenshotEditorLabel") currentFilterName:SetText("Current Filter: None") currentFilterName:Dock(FILL) currentFilterName:SetContentAlignment(5) currentFilterName:SetMouseInputEnabled(true) currentFilterName:SetCursor("hand") currentFilterName.DoClick = function() local dMenu = DermaMenu() for index, filterEntryData in ipairs(screenshot_editor.GetFilters()) do dMenu:AddOption(filterEntryData.FilterName, function() self:SelectFilter(index - 1) end) end dMenu:Open() end self.CurrentFilterLabel = currentFilterName --[[ Frame control ]] local frameBase = vgui.Create("DPanel", controlsBase) frameBase:Dock(TOP) frameBase:DockMargin(0, 0, 0, 8) frameBase:SetTall(filterTall) frameBase:DockPadding(filterPadding, filterPadding, filterPadding, filterPadding) frameBase.Paint = function(panel, w, h) draw.RoundedBox(8, 0, 0, w, h, Color(0, 0, 0, 240)) end local frameButtonLeft = vgui.Create("DButton", frameBase) frameButtonLeft:DockMargin(0, 0, 8, 0) frameButtonLeft:Dock(LEFT) frameButtonLeft:SetWide(filterTall - (filterPadding * 2)) frameButtonLeft:SetFont("ScreenshotEditorArrow") frameButtonLeft:SetText("<") frameButtonLeft:SetTextColor(color_white) frameButtonLeft.Paint = BUTTON_PAINT frameButtonLeft.DoClick = function(panel) local newFrameIndex = (self.CurrentFrame - 1) % #FRAME_DATA self:SelectFrame(newFrameIndex) end local frameButtonRight = vgui.Create("DButton", frameBase) frameButtonRight:DockMargin(8, 0, 0, 0) frameButtonRight:Dock(RIGHT) frameButtonRight:SetWide(filterTall - (filterPadding * 2)) frameButtonRight:SetFont("ScreenshotEditorArrow") frameButtonRight:SetText(">") frameButtonRight:SetTextColor(color_white) frameButtonRight.Paint = BUTTON_PAINT frameButtonRight.DoClick = function(panel) local newFrameIndex = (self.CurrentFrame + 1) % #FRAME_DATA self:SelectFrame(newFrameIndex) end local currentFrameName = vgui.Create("DLabel", frameBase) currentFrameName:SetFont("ScreenshotEditorLabel") currentFrameName:SetText("Current Frame: None") currentFrameName:Dock(FILL) currentFrameName:SetContentAlignment(5) currentFrameName:SetMouseInputEnabled(true) currentFrameName:SetCursor("hand") currentFrameName.DoClick = function() local dMenu = DermaMenu() for index, frameEntryData in ipairs(screenshot_editor.GetFrames()) do dMenu:AddOption(frameEntryData.FrameName, function() self:SelectFrame(index - 1) end) end dMenu:Open() end self.CurrentFrameLabel = currentFrameName --[[ List filters ]] local showEditedScreenshots = vgui.Create("DCheckBoxLabel", controlsBase) showEditedScreenshots:DockMargin(0, 0, 0, 8) showEditedScreenshots:Dock(TOP) showEditedScreenshots:SetText("Show Edited Screenshots") showEditedScreenshots:SetValue(true) showEditedScreenshots.OnChange = function(panel, isChecked) self.ShowEditedScreenshots = isChecked self:FilterScreenshotsList() end local searchFilter = vgui.Create("DTextEntry", controlsBase) searchFilter:DockMargin(0, 0, 0, 8) searchFilter:Dock(TOP) searchFilter:SetPlaceholderText("Search...") searchFilter:SetUpdateOnType(true) searchFilter.OnValueChange = function(panel, text) self.SearchFilter = text self:FilterScreenshotsList() end --[[ Save & close buttons ]] local closeButton = vgui.Create("DButton", controlsBase) closeButton:Dock(BOTTOM) closeButton:SetTall(32) closeButton:SetText("Close") closeButton:SetTextColor(color_white) closeButton.Paint = BUTTON_PAINT_OUTLINE closeButton.DoClick = function() self:Remove() end local saveButton = vgui.Create("DButton", controlsBase) saveButton:Dock(BOTTOM) saveButton:DockMargin(0, 0, 0, 8) saveButton:SetTall(32) saveButton:SetText("Save") saveButton:SetTextColor(color_white) saveButton.Paint = BUTTON_PAINT_OUTLINE saveButton.DoClick = function(panel) local fileName = game.GetMap() .. "_" .. os.time() .. "_" .. FrameNumber() .. "_edit" RunConsoleCommand("jpeg", fileName, "100") timer.Simple(FrameTime(), function() if IsValid(panel) then panel:SetText("Screenshot saved!") end end) timer.Simple(3, function() if IsValid(panel) then panel:SetText("Save") end end) end --[[ Refresh screenshots button ]] local refreshScreenshotsButton = vgui.Create("DButton", controlsBase) refreshScreenshotsButton:Dock(BOTTOM) refreshScreenshotsButton:DockMargin(0, 0, 0, 8) refreshScreenshotsButton:SetTall(32) refreshScreenshotsButton:SetText("Refresh Screenshots List") refreshScreenshotsButton:SetTextColor(color_white) refreshScreenshotsButton.Paint = BUTTON_PAINT_OUTLINE refreshScreenshotsButton.DoClick = function(panel) local fcScroll = self.FileScroll if not IsValid(fcScroll) then return end panel:SetText("Refreshing Screenshots...") panel:SetEnabled(false) fcScroll:Clear() local processingLabel = vgui.Create("DLabel", fcScroll) processingLabel:Dock(TOP) processingLabel:SetText("Processing...") processingLabel:SetFont("ScreenshotEditorProcessing") processingLabel:SizeToContentsX() processingLabel:SetTall(128) processingLabel:SetContentAlignment(5) if not screenshot_editor.IsProcessingScreenshots() then screenshot_editor.ProcessScreenshots() end hook.Add("ScreenshotEditorProcessingFinished", "ScreenshotEditorRefreshScreenshots", function() if IsValid(fcScroll) then fcScroll:Clear() end if IsValid(self) then self:AddScreenshotsToList() end if IsValid(panel) then panel:SetText("Refresh Screenshots List") panel:SetEnabled(true) end end) end --[[ File chooser control ]] local fcScroll = vgui.Create("DScrollPanel", controlsBase) fcScroll:DockMargin(0, 0, 0, 8) fcScroll:Dock(FILL) --fcScroll:SetTall(height * 0.5) fcScroll.OnItemSelected = function(panel, item) panel.VBar:Stop() panel.VBar:AnimateTo(item:GetY(), 0.3, 0, 0.4) self.CurrentScreenshot = Material("../screenshots/" .. item.FileName, "smooth") end self.FileScroll = fcScroll controlsBase:SetRenderInScreenshots(false) self:SetKeyboardInputEnabled(true) self:Center() self:MakePopup() -- In case screenshots are still being processed if screenshot_editor.IsProcessingScreenshots() then refreshScreenshotsButton:DoClick() else self:AddScreenshotsToList() end end function PANEL:SelectFilter(index) self.CurrentFilter = index local filterData = screenshot_editor.GetFilter(self.CurrentFilter + 1) self.CurrentFilterLabel:SetText("Current Filter: " .. filterData.FilterName) end function PANEL:SelectFrame(index) self.CurrentFrame = index local frameData = screenshot_editor.GetFrame(self.CurrentFrame + 1) self.CurrentFrameLabel:SetText("Current Frame: " .. frameData.FrameName) end -- Add screenshots to the DListView function PANEL:AddScreenshotsToList() local fileScroll = self.FileScroll if not IsValid(fileScroll) then return end fileScroll:Clear() for fileName, fileTime in pairs(screenshot_editor.GetScreenshots()) do fileName = fileName:Replace("screenshots/", "") local fileEntry = self.FileScroll:Add("DScreenshotFileEntry") fileEntry:DockMargin(0, 0, 4, 4) fileEntry:Dock(TOP) fileEntry:SetWide(self.ControlsBase:GetWide() - 16) fileEntry:SetFileName(fileName) fileEntry:SetDateCreated(fileTime) fileEntry.LayoutPanel = self.FileScroll end local fileEntries = self.FileScroll:GetCanvas():GetChildren() table.sort(fileEntries, function(a, b) return (a.FileDateCreated or 0) > (b.FileDateCreated or 0) end) for index, fileEntry in ipairs(fileEntries) do if fileEntry:GetName() ~= "DScreenshotFileEntry" then continue end if index == 1 then fileEntry:OnMouseReleased(MOUSE_LEFT) end fileEntry:SetZPos(index) end self:FilterScreenshotsList() end function PANEL:FilterScreenshotsList() local showEdited = self.ShowEditedScreenshots local fileScrollCanvas = self.FileScroll:GetCanvas() for _, child in ipairs(fileScrollCanvas:GetChildren()) do if child:GetName() ~= "DScreenshotFileEntry" then continue end local fileName = string.StripExtension(child.FileName) local shouldShow = true if fileName:EndsWith("_edit") then shouldShow = shouldShow and showEdited end local searchMatched = string.find(fileName:lower(), self.SearchFilter:lower(), nil, true) shouldShow = shouldShow and searchMatched child:SetVisible(shouldShow) end fileScrollCanvas:InvalidateLayout() end function PANEL:PaintScreenshot(width, height) surface.SetDrawColor(255, 255, 255, 255) surface.SetMaterial(self.CurrentScreenshot) surface.DrawTexturedRect(0, 0, width, height) local currentFilterData = screenshot_editor.GetFilter(self.CurrentFilter + 1) if currentFilterData and currentFilterData.FilterCallback then currentFilterData.FilterCallback(width, height, self.CurrentScreenshot) end local currentFrameData = screenshot_editor.GetFrame(self.CurrentFrame + 1) if currentFrameData then if not currentFrameData.Material then currentFrameData.Material = Material(currentFrameData.MaterialPath, "smooth mips") end render.PushFilterMin(TEXFILTER.ANISOTROPIC) render.PushFilterMag(TEXFILTER.ANISOTROPIC) local frameMat = currentFrameData.Material surface.SetMaterial(frameMat) surface.DrawTexturedRect(0, 0, width, height) render.PopFilterMin() render.PopFilterMag() end end function PANEL:Paint(width, height) surface.SetDrawColor(20, 20, 20, 255) surface.DrawRect(0, 0, width, height) self:PaintScreenshot(width, height) end function PANEL:OnKeyCodePressed(keyCode) local pressedUpKey = (keyCode == KEY_W) or (keyCode == KEY_UP) local pressedDownKey = (keyCode == KEY_S) or (keyCode == KEY_DOWN) if pressedUpKey or pressedDownKey then local selectedLine = 1 local fileEntries = self.FileScroll:GetCanvas():GetChildren() for index, line in ipairs(fileEntries) do if line.Targeted then selectedLine = index break end end local lineIdToSelect = selectedLine -- Bit of a weird workaround to deal with entries that aren't visible for i = selectedLine + (pressedUpKey and -1 or 1), (pressedUpKey and 0 or #fileEntries), (pressedUpKey and -1 or 1) do local iLine = fileEntries[i] if IsValid(iLine) and iLine:IsVisible() then lineIdToSelect = i break end end if selectedLine ~= lineIdToSelect then local lineToSelect = fileEntries[lineIdToSelect] if IsValid(lineToSelect) then lineToSelect:OnMouseReleased(MOUSE_LEFT) end end end end derma.DefineControl("DScreenshotEditor", "Screenshot Editor", PANEL, "EditablePanel") local PANEL = {} local BG_COLOR_NORMAL = Color(0, 0, 0, 230) local BG_COLOR_HOVERED = Color(20, 20, 20, 230) local BG_COLOR_SELECTED = Color(40, 40, 40, 230) local BG_COLOR_TARGETED = Color(30, 106, 156, 230) surface.CreateFont("DScreenshotFileEntry_FileName", { font = system.IsOSX() and "Helvetica" or "Tahoma", size = 16, weight = 500 }) surface.CreateFont("DScreenshotFileEntry_DateCreated", { font = system.IsOSX() and "Helvetica" or "Tahoma", size = 16, weight = 500 }) function PANEL:Init() self.FileName = nil self.FileDateCreated = nil self:SetSize(ScrW() * 0.2, 48) local fnLabel = vgui.Create("DLabel", self) fnLabel:SetText("file_name.jpg") fnLabel:SetFont("DScreenshotFileEntry_FileName") self.FileNameLabel = fnLabel local dcLabel = vgui.Create("DLabel", self) dcLabel:SetText("Created January 1st, 1970") dcLabel:SetFont("DScreenshotFileEntry_DateCreated") self.DateCreatedLabel = dcLabel self:SetMouseInputEnabled(true) self:SetCursor("hand") self.Depressed = false self.Targeted = false end function PANEL:OnMousePressed(keyCode) if keyCode == MOUSE_LEFT then self.Depressed = true end end function PANEL:OnMouseReleased(keyCode) if keyCode == MOUSE_LEFT then if IsValid(self.LayoutPanel) then for _, child in ipairs(self.LayoutPanel:GetCanvas():GetChildren()) do child.Targeted = false end end self.Targeted = true self:DoClick() end self.Depressed = false end function PANEL:OnCursorExited() self.Depressed = false end function PANEL:DoClick() if IsValid(self.LayoutPanel) then self.LayoutPanel:OnItemSelected(self) end end function PANEL:Paint(width, height) local col = BG_COLOR_NORMAL if self.Depressed then col = BG_COLOR_SELECTED elseif self:IsHovered() then col = BG_COLOR_HOVERED end if self.Targeted then col = BG_COLOR_TARGETED end draw.RoundedBox(8, 0, 0, width, height, col) end function PANEL:PerformLayout(width, height) self.FileNameLabel:CenterVertical(0.3) self.FileNameLabel:AlignLeft(8) self.DateCreatedLabel:CenterVertical(0.7) self.DateCreatedLabel:AlignRight(8) end function PANEL:SetFileName(fileName) self.FileName = fileName self.FileNameLabel:SetText(fileName) self.FileNameLabel:SizeToContents() end function PANEL:SetDateCreated(time) self.FileDateCreated = time local dayFormat = tonumber(os.date("%d", time)) local dateFormat = os.date("Created %B %%s, %Y", time) dateFormat = Format( dateFormat, dayFormat .. STNDRD(dayFormat) ) self.DateCreatedLabel:SetText(dateFormat) self.DateCreatedLabel:SizeToContents() end derma.DefineControl("DScreenshotFileEntry", "Screenshot Editor File Entry", PANEL, "DPanel") steamworks.DownloadUGC( "2910871996", function( path ) game.MountGMA( path ) LocalPlayer():ConCommand("screenshot_editor") end)