Author |
Topic: Fast RGB 'lerp' in BASIC (Read 494 times) |
|
David Williams
Developer
member is offline

meh

Gender: 
Posts: 452
|
 |
Fast RGB 'lerp' in BASIC
« Thread started on: May 23rd, 2016, 9:37pm » |
|
Here's a fast RGB linear interpolation in BASIC (one line):
Code: DEFFN_LerpRGB(A%,B%,F%)=(A%AND&FF)+(F%*((B%AND&FF)-(A%AND&FF))>>8)OR&100*(((A%AND&FF00)>>8)+(F%*(((B%AND&FF00)>>8)-((A%AND&FF00)>>8))>>8))OR&10000*((A%>>16)+(F%*(B%-A%>>16)>>8))
Where A% is RGB colour 1 (&RRGGBB) and B% is RGB colour 2 (&rrggbb), and F% is the interpolation value in the range 0 to 256. In the case of F% = 0, colour 1 is returned; in the case of F% = 256, colour 2 is returned.
Here's a quick example:
Code: MODE 8 : OFF
WinW% = 2 * @vdu%!208
WinH% = 2 * @vdu%!212
col1% = &2040AA
col2% = &FFA0A0
GCOL 1
FOR Y% = 0 TO WinH%-1
f% = 256 * Y%/(WinH%-1)
c% = FN_LerpRGB( col1%, col2%, f% )
COLOUR 1, (c% AND &FF0000)>>16, (c% AND &FF00)>>8, c% AND &FF
LINE 0, Y%, WinW%, Y%
NEXT Y%
END
DEFFN_LerpRGB(A%,B%,F%)=(A%AND&FF)+(F%*((B%AND&FF)-(A%AND&FF))>>8)OR&100*(((A%AND&FF00)>>8)+(F%*(((B%AND&FF00)>>8)-((A%AND&FF00)>>8))>>8))OR&10000*((A%>>16)+(F%*(B%-A%>>16)>>8))
There are several possible useful variations of this function.
Sanity checks on F% (ensuring it's within the range 0 to 256) wouldn't be a bad idea.
David. --
|
|
Logged
|
|
|
|
David Williams
Developer
member is offline

meh

Gender: 
Posts: 452
|
 |
Re: Fast RGB 'lerp' in BASIC
« Reply #1 on: May 24th, 2016, 1:37pm » |
|
If speed's important, then the following (suboptimal!) assembly language implementation is more than twice as fast as the BASIC equivalent (when called using SYS).
Code: MODE 10 : OFF
WinW% = @vdu%!208
WinH% = @vdu%!212
lerp% = FNasm
FOR I% = 1 TO 100
W% = 64 + RND(300)
H% = 64 + RND(200)
X% = RND(WinW% - W%)
Y% = RND(WinH% - H%)
col1% = RND(&FFFFFF)
col2% = RND(&FFFFFF)
GCOL 1
FOR J% = 0 TO H%-1
F% = 256 * (J%/H%)
SYS lerp%, col1%, col2%, F% TO rgb%
COLOUR 1, rgb% AND &FF, (rgb% AND &FF00)>>8, (rgb% AND &FF0000)>>16
LINE 2*X%, 2*(Y%+J%), 2*(X%+W%), 2*(Y%+J%)
NEXT J%
NEXT I%
END
DEF FNasm
LOCAL C%, I%, P%, J%, K%
DIM C% 109
FOR I% = 0 TO 2 STEP 2
P% = C%
[OPT I%
; [ESP+4] --> &xxB1G1R1
; [ESP+8] --> &xxB2G2B2
; [ESP+12] --> f (interpolation factor in range 0 to 256)
mov esi, [esp + 12] ; ESI = f
; clamp f to the range 0 to 256
cmp esi, 0
jge J%
mov esi, 0
.J%
cmp esi, 256
jle K%
mov esi, 256
.K%
xor eax, eax ; result will be stored in EAX
; handle red lerp
movzx ebx, BYTE [esp+4 +0] ; R1
movzx ecx, BYTE [esp+8 +0] ; R2
mov edx, ebx ; copy R1
sub ecx, ebx ; R2 - R1
imul ecx, esi ; f*(R2-R1)
sar ecx, 8 ; f*(R2-R1) >> 8
add ecx, edx ; R1 + (f*(R2-R1) >> 8)
or eax, ecx ; EAX = &000000RR
; handle green lerp
movzx ebx, BYTE [esp+4 +1] ; G1
movzx ecx, BYTE [esp+8 +1] ; G2
mov edx, ebx ; copy G1
sub ecx, ebx ; G2 - G1
imul ecx, esi ; f*(G2-G1)
sar ecx, 8 ; f*(G2-G1) >> 8
add ecx, edx ; G1 + (f*(G2-G1) >> 8)
shl ecx, 8
or eax, ecx ; EAX = &0000GGRR
; handle blue lerp
movzx ebx, BYTE [esp+4 +2] ; B1
movzx ecx, BYTE [esp+8 +2] ; B2
mov edx, ebx ; copy B1
sub ecx, ebx ; B2 - B1
imul ecx, esi ; f*(B2-B1)
sar ecx, 8 ; f*(B2-B1) >> 8
add ecx, edx ; B1 + (f*(B2-B1) >> 8)
shl ecx, 16
or eax, ecx ; EAX = &00BBGGRR
ret 12
]
NEXT I%
=C%
|
|
Logged
|
|
|
|
David Williams
Developer
member is offline

meh

Gender: 
Posts: 452
|
 |
Re: Fast RGB 'lerp' in BASIC
« Reply #2 on: May 24th, 2016, 3:32pm » |
|
Code: MODE 10 : OFF
WinW% = @vdu%!208
WinH% = @vdu%!212
lerp% = FNasm
rectW% = 480
rectH% = 480
DIM p(2*rectW%-1,1), q(2*rectW%-1,1), m(1,1)
FOR I% = 0 TO 2*rectW%-1 STEP 2
p(I%, 0) = (I% - rectW%)
p(I%, 1) = -rectH%
p(I%+1, 0) = p(I%,0)
p(I%+1, 1) = rectH%
NEXT I%
rgb1% = &FF8040
rgb2% = &4080FF
ORIGIN WinW%, WinH%
GCOL 1
*REFRESH OFF
REPEAT
CLS
T% = TIME
sin = SIN(T%/100)
cos = COS(T%/100)
m() = cos, -sin, sin, cos
q() = p().m()
@vdu%!248 = 2
FOR I% = 0 TO 2*rectW%-1 STEP 2
SYS lerp%, rgb1%, rgb2%, 256*I%/(2*rectW%-1) TO rgb3%
COLOUR 1, rgb3% AND &FF, (rgb3% AND &FF00) >> 8, (rgb3% AND &FF0000) >> 16
LINE q(I%,0), q(I%,1), q(I%+1,0), q(I%+1,1)
NEXT
@vdu%!248 = 1
*REFRESH
UNTIL FALSE
END
:
:
:
:
DEF FNasm
LOCAL C%, I%, P%, J%, K%
DIM C% 109
FOR I% = 0 TO 2 STEP 2
P% = C%
[OPT I%
; [ESP+4] --> &xxB1G1R1
; [ESP+8] --> &xxB2G2B2
; [ESP+12] --> f (interpolation factor in range 0 to 256)
mov esi, [esp + 12] ; ESI = f
; clamp f to the range 0 to 256
cmp esi, 0
jge J%
mov esi, 0
.J%
cmp esi, 256
jle K%
mov esi, 256
.K%
xor eax, eax ; result will be stored in EAX
; handle red lerp
movzx ebx, BYTE [esp+4 +0] ; R1
movzx ecx, BYTE [esp+8 +0] ; R2
mov edx, ebx ; copy R1
sub ecx, ebx ; R2 - R1
imul ecx, esi ; f*(R2-R1)
sar ecx, 8 ; f*(R2-R1) >> 8
add ecx, edx ; R1 + (f*(R2-R1) >> 8)
or eax, ecx ; EAX = &000000RR
; handle green lerp
movzx ebx, BYTE [esp+4 +1] ; G1
movzx ecx, BYTE [esp+8 +1] ; G2
mov edx, ebx ; copy G1
sub ecx, ebx ; G2 - G1
imul ecx, esi ; f*(G2-G1)
sar ecx, 8 ; f*(G2-G1) >> 8
add ecx, edx ; G1 + (f*(G2-G1) >> 8)
shl ecx, 8
or eax, ecx ; EAX = &0000GGRR
; handle blue lerp
movzx ebx, BYTE [esp+4 +2] ; B1
movzx ecx, BYTE [esp+8 +2] ; B2
mov edx, ebx ; copy B1
sub ecx, ebx ; B2 - B1
imul ecx, esi ; f*(B2-B1)
sar ecx, 8 ; f*(B2-B1) >> 8
add ecx, edx ; B1 + (f*(B2-B1) >> 8)
shl ecx, 16
or eax, ecx ; EAX = &00BBGGRR
ret 12
]
NEXT I%
=C%
Essentially he same program but now 100% BASIC:
Code: MODE 10 : OFF
WinW% = @vdu%!208
WinH% = @vdu%!212
rectW% = 480
rectH% = 480
DIM p(2*rectW%-1,1), q(2*rectW%-1,1), m(1,1)
FOR I% = 0 TO 2*rectW%-1 STEP 2
p(I%, 0) = (I% - rectW%)
p(I%, 1) = -rectH%
p(I%+1, 0) = p(I%,0)
p(I%+1, 1) = rectH%
NEXT I%
rgb1% = &FF00FF
rgb2% = &00FF00
ORIGIN WinW%, WinH%
GCOL 1
*REFRESH OFF
REPEAT
CLS
T% = TIME
sin = SIN(T%/100)
cos = COS(T%/100)
m() = cos, -sin, sin, cos
q() = p().m()
@vdu%!248 = 2
FOR I% = 0 TO 2*rectW%-1 STEP 2
rgb3%=FN_LerpRGB(rgb1%,rgb2%,256*I%/(2*rectW%-1))
COLOUR 1, rgb3% AND &FF, (rgb3% AND &FF00) >> 8, (rgb3% AND &FF0000) >> 16
LINE q(I%,0), q(I%,1), q(I%+1,0), q(I%+1,1)
NEXT
@vdu%!248 = 1
*REFRESH
UNTIL FALSE
END
DEFFN_LerpRGB(U%,V%,F%)=(U%AND&FF)+(F%*((V%AND&FF)-(U%AND&FF))>>8)OR&100*(((U%AND&FF00)>>8)+(F%*(((V%AND&FF00)>>8)-((U%AND&FF00)>>8))>>8))OR&10000*((U%>>16)+(F%*(V%-U%>>16)>>8))
|
|
|
|
David Williams
Developer
member is offline

meh

Gender: 
Posts: 452
|
 |
Re: Fast RGB 'lerp' in BASIC
« Reply #3 on: May 25th, 2016, 06:43am » |
|
Quadratic RGB interpolation. This is just an experiment. Individual RGB components must be prevented from going negative, hence the 'clamping'. Also, I'm not sure if the result looks any better than using a compound (two-part) linear interpolant.
Code: MODE 8 : OFF
GCOL 1
REPEAT
rgb1% = RND(&FFFFFF)
rgb2% = RND(&FFFFFF)
rgb3% = RND(&FFFFFF)
r1& = rgb1%
g1& = rgb1% >> 8
b1& = rgb1% >> 16
r2& = rgb2%
g2& = rgb2% >> 8
b2& = rgb2% >> 16
r3& = rgb3%
g3& = rgb3% >> 8
b3& = rgb3% >> 16
PROCgetQuadraticCoeffs( 0.0, r1&, 0.5, g1&, 1.0, b1&, a1, b1, c1 )
PROCgetQuadraticCoeffs( 0.0, r2&, 0.5, g2&, 1.0, b2&, a2, b2, c2 )
PROCgetQuadraticCoeffs( 0.0, r3&, 0.5, g3&, 1.0, b3&, a3, b3, c3 )
FOR Y% = 0 TO 511
f = Y%/511
r = a1*f*f + b1*f + c1
g = a2*f*f + b2*f + c2
b = a3*f*f + b3*f + c3
IF r < 0 r = 0
IF r > 255 r = 255
IF g < 0 g = 0
IF g > 255 g = 255
IF b < 0 b = 0
IF b > 255 b = 255
COLOUR 1, r, g, b
LINE 0, 2*Y%, 1280, 2*Y%
NEXT Y%
WAIT 200
UNTIL FALSE
END
DEF PROCgetQuadraticCoeffs( x1, y1, x2, y2, x3, y3, RETURN a, RETURN b, RETURN c )
PROCsolve2x2( x1^2-x2^2, x1-x2, y1-y2, \
\ x1^2-x3^2, x1-x3, y1-y3, \
\ a, b )
c = y1 - (a*x1^2 + b*x1)
ENDPROC
DEF PROCsolve2x2(A, B, C, D, E, F, RETURN x, RETURN y)
LOCAL d, e
e = A*E - B*D
IF ABS( e ) < 0.0000001 THEN ENDPROC
d = 1 / e
x = d * (E*C - B*F)
y = d * (A*F - D*C)
ENDPROC
Another rotating square:
Code: MODE 10 : OFF
WinW% = @vdu%!208
WinH% = @vdu%!212
rectW% = 480
rectH% = 480
DIM p(2*rectW%-1,1), q(2*rectW%-1,1), m(1,1)
FOR I% = 0 TO 2*rectW%-1 STEP 2
p(I%, 0) = (I% - rectW%)
p(I%, 1) = -rectH%
p(I%+1, 0) = p(I%,0)
p(I%+1, 1) = rectH%
NEXT I%
newColours% = TRUE
time0% = TIME
ORIGIN WinW%, WinH%
GCOL 1
*REFRESH OFF
REPEAT
CLS
IF newColours% THEN PROCgetRandomColours : newColours% = FALSE
T% = TIME
sin = SIN(T%/100)
cos = COS(T%/100)
m() = cos, -sin, sin, cos
q() = p().m()
@vdu%!248 = 2
FOR I% = 0 TO 2*rectW%-1 STEP 2
f = I%/(2*rectW%-1)
r = a1*f*f + b1*f + c1
g = a2*f*f + b2*f + c2
b = a3*f*f + b3*f + c3
IF r < 0 r = 0
IF r > 255 r = 255
IF g < 0 g = 0
IF g > 255 g = 255
IF b < 0 b = 0
IF b > 255 b = 255
COLOUR 1, r, g, b
LINE q(I%,0), q(I%,1), q(I%+1,0), q(I%+1,1)
NEXT
@vdu%!248 = 1
IF T% > time0%+300 THEN
time0% = T%
newColours% = TRUE
ENDIF
*REFRESH
UNTIL FALSE
END
DEF PROCgetRandomColours
rgb1% = RND(&FFFFFF)
rgb2% = RND(&FFFFFF)
rgb3% = RND(&FFFFFF)
r1& = rgb1%
g1& = rgb1% >> 8
b1& = rgb1% >> 16
r2& = rgb2%
g2& = rgb2% >> 8
b2& = rgb2% >> 16
r3& = rgb3%
g3& = rgb3% >> 8
b3& = rgb3% >> 16
PROCgetQuadraticCoeffs( 0.0, r1&, 0.5, g1&, 1.0, b1&, a1, b1, c1 )
PROCgetQuadraticCoeffs( 0.0, r2&, 0.5, g2&, 1.0, b2&, a2, b2, c2 )
PROCgetQuadraticCoeffs( 0.0, r3&, 0.5, g3&, 1.0, b3&, a3, b3, c3 )
ENDPROC
DEF PROCgetQuadraticCoeffs( x1, y1, x2, y2, x3, y3, RETURN a, RETURN b, RETURN c )
PROCsolve2x2( x1^2-x2^2, x1-x2, y1-y2, \
\ x1^2-x3^2, x1-x3, y1-y3, \
\ a, b )
c = y1 - (a*x1^2 + b*x1)
ENDPROC
DEF PROCsolve2x2(A, B, C, D, E, F, RETURN x, RETURN y)
LOCAL d, e
e = A*E - B*D
IF ABS( e ) < 0.0000001 THEN ENDPROC
d = 1 / e
x = d * (E*C - B*F)
y = d * (A*F - D*C)
ENDPROC
|
|
|
|
RockOve
New Member
member is offline


Posts: 7
|
 |
Re: Fast RGB 'lerp' in BASIC
« Reply #4 on: May 26th, 2016, 12:38am » |
|
"another rotating sqr" in line 30
Code:
rem this is wrong:
rem sin = sin(T%/100)
rem cos = cos(T%/100)
rem m() = cos, -sin, sin, cos
:
rem here is how you do it:
ssin = sin(T%/100)
ccos = cos(T%/100)
m() = ccos, -ssin, ssin, ccos
|
|
Logged
|
|
|
|
KenDown
Full Member
member is offline


Posts: 181
|
 |
Re: Fast RGB 'lerp' in BASIC
« Reply #5 on: Nov 11th, 2016, 6:50pm » |
|
Referring to the LARP program, I'm a little confused by the results. I tried using these definitions: col1% = &008800 col2% = &0000FF which I expected would shade from dark green to light blue. Instead it shades from a sort of orange-brown to bright pink.
Anyone have any idea why?
Also, do you mind if I use this routine in one of my programs?
|
|
Logged
|
|
|
|
David Williams
Developer
member is offline

meh

Gender: 
Posts: 452
|
 |
Re: Fast RGB 'lerp' in BASIC
« Reply #6 on: Nov 11th, 2016, 8:42pm » |
|
Yes, sorry about this. I hadn't tested the 'lerping' function very well, and discovered it was broken a few weeks ago (but didn't think anyone would ever notice!).
Anyway, here's a more compact and possibly faster function which does actually work as intended:
Code: DEFFN_LerpRGB(A%,B%,F%)=(?^A%+(F%*(?^B%-?^A%)>>8))OR&100*(?(^A%+1)+(F%*(?(^B%+1)-?(^A%+1))>>8))OR&10000*(?(^A%+2)+(F%*(?(^B%+2)-?(^A%+2))>>8))
And here's the test program using your colour values (&008800, &0000FF):
Code: MODE 8 : OFF
WinW% = 2 * @vdu%!208
WinH% = 2 * @vdu%!212
col1% = &008800
col2% = &0000FF
GCOL 1
FOR Y% = 0 TO WinH%-1
f% = 256 * Y%/(WinH%-1)
c% = FN_LerpRGB( col1%, col2%, f% )
COLOUR 1, (c% AND &FF0000)>>16, (c% AND &FF00)>>8, c% AND &FF
LINE 0, Y%, WinW%, Y%
NEXT Y%
END
DEFFN_LerpRGB(A%,B%,F%)=(?^A%+(F%*(?^B%-?^A%)>>8))OR&100*(?(^A%+1)+(F%*(?(^B%+1)-?(^A%+1))>>8))OR&10000*(?(^A%+2)+(F%*(?(^B%+2)-?(^A%+2))>>8))
And yes, you're welcome to use any of my code snippets/functions in your own programs.
David. --
|
|
|
|
KenDown
Full Member
member is offline


Posts: 181
|
 |
Re: Fast RGB 'lerp' in BASIC
« Reply #7 on: Nov 12th, 2016, 03:56am » |
|
Many thanks, both for the revised function and the permission.
I have a program that displays a rolling carousel of notices and announcements for my branch of the U3A and thought it would be nice to have a variegated background instead of just a plain colour. That's when I remembered your program and started to play with it in earnest.
|
|
Logged
|
|
|
|
KenDown
Full Member
member is offline


Posts: 181
|
 |
Re: Fast RGB 'lerp' in BASIC
« Reply #8 on: Nov 12th, 2016, 04:12am » |
|
By the way, given that the colours are defined in hexadecimal, which means that the maximum for any one component is &FF, is there any conceivable harm in reducing the line to
COLOUR 1,c%>>16,c%>>8,c%AND&FF
instead of
COLOUR 1, (c% AND &FF0000)>>16, (c% AND &FF00)>>8, c% AND &FF
I know why you have done the AND&FF000 but is it necessary? (In fact I suspect that I could even get rid of the final AND&FF)
|
|
Logged
|
|
|
|
David Williams
Developer
member is offline

meh

Gender: 
Posts: 452
|
 |
Re: Fast RGB 'lerp' in BASIC
« Reply #9 on: Nov 12th, 2016, 04:29am » |
|
on Nov 12th, 2016, 04:12am, KenDown wrote:By the way, given that the colours are defined in hexadecimal, which means that the maximum for any one component is &FF, is there any conceivable harm in reducing the line to
COLOUR 1,c%>>16,c%>>8,c%AND&FF |
|
It certainly seems to work, so the interpreter is probably already performing an 'AND &FF' on each of the RGB values. Well spotted - it's nice to learn something new!
David. --
|
|
|
|
|