Page 1 of 1

Fresh pair of eyes needed: working w/ canvas

Posted: Fri Jan 02, 2015 1:34 pm
by nice
Hello boys and girls!
I'm currently working on a project which is a MS paint clone and a kind forum user told me that I should use the canvas function, which I finally managed to get around to do now.
The kind user also said that my old code works fine as it is but will grind to a halt the more objects gets drawn on the screen.

From what I understand so far is that you need to put your draw calls (Please correct me if I'm wrong) inside:

Code: Select all

function love.load()
love.graphics.setCanvas(canvas)
canvas:clear()
--code here--
love.graphics.setCanvas()
end
And just to try out and see what happens I decided to copy and paste parts of my old code (see below) inside my new code to see if it's so easy as I don't think it is.
And as I thought it isn't, in this case it gets a bad argument (see attachment) stating that "ipairs" is expecting a table and got nothing.

I really need a fresh pair of eyes in this project as I have been spending some time on it and if you think I'm right or wrong please let me know but just give me hints as I want to learn as much as possible.

Thanks!

new code:

Code: Select all

function love.load()
--> Functions About Canvas <--
	canvas = love.graphics.newCanvas(800, 480)
	love.graphics.setCanvas(canvas)
	canvas:clear()

	love.graphics.setLineWidth(1) 
   	for key, square in ipairs( squares ) do
      love.graphics.setColor(square.color)
      love.graphics.rectangle( "fill", square.x, square.y, 16, 16 ) 
   	end

   love.graphics.setLineWidth(lineSize)
   for i = 1, #Lines do
      love.graphics.setColor(Lines[i].color)
      love.graphics.line(Lines[i].x1, Lines[i].y1, Lines[i].x2, Lines[i].y2)
   end

    love.graphics.setColor(colors[curColor])
    love.graphics.line(curLine.x1, curLine.y1, curLine.x2, curLine.y2)
	love.graphics.setCanvas()

--> Functions About Lines <--	
	Lines = {}
	curLine = {0, 0, 0, 0} -- Was named currentLine, Changed It
	lineSize = 16

--> Functions About The Brush <--
	squares = {}

--> Functions About The Mouse <--
	mouseIsDown = false

--> Functions About Colours <--
-- Colours
   colors = 
   --[[White]]--
   {{255, 255, 255},

      --[[Blue]]--
      {50, 130, 185}, 

      --[[Brown]]--
      {143, 114, 30}, 

      --[[Green]]--
      {50, 158, 30}, 

      --[[Orange]]--
      {199, 129, 68},

      --[[Pink]]--
      {226, 131, 239},

      --[[Purple]]--
      {143, 83, 185}, 

      --[[Red]]--
      {171, 91, 75}, 

      --[[Turquoise]]--
      {50, 137, 151}, 

      --[[Black]]--
      {0, 0, 0}}
   curColor = 1 

--> Other Functions <--
-- White Background
love.graphics.setBackgroundColor(255, 255, 255)

-- User Interface
ui = love.graphics.newImage("heliumUI.png")

-- Saving Icon
saveIcon = {x = 675, y = 500}
saveIcon.image = love.graphics.newImage("SaveIcon.png")

end

function love.update( dt )

-- The Brush

	local x, y = love.mouse.getPosition()

	if love.mouse.isDown( "l" ) and not love.keyboard.isDown("lshift") then
	local newPos = {} -- Was Named newPosition, Changed it
	newPos.x = x - 8
    newPos.y = y - 8
    newPos.color = colors[curColor]
    table.insert( squares, newPos )
   	end

   	if mouseIsDown then
   		curLine.x2 = x
   		curLine.y2 = y
   	end

end

function love.draw()

--> Graphical Functions <--
	local x, y = love.mouse.getPosition()

   love.graphics.setLineWidth(1) 
   for key, square in ipairs( squares ) do
      love.graphics.setColor(square.color)
      love.graphics.rectangle( "fill", square.x, square.y, 16, 16 ) 
   end

   -- Draws Current Colour In The Hollow Rectangle At Lower Left
   love.graphics.setColor(colors[curColor])
   love.graphics.rectangle("fill", 47, 515, 100, 50)

   -- Draws A 16x16 Square That Follows The Mouse
   if not saveIcon.naming then
      love.graphics.rectangle("line", x - 8, y - 8, 16, 16)
   end

   love.graphics.setLineWidth(lineSize)
   for i = 1, #Lines do
      love.graphics.setColor(Lines[i].color)
      love.graphics.line(Lines[i].x1, Lines[i].y1, Lines[i].x2, Lines[i].y2)
   end

   love.graphics.setColor(colors[curColor])
   love.graphics.line(curLine.x1, curLine.y1, curLine.x2, curLine.y2)

   -- User Interface
   love.graphics.setColor(255, 255, 255)
   love.graphics.draw(ui, 0, 480)
   
   love.graphics.draw(saveIcon.image, saveIcon.x, saveIcon.y)

end

function love.mousepressed(x, y, button)
	if button == "r" then
      mouseIsDown = true
      curLine = {x1 = x, y1 = y, x2 = x, y2 = y}
   elseif button == "l" then
      if x >= saveIcon.x and x <= saveIcon.x + saveIcon.image:getWidth() and
         y >= saveIcon.y and y <= saveIcon.y + saveIcon.image:getHeight() then
         
         saveIcon.naming = true
      end
   end
end

function love.mousereleased(x, y, button)
	  if button == "r" and mouseIsDown then
      mouseIsDown = false
      curLine.color = colors[curColor]
      table.insert(Lines, curLine)
      curLine = {0, 0, 0, 0}
   end
end

function love.keypressed(key)
	-- Selects Your Color By Pressing The Number Keys (1 to 0)
   if key == "1" then
      curColor = 1 -- White
   elseif key == "2" then
      curColor = 2 -- Blue
   elseif key == "3" then
      curColor = 3 -- Brown
   elseif key == "4" then
      curColor = 4 -- Green
   elseif key == "5" then
      curColor = 5 -- Orange
   elseif key == "6" then
      curColor = 6 -- Pink
   elseif key == "7" then
      curColor = 7 -- Purple
   elseif key == "8" then
      curColor = 8 -- Red
   elseif key == "9" then
      curColor = 9 -- Turquoise
   elseif key == "0" then
      curColor = 10 -- Black
   elseif key == "f" then
      squares = {}
      Lines = {}
      love.graphics.setBackgroundColor(colors[curColor])
   end
   
end
old code:

Code: Select all

function love.load()

   squares = {}
   Lines = {}
   currentLine = {0, 0, 0, 0}
   lineSize = 16
   mouseIsDown = false

-- Colors
   colors = 
   --[[White]]--
   {{255, 255, 255},

      --[[Blue]]--
      {50, 130, 185}, 

      --[[Brown]]--
      {143, 114, 30}, 

      --[[Green]]--
      {50, 158, 30}, 

      --[[Orange]]--
      {199, 129, 68},

      --[[Pink]]--
      {226, 131, 239},

      --[[Purple]]--
      {143, 83, 185}, 

      --[[Red]]--
      {171, 91, 75}, 

      --[[Turquoise]]--
      {50, 137, 151}, 

      --[[Black]]--
      {0, 0, 0}}
   curColor = 1 

-- User Interface
   ui = love.graphics.newImage("heliumUI.png")

   saveIcon = {x = 675, y = 500}
   
   saveIcon.image = love.graphics.newImage("SaveIcon.png")
   saveIcon.namingBarColor = {100, 100, 100}
   saveIcon.imageName = "Image_Name"
   love.filesystem.setIdentity("helium")

-- White Background
   love.graphics.setBackgroundColor(255, 255, 255)
   
   function OpenFolder()
      love.system.openURL("file://"..love.filesystem.getSaveDirectory())
   end
   
   love.keyboard.setKeyRepeat(true)
end

function love.update( dt )
-- The Brush
   local x, y = love.mouse.getPosition()

   if love.mouse.isDown( "l" ) and not love.keyboard.isDown("lshift") then
      local newPosition = {}
      newPosition.x = x - 8
      newPosition.y = y - 8
      newPosition.color = colors[curColor]
      table.insert( squares, newPosition )
   end

   if mouseIsDown then
      currentLine.x2 = x
      currentLine.y2 = y
   end

end


-- Draws All The Graphics --
function love.draw()
   local x, y = love.mouse.getPosition()

   love.graphics.setLineWidth(1) 
   for key, square in ipairs( squares ) do
      love.graphics.setColor(square.color)
      love.graphics.rectangle( "fill", square.x, square.y, 16, 16 ) 
   end

   -- Draws Current Colour
   love.graphics.setColor(colors[curColor])
   love.graphics.rectangle("fill", 47, 515, 100, 50)

   -- Draws A 16x16 Square That Follows The Mouse
   if not saveIcon.naming then
      love.graphics.rectangle("line", x - 8, y - 8, 16, 16)
   end

   love.graphics.setLineWidth(lineSize)
   for i = 1, #Lines do
      love.graphics.setColor(Lines[i].color)
      love.graphics.line(Lines[i].x1, Lines[i].y1, Lines[i].x2, Lines[i].y2)
   end

   love.graphics.setColor(colors[curColor])
   love.graphics.line(currentLine.x1, currentLine.y1, currentLine.x2, currentLine.y2)

   -- User Interface
   love.graphics.setColor(255, 255, 255)
   love.graphics.draw(ui, 0, 480)
   
   love.graphics.draw(saveIcon.image, saveIcon.x, saveIcon.y)

   if saveIcon.naming then
      love.graphics.setColor(saveIcon.namingBarColor)
      love.graphics.rectangle("fill", 650, 575, 120, 15)
      
      love.graphics.setColor(0, 0, 0)
      love.graphics.print(saveIcon.imageName, 650, 575)
   end
end

function love.mousepressed(x, y, button)

   -- Drop Tool -- I'll leave this here in case you ever need it.
--   if button == "l" and love.keyboard.isDown("lshift") then
--      local screenshot = love.graphics.newScreenshot()
--      local r, g, b = screenshot: getPixel(x - 1, y - 1)
--      colors[curColor] = {r, g, b}
--   end

   if button == "r" then
      mouseIsDown = true
      currentLine = {x1 = x, y1 = y, x2 = x, y2 = y}
   elseif button == "l" then
      if x >= saveIcon.x and x <= saveIcon.x + saveIcon.image:getWidth() and
         y >= saveIcon.y and y <= saveIcon.y + saveIcon.image:getHeight() then
         
         saveIcon.naming = true
      end
   end

end

function love.mousereleased(x, y, button)
   if button == "r" and mouseIsDown then
      mouseIsDown = false
      currentLine.color = colors[curColor]
      table.insert(Lines, currentLine)
      currentLine = {0, 0, 0, 0}
   end
end

function love.keypressed(key)
-- Select Your Color By Pressing The Number Keys (1 to 0)
   if key == "1" then
      curColor = 1 -- White
   elseif key == "2" then
      curColor = 2 -- Blue
   elseif key == "3" then
      curColor = 3 -- Brown
   elseif key == "4" then
      curColor = 4 -- Green
   elseif key == "5" then
      curColor = 5 -- Orange
   elseif key == "6" then
      curColor = 6 -- Pink
   elseif key == "7" then
      curColor = 7 -- Purple
   elseif key == "8" then
      curColor = 8 -- Red
   elseif key == "9" then
      curColor = 9 -- Turquoise
   elseif key == "0" then
      curColor = 10 -- Black
   elseif key == "f" then
      squares = {}
      Lines = {}
      love.graphics.setBackgroundColor(colors[curColor])
   end
   
   if saveIcon.naming then
      if key == "return" then
         saveIcon.naming = false
         
         love.mouse.setVisible(false)
         local screenShot = love.graphics.newScreenshot()
         local image = love.image.newImageData(800, 480)
         local tabler = {}
         
         for y = 0, 479 do
            for x = 0, 799 do
               local r, g, b, a = screenShot:getPixel(x, y)
               
               image:setPixel(x, y, r, g, b, a)
            end
         end
         
         image:encode(saveIcon.imageName..".png")
         
         love.mouse.setVisible(true)
         
         OpenFolder()
      elseif key == "backspace" then
         saveIcon.imageName = saveIcon.imageName:sub(1, -2)
      end
   end
end

function love.textinput(t)
   if saveIcon.naming then
      saveIcon.imageName = saveIcon.imageName .. t
   end
end

Re: Fresh pair of eyes needed: working w/ canvas

Posted: Fri Jan 02, 2015 2:30 pm
by Doctory
you havent created the squares table yet.

Re: Fresh pair of eyes needed: working w/ canvas

Posted: Fri Jan 02, 2015 2:47 pm
by s-ol
Your new way doesn't solve the problem though, you seem to have misunderstood whoever told you to use canvases.

Your program will get slower because your squares table will get larger and larger, and at some point the render time will get awfully large making your program useless.

Instead what you should do is every time the user draws something draw to the canvas, and in love.draw only draw the canvas to the screen, and don't redraw it's contents

So basically:

Code: Select all

local canvas = love.graphics.newCanvas()

function love.draw()
   -- this is it!
   -- no clearing, no redraws!
   love.graphics.draw( canvas )
end

function love.mousepressed( x, y )
   love.graphics.setCanvas( canvas )
   love.graphics.rectangle( "fill", x, y, 5, 5 )
   love.graphics.setCanvas()
   -- no saving the position of this rectangle!
end

Re: Fresh pair of eyes needed: working w/ canvas

Posted: Fri Jan 02, 2015 3:42 pm
by Muris
There is a pixel painter program that I was making maybe about a month back, I did use canvases on it. I spent only few days (2-3) on coding it, and the code is what it is. Anyways you might use it as a reference program, although like said the code is probably really far from anykind of optimal code. It has cool features like right clicking selects random color. I never implemented the curved line tool, even thought from right click you can select that tool (yes it always changes colors when you swap tools).

Re: Fresh pair of eyes needed: working w/ canvas

Posted: Fri Jan 02, 2015 4:43 pm
by nice
Doctory wrote: you havent created the squares table yet.
I feel ashamed for missing that..
S0lll0s wrote: Your new way doesn't solve the problem though, you seem to have misunderstood whoever told you to use canvases.

Your program will get slower because your squares table will get larger and larger, and at some point the render time will get awfully large making your program useless.

Instead what you should do is every time the user draws something draw to the canvas, and in love.draw only draw the canvas to the screen, and don't redraw it's contents

So basically:

Code: Select all

local canvas = love.graphics.newCanvas()

function love.draw()
   -- this is it!
   -- no clearing, no redraws!
   love.graphics.draw( canvas )
end

function love.mousepressed( x, y )
   love.graphics.setCanvas( canvas )
   love.graphics.rectangle( "fill", x, y, 5, 5 )
   love.graphics.setCanvas()
   -- no saving the position of this rectangle!
end
Well I do have a bad habit of overthinking things and take what I see or hear quite literally.
So when I looked at the canvas example and my "mentor" sayings, I guess I made it way too complex and forgot the KISS principle..
Muris wrote: There is a pixel painter program that I was making maybe about a month back, I did use canvases on it. I spent only few days (2-3) on coding it, and the code is what it is. Anyways you might use it as a reference program, although like said the code is probably really far from anykind of optimal code. It has cool features like right clicking selects random color. I never implemented the curved line tool, even thought from right click you can select that tool (yes it always changes colors when you swap tools).
I will be happy to check your project out and see in what ways it can help me, thanks! :D

Re: Fresh pair of eyes needed: working w/ canvas

Posted: Fri Jan 02, 2015 7:39 pm
by s-ol
Your first way of using the canvas didn't require it at all though, you could've just left out all the canvas stuff and it would've worked the same.

In the end this really is about chosing the right data structure to represent the image - in paint it pretty obviously is a bitmap and stored as such. Your method of storing every change since the beginning is a valid one aswell (and has some advantages like undo/redo) but is very performance and memory intensive.

Re: Fresh pair of eyes needed: working w/ canvas

Posted: Fri Jan 02, 2015 9:35 pm
by nice
S0lll0s wrote:Your first way of using the canvas didn't require it at all though, you could've just left out all the canvas stuff and it would've worked the same.

In the end this really is about chosing the right data structure to represent the image - in paint it pretty obviously is a bitmap and stored as such. Your method of storing every change since the beginning is a valid one aswell (and has some advantages like undo/redo) but is very performance and memory intensive.
Well in This case with the canvas I want to drop the performance and the memory, thus trying to implement the canvas function.