little code = big difference (in convenience, for the programmer)

Post your FreeBASIC tips and tricks here. Please don’t post your code without including an explanation.
MrSwiss
Posts: 3635
Joined: Jun 02, 2013 9:27
Location: Switzerland

little code = big difference (in convenience, for the programmer)

Postby MrSwiss » Apr 11, 2018 20:53

Hi all,

this is to show, how some #Define's can make a big difference (for the programmer)!
Tests made: FBC 1.05.0 WIN 32 (-gen gas) and FBC 1.05.0 WIN 64 (-gen gcc), aswell as:
FBC (current) 1.06.0 WIN 32 (-gen gas) and FBC (current) 1.06.0 WIN 64 (-gen gcc).

This is about Color (ULong, aka: unsigned 32-bit, int-type) and the matter of change, or
get the value, based on Alpha-/Color-Channels (the single UBytes, contained in a ULong).

All together, there are 8 #Define's, 4 x getter's and, 4 x setter's.

Getter's first, with some 'proof of concept' code (probably less used, than the Setter's):

<Edit=2018-04-12> Getter's needed a change, see Paul Doe's post and my reply. Code updated. </Edit>
<Edit> above comment landed in the code section, sorry! </Edit>

Code: Select all

' Color-Channel_getDef.bas --2018-04-11, MrSwiss
'
' compile: -s gui
'

' color definitions
Const As ULong  orange = &hBFFF7F00, white = &hFFFFFFFF, black = &hFF000000, _
                red = &h00FF0000, green = &h0000FF00, blue = &h000000FF
' screen definitions
Const As Short  scr_w = 480, scr_h = 160, clr_d = 32

' function like MACRO's (getters) for alpha-/color-channel's values (UByte, from 32-bit color)
#Define ga_ch(c)     ( CULng(c) Shr 24 And 255 )   ' get alpha-channel
#Define gr_ch(c)     ( CULng(c) Shr 16 And 255 )   ' get red-channel
#Define gg_ch(c)     ( CULng(c) Shr  8 And 255 )   ' get green-channel
#Define gb_ch(c)     ( CULng(c)        And 255 )   ' get blue-channel


' proof of concept code
ScreenRes(scr_w, scr_h, clr_d)  ' graphic screen definition
Width scr_w \ 8, scr_h \ 16     ' 8 x 16 Font
Color(orange, white) : Cls      ' set FG, BG colors

Dim As UByte    b, g, r, a      ' local variables (LSB to MSB) for channel's

' using the MACRO's for assignment (base is: const orange)
b = gb_ch(orange) : g = gg_ch(orange)
r = gr_ch(orange) : a = ga_ch(orange)

' show results to user ...
Print " Color-Channel #Define's test: String 'orange'"
Color(black)                    ' change FG color (after title)
Print " using the Color-Channel getter's on: 32bit orange!"
Print
Print " alpha: ", a; " (dec)", Hex(a, 2); " (hex)"
Color(red)
Print " red  : ", r; " (dec)", Hex(r, 2); " (hex)"
Color(green)
Print " green: ", g; " (dec)", Hex(g, 2); " (hex)"
Color(blue)
Print " blue : ", b; "   (dec)", Hex(b, 2); " (hex)"    ' <-- cheating a bit!
Print : Color(black)
Print " press a key, to exit ... ";

Sleep
' proof of concept code - end   ' ----- EOF -----

Setter's with some 'proof of concept' code:

Code: Select all

' Color-Channel_setDef.bas --2018-04-11, MrSwiss
'
' compile: -s gui
'

' color definitions
Const As ULong  orange = &hBFFF7F00, white = &hFFFFFFFF, black = &hFF000000, _
                red = &h00FF0000, green = &h0000FF00, blue = &h000000FF
' screen definitions
Const As Short  scr_w = 480, scr_h = 480, clr_d = 32

' function like MACRO's (setters) for alpha-/color-channel's (32-bit color)
#Define sa_ch(c,a)   ( CULng(c) + ((a And 255) Shl 24) )   ' set alpha-channel
#Define sr_ch(c,r)   ( CULng(c) + ((r And 255) Shl 16) )   ' set red-channel
#Define sg_ch(c,g)   ( CULng(c) + ((g And 255) Shl  8) )   ' set green-channel
#Define sb_ch(c,b)   ( CULng(c) +  (b And 255) )           ' set blue-channel
' getter use (see: Color-Channel_getDef.bas)
#Define gg_ch(c)     ( CULng(c) Shr  8 And 255 )   ' get green-channel

' proof of concept code
ScreenRes(scr_w, scr_h, clr_d)  ' graphic screen definition
Width scr_w \ 8, scr_h \ 16     ' 8 x 16 Font
Color(orange, white) : Cls      ' set FG, BG colors

Dim As ULong    c1, c2, c3, c4  ' local variables for color's

' proof of: same result as RGBA(). Here, we have to use modified var. as souce
' in the steps, following the first one! Otherwise, loss of prev. assignment(s).
c1 = sa_ch(0, 255) : c1 = sr_ch(c1, 127) : c1 = sg_ch(c1, 0) : c1 = sb_ch(c1, 127)
c2 = RGBA(127, 0, 127, 255)     ' alpha is last here (above: its first = MSB)
' show it
Color(c1) : Print " c1's value: "; c1; " set with Channel MACRO's"
Color(c2) : Print " c2's value: "; c2; " set with RGBA()"

' here we change one channel in a loop (usefulness test)
Color(black)
Print : Print " green Channel changes in a loop:"
Print " original's are: c3 = red, c4 = blue (alpha = 0)"
' use in a loop, demo ... IMPORTANT: don't use modified var. as source!
' unless, you want 'doubling' behaviour! (harder to calculate)
Print " forwards ... from: 0 --> 255 (&h00 --> &hFF)"
For i As UInteger = 0 To 5      ' 255 \ 5 = 51 (multiplier factor, used below)
    c3 = sg_ch(red, i * 51) : c4 = sg_ch(blue, i * 51)
    Color(c3) : Print " c3's value: "; c3, Hex(c3, 8),
    Color(black) : Print Hex(gg_ch(c3), 2); " green-ch"
    Color(c4) : Print " c4's value: "; c4, Hex(c4, 8),
    Color(black) : Print Hex(gg_ch(c4), 2); " green-ch"
Next
Print " and, backwards ... from: 255 --> 0 (&hFF --> &h00)"
For i As Integer = 5 To 0 Step -1   ' a UInteger, causes a 'endless loop'!
    c3 = sg_ch(red, i * 51) : c4 = sg_ch(blue, i * 51)
    Color(c3) : Print " c3's value: "; c3, Hex(c3, 8),
    Color(black) : Print Hex(gg_ch(c3), 2); " green-ch"
    Color(c4) : Print " c4's value: "; c4, Hex(c4, 8),
    Color(black) : Print Hex(gg_ch(c4), 2); " green-ch"
Next
Print : Print " press a key, to exit ... ";

Sleep
' proof of concept code - end   ' ----- EOF -----
For details, refer to comments in code.
(And no, I don't want to use 'Or' in the #Define's, I prefer '+' !)

I'll post an updated "GFX_MATH.bi" also, in its own thread ... (containing now: all the #Define's).
Last edited by MrSwiss on Apr 12, 2018 9:55, edited 2 times in total.
paul doe
Posts: 1325
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: little code = big difference (in convenience, for the programmer)

Postby paul doe » Apr 11, 2018 23:46

MrSwiss wrote:I'll post an updated "GFX_MATH.bi" also, in its own thread ... (containing now: all the #Define's).

Shouldn't they give identical results?

Code: Select all

#Define ga_ch(c)     (  CULng(c) Shr 24 )           ' get alpha-channel
#Define gr_ch(c)     ( (CULng(c) Shl  8) Shr 24 )   ' get red-channel
#Define gg_ch(c)     ( (CULng(c) Shl 16) Shr 24 )   ' get green-channel
#Define gb_ch(c)     ( (CULng(c) Shl 24) Shr 24 )   ' get blue-channel

#define pixel_r( c ) ( culng( c ) shr 16 and 255 )
#define pixel_g( c ) ( culng( c ) shr  8 and 255 )
#define pixel_b( c ) ( culng( c )        and 255 )
#define pixel_a( c ) ( culng( c ) shr 24         )

dim as ulong clr = rgba( 255, 128, 64, 32 )

? gr_ch( clr )
? gg_ch( clr )
? gb_ch( clr )
? ga_ch( clr )
?
? pixel_r( clr )
? pixel_g( clr )
? pixel_b( clr )
? pixel_a( clr )

sleep()
MrSwiss
Posts: 3635
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: little code = big difference (in convenience, for the programmer)

Postby MrSwiss » Apr 12, 2018 9:00

paul doe wrote:Shouldn't they give identical results?
Yes, they should FBC 32 is OK (usually more problems, than FBC 64).

Funny thing is, the CULng() should take care of that (which works, with FBC 32 but not
in FBC 64). It's this "Integer behind the scenes" thing, that seems to bleed through here.

I've checked my "old" Asm routines again and, because a ULong is always 4 bytes, there
it works like "a charm", with shifting alone. (that was the base, to the current imple-
mentation)

Seems that the underlying C (or Asm), in FBC 64, doesn't care about the size (of the
integer type) its been fed with.
So, it seems, that I have to go back to: "mask the UByte out", in FBC. :-((

GFX_MATH.bi --> https://www.freebasic.net/forum/viewtopic.php?f=7&t=25635&p=231423
paul doe
Posts: 1325
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: little code = big difference (in convenience, for the programmer)

Postby paul doe » Apr 12, 2018 12:59

MrSwiss wrote:Seems that the underlying C (or Asm), in FBC 64, doesn't care about the size (of the
integer type) its been fed with.
So, it seems, that I have to go back to: "mask the UByte out", in FBC. :-((

It's only present in 64 bit, indeed. It does give correct results in 32 bit though (both GCC and GAS).
dodicat
Posts: 6727
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: little code = big difference (in convenience, for the programmer)

Postby dodicat » Apr 12, 2018 22:22

This seems to work in 32 and 64 bit.
(for fun, not conveniance)

Code: Select all

 
screen 19,32,,64
   
function red(byref c as ulong=0) byref as ubyte
   return   Cptr(Ubyte Ptr,@c)[2]
end function

function green(byref c as ulong) byref as ubyte
   return   Cptr(Ubyte Ptr,@c)[1]
end function

function blue(byref c as ulong) byref as ubyte
   return   Cptr(Ubyte Ptr,@c)[0]
end function

function alp(byref c as ulong) byref as ubyte
   return   Cptr(Ubyte Ptr,@c)[3]
end function

'==========================
dim as ulong back  'setters for background
 
for y as long=0 to 600-1
    for x as long=0 to 800-1
        (red(back))=x
        (green(back))=x\3 xor y\3
        (blue(back))=y
        (alp(back))= x\6 + y\6
        pset(x,y),back
    next
    next

dim as ulong c=rgba(51,100,235,220)

print red(c),green(c),blue(c),alp(c),"original" 'getters
circle(200,300),100,c,,,,f


(red(c))=100  'setters
(green(c))=123
(blue(c))=13
(alp(c))=210


print red(c),green(c),blue(c),alp(c),"changed"
circle(600,300),100,c,,,,f
sleep
 
MrSwiss
Posts: 3635
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: little code = big difference (in convenience, for the programmer)

Postby MrSwiss » Apr 13, 2018 23:59

Some more general use #Define's: Nibbles / Bytes / Word (Short):

Code: Select all

' Ninbble_Byte_Word_Defs.bas -- 2018-04-14, MrSwiss
'
' general use Function like Macro's (aka: #Define's)
' -----------------------------------------------------------------------------
' two bytes in (only each low Nibble used), one "combined" byte out
#Define MakeByte(h,l)   ( CUByte((h And &h0F) Shl 4 + (l And &h0F)) )
' two bytes in, one "combined" short (aka: Word) out
' (high byte = h, low byte = l)
#Define MakeWord(h,l)   ( CUShort((h And &hFF) Shl 8 + (l And &hFF)) )
' -----------------------------------------------------------------------------
' one byte in, one "combined" short (aka: Word) out
' (high byte = high Nibble, low byte = low Nibble)
#Define SplitByte(b)    ( CUShort((b And &hF0) Shl 4 + (b And &h0F)) )
' one Word in, one "combined" long (aka: DWord) out
' (high word = high Byte, low word = low byte)
#Define SplitWord(w)    ( CULng((w And &hFF00) Shl 8 + (w And &h00FF)) )
' -----------------------------------------------------------------------------
Btw. doing the "masking" in HEX, allowes for UByte/Byte, UShort/Short e.t.c.
Simply by assigning the result, to a Byte/Short, returns it to signed behaviour again.
paul doe
Posts: 1325
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: little code = big difference (in convenience, for the programmer)

Postby paul doe » Apr 14, 2018 1:23

Nice!

These are some defines that I also found very useful when dealing with endianness. The '#defines' simply invert the byte order of the value.

Code: Select all

/'
   Helpers to deal with endianness
'/
'' Return the low and high 32-bits of a 64-bit value
#define loQWord( x )   ( clngInt( x and &h00000000ffffffff ) )
#define hiQWord( x )   ( clngInt( ( x shr 32 ) and &h00000000ffffffff ) )

'' Swap the low and high bytes of a 16-bit value
#define swap16( x )         ( cshort( ( ( loByte( x ) shl 8 ) or ( hiByte( x ) ) ) ) )
#define swapWord( x )      swap16( x )
'' Swap the low and high 16-bits of a 32-bit value
#define swap32( x )         ( clng( swap16( loWord( x ) ) shl 16 ) or clng( swap16( hiWord( x ) ) and &h0000ffff ) )
#define swapDWord( x )   swap32( x )
'' Swap the low and high 32-bits of a 64-bit value
#define swap64( x )         ( clngInt( swap32( loQWord( x ) ) shl 32 ) or clngInt( swap32( hiQWord( x ) ) and &h00000000ffffffff ) )
#define swapQWord( x )   swap64( x )
'' .. et al ;)
paul doe
Posts: 1325
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: little code = big difference (in convenience, for the programmer)

Postby paul doe » Apr 14, 2018 1:33

dodicat wrote:This seems to work in 32 and 64 bit.
(for fun, not conveniance)

Nice one, and very useful to boot =D
MrSwiss
Posts: 3635
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: little code = big difference (in convenience, for the programmer)

Postby MrSwiss » Apr 14, 2018 11:44

Yep, useful indeed. I've also implemented some of those (hiDWord/loDWord getters).

paul doe wrote:These are some defines that I also found very useful when dealing with endianness.

However, for "endianness" I'm using #Macro's with "inline ASM" for 32/64 bit Int's.
The *bswap* instruction is, "so inviting" (its asking, to be used).

32-bit, (aka: [U]Long):

Code: Select all

#Macro swap32(L)
If SizeOf(L) = 4 Then  ' only with 32bit INT Types!
    Asm
        mov     eax, dword Ptr[L]
        bswap   eax
        mov     dword Ptr[L], eax
    End Asm
EndIf
#EndMacro

Dim As Long  a = &hFFEEDDCC
Dim As ULong b = a

Print "INT32:  "; Hex(a, 8), Str(a)
Print "UINT32: "; Hex(b, 8), Str(b)
Print
swap32(a) : swap32(b)
Print "swap64() here, for a and b"
Print
Print "INT32:  "; Hex(a, 8), Str(a)
Print "UINT32: "; Hex(b, 8), Str(b)
Print

Sleep

64-bit, (aka: [U]LongInt)

Code: Select all

#Macro swap64(LI)
If SizeOf(LI) = 8 Then  ' only with 64bit INT Types!
  #Ifdef __FB_64BIT__   ' x64 FBC
    Asm
        mov     rax, qword Ptr[LI]
        bswap   rax
        mov     qword Ptr[LI], rax
    End Asm
  #Else                 ' x32 FBC
    Asm
        mov     eax, dword Ptr[LI]
        mov     ebx, dword Ptr[LI + 4]
        bswap   eax
        bswap   ebx
        mov     dword Ptr[LI + 4], eax
        mov     dword Ptr[LI], ebx
    End Asm
  #EndIf
EndIf
#EndMacro

Dim As LongInt  a = &hFFEEDDCCBBAA9988
Dim As ULongInt b = a

Print "INT64:  "; Hex(a, 16), Str(a)
Print "UINT64: "; Hex(b, 16), Str(b)
Print
swap64(a) : swap64(b)
Print "swap64() here, for a and b"
Print
Print "INT64:  "; Hex(a, 16), Str(a)
Print "UINT64: "; Hex(b, 16), Str(b)
Print

Sleep
Those work for variables only (no Const's, for obvious reasons) ...

The 64 bit swap, illustrates clearly, the advantage of FBC 64 (and ASM), over its 32 bit counterpart.
The drawback of "inline ASM" is also shown, since one has to make a "double" implementation.

Return to “Tips and Tricks”

Who is online

Users browsing this forum: No registered users and 10 guests