English Amiga Board


Go Back   English Amiga Board > Coders > Coders. Asm / Hardware

 
 
Thread Tools
Old 10 February 2015, 08:32   #1
AGS
XoXo/Tasko Developer
 
AGS's Avatar
 
Join Date: Dec 2013
Location: Munich
Age: 48
Posts: 450
Happy Calculate a color gradient.

Hi,

I did calculate gradients once in BASIC. After doing some reading I think the formula would be:

- do the next steps for each channel, r, g and b
- substract the start color value from the end
- divide result by number of pixels
- multiply with number of pixel for each dot
- add result to start color value, result is pixel color value

I would do the multiplication first because having no floating point registers. How would you do this?

greez
AGS
AGS is offline  
Old 10 February 2015, 08:42   #2
thomas
Registered User
 
thomas's Avatar
 
Join Date: Jan 2002
Location: Germany
Posts: 7,002
n = n0 + (y - y0) * (n1 - n0) / (y1 - y0)
thomas is offline  
Old 10 February 2015, 08:44   #3
AGS
XoXo/Tasko Developer
 
AGS's Avatar
 
Join Date: Dec 2013
Location: Munich
Age: 48
Posts: 450
Looks impressive. What do the variable names mean?


Btw, shouldn't the y read y1 instead?

Last edited by AGS; 10 February 2015 at 08:53.
AGS is offline  
Old 10 February 2015, 16:51   #4
AGS
XoXo/Tasko Developer
 
AGS's Avatar
 
Join Date: Dec 2013
Location: Munich
Age: 48
Posts: 450
In the meanwhile I coded a function that allocates a color on the fly and drops it into an off-rastport. I draw a horizontal line of 32 pixels length into the off-rastport. Then I calculate the next color and draw the next line with that at the next y position and so on. When the result is ready, I blit the contents of the off-rastport's bitmap into the window rastport. I plan to 'fill' the destination rectangle with the content of the off-bitmap in x-direction, when the rectangle is wider than the off-rastport which is yet always 32 pixel. What do you think? Is this faster than drawing all gradient pixels directly into the window? Proper gradient formula still missing ... puzzling. PS: I am on the way, see next post below!

Is my off-RastPort somehow connected to the screen? I use the screen's color map to fetch the pen. I wonder why the color allocation seems to work. Do you know?

Last edited by AGS; 10 February 2015 at 17:30.
AGS is offline  
Old 10 February 2015, 17:21   #5
AGS
XoXo/Tasko Developer
 
AGS's Avatar
 
Join Date: Dec 2013
Location: Munich
Age: 48
Posts: 450
I found this elsewhere:

${rgb} = $startvalue{rgb} - ((($endvalue{rgb}-$startvalue{rgb})/-$rectheight)*$linenumber)

Looks easy but in ASM i can't do the divide first as there are no values with comma. Would it be an idea to shift everything up and then down again?

Last edited by AGS; 10 February 2015 at 17:26.
AGS is offline  
Old 10 February 2015, 18:52   #6
AGS
XoXo/Tasko Developer
 
AGS's Avatar
 
Join Date: Dec 2013
Location: Munich
Age: 48
Posts: 450
Here is my current progress:

Code:
test	; ${rgb} = $startvalue{rgb} - ((($endvalue{rgb}-$startvalue{rgb})/-$rectheight)*$linenumber)

		move.w	8+6(a7),d3		; rectangle height at offset on stack
		neg.w	d3			; negative value required

		ext.l	d3

	pushm	d4-d7

		move.l	#$123400,d6		; color 1
		move.l	#$6543ff,d7		; color 2
		
		; fetch the blue values
		
		moveq	#0,d0
		moveq	#0,d1
		move.b	d6,d0			; startvalue
		move.b	d7,d1			; endvalue	

		sub.l	d0,d1
	;	ext.w	d1
	;	ext.l	d1
		lsl.l	#8,d1
		divs.l	d3,d1
		move.l	d0,d5
		move.l	d1,d4

		neg.w	d3			; make height positive again
		subq.w	#1,d3			; sub 1 for loop
		ext.l	d3

.grad_loop	move.w	d4,d1
		muls.l	d3,d1
		asr.w	#8,d1
		move.l	d5,d0
		sub.l	d1,d0

		and.l	#$FF,d0
		
		move.l	16(a7),a1
		bsr	make_color_line

		dbf	d3,.grad_loop

	popm	d4-d7
It now should only handle the blue component. As always it yet does not bring the expected result. Can you see something? It should be the formula in the comment at top of source, but maybe it isn't.

'make_color_line' draws a line of the gradient, where d0 is the color, d3 the line number and a1 the off-rastport.


EDIT: I changed the func to use LONG mul/div and now it works. Question remains, if all that can be done also on plain 68000.

EDIT 2: I checked the results of the func with and without the shifting and output seems to be the same!

Last edited by AGS; 10 February 2015 at 19:36.
AGS is offline  
Old 10 February 2015, 20:02   #7
AGS
XoXo/Tasko Developer
 
AGS's Avatar
 
Join Date: Dec 2013
Location: Munich
Age: 48
Posts: 450
Here is the optimized variant, runs now with div/mul of WORDS. The need to be signed.

Code:
test	; ${rgb} = $startvalue{rgb} - ((($endvalue{rgb}-$startvalue{rgb})/-$rectheight)*$linenumber)

		move.w	8+6(a7),d3		; rectangle height at offset on stack
		neg.w	d3			; negative value required

		ext.l	d3

		move.l	#$123400,d6		; color 1
		move.l	#$6543ff,d7		; color 2
		
		; fetch the blue values
		
		moveq	#0,d0
		moveq	#0,d1
		move.b	d6,d0			; startvalue
		move.b	d7,d1			; endvalue	

		sub.b	d0,d1
		divs.w	d3,d1
		move.b	d0,d5
		move.b	d1,d4

		neg.w	d3			; make height positive again
		subq.w	#1,d3			; sub 1 for loop

.grad_loop	move.b	d4,d1
		ext.w	d1
		muls.w	d3,d1
		move.b	d5,d0
		sub.b	d1,d0

		and.l	#$FF,d0
		lsl.l	#8,d0
		lsl.l	#8,d0
		
		move.l	16(a7),a1
		bsr	make_color_line

		dbf	d3,.grad_loop
Oh, I found that this code only works when color 1 value < color 2 value.

Last edited by AGS; 10 February 2015 at 21:16.
AGS is offline  
Old 10 February 2015, 22:02   #8
AGS
XoXo/Tasko Developer
 
AGS's Avatar
 
Join Date: Dec 2013
Location: Munich
Age: 48
Posts: 450
Here is another method I found. However it is with floating point variables. How to do that in ASM?

Code:
diff_r=r2-r1
diff_g=g2-g1
diff_b=b2-b1

for (l=0; l<lines; l++) {
    factor= l / lines;

    r= r1 + diff_r * factor);
    g= g1 + diff_g * factor);
    b= b1 + diff_b * factor);
}
AGS is offline  
Old 10 February 2015, 22:08   #9
thomas
Registered User
 
thomas's Avatar
 
Join Date: Jan 2002
Location: Germany
Posts: 7,002
Quote:
Originally Posted by AGS View Post
Looks impressive. What do the variable names mean?
Code:
x0 = left edge of area to fill
y0 = top edge of area to fill
x1 = right edge of area to fill (= x0 + width - 1)
y1 = bottom edge of area to fill (= y0 + height - 1)
r0 = top color, red component
g0 = top color, green component
b0 = top color, blue component
r1 = bottom color, red component
g1 = bottom color, green component
b1 = bottom color, blue component

do y = y0 to y1
   r = r0 + (y - y0) * (r1 - r0) / (y1 - y0)
   g = g0 + (y - y0) * (g1 - g0) / (y1 - y0)
   b = b0 + (y - y0) * (b1 - b0) / (y1 - y0)
   set color (r, g, b)
   draw line from (x0,y) to (x1,y)
end
And no, y should not read y1, then the entire formula would be senseless.
thomas is offline  
Old 10 February 2015, 22:17   #10
AGS
XoXo/Tasko Developer
 
AGS's Avatar
 
Join Date: Dec 2013
Location: Munich
Age: 48
Posts: 450
Thomas, thanks. As far I can see that method is the same as the one in my last posting. We can precalculate the rgb 1 and rgb 2 differences as well as the rectangle height. your order of mul and div instructions would need not comma numbers, is this so?

And further, I compared the formula which I coded before with your one an found, that they are principally the same, while my code tries to do the divisions only once and then only multiplications in the loop. Thus, my method was correct but implementation failed so far.

Last edited by AGS; 10 February 2015 at 22:37.
AGS is offline  
Old 11 February 2015, 05:09   #11
AGS
XoXo/Tasko Developer
 
AGS's Avatar
 
Join Date: Dec 2013
Location: Munich
Age: 48
Posts: 450
Question

EDIT: Here is my full code for all channels. It works for both positive and negative values but the resulting gradient is not exact. The effective destination color differs from the defined one. I tried Thomas' variant also and there was that error, too.

Code:
		move.w	#21,d3			; rectangle height

		move.l	#$ff00ff,d6		; color 1
		move.l	#$00ff88,d7		; color 2

		moveq	#0,d2

		; precalc the blue component
		
		moveq	#0,d1
		move.b	d7,d1	
		move.b	d6,d2

		sub.w	d2,d1
		ext.l	d1
		divs.w	d3,d1
		move.b	d1,d4

		lsl.w	#8,d4
		lsr.l	#8,d7
		ror.l	#8,d6

		; precalc the green component
		
		moveq	#0,d1
		move.b	d7,d1	
		move.b	d6,d2

		sub.w	d2,d1
		ext.l	d1
		divs.w	d3,d1
		move.b	d1,d4

		lsl.l	#8,d4
		lsr.w	#8,d7
		ror.l	#8,d6

		; precalc the red component
		
		moveq	#0,d1
		move.b	d7,d1	
		move.b	d6,d2

		sub.w	d2,d1
		ext.l	d1
		divs.w	d3,d1
		move.b	d1,d4

		; d4 is now the precalculated 0BGR (in this order!)
		; d6 is now $GGBB00RR (again, in this order!)

		subq.w	#1,d3			; sub 1 for loop

.grad_loop	move.b	d4,d1
		ext.w	d1
		muls.w	d3,d1
		move.b	d6,d0
		add.b	d1,d0

		lsl.l	#8,d0
		ror.l	#8,d4
		rol.l	#8,d6

		move.b	d4,d1
		ext.w	d1
		muls.w	d3,d1
		move.b	d6,d0
		add.b	d1,d0

		lsl.l	#8,d0
		ror.l	#8,d4
		rol.l	#8,d6

		move.b	d4,d1
		ext.w	d1
		muls.w	d3,d1
		move.b	d6,d0
		add.b	d1,d0

		swap	d6
		swap	d4

		move.l	16(a7),a1
		bsr	make_color_line

		dbf	d3,.grad_loop
And here is Thomas' variant:

Code:
		move.w	8+6(a7),d2		; rectangle height at offset on stack

		move.l	#$ff00ff,d6		; color 1
		move.l	#$00ff88,d7		; color 2

		; precalc the blue component
		
		move.b	d7,d3
		sub.b	d6,d3
		ext.w	d3

		lsr.l	#8,d7
		ror.l	#8,d6

		; precalc the green component
		
		move.b	d7,d4
		sub.b	d6,d4
		ext.w	d4

		lsr.w	#8,d7
		ror.l	#8,d6

		; precalc the red component
		
		move.b	d7,d5
		sub.b	d6,d5
		ext.w	d5
		
		swap	d6			; reset d6 to 00RRGGBB

		move.w	d2,d7			; remember height
		beq	.null
		
		subq.w	#1,d2			; sub 1 for lines loop

.lines_loop	move.w	d3,d1
		muls.w	d2,d1
		divs.w	d7,d1
		move.b	d1,d0
		add.b	d6,d0

		ror.l	#8,d0			; d0 -> bb000000
		ror.l	#8,d6
		
		move.w	d4,d1
		muls.w	d2,d1
		divs.w	d7,d1
		move.b	d1,d0
		add.b	d6,d0

		ror.l	#8,d0			; d0 -> ggbb0000
		ror.l	#8,d6

		move.w	d5,d1
		muls.w	d2,d1
		divs.w	d7,d1
		move.b	d1,d0
		add.b	d6,d0

		swap	d0			; d0 -> 00rrggbb
		swap	d6
	
		move.l	16(a7),a1
		
		movem.w	d2/d3,-(a7)
		move.w	d2,d3
		bsr	make_color_line
		movem.w	(a7)+,d2/d3

		dbf	d2,.lines_loop

.null
EDIT: I found that Thomas' variant is EXACT when we do the sub 1 from the height before the precalculations. This was my error. Good work Thomas.

Last edited by AGS; 11 February 2015 at 07:41.
AGS is offline  
Old 11 February 2015, 07:14   #12
Thorham
Computer Nerd
 
Thorham's Avatar
 
Join Date: Sep 2007
Location: Rotterdam/Netherlands
Age: 47
Posts: 3,764
Here's a 16bit.16bit fixed point version for 68020+. You can easily change it to 8bit.8bit for 68000, but it's less precise.

Code:
;
; input:
;
; a0 = start rgb
; a1 = end rgb
; d0 = number of steps
;
; output:
;
; a2 = gradiant table
;
; Start and end rgb are longwords, so red 255
; would be $000000ff. Gradient table is in rgb
; format too, but as bytes.
;
gradient
    movem.l d0-a6,-(sp)

    subq.l  #1,d0

    move.l  (a0)+,d1
    swap    d1
    move.l  (a1)+,d2
    swap    d2
    sub.l   d1,d2
    divs.l  d0,d2

    move.l  (a0)+,d3
    swap    d3
    move.l  (a1)+,d4
    swap    d4
    sub.l   d3,d4
    divs.l  d0,d4

    move.l  (a0)+,d5
    swap    d5
    move.l  (a1)+,d6
    swap    d6
    sub.l   d5,d6
    divs.l  d0,d6

.loop
    move.l  d1,d7
    swap    d7
    move.b  d7,(a2)+
    add.l   d2,d1

    move.l  d3,d7
    swap    d7
    move.b  d7,(a2)+
    add.l   d4,d3

    move.l  d5,d7
    swap    d7
    move.b  d7,(a2)+
    add.l   d6,d5

    dbra    d0,.loop

    movem.l (sp)+,d0-a6
    rts

Last edited by Thorham; 11 February 2015 at 07:20.
Thorham is offline  
Old 11 February 2015, 07:51   #13
AGS
XoXo/Tasko Developer
 
AGS's Avatar
 
Join Date: Dec 2013
Location: Munich
Age: 48
Posts: 450
Thorham, I guess your variant would be fastest. I think I can put everything into registers. I'll check it out later. Thanks.
AGS is offline  
Old 11 February 2015, 11:20   #14
AGS
XoXo/Tasko Developer
 
AGS's Avatar
 
Join Date: Dec 2013
Location: Munich
Age: 48
Posts: 450
I've got news. What I said before, substracting 1 from the height before the precalculations in Thomas' variant is NOT always correct. It looks like sometimes it is required and sometimes not. And sometimes neither of both works and the result is not precise. How could I regard this? I am thinking of some rounding ...

EDIT: I kept the sub before the precalcs and added a roundig to the div. Now results are much better.

Rounding this way: add half of the divisor to the dividend


EDIT 2: I got Thomas' variant now exact by doing the sub before the precalc and by clearing some registers before using them. was my fault. No rounding needed. Now it works as it should. I will see how it performs later when I draw a lot of gradients.


EDIT 3: Performs well. Can be directly drawn into the window rastport.

Last edited by AGS; 11 February 2015 at 17:47.
AGS is offline  
 


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools

Similar Threads
Thread Thread Starter Forum Replies Last Post
Best Amiga Games (gradient Colors) sebmacfly Nostalgia & memories 68 28 January 2024 11:09
Color Saturation and Color Tint/Hue Retro-Nerd support.WinUAE 22 02 August 2018 10:38
Printing in color with WinUAE on color laser source support.Apps 7 14 April 2013 00:32
WbVerlauf copper gradient in fastest possible not working 100% rsn8887 support.WinUAE 2 24 April 2012 03:08
ISO true color to 256 color algorithm Lord Riton Coders. General 19 15 April 2011 17:49

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +2. The time now is 11:22.

Top

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.
Page generated in 0.09450 seconds with 13 queries