CanvasDraw - A powerful pixel-based graphics engine (Draw pixels, lines, triangles, read/modify image data, and much more!)

Yoo thats amazing! You got a discord?

Patch Update - v3.0.1

This small updates contains some minor changes and bug fixes to CanvasDraw.

Here’s a small list of all the noticable changes:

  • Slightly optimised the DrawPixel() and SetPixel() methods
  • Slightly optimised the canvas rendering procedure
  • Small bug fixes (I kinda forgot what I fixed, but oh well)
  • Fixed some errors on the API documentations

This thread has also been updated a bit too

2 Likes

Question, how can you read PNG image data?

1 Like

You can read PNG image data by selecting a PNG image file on your computer via the plugin I have made for this module

And then you can load the ImageData from the imported image object instance with CanvasDraw via the .GetImageDataFromSaveObject() method, and afterwards you can use the :GetPixel() method to grab a pixel colour and alpha value from your image from a desired point.

Great! Well, how does it work tho?
How do you manage to import the File and get the binary contents from it, and decode it?

I tried using a PNG decoder, ZLIB Deflat, but i couldnt manage to do anything with the data?..

Hello, is there a way to increase the size of a pixel when using Canvas:DrawLine? I’m making a simple Draw GUI and I would really love being able to resize my pixel.

1 Like

Well, roblox has only one possible way to get any form of data from image files, and just files overall, and thats with the :GetBinaryContents() method in the file class which returns compressed code as a string, which is normally useless to most roblox devs.

But good ol @Maximum_ADHD here made a PNG module which translates any compressed PNG data, into readable roblox data with color3 values and alpha values too.

I have no idea how any of this works, but this guy obviously knows how to encode and decode files.


Now to get this to work in game for texture sampling, I had invent a custom image class which stores all the colours and alpha values created from my plugin. To do this, I used a folder instance which takes advantage of instance attributes to store the data.

All though normally there would be a problem with this… Storing a whole table of pixel colours in a string would be a massive file size, and also roblox has a string length limit too…

To get around this, I used a custom string compressor modules which reduces the file size and string length to at least about 80%


So now we have can store custom image instances in our games! From there we just need to decode and decompress the attributes of the custom image instance, and then from there, we have all the Color3 and alpha values of every pixel in our image!


I don’t think I have ever seen anyone try to attempt anything like this before, but it is super efficient and easy to use with CanvasDraw!

-- Get the save object and it's data
local SaveObject = workspace.Saves.MyFirstDrawing -- Path to the save object
local ImageData = CanvasDraw.GetImageDataFromSaveObject(SaveObject) -- Decompresses the attributes into lua tables

-- ^ this function is meant to be called only once, as it is a bit expensive ^

---------------------------------------------------------

-- how ever this method is extremely fast and can be called 
-- 10th of thousands of times every frame without effecting frame rate at all!
--   v           v           v

local PixelColour, PixelAlpha = ImageData:GetPixel(Vector2.new(5, 5))

-- Alternative non-vector constructing method (super fast)
local PixelColour, PixelAlpha = ImageData:GetPixelXY(5, 5)

2 Likes

Thanks! This is such a nice thing to learn

1 Like

Hello! Currently CanvasDraw does not have it’s own line thickness parameter in the DrawLine method.

But, you can create your own line with variable thicknesses by using the DrawCircle method with it.


We can take advantage of the fact that the DrawLine method will return an array of every pixel point that the line has used to be drawn, and draw a filled circle at those points to create a thicker line

local LineColour = Color3.new(1, 0, 0)
local LineThickness = 3

local LinePoints = Canvas:DrawLine(PointA, PointB, LineColour) -- Draw the initial line and returns it's points

for i, Point in pairs(LinePoints) do -- Loop thorugh every pixel point used, and draw a filled circle there
    Canvas:DrawCircle(Point, LineThickness, LineColour)
end

Hopefully we will see a proper thickness parameter in the DrawLine function, but for now this is the best way to do it without creating a new algorithm to draw thick lines.

1 Like

Thank you very much for this! Didn’t think of doing that.

1 Like

Super Fast Image Importing Update - v3.1.0

This update contains some massive needed optimizations and speed increase to importing PNG images with the CanvasDraw Image Importer plugin.

Before, creating a SaveObject from a large 256x256 PNG image, used to take up to 40 seconds, which is really slow, especially for those who like to mass import movie/GIF frames.

But the pain is no longer as importing a PNG image as large as 256x256 can now take 1 second or less to create!

That is 40 times faster than before!

Mass importing large PNG images is now a much more enjoyable task and not painful.


The same update has also been applied to the module’s :CreateSaveObject() method which internally uses the same code.


@JojogamezYT
I know this is a bit old, but hopefully this clears things up a bit now!

2 Likes

Mind telling what was the method used to boost the upload speed? is it luau parallel? Multithreading?

1 Like

Nope. None of that was used at all. It’s just that my method for converting Color3 values into strings with string concatenations (the ‘..’ operator) was incredibly slow. So instead, I used table concatenations with some basic optimsations, and it is much faster.

Previous method

-- This is incredibly slow
local function ConvertColoursToListString(Colours)
	local ColoursListString = ""

	for i, Colour in pairs(Colours) do
		local ColourR = RoundN(Colour.R * 255)
		local ColourG = RoundN(Colour.G * 255)
		local ColourB = RoundN(Colour.B * 255)

        -- Real performance killer here
		local StringSegment = tostring(ColourR) .. "," .. tostring(ColourG) .. "," .. tostring(ColourB)
		ColoursListString = ColoursListString .. StringSegment
		if i ~= #Colours then
			ColoursListString = ColoursListString .. "S"
		end


	end

	return ColoursListString
end

New method

-- This is incredibly fast
local function ConvertColoursToListString(Colours)
	local ColoursListTable = {}

	for i, Colour in ipairs(Colours) do
		if i == 1 and i ~= #Colours then
			TableInsert(ColoursListTable, RoundN(Colour.R * 255))
		end

		TableInsert(ColoursListTable, RoundN(Colour.G * 255))
		TableInsert(ColoursListTable, tostring(RoundN(Colour.B * 255))..(i ~= #Colours and "S"..tostring(RoundN(Colours[i + 1].R * 255)) or ""))
	end

	return table.concat(ColoursListTable, ",") -- Much faster than using the .. operator
end

Now that you have brought up multithreading and parallel luau, I wonder how much faster CanvasDraw would be if I somehow managed to apply them to the main canvas rendering…

1 Like

How do you even use GetPixelFromImageXY()? it says “image data” but how do I get that?

I made a cool raytracer using this cool raytracer - Roblox

There seems to be a issue when having too many different colors at a high resolution such as 720x720 or higher.

ex:

It appears the game engine refuses to render a certain amount of UIGradients which is kinda unfortunate.

6 Likes

You can extract ImageData from SaveObjects, which are custom Image instances imported by this plugin that CanvasDraw can read.

And here’s some code sample:

-- Load our image
local ImageData = CanvasDraw.GetImageDataFromSaveObject(Textures.WallTexture)

-- Get the pixel colour and pixel alpha value from our image at point 50, 50
local Colour, Alpha = Canvas:GetPixelFromImageXY(
    ImageData, 50, 50
)

Woah, very cool indeed!

And yeah, that’s why I originally gave the canvas a resolution limit, because roblox just completely breaks from rendering too many UI elements

Would it be possible to have a transparent canvas? Being able to specify the transparency of individual pixels would be great.

Hm. It is possible, but I dont imagine that being very performant as it would just add more complexity to the per-pixel calculations during rendering.