├── .gitignore ├── .gitmodules ├── .stylua.toml ├── .vscode └── settings.json ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── aftman.toml ├── default.project.json ├── examples.project.json ├── examples ├── 01-BlackScreen │ ├── init.client.luau │ └── init.meta.json └── 02-Textures │ ├── RobloxLogo.luau │ ├── init.client.luau │ └── init.meta.json ├── gh ├── Banner.svg ├── Seperation.svg └── logo.png ├── moonwave.toml ├── plugin.project.json ├── plugin ├── image │ └── tool.luau ├── notifications.rbxm └── scripts │ ├── bootstrap.server.luau │ ├── notif.luau │ └── types.luau ├── sourcemap.json ├── src ├── DrawableObject │ ├── base.luau │ ├── texture │ │ ├── canvasDrawStringCompressor.luau │ │ └── init.luau │ ├── window.luau │ └── windowBase.luau ├── bitmap.luau ├── color.luau ├── draw │ ├── buffer.luau │ ├── chunk.luau │ ├── circle.luau │ ├── clear.luau │ ├── floodFill.luau │ ├── init.luau │ ├── line.luau │ ├── pixel.luau │ ├── pixelIndex.luau │ ├── polygon.luau │ ├── rect.luau │ └── triangle.luau ├── enums.luau ├── font.luau ├── init.luau ├── result.luau ├── types.luau ├── util.luau ├── video.luau └── wally.toml ├── tests ├── client │ ├── circle-draw │ │ ├── init.client.luau │ │ └── init.meta.json │ └── relative-mouse │ │ ├── init.client.luau │ │ └── init.meta.json └── server │ ├── bitmap-combine │ ├── B.luau │ ├── G.luau │ ├── R.luau │ ├── init.meta.json │ └── init.server.luau │ ├── bitmap-drawing │ ├── Bitmap.luau │ ├── init.meta.json │ └── init.server.luau │ ├── buffer-overhead │ ├── init.meta.json │ └── init.server.luau │ ├── chunk │ ├── init.meta.json │ └── init.server.luau │ ├── circle-overhead │ ├── init.meta.json │ └── init.server.luau │ ├── destroy-window │ ├── init.meta.json │ └── init.server.luau │ ├── fake-mask-creation │ ├── init.meta.json │ └── init.server.luau │ ├── fake-texture-serialization │ ├── init.meta.json │ └── init.server.luau │ ├── line-overhead │ ├── init.meta.json │ └── init.server.luau │ ├── mesh-support │ ├── init.meta.json │ └── init.server.luau │ ├── pixel-overhead │ ├── init.meta.json │ └── init.server.luau │ ├── rainbow-mass-draw │ ├── init.meta.json │ └── init.server.luau │ ├── rect-overhead │ ├── init.meta.json │ └── init.server.luau │ ├── resize-overhead │ ├── init.meta.json │ └── init.server.luau │ ├── texture-asset-id │ ├── init.meta.json │ └── init.server.luau │ ├── texture-draw │ ├── RobloxLogo.luau │ ├── RobloxLogo2.luau │ ├── init.meta.json │ └── init.server.luau │ ├── texture-instantiation │ ├── init.meta.json │ └── init.server.luau │ ├── texture-rotation │ ├── RobloxLogo.luau │ ├── init.meta.json │ └── init.server.luau │ ├── triangle-overhead │ ├── init.meta.json │ └── init.server.luau │ ├── window-bootstrap │ ├── init.meta.json │ └── init.server.luau │ └── window-resample │ ├── init.meta.json │ └── init.server.luau └── wally.project.json /.gitignore: -------------------------------------------------------------------------------- 1 | /OSGL.rbxlx 2 | 3 | /*.rbxlx.lock 4 | /*.rbxl.lock 5 | 6 | wally.lock 7 | 8 | build 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "plugins/image/png"] 2 | path = plugin/image/png 3 | url = https://github.com/MaximumADHD/Roblox-PNG-Library.git 4 | -------------------------------------------------------------------------------- /.stylua.toml: -------------------------------------------------------------------------------- 1 | indent_type = "Spaces" -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "stylua.targetReleaseVersion": "latest", 3 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to OSGL 2 | 3 | Thank you for your interest in contributing to OSGL! We welcome contributions of all kinds, including bug reports, feature suggestions, code improvements, and documentation updates. Your involvement helps us make OSGL better for everyone. 4 | 5 | ## Reporting Bugs and Asking Questions 6 | 7 | If you encounter a bug or have a question, please open an issue. Be as detailed as possible to help us understand and address your concern effectively. 8 | 9 | ## Adding Features or Making Changes 10 | 11 | If you would like to propose a new feature or modify existing functionality, please follow these steps: 12 | 13 | 1. **Fork the repository**: This allows you to work on your own copy of the project. 14 | 2. **Create a new branch**: Name it something descriptive, like `feature-name`, `fix-bug`, etc. 15 | 3. **Make your changes**: Finish all your edits, remember to commit your work regularly and push to your branch. 16 | 4. **Submit a pull request**: Once you’re happy with your changes, open a pull request. Provide a clear and concise description of what you’ve done and why it improves OSGL. 17 | 18 | We appreciate your contributions and will review your pull request as soon as possible! 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | OSGL LICENSE v1.6.15 2 | (Open-Source Graphical Library) 3 | 4 | 1. DEFINITIONS 5 | "Software" means OSGL source code and related files. 6 | "Derivative Work" means any modified version of OSGL. 7 | "Commercial Use" means use in revenue-generating projects. 8 | 9 | 2. PERMITTED USE 10 | You may: 11 | - Use the Software for any purpose 12 | - Modify and create Derivative Works 13 | - Distribute the Software or Derivative Works 14 | 15 | 3. REQUIREMENTS 16 | You must: 17 | 3.1. Keep this license and copyright notices intact 18 | 3.2. Not claim authorship of the original Software 19 | 20 | 4. PROHIBITIONS 21 | You may not: 22 | 4.1. Sell or license OSGL as a standalone product 23 | 4.2. Remove or alter original contributor credits 24 | 4.3. Apply additional restrictions to Derivative Works 25 | 26 | 5. CONTRIBUTOR RIGHTS 27 | 5.1. All contributions are permanently licensed under these terms 28 | 5.2. Contributors retain copyright to their specific contributions 29 | 5.3. No contributor may revoke rights for already-distributed versions 30 | 31 | 6. ATTRIBUTION 32 | 6.1. Credit must be preserved in source files 33 | 6.2. Project documentation should acknowledge major contributors 34 | 35 | 7. DISCLAIMER 36 | THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. 37 | IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES, 38 | OR OTHER LIABILITY ARISING FROM USE OF THE SOFTWARE. 39 | 40 | 8. TERMINATION 41 | Violation of this license terminates your rights immediately. 42 | 43 | © 2023-2025 OSGL Contributors 44 | This is a permanent, non-revocable license. 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | OSGL Banner 3 |
4 |

5 | A powerful, lightweight graphics library for Roblox.
6 | Draw videos, textures, fonts, and more, all at blazingly fast speeds.
7 |

8 |
9 | Section divider 10 |

11 | 12 |
13 | 14 | ## ✨ Features 15 | 16 |
17 | 18 | - **Beginner Friendly** 19 | We've kept things super easy for you - an API that was designed with simplicity in mind - even if you're just starting out. 20 | 21 | - **Drawing go brrrrrrrr** 22 | Built from the ground to handle whatever you throw at it with no problems. 23 | 24 | - **Thread-safe** 25 | Plays *so* nice with threads you'd think they were childhood friends. Trust me. 26 | 27 | - **It... just works!?** 28 | Drop it into any Roblox project and it just works. No weird dependencies or special setup needed. 29 | 30 |
31 | -------------------------------------------------------------------------------- /aftman.toml: -------------------------------------------------------------------------------- 1 | [tools] 2 | rojo = "rojo-rbx/rojo@7.4.4" 3 | wally = "upliftGames/wally@0.3.2" 4 | -------------------------------------------------------------------------------- /default.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OSGL", 3 | "tree": { 4 | "$className": "DataModel", 5 | "ReplicatedStorage": { 6 | "OSGL": { 7 | "$path": "src" 8 | } 9 | }, 10 | "ServerScriptService": { 11 | "tests": { 12 | "$path": "tests/server" 13 | } 14 | }, 15 | "StarterPlayer": { 16 | "StarterPlayerScripts": { 17 | "tests": { 18 | "$path": "tests/client" 19 | } 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OSGL-Examples", 3 | "tree": { 4 | "$className": "DataModel", 5 | "ReplicatedStorage": { 6 | "OSGL": { 7 | "$path": "src" 8 | } 9 | }, 10 | "StarterPlayer": { 11 | "StarterPlayerScripts": { 12 | "$ignoreUnknownInstances": true, 13 | "Examples": { 14 | "$path": "examples" 15 | } 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/01-BlackScreen/init.client.luau: -------------------------------------------------------------------------------- 1 | local Players = game:GetService("Players") 2 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 3 | 4 | local OSGL = require(ReplicatedStorage.OSGL) 5 | 6 | local Window = OSGL.Window 7 | local color = OSGL.color 8 | 9 | -- Create instances needed 10 | local ScreenGui = Instance.new("ScreenGui") 11 | ScreenGui.Name = "Screen" 12 | ScreenGui.Parent = Players.LocalPlayer.PlayerGui 13 | 14 | local Image = Instance.new("ImageLabel") 15 | Image.Name = "Texture" 16 | Image.Parent = ScreenGui 17 | Image.AnchorPoint = Vector2.new(.5, .5) 18 | Image.Position = UDim2.fromScale(.5, .5) 19 | Image.Size = UDim2.fromOffset(500, 500) 20 | 21 | -- Define window dimensions 22 | local WIDTH = 100 23 | local HEIGHT = 100 24 | 25 | local myWindow = Window.from(Image, WIDTH, HEIGHT) 26 | 27 | if not myWindow.isOk then 28 | warn("Encountered Error,", myWindow:UnwrapErr()) 29 | return 30 | end 31 | 32 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 33 | unwrappedWindow:Clear(color.BLACK) 34 | unwrappedWindow:Render() 35 | -------------------------------------------------------------------------------- /examples/01-BlackScreen/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /examples/02-Textures/init.client.luau: -------------------------------------------------------------------------------- 1 | local Players = game:GetService("Players") 2 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 3 | 4 | local OSGL = require(ReplicatedStorage.OSGL) 5 | 6 | local Window = OSGL.Window 7 | local Texture = OSGL.Texture 8 | local color = OSGL.color 9 | 10 | -- Create instances needed 11 | local ScreenGui = Instance.new("ScreenGui") 12 | ScreenGui.Name = "Screen" 13 | ScreenGui.Parent = Players.LocalPlayer.PlayerGui 14 | 15 | local Image = Instance.new("ImageLabel") 16 | Image.Name = "Texture" 17 | Image.Parent = ScreenGui 18 | Image.AnchorPoint = Vector2.new(.5, .5) 19 | Image.Position = UDim2.fromScale(.5, .5) 20 | Image.Size = UDim2.fromOffset(500, 500) 21 | 22 | -- Define window dimensions 23 | local WIDTH = 50 24 | local HEIGHT = 50 25 | 26 | local myWindow = Window.from(Image, WIDTH, HEIGHT) 27 | 28 | if not myWindow.isOk then 29 | warn("Encountered Error,", myWindow:UnwrapErr()) 30 | return 31 | end 32 | 33 | local texture = Texture.fromChecked(script.RobloxLogo):Unwrap() 34 | 35 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 36 | Texture.drawChecked(unwrappedWindow, texture, 0, 0) 37 | 38 | unwrappedWindow:Render() -------------------------------------------------------------------------------- /examples/02-Textures/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /gh/Banner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /gh/Seperation.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /gh/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osgl-rbx/osgl/417d77e7cc49760144fcbe04cbe752e09979aee6/gh/logo.png -------------------------------------------------------------------------------- /moonwave.toml: -------------------------------------------------------------------------------- 1 | title = "OSGL" 2 | gitRepoUrl = "https://github.com/osgl-rbx/osgl" # From Git 3 | 4 | gitSourceBranch = "main" 5 | changelog = true 6 | classOrder = [] 7 | 8 | [docusaurus] 9 | onBrokenLinks = "throw" 10 | onBrokenMarkdownLinks = "warn" 11 | tagline = "Open Source Graphical Library" 12 | favicon = "image/favicon.ico" 13 | 14 | [home] 15 | enabled = true 16 | includeReadme = false 17 | bannerImage = "image/banner.svg" 18 | 19 | [[home.features]] 20 | title = "High performance" 21 | description = "OSGL is designed for speed and efficiency, which means it can render graphics in real time with minimal delay. You can always count on OSGL to provide top-notch performance." 22 | 23 | [[home.features]] 24 | title = "Battle-tested" 25 | description = "OSGL has been tested in many different situations to make sure it works reliably, even on low-end devices." 26 | 27 | [[home.features]] 28 | title = "User-Friendly documentation" 29 | description = "We know that starting with a new library can be tough. That’s why OSGL comes with easy-to-follow documentation that explains each feature in simple terms. And if you ever have questions or run into problems, we're here to help!" 30 | 31 | projectName = "OSGL" 32 | 33 | [footer] 34 | style = "dark" 35 | copyright = "Copyright © 2025 OSGL" 36 | -------------------------------------------------------------------------------- /plugin.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "osgl-plugin", 3 | "tree": { 4 | "$className": "DataModel", 5 | "ServerStorage": { 6 | "osgl-tools": { 7 | "$className": "Folder", 8 | "OSGLNotifications": { 9 | "$path": "plugin/notifications.rbxm" 10 | }, 11 | "scripts": { 12 | "$path": "plugin/scripts" 13 | }, 14 | "OSGL": { 15 | "$path": "src" 16 | }, 17 | "osgl-image-uploader": { 18 | "$path": "plugin/image" 19 | } 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /plugin/image/tool.luau: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------ 2 | -- OSGL v1.6.15 - Open-Source Graphical Library 3 | -- Created/maintained by @saaawdust and contributors 4 | -- 5 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 6 | -- Credit appreciated but not required 7 | -- 8 | -- Do NOT: 9 | -- - Repackage and sell OSGL directly 10 | -- - Claim you created the core library 11 | -- 12 | -- By using OSGL, you agree to these terms. 13 | -- Full license available in the LICENSE file 14 | ------------------------------------------------------------ 15 | 16 | local StudioService = game:GetService("StudioService") 17 | local Selection = game:GetService("Selection") 18 | 19 | local image = script.Parent 20 | local osglTools = image.Parent 21 | local osglScripts = osglTools.scripts 22 | 23 | local PNG = require(image.png) 24 | local notif = require(osglScripts.notif) 25 | 26 | local CHUNK_LIMIT = 200000 -- Equivilent to 200KB 27 | 28 | function toHex(num: number): string 29 | return string.format("%02X", num) 30 | end 31 | 32 | function estimateOutput(width: number, height: number) 33 | local bufferLength = width * height * 12 34 | 35 | local headerLength = 84 36 | local widthLength = string.len(tostring(width)) 37 | local heightLength = string.len(tostring(height)) 38 | 39 | return headerLength + widthLength + heightLength + bufferLength + (width * height * 4) 40 | end 41 | 42 | return { 43 | Name = "Image Uploader", 44 | Icon = "rbxassetid://71989091852609", 45 | 46 | OnButtonClick = function(pluginButton: PluginToolbarButton) 47 | if _G.osglTools.imageEditorRunning == true then 48 | return 49 | end 50 | 51 | _G.osglTools.imageEditorRunning = true 52 | pluginButton.Icon = "rbxassetid://73679864301651" 53 | 54 | local totalTime = 0 55 | local selectedParents = Selection:Get() 56 | local selectedItems = {} 57 | local successFiles = 0 58 | 59 | if #selectedParents == 0 then 60 | table.insert(selectedParents, workspace) 61 | end 62 | 63 | -- Prompt the user for a PNG 64 | local files: {File} = StudioService:PromptImportFiles({ "png" }) 65 | for _, file in files do 66 | local Success, pngData = pcall(function() 67 | return PNG.new(file:GetBinaryContents()) 68 | end) 69 | 70 | local fileName = string.match(file.Name, "(.+)%..+$") or file.Name 71 | if not Success then 72 | notif.invoke(`Failed to convert Texture '{fileName}'! Could not convert PNG.`) 73 | continue 74 | end 75 | 76 | local width, height = pngData.Width, pngData.Height 77 | local indexes = width * height 78 | local bfr = "" 79 | 80 | if indexes > 12494 or estimateOutput(width, height) >= CHUNK_LIMIT then 81 | notif.invoke(`Failed to convert Texture '{fileName}'! This Texture is too big. Try to keep Textures under 111x111.`) 82 | continue 83 | end 84 | 85 | notif.invoke(`Converting '{fileName}'.`) 86 | 87 | task.wait(.1) 88 | 89 | local startTime = os.clock() 90 | local endTime 91 | Success, _ = pcall(function() 92 | for offset = 0, indexes do 93 | local Col, A = pngData:GetPixel((offset % width) + 1, (offset // width) + 1) 94 | bfr ..= `\\x{toHex(Col.R * 255)}\\x{toHex(Col.G * 255)}\\x{toHex(Col.B * 255)}\\x{toHex(A)}` 95 | end 96 | 97 | endTime = math.floor((os.clock() - startTime) * 100 + 0.5) / 100 98 | end) 99 | 100 | if not Success then 101 | notif.invoke(`Failed to convert Texture '{fileName}'! Could not convert PNG.`) 102 | continue 103 | end 104 | 105 | totalTime += endTime 106 | 107 | local output = Instance.new("ModuleScript") 108 | output.Name = fileName 109 | output.Source = `return \{\n\tversion = "1.6.1",\n\twidth = {width},\n\theight = {height},\n\tpixels = buffer.fromstring("{bfr}")\n}` 110 | 111 | for _, parent in selectedParents do 112 | local newOutput = output:Clone() 113 | newOutput.Parent = parent 114 | 115 | table.insert(selectedItems, newOutput) 116 | end 117 | 118 | output:Destroy() 119 | notif.invoke(`Successfully converted '{fileName}' in {endTime}s`) 120 | 121 | successFiles += 1 122 | end 123 | 124 | local amountOfFiles = #files 125 | if amountOfFiles > 1 then 126 | local diff = amountOfFiles - successFiles 127 | local extraMessage = successFiles == amountOfFiles and "" or ` with {amountOfFiles - successFiles} error{diff == 1 and "" or "s"}.` 128 | notif.invoke(`Successfully converted {successFiles}/{amountOfFiles} files in {totalTime}s{extraMessage}`) 129 | end 130 | 131 | Selection:Set(selectedItems) 132 | 133 | _G.osglTools.imageEditorRunning = false 134 | pluginButton.Icon = "rbxassetid://71989091852609" 135 | end 136 | } 137 | -------------------------------------------------------------------------------- /plugin/notifications.rbxm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osgl-rbx/osgl/417d77e7cc49760144fcbe04cbe752e09979aee6/plugin/notifications.rbxm -------------------------------------------------------------------------------- /plugin/scripts/bootstrap.server.luau: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------ 2 | -- OSGL v1.6.15 - Open-Source Graphical Library 3 | -- Created/maintained by @saaawdust and contributors 4 | -- 5 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 6 | -- Credit appreciated but not required 7 | -- 8 | -- Do NOT: 9 | -- - Repackage and sell OSGL directly 10 | -- - Claim you created the core library 11 | -- 12 | -- By using OSGL, you agree to these terms. 13 | -- Full license available in the LICENSE file 14 | ------------------------------------------------------------ 15 | 16 | local RunService = game:GetService("RunService") 17 | if not plugin or not RunService:IsStudio() or RunService:IsRunning() then 18 | return 19 | end 20 | 21 | local osglScripts = script.Parent 22 | local osglTools = osglScripts.Parent 23 | 24 | local types = require(osglScripts.types) 25 | local notif = require(osglScripts.notif) 26 | 27 | local Toolbar = plugin:CreateToolbar("OSGL Tools") 28 | 29 | _G.osglTools = {} 30 | 31 | local connections: {RBXScriptConnection} = {} 32 | 33 | for _, tool: Instance in osglTools:GetChildren() do 34 | local toolModule = tool:FindFirstChild("tool") 35 | if tool:IsA("Folder") and toolModule and toolModule:IsA("ModuleScript") then 36 | local moduleData: types.ToolbarData = require(toolModule) 37 | moduleData.Tooltip = moduleData.Tooltip or "OSGL ".. moduleData.Name 38 | moduleData.Id = moduleData.Id or moduleData.Name 39 | 40 | local pluginButton = Toolbar:CreateButton(moduleData.Id, moduleData.Tooltip, moduleData.Icon, moduleData.Name) 41 | table.insert(connections, pluginButton.Click:Connect(function() 42 | moduleData.OnButtonClick(pluginButton) 43 | end)) 44 | end 45 | end 46 | 47 | notif.setup() 48 | 49 | table.insert(connections, plugin.Unloading:Connect(function() 50 | for _, v in connections do 51 | v:Disconnect() 52 | end 53 | 54 | _G.osglTools.imageEditorRunning = false 55 | end)) 56 | -------------------------------------------------------------------------------- /plugin/scripts/notif.luau: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------ 2 | -- OSGL v1.6.15 - Open-Source Graphical Library 3 | -- Created/maintained by @saaawdust and contributors 4 | -- 5 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 6 | -- Credit appreciated but not required 7 | -- 8 | -- Do NOT: 9 | -- - Repackage and sell OSGL directly 10 | -- - Claim you created the core library 11 | -- 12 | -- By using OSGL, you agree to these terms. 13 | -- Full license available in the LICENSE file 14 | ------------------------------------------------------------ 15 | 16 | local CoreGui = game:GetService("CoreGui") 17 | local TweenService = game:GetService("TweenService") 18 | 19 | local osglScripts = script.Parent 20 | local osglPlugin = osglScripts.Parent 21 | 22 | local notifGui 23 | local quickTween = TweenInfo.new(.125, Enum.EasingStyle.Quad) 24 | 25 | local Notifications = {} 26 | 27 | function Notifications.setup() 28 | local template = osglPlugin:FindFirstChildOfClass("ScreenGui") 29 | local preexistingItem = CoreGui:FindFirstChild(template.Name) 30 | if preexistingItem then 31 | notifGui = preexistingItem 32 | return 33 | end 34 | 35 | local newClone = template:Clone() 36 | newClone.Parent = CoreGui 37 | notifGui = newClone 38 | 39 | return 40 | end 41 | 42 | local function destroyNotif(notification) 43 | local notifContents = notification.Notif.Content.Contents 44 | local textObject = notifContents.Info 45 | 46 | TweenService:Create(notification.Notif, quickTween, { ImageTransparency = 1, Position = UDim2.fromScale(1, 0) }):Play() 47 | TweenService:Create(textObject, quickTween, { TextTransparency = 1 }):Play() 48 | TweenService:Create(notifContents.Logo, quickTween, { ImageTransparency = 1 }):Play() 49 | 50 | task.wait(.5) 51 | notification:Destroy() 52 | 53 | return 54 | end 55 | 56 | function Notifications.invoke(message: string) 57 | local messageTemplate = notifGui.Template 58 | local newMessage = messageTemplate:Clone() 59 | newMessage.Visible = false 60 | 61 | local notifContents = newMessage.Notif.Content.Contents 62 | local textObject = notifContents.Info 63 | textObject.Text = message 64 | 65 | newMessage.Parent = notifGui 66 | newMessage.Size = UDim2.fromOffset(math.max(textObject.TextBounds.X, 32) + 88, math.max(textObject.TextBounds.Y, 32) + 27.4) 67 | 68 | newMessage.Notif.ImageTransparency = 1 69 | textObject.TextTransparency = 1 70 | notifContents.Logo.ImageTransparency = 1 71 | 72 | newMessage.Visible = true 73 | 74 | TweenService:Create(newMessage.Notif, quickTween, { ImageTransparency = 0, Position = UDim2.fromOffset(0, 0) }):Play() 75 | TweenService:Create(textObject, quickTween, { TextTransparency = 0 }):Play() 76 | TweenService:Create(notifContents.Logo, quickTween, { ImageTransparency = 0 }):Play() 77 | 78 | local connectionOnClick 79 | connectionOnClick = newMessage.MouseButton1Click:Connect(function() 80 | connectionOnClick:Disconnect() 81 | destroyNotif(newMessage) 82 | end) 83 | 84 | task.delay(5, function() 85 | if newMessage.Parent then 86 | destroyNotif(newMessage) 87 | end 88 | end) 89 | end 90 | 91 | return Notifications 92 | -------------------------------------------------------------------------------- /plugin/scripts/types.luau: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------ 2 | -- OSGL v1.6.15 - Open-Source Graphical Library 3 | -- Created/maintained by @saaawdust and contributors 4 | -- 5 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 6 | -- Credit appreciated but not required 7 | -- 8 | -- Do NOT: 9 | -- - Repackage and sell OSGL directly 10 | -- - Claim you created the core library 11 | -- 12 | -- By using OSGL, you agree to these terms. 13 | -- Full license available in the LICENSE file 14 | ------------------------------------------------------------ 15 | 16 | export type ToolbarData = { 17 | Name: string, 18 | Icon: string, 19 | Tooltip: string?, 20 | Id: string?, 21 | 22 | OnButtonClick: (Button: PluginToolbarButton) -> (), 23 | } 24 | 25 | return nil 26 | -------------------------------------------------------------------------------- /sourcemap.json: -------------------------------------------------------------------------------- 1 | {"name":"OSGL","className":"DataModel","filePaths":["default.project.json"],"children":[{"name":"ReplicatedStorage","className":"ReplicatedStorage","children":[{"name":"OSGL","className":"ModuleScript","filePaths":["src\\init.luau"],"children":[{"name":"DrawableObject","className":"Folder","children":[{"name":"base","className":"ModuleScript","filePaths":["src\\DrawableObject\\base.luau"]},{"name":"texture","className":"ModuleScript","filePaths":["src\\DrawableObject\\texture\\init.luau"],"children":[{"name":"canvasDrawStringCompressor","className":"ModuleScript","filePaths":["src\\DrawableObject\\texture\\canvasDrawStringCompressor.luau"]}]},{"name":"window","className":"ModuleScript","filePaths":["src\\DrawableObject\\window.luau"]},{"name":"windowBase","className":"ModuleScript","filePaths":["src\\DrawableObject\\windowBase.luau"]}]},{"name":"bitmap","className":"ModuleScript","filePaths":["src\\bitmap.luau"]},{"name":"color","className":"ModuleScript","filePaths":["src\\color.luau"]},{"name":"draw","className":"ModuleScript","filePaths":["src\\draw\\init.luau"],"children":[{"name":"buffer","className":"ModuleScript","filePaths":["src\\draw\\buffer.luau"]},{"name":"chunk","className":"ModuleScript","filePaths":["src\\draw\\chunk.luau"]},{"name":"circle","className":"ModuleScript","filePaths":["src\\draw\\circle.luau"]},{"name":"clear","className":"ModuleScript","filePaths":["src\\draw\\clear.luau"]},{"name":"floodFill","className":"ModuleScript","filePaths":["src\\draw\\floodFill.luau"]},{"name":"line","className":"ModuleScript","filePaths":["src\\draw\\line.luau"]},{"name":"pixel","className":"ModuleScript","filePaths":["src\\draw\\pixel.luau"]},{"name":"pixelIndex","className":"ModuleScript","filePaths":["src\\draw\\pixelIndex.luau"]},{"name":"polygon","className":"ModuleScript","filePaths":["src\\draw\\polygon.luau"]},{"name":"rect","className":"ModuleScript","filePaths":["src\\draw\\rect.luau"]},{"name":"triangle","className":"ModuleScript","filePaths":["src\\draw\\triangle.luau"]}]},{"name":"enums","className":"ModuleScript","filePaths":["src\\enums.luau"]},{"name":"font","className":"ModuleScript","filePaths":["src\\font.luau"]},{"name":"result","className":"ModuleScript","filePaths":["src\\result.luau"]},{"name":"types","className":"ModuleScript","filePaths":["src\\types.luau"]},{"name":"util","className":"ModuleScript","filePaths":["src\\util.luau"]},{"name":"video","className":"ModuleScript","filePaths":["src\\video.luau"]},{"name":"wally","className":"ModuleScript","filePaths":["src\\wally.toml"]}]}]},{"name":"ServerScriptService","className":"ServerScriptService","children":[{"name":"tests","className":"Folder","children":[{"name":"bitmap-combine","className":"Script","filePaths":["tests/server\\bitmap-combine\\init.meta.json","tests/server\\bitmap-combine\\init.server.luau"],"children":[{"name":"R","className":"ModuleScript","filePaths":["tests/server\\bitmap-combine\\R.luau"]},{"name":"G","className":"ModuleScript","filePaths":["tests/server\\bitmap-combine\\G.luau"]},{"name":"B","className":"ModuleScript","filePaths":["tests/server\\bitmap-combine\\B.luau"]}]},{"name":"bitmap-drawing","className":"Script","filePaths":["tests/server\\bitmap-drawing\\init.meta.json","tests/server\\bitmap-drawing\\init.server.luau"],"children":[{"name":"Bitmap","className":"ModuleScript","filePaths":["tests/server\\bitmap-drawing\\Bitmap.luau"]}]},{"name":"buffer-overhead","className":"Script","filePaths":["tests/server\\buffer-overhead\\init.meta.json","tests/server\\buffer-overhead\\init.server.luau"]},{"name":"chunk","className":"Script","filePaths":["tests/server\\chunk\\init.meta.json","tests/server\\chunk\\init.server.luau"]},{"name":"circle-overhead","className":"Script","filePaths":["tests/server\\circle-overhead\\init.meta.json","tests/server\\circle-overhead\\init.server.luau"]},{"name":"destroy-window","className":"Script","filePaths":["tests/server\\destroy-window\\init.meta.json","tests/server\\destroy-window\\init.server.luau"]},{"name":"fake-mask-creation","className":"Script","filePaths":["tests/server\\fake-mask-creation\\init.meta.json","tests/server\\fake-mask-creation\\init.server.luau"]},{"name":"fake-texture-serialization","className":"Script","filePaths":["tests/server\\fake-texture-serialization\\init.meta.json","tests/server\\fake-texture-serialization\\init.server.luau"]},{"name":"line-overhead","className":"Script","filePaths":["tests/server\\line-overhead\\init.meta.json","tests/server\\line-overhead\\init.server.luau"]},{"name":"mesh-support","className":"Script","filePaths":["tests/server\\mesh-support\\init.meta.json","tests/server\\mesh-support\\init.server.luau"]},{"name":"pixel-overhead","className":"Script","filePaths":["tests/server\\pixel-overhead\\init.meta.json","tests/server\\pixel-overhead\\init.server.luau"]},{"name":"rainbow-mass-draw","className":"Script","filePaths":["tests/server\\rainbow-mass-draw\\init.meta.json","tests/server\\rainbow-mass-draw\\init.server.luau"]},{"name":"rect-overhead","className":"Script","filePaths":["tests/server\\rect-overhead\\init.meta.json","tests/server\\rect-overhead\\init.server.luau"]},{"name":"resize-overhead","className":"Script","filePaths":["tests/server\\resize-overhead\\init.meta.json","tests/server\\resize-overhead\\init.server.luau"]},{"name":"texture-asset-id","className":"Script","filePaths":["tests/server\\texture-asset-id\\init.meta.json","tests/server\\texture-asset-id\\init.server.luau"]},{"name":"texture-draw","className":"Script","filePaths":["tests/server\\texture-draw\\init.meta.json","tests/server\\texture-draw\\init.server.luau"],"children":[{"name":"RobloxLogo","className":"ModuleScript","filePaths":["tests/server\\texture-draw\\RobloxLogo.luau"]},{"name":"RobloxLogo2","className":"ModuleScript","filePaths":["tests/server\\texture-draw\\RobloxLogo2.luau"]}]},{"name":"texture-instantiation","className":"Script","filePaths":["tests/server\\texture-instantiation\\init.meta.json","tests/server\\texture-instantiation\\init.server.luau"]},{"name":"texture-rotation","className":"Script","filePaths":["tests/server\\texture-rotation\\init.meta.json","tests/server\\texture-rotation\\init.server.luau"],"children":[{"name":"RobloxLogo","className":"ModuleScript","filePaths":["tests/server\\texture-rotation\\RobloxLogo.luau"]}]},{"name":"triangle-overhead","className":"Script","filePaths":["tests/server\\triangle-overhead\\init.meta.json","tests/server\\triangle-overhead\\init.server.luau"]},{"name":"window-bootstrap","className":"Script","filePaths":["tests/server\\window-bootstrap\\init.meta.json","tests/server\\window-bootstrap\\init.server.luau"]},{"name":"window-resample","className":"Script","filePaths":["tests/server\\window-resample\\init.meta.json","tests/server\\window-resample\\init.server.luau"]}]}]},{"name":"StarterPlayer","className":"StarterPlayer","children":[{"name":"StarterPlayerScripts","className":"StarterPlayerScripts","children":[{"name":"tests","className":"Folder","children":[{"name":"circle-draw","className":"LocalScript","filePaths":["tests/client\\circle-draw\\init.meta.json","tests/client\\circle-draw\\init.client.luau"]},{"name":"relative-mouse","className":"LocalScript","filePaths":["tests/client\\relative-mouse\\init.meta.json","tests/client\\relative-mouse\\init.client.luau"]}]}]}]}]} -------------------------------------------------------------------------------- /src/DrawableObject/base.luau: -------------------------------------------------------------------------------- 1 | --!optimize 2 2 | --!native 3 | 4 | ------------------------------------------------------------ 5 | -- OSGL v1.6.2 - Open-Source Graphical Library 6 | -- Created/maintained by @saaawdust and contributors 7 | -- 8 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 9 | -- Credit appreciated but not required 10 | -- 11 | -- Do NOT: 12 | -- - Repackage and sell OSGL directly 13 | -- - Claim you created the core library 14 | -- 15 | -- By using OSGL, you agree to these terms. 16 | -- Full license available in the LICENSE file 17 | ------------------------------------------------------------ 18 | 19 | local AssetService = game:GetService("AssetService") 20 | 21 | local DrawableObject = script.Parent 22 | local OSGL = DrawableObject.Parent 23 | local types = require(OSGL.types) 24 | local result = require(OSGL.result) 25 | local oEnum = require(OSGL.enums) 26 | local util = require(OSGL.util) 27 | local draw = require(OSGL.draw) 28 | local color = require(OSGL.color) 29 | 30 | type Result = result.Result 31 | 32 | local DrawBaseImpl = {} 33 | 34 | --[=[ 35 | @OSGL/Drawable 36 | Resizes the DrawableObject to the given width and height. 37 | ]=] 38 | function DrawBaseImpl.Resize( 39 | self: types.DrawableObject, 40 | width: number?, 41 | height: number? 42 | ): Result 43 | local currentWidth, currentHeight = self.width, self.height 44 | 45 | width = width or currentWidth 46 | height = height or currentHeight 47 | 48 | if width == currentHeight and height == currentHeight then 49 | return result.new(true, nil) 50 | end 51 | 52 | if width > 1024 or height > 1024 or width < 0 or height < 0 then 53 | return result.new(false, oEnum.DrawableObjectError.ResizeOutOfBounds) 54 | end 55 | 56 | local copy_size 57 | local bfrSize = width * height * 4 58 | local currentSize = buffer.len(self.buffer) 59 | if bfrSize < currentSize then 60 | copy_size = bfrSize 61 | else 62 | copy_size = currentSize 63 | end 64 | 65 | local resizedBuffer = buffer.create(bfrSize) 66 | buffer.copy(resizedBuffer, 0, self.buffer, 0, copy_size) 67 | 68 | self.buffer = resizedBuffer 69 | 70 | local size = Vector2.new(width, height) 71 | self.size = size 72 | self.width = width 73 | self.height = height 74 | 75 | -- Is a window 76 | if self.editableImage then 77 | local editableImage = AssetService:CreateEditableImage({ Size = size }) 78 | if not editableImage then 79 | return result.new(false, oEnum.DrawableObjectError.NotEnoughMemory) 80 | end 81 | 82 | self.editableImage = editableImage 83 | local content = Content.fromObject(editableImage) 84 | for _, v in self.surfaces do 85 | local prop = util.getRendererProp(v) 86 | if prop == "" then 87 | continue 88 | end 89 | 90 | v[prop] = content 91 | end 92 | end 93 | 94 | return result.new(true, nil) 95 | end 96 | 97 | --[=[ 98 | @OSGL/Drawable 99 | Serializes the DrawableObject. 100 | ]=] 101 | function DrawBaseImpl.Serialize(self: types.DrawableObject): (buffer, number, number) 102 | return self.buffer, self.width, self.height 103 | end 104 | 105 | --[=[ 106 | @OSGL/Drawable 107 | Deserializes a serialized object onto the DrawableObject. 108 | ]=] 109 | function DrawBaseImpl.Deserialize( 110 | self: types.DrawableObject, 111 | buff: buffer, 112 | width: number, 113 | height: number 114 | ): Result 115 | 116 | local otherBuffLen = buffer.len(buff) 117 | if (otherBuffLen ~= width * height * 4) then 118 | return result.new(false, oEnum.DrawableObjectError.BadBufferSize) 119 | end 120 | 121 | if otherBuffLen > buffer.len(self.buffer) then 122 | DrawBaseImpl.Resize(self, math.max(width, self.width), math.max(height, self.height)) 123 | end 124 | 125 | buffer.copy(self.buffer, 0, buff) 126 | 127 | return result.new(true, nil) 128 | end 129 | 130 | --[=[ 131 | @OSGL/Drawable 132 | @Checked 133 | Returns the color of the given pixel, if in bound. 134 | ]=] 135 | function DrawBaseImpl.ReadPixelChecked( 136 | self: types.DrawableObject, 137 | x: number, 138 | y: number 139 | ): Result 140 | if x >= 0 and y >= 0 and x < self.width and y < self.height then 141 | return result.new(true, buffer.readu32(self.buffer, (y * self.width + x) * 4)) 142 | end 143 | 144 | return result.new(false, oEnum.DrawableObjectError.OutOfBounds) 145 | end 146 | 147 | --[=[ 148 | @OSGL/Drawable 149 | @Unhecked! 150 | Returns the color of the given pixel 151 | ]=] 152 | function DrawBaseImpl.ReadPixelUnchecked(self: types.DrawableObject, x: number, y: number): types.Color 153 | return buffer.readu32(self.buffer, (y * self.width + x) * 4) 154 | end 155 | 156 | local function doTint(self, tint, factor, x, y, finalX, finalY) 157 | local tintR = color.r(tint) 158 | local tintG = color.g(tint) 159 | local tintB = color.b(tint) 160 | local tintA = color.a(tint) 161 | 162 | local sizeX = self.width 163 | local bfr = self.buffer 164 | for x = x, finalX do 165 | for y = y, finalY do 166 | local pixelColor = buffer.readu32(bfr, (y * sizeX + x) * 4) 167 | 168 | buffer.writeu32( 169 | bfr, 170 | (y * sizeX + x) * 4, 171 | color.new( 172 | math.lerp(color.r(pixelColor), tintR, factor), 173 | math.lerp(color.g(pixelColor), tintG, factor), 174 | math.lerp(color.b(pixelColor), tintB, factor), 175 | math.lerp(color.a(pixelColor), tintA, factor) 176 | ) 177 | ) 178 | end 179 | end 180 | end 181 | 182 | --[=[ 183 | @OSGL/Drawable 184 | @Checked 185 | Tints a region of pixels. 186 | ]=] 187 | function DrawBaseImpl.TintRegionChecked( 188 | self: types.DrawableObject, 189 | tint: types.Color, 190 | factor: number, 191 | x: number, 192 | y: number, 193 | width: number, 194 | height: number 195 | ) 196 | local finalX = x + width 197 | local finalY = y + height 198 | 199 | if x < 0 or x > width or y < 0 or y > height or finalX < 0 or finalX > width or finalY < 0 or finalY > height then 200 | return result.new(false, oEnum.DrawableObjectError.OutOfBounds) 201 | end 202 | 203 | doTint(self, tint, factor, x, y, finalX, finalY) 204 | 205 | return result.new(true, nil) 206 | end 207 | 208 | --[=[ 209 | @OSGL/Drawable 210 | @Unchecked! 211 | Tints a region of pixels. 212 | ]=] 213 | function DrawBaseImpl.TintRegionUnchecked( 214 | self: types.DrawableObject, 215 | tint: types.Color, 216 | factor: number, 217 | x: number, 218 | y: number, 219 | width: number, 220 | height: number 221 | ) 222 | return doTint(self, tint, factor, x, y, x + width, x + height) 223 | end 224 | 225 | --[=[ 226 | @OSGL/Drawable 227 | Tints the entire object. 228 | ]=] 229 | function DrawBaseImpl.Tint(self: types.DrawableObject, tint: number, factor: number) 230 | doTint(self, tint, factor, 0, 0, self.width - 1, self.height - 1) 231 | end 232 | 233 | --[=[ 234 | @OSGL/Drawable 235 | Resamples the entire object. 236 | ]=] 237 | function DrawBaseImpl.Resample(self: types.DrawableObject, scale: number?) 238 | scale = scale or 1 239 | 240 | -- Get original texture size and buffer 241 | local originalWidth = self.width 242 | local originalHeight = self.height 243 | 244 | -- Calculate the new size of the texture 245 | local newWidth = math.clamp(math.floor(originalWidth * scale), 1, 1024) 246 | local newHeight = math.clamp(math.floor(originalHeight * scale), 1, 1024) 247 | 248 | local newBuffer = buffer.create(newWidth * newHeight * 4) 249 | 250 | for newY = 0, newHeight - 1 do 251 | for newX = 0, newWidth - 1 do 252 | local srcX = math.floor(math.clamp((newX + 0.5) / scale - 0.5, 0, originalWidth - 1) + 0.5) 253 | local srcY = math.floor(math.clamp((newY + 0.5) / scale - 0.5, 0, originalHeight - 1) + 0.5) 254 | local color = buffer.readu32(self.buffer, (srcY * originalWidth + srcX) * 4) 255 | buffer.writeu32(newBuffer, (newY * newWidth + newX) * 4, color) 256 | end 257 | end 258 | 259 | local size = Vector2.new(newWidth, newHeight); 260 | if self.editableImage then 261 | local editableImage = AssetService:CreateEditableImage({ Size = size }) 262 | if not editableImage then 263 | return result.new(false, oEnum.DrawableObjectError.NotEnoughMemory) 264 | end 265 | 266 | self.editableImage = editableImage 267 | local content = Content.fromObject(editableImage) 268 | for _, v in self.surfaces do 269 | local prop = util.getRendererProp(v) 270 | if prop == "" then 271 | continue 272 | end 273 | 274 | v[prop] = content 275 | end 276 | end 277 | 278 | self.size = size 279 | self.width = newWidth 280 | self.height = newHeight 281 | self.buffer = newBuffer 282 | end 283 | 284 | export type Base = typeof(DrawBaseImpl) 285 | 286 | return { 287 | new = function(): Base 288 | return { 289 | Resize = DrawBaseImpl.Resize, 290 | Serialize = DrawBaseImpl.Serialize, 291 | Deserialize = DrawBaseImpl.Deserialize, 292 | ReadPixelChecked = DrawBaseImpl.ReadPixelChecked, 293 | ReadPixelUnchecked = DrawBaseImpl.ReadPixelUnchecked, 294 | TintRegionChecked = DrawBaseImpl.TintRegionChecked, 295 | TintRegionUnchecked = DrawBaseImpl.TintRegionUnchecked, 296 | Tint = DrawBaseImpl.Tint, 297 | Resample = DrawBaseImpl.Resample, 298 | 299 | Pixel = draw.Pixel, 300 | PixelIndex = draw.PixelIndex, 301 | Line = draw.Line, 302 | Rectangle = draw.Rectangle, 303 | Polygon = draw.Polygon, 304 | Triangle = draw.Triangle, 305 | Circle = draw.Circle, 306 | Buffer = draw.Buffer, 307 | Clear = draw.Clear, 308 | Chunk = draw.Chunk, 309 | FloodFill = draw.FloodFill, 310 | } 311 | end 312 | } 313 | -------------------------------------------------------------------------------- /src/DrawableObject/texture/canvasDrawStringCompressor.luau: -------------------------------------------------------------------------------- 1 | --!native 2 | 3 | -- Module by 1waffle1, optimized by boatbomber 4 | -- https://devforum.roblox.com/t/text-compression/163637 5 | 6 | local gsub = string.gsub 7 | local sub = string.sub 8 | local insert = table.insert 9 | local rep = string.rep 10 | 11 | local dictionary = {} 12 | do -- populate dictionary 13 | local length = 0 14 | for i = 32, 127 do 15 | if i ~= 34 and i ~= 92 then 16 | local c = string.char(i) 17 | dictionary[c], dictionary[length] = length, c 18 | length = length + 1 19 | end 20 | end 21 | end 22 | 23 | local escapemap = {} 24 | do -- Populate escape map 25 | for i = 1, 34 do 26 | i = ({ 34, 92, 127 })[i - 31] or i 27 | local c, e = string.char(i), string.char(i + 31) 28 | escapemap[c], escapemap[e] = e, c 29 | end 30 | end 31 | 32 | local function escape(s) 33 | return gsub(s, '[%c"\\]', function(c) 34 | return "\127" .. escapemap[c] 35 | end) 36 | end 37 | local function unescape(s) 38 | return gsub(s, "\127(.)", function(c) 39 | return escapemap[c] 40 | end) 41 | end 42 | 43 | local function copy(t) 44 | local new = {} 45 | for k, v in pairs(t) do 46 | new[k] = v 47 | end 48 | return new 49 | end 50 | 51 | local b93Cache = {} 52 | local function tobase93(n) 53 | local value = b93Cache[n] 54 | if value then 55 | return value 56 | end 57 | 58 | value = "" 59 | repeat 60 | local remainder = n % 93 61 | value = dictionary[remainder] .. value 62 | n = (n - remainder) / 93 63 | until n == 0 64 | 65 | b93Cache[n] = value 66 | return value 67 | end 68 | 69 | local b10Cache = {} 70 | local function tobase10(value) 71 | local n = b10Cache[value] 72 | if n then 73 | return n 74 | end 75 | 76 | n = 0 77 | for i = 1, #value do 78 | n = n + 93 ^ (i - 1) * dictionary[sub(value, -i, -i)] 79 | end 80 | 81 | b10Cache[value] = n 82 | return n 83 | end 84 | 85 | local function compress(text) 86 | local dictionaryCopy = copy(dictionary) 87 | local key, sequence, size = "", {}, #dictionaryCopy 88 | local width, spans, span = 1, {}, 0 89 | local function listkey(k) 90 | local value = tobase93(dictionaryCopy[k]) 91 | local valueLength = #value 92 | if valueLength > width then 93 | width, span, spans[width] = valueLength, 0, span 94 | end 95 | insert(sequence, rep(" ", width - valueLength) .. value) 96 | span += 1 97 | end 98 | text = escape(text) 99 | for i = 1, #text do 100 | local c = sub(text, i, i) 101 | local new = key .. c 102 | if dictionaryCopy[new] then 103 | key = new 104 | else 105 | listkey(key) 106 | key = c 107 | size += 1 108 | dictionaryCopy[new], dictionaryCopy[size] = size, new 109 | end 110 | end 111 | listkey(key) 112 | spans[width] = span 113 | return table.concat(spans, ",") .. "|" .. table.concat(sequence) 114 | end 115 | 116 | local function decompress(text) 117 | local dictionaryCopy = copy(dictionary) 118 | local sequence, spans, content = {}, string.match(text, "(.-)|(.*)") 119 | local groups, start = {}, 1 120 | for span in string.gmatch(spans, "%d+") do 121 | local width = #groups + 1 122 | groups[width] = sub(content, start, start + span * width - 1) 123 | start = start + span * width 124 | end 125 | local previous 126 | 127 | for width, group in ipairs(groups) do 128 | for value in string.gmatch(group, rep(".", width)) do 129 | local entry = dictionaryCopy[tobase10(value)] 130 | if previous then 131 | if entry then 132 | insert(dictionaryCopy, previous .. sub(entry, 1, 1)) 133 | else 134 | entry = previous .. sub(previous, 1, 1) 135 | insert(dictionaryCopy, entry) 136 | end 137 | insert(sequence, entry) 138 | else 139 | sequence[1] = entry 140 | end 141 | previous = entry 142 | end 143 | end 144 | return unescape(table.concat(sequence)) 145 | end 146 | 147 | return { Compress = compress, Decompress = decompress } -------------------------------------------------------------------------------- /src/DrawableObject/texture/init.luau: -------------------------------------------------------------------------------- 1 | --!optimize 2 2 | --!native 3 | 4 | ------------------------------------------------------------ 5 | -- OSGL v1.6.2 - Open-Source Graphical Library 6 | -- Created/maintained by @saaawdust and contributors 7 | -- 8 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 9 | -- Credit appreciated but not required 10 | -- 11 | -- Do NOT: 12 | -- - Repackage and sell OSGL directly 13 | -- - Claim you created the core library 14 | -- 15 | -- By using OSGL, you agree to these terms. 16 | -- Full license available in the LICENSE file 17 | ------------------------------------------------------------ 18 | 19 | local AssetService = game:GetService("AssetService") 20 | local HttpService = game:GetService("HttpService") 21 | 22 | local DrawableObject = script.Parent 23 | local base = require(DrawableObject.base) 24 | local stringCompressor = require(script.canvasDrawStringCompressor) 25 | 26 | local OSGL = DrawableObject.Parent 27 | local types = require(OSGL.types) 28 | local result = require(OSGL.result) 29 | local oEnum = require(OSGL.enums) 30 | local util = require(OSGL.util) 31 | local draw = require(OSGL.draw) 32 | local pixel = draw.Pixel 33 | 34 | type Result = result.Result 35 | 36 | local floor = math.floor 37 | 38 | local Texture = {} 39 | 40 | local function drawRotated( 41 | object: types.DrawableObject, 42 | texture: types.OSGLTexture, 43 | textureX: number, 44 | textureY: number, 45 | rotation: number 46 | ) 47 | local sizeY = texture.height 48 | local sizeX = texture.width 49 | local bfr = texture.buffer 50 | 51 | local radians = math.rad(rotation) 52 | local cosR = math.cos(radians) 53 | local sinR = math.sin(radians) 54 | 55 | local centerX = textureX + sizeX / 2 56 | local centerY = textureY + sizeY / 2 57 | 58 | local function rotate(x: number, y: number): number 59 | local relX = x - centerX 60 | local relY = y - centerY 61 | 62 | local rotatedX = cosR * relX - sinR * relY 63 | local rotatedY = sinR * relX + cosR * relY 64 | 65 | return rotatedX + centerX, rotatedY + centerY 66 | end 67 | 68 | for y = 0, sizeY - 1 do 69 | for x = 0, sizeX - 1 do 70 | x += textureX 71 | y += textureY 72 | 73 | local sourceX, sourceY = rotate(x, y) 74 | 75 | if sourceX >= 0 and sourceX < sizeX and sourceY >= 0 and sourceY < sizeY then 76 | local pixelColor = buffer.readu32(bfr, floor(sourceY) * sizeX * 4 + floor(sourceX) * 4) 77 | pixel(object, x, y, pixelColor) 78 | end 79 | end 80 | end 81 | end 82 | 83 | local function drawRotatedUnchecked( 84 | object: types.DrawableObject, 85 | texture: types.OSGLTexture, 86 | textureX: number, 87 | textureY: number, 88 | rotation: number 89 | ) 90 | local sizeY = texture.height 91 | local sizeX = texture.width 92 | local bfr = texture.buffer 93 | 94 | local radians = math.rad(rotation) 95 | local cosR = math.cos(radians) 96 | local sinR = math.sin(radians) 97 | 98 | local centerX = textureX + sizeX / 2 99 | local centerY = textureY + sizeY / 2 100 | 101 | local function rotate(x: number, y: number): number 102 | local relX = x - centerX 103 | local relY = y - centerY 104 | 105 | local rotatedX = cosR * relX - sinR * relY 106 | local rotatedY = sinR * relX + cosR * relY 107 | 108 | return rotatedX + centerX, rotatedY + centerY 109 | end 110 | 111 | for y = 0, sizeY - 1 do 112 | for x = 0, sizeX - 1 do 113 | x += textureX 114 | y += textureY 115 | 116 | local sourceX, sourceY = rotate(x, y) 117 | 118 | local pixelColor = buffer.readu32(bfr, floor(sourceY) * sizeX * 4 + floor(sourceX) * 4) 119 | pixel(object, x, y, pixelColor) 120 | end 121 | end 122 | end 123 | 124 | local function checkUnloadedTexture( 125 | unloadedTexture: types.RawTexture 126 | ): Result 127 | if typeof(unloadedTexture) == "Instance" and unloadedTexture:IsA("ModuleScript") then 128 | unloadedTexture = require(unloadedTexture) 129 | end 130 | 131 | if type(unloadedTexture) ~= "table" or not table.find(util.VALID_TEXTURE_VERSIONS, unloadedTexture.version) then 132 | return result.new(false, oEnum.TextureError.BadInput) 133 | end 134 | 135 | return result.new(true, unloadedTexture) 136 | end 137 | 138 | --[=[ 139 | @OSGL/Texture 140 | @Checked 141 | Creates a Texture from raw data. 142 | ]=] 143 | function Texture.fromChecked(rawTexture: types.RawTexture): Result 144 | local raw = checkUnloadedTexture(rawTexture) 145 | 146 | if not raw.isOk then 147 | return raw 148 | end 149 | 150 | return result.new(true, Texture.fromUnchecked(raw:Unwrap())) 151 | end 152 | 153 | --[=[ 154 | @OSGL/Texture 155 | @Unchecked! 156 | Creates a Texture from an unloaded one. 157 | ]=] 158 | function Texture.fromUnchecked(rawTexture: types.BaseUnloadedTexture): types.OSGLTexture 159 | local width, height = rawTexture.width, rawTexture.height 160 | 161 | local t = base.new() 162 | 163 | t.buffer = rawTexture.pixels 164 | t.width = width 165 | t.height = height 166 | t.size = Vector2.new(width, height) 167 | 168 | return t 169 | end 170 | 171 | --[=[ 172 | @OSGL/Texture 173 | @Checked 174 | Serializes the Texture. 175 | ]=] 176 | function Texture.serializeChecked(rawTexture: types.RawTexture): Result<(buffer, number, number), oEnum.TextureError> 177 | local texture = checkUnloadedTexture(rawTexture) 178 | 179 | if not texture.isOk then 180 | return rawTexture 181 | end 182 | 183 | return result.new(true, Texture.serializeUnchecked(texture:Unwrap())) 184 | end 185 | 186 | --[=[ 187 | @OSGL/Texture 188 | @Unchecked! 189 | Serializes the Texture. 190 | ]=] 191 | function Texture.serializeUnchecked(rawTexture: types.BaseUnloadedTexture): (buffer, number, number) 192 | return rawTexture.pixels, rawTexture.width, rawTexture.height 193 | end 194 | 195 | --[=[ 196 | @OSGL/Texture 197 | @Checked 198 | Draws the Texture somwhere. 199 | ]=] 200 | function Texture.drawChecked( 201 | object: types.DrawableObject, 202 | texture: types.OSGLTexture, 203 | textureX: number, 204 | textureY: number, 205 | rotation: number? 206 | ) 207 | rotation = rotation or 0 208 | 209 | if rotation == 0 then 210 | local xOffset = if textureX < 0 then math.abs(textureX) else 0 211 | local yOffset = if textureY < 0 then math.abs(textureY) else 0 212 | 213 | texture.width -= xOffset 214 | texture.height -= yOffset 215 | 216 | local drawWidth = math.min(texture.width, object.width - textureX - xOffset) 217 | local drawHeight = math.min(texture.height, object.height - textureY - yOffset) 218 | 219 | if drawWidth <= 0 or drawHeight <= 0 then 220 | return 221 | end 222 | 223 | local targetBuffer = object.buffer 224 | local sourceBuffer = texture.buffer 225 | local targetSize = texture.width * 4 226 | 227 | local maxY = if textureY + drawHeight - 1 > object.height 228 | then object.height - textureY - 1 229 | else drawHeight - 1 230 | 231 | for y = yOffset, maxY do 232 | buffer.copy( 233 | targetBuffer, 234 | ((textureY + y) * object.width + textureX + xOffset) * 4, 235 | sourceBuffer, 236 | (y * (texture.width + xOffset)) * 4, 237 | drawWidth * 4 238 | ) 239 | end 240 | 241 | texture.width += xOffset 242 | texture.height += yOffset 243 | else 244 | drawRotated(object, texture, textureX, textureY, rotation) 245 | end 246 | end 247 | 248 | --[=[ 249 | @OSGL/Texture 250 | @Unchecked! 251 | Draws the Texture somwhere. 252 | ]=] 253 | function Texture.drawUnchecked( 254 | object: types.DrawableObject, 255 | texture: types.OSGLTexture, 256 | textureX: number, 257 | textureY: number, 258 | rotation: number? 259 | ) 260 | rotation = rotation or 0 261 | 262 | if rotation == 0 then 263 | Texture.drawChecked(object, texture, textureX, textureY, rotation) 264 | else 265 | drawRotatedUnchecked(object, texture, textureX, textureY, rotation) 266 | end 267 | end 268 | 269 | --[=[ 270 | @OSGL/Texture 271 | Creates a Texture from an asset id. 272 | ]=] 273 | function Texture.fromAssetId(assetId: number): Result 274 | local newContent = Content.fromAssetId(assetId) 275 | 276 | local temporaryEditableImage = AssetService:CreateEditableImageAsync(newContent) 277 | if not temporaryEditableImage then 278 | return result.new(false, oEnum.DrawableObjectError.NotEnoughMemory) 279 | end 280 | 281 | local size = temporaryEditableImage.Size 282 | local width, height = size.X, size.Y 283 | 284 | local pixelBuffer = temporaryEditableImage:ReadPixelsBuffer(Vector2.zero, size) 285 | temporaryEditableImage:Destroy() 286 | 287 | local t = base.new() 288 | 289 | t.buffer = pixelBuffer 290 | t.width = width 291 | t.height = height 292 | t.size = Vector2.new(width, height) 293 | 294 | return result.new(true, t) 295 | end 296 | 297 | --[=[ 298 | @OSGL/Texture 299 | @Checked 300 | Loads a CanvasDraw SaveObject as an OSGL Texture. 301 | ]=] 302 | function Texture.fromCanvasDrawSaveObject( 303 | saveObject: Instance, 304 | slowLoad: boolean? 305 | ): Result 306 | local isV4Point9AndAbove = saveObject:GetAttribute("Resolution") 307 | and not saveObject:GetAttribute("Colour") 308 | and not saveObject:GetAttribute("Alpha") 309 | local isFromV4Point4ToV4Point8 = saveObject:GetAttribute("Colour") 310 | and saveObject:GetAttribute("Alpha") 311 | and saveObject:GetAttribute("Resolution") 312 | local isFromV2ToV4Point3 = saveObject:GetAttribute("ImageColours") 313 | and saveObject:GetAttribute("ImageAlphas") 314 | and saveObject:GetAttribute("ImageResolution") 315 | 316 | if 317 | not saveObject 318 | or typeof(saveObject) ~= "Instance" 319 | or not isV4Point9AndAbove and not isFromV4Point4ToV4Point8 and not isFromV2ToV4Point3 320 | then 321 | return result.new(false, oEnum.TextureError.BadInput) 322 | end 323 | 324 | -- Extracts taken from CanvasDraw/SaveObjectReader.luau by EthanTheGrand 325 | local width, height, bfr 326 | if isV4Point9AndAbove then 327 | local resolution = saveObject:GetAttribute("Resolution") 328 | width, height = resolution.X, resolution.Y 329 | 330 | bfr = buffer.create(width * height * 4) 331 | local currentBufferIndex = 0 332 | 333 | -- Decompress the data 334 | local chunkCount = #saveObject:GetChildren() 335 | 336 | for i = 1, chunkCount do 337 | local chunkString = saveObject["Chunk" .. i] 338 | local array = HttpService:JSONDecode(stringCompressor.Decompress(chunkString.Value)) 339 | 340 | for j = 1, #array do 341 | buffer.writeu8(bfr, currentBufferIndex, array[j]) 342 | currentBufferIndex += 1 343 | end 344 | 345 | if slowLoad then 346 | task.wait() 347 | end 348 | end 349 | elseif isFromV4Point4ToV4Point8 then 350 | local compressedRGB = saveObject:GetAttribute("Colour") 351 | local compressedAlpha = saveObject:GetAttribute("Alpha") 352 | local resolution = saveObject:GetAttribute("Resolution") 353 | width, height = resolution.X, resolution.Y 354 | 355 | -- Decompress the data 356 | local RGBArray = HttpService:JSONDecode(stringCompressor.Decompress(compressedRGB)) 357 | local alphaArray = HttpService:JSONDecode(stringCompressor.Decompress(compressedAlpha)) 358 | 359 | bfr = buffer.create(width * height * 4) 360 | 361 | for i = 1, width * height do 362 | local RGBIndex = i * 3 - 2 363 | local bfrIndex = i * 4 - 4 364 | 365 | buffer.writeu8(bfr, bfrIndex, RGBArray[RGBIndex]) 366 | buffer.writeu8(bfr, bfrIndex + 1, RGBArray[RGBIndex + 1]) 367 | buffer.writeu8(bfr, bfrIndex + 2, RGBArray[RGBIndex + 2]) 368 | buffer.writeu8(bfr, bfrIndex + 3, alphaArray[i]) 369 | end 370 | else 371 | local saveDataImageColours = saveObject:GetAttribute("ImageColours") 372 | local saveDataImageAlphas = saveObject:GetAttribute("ImageAlphas") 373 | local saveDataImageResolution = saveObject:GetAttribute("ImageResolution") 374 | 375 | width, height = saveDataImageResolution.X, saveDataImageResolution.Y 376 | 377 | -- Decompress the data 378 | local decompressedSaveDataImageColours = stringCompressor.Decompress(saveDataImageColours) 379 | local decompressedSaveDataImageAlphas = stringCompressor.Decompress(saveDataImageAlphas) 380 | 381 | -- Get a single pixel colour info form the data 382 | local pixelDataColoursString = string.split(decompressedSaveDataImageColours, "S") 383 | local pixelDataAlphasString = string.split(decompressedSaveDataImageAlphas, "S") 384 | 385 | bfr = buffer.create(saveDataImageResolution.X * saveDataImageResolution.Y * 4) 386 | 387 | for i, pixelColourString in pairs(pixelDataColoursString) do 388 | local RGBValues = string.split(pixelColourString, ",") 389 | local R, G, B = table.unpack(RGBValues) 390 | local A = tonumber(pixelDataAlphasString[i]) 391 | 392 | local index = i * 4 - 4 393 | 394 | buffer.writeu8(bfr, index, R) 395 | buffer.writeu8(bfr, index + 1, G) 396 | buffer.writeu8(bfr, index + 2, B) 397 | buffer.writeu8(bfr, index + 3, A) 398 | end 399 | end 400 | 401 | return result.new(true, Texture.new(width, height, bfr):Unwrap()) 402 | end 403 | 404 | --[=[ 405 | @OSGL/Texture 406 | Creates a new Texture from the data provided. 407 | ]=] 408 | function Texture.new(width: number, height: number, bfr: buffer?): Result 409 | local len = width * height * 4 410 | local buff = bfr or buffer.create(len) 411 | 412 | if buffer.len(buff) ~= len then 413 | return result.new(false, oEnum.DrawableObjectError.BadBufferSize) 414 | end 415 | 416 | local t = base.new() 417 | t.width = width 418 | t.height = height 419 | t.buffer = buff 420 | 421 | return result.new(true, t) 422 | end 423 | 424 | return Texture 425 | -------------------------------------------------------------------------------- /src/DrawableObject/window.luau: -------------------------------------------------------------------------------- 1 | --!optimize 2 2 | --!native 3 | 4 | ------------------------------------------------------------ 5 | -- OSGL v1.6.2 - Open-Source Graphical Library 6 | -- Created/maintained by @saaawdust and contributors 7 | -- 8 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 9 | -- Credit appreciated but not required 10 | -- 11 | -- Do NOT: 12 | -- - Repackage and sell OSGL directly 13 | -- - Claim you created the core library 14 | -- 15 | -- By using OSGL, you agree to these terms. 16 | -- Full license available in the LICENSE file 17 | ------------------------------------------------------------ 18 | 19 | local AssetService = game:GetService("AssetService") 20 | 21 | local drawableObject = script.Parent 22 | local OSGL = drawableObject.Parent 23 | local types = require(OSGL.types) 24 | local result = require(OSGL.result) 25 | local oEnum = require(OSGL.enums) 26 | local windowBase = require(drawableObject.windowBase) 27 | 28 | type Result = result.Result 29 | 30 | local Window = {} 31 | 32 | local function dynamicCreateEditableImage( 33 | Parent: types.Drawable, 34 | width: number, 35 | height: number 36 | ): Result 37 | if Parent and not Window.isDrawableInstance(Parent) then 38 | return result.new(false, oEnum.WindowError.InvalidCreationInstance) 39 | end 40 | 41 | if not width or not height or width < 1 or width > 1024 or height < 1 or height > 1024 then 42 | return result.new(false, oEnum.WindowError.WindowDimensionsOutOfBounds) 43 | end 44 | 45 | local success, EditableImage: EditableImage = pcall(function() 46 | return AssetService:CreateEditableImage({ 47 | Size = Vector2.new(width, height), 48 | }) 49 | end) 50 | 51 | if not success then 52 | return result.new(false, oEnum.DrawableObjectError.NotEnoughMemory) 53 | end 54 | 55 | local success, error = pcall(function() 56 | return EditableImage:WritePixelsBuffer(Vector2.zero, Vector2.one, buffer.create(4)) 57 | end) 58 | 59 | if not success and error == "EditableImage is not accessible. Go to the Security Tab in Game Settings to enable this API." then 60 | return result.new(false, oEnum.WindowError.APINotEnabled) 61 | end 62 | 63 | return result.new(true, EditableImage) 64 | end 65 | 66 | --[=[ 67 | @OSGL/Window 68 | Creates a new Window object given a EditableImage. 69 | ]=] 70 | function Window.new(editableImage: EditableImage, renderers: { types.Drawable }): types.Window 71 | local content = Content.fromObject(editableImage) 72 | for _, renderObject in ipairs(renderers) do 73 | local sourceProperty = Window.getRenderingProperty(renderObject) 74 | if sourceProperty == "" then 75 | continue 76 | end 77 | 78 | renderObject[sourceProperty] = content 79 | end 80 | 81 | local size = editableImage.Size 82 | local width, height = size.X, size.Y 83 | 84 | local t = windowBase.new() 85 | 86 | t.surfaces = renderers 87 | t.editableImage = editableImage 88 | t.buffer = buffer.create(width * height * 4) 89 | t.width = width 90 | t.height = height 91 | t.size = size 92 | t.targetFPS = 60 93 | t.lastRenderTime = os.clock() 94 | 95 | return t 96 | end 97 | 98 | --[=[ 99 | @OSGL/Window 100 | Creates a new Window object given a drawable instance. 101 | ]=] 102 | function Window.from( 103 | drawableObject: types.Drawable, 104 | width: number, 105 | height: number 106 | ): Result 107 | local editableImage = dynamicCreateEditableImage(drawableObject, width, height) 108 | if not editableImage.isOk then 109 | return editableImage 110 | end 111 | 112 | return result.new(true, Window.new(editableImage:Unwrap(), { drawableObject })) 113 | end 114 | 115 | --[=[ 116 | @OSGL/Window 117 | Creates a new Window object given an asset id. 118 | ]=] 119 | function Window.fromAssetId(assetId: number): Result 120 | local texture = Content.fromAssetId(assetId) 121 | 122 | local success, editableImage = pcall(function() 123 | return AssetService:CreateEditableImageAsync(texture) 124 | end) 125 | 126 | if not success then 127 | return result.new(false, oEnum.DrawableObjectError.NotEnoughMemory) 128 | end 129 | 130 | local size = editableImage.Size 131 | local width, height = size.X, size.Y 132 | 133 | local t = windowBase.new() 134 | 135 | t.surfaces = {} 136 | t.editableImage = editableImage 137 | t.buffer = editableImage:ReadPixelsBuffer(Vector2.zero, size) 138 | t.width = width 139 | t.height = height 140 | t.size = size 141 | t.targetFPS = 60 142 | t.lastRenderTime = os.clock() 143 | 144 | return result.new(true, t) 145 | end 146 | 147 | --[=[ 148 | @OSGL/Window 149 | Creates a new Window object given a buffer. 150 | ]=] 151 | function Window.fromBuffer(buffer: buffer, width: number, height: number): Result 152 | local editableImage = dynamicCreateEditableImage(nil, width, height) 153 | if not editableImage.isOk then 154 | return editableImage 155 | end 156 | 157 | -- No other way other than to create Vector2 here 158 | local image = editableImage:Unwrap() 159 | image:WritePixelsBuffer(Vector2.zero, Vector2.new(width, height), buffer) 160 | 161 | local size = image.Size 162 | 163 | local t = windowBase.new() 164 | 165 | t.surfaces = {} 166 | t.editableImage = image 167 | t.buffer = buffer 168 | t.width = width 169 | t.height = height 170 | t.size = size 171 | t.targetFPS = 60 172 | t.lastRenderTime = os.clock() 173 | 174 | return result.new(true, t) 175 | end 176 | 177 | --[=[ 178 | @OSGL/Window 179 | @Debug 180 | Checks if the Instance provided could be drawn onto a Window. 181 | ]=] 182 | function Window.isDrawableInstance(object: Instance): boolean 183 | if typeof(object) ~= "Instance" then 184 | return 185 | end 186 | 187 | return object:IsA("ImageLabel") or 188 | object:IsA("ImageButton") or 189 | object:IsA("Decal") or 190 | object:IsA("Texture") or 191 | object:IsA("MeshPart") 192 | end 193 | 194 | --[=[ 195 | @OSGL/Window 196 | @Debug 197 | Given an Instance, returns the property that should be used to render the EditableImage. 198 | ]=] 199 | function Window.getRenderingProperty(object: types.Drawable): string 200 | if not Window.isDrawableInstance(object) then 201 | return "" 202 | end 203 | 204 | if object:IsA("ImageLabel") or object:IsA("ImageButton") then 205 | return "ImageContent" 206 | end 207 | 208 | return "TextureContent" 209 | end 210 | 211 | return Window 212 | -------------------------------------------------------------------------------- /src/DrawableObject/windowBase.luau: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------ 2 | -- OSGL v1.6.2 - Open-Source Graphical Library 3 | -- Created/maintained by @saaawdust and contributors 4 | -- 5 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 6 | -- Credit appreciated but not required 7 | -- 8 | -- Do NOT: 9 | -- - Repackage and sell OSGL directly 10 | -- - Claim you created the core library 11 | -- 12 | -- By using OSGL, you agree to these terms. 13 | -- Full license available in the LICENSE file 14 | ------------------------------------------------------------ 15 | 16 | local GuiService = game:GetService("GuiService") 17 | local UserInputService = game:GetService("UserInputService") 18 | 19 | local DrawableObject = script.Parent 20 | local base = require(DrawableObject.base) 21 | 22 | local OSGL = DrawableObject.Parent 23 | local types = require(OSGL.types) 24 | local util = require(OSGL.util) 25 | 26 | -- Luau Micro-Optimisations 27 | local VecZero = Vector2.zero 28 | 29 | local WindowBaseImpl = {} 30 | 31 | --[=[ 32 | @OSGL/Window 33 | Renders a Window's buffer to the EditableImage. 34 | ]=] 35 | function WindowBaseImpl.Render(self: types.Window) 36 | self.editableImage:WritePixelsBuffer(VecZero, self.size, self.buffer) 37 | end 38 | 39 | --[=[ 40 | @OSGL/Window 41 | Renders a Window's buffer to the EditableImage at the Target FPS provided. 42 | ]=] 43 | function WindowBaseImpl.RenderTargetFPS(self: types.Window) 44 | self.editableImage:WritePixelsBuffer(VecZero, self.size, self.buffer) 45 | 46 | local now = os.clock() 47 | local fps = 1 / (now - self.lastRenderTime) 48 | if fps > self.targetFPS then 49 | task.wait(1 / self.targetFPS - 1 / fps) 50 | end 51 | 52 | self.lastRenderTime = os.clock() 53 | end 54 | 55 | --[=[ 56 | @OSGL/Window 57 | Adds an additional list of objects to render the Window to. 58 | ]=] 59 | function WindowBaseImpl.AddRenderers(self: types.Window, ...: types.Drawable) 60 | local content = Content.fromObject(self.editableImage) 61 | 62 | for _, v in {...} do 63 | if not table.find(self.surfaces, v) then 64 | local prop = util.getRendererProp(v) 65 | if prop == "" then 66 | return 67 | end 68 | 69 | v[prop] = content 70 | 71 | table.insert(self.surfaces, v) 72 | end 73 | end 74 | end 75 | 76 | --[=[ 77 | @OSGL/Window 78 | Removes a list of objects that the Window is currently rendering to 79 | ]=] 80 | function WindowBaseImpl.RemoveRenderers(self: types.Window, ...: types.Drawable) 81 | for _, v in {...} do 82 | local indexFound = table.find(self.surfaces, v) 83 | if indexFound then 84 | local prop = util.getRendererProp(v) 85 | if prop == "" then 86 | return 87 | end 88 | 89 | v[prop] = Content.none 90 | 91 | table.remove(self.surfaces, indexFound) 92 | end 93 | end 94 | end 95 | 96 | --[=[ 97 | @OSGL/Window 98 | Returns whether the mouse is hovering over the Window, and if so what its X and Y positions are relative to the top-left corner. 99 | ]=] 100 | function WindowBaseImpl.GetRelativeMousePosition(self: types.Window, image: types.ImageBase): (boolean, number, number) 101 | local width, height = self.width, self.height 102 | local sizeFactor = self.size / image.AbsoluteSize 103 | local inset, _ = GuiService:GetGuiInset() 104 | local mousePosition = UserInputService:GetMouseLocation() - image.AbsolutePosition - inset 105 | 106 | local relativeMousePosition = mousePosition * sizeFactor 107 | 108 | local x = math.round(relativeMousePosition.X) 109 | local y = math.round(relativeMousePosition.Y) 110 | 111 | local isInImage = x >= 0 and y >= 0 and x < width and y < height 112 | 113 | return isInImage, isInImage and x or 0, isInImage and y or 0 114 | end 115 | 116 | --[=[ 117 | @OSGL/Window 118 | Destroys all information associated with the Window. 119 | ]=] 120 | function WindowBaseImpl.Destroy(self: types.Window) 121 | self.editableImage:Destroy() 122 | 123 | for _, v in self.surfaces do 124 | v[util.getRendererProp(v)] = Content.none 125 | end 126 | 127 | table.clear(self) 128 | end 129 | 130 | export type WindowBase = base.Base & typeof(WindowBaseImpl) 131 | 132 | return { 133 | new = function(): WindowBase 134 | local t = base.new() 135 | 136 | t.Render = WindowBaseImpl.Render 137 | t.AddRenderers = WindowBaseImpl.AddRenderers 138 | t.RemoveRenderers = WindowBaseImpl.RemoveRenderers 139 | t.GetRelativeMousePosition = WindowBaseImpl.GetRelativeMousePosition 140 | t.RenderTargetFPS = WindowBaseImpl.RenderTargetFPS 141 | t.Destroy = WindowBaseImpl.Destroy 142 | 143 | return t 144 | end, 145 | } 146 | -------------------------------------------------------------------------------- /src/bitmap.luau: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------ 2 | -- OSGL v1.6.2 - Open-Source Graphical Library 3 | -- Created/maintained by @saaawdust and contributors 4 | -- 5 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 6 | -- Credit appreciated but not required 7 | -- 8 | -- Do NOT: 9 | -- - Repackage and sell OSGL directly 10 | -- - Claim you created the core library 11 | -- 12 | -- By using OSGL, you agree to these terms. 13 | -- Full license available in the LICENSE file 14 | ------------------------------------------------------------ 15 | 16 | local OSGL = script.Parent 17 | local types = require(OSGL.types) 18 | local result = require(OSGL.result) 19 | local oEnum = require(OSGL.enums) 20 | local util = require(OSGL.util) 21 | 22 | type Result = result.Result 23 | 24 | local function checkUnloadedBitmap(unloadedBitmap: types.RawTexture): Result 25 | if typeof(unloadedBitmap) == "Instance" and unloadedBitmap:IsA("ModuleScript") then 26 | unloadedBitmap = require(unloadedBitmap) 27 | end 28 | 29 | if type(unloadedBitmap) ~= "table" or not table.find(util.VALID_TEXTURE_VERSIONS, unloadedBitmap.version) then 30 | return result.new(false, oEnum.TextureError.BadInput) 31 | end 32 | 33 | return result.new(true, unloadedBitmap) 34 | end 35 | 36 | local Bitmap = {} 37 | 38 | --[=[ 39 | @OSGL/Bitmap 40 | Reads from the Bitmap. 41 | ]=] 42 | function Read(self: types.Bitmap, x: number, y: number, channel: number?): number 43 | channel = channel or 1 44 | return buffer.readu8(self.buffer, (y * self.width + x) * self.channels + (channel - 1)) 45 | end 46 | 47 | 48 | --[=[ 49 | @OSGL/Bitmap 50 | Writes to the Bitmap. 51 | ]=] 52 | function Write(self: types.Bitmap, x: number, y: number, channel: number, value: number) 53 | return buffer.writeu8(self.buffer, (y * self.width + x) * self.channels + (channel - 1), value) 54 | end 55 | 56 | --[=[ 57 | @OSGL/Bitmap 58 | @Checked 59 | Creates a new Bitmap from a raw one. 60 | ]=] 61 | function Bitmap.fromChecked(rawBitmap: types.RawTexture): Result 62 | local raw = checkUnloadedBitmap(rawBitmap) 63 | 64 | if not raw.isOk then 65 | return raw 66 | end 67 | 68 | return result.new(true, Bitmap.fromUnchecked(raw:Unwrap())) 69 | end 70 | 71 | --[=[ 72 | @OSGL/Bitmap 73 | @Unchecked! 74 | Creates a new Bitmap from an unloaded one. 75 | ]=] 76 | function Bitmap.fromUnchecked(rawBitmap: types.BaseUnloadedTexture): types.Bitmap 77 | local width, height, bfr = rawBitmap.width, rawBitmap.height, rawBitmap.pixels 78 | local channels = buffer.len(bfr) / (width * height) 79 | 80 | return { 81 | width = width, 82 | height = height, 83 | buffer = bfr, 84 | channels = channels, 85 | 86 | Read = Read, 87 | Write = Write, 88 | } 89 | end 90 | 91 | return Bitmap 92 | -------------------------------------------------------------------------------- /src/color.luau: -------------------------------------------------------------------------------- 1 | --!optimize 2 2 | 3 | ------------------------------------------------------------ 4 | -- OSGL v1.6.2 - Open-Source Graphical Library 5 | -- Created/maintained by @saaawdust and contributors 6 | -- 7 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 8 | -- Credit appreciated but not required 9 | -- 10 | -- Do NOT: 11 | -- - Repackage and sell OSGL directly 12 | -- - Claim you created the core library 13 | -- 14 | -- By using OSGL, you agree to these terms. 15 | -- Full license available in the LICENSE file 16 | ------------------------------------------------------------ 17 | 18 | local OSGL = script.Parent 19 | local types = require(OSGL.types) 20 | 21 | local bit32bor = bit32.bor 22 | local bit32lshift = bit32.lshift 23 | local bit32rshift = bit32.rshift 24 | local bit32band = bit32.band 25 | 26 | --[=[ 27 | @class Color 28 | 29 | The color class. Handles manipulations of color values. 30 | ]=] 31 | local color = {} 32 | 33 | --[=[ 34 | Creates a new color value. Values cannot be changed manually and must be changed 35 | via a function. 36 | 37 | ```lua 38 | local OSGL = require(path.to.osgl) 39 | local color = OSGL.color 40 | 41 | -- All other parameters are defaulted 42 | -- to 0, except for alpha, which is 43 | -- defaulted to 255. 44 | local myAwesomeRedColor = color.new(255) 45 | ``` 46 | 47 | @within Color 48 | @return Color -- Returns a color Value 49 | ]=] 50 | --[=[ 51 | @OSGL/Color 52 | Creates a new RGBA Color. 53 | ]=] 54 | function color.new(r: number?, g: number?, b: number?, a: number?): types.Color 55 | r = r or 0 56 | g = g or 0 57 | b = b or 0 58 | a = a or 255 59 | 60 | return bit32bor( 61 | bit32lshift(a, 24), 62 | bit32lshift(b, 16), 63 | bit32lshift(g, 8), 64 | r 65 | ) 66 | end 67 | 68 | --[=[ 69 | Creates a new color value with the alpha channel being 255. Values cannot be changed 70 | manually and must be changed via a function. 71 | 72 | ```lua 73 | local OSGL = require(path.to.osgl) 74 | local color = OSGL.color 75 | 76 | -- All other parameters are defaulted 77 | -- to 0. 78 | local myAwesomeRedColor = color.newRGB(255) 79 | ``` 80 | 81 | @within Color 82 | @return Color -- Returns a color Value 83 | ]=] 84 | --[=[ 85 | @OSGL/Color 86 | Creates a new RGBA Color from an RGB one. 87 | ]=] 88 | function color.newRGB(r: number?, g: number?, b: number?): types.Color 89 | r = r or 0 90 | g = g or 0 91 | b = b or 0 92 | 93 | return bit32bor( 94 | color.BLACK, 95 | bit32lshift(b, 16), 96 | bit32lshift(g, 8), 97 | r 98 | ) 99 | end 100 | 101 | --[=[ 102 | Sets the 'r' channel of the color. 103 | 104 | ```lua 105 | local OSGL = require(path.to.osgl) 106 | local color = OSGL.color 107 | 108 | local c = color.setR(color.RED, 100) 109 | print(color.r(c)) -- Ouput: 100 110 | ``` 111 | 112 | For performance boost, if you're creating colors using only 1 or 2 channels, you 113 | should do: 114 | 115 | ```lua 116 | local almostRed = color.setR(color.BLACK, 100) 117 | ``` 118 | 119 | @param c Color -- The color to edit 120 | @param r number -- The new value 121 | @within Color 122 | @return number 123 | ]=] 124 | --[=[ 125 | @OSGL/Color 126 | Sets the R channel of a Color. 127 | ]=] 128 | function color.setR(c: types.Color, r: number): number 129 | return bit32bor(c, r) 130 | end 131 | 132 | --[=[ 133 | Sets the 'g' channel of the color. 134 | 135 | ```lua 136 | local OSGL = require(path.to.osgl) 137 | local color = OSGL.color 138 | 139 | local c = color.setG(color.GREEN, 100) 140 | print(color.g(c)) -- Ouput: 100 141 | ``` 142 | 143 | For performance boost, if you're creating colors using only 1 or 2 channels, you 144 | should do: 145 | 146 | ```lua 147 | local almostGreen = color.setG(color.BLACK, 100) 148 | ``` 149 | 150 | @param c Color -- The color to edit 151 | @param g number -- The new value 152 | @within Color 153 | @return number 154 | ]=] 155 | --[=[ 156 | @OSGL/Color 157 | Sets the G channel of a Color. 158 | ]=] 159 | function color.setG(c: types.Color, g: number): number 160 | return bit32bor(c, bit32bor(bit32band(c, 0xFF00FFFF), bit32lshift(g, 8))) 161 | end 162 | 163 | --[=[ 164 | Sets the 'b' channel of the color. 165 | 166 | ```lua 167 | local OSGL = require(path.to.osgl) 168 | local color = OSGL.color 169 | 170 | local c = color.setB(color.BLUE, 100) 171 | print(color.b(c)) -- Ouput: 100 172 | ``` 173 | 174 | For performance boost, if you're creating colors using only 1 or 2 channels, you 175 | should do: 176 | 177 | ```lua 178 | local almostBlue = color.setB(color.BLACK, 100) 179 | ``` 180 | 181 | @param c Color -- The color to edit 182 | @param b number -- The new value 183 | @within Color 184 | @return number 185 | ]=] 186 | --[=[ 187 | @OSGL/Color 188 | Sets the B channel of a Color. 189 | ]=] 190 | function color.setB(c: types.Color, b: number): number 191 | return bit32bor(bit32band(c, 0xFFFF00FF), bit32lshift(b, 16)) 192 | end 193 | 194 | --[=[ 195 | Sets the 'a' channel of the color. 196 | 197 | ```lua 198 | local OSGL = require(path.to.osgl) 199 | local color = OSGL.color 200 | 201 | local c = color.setA(color.TRANSPARENT, 100) 202 | print(color.a(c)) -- Ouput: 100 203 | ``` 204 | 205 | For performance boost, if you're creating colors using only 1 or 2 channels, you 206 | should do: 207 | 208 | ```lua 209 | local almostBlack = color.setA(color.TRANSPARENT, 100) 210 | ``` 211 | 212 | @param c Color -- The color to edit 213 | @param a number -- The new value 214 | @within Color 215 | @return number 216 | ]=] 217 | --[=[ 218 | @OSGL/Color 219 | Sets the A channel of a Color. 220 | ]=] 221 | function color.setA(c: types.Color, a: number): number 222 | return bit32bor(bit32band(c, 0x00FFFFFF), bit32lshift(a, 24)) 223 | end 224 | 225 | --[=[ 226 | Reads the value of the color 227 | 228 | ```lua 229 | local OSGL = require(path.to.osgl) 230 | local color = OSGL.color 231 | 232 | -- All other parameters are defaulted to 0, except for alpha, 233 | -- which is defaulted to 255. 234 | local myAwesomeRedColor = color.new(255) 235 | print(color.read(myAwesomeRedColor)) -- Output: { 255, 0, 0, 255 } 236 | ``` 237 | 238 | @param rgbaColor Color -- The color to read 239 | @within Color 240 | @return number 241 | ]=] 242 | --[=[ 243 | @OSGL/Color 244 | Returns all 4 channels of a Color. 245 | ]=] 246 | function color.read(rgbaColor: types.Color): (number, number, number, number) 247 | return 248 | color.r(rgbaColor), 249 | color.g(rgbaColor), 250 | color.b(rgbaColor), 251 | color.a(rgbaColor) 252 | end 253 | 254 | --[=[ 255 | Reads the 'r' value of the color 256 | 257 | ```lua 258 | local OSGL = require(path.to.osgl) 259 | local color = OSGL.color 260 | 261 | -- All other parameters are defaulted to 0, except for alpha, 262 | -- which is defaulted to 255. 263 | local myAwesomeRedColor = color.new(255) 264 | print color.r(myAwesomeRedColor)) -- Ouput: 255 265 | ``` 266 | 267 | @param color Color -- The color to read 268 | @within Color 269 | @return number 270 | ]=] 271 | --[=[ 272 | @OSGL/Color 273 | Returns the R channel of a Color. 274 | ]=] 275 | function color.r(color: types.Color): number 276 | return bit32band(color, 0xFF) 277 | end 278 | 279 | --[=[ 280 | Reads the 'g' value of the color 281 | 282 | ```lua 283 | local OSGL = require(path.to.osgl) 284 | local color = OSGL.color 285 | 286 | -- All other parameters are defaulted to 0, except for alpha, 287 | -- which is defaulted to 255. 288 | local myAwesomeRedColor = color.new(255) 289 | print color.g(myAwesomeRedColor)) -- Ouput: 0 290 | ``` 291 | 292 | @param color Color -- The color to read 293 | @within Color 294 | @return number 295 | ]=] 296 | --[=[ 297 | @OSGL/Color 298 | Returns the G channel of a Color. 299 | ]=] 300 | function color.g(color: types.Color): number 301 | return bit32band(bit32rshift(color, 8), 0xFF) 302 | end 303 | 304 | --[=[ 305 | Reads the 'b' value of the color 306 | 307 | ```lua 308 | local OSGL = require(path.to.osgl) 309 | local color = OSGL.color 310 | 311 | -- All other parameters are defaulted to 0, except for alpha, 312 | -- which is defaulted to 255. 313 | local myAwesomeRedColor = color.new(255) 314 | print color.b(myAwesomeRedColor)) -- Ouput: 0 315 | ``` 316 | 317 | @param color Color -- The color to read 318 | @within Color 319 | @return number 320 | ]=] 321 | --[=[ 322 | @OSGL/Color 323 | Returns the B channel of a Color. 324 | ]=] 325 | function color.b(color: types.Color): number 326 | return bit32band(bit32rshift(color, 16), 0xFF) 327 | end 328 | 329 | --[=[ 330 | Reads the 'a' value of the color 331 | 332 | ```lua 333 | local OSGL = require(path.to.osgl) 334 | local color = OSGL.color 335 | 336 | -- All other parameters are defaulted to 0, except for alpha, 337 | -- which is defaulted to 255. 338 | local myAwesomeRedColor = color.new(255) 339 | print(color.a(myAwesomeRedColor)) -- Ouput: 255 340 | ``` 341 | 342 | @param color Color -- The color to read 343 | @within Color 344 | @return number 345 | ]=] 346 | --[=[ 347 | @OSGL/Color 348 | Returns the A channel of a Color. 349 | ]=] 350 | function color.a(color: types.Color): number 351 | return bit32rshift(color, 24) 352 | end 353 | 354 | --[=[ 355 | @OSGL/Color 356 | Creates a Color from a Color3. 357 | ]=] 358 | function color.fromColor3(color3: Color3, a: number?): number 359 | return color.new(color3.R * 255, color3.G * 255, color3.B * 255, a or 255) 360 | end 361 | 362 | --[=[ 363 | @OSGL/Color 364 | Creates a Color from a Vector3. 365 | ]=] 366 | function color.fromVector3(vector3: Vector3, a: number?): number 367 | return color.new(vector3.X * 255, vector3.Y * 255, vector3.Z * 255, a or 255) 368 | end 369 | 370 | --[=[ 371 | Tints the color towards the other color using a tint factor. A tint factor of 0 372 | means `color1` will be returned, while a tint factor of 1 means `color2` will be 373 | returned. 374 | 375 | ```lua 376 | local OSGL = require(path.to.osgl) 377 | local color = OSGL.color 378 | 379 | local whiteTintedRed = color.tint(color.WHITE, color.RED, 0.5) 380 | ``` 381 | 382 | @param color1 Color -- The color to tint 383 | @param color2 Color -- The tint that will be applied 384 | @param factor number -- How strong the tint is 385 | @within Color 386 | @return number 387 | ]=] 388 | --[=[ 389 | @OSGL/Color 390 | Tints a Color. 391 | ]=] 392 | function color.tint(color1: types.Color, color2: types.Color, factor: number) 393 | if factor == 0 then 394 | return color1 395 | elseif factor == 1 then 396 | return color2 397 | else 398 | return color.new( 399 | math.lerp(color.r(color1), color.r(color2), factor), 400 | math.lerp(color.g(color1), color.g(color2), factor), 401 | math.lerp(color.b(color1), color.b(color2), factor), 402 | math.lerp(color.a(color1), color.a(color2), factor) 403 | ) 404 | end 405 | end 406 | 407 | --- @prop RED Color 408 | --- @within Color 409 | --- A red color 410 | color.RED = color.new(255) 411 | 412 | --- @prop GREEN Color 413 | --- @within Color 414 | --- A green color 415 | color.GREEN = color.new(0, 255) 416 | 417 | --- @prop BLUE Color 418 | --- @within Color 419 | --- A blue color 420 | color.BLUE = color.new(0, 0, 255) 421 | 422 | --- @prop WHITE Color 423 | --- @within Color 424 | --- A white color 425 | color.WHITE = color.new(255, 255, 255) 426 | 427 | --- @prop BLACK Color 428 | --- @within Color 429 | --- A black color 430 | color.BLACK = color.new(0, 0, 0, 255) 431 | 432 | --- @prop YELLOW Color 433 | --- @within Color 434 | --- A yellow color 435 | color.YELLOW = color.new(255, 255) 436 | 437 | --- @prop MAGENTA Color 438 | --- @within Color 439 | --- A magenta color 440 | color.MAGENTA = color.new(255, 0, 255) 441 | 442 | --- @prop CYAN Color 443 | --- @within Color 444 | --- A cyan color 445 | color.CYAN = color.new(0, 255, 255) 446 | 447 | --- @prop TRANSPARENT Color 448 | --- @within Color 449 | --- A completely transparent color 450 | color.TRANSPARENT = color.new(0, 0, 0, 0) 451 | 452 | return color 453 | -------------------------------------------------------------------------------- /src/draw/buffer.luau: -------------------------------------------------------------------------------- 1 | --!optimize 2 2 | --!native 3 | 4 | ------------------------------------------------------------ 5 | -- OSGL v1.6.2 - Open-Source Graphical Library 6 | -- Created/maintained by @saaawdust and contributors 7 | -- 8 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 9 | -- Credit appreciated but not required 10 | -- 11 | -- Do NOT: 12 | -- - Repackage and sell OSGL directly 13 | -- - Claim you created the core library 14 | -- 15 | -- By using OSGL, you agree to these terms. 16 | -- Full license available in the LICENSE file 17 | ------------------------------------------------------------ 18 | 19 | local OSGL = script.Parent.Parent 20 | local types = require(OSGL.types) 21 | 22 | return function(object: types.DrawableObject, bfr: buffer, width: number, height: number, x: number, y: number) 23 | local drawWidth = math.min(width, object.width - x) 24 | local drawHeight = math.min(height, object.height - y) 25 | 26 | local targetBuffer = object.buffer 27 | local sourceBuffer = bfr 28 | local targetSize = width * 4 29 | 30 | local targetOffset = (y * object.width + x) * 4 31 | local sourceOffset = 0 32 | 33 | for y = 0, drawHeight - 1 do 34 | buffer.copy( 35 | targetBuffer, 36 | targetOffset + y * targetSize, 37 | sourceBuffer, 38 | sourceOffset + y * targetSize, 39 | drawWidth * 4 40 | ) 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /src/draw/chunk.luau: -------------------------------------------------------------------------------- 1 | --!optimize 2 2 | 3 | ------------------------------------------------------------ 4 | -- OSGL v1.6.2 - Open-Source Graphical Library 5 | -- Created/maintained by @saaawdust and contributors 6 | -- 7 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 8 | -- Credit appreciated but not required 9 | -- 10 | -- Do NOT: 11 | -- - Repackage and sell OSGL directly 12 | -- - Claim you created the core library 13 | -- 14 | -- By using OSGL, you agree to these terms. 15 | -- Full license available in the LICENSE file 16 | ------------------------------------------------------------ 17 | 18 | local OSGL = script.Parent.Parent 19 | local types = require(OSGL.types) 20 | 21 | return function( 22 | self: types.DrawableObject, 23 | color: types.Color, 24 | x1: number, 25 | y1: number, 26 | x2: number, 27 | y2: number 28 | ) 29 | local sizeX = self.width 30 | local startIndex = (y1 * sizeX + x1) * 4 31 | local endIndex = (y2 * sizeX + x2) * 4 32 | 33 | local bufferLength = endIndex - startIndex 34 | local currentSize = 4 35 | local numCopies = math.ceil(math.log(bufferLength / 4, 2)) 36 | 37 | buffer.writeu32(self.buffer, startIndex, color) 38 | 39 | for _ = 1, numCopies do 40 | local copySize = math.min(currentSize, bufferLength - currentSize) 41 | 42 | buffer.copy( 43 | self.buffer, 44 | startIndex + currentSize, 45 | self.buffer, 46 | startIndex, 47 | copySize 48 | ) 49 | 50 | currentSize += copySize 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /src/draw/circle.luau: -------------------------------------------------------------------------------- 1 | --!optimize 2 2 | --!native 3 | 4 | ------------------------------------------------------------ 5 | -- OSGL v1.6.2 - Open-Source Graphical Library 6 | -- Created/maintained by @saaawdust and contributors 7 | -- 8 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 9 | -- Credit appreciated but not required 10 | -- 11 | -- Do NOT: 12 | -- - Repackage and sell OSGL directly 13 | -- - Claim you created the core library 14 | -- 15 | -- By using OSGL, you agree to these terms. 16 | -- Full license available in the LICENSE file 17 | ------------------------------------------------------------ 18 | 19 | local OSGL = script.Parent.Parent 20 | local draw = OSGL.draw 21 | local types = require(OSGL.types) 22 | local color = require(OSGL.color) 23 | local chunk = require(draw.chunk) 24 | 25 | return function( 26 | object: types.DrawableObject, 27 | centerX: number, centerY: number, radius: number, 28 | fill: types.Color?, stroke: types.Color?, strokeThickness: number? 29 | ) 30 | fill = fill or color.BLACK 31 | stroke = stroke or color.TRANSPARENT 32 | strokeThickness = strokeThickness or 0 33 | 34 | local sizeX, sizeY = object.width, object.height 35 | 36 | -- Only apply stroke if it's not transparent 37 | strokeThickness = (stroke ~= color.TRANSPARENT) and strokeThickness or 0 38 | 39 | local radiusSquared = radius * radius 40 | local totalRadius = radius + strokeThickness 41 | local strokeSquared = totalRadius * totalRadius 42 | 43 | for dy = -totalRadius, totalRadius do 44 | local y = centerY + dy 45 | if y < 0 or y >= sizeY then 46 | continue 47 | end 48 | 49 | local dySquared = dy * dy 50 | local innerX = math.floor(math.sqrt(math.max(radiusSquared - dySquared, 0))) 51 | local outerX = math.floor(math.sqrt(math.max(strokeSquared - dySquared, 0))) 52 | 53 | local startX = math.clamp(centerX - innerX, 0, sizeX - 1) 54 | local endX = math.clamp(centerX + innerX, 0, sizeX - 1) 55 | if endX >= startX then 56 | chunk(object, fill, startX, y, endX, y) 57 | end 58 | 59 | if strokeThickness > 0 and outerX > innerX then 60 | local leftStart = math.clamp(centerX - outerX, 0, sizeX - 1) 61 | local leftEnd = math.clamp(centerX - innerX - 1, 0, sizeX - 1) 62 | if leftEnd >= leftStart then 63 | chunk(object, stroke, leftStart, y, leftEnd,y) 64 | end 65 | 66 | local rightStart = math.clamp(centerX + innerX + 1, 0, sizeX - 1) 67 | local rightEnd = math.clamp(centerX + outerX, 0, sizeX - 1) 68 | if rightEnd >= rightStart then 69 | chunk(object, stroke, rightStart, y, rightEnd, y) 70 | end 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /src/draw/clear.luau: -------------------------------------------------------------------------------- 1 | --!optimize 2 2 | --!native 3 | 4 | ------------------------------------------------------------ 5 | -- OSGL v1.6.2 - Open-Source Graphical Library 6 | -- Created/maintained by @saaawdust and contributors 7 | -- 8 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 9 | -- Credit appreciated but not required 10 | -- 11 | -- Do NOT: 12 | -- - Repackage and sell OSGL directly 13 | -- - Claim you created the core library 14 | -- 15 | -- By using OSGL, you agree to these terms. 16 | -- Full license available in the LICENSE file 17 | ------------------------------------------------------------ 18 | 19 | local Draw = script.Parent 20 | local OSGL = Draw.Parent 21 | local types = require(OSGL.types) 22 | local color = require(OSGL.color) 23 | 24 | return function(self: types.DrawableObject, Color: types.Color) 25 | local bufferLength = buffer.len(self.buffer) 26 | local current_size = 4 27 | local numCopies = math.ceil(math.log(bufferLength / 4, 2)) 28 | 29 | buffer.writeu32(self.buffer, 0, Color or color.TRANSPARENT) 30 | 31 | for _ = 1, numCopies do 32 | local copy_size = math.min(current_size, bufferLength - current_size) 33 | buffer.copy(self.buffer, current_size, self.buffer, 0, copy_size) 34 | 35 | current_size += copy_size 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /src/draw/floodFill.luau: -------------------------------------------------------------------------------- 1 | --!optimize 2 2 | --!native 3 | 4 | ------------------------------------------------------------ 5 | -- OSGL v1.6.2 - Open-Source-Graphical-Library 6 | -- Copyright © 2023-2025 OSGL (@saaawdust) 7 | -- 8 | -- This software is provided ‘as-is’, without any express or implied warranty. 9 | -- In no event will the authors be held liable for any damages arising from the use of this software. 10 | -- 11 | -- Permission is granted to anyone to use this software for any purpose, 12 | -- including commercial applications, and to alter it and redistribute it freely, 13 | -- subject to the following restrictions: 14 | -- 15 | -- 1. The origin of this software must not be misrepresented; 16 | -- you must not claim that you wrote the original software. 17 | -- If you use this software in a product, an acknowledgment 18 | -- in the product documentation would be appreciated but is not required. 19 | -- 20 | -- 2. Altered source versions must be plainly marked as such, 21 | -- and must not be misrepresented as being the original software. 22 | -- 23 | -- 3. This notice may not be removed or altered from any source distribution. 24 | -- 25 | ------------------------------------------------------------ 26 | 27 | local OSGL = script.Parent.Parent 28 | local types = require(OSGL.types) 29 | 30 | function getIndex(x, y, sizeX) 31 | return (y * sizeX + x) * 4 32 | end 33 | 34 | function offsetIndex(i, x, y, sizeX) 35 | return i + (y * sizeX + x) * 4 36 | end 37 | 38 | return function(object: types.DrawableObject, x: number, y: number, color: types.Color) 39 | x = x or 0 40 | y = y or 0 41 | 42 | local sizeX, sizeY = object.width, object.height 43 | local originalColor = object:ReadPixelUnchecked(x, y) 44 | 45 | -- Creates more than enough memory, better optimization. 46 | local checked = table.create(sizeX * sizeY, false) 47 | local stack = table.create(sizeX * sizeY, -1) 48 | stack[1] = getIndex(x, y, sizeX) 49 | 50 | local MAX_INDEX = getIndex(sizeX - 1, sizeY - 1, sizeX) 51 | local bfr = object.buffer 52 | while #stack > 0 do 53 | local currentIndex = stack[#stack] 54 | stack[#stack] = nil 55 | 56 | if checked[currentIndex] or currentIndex < 0 or currentIndex > MAX_INDEX then 57 | continue 58 | end 59 | checked[currentIndex] = true 60 | 61 | if buffer.readu32(bfr, currentIndex) ~= originalColor then 62 | continue 63 | end 64 | 65 | buffer.writeu32(bfr, currentIndex, color) 66 | 67 | stack[#stack + 1] = offsetIndex(currentIndex, 1, 0, sizeX) 68 | stack[#stack + 1] = offsetIndex(currentIndex, -1, 0, sizeX) 69 | stack[#stack + 1] = offsetIndex(currentIndex, 0, 1, sizeX) 70 | stack[#stack + 1] = offsetIndex(currentIndex, 0, -1, sizeX) 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /src/draw/init.luau: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------ 2 | -- OSGL v1.6.2 - Open-Source Graphical Library 3 | -- Created/maintained by @saaawdust and contributors 4 | -- 5 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 6 | -- Credit appreciated but not required 7 | -- 8 | -- Do NOT: 9 | -- - Repackage and sell OSGL directly 10 | -- - Claim you created the core library 11 | -- 12 | -- By using OSGL, you agree to these terms. 13 | -- Full license available in the LICENSE file 14 | ------------------------------------------------------------ 15 | 16 | local Pixel = require(script.pixel) 17 | local PixelIndex = require(script.pixelIndex) 18 | local Line = require(script.line) 19 | local Rect = require(script.rect) 20 | local Polygon = require(script.polygon) 21 | local Triangle = require(script.triangle) 22 | local Circle = require(script.circle) 23 | local Buffer = require(script.buffer) 24 | local FloodFill = require(script.floodFill) 25 | 26 | local Clear = require(script.clear) 27 | local Chunk = require(script.chunk) 28 | 29 | return { 30 | Pixel = Pixel, 31 | PixelIndex = PixelIndex, 32 | Line = Line, 33 | Rectangle = Rect, 34 | Polygon = Polygon, 35 | Triangle = Triangle, 36 | Circle = Circle, 37 | Buffer = Buffer, 38 | FloodFill = FloodFill, 39 | 40 | Clear = Clear, 41 | Chunk = Chunk, 42 | } 43 | -------------------------------------------------------------------------------- /src/draw/line.luau: -------------------------------------------------------------------------------- 1 | --!optimize 2 2 | --!native 3 | 4 | ------------------------------------------------------------ 5 | -- OSGL v1.6.2 - Open-Source Graphical Library 6 | -- Created/maintained by @saaawdust and contributors 7 | -- 8 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 9 | -- Credit appreciated but not required 10 | -- 11 | -- Do NOT: 12 | -- - Repackage and sell OSGL directly 13 | -- - Claim you created the core library 14 | -- 15 | -- By using OSGL, you agree to these terms. 16 | -- Full license available in the LICENSE file 17 | ------------------------------------------------------------ 18 | 19 | local Draw = script.Parent 20 | local OSGL = Draw.Parent 21 | local types = require(OSGL.types) 22 | local pixel = require(Draw.pixel) 23 | local color = require(OSGL.color) 24 | 25 | return function(object: types.DrawableObject, startX: number, startY: number, stopX: number, stopY: number, thickness: number, c: types.Color) 26 | thickness = thickness or 1 27 | c = c or color.BLACK 28 | 29 | if thickness == 0 then 30 | return 31 | end 32 | 33 | local width, height = object.width, object.height 34 | 35 | local dx = stopX - startX 36 | local dy = stopY - startY 37 | 38 | local steps = math.max(math.abs(dx), math.abs(dy)) 39 | 40 | local xIncrement = dx / steps 41 | local yIncrement = dy / steps 42 | 43 | local x = startX 44 | local y = startY 45 | 46 | local function drawThickPoint(px: number, py: number) 47 | local halfThickness = thickness / 2 48 | for offsetX = -halfThickness, halfThickness do 49 | for offsetY = -halfThickness, halfThickness do 50 | local pixelX = math.floor(px + offsetX + .5) 51 | local pixelY = math.floor(py + offsetY + .5) 52 | if pixelX >= 0 and pixelX < width and pixelY >= 0 and pixelY < height then 53 | pixel(object, pixelX, pixelY, c) 54 | end 55 | end 56 | end 57 | end 58 | 59 | for _ = 0, steps do 60 | drawThickPoint(x, y) 61 | x += xIncrement 62 | y += yIncrement 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /src/draw/pixel.luau: -------------------------------------------------------------------------------- 1 | --!optimize 2 2 | --!native 3 | 4 | ------------------------------------------------------------ 5 | -- OSGL v1.6.2 - Open-Source Graphical Library 6 | -- Created/maintained by @saaawdust and contributors 7 | -- 8 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 9 | -- Credit appreciated but not required 10 | -- 11 | -- Do NOT: 12 | -- - Repackage and sell OSGL directly 13 | -- - Claim you created the core library 14 | -- 15 | -- By using OSGL, you agree to these terms. 16 | -- Full license available in the LICENSE file 17 | ------------------------------------------------------------ 18 | 19 | local Draw = script.Parent 20 | local OSGL = Draw.Parent 21 | local types = require(OSGL.types) 22 | 23 | return function(self: types.DrawableObject, X: number, Y: number, Color: types.Color) 24 | buffer.writeu32(self.buffer, (Y * self.width + X) * 4, Color) 25 | end 26 | -------------------------------------------------------------------------------- /src/draw/pixelIndex.luau: -------------------------------------------------------------------------------- 1 | --!optimize 2 2 | --!native 3 | 4 | ------------------------------------------------------------ 5 | -- OSGL v1.6.2 - Open-Source Graphical Library 6 | -- Created/maintained by @saaawdust and contributors 7 | -- 8 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 9 | -- Credit appreciated but not required 10 | -- 11 | -- Do NOT: 12 | -- - Repackage and sell OSGL directly 13 | -- - Claim you created the core library 14 | -- 15 | -- By using OSGL, you agree to these terms. 16 | -- Full license available in the LICENSE file 17 | ------------------------------------------------------------ 18 | 19 | local Draw = script.Parent 20 | local OSGL = Draw.Parent 21 | local types = require(OSGL.types) 22 | 23 | return function(self: types.DrawableObject, Index: number, Color: types.Color) 24 | buffer.writeu32(self.buffer, Index, Color) 25 | end 26 | -------------------------------------------------------------------------------- /src/draw/polygon.luau: -------------------------------------------------------------------------------- 1 | --!optimize 2 2 | --!native 3 | 4 | ------------------------------------------------------------ 5 | -- OSGL v1.6.2 - Open-Source Graphical Library 6 | -- Created/maintained by @saaawdust and contributors 7 | -- 8 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 9 | -- Credit appreciated but not required 10 | -- 11 | -- Do NOT: 12 | -- - Repackage and sell OSGL directly 13 | -- - Claim you created the core library 14 | -- 15 | -- By using OSGL, you agree to these terms. 16 | -- Full license available in the LICENSE file 17 | ------------------------------------------------------------ 18 | 19 | local OSGL = script.Parent.Parent 20 | local draw = OSGL.draw 21 | local types = require(OSGL.types) 22 | local color = require(OSGL.color) 23 | local line = require(draw.line) 24 | local chunk = require(draw.chunk) 25 | 26 | return function(object: types.DrawableObject, corners: { { number } }, fill: types.Color, strokeColor: types.Color?, strokeWidth: number?) 27 | -- [saaawdust]: These are temporary, cause 28 | -- if I calculate it, it takes up 29 | -- like 10FPS 30 | 31 | fill = fill or color.BLACK 32 | strokeColor = strokeColor or color.TRANSPARENT 33 | strokeWidth = strokeWidth or 1 34 | 35 | local sizeX = object.width 36 | local maxY = object.height 37 | 38 | if strokeColor ~= color.TRANSPARENT and strokeWidth > 0 then 39 | for i = 1, #corners do 40 | local p1 = corners[i] 41 | local p2 = corners[i % #corners + 1] 42 | 43 | line( 44 | object, 45 | math.floor(p1[1]), 46 | math.floor(p1[2]), 47 | math.floor(p2[1]), 48 | math.floor(p2[2]), 49 | strokeWidth, 50 | strokeColor 51 | ) 52 | end 53 | end 54 | 55 | for y = 0, maxY do 56 | local intersections = {} 57 | local count = 0 58 | 59 | for i = 1, #corners do 60 | local p1 = corners[i] 61 | local p2 = corners[i % #corners + 1] 62 | 63 | if (p1[2] <= y and p2[2] > y) or (p2[2] <= y and p1[2] > y) then 64 | local t = (y - p1[2]) / (p2[2] - p1[2]) 65 | local intersectX = p1[1] + t * (p2[1] - p1[1]) 66 | count += 1 67 | intersections[count] = intersectX 68 | end 69 | end 70 | 71 | if count < 2 then 72 | continue 73 | end 74 | 75 | if count == 2 then 76 | local x1, x2 = intersections[1], intersections[2] 77 | if x1 > x2 then 78 | x1, x2 = x2, x1 79 | end 80 | 81 | chunk(object, fill, math.max(0, math.floor(x1)), y, math.min(sizeX - 1, math.ceil(x2)), y) 82 | 83 | continue 84 | end 85 | 86 | table.sort(intersections) 87 | 88 | for i = 1, count - 1, 2 do 89 | local x1, x2 = intersections[i], intersections[i + 1] 90 | chunk(object, fill, math.max(0, math.floor(x1)), y, math.min(sizeX - 1, math.ceil(x2)), y) 91 | end 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /src/draw/rect.luau: -------------------------------------------------------------------------------- 1 | --!optimize 2 2 | --!native 3 | 4 | ------------------------------------------------------------ 5 | -- OSGL v1.6.2 - Open-Source Graphical Library 6 | -- Created/maintained by @saaawdust and contributors 7 | -- 8 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 9 | -- Credit appreciated but not required 10 | -- 11 | -- Do NOT: 12 | -- - Repackage and sell OSGL directly 13 | -- - Claim you created the core library 14 | -- 15 | -- By using OSGL, you agree to these terms. 16 | -- Full license available in the LICENSE file 17 | ------------------------------------------------------------ 18 | 19 | local OSGL = script.Parent.Parent 20 | local draw = OSGL.draw 21 | local types = require(OSGL.types) 22 | local color = require(OSGL.color) 23 | local fillPolygon = require(draw.polygon) 24 | local pixel = require(draw.pixel) 25 | 26 | return function(object: types.DrawableObject, xPos: number, yPos: number, width: number, height: number, fill: types.Color?, stroke: types.Color?, strokeThickness: number?, rotation: number?) 27 | fill = fill or color.BLACK 28 | rotation = rotation or 0 29 | stroke = stroke or color.TRANSPARENT 30 | strokeThickness = strokeThickness or 0 31 | 32 | local strokeLength = if stroke ~= color.TRANSPARENT then strokeThickness else 0 33 | 34 | local sizeX, sizeY = object.width, object.height 35 | 36 | local centerX = xPos + width / 2 37 | local centerY = yPos + height / 2 38 | 39 | if rotation % 90 == 0 then 40 | local w, h 41 | 42 | if rotation % 180 == 0 then 43 | w, h = width, height 44 | else 45 | w, h = height, width 46 | end 47 | 48 | w += strokeLength 49 | h += strokeLength 50 | 51 | for x = 0, w do 52 | x += xPos 53 | 54 | if x >= sizeX then 55 | continue 56 | end 57 | 58 | for y = 0, h do 59 | y += yPos 60 | if y >= sizeY then 61 | continue 62 | end 63 | 64 | local pixelColor = if ( 65 | x + strokeLength > (w + xPos) 66 | or x - xPos < strokeLength 67 | or y + strokeLength > (h + yPos) 68 | or y - yPos < strokeLength 69 | ) then stroke 70 | else fill 71 | 72 | pixel(object, x, y, pixelColor) 73 | end 74 | end 75 | 76 | return 77 | end 78 | 79 | local radians = rotation * (math.pi / 180) 80 | local cosTheta = math.cos(radians) 81 | local sinTheta = math.sin(radians) 82 | 83 | local function rotatePoint(x, y) 84 | local relativeX = x - centerX 85 | local relativeY = y - centerY 86 | local rotatedX = relativeX * cosTheta - relativeY * sinTheta 87 | local rotatedY = relativeX * sinTheta + relativeY * cosTheta 88 | return centerX + rotatedX, centerY + rotatedY 89 | end 90 | 91 | local x0, y0 = rotatePoint(xPos, yPos) 92 | local x1, y1 = rotatePoint(xPos + width, yPos) 93 | local x2, y2 = rotatePoint(xPos + width, yPos + height) 94 | local x3, y3 = rotatePoint(xPos, yPos + height) 95 | 96 | 97 | fillPolygon(object, { 98 | { x0, y0 }, 99 | { x1, y1 }, 100 | { x2, y2 }, 101 | { x3, y3 }, 102 | }, fill) 103 | end 104 | -------------------------------------------------------------------------------- /src/draw/triangle.luau: -------------------------------------------------------------------------------- 1 | --!optimize 2 2 | --!native 3 | 4 | ------------------------------------------------------------ 5 | -- OSGL v1.6.2 - Open-Source Graphical Library 6 | -- Created/maintained by @saaawdust and contributors 7 | -- 8 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 9 | -- Credit appreciated but not required 10 | -- 11 | -- Do NOT: 12 | -- - Repackage and sell OSGL directly 13 | -- - Claim you created the core library 14 | -- 15 | -- By using OSGL, you agree to these terms. 16 | -- Full license available in the LICENSE file 17 | ------------------------------------------------------------ 18 | 19 | local OSGL = script.Parent.Parent 20 | local draw = OSGL.draw 21 | local types = require(OSGL.types) 22 | local color = require(OSGL.color) 23 | local fillPolygon = require(draw.polygon) 24 | 25 | return function(object: types.DrawableObject, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, fill: types.Color?, stroke: types.Color?, strokeThickness: number?) 26 | fill = fill or color.BLACK 27 | stroke = stroke or color.TRANSPARENT 28 | strokeThickness = strokeThickness or 0 29 | 30 | -- [saaawdust]: Somehow this is faster than 31 | -- an optimised triangle rasterizer. 32 | -- Use this implementation until something 33 | -- faster is found. 34 | fillPolygon(object, { 35 | { x1, y1 }, 36 | { x2, y2 }, 37 | { x3, y3 }, 38 | }, fill, stroke, strokeThickness) 39 | end 40 | -------------------------------------------------------------------------------- /src/enums.luau: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------ 2 | -- OSGL v1.6.2 - Open-Source Graphical Library 3 | -- Created/maintained by @saaawdust and contributors 4 | -- 5 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 6 | -- Credit appreciated but not required 7 | -- 8 | -- Do NOT: 9 | -- - Repackage and sell OSGL directly 10 | -- - Claim you created the core library 11 | -- 12 | -- By using OSGL, you agree to these terms. 13 | -- Full license available in the LICENSE file 14 | ------------------------------------------------------------ 15 | 16 | local WindowError = { 17 | InvalidCreationInstance = 0 :: WindowError, 18 | WindowDimensionsOutOfBounds = 1 :: WindowError, 19 | APINotEnabled = 8 :: WindowError, 20 | } 21 | 22 | local DrawableObjectError = { 23 | NotEnoughMemory = 2 :: DrawableObjectError, 24 | ResizeOutOfBounds = 3 :: DrawableObjectError, 25 | BadBufferSize = 4 :: DrawableObjectError, 26 | OutOfBounds = 5 :: DrawableObjectError, 27 | } 28 | 29 | local TextureError = { 30 | BadInput = 6 :: TextureError, 31 | } 32 | 33 | local FontError = { 34 | BadInput = 7 :: TextureError, 35 | } 36 | 37 | local enums = { 38 | WindowError = WindowError :: typeof(WindowError), 39 | DrawableObjectError = DrawableObjectError :: typeof(DrawableObjectError), 40 | TextureError = TextureError :: typeof(TextureError), 41 | FontError = FontError :: typeof(FontError), 42 | } 43 | 44 | export type WindowError = number 45 | export type DrawableObjectError = number 46 | export type TextureError = number 47 | export type FontError = number 48 | export type OEnum = typeof(enums) 49 | 50 | return enums :: OEnum 51 | -------------------------------------------------------------------------------- /src/font.luau: -------------------------------------------------------------------------------- 1 | --!optimize 2 2 | --!native 3 | 4 | ------------------------------------------------------------ 5 | -- OSGL v1.6.2 - Open-Source Graphical Library 6 | -- Created/maintained by @saaawdust and contributors 7 | -- 8 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 9 | -- Credit appreciated but not required 10 | -- 11 | -- Do NOT: 12 | -- - Repackage and sell OSGL directly 13 | -- - Claim you created the core library 14 | -- 15 | -- By using OSGL, you agree to these terms. 16 | -- Full license available in the LICENSE file 17 | ------------------------------------------------------------ 18 | 19 | local OSGL = script.Parent 20 | local types = require(OSGL.types) 21 | local utils = require(OSGL.util) 22 | local draw = require(OSGL.draw) 23 | local result = require(OSGL.result) 24 | local oEnum = require(OSGL.enums) 25 | 26 | type Result = result.Result 27 | 28 | --[=[ 29 | @class Font 30 | 31 | The Font class handles loading and rendering of fonts. 32 | ]=] 33 | local Font = {} 34 | 35 | local function readBitNumber(n: number, position: number): number 36 | return bit32.extract(n, position, 1) 37 | end 38 | 39 | function splitStringWithEscapeSequences(input: string) 40 | local result = {} 41 | local i = 1 42 | local length = #input 43 | 44 | while i <= length do 45 | local char = input:sub(i, i) 46 | local nextChar = input:sub(i + 1, i + 1) 47 | 48 | if char == "\\" and (nextChar == "t" or nextChar == "n") then 49 | table.insert(result, "\\" .. nextChar) 50 | i += 2 51 | else 52 | table.insert(result, char) 53 | i += 1 54 | end 55 | end 56 | 57 | return result 58 | end 59 | 60 | --[=[ 61 | Draws text at a specific location on-screen 62 | 63 | ```lua 64 | local OSGL = require(path.to.osgl) 65 | local Window = OSGL.Window 66 | local Font = OSGL.Font 67 | local color = OSGL.color 68 | 69 | -- Creates a Window with a size of 50x50 70 | -- This window is a DrawableObject 71 | local myWindow = Window.new(path.to.instance, { sizeX = 50, sizeY = 50 }) 72 | 73 | local data = -- Reference to font 74 | local font = Front.from(data) 75 | font:Draw(myWindow, "Hello!", 0, 0) -- Draw the font 76 | ``` 77 | 78 | @within Font 79 | @param object -- A [DrawableObject] 80 | @param text -- The text to draw 81 | @param x -- X position 82 | @param y -- Y position 83 | @param color -- The color of the text 84 | @return Font -- Returns itself. 85 | ]=] 86 | --[=[ 87 | @OSGL/Font 88 | Draws Text on-screen. 89 | ]=] 90 | function Font.Draw( 91 | self: types.Font, 92 | object: types.DrawableObject, 93 | text: string, 94 | x: number, 95 | y: number, 96 | color: types.Color 97 | ) 98 | local letters = splitStringWithEscapeSequences(text) 99 | 100 | local glyphs = self.glyphs 101 | local iSize = glyphs["i"][1] 102 | local iWidth = bit32.extract(iSize, 16, 16) 103 | 104 | local originalX = x 105 | 106 | for _, letter in ipairs(letters) do 107 | local glyph = glyphs[letter] 108 | 109 | if letter == " " then 110 | x += iWidth 111 | continue 112 | elseif letter == "\n" then 113 | x = originalX 114 | y += self.linePadding 115 | continue 116 | elseif letter == "\t" then 117 | x += iWidth * 4 118 | continue 119 | end 120 | 121 | if glyph then 122 | local size = glyph[1] :: number 123 | local height = bit32.extract(size, 0, 16) 124 | local width = bit32.extract(size, 16, 16) 125 | local pixelData = glyph[2] 126 | 127 | local heightDrop = self._maxHeight - height 128 | 129 | if typeof(pixelData) == "buffer" then 130 | local index = 0 131 | for fw = 1, width do 132 | for fh = 1, height do 133 | local bit = buffer.readu8(pixelData, index) 134 | if bit == 1 then 135 | draw.Pixel(object, x + fw, y + fh + heightDrop, color) 136 | end 137 | 138 | index += 1 139 | end 140 | end 141 | else 142 | for fh = 1, height do 143 | for fw = 1, width do 144 | local bit = readBitNumber(pixelData[fw], fh) 145 | if bit == 1 then 146 | draw.Pixel(object, x + fw, y + fh + heightDrop, color) 147 | end 148 | end 149 | end 150 | end 151 | 152 | x += width + self.spacing 153 | end 154 | end 155 | 156 | return self 157 | end 158 | 159 | --[=[ 160 | Creates a new font 161 | 162 | ```lua 163 | local OSGL = require(path.to.osgl) 164 | local Window = OSGL.Window 165 | local Font = OSGL.Font 166 | local color = OSGL.color 167 | 168 | -- Creates a Window with a size of 50x50 169 | -- This window is a DrawableObject 170 | local myWindow = Window.new(path.to.instance, { sizeX = 50, sizeY = 50 }) 171 | 172 | local font = Font.from(data) 173 | ``` 174 | @within Font 175 | @param data -- The loaded font-date 176 | @return Font -- Returns a new font. 177 | ]=] 178 | --[=[ 179 | @OSGL/Font 180 | Creates a new Font from loaded font-data. 181 | ]=] 182 | function Font.from(data: types.StoredFont): Result 183 | local v = data.version 184 | if v and not table.find(utils.VALID_FONT_VERSIONS, v) then 185 | return result.new(false, oEnum.FontError.BadInput) 186 | end 187 | 188 | return result.new(true, { 189 | glyphs = data.letters, 190 | 191 | _maxHeight = data.maxHeight, 192 | spacing = 5, 193 | linePadding = 0, 194 | 195 | Draw = Font.Draw, 196 | }) 197 | end 198 | 199 | return Font 200 | -------------------------------------------------------------------------------- /src/init.luau: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------ 2 | -- OSGL v1.6.2 - Open-Source Graphical Library 3 | -- Created/maintained by @saaawdust and contributors 4 | -- 5 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 6 | -- Credit appreciated but not required 7 | -- 8 | -- Do NOT: 9 | -- - Repackage and sell OSGL directly 10 | -- - Claim you created the core library 11 | -- 12 | -- By using OSGL, you agree to these terms. 13 | -- Full license available in the LICENSE file 14 | ------------------------------------------------------------ 15 | 16 | local DrawableObject = script.DrawableObject 17 | local types = require(script.types) 18 | 19 | export type Window = types.Window 20 | export type Color = types.Color 21 | export type Texture = types.OSGLTexture 22 | export type Bitmap = types.Bitmap 23 | export type Video = types.Video 24 | export type Font = types.Font 25 | 26 | return { 27 | Window = require(DrawableObject.window), 28 | Texture = require(DrawableObject.texture), 29 | Bitmap = require(script.bitmap), 30 | Enum = require(script.enums), 31 | Video = require(script.video), 32 | Font = require(script.font), 33 | 34 | color = require(script.color), 35 | } 36 | -------------------------------------------------------------------------------- /src/result.luau: -------------------------------------------------------------------------------- 1 | --!optimize 2 2 | -- saaawdust ~ A port of Rusts `Result` in Roblox 3 | 4 | ------------------------------------------------------------ 5 | -- OSGL v1.6.2 - Open-Source Graphical Library 6 | -- Created/maintained by @saaawdust and contributors 7 | -- 8 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 9 | -- Credit appreciated but not required 10 | -- 11 | -- Do NOT: 12 | -- - Repackage and sell OSGL directly 13 | -- - Claim you created the core library 14 | -- 15 | -- By using OSGL, you agree to these terms. 16 | -- Full license available in the LICENSE file 17 | ------------------------------------------------------------ 18 | 19 | export type Result = { 20 | isOk: boolean, 21 | value: { T | E }, 22 | 23 | Unwrap: (self: Result) -> T, 24 | Expect: (self: Result, message: string) -> T, 25 | 26 | UnwrapErr: (self: Result) -> E, 27 | ExpectErr: (self: Result, message: string) -> E, 28 | 29 | UnwrapOr: (self: Result, value: T) -> T, 30 | Or: (self: Result, value: Result) -> Result, 31 | } 32 | 33 | local Result = {} 34 | 35 | --- A `Result` represents the outcome of an operation that can either succeed (`Ok`) or fail (`Err`). 36 | --- Inspired by Rust's `Result` type, it provides a structured way to handle errors without exceptions or `nil` checks. 37 | function Result.new(isOk: boolean, ...: T | E): Result 38 | local Res: Result = { 39 | isOk = isOk, 40 | value = {...}, 41 | } 42 | 43 | --- Unwraps the `Result`, returning the value if `Ok`. 44 | --- Throws an error if the `Result` is `Err`. 45 | --- @returns The value wrapped in `Ok`. 46 | --- @error Throws an error if the `Result` is `Err`. 47 | function Res:Unwrap() 48 | return Res:Expect("Attempt to `unwrap` an erroneous value") 49 | end 50 | 51 | function Res:Expect(message: string) 52 | if not Res.isOk then 53 | error(message, 3) 54 | end 55 | 56 | return table.unpack(Res.value) 57 | end 58 | 59 | --- Unwraps the `Result`, returning the error if `Err`. 60 | --- Throws an error if the `Result` is `Ok`. 61 | --- @returns The error wrapped in `Err`. 62 | --- @error Throws an error if the `Result` is `Ok`. 63 | function Res:UnwrapErr() 64 | return Res:ExpectErr("Attempt to unwrap an error out of a correct value") 65 | end 66 | 67 | function Res:ExpectErr(message: string) 68 | if not Res.isOk then 69 | return Res.value[1] 70 | end 71 | 72 | error(message, 3) 73 | end 74 | 75 | --- Unwraps the `Result`, returning the value if `Ok` or a default value if `Err`. 76 | --- @param v any The default value to return if the `Result` is `Err`. 77 | --- @returns The value wrapped in `Ok`, or `default` if `Err`. 78 | function Res:UnwrapOr(v: T) 79 | if Res.isOk then 80 | return table.unpack(Res.value) 81 | end 82 | 83 | return v 84 | end 85 | 86 | function Res:Or(otherValue: Result) 87 | if not Res.isOk then 88 | Res = otherValue 89 | end 90 | 91 | return Res 92 | end 93 | 94 | return Res 95 | end 96 | 97 | return Result 98 | -------------------------------------------------------------------------------- /src/types.luau: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------ 2 | -- OSGL v1.6.2 - Open-Source Graphical Library 3 | -- Created/maintained by @saaawdust and contributors 4 | -- 5 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 6 | -- Credit appreciated but not required 7 | -- 8 | -- Do NOT: 9 | -- - Repackage and sell OSGL directly 10 | -- - Claim you created the core library 11 | -- 12 | -- By using OSGL, you agree to these terms. 13 | -- Full license available in the LICENSE file 14 | ------------------------------------------------------------ 15 | 16 | local OSGL = script.Parent 17 | local result = require(OSGL.result) 18 | local oEnum = require(OSGL.enums) 19 | 20 | type Result = result.Result 21 | 22 | export type ImageBase = ImageLabel | ImageButton 23 | export type TextureBase = Decal | Texture 24 | export type Drawable = ImageBase | TextureBase | MeshPart 25 | export type Color = number 26 | 27 | export type Window = DrawableObject & { 28 | surfaces: {Drawable}, 29 | editableImage: EditableImage, 30 | targetFPS: number, 31 | 32 | Render: (self: Window) -> (), 33 | RenderTargetFPS: (self: Window) -> (), 34 | AddRenderers: (self: Window, ...Drawable) -> (), 35 | RemoveRenderers: (self: Window, ...Drawable) -> (), 36 | GetRelativeMousePosition: (self: Window, image: ImageBase) -> (boolean, number, number), 37 | Destroy: (self: Window) -> (), 38 | } 39 | 40 | export type DrawableObject = DrawingContext & { 41 | buffer: buffer, 42 | 43 | width: number, 44 | height: number, 45 | size: Vector2, 46 | 47 | Resize: (self: T, width: number, height: number) -> Result, 48 | Serialize: (self: T) -> (buffer, number, number), 49 | Deserialize: (self: T, buffer: buffer, width: number, height: number) -> Result, 50 | ReadPixelChecked: (self: T, X: number, Y: number) -> Result, 51 | ReadPixelUnchecked: (self: T, X: number, Y: number) -> Color, 52 | TintRegionChecked: (self: T, tint: Color, factor: number, X: number, Y: number, width: number, height: number) -> Result, 53 | TintRegionUnchecked: (self: T, tint: Color, factor: number, X: number, Y: number, width: number, height: number) -> nil, 54 | Tint: (self: T, tint: Color, factor: number) -> nil, 55 | Resample: (self: T, scale: number?) -> nil, 56 | } 57 | 58 | export type DrawingContext = { 59 | Pixel: (self: T, X: number, Y: number, Color: Color) -> nil, 60 | PixelIndex: (self: T, Index: number, Color: Color) -> nil, 61 | Line: (self: T, X1: number, Y1: number, X2: number, Y2: number, Thickness: number, Color: Color?) -> nil, 62 | Rectangle: (self: T, X: number, Y: number, Width: number, Height: number, Fill: Color?, Stroke: Color?, StrokeThickness: number?, Rotation: number?) -> nil, 63 | Polygon: (self: T, Corners: { { number } }, Fill: Color, StrokeColor: Color?, StrokeWidth: number?) -> nil, 64 | Triangle: (self: T, X1: number, Y1: number, X2: number, Y2: number, X3: number, Y3: number, Fill: Color?, Stroke: Color?, StrokeThickness: number?) -> nil, 65 | Circle: (self: T, CenterX: number, CenterY: number, Radius: number, Fill: Color?, Stroke: Color?, StrokeThickness: number?) -> nil, 66 | Chunk: (self: T, color: Color, x1: number, y1: number, x2: number, y2: number) -> nil, 67 | FloodFill: (self: T, x: number, y: number, color: Color) -> nil, 68 | Buffer: (self: T, Buffer: buffer, Width: number, Height: number, X: number, Y: number) -> nil, 69 | 70 | Clear: (self: T, Color: Color?) -> nil, 71 | } 72 | 73 | export type BaseRawTexture = { 74 | version: string & T, 75 | width: number, 76 | height: number, 77 | pixels: buffer 78 | } 79 | 80 | export type BaseUnloadedTexture = BaseRawTexture<"1.6.1"> 81 | export type OSGLTexture = DrawableObject 82 | export type RawTexture = ModuleScript | BaseUnloadedTexture 83 | export type Bitmap = { 84 | channels: number, 85 | width: number, 86 | height: number, 87 | buffer: buffer, 88 | Read: (self: Bitmap, X: number, Y: number, channel: number?) -> number, 89 | Write: (self: Bitmap, X: number, Y: number, channel: number, value: number) -> nil, 90 | } 91 | 92 | export type Glyph = { 93 | number | buffer | { number } 94 | } 95 | export type Glyphs = { [string]: Glyph } 96 | export type StoredFont = { 97 | version: string, 98 | letters: Glyphs, 99 | maxHeight: number, 100 | } 101 | export type Font = { 102 | glyphs: Glyphs, 103 | 104 | spacing: number, 105 | linePadding: number, 106 | 107 | Draw:(self: Font, object: DrawableObject, text: string, x: number, y: number, color: Color) -> Font, 108 | } 109 | 110 | export type Video = { 111 | width: number, 112 | height: number, 113 | playbackFrame: number, 114 | frameRate: number, 115 | playing: boolean, 116 | loop: boolean, 117 | frames: {buffer}, 118 | 119 | PlaySync: (self: Video, callback: (width: number, height: number, buffer: buffer) -> ()) -> Video, 120 | PlayAsync: (self: Video, callback: (width: number, height: number, buffer: buffer) -> ()) -> Video, 121 | 122 | Stop: (self: Video) -> Video, 123 | 124 | GetFrame: (self: Video, frame: number) -> (buffer, number, number), 125 | GetBufferOfFrame: (self: Video, frame: number) -> buffer, 126 | 127 | Previous: (self: Video) -> Video, 128 | Next: (self: Video) -> Video, 129 | } 130 | 131 | return nil 132 | -------------------------------------------------------------------------------- /src/util.luau: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------ 2 | -- OSGL v1.6.2 - Open-Source Graphical Library 3 | -- Created/maintained by @saaawdust and contributors 4 | -- 5 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 6 | -- Credit appreciated but not required 7 | -- 8 | -- Do NOT: 9 | -- - Repackage and sell OSGL directly 10 | -- - Claim you created the core library 11 | -- 12 | -- By using OSGL, you agree to these terms. 13 | -- Full license available in the LICENSE file 14 | ------------------------------------------------------------ 15 | 16 | local util = {} 17 | util.VALID_TEXTURE_VERSIONS = { "1.5b", "1.6b", "1.6.1", "1.6.2" } 18 | util.VALID_FONT_VERSIONS = { "1.5b", "1.6b", "1.6.1", "1.6.2", } 19 | 20 | function util.getRendererProp(object: Instance): string 21 | if object:IsA("ImageLabel") or object:IsA("ImageButton") then 22 | return "ImageContent" 23 | elseif object:IsA("Texture") or object:IsA("Decal") then 24 | return "TextureContent" 25 | end 26 | 27 | return "" 28 | end 29 | 30 | return util 31 | -------------------------------------------------------------------------------- /src/video.luau: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------ 2 | -- OSGL v1.6.2 - Open-Source Graphical Library 3 | -- Created/maintained by @saaawdust and contributors 4 | -- 5 | -- Free to use, modify and distribute in your projects (including commercial games ;3) 6 | -- Credit appreciated but not required 7 | -- 8 | -- Do NOT: 9 | -- - Repackage and sell OSGL directly 10 | -- - Claim you created the core library 11 | -- 12 | -- By using OSGL, you agree to these terms. 13 | -- Full license available in the LICENSE file 14 | ------------------------------------------------------------ 15 | 16 | local OSGL = script.Parent 17 | local types = require(OSGL.types) 18 | 19 | local Video = {} 20 | 21 | --[=[ 22 | @OSGL/Video 23 | Plays a Video synchronously. 24 | ]=] 25 | function PlaySync(self: types.Video, callback: (width: number, height: number, buffer: buffer) -> ()) 26 | local frames = #self.frames 27 | local timePerFrame = 1 / self.frameRate 28 | 29 | self.playing = true 30 | while self.playing do 31 | self.playbackFrame += 1 32 | 33 | if self.playbackFrame > frames then 34 | if self.loop then 35 | self.playbackFrame = 1 36 | else 37 | self.playing = false 38 | break 39 | end 40 | end 41 | 42 | callback(self.width, self.height, self.frames[self.playbackFrame]) 43 | task.wait(timePerFrame) 44 | end 45 | 46 | return self 47 | end 48 | 49 | --[=[ 50 | @OSGL/Video 51 | Plays a Video asynchronously. 52 | ]=] 53 | function PlayAsync(self: types.Video, callback: (width: number, height: number, buffer: buffer) -> ()) 54 | task.spawn(self.PlaySync, self, callback) 55 | return self 56 | end 57 | 58 | --[=[ 59 | @OSGL/Video 60 | Skips a frame of the Video. 61 | ]=] 62 | function Next(self: types.Video) 63 | local len = #self.frames 64 | if self.playbackFrame < len then 65 | self.playbackFrame += 1 66 | elseif self.loop then 67 | self.playbackFrame = 0 68 | end 69 | 70 | return self 71 | end 72 | 73 | --[=[ 74 | @OSGL/Video 75 | Goes to the previous frame of the Video. 76 | ]=] 77 | function Previous(self: types.Video) 78 | if self.playbackFrame > 0 then 79 | self.playbackFrame -= 1 80 | end 81 | 82 | return self 83 | end 84 | 85 | --[=[ 86 | @OSGL/Video 87 | Stops playback of the Video. 88 | ]=] 89 | function Stop(self: types.Video) 90 | self.playing = false 91 | self.playbackFrame = 0 92 | 93 | return self 94 | end 95 | 96 | --[=[ 97 | @OSGL/Video 98 | Returns a frame of theVideo. 99 | ]=] 100 | function GetFrame(self: types.Video, frame: number): (buffer, number, number) 101 | return self.frames[frame], self.width, self.height 102 | end 103 | 104 | --[=[ 105 | @OSGL/Video 106 | Returns the buffer of a frame from the Video. 107 | ]=] 108 | function GetBufferOfFrame(self: types.Video, frame: number): buffer 109 | return self.frames[frame] or buffer.create(0) 110 | end 111 | 112 | --[=[ 113 | @OSGL/Video 114 | Creates a new Video. 115 | ]=] 116 | function Video.new(width: number, height: number, frames: { buffer }?): types.Video 117 | local frames = frames or {} 118 | 119 | local size 120 | for i, v: buffer in ipairs(frames) do 121 | local len = buffer.len(v) 122 | 123 | if not size then 124 | size = len 125 | elseif len ~= size then 126 | warn("Failed to load video frame ".. i.. ". The frame has been discarded, all frames but be the same size!") 127 | table.remove(frames, i) 128 | end 129 | end 130 | 131 | return { 132 | width = width, 133 | height = height, 134 | playbackFrame = 0, 135 | frameRate = 1 / #frames, 136 | frames = frames, 137 | playing = false, 138 | loop = false, 139 | 140 | PlaySync = PlaySync, 141 | PlayAsync = PlayAsync, 142 | Previous = Previous, 143 | Next = Next, 144 | Stop = Stop, 145 | 146 | GetBufferOfFrame = GetBufferOfFrame, 147 | GetFrame = GetFrame 148 | } 149 | end 150 | 151 | --[=[ 152 | @OSGL/Video 153 | Creates a new Video from a collection of raw Textures. 154 | ]=] 155 | function Video.from(collection: { types.RawTexture }): types.Video 156 | local framesAmount = #collection 157 | 158 | if framesAmount == 0 then 159 | return Video.new(0, 0) 160 | end 161 | 162 | local bfrCollection = table.create(framesAmount) 163 | 164 | local width, height 165 | for i, v in collection do 166 | if typeof(v) == "Instance" and v:IsA("ModuleScript") then 167 | v = require(v) 168 | end 169 | 170 | if not width or not height then 171 | width, height = v.width, v.height 172 | elseif width ~= v.width or height ~= v.height then 173 | warn("Failed to load video frame ".. i.. ". The frame has been discarded, all frames but be the same size!") 174 | continue 175 | end 176 | 177 | bfrCollection[i] = v.pixels 178 | end 179 | 180 | return Video.new(width, height, bfrCollection) 181 | end 182 | 183 | return Video 184 | -------------------------------------------------------------------------------- /src/wally.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gunshotsoundstudios/osgl" 3 | version = "0.1.627" 4 | registry = "https://github.com/UpliftGames/wally-index" 5 | realm = "shared" 6 | authors = ["SawWasTaken", "jukepilot", "msix", "Sle_l", "notclaram"] 7 | description = "OSGL is an easy to learn, and super fast efficient module that allows you to draw pixels straight to the screen in Roblox." 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /tests/client/circle-draw/init.client.luau: -------------------------------------------------------------------------------- 1 | local Players = game:GetService("Players") 2 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 3 | local UserInputService = game:GetService("UserInputService") 4 | local OSGL = require(ReplicatedStorage.OSGL) 5 | local Window = OSGL.Window 6 | local color = OSGL.color 7 | 8 | local ScreenGui = Instance.new("ScreenGui") 9 | ScreenGui.Name = "Screen" 10 | ScreenGui.Parent = Players.LocalPlayer.PlayerGui 11 | 12 | local Image = Instance.new("ImageLabel") 13 | Image.Name = "Texture" 14 | Image.Parent = ScreenGui 15 | Image.AnchorPoint = Vector2.new(.5, .5) 16 | Image.Position = UDim2.fromScale(.5, .5) 17 | Image.Size = UDim2.fromOffset(500, 500) 18 | Image.ResampleMode = Enum.ResamplerMode.Pixelated 19 | 20 | local WIDTH = 1024 21 | local HEIGHT = 1024 22 | 23 | local myWindow = Window.from(Image, WIDTH, HEIGHT) 24 | 25 | if not myWindow.isOk then 26 | warn("Encountered Error,", myWindow:UnwrapErr()) 27 | return 28 | end 29 | 30 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 31 | while task.wait() do 32 | local inWindow, X, Y = unwrappedWindow:GetRelativeMousePosition(Image) 33 | if not inWindow or not UserInputService:IsMouseButtonPressed(Enum.UserInputType.MouseButton1) then 34 | continue 35 | end 36 | 37 | unwrappedWindow:Clear() 38 | unwrappedWindow:Circle(X, Y, 500, color.RED, color.BLACK, 50) 39 | unwrappedWindow:Render() 40 | end -------------------------------------------------------------------------------- /tests/client/circle-draw/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/client/relative-mouse/init.client.luau: -------------------------------------------------------------------------------- 1 | local Players = game:GetService("Players") 2 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 3 | local OSGL = require(ReplicatedStorage.OSGL) 4 | local Window = OSGL.Window 5 | local color = OSGL.color 6 | 7 | local ScreenGui = Instance.new("ScreenGui") 8 | ScreenGui.Name = "Screen" 9 | ScreenGui.Parent = Players.LocalPlayer.PlayerGui 10 | 11 | local Image = Instance.new("ImageLabel") 12 | Image.Name = "Texture" 13 | Image.Parent = ScreenGui 14 | Image.AnchorPoint = Vector2.new(.5, .5) 15 | Image.Position = UDim2.fromScale(.5, .5) 16 | Image.Size = UDim2.fromOffset(500, 500) 17 | 18 | local WIDTH = 100 19 | local HEIGHT = 100 20 | 21 | local myWindow = Window.from(Image, WIDTH, HEIGHT) 22 | 23 | if not myWindow.isOk then 24 | warn("Encountered Error,", myWindow:UnwrapErr()) 25 | return 26 | end 27 | 28 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 29 | while task.wait() do 30 | local inWindow, X, Y = unwrappedWindow:GetRelativeMousePosition(Image) 31 | if not inWindow then 32 | continue 33 | end 34 | 35 | unwrappedWindow:Pixel(X, Y, color.BLACK) 36 | unwrappedWindow:Render() 37 | end -------------------------------------------------------------------------------- /tests/client/relative-mouse/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/server/bitmap-combine/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/server/bitmap-combine/init.server.luau: -------------------------------------------------------------------------------- 1 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 2 | local OSGL = require(ReplicatedStorage.OSGL) 3 | local Bitmap = OSGL.Bitmap 4 | local Window = OSGL.Window 5 | local color = OSGL.color 6 | 7 | local Block = Instance.new("Part") 8 | Block.Name = "WindowPart" 9 | Block.Parent = workspace 10 | Block.Anchored = true 11 | Block.Size = Vector3.new(5, 0, 5) 12 | Block.Position = Vector3.new(0, 5, 0) 13 | 14 | local Texture = Instance.new("Texture") 15 | Texture.Name = "WindowTexture" 16 | Texture.Face = Enum.NormalId.Top 17 | Texture.Parent = Block 18 | 19 | local WIDTH = 622 20 | local HEIGHT = 622 21 | 22 | local myWindow = Window.from(Texture, WIDTH, HEIGHT) 23 | 24 | if not myWindow.isOk then 25 | warn("Encountered Error,", myWindow:UnwrapErr()) 26 | return 27 | end 28 | 29 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 30 | 31 | local RBitmap = Bitmap.fromChecked(script.R):Unwrap() 32 | local GBitmap = Bitmap.fromChecked(script.G):Unwrap() 33 | local BBitmap = Bitmap.fromChecked(script.B):Unwrap() 34 | 35 | for X = 0, 621 do 36 | for Y = 0, 621 do 37 | local R = RBitmap:Read(X, Y) 38 | local G = GBitmap:Read(X, Y) 39 | local B = BBitmap:Read(X, Y) 40 | local col = color.newRGB(R, G, B) 41 | 42 | unwrappedWindow:Pixel(X, Y, col) 43 | end 44 | end 45 | 46 | unwrappedWindow:Render() 47 | -------------------------------------------------------------------------------- /tests/server/bitmap-drawing/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/server/bitmap-drawing/init.server.luau: -------------------------------------------------------------------------------- 1 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 2 | local OSGL = require(ReplicatedStorage.OSGL) 3 | local Bitmap = OSGL.Bitmap 4 | local Window = OSGL.Window 5 | local color = OSGL.color 6 | 7 | local Block = Instance.new("Part") 8 | Block.Name = "WindowPart" 9 | Block.Parent = workspace 10 | Block.Anchored = true 11 | Block.Size = Vector3.new(5, 0, 5) 12 | Block.Position = Vector3.new(0, 5, 0) 13 | 14 | local Texture = Instance.new("Texture") 15 | Texture.Name = "WindowTexture" 16 | Texture.Face = Enum.NormalId.Top 17 | Texture.Parent = Block 18 | 19 | local WIDTH = 622 20 | local HEIGHT = 622 21 | 22 | local myWindow = Window.from(Texture, WIDTH, HEIGHT) 23 | 24 | if not myWindow.isOk then 25 | warn("Encountered Error,", myWindow:UnwrapErr()) 26 | return 27 | end 28 | 29 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 30 | 31 | local newBitmap = Bitmap.fromChecked(script.Bitmap) 32 | if not newBitmap.isOk then 33 | warn("Encountered Error,", newBitmap:UnwrapErr()) 34 | return 35 | end 36 | 37 | local unwrappedBitmap: OSGL.Bitmap = newBitmap:Unwrap() 38 | for X = 0, 621 do 39 | for Y = 0, 621 do 40 | local R = unwrappedBitmap:Read(X, Y) 41 | if R ~= 0 then 42 | local col = color.newRGB(R, 0, 0) 43 | unwrappedWindow:Pixel(X, Y, col) 44 | end 45 | end 46 | end 47 | 48 | unwrappedWindow:Render() 49 | -------------------------------------------------------------------------------- /tests/server/buffer-overhead/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/server/buffer-overhead/init.server.luau: -------------------------------------------------------------------------------- 1 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 2 | local OSGL = require(ReplicatedStorage.OSGL) 3 | local Window = OSGL.Window 4 | local color = OSGL.color 5 | 6 | local Block = Instance.new("Part") 7 | Block.Name = "WindowPart" 8 | Block.Parent = workspace 9 | Block.Anchored = true 10 | Block.Size = Vector3.new(5, 0, 5) 11 | Block.Position = Vector3.new(0, 5, 0) 12 | 13 | local Texture = Instance.new("Texture") 14 | Texture.Name = "WindowTexture" 15 | Texture.Face = Enum.NormalId.Top 16 | Texture.Parent = Block 17 | 18 | local WIDTH = 1024 19 | local HEIGHT = 1024 20 | 21 | local oldWindow = Window.from(Texture, WIDTH, HEIGHT) 22 | local myWindow = Window.from(Texture, WIDTH, HEIGHT) 23 | 24 | if not myWindow.isOk then 25 | warn("Encountered Error,", myWindow:UnwrapErr()) 26 | return 27 | end 28 | 29 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 30 | local oldWindow: OSGL.Window = oldWindow:Unwrap() 31 | 32 | oldWindow:Clear(color.RED) 33 | unwrappedWindow:Buffer(oldWindow.buffer, oldWindow.width, oldWindow.height, 0, 0) 34 | unwrappedWindow:Render() -------------------------------------------------------------------------------- /tests/server/chunk/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/server/chunk/init.server.luau: -------------------------------------------------------------------------------- 1 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 2 | local OSGL = require(ReplicatedStorage.OSGL) 3 | local Window = OSGL.Window 4 | local color = OSGL.color 5 | 6 | local Block = Instance.new("Part") 7 | Block.Name = "WindowPart" 8 | Block.Parent = workspace 9 | Block.Anchored = true 10 | Block.Size = Vector3.new(5, 0, 5) 11 | Block.Position = Vector3.new(0, 5, 0) 12 | 13 | local Texture = Instance.new("Decal") 14 | Texture.Name = "WindowTexture" 15 | Texture.Face = Enum.NormalId.Top 16 | Texture.Parent = Block 17 | 18 | local WIDTH = 100 19 | local HEIGHT = 100 20 | 21 | local myWindow = Window.from(Texture, WIDTH, HEIGHT) 22 | 23 | if not myWindow.isOk then 24 | warn("Encountered Error,", myWindow:UnwrapErr()) 25 | return 26 | end 27 | 28 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 29 | unwrappedWindow:Destroy() 30 | 31 | -------------------------------------------------------------------------------- /tests/server/circle-overhead/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/server/circle-overhead/init.server.luau: -------------------------------------------------------------------------------- 1 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 2 | local OSGL = require(ReplicatedStorage.OSGL) 3 | local Window = OSGL.Window 4 | local color = OSGL.color 5 | 6 | local Block = Instance.new("Part") 7 | Block.Name = "WindowPart" 8 | Block.Parent = workspace 9 | Block.Anchored = true 10 | Block.Size = Vector3.new(5, 0, 5) 11 | Block.Position = Vector3.new(0, 5, 0) 12 | 13 | local Texture = Instance.new("Texture") 14 | Texture.Name = "WindowTexture" 15 | Texture.Face = Enum.NormalId.Top 16 | Texture.Parent = Block 17 | 18 | local WIDTH = 1024 19 | local HEIGHT = 1024 20 | 21 | local myWindow = Window.from(Texture, WIDTH, HEIGHT) 22 | 23 | if not myWindow.isOk then 24 | warn("Encountered Error,", myWindow:UnwrapErr()) 25 | return 26 | end 27 | 28 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 29 | 30 | while task.wait() do 31 | local col = color.fromColor3(Color3.fromHSV(tick() % 1, 1, 1)) 32 | 33 | -- Bad! 34 | unwrappedWindow:Circle(500, 500, 500, col) 35 | unwrappedWindow:Render() 36 | end -------------------------------------------------------------------------------- /tests/server/destroy-window/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/server/destroy-window/init.server.luau: -------------------------------------------------------------------------------- 1 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 2 | local OSGL = require(ReplicatedStorage.OSGL) 3 | local Window = OSGL.Window 4 | local color = OSGL.color 5 | 6 | local Block = Instance.new("Part") 7 | Block.Name = "WindowPart" 8 | Block.Parent = workspace 9 | Block.Anchored = true 10 | Block.Size = Vector3.new(5, 0, 5) 11 | Block.Position = Vector3.new(0, 5, 0) 12 | 13 | local Texture = Instance.new("Decal") 14 | Texture.Name = "WindowTexture" 15 | Texture.Face = Enum.NormalId.Top 16 | Texture.Parent = Block 17 | 18 | local WIDTH = 100 19 | local HEIGHT = 100 20 | 21 | local myWindow = Window.from(Texture, WIDTH, HEIGHT) 22 | 23 | if not myWindow.isOk then 24 | warn("Encountered Error,", myWindow:UnwrapErr()) 25 | return 26 | end 27 | 28 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 29 | print("Test started") 30 | 31 | for i = 1, 5 do 32 | print(`Window is destroyed in {5 - i} seconds, watch memory usage`) 33 | task.wait(1) 34 | end 35 | 36 | print("Window destroyed") 37 | unwrappedWindow:Destroy() -------------------------------------------------------------------------------- /tests/server/fake-mask-creation/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/server/fake-mask-creation/init.server.luau: -------------------------------------------------------------------------------- 1 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 2 | local OSGL = require(ReplicatedStorage.OSGL) 3 | local Bitmap = OSGL.Bitmap 4 | 5 | local WIDTH = 50 6 | local HEIGHT = 50 7 | 8 | local fakeBitmap = { 9 | version = "1.6.1", 10 | pixels = buffer.fromstring(string.rep("\255\127", WIDTH * HEIGHT)), 11 | width = WIDTH, 12 | height = HEIGHT, 13 | } 14 | 15 | local newBitmap = Bitmap.fromChecked(fakeBitmap) 16 | if not newBitmap.isOk then 17 | warn("Encountered Error,", newBitmap:UnwrapErr()) 18 | return 19 | end 20 | 21 | local unwrappedBitmap: OSGL.Bitmap = newBitmap:Unwrap() 22 | print(unwrappedBitmap:Read(0, 0)) 23 | print(unwrappedBitmap:Read(1, 0)) 24 | print(unwrappedBitmap:Read(0, 0, 2)) 25 | print(unwrappedBitmap:Read(1, 0, 2)) 26 | -------------------------------------------------------------------------------- /tests/server/fake-texture-serialization/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/server/fake-texture-serialization/init.server.luau: -------------------------------------------------------------------------------- 1 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 2 | local OSGL = require(ReplicatedStorage.OSGL) 3 | local Window = OSGL.Window 4 | local Texture = OSGL.Texture 5 | local color = OSGL.color 6 | 7 | local WIDTH = 500 8 | local HEIGHT = 500 9 | 10 | local oldWindow = Window.from(workspace.SpawnLocation.Decal, 50, 50) 11 | local myWindow = Window.from(workspace.SpawnLocation.Decal, WIDTH, HEIGHT) 12 | 13 | if not myWindow.isOk then 14 | warn("Encountered Error,", myWindow:UnwrapErr()) 15 | return 16 | end 17 | 18 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 19 | local unwrappedOld: OSGL.Window = oldWindow:Unwrap() 20 | unwrappedOld:Clear(color.RED) 21 | 22 | local fakeTexture = { 23 | version = "1.6.1", 24 | width = 50, 25 | height = 50, 26 | pixels = unwrappedOld.buffer 27 | } 28 | 29 | local bfr, w, h = Texture.serializeChecked(fakeTexture) 30 | print(bfr, w, h) 31 | -------------------------------------------------------------------------------- /tests/server/line-overhead/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/server/line-overhead/init.server.luau: -------------------------------------------------------------------------------- 1 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 2 | local OSGL = require(ReplicatedStorage.OSGL) 3 | local Window = OSGL.Window 4 | local color = OSGL.color 5 | 6 | local Block = Instance.new("Part") 7 | Block.Name = "WindowPart" 8 | Block.Parent = workspace 9 | Block.Anchored = true 10 | Block.Size = Vector3.new(5, 0, 5) 11 | Block.Position = Vector3.new(0, 5, 0) 12 | 13 | local Texture = Instance.new("Texture") 14 | Texture.Name = "WindowTexture" 15 | Texture.Face = Enum.NormalId.Top 16 | Texture.Parent = Block 17 | 18 | local WIDTH = 1024 19 | local HEIGHT = 1024 20 | 21 | local myWindow = Window.from(Texture, WIDTH, HEIGHT) 22 | 23 | if not myWindow.isOk then 24 | warn("Encountered Error,", myWindow:UnwrapErr()) 25 | return 26 | end 27 | 28 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 29 | 30 | local col = color.fromColor3(Color3.fromHSV(tick() % 1, 1, 1)) 31 | unwrappedWindow:Line(0, 0, 1023, 1023, 100, col) 32 | unwrappedWindow:Render() -------------------------------------------------------------------------------- /tests/server/mesh-support/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/server/mesh-support/init.server.luau: -------------------------------------------------------------------------------- 1 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 2 | local OSGL = require(ReplicatedStorage.OSGL) 3 | local Window = OSGL.Window 4 | local color = OSGL.color 5 | 6 | local Mesh = Instance.new("MeshPart") 7 | Mesh.Name = "WindowPart" 8 | Mesh.Parent = workspace 9 | Mesh.Anchored = true 10 | Mesh.Size = Vector3.new(5, 0, 5) 11 | Mesh.Position = Vector3.new(0, 5, 0) 12 | 13 | local WIDTH = 5 14 | local HEIGHT = 5 15 | 16 | local myWindow = Window.from(Mesh, WIDTH, HEIGHT) 17 | 18 | if not myWindow.isOk then 19 | warn("Encountered Error,", myWindow:UnwrapErr()) 20 | return 21 | end 22 | 23 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 24 | unwrappedWindow:Clear(color.RED) 25 | unwrappedWindow:Render() -------------------------------------------------------------------------------- /tests/server/pixel-overhead/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/server/pixel-overhead/init.server.luau: -------------------------------------------------------------------------------- 1 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 2 | local OSGL = require(ReplicatedStorage.OSGL) 3 | local Window = OSGL.Window 4 | local color = OSGL.color 5 | 6 | local Block = Instance.new("Part") 7 | Block.Name = "WindowPart" 8 | Block.Parent = workspace 9 | Block.Anchored = true 10 | Block.Size = Vector3.new(5, 0, 5) 11 | Block.Position = Vector3.new(0, 5, 0) 12 | 13 | local Texture = Instance.new("Texture") 14 | Texture.Name = "WindowTexture" 15 | Texture.Face = Enum.NormalId.Top 16 | Texture.Parent = Block 17 | 18 | local WIDTH = 1024 19 | local HEIGHT = 1024 20 | 21 | local myWindow = Window.from(Texture, WIDTH, HEIGHT) 22 | 23 | if not myWindow.isOk then 24 | warn("Encountered Error,", myWindow:UnwrapErr()) 25 | return 26 | end 27 | 28 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 29 | 30 | while task.wait() do 31 | local col = Color3.fromHSV(tick() % 1, 1, 1) 32 | unwrappedWindow:Clear(color.fromColor3(col)) 33 | unwrappedWindow:Render() 34 | end -------------------------------------------------------------------------------- /tests/server/rainbow-mass-draw/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/server/rainbow-mass-draw/init.server.luau: -------------------------------------------------------------------------------- 1 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 2 | local OSGL = require(ReplicatedStorage.OSGL) 3 | local Window = OSGL.Window 4 | local color = OSGL.color 5 | 6 | local Block = Instance.new("Part") 7 | Block.Name = "WindowPart" 8 | Block.Parent = workspace 9 | Block.Anchored = true 10 | Block.Size = Vector3.new(5, 0, 5) 11 | Block.Position = Vector3.new(0, 5, 0) 12 | 13 | local Surface = Instance.new("SurfaceGui") 14 | Surface.Name = "WindowTexture" 15 | Surface.Face = Enum.NormalId.Top 16 | Surface.Parent = Block 17 | 18 | local Texture = Instance.new("ImageLabel") 19 | Texture.Parent = Surface 20 | Texture.Name = "Texture" 21 | Texture.Size = UDim2.fromScale(1, 1) 22 | 23 | local WIDTH = 500 24 | local HEIGHT = 500 25 | 26 | local myWindow = Window.from(Texture, WIDTH, HEIGHT) 27 | 28 | if not myWindow.isOk then 29 | warn("Encountered Error,", myWindow:UnwrapErr()) 30 | return 31 | end 32 | 33 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 34 | 35 | while task.wait() do 36 | for y = 0, HEIGHT - 1 do 37 | for x = 0, WIDTH - 2 do 38 | local pixelColor = unwrappedWindow:ReadPixelUnchecked(x + 1, y) 39 | unwrappedWindow:Pixel(x, y, pixelColor) 40 | end 41 | end 42 | 43 | for y = 0, HEIGHT - 1 do 44 | local hue = (tick() * 0.1 + y / HEIGHT) % 1 45 | local col = Color3.fromHSV(hue, 1, 1) 46 | unwrappedWindow:Pixel(WIDTH - 1, y, color.fromColor3(col)) 47 | end 48 | 49 | unwrappedWindow:Render() 50 | end -------------------------------------------------------------------------------- /tests/server/rect-overhead/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/server/rect-overhead/init.server.luau: -------------------------------------------------------------------------------- 1 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 2 | local OSGL = require(ReplicatedStorage.OSGL) 3 | local Window = OSGL.Window 4 | local color = OSGL.color 5 | 6 | local Block = Instance.new("Part") 7 | Block.Name = "WindowPart" 8 | Block.Parent = workspace 9 | Block.Anchored = true 10 | Block.Size = Vector3.new(5, 0, 5) 11 | Block.Position = Vector3.new(0, 5, 0) 12 | 13 | local Texture = Instance.new("Texture") 14 | Texture.Name = "WindowTexture" 15 | Texture.Face = Enum.NormalId.Top 16 | Texture.Parent = Block 17 | 18 | local WIDTH = 1024 19 | local HEIGHT = 1024 20 | 21 | local myWindow = Window.from(Texture, WIDTH, HEIGHT) 22 | 23 | if not myWindow.isOk then 24 | warn("Encountered Error,", myWindow:UnwrapErr()) 25 | return 26 | end 27 | 28 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 29 | 30 | local i = 0 31 | while task.wait() do 32 | i += 1 33 | local col = color.fromColor3(Color3.fromHSV(tick() % 1, 1, 1)) 34 | unwrappedWindow:Rectangle(500, 500, 250, 250, col, nil, nil, i) 35 | unwrappedWindow:Render() 36 | unwrappedWindow:Clear() 37 | end -------------------------------------------------------------------------------- /tests/server/resize-overhead/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/server/resize-overhead/init.server.luau: -------------------------------------------------------------------------------- 1 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 2 | local OSGL = require(ReplicatedStorage.OSGL) 3 | local Window = OSGL.Window 4 | local color = OSGL.color 5 | 6 | local Block = Instance.new("Part") 7 | Block.Name = "WindowPart" 8 | Block.Parent = workspace 9 | Block.Anchored = true 10 | Block.Size = Vector3.new(5, 0, 5) 11 | Block.Position = Vector3.new(0, 5, 0) 12 | 13 | local Texture = Instance.new("Texture") 14 | Texture.Name = "WindowTexture" 15 | Texture.Face = Enum.NormalId.Top 16 | Texture.Parent = Block 17 | 18 | local WIDTH = 1024 19 | local HEIGHT = 1024 20 | 21 | local myWindow = Window.from(Texture, WIDTH, HEIGHT) 22 | 23 | if not myWindow.isOk then 24 | warn("Encountered Error,", myWindow:UnwrapErr()) 25 | return 26 | end 27 | 28 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 29 | unwrappedWindow:Clear(color.RED) 30 | unwrappedWindow:Resize(1, 1) 31 | unwrappedWindow:Render() -------------------------------------------------------------------------------- /tests/server/texture-asset-id/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/server/texture-asset-id/init.server.luau: -------------------------------------------------------------------------------- 1 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 2 | local OSGL = require(ReplicatedStorage.OSGL) 3 | local Window = OSGL.Window 4 | local Texture = OSGL.Texture 5 | 6 | local Block = Instance.new("Part") 7 | Block.Name = "WindowPart" 8 | Block.Parent = workspace 9 | Block.Anchored = true 10 | Block.Size = Vector3.new(5, 0, 5) 11 | Block.Position = Vector3.new(0, 5, 0) 12 | 13 | local Decal = Instance.new("Decal") 14 | Decal.Name = "WindowTexture" 15 | Decal.Face = Enum.NormalId.Top 16 | Decal.Parent = Block 17 | 18 | local loadedTexture = Texture.fromAssetId(17544804292):Unwrap() 19 | 20 | local WIDTH = loadedTexture.width 21 | local HEIGHT = loadedTexture.height 22 | 23 | local myWindow = Window.from(Decal, WIDTH, HEIGHT) 24 | 25 | if not myWindow.isOk then 26 | warn("Encountered Error,", myWindow:UnwrapErr()) 27 | return 28 | end 29 | 30 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 31 | Texture.drawChecked(unwrappedWindow, loadedTexture, 0, 0) 32 | unwrappedWindow:Render() -------------------------------------------------------------------------------- /tests/server/texture-draw/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/server/texture-draw/init.server.luau: -------------------------------------------------------------------------------- 1 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 2 | local OSGL = require(ReplicatedStorage.OSGL) 3 | local Window = OSGL.Window 4 | local Texture = OSGL.Texture 5 | 6 | local Block = Instance.new("Part") 7 | Block.Name = "WindowPart" 8 | Block.Parent = workspace 9 | Block.Anchored = true 10 | Block.Size = Vector3.new(5, 0, 5) 11 | Block.Position = Vector3.new(0, 5, 0) 12 | 13 | local Decal = Instance.new("Decal") 14 | Decal.Name = "WindowTexture" 15 | Decal.Face = Enum.NormalId.Top 16 | Decal.Parent = Block 17 | 18 | local WIDTH = 50 19 | local HEIGHT = 50 20 | 21 | local textureObject = script.RobloxLogo 22 | local textureCopy = script.RobloxLogo2 23 | 24 | local myWindow = Window.from(Decal, WIDTH, HEIGHT) 25 | 26 | if not myWindow.isOk then 27 | warn("Encountered Error,", myWindow:UnwrapErr()) 28 | return 29 | end 30 | 31 | local ITERATIONS = 256 32 | 33 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 34 | 35 | local uncheckedAvg = 0 36 | for _ = 1, ITERATIONS do 37 | local start = os.clock() 38 | local txt = Texture.fromUnchecked(require(textureObject)) 39 | Texture.drawUnchecked(unwrappedWindow, txt, 0, 0) 40 | unwrappedWindow:Render() 41 | uncheckedAvg += os.clock() - start 42 | end 43 | 44 | local checkedAvg = 0 45 | for _ = 1, ITERATIONS do 46 | local start = os.clock() 47 | local txt = Texture.fromChecked(textureCopy):Unwrap() 48 | Texture.drawChecked(unwrappedWindow, txt, 0, 0) 49 | unwrappedWindow:Render() 50 | checkedAvg += os.clock() - start 51 | end 52 | 53 | checkedAvg /= ITERATIONS 54 | uncheckedAvg /= ITERATIONS 55 | 56 | warn(`Checked avg: {checkedAvg}s`) 57 | warn(`Unchecked avg: {uncheckedAvg}s`) -------------------------------------------------------------------------------- /tests/server/texture-instantiation/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/server/texture-instantiation/init.server.luau: -------------------------------------------------------------------------------- 1 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 2 | local OSGL = require(ReplicatedStorage.OSGL) 3 | local Window = OSGL.Window 4 | local Texture = OSGL.Texture 5 | local color = OSGL.color 6 | 7 | local Block = Instance.new("Part") 8 | Block.Name = "WindowPart" 9 | Block.Parent = workspace 10 | Block.Anchored = true 11 | Block.Size = Vector3.new(5, 0, 5) 12 | Block.Position = Vector3.new(0, 5, 0) 13 | 14 | local Decal = Instance.new("Decal") 15 | Decal.Name = "WindowTexture" 16 | Decal.Face = Enum.NormalId.Top 17 | Decal.Parent = Block 18 | 19 | local WIDTH = 50 20 | local HEIGHT = 50 21 | 22 | local myWindow = Window.from(Decal, WIDTH, HEIGHT) 23 | 24 | if not myWindow.isOk then 25 | warn("Encountered Error,", myWindow:UnwrapErr()) 26 | return 27 | end 28 | 29 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 30 | local newTexture = Texture.new(WIDTH, HEIGHT) 31 | newTexture:Clear(color.RED) 32 | Texture.drawChecked(unwrappedWindow, newTexture, 0, 0) 33 | unwrappedWindow:Render() -------------------------------------------------------------------------------- /tests/server/texture-rotation/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/server/texture-rotation/init.server.luau: -------------------------------------------------------------------------------- 1 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 2 | local OSGL = require(ReplicatedStorage.OSGL) 3 | local Window = OSGL.Window 4 | local Texture = OSGL.Texture 5 | 6 | local Block = Instance.new("Part") 7 | Block.Name = "WindowPart" 8 | Block.Parent = workspace 9 | Block.Anchored = true 10 | Block.Size = Vector3.new(5, 0, 5) 11 | Block.Position = Vector3.new(0, 5, 0) 12 | 13 | local Decal = Instance.new("Decal") 14 | Decal.Name = "WindowTexture" 15 | Decal.Face = Enum.NormalId.Top 16 | Decal.Parent = Block 17 | 18 | local WIDTH = 50 19 | local HEIGHT = 50 20 | 21 | local textureObject = script.RobloxLogo 22 | local myWindow = Window.from(Decal, WIDTH, HEIGHT) 23 | 24 | if not myWindow.isOk then 25 | warn("Encountered Error,", myWindow:UnwrapErr()) 26 | return 27 | end 28 | 29 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 30 | 31 | local r = 0 32 | local txt = Texture.fromChecked(textureObject):Unwrap() 33 | while task.wait() do 34 | r += 1 35 | Texture.drawUnchecked(unwrappedWindow, txt, 0, 0, r) 36 | unwrappedWindow:Render() 37 | unwrappedWindow:Clear() 38 | end -------------------------------------------------------------------------------- /tests/server/triangle-overhead/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/server/triangle-overhead/init.server.luau: -------------------------------------------------------------------------------- 1 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 2 | local OSGL = require(ReplicatedStorage.OSGL) 3 | local Window = OSGL.Window 4 | local color = OSGL.color 5 | 6 | local Block = Instance.new("Part") 7 | Block.Name = "WindowPart" 8 | Block.Parent = workspace 9 | Block.Anchored = true 10 | Block.Size = Vector3.new(5, 0, 5) 11 | Block.Position = Vector3.new(0, 5, 0) 12 | 13 | local Texture = Instance.new("Texture") 14 | Texture.Name = "WindowTexture" 15 | Texture.Face = Enum.NormalId.Top 16 | Texture.Parent = Block 17 | 18 | local WIDTH = 1024 19 | local HEIGHT = 1024 20 | 21 | local myWindow = Window.from(Texture, WIDTH, HEIGHT) 22 | 23 | if not myWindow.isOk then 24 | warn("Encountered Error,", myWindow:UnwrapErr()) 25 | return 26 | end 27 | 28 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 29 | 30 | while task.wait() do 31 | local col = color.fromColor3(Color3.fromHSV(tick() % 1, 1, 1)) 32 | unwrappedWindow:Triangle(0,0, 500, 500, 500, 0, col) 33 | unwrappedWindow:Render() 34 | end -------------------------------------------------------------------------------- /tests/server/window-bootstrap/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/server/window-bootstrap/init.server.luau: -------------------------------------------------------------------------------- 1 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 2 | local OSGL = require(ReplicatedStorage.OSGL) 3 | local Window = OSGL.Window 4 | 5 | local Block = Instance.new("Part") 6 | Block.Name = "WindowPart" 7 | Block.Parent = workspace 8 | Block.Anchored = true 9 | Block.Size = Vector3.new(5, 0, 5) 10 | Block.Position = Vector3.new(0, 5, 0) 11 | 12 | local Texture = Instance.new("Texture") 13 | Texture.Name = "WindowTexture" 14 | Texture.Face = Enum.NormalId.Top 15 | Texture.Parent = Block 16 | 17 | local WIDTH = 5 18 | local HEIGHT = 5 19 | 20 | local myWindow = Window.from(Texture, WIDTH, HEIGHT) 21 | 22 | if not myWindow.isOk then 23 | warn("Encountered Error,", myWindow:UnwrapErr()) 24 | return 25 | end 26 | 27 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 28 | assert(unwrappedWindow.width == WIDTH, "Test failed! (1)") 29 | assert(unwrappedWindow.height == HEIGHT, "Test failed! (2)") 30 | assert(buffer.len(unwrappedWindow.buffer) == WIDTH * HEIGHT * 4, "Test failed! (3)") 31 | 32 | warn("All tests (3) passed!") -------------------------------------------------------------------------------- /tests/server/window-resample/init.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "Disabled": true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/server/window-resample/init.server.luau: -------------------------------------------------------------------------------- 1 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 2 | local OSGL = require(ReplicatedStorage.OSGL) 3 | local Window = OSGL.Window 4 | local color = OSGL.color 5 | 6 | local Block = Instance.new("Part") 7 | Block.Name = "WindowPart" 8 | Block.Parent = workspace 9 | Block.Anchored = true 10 | Block.Size = Vector3.new(5, 0, 5) 11 | Block.Position = Vector3.new(0, 5, 0) 12 | 13 | local Texture = Instance.new("Decal") 14 | Texture.Name = "WindowTexture" 15 | Texture.Face = Enum.NormalId.Top 16 | Texture.Parent = Block 17 | 18 | local WIDTH = 100 19 | local HEIGHT = 100 20 | 21 | local myWindow = Window.from(Texture, WIDTH, HEIGHT) 22 | 23 | if not myWindow.isOk then 24 | warn("Encountered Error,", myWindow:UnwrapErr()) 25 | return 26 | end 27 | 28 | local unwrappedWindow: OSGL.Window = myWindow:Unwrap() 29 | 30 | unwrappedWindow:Resample(2) 31 | unwrappedWindow:Render() 32 | -------------------------------------------------------------------------------- /wally.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OSGL", 3 | "tree": { 4 | "$path": "src" 5 | } 6 | } 7 | --------------------------------------------------------------------------------