├── .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 |
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 the Video
.
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 Texture
s.
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 |
--------------------------------------------------------------------------------