that’s weird, i think I might know why that is possibly happening. Ill have a look at it later
Made a real-time renderer with this module, working at 50-60 fps (resolution 1000*1000), very good module, I hope you update’ll it till perfection!
Can you provide a repro or a code snippet? Are your X and Y values for the line whole numbers?
Yeah that the X and Y values not being whole numbers might have been the problem sorry for the inconvenience
Module Small Patch - v4.9.3.b
-
Canvas:DrawLineXY()
now has coordinate rounding -
Fixed some small typos in the documentation within the module
CanvasDraw4.9.3.b.rbxm (68.5 KB)
I have a question, how do I send ImageData from the client to the server using a RemoteEvent? I’m sorry if this question is dumb
Did you create all this within studio?
You may be interested in moving your code to GitHub and possibly using moonwave. If you do, you’ll be able to make it open source so others could contribute, and with moonwave, be able to have a website and easy documentation for it (there’s a lot more potential benefits to moving the code to an external code base). It’s a bit hard/complex to setup, but the effort would be well worth it.
CanvasDraw 4.10.0 - Full Release!
It’s finally happened. CanvasDraw 4.0 can now be used in live published experiences!
However, roblox has announced that EditableImage has certain restrictions to prevent misuse.
In order to use CanvasDraw 4.0, you must be:
- 13+
- ID-verified
- explicitly opted-in to using the API in published experiences
You can read more about these new EditableImage changes and limitations here:
Full changelog:
-
CanvasDraw 4.0 full release (Out of beta)
-
Improved
CanvasDraw.CompressImageData()
andCanvasDraw.DecompressImageData()
to use purely buffers instead as they are smaller and faster than string compression.- You should be able to store a single high detailed 1024x1024 image on a single datastore key as the new CompressedImageData will not go any higher than 2.5 MB. Simpler images with less detail and colour will be much lower in size.
CanvasDraw4.10.0.rbxm (68.5 KB)
Question, how come there’s no option to preset the canvas to be transparent? Unless it would make the canvas just be completely transparent Even with the image data being loaded.
It’s just never been suggested or contributed by anyone before. You can make a canvas transparent like so:
Canvas:SetClearRGBA(0, 0, 0, 0)
Canvas:Clear()
As for drawing things on a transparent canvas, there currently isn’t any good automatic support for that yet, ill be sure to see if i can add better support for that soon.
In the mean time, you can interate through specific pixels and use Canvas:SetAlpha() to set the transparency accordingly
To be honest, if I made something as good as this my ego would be also as high.
people might see this as useless but i think it’s very useful if you wanna make a game from scratch while still using luau
I tried using those two functions before but all it did was make the canvas completely invisible with the Save image loaded in.
But I managed to get something by using the Canvas:FloodFillXY()
While still keeping the Save image visible. Minus the remains of the color from the canvas that’s surrounding the image (The red crust)
hey is it optional to not use editableimages and use the pixel render instead? i prefer the pixel render where it created pixel frames.
Yeah, you can go to the CanvasDraw archive here:
Not really sure what you mean here. There are many ways to update pixels in bulk.
Some examples;
-- Fill a certain region of pixels
for X = 50, 100 do
for Y = 50, 100 do
Canvas:SetRGB(X, Y, R, G, B)
end
end
-- Draw an image
Canvas:DrawImage(ImageData)
-- Draw using an array of pixels
Canvas:SetGrid(PixelArray)
-- Draw using a buffer
Canvas:SetBuffer(PixelBuffer)
This is pretty common practice in most rendering APIs and game engines
local ImageData = CanvasDraw.GetImageData(Textures["BrickWall1.png"])
-- OR
local ImageData = CanvasDraw.GetImageDataFromTextureId("rbxassetid://12345678")
local R, G, B = ImageData:GetRGB(X, Y)
It was originally based off of a simple C++ pixel engine. It’s intended to be very simple to understand and use and it just consists of using X and Y values.
the API is intended to as fast as possible as well, and this was designed with that in mind.
Should be basically 1:1 for Canvas:SetBuffer()
One thing to note, is that drawing or setting the grid, doesn’t directly apply it to the EditableImage immediately.
For an example, using :SetRGB, or :DrawCircle, etc, will simply just draw to an internal buffer, by default, for every heartbeat, the pixels are then rendered to the EditableImage.
Depending on what you use the canvas for, you may only want to update the EditableImage only when necessary;
Canvas.AutoRender = false -- Disable auto rendering
-- Drawing stuffs:
Canvas:DrawCircle(...) -- Only set's the pixels nessecary.
-- Manually render the canvas
-- (which internally calls EditableImage:WritePixelBuffer)
Canvas:Render()
From my testing, the memory usage was fine, at least for lower resolutions. You can convert the numbers to buffers, which is much more memory efficient and then you could easily store like 50 or more snapshots of a high resolution just fine, I don’t think 50 frames will go over 200 MB if you use unsigned integer values. Simpler drawings will be much lower however, as low as 20 MB even. It all depends on image detail as roblox buffers are already compressed.
I could provide an example for you if you wish!
From personal testing, i have never had a performance issue with drawing a circle or other shapes. The code is quite well optimised. Optimsing has been something I have been doing for more than 2 years on this project, so it should be almost just as fast!
The main reason why this doesn’t use the EditableImage drawing functions is simply because it isn’t directly compatible with this workflow. I can’t store a drawn circle in memory before manually rendering it to the canvas.
Another big reason, is control. With CanvasDraw, you get way more control of how you draw things.
For an example, with a circle, you can either draw a filled circle, or a circle outline, which EditableImage cannot do.
I don’t even think EditableImage can do lines with thicknesses.
Also another thing to note, is that this module is 3 years old. It was designed before EditableImage came out, so that’s another reason why you don’t see use of EditableImage shapes or drawing methods.
:GetPixels() is mainly just a leftover from the early 2023 version, it really isn’t that performant and shouldn’t really be used. I might either rewrite it or deprecate it. It’s mostly just there for backwards compatibility.
I can understand that, to be honest around 20% of the content on this module is here mainly for backwards compatibility with older projects using CanvasDraw. There are games, even a couple games with 500+ players that rely on the older CanvasDraw API that was made before EditableImage.
Well, despite the differences between how other people code, this still runs really great, and I haven’t seen any other roblox graphics library attempts do better. Here is an example of a 3D raycaster engine running around up to 128 fps at 256x256
implementation for what sorry? is there a particular method you’d like? I can try my best to help out. I’m also open for changes, ideas and additions to help improve the module in any way.
Here is an optimised version of my undo code example. Works completely lag free at 1024x1024.
Memory usage only went up as high as 0.29 GB on my machine for 50 saved frames using buffers, which is quite impressive. You could improve upon memory usage even further with a different approach, such as saving the strokes rather than the whole canvas, but then you’ll have to account for other possible advanced brush tools, such as shapes, flood fill, etc. This is the simplest example which works well.
This newer and more optimized example avoids iteration;
FPS seems to be unaffected as well.
local Players = game:GetService("Players")
local LocalPlr = Players.LocalPlayer
local Gui = script.Parent
local Frame = Gui:WaitForChild("CanvasFrame")
local CanvasDraw = require(Gui:WaitForChild("CanvasDraw"))
local Mouse = LocalPlr:GetMouse()
local DrawColour = Color3.new(0, 0, 0)
local Radius = 3
-- Main
local MouseDown = false
local LastMousePoint
local Resolution = Vector2.new(1024, 1024)
local ResX, ResY = Resolution.X, Resolution.Y
local Canvas = CanvasDraw.new(Frame, Resolution)
local Canvas2 = CanvasDraw.new(Gui:WaitForChild("CanvasFrame2"), Resolution)
Canvas2:SetClearRGBA(1, 1, 1, 0)
local UndoFrames = {}
local MaxUndoFrames = 50
local function AddToUndoHistory()
-- Get all current pixels on the canvas
local FramePixels = Canvas:GetBuffer()
if #UndoFrames >= MaxUndoFrames then
table.remove(UndoFrames, 1)
end
table.insert(UndoFrames, FramePixels)
end
local function Undo()
local LastFrame = UndoFrames[#UndoFrames]
if LastFrame then
Canvas:SetBuffer(LastFrame)
table.remove(UndoFrames, #UndoFrames)
end
end
Mouse.Move:Connect(function()
local MousePoint = Canvas:GetMousePoint()
-- Check if we have a valid point on the canvas and the mouse button is being held down
if MousePoint and MouseDown then
-- Draw a line between the last mouse point and the current mouse point to avoid gaps from dragging the mouse too quickly
if LastMousePoint then
Canvas:DrawLine(LastMousePoint, MousePoint, DrawColour, Radius)
end
LastMousePoint = MousePoint
end
-- Draw cursor
if MousePoint then
Canvas2:Clear()
Canvas2:SetAlpha(MousePoint.X, MousePoint.Y, 1)
Canvas2:SetRGB(MousePoint.X, MousePoint.Y, 1, 0, 0)
end
end)
Mouse.Button1Down:Connect(function()
MouseDown = true
AddToUndoHistory()
local MousePoint = Canvas:GetMousePoint()
-- For those who like dots
if MousePoint then
LastMousePoint = MousePoint
Canvas:DrawCircle(MousePoint, Radius, DrawColour)
end
end)
Mouse.Button2Down:Connect(function()
AddToUndoHistory()
local MousePoint = Canvas:GetMousePoint()
-- Le flood fill
if MousePoint then
Canvas:FloodFillXY(MousePoint.X, MousePoint.Y, DrawColour)
end
end)
Mouse.Button1Up:Connect(function()
LastMousePoint = nil
MouseDown = false
end)
-- Undo
game.UserInputService.InputBegan:Connect(function(Inp)
if Inp.KeyCode == Enum.KeyCode.Z then
Undo()
end
end)
Module and Plugin Update/Patch - v4.11.0
Module changes:
-
Added error handling for situations where an EditableImage may fail to create incase of a memory limit being hit
-
Optimised Canvas:FloodFill()
-
Fixed a possible memory leak issue from destroying, resizing and recreating canvases
-
Deprecated the following methods:
Canvas:GetPixels()
Canvas:ClearPixels()
Canvas:FillPixels()
Plugin changes:
- Added an Import from TextureId option for the Image Editor