pixler/include/tools.bm
2026-05-21 09:22:52 +02:00

339 lines
11 KiB
Text

sub thickbox(sx,sy,ex,ey,col as long)
thickline sx,sy,ex,sy,col
thickline ex,sy,ex,ey,col
thickline ex,ey,sx,ey,col
thickline sx,ey,sx,sy,col
end sub
sub filledbox(sx,sy,ex,ey,col as long)
line(sx,sy)-(ex,ey),col,bf
end sub
sub filledpolygon (points() as long,col as long)
dim i as integer,j as integer
dim x1 as single,y1 as single,x2 as single,y2 as single
dim intersectx as single
' get the number of points from the upper bound of the array
' divide by 2 since we have x,y pairs
dim numpoints as integer
numpoints=(ubound(points)+1) \ 2
' loop through each scanline (rows of pixels)
dim intersections(100) as single
dim numintersections as integer
for y=0 to _height ' adjust for screen height
numintersections=0
' check for intersections between the polygon edges and this scanline
for i=0 to numpoints-1
x1=points(i*2)
y1=points(i*2+1)
x2=points(((i+1) mod numpoints)*2)
y2=points(((i+1) mod numpoints)*2+1)
' check if the scanline intersects with the edge of the polygon
if ((y1>y and y2<=y) or (y2>y and y1<=y)) then
' calculate intersection point with the scanline
intersectx=x1+(y-y1)*(x2-x1)/(y2-y1)
intersections(numintersections)=intersectx
numintersections=numintersections+1
end if
next i
' sort the intersections (sort by x-coordinates)
for i=0 to numintersections-1
for j=i+1 to numintersections-1
if intersections(i)>intersections(j) then
swap intersections(i),intersections(j)
end if
next j
next i
' fill the area between pairs of intersections
for i=0 to numintersections-1 step 2
line(intersections(i),y)-(intersections(i+1),y),col
next i
next y
end sub
sub thickpixel(x,y,col as long)
if state.brushsize=1 then
pset(x,y),col
else
line(x-0.5*state.brushsize,y-0.5*state.brushsize)-(x+0.5*state.brushsize,y+0.5*state.brushsize),col,bf
end if
end sub
sub thickline(x1,y1,x2,y2,col as long)
if state.brushsize=1 then
line(x1,y1)-(x2,y2),col
else
dim tempimg as long
dim od as long
tempimg=_newimage(1,1,32)
od=_dest
_dest tempimg
pset(0,0),col
_dest od
a=_atan2(y2-y1,x2-x1)
a=a+_pi/2
x0=0.5*state.brushsize*cos(a)
y0=0.5*state.brushsize*sin(a)
_maptriangle _seamless(0,0)-(0,0)-(0,0),tempimg to (x1-x0,y1-y0)-(x1+x0,y1+y0)-(x2+x0,y2+y0),,_smooth
_maptriangle _seamless(0,0)-(0,0)-(0,0),tempimg to (x1-x0,y1-y0)-(x2+x0,y2+y0)-(x2-x0,y2-y0),,_smooth
_freeimage tempimg
end if
end sub
sub polygon (pa() as long,col as long)
for i=2 to ubound(pa) step 2
thickline pa(i-2),pa(i-1),pa(i),pa(i+1),col
next i
thickline pa(ubound(pa)-1),pa(ubound(pa)),pa(0),pa(1),col
end sub
sub thickcircle(x,y,r,col as long)
if state.brushsize<=1 then
circle (x,y),r,col
else
dim rp as single,rm as single,rp2 as single,rm2 as single
dim rpi2 as single,rmi2 as single,sp as single,sm as single
dim i as single
rp=r+state.brushsize/2
rm=r-state.brushsize/2
' If the brush is thicker than the circle, it's just a filled circle
if rm<0 then
filledcircle x,y,rp,col
exit sub
end if
rp2=rp ^ 2
rm2=rm ^ 2
' Outer edges (Top/Bottom caps)
for i=-rp to -rm step .2
rpi2=rp2-i ^ 2
if rpi2<0 then rpi2=0 ' Safety Gate
sp=sqr(rpi2)
line (x+i,y-sp)-(x+i,y+sp),col,bf
next
' Side rings (where the hole in the middle exists)
for i=-rm to rm step .2
rpi2=rp2-i ^ 2
rmi2=rm2-i ^ 2
if rpi2<0 then rpi2=0 ' Safety Gate
if rmi2<0 then rmi2=0 ' Safety Gate
sp=sqr(rpi2)
sm=sqr(rmi2)
' Draw the top and bottom segments only
line (x+i,y+sm)-(x+i,y+sp),col,bf
line (x+i,y-sm)-(x+i,y-sp),col,bf
next
' Outer edges (Right cap)
for i=rm to rp step .2
rpi2=rp2-i ^ 2
if rpi2<0 then rpi2=0 ' Safety Gate
sp=sqr(rpi2)
line (x+i,y-sp)-(x+i,y+sp),col,bf
next
end if
end sub
sub filledcircle(x,y,r,col as long)
dim __radius as integer,radiuserror as integer
dim tx as integer,ty as integer
__radius=abs(r)-1
radiuserror=-__radius
tx=__radius
ty=0
line (x-tx,y)-(x+tx,y),col
while tx>ty
radiuserror=radiuserror+ty*2+1
if radiuserror>=0 then
if tx<>ty+1 then
line (x-ty,y-tx)-(x+ty,y-tx),col
line (x-ty,y+tx)-(x+ty,y+tx),col
end if
tx=tx-1
radiuserror=radiuserror-tx*2
end if
ty=ty+1
line (x-tx,y-ty)-(x+tx,y-ty),col
line (x-tx,y+ty)-(x+tx,y+ty),col
wend
end sub
sub floodfill (startx,starty,fillcolor~&)
' We use a simple array as a stack for (x, y) pairs
' For large images, you may need to increase this size
dim stackx(2000) as integer
dim stacky(2000) as integer
targetcolor~&=point(startx,starty)
if targetcolor~&=fillcolor~& then exit sub
stackptr=1
stackx(stackptr)=startx
stacky(stackptr)=starty
while stackptr>0
curx=stackx(stackptr)
cury=stacky(stackptr)
stackptr=stackptr-1
' Move to the left edge of the span
x=curx
while point(x,cury)=targetcolor~& and x>=0
x=x-1
wend
x=x+1
spanabove=0
spanbelow=0
' Process the span moving right
while point(x,cury)=targetcolor~& and x<_width
pset (x,cury),fillcolor~&
' Check row above
if cury>0 then
if spanabove=0 and point(x,cury-1)=targetcolor~& then
stackptr=stackptr+1
stackx(stackptr)=x
stacky(stackptr)=cury-1
spanabove=1
elseif spanabove=1 and point(x,cury-1)<>targetcolor~& then
spanabove=0
end if
end if
' Check row below
if cury<_height-1 then
if spanbelow=0 and point(x,cury+1)=targetcolor~& then
stackptr=stackptr+1
stackx(stackptr)=x
stacky(stackptr)=cury+1
spanbelow=1
elseif spanbelow=1 and point(x,cury+1)<>targetcolor~& then
spanbelow=0
end if
end if
x=x+1
wend
wend
end sub
sub ditheredgradient (x1 as long,y1 as long,x2 as long,y2 as long,fcol as _unsigned long,bcol as _unsigned long)
' 1. Get canvas boundaries from the active drawing layer
dim canvasw as integer:canvasw=_width(layers(1).ihandle)
dim canvash as integer:canvash=_height(layers(1).ihandle)
' 2. Calculate vector properties of the drawn line
dim dx as single:dx=x2-x1
dim dy as single:dy=y2-y1
dim linelensq as single:linelensq=(dx*dx)+(dy*dy)
' If the user didn't drag the mouse anywhere, just paint a solid pixel and exit
if linelensq=0 then
pset (x1,y1),fcol
exit sub
end if
' 3. Define a standard 4x4 Bayer Dithering Matrix (scaled 0 to 15)
dim bayer(3,3) as integer
bayer(0,0)=0:bayer(0,2)=8:bayer(0,1)=2:bayer(0,3)=10
bayer(2,0)=12:bayer(2,2)=4:bayer(2,1)=14:bayer(2,3)=6
bayer(1,0)=3:bayer(1,2)=11:bayer(1,1)=1:bayer(1,3)=9
bayer(3,0)=15:bayer(3,2)=7:bayer(3,1)=13:bayer(3,3)=5
' 4. Loop through every single pixel on the canvas layer
dim x as long,y as long
dim t as single
for y=0 to canvash-1
for x=0 to canvasw-1
' Project current pixel onto the line vector to get interpolation factor 't'
t=((x-x1)*dx+(y-y1)*dy)/linelensq
' Clamp t strictly between 0.0 and 1.0
if t<0 then t=0
if t>1 then t=1
' Compare t directly against the normalized dither matrix value.
' Adding 0.5 ensures pure fcol at t=0 and pure bcol at t=1 without edge bleeding.
if t >= (bayer(x mod 4, y mod 4) + 0.5) / 16.0 then
pset (x,y),bcol
else
pset (x,y),fcol
end if
next x
next y
end sub
sub old_ditheredgradient (x1 as long,y1 as long,x2 as long,y2 as long,fcol as _unsigned long,bcol as _unsigned long)
' 1. Get canvas boundaries from the active drawing layer
dim canvasw as integer:canvasw=_width(layers(1).ihandle)
dim canvash as integer:canvash=_height(layers(1).ihandle)
' 2. Calculate vector properties of the drawn line
dim dx as single:dx=x2-x1
dim dy as single:dy=y2-y1
dim linelensq as single:linelensq=(dx*dx)+(dy*dy)
' If the user didn't drag the mouse anywhere, just paint a solid pixel and exit
if linelensq=0 then
pset (x1,y1),fcol
exit sub
end if
' 3. Extract RGB components of Foreground and Background colors
dim fr as integer:fr=_red(fcol)
dim fg as integer:fg=_green(fcol)
dim fb as integer:fb=_blue(fcol)
dim br as integer:br=_red(bcol)
dim bg as integer:bg=_green(bcol)
dim bb as integer:bb=_blue(bcol)
' 4. Define a standard 4x4 Bayer Dithering Matrix (scaled 0 to 15)
dim bayer(3,3) as integer
bayer(0,0)=0:bayer(0,2)=8:bayer(0,1)=2:bayer(0,3)=10
bayer(2,0)=12:bayer(2,2)=4:bayer(2,1)=14:bayer(2,3)=6
bayer(1,0)=3:bayer(1,2)=11:bayer(1,1)=1:bayer(1,3)=9
bayer(3,0)=15:bayer(3,2)=7:bayer(3,1)=13:bayer(3,3)=5
' 5. Loop through every single pixel on the canvas layer
dim x as long,y as long
dim t as single,threshold as single
dim targetr as integer,targetg as integer,targetb as integer
dim mixedcolor as _unsigned long
for y=0 to canvash-1
for x=0 to canvasw-1
' Project current pixel onto the line vector to get interpolation factor 't'
t=((x-x1)*dx+(y-y1)*dy)/linelensq
' Clamp t strictly between 0.0 and 1.0
if t<0 then t=0
if t>1 then t=1
' Get the ordered dither adjustment ratio (-0.5 to +0.5 range influence)
threshold=(bayer(x mod 4,y mod 4)/16.0)-0.5
' Apply blend factor altered by our dither threshold noise
dim blend as single:blend=t+threshold
if blend<0 then blend=0
if blend>1 then blend=1
' Linearly interpolate colors
targetr=fr+(br-fr)*blend
targetg=fg+(bg-fg)*blend
targetb=fb+(bb-fb)*blend
' Find the absolute closest matching color available in your specific palette
mixedcolor=closestcolor(_rgb32(targetr,targetg,targetb),pal())
' Draw directly onto the destination image
pset (x,y),mixedcolor
next x
next y
end sub