Drawing lines

Just starting out? Need help? Post your questions and find answers here.
wombats
Enthusiast
Enthusiast
Posts: 664
Joined: Thu Dec 29, 2011 5:03 pm

Drawing lines

Post by wombats »

Hi,

I'm trying to draw lines like you can in Paint and other image editors. The example is setup to be like 200% zoom, but the lines are far too smooth - they should be pixelated like if the image were doubled in size. Does anyone know how I can make it behave like that?

Code: Select all

Global mX, mY, oX, oY, iX, iY, lineSize, lineImg

lineSize = 2

lineImg = CreateImage(#PB_Any, lineSize, lineSize, 24, RGB(0, 0, 0))

Procedure DrawLine(imgID, x0, y0, x1, y1)
  ; https://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#PureBasic
  Protected steep, error, dX, dY, x, y, xStep, yStep, plotX, plotY
  If Abs(y1 - y0) > Abs(x1 - x0)
    steep = #True
    Swap x0, y0
    Swap x1, y1
  EndIf
  If x0 > x1 
    Swap x0, x1
    Swap y0, y1
  EndIf 
  dX = x1 - x0
  dY = Abs(y1 - y0)
  error = dX / 2
  y = y0
  If y0 < y1
    yStep = 1
  Else
    yStep = -1
  EndIf
  For x = x0 To x1
    If steep
      plotY = x / lineSize : plotX = y / lineSize
    Else
      plotX = x / lineSize : plotY = y / lineSize
    EndIf
    DrawAlphaImage(imgID, plotX, plotY)
    error - dY
    If error < 0
      y + yStep
      error + dX
    EndIf
  Next
EndProcedure

Procedure OnLeftButtonDown()
  iX = GetGadgetAttribute(0, #PB_Canvas_MouseX) * lineSize
  iY = GetGadgetAttribute(0, #PB_Canvas_MouseY) * lineSize
EndProcedure

Procedure OnMouseMove()
  mX = GetGadgetAttribute(0, #PB_Canvas_MouseX) * lineSize
  mY = GetGadgetAttribute(0, #PB_Canvas_MouseY) * lineSize
  If GetGadgetAttribute(0, #PB_Canvas_Buttons) & #PB_Canvas_LeftButton
    oX = iX
    oY = iY
    iX = mX / lineSize * lineSize
    iY = mY / lineSize * lineSize
    If StartDrawing(CanvasOutput(0))
      DrawLine(ImageID(lineImg), oX, oY, iX, iY)
      StopDrawing()
    EndIf
  EndIf
EndProcedure

OpenWindow(0, 0, 0, 500, 400, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
CanvasGadget(0, 0, 0, 500, 500)

BindGadgetEvent(0, @OnLeftButtonDown(), #PB_EventType_LeftButtonDown)
BindGadgetEvent(0, @OnMouseMove(), #PB_EventType_MouseMove)

Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
User avatar
Demivec
Addict
Addict
Posts: 4090
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Drawing lines

Post by Demivec »

wombats wrote: The example is setup to be like 200% zoom, but the lines are far too smooth - they should be pixelated like if the image were doubled in size. Does anyone know how I can make it behave like that?
Assuming I guessed what you want, I know how to do that.

Your code took some strange steps like multiplying the original coordinates and then dividing them when you drew the line. In other words you did exactly nothing to change the effect.

What you want to do is to step through the coordinates to make it less smooth. The corrected routine below adjusts the initial coordinates so they fall on the placement for a large pixel. It then draws the lines by moving along the coordinates at the pixel's size. In the line drawing procedure I switched from a For/Next loop to a While/Wend loop because I needed to increment by a variable (the pixel size). I also made two separate loops to move the If/EndIf test at each pixel so it was only done once outside the loops. This wasn't really necessary because you are drawing very short lines but it was just bugging me. :)

Code: Select all

Global mX, mY, oX, oY, iX, iY, lineSize, lineImg

lineSize = 2

lineImg = CreateImage(#PB_Any, lineSize, lineSize, 24, RGB(0, 0, 0))

Procedure DrawLine(imgID, x0, y0, x1, y1)
  ; https://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#PureBasic
  Protected steep, error, dX, dY, x, y, xStep, yStep, plotX, plotY
  Debug "line (" + x0 + ", " + y0 + ", " + x1 + ", " + y1 + ")"
  If Abs(y1 - y0) > Abs(x1 - x0)
    steep = #True
    Swap x0, y0
    Swap x1, y1
  EndIf
  If x0 > x1
    Swap x0, x1
    Swap y0, y1
  EndIf
  dX = x1 - x0
  dY = Abs(y1 - y0)
  error = dX / 2
  y = y0
  If y0 < y1
    yStep = lineSize ;1
  Else
    yStep = -lineSize ;-1
  EndIf
  
  x = x0
  If steep
    While x <= x1
      DrawAlphaImage(imgID, y, x)
      error - dY
      If error < 0
        y + yStep
        error + dX
      EndIf
      x + lineSize
    Wend
  Else
    While x <= x1
      DrawAlphaImage(imgID, x, y)
      error - dY
      If error < 0
        y + yStep
        error + dX
      EndIf
      x + lineSize
    Wend
  EndIf
EndProcedure

Procedure OnLeftButtonDown()
  iX = GetGadgetAttribute(0, #PB_Canvas_MouseX)
  iY = GetGadgetAttribute(0, #PB_Canvas_MouseY)
  iX - iX % lineSize
  iY - iY % lineSize
EndProcedure

Procedure OnMouseMove()
  mX = GetGadgetAttribute(0, #PB_Canvas_MouseX)
  mY = GetGadgetAttribute(0, #PB_Canvas_MouseY)
  If GetGadgetAttribute(0, #PB_Canvas_Buttons) & #PB_Canvas_LeftButton
    oX = iX
    oY = iY
    iX = mX - mX % lineSize
    iY = mY - mY % lineSize
    If StartDrawing(CanvasOutput(0))
      DrawLine(ImageID(lineImg), oX, oY, iX, iY)
      StopDrawing()
    EndIf
  EndIf
EndProcedure

OpenWindow(0, 0, 0, 500, 400, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
CanvasGadget(0, 0, 0, 500, 500)

BindGadgetEvent(0, @OnLeftButtonDown(), #PB_EventType_LeftButtonDown)
BindGadgetEvent(0, @OnMouseMove(), #PB_EventType_MouseMove)

Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
wombats
Enthusiast
Enthusiast
Posts: 664
Joined: Thu Dec 29, 2011 5:03 pm

Re: Drawing lines

Post by wombats »

Thank you! That's excellent.
wombats
Enthusiast
Enthusiast
Posts: 664
Joined: Thu Dec 29, 2011 5:03 pm

Re: Drawing lines

Post by wombats »

I'm afraid I'm having trouble with the code. When the image is semi-transparent, it doesn't draw the lines with a consistent colour. It looks like it's going back and going over some of the pixels, so they end up appearing darker. How would I fix that? I tried clearing the pixels entirely before drawing the image, but that ends up clearing other pixels that shouldn't be cleared.
User avatar
Tenaja
Addict
Addict
Posts: 1949
Joined: Tue Nov 09, 2010 10:15 pm

Re: Drawing lines

Post by Tenaja »

I am not experienced in pb drawing, but one possible method would be too draw it in an invisible canvas, then scale it up on the display. That will give it the low res look you are going for.
Last edited by Tenaja on Wed Apr 01, 2020 7:21 pm, edited 1 time in total.
User avatar
Demivec
Addict
Addict
Posts: 4090
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Drawing lines

Post by Demivec »

wombats wrote:I'm afraid I'm having trouble with the code. When the image is semi-transparent, it doesn't draw the lines with a consistent colour. It looks like it's going back and going over some of the pixels, so they end up appearing darker. How would I fix that? I tried clearing the pixels entirely before drawing the image, but that ends up clearing other pixels that shouldn't be cleared.
]

You can take several different approaches. One would be to record the line drawing to a separate image and then compose the final image by overlaying the one with the lines in it. Another approach would be to track where you've drawn line pixels to and not redraw any of those pixels (mask-like). There are other approaches to, depending on your specific needs.
Last edited by Demivec on Wed Apr 01, 2020 7:53 pm, edited 1 time in total.
wombats
Enthusiast
Enthusiast
Posts: 664
Joined: Thu Dec 29, 2011 5:03 pm

Re: Drawing lines

Post by wombats »

Thanks for your reply.

I thought of the latter idea, but I'm concerned it could be slow if the user does a lot of drawing in one go. Do you know what the most efficient way would be? I could store them in an array, of course, but as the array gets larger, wouldn't it slow down?

Your first idea is interesting, but as it's for a drawing program, the user needs to immediately see what they're doing.
User avatar
Demivec
Addict
Addict
Posts: 4090
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Drawing lines

Post by Demivec »

A few questions. What size images are going to be drawn or edited? Will there be any type of others implemented? Were the questions about line drawing for a special effect or to implement a type of zoom feature while editing?

With any methods it wouldn't be hard to create stress tests to see how they work or if they are fast enough.
Last edited by Demivec on Wed Apr 01, 2020 7:57 pm, edited 1 time in total.
wombats
Enthusiast
Enthusiast
Posts: 664
Joined: Thu Dec 29, 2011 5:03 pm

Re: Drawing lines

Post by wombats »

To implement a zoom feature while editing. The images can be any size, so up to anything PureBasic can handle.

Not to go completely off-topic, but is it possible to adapt the line drawing code to handle lines of different widths?
User avatar
Demivec
Addict
Addict
Posts: 4090
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Drawing lines

Post by Demivec »

wombats wrote:To implement a zoom feature while editing. The images can be any size, so up to anything PureBasic can handle.

Not to go completely off-topic, but is it possible to adapt the line drawing code to handle lines of different widths?
Set linesize to different integer values.
wombats
Enthusiast
Enthusiast
Posts: 664
Joined: Thu Dec 29, 2011 5:03 pm

Re: Drawing lines

Post by wombats »

Demivec wrote:
wombats wrote:To implement a zoom feature while editing. The images can be any size, so up to anything PureBasic can handle.

Not to go completely off-topic, but is it possible to adapt the line drawing code to handle lines of different widths?
Set linesize to different integer values.
*smacks head* I'm embarrassed now. I already do that when zooming. Forgive me.

I will try storing the already drawn pixels in an array and see how it affects the speed.
Post Reply