How to draw lines of a specific width and format

Just starting out? Need help? Post your questions and find answers here.
User avatar
charvista
Addict
Addict
Posts: 949
Joined: Tue Sep 23, 2008 11:38 pm
Location: Belgium

Re: How to draw lines of a specific width and format

Post by charvista »

I have been interested by the API from srod to draw dotted lines.
However, I am not sure how to change the color of the line?? Imagine I would like to draw the last four LineTo_() in red...

Code: Select all

Define lBrush.LOGBRUSH

With lBrush
    \lbStyle = #BS_SOLID
    \lbColor = #Green
EndWith

Thickness=1
hPen1 = ExtCreatePen_(#PS_GEOMETRIC|#PS_ENDCAP_ROUND|#PS_JOIN_MITER|#PS_SOLID, Thickness, lBrush, 0, 0)
hPen2 = ExtCreatePen_(#PS_GEOMETRIC|#PS_ENDCAP_ROUND|#PS_JOIN_MITER|#PS_DASH, Thickness, lBrush, 0, 0)

If hPen1 And hPen2
    If OpenWindow(0, 100, 200, 800, 700, "2D Drawing Test")
        If CreateImage(0, 800, 700)
            hdc = StartDrawing(ImageOutput(0))
            If hdc
                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, hPen2)
                MoveToEx_(hdc,400,10,0)
                LineTo_(hdc,700,10)
                LineTo_(hdc,700,410)
                LineTo_(hdc,400,10)
                LineTo_(hdc,400,410)
                LineTo_(hdc,700,410)
                MoveToEx_(hdc,400,410,0)
                LineTo_(hdc,700,10)
                MoveToEx_(hdc,550,10,0)
                LineTo_(hdc,400,210)
                LineTo_(hdc,550,410)
                LineTo_(hdc,700,210)
                LineTo_(hdc,550,10)
                
                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
- Windows 11 Home 64-bit
- PureBasic 6.10 LTS (x64)
- 64 Gb RAM
- 13th Gen Intel(R) Core(TM) i9-13900K 3.00 GHz
- 5K monitor with DPI @ 200%
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: How to draw lines of a specific width and format

Post by srod »

You need to create additional pens.
I may look like a mule, but I'm not a complete ass.
User avatar
charvista
Addict
Addict
Posts: 949
Joined: Tue Sep 23, 2008 11:38 pm
Location: Belgium

Re: How to draw lines of a specific width and format

Post by charvista »

Thank you srod.

Code: Select all

;
Define lBrush.LOGBRUSH
With lBrush
    \lbStyle = #BS_SOLID
    \lbColor = #Green
EndWith

Define lBrushRed.LOGBRUSH
With lBrushRed
    \lbStyle = #BS_SOLID
    \lbColor = #Red
EndWith


Thickness=1
hPen1 = ExtCreatePen_(#PS_GEOMETRIC|#PS_ENDCAP_ROUND|#PS_JOIN_MITER|#PS_SOLID, Thickness, lBrush, 0, 0)
hPen2 = ExtCreatePen_(#PS_GEOMETRIC|#PS_ENDCAP_ROUND|#PS_JOIN_MITER|#PS_DOT, Thickness, lBrush, 0, 0) ; #PS_DOT (dotted) #PS_DASH (dashed)
hPen3 = ExtCreatePen_(#PS_GEOMETRIC|#PS_ENDCAP_ROUND|#PS_JOIN_MITER|#PS_DASH, Thickness, lBrushRed, 0, 0)

    If OpenWindow(0, 100, 200, 800, 700, "2D Drawing Test")
        If CreateImage(0, 800, 700)
            hdc = StartDrawing(ImageOutput(0))
            If hdc
                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, hPen2)
                MoveToEx_(hdc,400,10,0)
                LineTo_(hdc,700,10)
                LineTo_(hdc,700,410)
                LineTo_(hdc,400,10)
                LineTo_(hdc,400,410)
                LineTo_(hdc,700,410)
                MoveToEx_(hdc,400,410,0)
                LineTo_(hdc,700,10)
                MoveToEx_(hdc,550,10,0)
                
                SelectObject_(hdc,hPen3)
                LineTo_(hdc,400,210)
                LineTo_(hdc,550,410)
                LineTo_(hdc,700,210)
                LineTo_(hdc,550,10)
                
                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


DeleteObject_(hPen1)
DeleteObject_(hPen2)
DeleteObject_(hPen3)
- Windows 11 Home 64-bit
- PureBasic 6.10 LTS (x64)
- 64 Gb RAM
- 13th Gen Intel(R) Core(TM) i9-13900K 3.00 GHz
- 5K monitor with DPI @ 200%
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: How to draw lines of a specific width and format

Post by srod »

For simple dashed lines you can make do with a regular pen (CreatePen_()) which doesn't need you to fill in a LOGBRUSH structure etc. Mind you, dashed lines are then restricted to 1 pixel thickness.
I may look like a mule, but I'm not a complete ass.
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: How to draw lines of a specific width and format

Post by Kwai chang caine »

@GALAH
Perfect lines :shock:
Thanks for sharing

@MICHAEL VOGEL
DxStructure missing :(

Code: Select all

Global *DX.DxStructure
ImageThe happiness is a road...
Not a destination
User avatar
Michael Vogel
Addict
Addict
Posts: 2797
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: How to draw lines of a specific width and format

Post by Michael Vogel »

Kwaï chang caïne wrote: @MICHAEL VOGEL
DxStructure missing :(

Code: Select all

Global *DX.DxStructure
Sorry,
here we are:

Code: Select all

    Structure DxStructure
        StructureUnion
            l.l
            w.w
        EndStructureUnion
    EndStructure
Okay, now you will see, that ScreenX and ScreenY are missing, and so on...
...here are more parts to get a running version (32 bit colors only) of my lines:

Code: Select all

#winID=1
#AllowAssembler=#True
#PlotSpeedup=#True

Global ScreenX=GetSystemMetrics_(#SM_CXSCREEN)
Global ScreenY=GetSystemMetrics_(#SM_CYSCREEN)
Global win

Global Dim AntiA(255)
Global Dim AntiB(255)

Global OptAntialiasing=#True

Procedure.l Limit(x.l)

    CompilerIf #AllowAssembler
        !mov eax,dword[p.v_x]
        !xor edx,edx        ; set EDX to zero
        !cmp eax,0            ; compare with top limit
        !cmovl eax,edx    ;
        !mov edx,255        ; 255
        !cmp eax,edx        ; compare with bottom limit
        !cmovg eax,edx    ; if lower, set value to bottom limit
        ProcedureReturn

    CompilerElse

        If x<0
            ProcedureReturn 0
        ElseIf x>255
            ProcedureReturn 255
        Else
            ProcedureReturn x
        EndIf

    CompilerEndIf

EndProcedure
Macro ColRed(col)
    (col&$ff)
EndMacro
Macro ColGreen(col)
    (col>>8&$ff)
EndMacro
Macro ColBlue(col)
    (col>>16&$ff)
EndMacro
Procedure.l Min(a.l, b.l)

    CompilerIf #AllowAssembler

        !mov eax,dword[p.v_a]
        !mov ecx,dword[p.v_b]
        !cmp eax,ecx
        !jl @f
        !mov eax,ecx
        !@@:
        ProcedureReturn

    CompilerElse

        If a<b
            ProcedureReturn a
        Else
            ProcedureReturn b
        EndIf

    CompilerEndIf

EndProcedure
Procedure AntiTable()
    
    Protected Dummy=8
    Protected k
    
    For k=0 To 255
        AntiA(k)=Limit(255-k+Dummy)
        AntiB(k)=Limit(k+Dummy)
    Next k
    
EndProcedure


Structure DxStructure
    StructureUnion
        l.l
        w.w
    EndStructureUnion
EndStructure

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)
    ;
EndProcedure
Procedure MergePixel16(x,y,r,g,b,w)
    ;
EndProcedure

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
Procedure AntiLineXY(x1,y1,x2,y2,col)

    ; Debug Str(x1)+"|"+Str(y1)+" ... "+Str(x2)+"|"+Str(y2)

    If OptAntialiasing

        Protected r=ColRed(col)
        Protected g=ColGreen(col)
        Protected b=ColBlue(col)
        Protected xd=x2-x1
        Protected yd=y2-y1
        Protected x,y,xf,yf
        Protected grad,w

        ; Debug Str(x1)+", "+Str(y1)+", "+Str(x2)+", "+Str(y2)
        CompilerIf #AllowAssembler
            MergePixelRoutine(x1,y1,r,g,b,255)
        CompilerElse
            If x1>=0
                If x1<ScreenX
                    If y1>=0
                        If y1<ScreenY
                            MergePixelRoutine(x1,y1,r,g,b,255)
                        EndIf
                    EndIf
                EndIf
            EndIf
        CompilerEndIf


        CompilerIf #AllowAssembler
            MergePixelRoutine(x2,y2,r,g,b,255)
        CompilerElse
            If x2>=0
                If x2<ScreenX
                    If y2>=0
                        If y2<ScreenY
                            MergePixelRoutine(x2,y2,r,g,b,255)
                        EndIf
                    EndIf
                EndIf
            EndIf
        CompilerEndIf


        If xd=0
            If yd=0
                ; Division durch Null vermeiden...
                ProcedureReturn
            EndIf
        EndIf

        ; "Original-Linie" für waagrechte und senkrechte Linien (schneller, aber immer gleich dick!)
        ; If xd=0 Or yd=0
        ;     LineXY(x1,y1,x2,y2,col)
        ;     ProcedureReturn
        ; EndIf


        If Abs(xd)>=Abs(yd); schwache Steigung oder Schwaches Gefälle (DeltaX > DeltaY)
            ; von links nach rechts zeichnen...
            If (x1>x2)
                Swap x1,x2
                Swap y1,y2
                xd=-xd
                yd=-yd
            EndIf

            grad=yd<<16/xd;                Steigung in 1/65536-Pixel
            yf=y1<<16;                    Starthöhe in 1/65536-Pixel

            If x1<0;                        Startpunkt außerhalb des Bildschirms (links)...
                yf-grad*x1;                Startpunkt nach oben/unten verschieben...
                x1=0;                        ...und am linken Bildrand beginnen
            EndIf

            If grad<0
                If y1>=ScreenY;            Steigende Linie mit Startpunkt außerhalb des Bildschirms (unten)...
                    ; yf wird um n×grad erhöht, bis yf<ScreenY<<16
                    ; daraus folgt n=(ScreenY<<16-yf)/grad   (somit muss grad auch ungleich Null sein)
                    x1+(ScreenY<<16-yf)/grad;    Startpunkt nach rechts verschieben
                    yf=(ScreenY-1)<<16;            Startpunkt rauf zum unteren Bildrand
                    If x1>=ScreenX
                        ProcedureReturn;            Startpunkt schon rechts außerhalb des Bildschirms?
                    EndIf
                EndIf
            EndIf


            For x=x1 To Min(x2,ScreenX-1)

                w=(yf>>8)&$FF
                y=yf>>16

                If y>=ScreenY
                    Break
                Else

                    CompilerIf #AllowAssembler
                        MergePixelRoutine(x,y,r,g,b,AntiA(w))
                        MergePixelRoutine(x,y+1,r,g,b,AntiB(w))
                    CompilerElse
                        If y>=0; And y<ScreenY
                            MergePixelRoutine(x,y,r,g,b,AntiA(w))
                        EndIf
                        If y>=-1
                            If y+1<ScreenY
                                MergePixelRoutine(x,y+1,r,g,b,AntiB(w))
                            EndIf
                        EndIf
                    CompilerEndIf

                EndIf

                yf+grad

            Next x

        Else ; schwache Steigung oder Schwaches Gefälle (DeltaX > DeltaY)

            ; von oben nach unten zeichnen...
            If (y1>y2)
                Swap x1,x2
                Swap y1,y2
                xd=-xd
                yd=-yd
            EndIf

            grad=xd<<16/yd;    Steigung in 1/65536-Pixel
            xf=x1<<16;        Startposition in 1/65536-Pixel

            If y1<0
                xf-grad*y1
                y1=0
            EndIf

            For y=y1 To Min(y2,ScreenY-1)
                w=(xf>>8)&$FF
                x=xf>>16

                If x>=ScreenX
                    Break
                Else

                    CompilerIf #AllowAssembler
                        MergePixelRoutine(x,y,r,g,b,AntiA(w))
                        MergePixelRoutine(x+1,y,r,g,b,AntiB(w))
                    CompilerElse
                        If x>=0; And x<ScreenX
                            MergePixelRoutine(x,y,r,g,b,AntiA(w))
                        EndIf
                        If x>=-1
                            If x+1<ScreenX
                                MergePixelRoutine(x+1,y,r,g,b,AntiB(w))
                            EndIf
                        EndIf
                    CompilerEndIf

                EndIf

                xf+grad

            Next y

        EndIf

    Else
        LineXY(x1,y1,x2,y2,col)
    EndIf

EndProcedure

InitSprite()
win=OpenWindow(#winID,0,0,ScreenX,ScreenY,"",#PB_Window_ScreenCentered|#PB_Window_BorderLess)
CompilerIf #PB_Compiler_Version>=440
    OpenWindowedScreen(win,0,0,ScreenX,ScreenY,0,0,0,#PB_Screen_SmartSynchronization)
CompilerElse
    OpenWindowedScreen(win,0,0,ScreenX,ScreenY,0,0,0)
CompilerEndIf

AntiTable()

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

windc=StartDrawing(ScreenOutput())
For i=0 To 99
    AntiLineXY(Random(ScreenX),Random(ScreenY),Random(ScreenX),Random(ScreenY),#Red)
Next i
StopDrawing()
FlipBuffers()
Delay(2000)
PS the code may look a little bit complicate now, just ask if anything is unclear
PSS everything should be tuned also for more speed :mrgreen:
Post Reply