How to draw lines of a specific width and format
How to draw lines of a specific width and format
Can somebody tell me how to draw lines of a different width and format
More specifically, a line of specific width with straight and/or round ends.
And also how to draw lines in different styles (e.g. solid, dotted, ...)
Thanks in advance
Braingap
More specifically, a line of specific width with straight and/or round ends.
And also how to draw lines in different styles (e.g. solid, dotted, ...)
Thanks in advance
Braingap
Re: How to draw lines of a specific width and format
On Windows you need to use a geometric pen.
Code: Select all
Define lBrush.LOGBRUSH
With lBrush
\lbStyle = #BS_SOLID
\lbColor = #Blue
EndWith
hPen1 = ExtCreatePen_(#PS_GEOMETRIC|#PS_ENDCAP_ROUND |#PS_JOIN_MITER|#PS_SOLID, 20, lBrush, 0, 0)
hPen2 = ExtCreatePen_(#PS_GEOMETRIC|#PS_ENDCAP_ROUND |#PS_JOIN_MITER|#PS_DASH, 20, lBrush, 0, 0)
If hPen1 And hPen2
If OpenWindow(0, 100, 200, 300, 200, "2D Drawing Test")
If CreateImage(0, 300, 200)
hdc = StartDrawing(ImageOutput(0))
If hdc
Box(0, 0, 300, 200, #White)
LineXY(10,10+k*8,200, 0)
oldPen = SelectObject_(hdc, hPen1)
MoveToEx_(hdc, 20, 20, 0)
LineTo_(hdc, 280, 20)
SelectObject_(hdc, hPen2)
MoveToEx_(hdc, 20, 180, 0)
LineTo_(hdc, 280, 180)
SelectObject_(hdc, oldPen)
EndIf
StopDrawing()
EndIf
ImageGadget(0, 0, 0, 0, 0, ImageID(0))
Repeat
EventID = WaitWindowEvent()
Until EventID = #PB_Event_CloseWindow ; If the user has pressed on the window close button
EndIf
EndIf
If hPen1
DeleteObject_(hPen1)
EndIf
If hPen2
DeleteObject_(hPen2)
EndIf
I may look like a mule, but I'm not a complete ass.
Re: How to draw lines of a specific width and format
Yeah, a 'line format' statement in PureBasic would be nice 

( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
( The path to enlightenment and the PureBasic Survival Guide right here... )
Re: How to draw lines of a specific width and format
Line Format in PureBasic
Code: Select all
If OpenWindow(0, 0, 0, 200, 200, "2DDrawing Example", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
If CreateImage(0, 200, 200) And StartDrawing(ImageOutput(0))
Box(5,5,180,10,$0506FA)
RoundBox(5, 30,180,14, 7, 7, $EB9A14)
For x = 0 To 150 Step 30
RoundBox(5+x, 60, 18,8, 4, 4, $7DF682)
Next
For x = 0 To 150 Step 30
Box(5+x, 90, 15,4,$03FFFC)
Circle(27+x,92,2,$03FFFC)
Next
StopDrawing()
ImageGadget(0, 0, 0, 200, 200, ImageID(0))
EndIf
Repeat
Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow
EndIf
Egypt my love
Re: How to draw lines of a specific width and format
And diagonal lines? 

I may look like a mule, but I'm not a complete ass.
Re: How to draw lines of a specific width and format

Sine & Cosine with multiple lines for the width
Beside may be he will not need diagonals

All what we need now is a RotateBox

Egypt my love
- netmaestro
- PureBasic Bullfrog
- Posts: 8451
- Joined: Wed Jul 06, 2005 5:42 am
- Location: Fort Nelson, BC, Canada
Re: How to draw lines of a specific width and format
The pens as srod demonstrates them are a good solution on Windows. However, versatile as they are they will always look choppy and oldskool because they lack antialias capability. Here is where gdiplus can really take things up a notch. Any pattern or gradient, antialias and alphablending, any curve, arc or line, it's all there. Have you ever used gdiplus?
BERESHEIT
Re: How to draw lines of a specific width and format
@Lloyd, a question if I may regarding gdi+. I've used it occasionally, but not in any great depth. The reason being that, well, it requires XP onwards (unless you install the redistributable etc.) But now that I generally only give support for XP onwards with my apps, I have been meaning to take a deeper look. My question; how do you use gdi+ to draw to a printer DC? Do you have to create a printer dc first and then use GdipCreateFromHDC() etc. or is there a more direct way?
Thanks.
Thanks.
I may look like a mule, but I'm not a complete ass.
- netmaestro
- PureBasic Bullfrog
- Posts: 8451
- Joined: Wed Jul 06, 2005 5:42 am
- Location: Fort Nelson, BC, Canada
Re: How to draw lines of a specific width and format
I've never used gdi+ directly to a printer!
But your approach of creating the printer dc and then GdipCreateFromDC is what I'd try first, seems sound.

But your approach of creating the printer dc and then GdipCreateFromDC is what I'd try first, seems sound.
BERESHEIT
Re: How to draw lines of a specific width and format
Aye, I just wonder if StartPage_() / EndPage_() would somehow get in the way?
Right, that's on the 'to-do' list... test out GDI+ with a printer!
Right, that's on the 'to-do' list... test out GDI+ with a printer!

I may look like a mule, but I'm not a complete ass.
- Michael Vogel
- Addict
- Posts: 2797
- Joined: Thu Feb 09, 2006 11:27 pm
- Contact:
Re: How to draw lines of a specific width and format
Would like to have a simple native LineXY routine for PB which allows drawing thicker lines (width=2 at least)...
My quick and dirty code is not only slow but would fail when drawing very thick short lines (see below):
My quick and dirty code is not only slow but would fail when drawing very thick short lines (see below):
Code: Select all
Procedure Liner(x1,y1,x2,y2,color=#Black,r=1)
Protected n.f
Protected c.f
Protected x
Protected y
LineXY(x1,y1,x2,y2,color)
If r>1 And r<180
c=360/r/#PI/2/6
n=0
While n<360
x=Sin(n/180*#PI)*r
y=Cos(n/180*#PI)*r
LineXY(x1+x,y1+y,x2+x,y2+y,color)
n+c
Wend
EndIf
EndProcedure
OpenWindow(0, 100, 200, 300, 200, "2D Drawing Test")
CreateImage(0, 300, 200)
StartDrawing(ImageOutput(0))
Box(0, 0, 300, 200, #White)
Liner(20,20,280,180,#Blue,8)
Liner(20,120,220,40,#Red,4)
Liner(80,120,180,80,#Green,14)
;Liner(120,170,180,150,#Gray,40); :-(
StopDrawing()
ImageGadget(0, 0, 0, 0, 0, ImageID(0))
Repeat
EventID = WaitWindowEvent()
Until EventID = #PB_Event_CloseWindow ; If the user has pressed on the window close button
Re: How to draw lines of a specific width and format
I am a beginner to Purebasic, just converting to Purebasic from 3 other dialects of Basic, and was surprised that LineXY has no thickness setting (two of the other Basic dialects did have) but will be doing 95% of my PC programming with Purebasic , so have had to roll my own with a workaround. My code could probably be improved and optimized a lot, but as mentioned I am still a beginner with Purebasic. See code below. I would also prefer a native LineXY with thickness setting.Michael Vogel wrote:Would like to have a simple native LineXY routine for PB which allows drawing thicker lines (width=2 at least)...
My quick and dirty code is not only slow but would fail when drawing very thick short lines (see below):
My code below makes use of the Bresenham line drawing Algorithm, which is very quick, as it ONLY uses integer ADDITION and SUBTRACTION, no multiplication, division or sines or cosines. There is a lot on the web about the Bresenham Line Drawing Algorithm. There are other later algorithms almost as fast that also do antialiasing. I do not understand the line drawing algorithm, but it works. For explanation you will need to look up an explanation on the web.
Essentially the Bresenham line drawing algorithm "thickLineXY(start_x,start_y,end_x,end_y,thickness,color)" is called to calculate the pixels on the line, and instead of the procedure calling plot() to turn each pixel on, it calls circle() to draw a circle at the pixel location using the thickness of the required line as the diameter. This results in a lot of circles being drawn, so I feel that a native LineXY with thickness setting would speed this up.
Hope this helps.
Code: Select all
;thickLineXY(startx,starty,endx,endy,thickness,color)
;------------------------------------
;Bresenhams Line Drawing Algorithm
;The Bresenham line drawing algorithm uses
; integer addition And subtraction ONLY
; NO multiplication, NO division, NO sines or cosines
;------------------------------------
Procedure thickLineXY (start_x,start_y,end_x,end_y,thickness,whatcolor)
err_x = 0 : err_y = 0 : inc_x = 0 : inc_y = 0
delta_x = end_x - start_x;
delta_y = end_y - start_y;
If(delta_x > 0) :inc_x = 1:ElseIf (delta_x = 0) :inc_x = 0: Else :inc_x = -1:EndIf
If(delta_y > 0) :inc_y = 1:ElseIf (delta_y = 0) :inc_y = 0: Else :inc_y = -1:EndIf
delta_x = Abs(delta_x);
delta_y = Abs(delta_y);
If(delta_x > delta_y)
distance = delta_x;
Else
distance = delta_y;
EndIf
For xyz = 0 To distance+1 Step 1
; modified to place a circle at the pixel location to get a thick line
;Plot(start_x,start_y,whatcolor)
Circle(start_x,start_y,thickness,whatcolor)
err_x = err_x + delta_x
err_y = err_y + delta_y
If (err_x > distance)
err_x = err_x - distance
start_x = start_x + inc_x
EndIf
If (err_y > distance)
err_y = err_y - distance
start_y = start_y +inc_y
EndIf
Next
EndProcedure
;-----------------------------------------------------
; End of Bresenhams algorithm
;-----------------------------------------------------
;----------------------------------------------------------
; modified 2DDrawing Example to draw a multihand clock face
;----------------------------------------------------------
If OpenWindow(0, 0, 0, 800, 800, "2DDrawing Thick Lines", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_2DDrawing_Gradient)
If CreateImage(0, 800, 800) And StartDrawing(ImageOutput(0))
thickLineXY( 40, 40, 760, 40, 10, RGB(255,1,1)) ;outer red box top
thickLineXY(760, 40, 760, 760, 10, RGB(255,1,1));outer red box right side
thickLineXY(760, 760, 40, 760, 10, RGB(255,1,1));outer red box bottom
thickLineXY( 40, 760, 40, 40, 10, RGB(255,1,1));outer red box left side
thickLineXY(100, 680, 700, 680, 50, RGB(255,100,1)) ;orange base
thickLineXY(400, 500, 100,100, 1, RGB(255,1,1)) ; red hand
thickLineXY(100, 100, 100,100, 10, RGB(255,1,1)) ; red hand blob
thickLineXY(400, 500, 300, 100, 5, RGB(1,255,1)) ; green hand
thickLineXY(400, 500, 600, 100,10, RGB(1,1,255)) ; blue hand
thickLineXY(400, 500, 600, 600,15,RGB(1,255,255)) ; teal hand
thickLineXY(400, 500, 100, 600,20,RGB(255,255,1)) ; yellow hand
thickLineXY(400, 500, 100, 600,5,RGB(5,25,25)) ; yellow hand
thickLineXY(100, 100, 400, 500, 1, RGB(255,1,1)) ; red hand
thickLineXY(300, 100, 400, 500, 5, RGB(1,255,1)) ; green hand
thickLineXY(600, 100, 400, 500, 10, RGB(1,1,255)) ; blue hand
thickLineXY(400, 500, 400, 500, 36, RGB(255,255,255)) ; white dot
thickLineXY(400, 500, 400, 500, 22, RGB(255,0,255)) ; pink dot
StopDrawing()
ImageGadget(0, 0, 0, 200, 200, ImageID(0))
EndIf
Repeat
Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow
EndIf
End
;-------------------------------------------------------
;============================================================================
Re: How to draw lines of a specific width and format
Nice (and colorful too).
Thanks for sharing.
cheers
Thanks for sharing.
cheers
Re: How to draw lines of a specific width and format
galah,
Thanks for sharing your simple Bresenham code. I believe a change is needed in this line:
Since the third parameter in the Circle() command is the radius of the circle, not its diameter,
you are drawing the lines twice as thick as desired. Changing the line to: will make the lines the correct width.
Regards,
Eric
Thanks for sharing your simple Bresenham code. I believe a change is needed in this line:
Code: Select all
Circle(start_x,start_y,thickness,whatcolor)
you are drawing the lines twice as thick as desired. Changing the line to:
Code: Select all
Circle(start_x,start_y,thickness/2,whatcolor)
Regards,
Eric
- Michael Vogel
- Addict
- Posts: 2797
- Joined: Thu Feb 09, 2006 11:27 pm
- Contact:
Re: How to draw lines of a specific width and format
All Bresenhams routines are quite fast in calculation but slow in drawing the pixels.
As I am not very good in assembl[er|y] programming, I created the following:
As I am not very good in assembl[er|y] programming, I created the following:
Code: Select all
Global DxMem,DxMul,DxPix;
Global *DX.DxStructure
Declare MergePixel32(x,y,r,g,b,w)
Declare MergePixel24(x,y,r,g,b,w)
Declare MergePixel16(x,y,r,g,b,w)
Declare MergePixel(x,y,r,g,b,w)
Prototype MergePixelType(x,y,r,g,b,w)
Global MergePixelRoutine.MergePixelType
Macro PlotXY(x,y,color)
CompilerIf #PlotSpeedup
Box(x,y,1,1,color)
CompilerElse
Plot(x,y,color)
CompilerEndIf
EndMacro
Macro ColMix(c,r,g,b,w,iw)
;
; 24/32 Bit-Pixel: bbbbbbbb|gggggggg|rrrrrrrr
( (r*w+ColRed(c)*(iw))>>8 + (g*w+ColGreen(c)*(iw))&$ff00 + (b*w+ColBlue(c)*(iw))<<8&$ff0000 )
EndMacro
Procedure MergePixel32(x,y,r,g,b,w)
CompilerIf #AllowAssembler
MOV eax,x
MOV ecx,y
CMP eax,ScreenX ; x >= ScreenX ?
JGE exitMP32
CMP ecx,ScreenY ; y >= ScreenY ?
JGE exitMP32
Or eax,ecx ; x OR y < 0?
JS exitMP32
;*DX=DxMem+x<<2+y*DxMul
IMUL ecx,DxMul ; y*DxMul
MOV eax,x ; x
SHL eax,2 ; x*4
ADD eax,ecx ; x*4+y*DxMul
ADD eax,DxMem ; DxMem+x<<2+y*DxMul
MOV *DX,eax ; -> *DX
;*DX\l=ColMix(*DX\l,b,g,r,w,255-w)
MOV ebx,*DX\l ; color
MOV edx,255 ; 255
MOV ecx,w ; w (ecx)
SUB edx,ecx ; 255-w (edx)
MOV eax,ebx ; color
And eax,$ff ; cr=Red(color)
MUL dl ; cr*(255-w)
MOV edi,eax ; parken
MOV eax,b ; r
MUL cl ; r*w
ADD eax,edi ; r*w+cr*(255-w)
SAR eax,8 ; [r*w+cr*(255-w)] >>8
MOV esi,eax ; rot
MOV eax,ebx ; color
SAR eax,8 ; >>8
And eax,$ff ; cg=Green(color)
MUL dl ; cr*(255-w)
MOV edi,eax ; parken
MOV eax,g ; g
MUL cl ; g*w
ADD eax,edi ; g*w+cg*(255-w)
And eax,$ff00 ; [g*w+cg*(255-w)] &$ff00
ADD esi,eax; grün|rot
MOV eax,ebx ; color
SAR eax,16 ; >>16
And eax,$ff ; cb=Blue(color)
MUL dl ; cb*(255-w)
MOV edi,eax ; parken
MOV eax,r ; b
MUL cl ; b*w
ADD eax,edi ; b*w+cb*(255-w)
And eax,$ff00 ; [b*w+cb*(255-w)] &$ff00
SAL eax,8
ADD esi,eax; blau|grün|rot
MOV *DX\l,esi
!exitMP32:
CompilerElse
; If x>=0 And x<ScreenX And y>=0 And y<ScreenY
*DX=DxMem+x<<2+y*DxMul
*DX\l=ColMix(*DX\l,b,g,r,w,255-w)
; EndIf
CompilerEndIf
EndProcedure
Procedure MergePixel24(x,y,r,g,b,w)
:
ProcedureReturn
Procedure MergePixel16(x,y,r,g,b,w)
:
ProcedureReturn
Procedure MergePixel(x,y,r,g,b,w)
CompilerIf #AllowAssembler
CompilerIf #PlotSpeedup=0
MOV eax,x
MOV ecx,y
CMP eax,ScreenX ; x >= ScreenX ?
JGE exitMP
CMP ecx,ScreenY ; y >= ScreenY ?
JGE exitMP
Or eax,ecx ; x OR y < 0?
JS exitMP
CompilerEndIf
PlotXY(x,y,ColMix(Point(x,y),r,g,b,w,255-w))
!exitMP:
CompilerElse
; If x>=0 And x<ScreenX And y>=0 And y<ScreenY
PlotXY(x,y,ColMix(Point(x,y),r,g,b,w,255-w))
; EndIf
CompilerEndIf
EndProcedure
If DirectMemory
DxMem=DrawingBuffer()
DxMul=DrawingBufferPitch()
DxPix=DrawingBufferPixelFormat(); liefert bei einigen Notebooks (Amilo) leider 0...
If DxPix=0
i=GetDC_(0)
Select GetDeviceCaps_(i,#BITSPIXEL)
Case 32
DxPix=#PB_PixelFormat_32Bits_RGB
Case 24
DxPix=#PB_PixelFormat_24Bits_RGB
Case 15,16
DxPix=#PB_PixelFormat_16Bits
EndSelect
ReleaseDC_(0,i)
EndIf
If DxPix>=#PB_PixelFormat_32Bits_RGB
MergePixelRoutine=@MergePixel32()
ElseIf DxPix>=#PB_PixelFormat_24Bits_RGB
MergePixelRoutine=@MergePixel24()
ElseIf DxPix>=#PB_PixelFormat_15Bits
MergePixelRoutine=@MergePixel16()
Else
OptAntialiasing=0
EndIf
Else
MergePixelRoutine=@MergePixel()
EndIf