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

Due to the fact that this workflow requires us to manually draw the sprites on each frame, what if, Drawing an Image onto the canvas could come with additional properties like RectOffset and RectSize.

This would mean that the developer would have to write code for a Sprite Player, yes. but that could be a built-in, optional module.

2 Likes

Ill take note of this, I’ll figure something out for sure

1 Like

yup
image

6 Likes

holy crap man, I love it!

What’s the resolution there and the render times?


I have also made my own hacky raytracer in the past, thought i’d show off a render I did:

this took 2 minutes and 48 seconds at 340x200

40 samples per pixel for shadows and reflections

EDIT: Did another one:

6 Likes

Also tried that once:


image

10 Likes

My code is heavily based off of this video https://www.youtube.com/watch?v=Qz0KTGYJtUk
(Parts are converted into meshes)

I’m using a i7-8850H (up to 4.3ghz, 6 cores 12 threads)
It runs on several threads at once using actors, and I plan to figure out a way to autodetect the optimal number of threads to use depending on the users hardware
image
Code uses strict mode and the native code gen beta
image
All Vector3s have been replaced with 3 number variables, as its MUCH faster when using native code gen at the moment

The resolution was 620x414, and the final render was supposed to have 480 samples per pixel (1 sample per pixel for each frame, 480 frames), but that screenshot was with only 78 samples, and estimated 9 minutes left (though I was running a cpu consuming task in the background so likely inaccurate)

Here’s me rendering a scene similar to yours, at 340x200 (1 sample * 60 frames * 8 threads = 480 samples per pixel)
Not sure why you’re getting less noise with less samples

7 Likes

will this ever support like decals like editable image does?
image

1 Like

yeah definitely something i can do. I will consider this for the next version!

2 Likes

I just made an update to the internal FastCanvas module that manages the pixel placement and EditableImage work.

Applying this update to CanvasDraw might take a bit to do, so in the mean time, you can use this module to work with both Frames, and Decals, Textures, MeshParts, etc.

The API only has barebones SetColor3, SetRGB, etc.


I am hoping to release this change to CanvasDraw sometime within a month

2 Likes

CanvasDraw support for Decals, Textures, MeshParts and SurfaceAppearence is going really well!

12 Likes

Hello.
How would you resize the canvas in code?

Canvas:Destroy()
Canvas = CanvasDraw.new(Frame, NewSize)

Something like this should work

1 Like

Ooo…


I settled to this workaround. using this very convenient function CreateBlankImageData I copy the colors and alphas to the given pixels

local RectOffsetX, RectOffsetY, RectSizeX, RectSizeY = quad[1], quad[2], quad[3], quad[4]
local SplicedImageData = CanvasDraw.CreateBlankImageData(RectSizeX, RectSizeY)

for y = 1, RectSizeY do
	for x = 1, RectSizeX do
		SplicedImageData:SetPixel(x, y, drawable.image:GetPixelXY(RectOffsetX + x, RectOffsetY + y))
	end
end
1 Like

Thats similar to how i probably would’ve coded it! I do plan on adding proper spritesheet/rect offset stuff soon!

Cool to see a work around!

1 Like

Actually, completely scratch what I just sent. It is a poorly optimized method.

I have devised a better strategy, doesn’t need you to clone anything:

function Canvas:DrawDistortedImageRect(PointA: Vector2, PointB: Vector2, PointC: Vector2, PointD: Vector2, RectOffset, RectSize, ImageData: {}, Brightness: number?)
	Canvas:DrawDistortedImageRectXY(
		PointA.X, PointA.Y, PointB.X, PointB.Y, PointC.X, PointC.Y, PointD.X, PointD.Y,
		RectOffset, RectSize,
		ImageData, Brightness
	)
end

function Canvas:DrawDistortedImageRectXY(X1, Y1, X2, Y2, X3, Y3, X4, Y4, RectOffset, RectSize, ImageData: {}, Brightness: number?)
	local U1, V1 = RectOffset.X / ImageData.Width, RectOffset.Y / ImageData.Height
	local U2, V2 = (RectOffset.X + RectSize.X) / ImageData.Width, RectOffset.Y / ImageData.Height
	local U3, V3 = (RectOffset.X + RectSize.X) / ImageData.Width, (RectOffset.Y + RectSize.Y) / ImageData.Height
	local U4, V4 = RectOffset.X / ImageData.Width, (RectOffset.Y + RectSize.Y) / ImageData.Height

	Canvas:DrawTexturedTriangleXY(
		X1, Y1, X2, Y2, X3, Y3,
		U1, V1, U2, V2, U3, V3,
		ImageData, Brightness
	)

	Canvas:DrawTexturedTriangleXY(
		X1, Y1, X4, Y4, X3, Y3,
		U1, V1, U4, V4, U3, V3,
		ImageData, Brightness
	)
end

local x, y, width, height = 0,0, 320, 320
local currentsize = Vector2.new(.1, .1)
while true do
	local dt = RunService.Heartbeat:Wait()
	local PointA = Vector2.new(x, y)  -- Top-left
	local PointB = Vector2.new(x + width, y)  -- Top-right
	local PointC = Vector2.new(x + width, y + height)  -- Bottom-right
	local PointD = Vector2.new(x, y + height)  -- Bottom-left
	Canvas:DrawDistortedImageRect(PointA, PointB, PointC, PointD, Vector2.new(), currentsize, ImageData)
	currentsize = currentsize:Lerp(Vector2.new(ImageData.Width, ImageData.Height), .1)
end

1 Like

Oh very cool!

I haven’t considered that, and also if you’re doing real-time stuff like this interpolation loop, yeah, you’re gonna wanna something fast. It could be much faster if you didnt use the DrawDistortedImage() method as that has a lot of math and pixel translations involved.

In most cases, you’re never going to rotate or distort the image, so you can just use a simple for loop in the X and Y directions and just draw the pixels within the rect size and position.

I will make an official draw image rect method soon!


EDIT:

You gave me an idea. I’m going to be adding a DrawRotatedImage method, as well!

Also I plan to make the API look something like this:

Canvas:DrawRotatedImageXY(
    ImageData, 
    math.rad(45), -- Angle 
    64, 64, -- Placement position
    0.5, 0.5, -- Position/Rotation anchor point
    0.5, 0.5 -- Image scale
)

Canvas:DrawImageRectXY(
    ImageData, 
    1, 1, -- Rect offset
    16, 16, -- Rect size
    0.5, 0.5 -- Image scale
)

-- DrawImageRect() will use Vector2 values

2 Likes

Looks awesome!
Would it be possible to add the rotation property onto DrawImageRectXY too? It would better suit my usecase of importing maps from Tiled to CanvasDraw

1 Like

Yeah sure! Can definitely do that since I’m using DrawTexturedTriangle internally

2 Likes

Unrelated, I’ve been working on a framework that is a simple fork of Love2D (dubbed Love2C, abbreviated Love2Canvas) and I was wondering how would someone go from making a roblox module to a github repo? Is there any known steps in doing this or is it something I’d have to make manually?

1 Like