Could you ask me how to load image data into canvas? I keep getting errors.
if savedData and next(savedData) then
print("loading...")
local decompressedImageData = CanvasDraw.DecompressImageData(savedData)
CanvasDraw:GetImageData(decompressedImageData)
end
You should probably read the API documentation in cases like these as you are using GetImageData incorrectly
CanvasDraw.GetImageData is used for loading ImageData from save object instances. However the use of it is still useless here as you already have image data (decompressedImageData)
What you want is Canvas:DrawImage(decompressedImageData)
Fonts can now created with a bitmap font image that I import via a script of a selected bitmap font sheet image.
For an example, here’s the code page 437 font sheet I used:
NOTE: Currently, there isn’t a way for other users to create custom fonts. However, if there is enough demand, I will create a font creator for the CanvasDraw Tools plugin that will allow you to create custom fonts for CanvasDraw via PNG images of bitmap font sheets.
This update adds a new font parameter to the DrawText and DrawTextXY methods:
-- Draw black 'Hello, World!' text at point 20x20 with the Codepage font
local Text = "Hello, World!"
Canvas:DrawTextXY(Text, 20, 20, Color3.new(0, 0, 0), "Codepage")
All useable fonts can be found under the CanvasDraw module in a folder called Fonts:
Here’s the full list of changes:
Added FontName parameter to Canvas:DrawText() and Canvas:DrawTextXY()
Removed Canvas.FpsLimit as it had some serious performance issues and never seemed to perform reliably. You will have to come up with your own way to limit auto render rates
It uses a modified CanvasDraw because it doesn’t provide everything I needed. Here’s the things I added for my version:
Canvas:GetGrid(): {number}
Canvas:SetGrid(grid: {number})
Canvas:IsDestroyed()
I need to get and set the raw grid to quickly save the result of each render. Using GetImageData() and :GetPixels() failed because of how long it took, or because it couldn’t be reused without other modifications.
The main problem with this is the memory usage. My memory fills up rapidly at the maximum 1024x1024 quality, so I made it so you could split the recording up. It took 26 videos and a few hours of time to create this video!
This is a different canvas, because you cannot dynamically change the resolution using a single canvas instance! When one is not visible, it is destroyed. While I’m rendering, if the canvas is destroyed, it will fail when trying to set pixels. Since there was no way to tell if it was destroyed so I could cancel the render, I had to add that method. It simply returns not Grid, because Grid gets set to nil when destroyed.
Preferably, each character could be indexed automatically with it’s ASCII code
Now, each font could have it’s own bitmap data array (in ascii order), and return other functions along with that can return/find the relative character data, etc.
Well, the main reason why i did this is so i didn’t have to rewrite my DrawText() function when I added the fonts.
The other reason is simplicity. This is easy to modify and can be arranged in a way so you can somewhat visualise each character’s pixels too.
Also I didn’t really see how binary numerals can give me an advantage, so I tend to not use them often. I’d love to know why this might be better though!
How would one achieve this? Because I have realised that some characters cannot be used to index my character sheet, meaning i cant use most special characters
This might be a stupid question, I’m trying to make a drawing system with this module but I’m quite confused on how to make an Undo and Redo function? I appreciate if you could help me.
Yeah exactly what i was trying to do but i don’t know how to get the last stroke of frames the player did with their mouse, I looked over the module to get the frames but still couldn’t find anything
Using binary numerals would be as intuitive as using nested tables, but more efficient:
Instead of having {{0,0,1,0,1},{0,1,0,0,1}}, you’d have {0b00101, 0b01001}
You can then get the pixel value by using bitwise functions (I’d me more than glad to help over discord, but here’s how I did it on a 5x8 20x4 LCD with the original hitachi bitmap: Dot Matrix LCD in Roblox! (with hd44780 bitmaps))
Creating an undo system is relatively easy to do. One way to do this is to store multiple copies of the canvas as you draw your image (i.e, upon every mouse button click, save the current state of our image)
You can do this by using a table
local UndoFrames = {}
local MaxUndoFrames = 32
local function AddToUndoHistory()
-- Get all current pixels on the canvas
local Pixels = {}
for Y = 1, Resolution.Y do
for X = 1, Resolution.X do
table.insert(Pixels, Canvas:GetPixelXY(X, Y))
end
end
-- Avoid excessive memory consumption
if #UndoFrames >= MaxUndoFrames then
table.remove(UndoFrames, 1)
end
table.insert(UndoFrames, Pixels)
end
local function Undo()
-- Draw and remove the previous state of the canvas
local LastFrame = UndoFrames[#UndoFrames]
if LastFrame then
for X = 1, Resolution.X do
for Y = 1, Resolution.Y do
Canvas:SetPixel(X, Y, LastFrame[X + (Y - 1) * Resolution.X]) -- Formula for getting a 1D position from 2D coordinates
end
end
table.remove(UndoFrames, #UndoFrames)
end
end
In this example, I am saving to an array of a size of 32 images max every time I press the mouse button, and pressing Z to undo:
Full code
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 = 1
-- Main
local MouseDown = false
local LastMousePoint
local Resolution = Vector2.new(158, 128)
local Canvas = CanvasDraw.new(Frame, Resolution)
local UndoFrames = {}
local MaxUndoFrames = 32
local function AddToUndoHistory()
-- Get all current pixels on the canvas
local Pixels = {}
for Y = 1, Resolution.Y do
for X = 1, Resolution.X do
table.insert(Pixels, Canvas:GetPixelXY(X, Y))
end
end
if #UndoFrames >= MaxUndoFrames then
table.remove(UndoFrames, 1)
end
table.insert(UndoFrames, Pixels)
end
local function Undo()
local LastFrame = UndoFrames[#UndoFrames]
if LastFrame then
for X = 1, Resolution.X do
for Y = 1, Resolution.Y do
Canvas:SetPixel(X, Y, LastFrame[X + (Y - 1) * Resolution.X])
end
end
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
--MousePoint += Vector2.new(25, 25)
-- 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
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()
-- For those who like dots
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)
AddToUndoHistory() -- Initalise
Hey all. We have some new and very useful additions that a couple of you guys have suggested in the past.
Canvas:Resize()
Yes, that’s right! We can now dynamically resize the canvas without having to destroy and create a new one.
NOTE: Currently, contents of the canvas do not rescale, and instead clears.
You will have to redraw and render the image after clearing for a clean transition.
Canvas:SetGrid() / Canvas:GetGrid()
These new methods were honestly quite overdue. These are very useful for when you want to do per pixel processing or copying/pasting of pixel data
Full list of changes:
Added Canvas:SetGrid()
Added Canvas:GetGrid()
Added Canvas:Resize()
Optimised font storage and rendering for Canvas:DrawText() and Canvas:DrawTextXY()
Fixed Canvas:GetPixels() not working correctly on aspect ratios that aren’t 1:1
Special thanks to: @bluebxrrybot and @Lxi099 for contributing some of these ideas
EDIT:
I noticed most of the characters weren’t being displayed. This is due to the string.split(TextLine, "") in the Characters definition on the DrawTextXY script. You have to replace Characters with this; which adds support for multi-byte utf8 graphemes (like the smiley faces, greek letters and blocks)
local Characters = {}
for _, c in utf8.codes(TextLine) do
table.insert(Characters, utf8.char(c))
end