How did you manage it to run at 256x256? If I try anything more than 150x82 it runs at 4 FPS, and every GUI (Including the one from CanvasDraw) starts to flash, pretty seizure incurring.
Also is there any way to draw more efficiently?
Lets say I have this code:
function renderImage()
local currentTime = (tick() - math.floor(tick())) * 255
local color_y = currentTime
if lastCurrentTime > currentTime then
iteration += 1
end
if iteration % 2 == 0 then
color_y = 255 - currentTime
end
lastCurrentTime = currentTime
for x = 1, resolution.X do
for y = 1, resolution.Y do
local color_x = math.floor((256 / resolution.X) * x) - 1
local color_z = math.floor((256 / resolution.Y) * y) - 1
Canvas:SetPixel(x, y, Color3.fromRGB(color_x, color_y, color_z))
end
end
end
It barely runs at 30FPS (on 150x82 and calling renderImage 10 times a second.
Is there maybe a image object, so I can make a top variable, and only change the pixels that need changing (Since Im not changing them) and apply the image after I set some pixels, at the end of the function?
Also it seems that Update is slower than normal.
If I do it without Update (And let Canvas.AutoUpdate to true) it runs at 30FPS
If I set Canvas.AutoUpdate to false and call Canvas:Update() every time the functions runs it only manages 20FPS
Time to go through the painful time of having to learn this resource to build a video player, but it will be worth it, I will finally be able to play 144p videos of stupid memes.
The first thing I immediately noticed is that your not taking one of the major limitations into account
You are using way too many colours in that demo.
The reason why this is an issue, is because CanvasDraw uses UIGradients to merge same coloured pixels, meaning way less calculations are done, and since you are rendering a lossless gradient, this will cause lag as we are doing waaaay more per-pixel calculations.
to solve this, just reduce the amount of colours allowed through each of the RBG values. The less colours, the more FPS you’ll get.
I also tested this with your code, and now I get almost 60 FPS in real time with the compressionAmount variable set to 5
Here’s your updated code:
function renderImage()
local currentTime = (tick() - math.floor(tick())) * 255
local color_y = currentTime
if lastCurrentTime > currentTime then
iteration += 1
end
if iteration % 2 == 0 then
color_y = 255 - currentTime
end
color_y = math.floor(color_y / compressionAmount) * compressionAmount
lastCurrentTime = currentTime
for x = 1, resolution.X do
-- I moved this so we aren't doing as much calculations
local color_x = math.floor((256 / resolution.X) * x) - 1
color_x = math.floor(color_x / compressionAmount) * compressionAmount
for y = 1, resolution.Y do
local color_z = math.floor((256 / resolution.Y) * y) - 1
color_z = math.floor(color_z / compressionAmount) * compressionAmount
Canvas:SetPixel(x, y, Color3.fromRGB(color_x, color_y, color_z))
end
end
end
-- This is how I tested the rendering, I used the updated event (which is just RunService.Heartbeat)
Canvas.Updated:Connect(function()
renderImage()
Canvas:Update()
end)
That formula is just (x - floor(x)) * 255. A CPU does billions of calculations every frame, there is no reason it slows by a few thousand percent from 3 simple calculations every 1/10 seconds.
Outside of moving color_x up a row there is no possible optimisation. Also removing Canvas:SetPixels shows that the code only uses 0.1% of script usage, so its pretty light.
Now it looks horrible, at this point, I could just decrease the pixel ratio. Is there a reason you removed color blending a few updates before? It would help very much.
Ah, the BlurEnabled parameter. Well, main reason why I removed it is because almost no one uses it as it doesn’t look that good at all, and it only works in the one direction. It especially looks bad if you have textures in your project too as 10 pixels in a column, now can only hold 2 colours.
Removing this parameter also meant that i can slightly improve performance on the per-pixel calculations.
UIGradients are by far the best way to have minimal instances being updated as you can store up to 10 pixels in a UIGradient frame, and with parts or frames, you can only store the one colour which heavily increases the amount of instances being updated
When you create a image using CanvasDraw.GetImageDataFromSaveObject, the ImageData doesn’t come with the GetPixel function. That happens because in the line 1355 of Canvas Draw, when creating the image data, the module doesn’t put the GetPixel function in the table
-- Convert the SaveObject into image data
local ImageData = {ImageColours = PixelColours, ImageAlphas = PixelAlphas, ImageResolution = SaveDataImageResolution}
I think it should be like:
-- Convert the SaveObject into image data
local ImageData = {
ImageColours = PixelColours,
ImageAlphas = PixelAlphas,
ImageResolution = SaveDataImageResolution,
GetPixel = CanvasDrawModule.GetPixelFromImage,
GetPixelXY = CanvasDrawModule.GetPixelFromImageXY
}
Hey all. I have made some small needed changes to how we work and use ImageData with CanvasDraw.
For those who don’t know, ImageData is an object type containing cached pixel data from a PNG image file created from a SaveObject instance (Which can be imported by using this plugin)
Here’s a complete list of the changes:
ImageData is no longer treated as a table, it is now treated as an object (like the Canvas object) and can now hold methods.
ImageData now has two new usable methods to sample pixel data:
ImageData:GetPixel() and ImageData:GetPixelXY()
These methods replace the CanvasDraw.GetPixelFromImage() and CanvasDraw.GetPixelFromImageXY() functions as they cause some mild problems and are less practical as far as modern image manipulation standards goes. These functions have now been marked as deprecated.
The CanvasDraw functions GetPixelFromImage() and GetPixelFromImageXY() have now been marked as deprecated and is no longer listed in the API documentation.
Please use the new ImageData methods instead for new work as these functions will no longer be maintained.
Renamed the function CanvasDraw.GetImageDataFromSaveObject() to CanvasDraw.GetImageData(). This was needed as the function name was far too long, and there currently isn’t any other ways to obtain ImageData.
Slight performance increase with pixel sampling (Applies to all pixel-based fetch methods)
Sorry if this might be a bother, but I’m still very confused on how to make a working videoplayer without causing a huge amount of lag everytime it loads an image, my code is currently a sample, however everytime I load the image, it causes alot of lag, I can’t imagine loading those images every 0.1 seconds. Is there anyway to make it so it doesn’t create a whole bunch of lag when loading in the pixels? I’m not really familiar with the module as I just started using it a couple days ago. (Also yes im aware this code will only load one image.)
heres the code:
local Gui = script.Parent
local Frame = Gui.Frame
local CanvasDraw = require(Gui.CanvasDraw)
local Canvas = CanvasDraw.new(Frame)
-- Load our image
local ImageData = CanvasDraw.GetImageDataFromSaveObject(script.Parent.ImageTest) -- Our Image
local Canvas = CanvasDraw.new(Frame, ImageData.ImageResolution)
-- Main
local CompressionAmount = 10 -- From 0 to 256 (The lower, more colours, the higher, less colours)
local NewColours = {}
for i, OldColour in pairs(ImageData.ImageColours) do -- Loop through the raw image Color3 values
-- Compress the RGB channels
local CompressedR = math.floor((OldColour.R * 255) / CompressionAmount) * CompressionAmount
local CompressedG = math.floor((OldColour.G * 255) / CompressionAmount) * CompressionAmount
local CompressedB = math.floor((OldColour.B * 255) / CompressionAmount) * CompressionAmount
CompressedColour = Color3.fromRGB(CompressedR, CompressedG, CompressedB)
NewColours[i] = CompressedColour
end
ImageData.ImageColours = NewColours
Canvas:DrawImageXY(ImageData, 1, 1)
What you’re meant to be doing is slowly loading all the images once, and then store them in a table, and then play the video based on that large table.
Once you have a table full of your loaded imageData, loop through that table and call DrawImageXY() for each object in that table
I have tried using imageData and its very slow at loading + I am not getting stable FPS its very jumpy. Any recommendations?
I am using the code by PixelatedCherry
Im new to using CanvasDraw and want to get smooth video playing. Thanks!
Heres my current code:
local Gui = game.Players.LocalPlayer.PlayerGui:WaitForChild("ScreenGui", 3)
local Frame = Gui.Frame
local CanvasDraw = require(script:WaitForChild("CanvasDraw", 3))
-- Load our image
local Canvas = CanvasDraw.new(Frame, Vector2.new(256, 256))
-- Main
local CompressionAmount = 10 -- From 0 to 256 (The lower, more colours, the higher, less colours)
local NewColours = {}
local Frames = {}
for i, v in pairs(game.Workspace.Files:GetChildren()) do
local ImageData = CanvasDraw.GetImageData(v)
for i, OldColour in pairs(ImageData.ImageColours) do -- Loop through the raw image Color3 values
-- Compress the RGB channels
local CompressedR = math.floor((OldColour.R * 255) / CompressionAmount) * CompressionAmount
local CompressedG = math.floor((OldColour.G * 255) / CompressionAmount) * CompressionAmount
local CompressedB = math.floor((OldColour.B * 255) / CompressionAmount) * CompressionAmount
CompressedColour = Color3.fromRGB(CompressedR, CompressedG, CompressedB)
NewColours[i] = CompressedColour
end
print("Loaded frame number: " .. i)
ImageData.ImageColours = NewColours
table.insert(Frames, ImageData)
task.wait()
end
for i, iFrame in pairs(Frames) do
Canvas:DrawImageXY(iFrame, 1, 1)
task.wait()
end
print("done")
The reason for the jumpy framerate is due to different colour positions and quantities.
This is pretty bad, you are loading and rendering large 256x256 images every frame (or heartbeat idk).
I don’t reccomend trying to do real-time videos at 256x256, and especially trying to do it on every task.wait(). it is just way too computational for roblox on the CPU to run smoothly.
The highest resolution I ever went for a GIF player of mine was 256x150 at 30FPS, which i’d say is a nice hard limit for CanvasDraw videos.
Try rendering the video at 30 or 25 FPS, at a lower resolution. it should be a more consistent and put way less stress on roblox.
Another thing too, is that you can crash or slow down the roblox client if you load up way too many high-resolution ImageData frames, for example, if you were to do a 256x256 video with 1000 frames, you would end up storing around 65 million Color3 values in tables, which will take up A LOT of memory.
The best way around this is just to stick to a lower resolution and framerate.