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

Unrelated, but I also added a simple TransparentColor filter to GetRGBA
RobloxStudioBeta_dLUUJ4lL9Q
RobloxStudioBeta_d63LZVzmCQ

really hacky, but it works and gets the job done

	function ImageData:GetRGBA(X: number, Y: number): (number, number, number, number)
		local PixelBuffer = self.ImageBuffer
		local Index = GetIndex(X, Y)
	
		local R,G,B,A = buffer.readu8(PixelBuffer, Index) / 255, buffer.readu8(PixelBuffer, Index + 1) / 255, buffer.readu8(PixelBuffer, Index + 2) / 255, buffer.readu8(PixelBuffer, Index + 3) / 255
		local tColor = self.TransparentColor
		if tColor then
			if Vector3.new(R,G,B):FuzzyEq(tColor) then
				return 0,0,0,0
			end
		end
		return R,G,B,A 
	end

||yes they’re both vector3s, i have no idea why this doesn’t exist in color3, you’d THINK||

I have found the issue. For some reason, textured triangles are 1 pixel off to the bottom coordinates for some reason (again). I’ve fixed this issue and now just adding the rounding to some of the draw shapes methods like you suggested

1 Like

gotcha ;)​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

Patch - v4.7.4


  • Added coordinate rounding to :DrawRectangleXY() and :DrawCircleXY()

  • Slight improvements to Canvas:DrawLine() and Canvas:DrawLineXY()

  • Fixed textured triangles being 1 pixel off from the bottom coordinates
    This also fixes rendering bugs for the following methods:

    • Canvas:DrawImageRect()
    • Canvas:DrawTexturedTriangle()
    • Canvas:DrawRotatedImage()
    • Canvas:DrawDistortedImage()

CanvasDraw4.7.4.b.rbxm (64.2 KB)

1 Like

Hi! The square issue has been fixed now.
However, the little gap still exists that was there before is still there


But, I have a suspicion that it is most likely cuz there’s a big ol’ margins inside the spritesheet itself that’s causing the black lines to be drawn

Perhaps there’s still some small inaccuracies with how the drawing methods are, or that we should just avoid using spriters resource tiles with gaps between them lmao

I did an entire spritesheet test, and it seemed to work perfectly for a sprite sheet with no margins, so it’s most likely a math or spritesheet error of yours then. Margins are a pain to deal with

if the margin is the culprit, we should put a disclaimer in the drawing method
“DISCLAIMER: Do not use sprite sheets with margins between tiles, they can cause some black lines to appear.”

This would be the same result if you did a similar thing with roblox’s image labels ImageRect thingy, but I will put a note about padding/margins somewhere

1 Like

Hey, I’m looking to make a wave visualizer based of FFT data. Would this work better with EditableImage or your API, and if so how could I use your api to do this? Would I just keep drawing pixels individually or??

CanvasDraw would be very useful for creating something like this as you have plently of shape-based methods you can use.

e.g:

  • DrawLine(), DrawRectange(), DrawTriangle(), etc

Performance between EditableImage’s shape drawing and CanvasDraw’s shapes is probably quite negligible, especially with lines as there are hardly much pixels to calculate at all and I’ve already used the most performant code for it.


EditableImage does have a couple of these methods built in, but you get more customisation and control with this library. Not to mention doing per pixel work is very easy and performant with this.

Canvas:SetRGB(X, Y, R, G, B)

Eitherway, I can’t really say what’s better for your use case. Whatever suits your workflow best I suppose!

1 Like

mario world camera yay

4 Likes

That’s so awesome. I’d love to see more progress

1 Like

Sure thing!
I was wondering what is the best way to add "effectsl on screen
as once the game renders everything, i’d go and change each pixel to be wavy like ur underwater, but that’s incredibly laggy.
Have you ever tried that before? and what would be the most optimal method (do I need to parallelize it somehow)

Is their anyway I could “fill” a custom gui circle in to have a brush effect?

Currently I fill every single pixel within the circles boundaries and it can cause a lot of lag when using a big brush and can also be a bit unstable.

Not quite sure what you mean by this. Do you mean just drawing a filled circle? Or something custom? If you could show a video/image of what you’re trying to achieve I could help


So how would I fill the pixels within that black circle (which is a seperate frame) accurately and efficiently no matter the size?

Not quite sure why you are using a separate frame for this, does Canvas:DrawCircle() not solve your case?

Any other solution would require some math and transformations from screen space to pixel space, which I’ll have to figure out tomorrow

It’s just to show the player how much they are going to fill. So for zooming and things the player would fill a different amount of space like photoshop

1 Like

Alright you have two options for a drawing based system.

A. Pixel cursor

You could use a pixel-type circle instead, since you can draw in layers. You could have your image layer, and then your cursor/UI layer that you simply just draw last.

Or you can use two canvases layered on top of each other to have more controllable layers.

This would be your easiest solution as you don’t have to ever use math for translating coordinates to canvas pixel space at all. Getting the mouse coordinates on the canvas is as simple as:

local MousePoint = Canvas:GetMousePoint() -- Returns Vector2 or nil

if MousePoint then
    -- Draw the outline preview cursor (Use DrawCircleXY as it's faster than DrawCircle)
    local MouseX, MouseY = MousePoint.X, MousePoint.Y
    Canvas:DrawCircleXY(MouseX, MouseY, BrushSize, BrushColour, false)
end

B. Frame-based cursor

For this approach, I scaled the outline frame based on canvas pixel space with a bit of math:

local CanvasRes = Vector2.new(158, 128)

local Cursor = Gui.CursorFrame -- Your circle outline frame
local BrushRadius = 5 -- In canvas pixels

local MousePoint = Canvas:GetMousePoint()

-- Mouse cursor to our mouse
Cursor.Position = UDim2.fromOffset(Mouse.X, Mouse.Y + TopBarOffsetY)
	
-- Convert canvas pixel space to screen pixel space
local Scale = (BrushRadius / CanvasRes.Y) * Gui.CanvasFrame.AbsoluteSize.Y * 2
Gui.Cursor.Size = UDim2.fromOffset(Scale, Scale)

-- Fill within the outline
if MousePoint and MouseDown then
    Canvas:DrawCircle(MousePoint, BrushRadius, Color3.new(0, 0, 0))
end

this solution should hopefully work for any sized canvas

1 Like

That’s exactly what I’m looking for thanks so much

2 Likes