10 February 2015, 08:32 | #1 |
XoXo/Tasko Developer
Join Date: Dec 2013
Location: Munich
Age: 48
Posts: 450
|
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 |
10 February 2015, 08:42 | #2 |
Registered User
Join Date: Jan 2002
Location: Germany
Posts: 7,002
|
n = n0 + (y - y0) * (n1 - n0) / (y1 - y0)
|
10 February 2015, 08:44 | #3 |
XoXo/Tasko Developer
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. |
10 February 2015, 16:51 | #4 |
XoXo/Tasko Developer
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. |
10 February 2015, 17:21 | #5 |
XoXo/Tasko Developer
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. |
10 February 2015, 18:52 | #6 |
XoXo/Tasko Developer
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 '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. |
10 February 2015, 20:02 | #7 |
XoXo/Tasko Developer
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 Last edited by AGS; 10 February 2015 at 21:16. |
10 February 2015, 22:02 | #8 |
XoXo/Tasko Developer
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); } |
10 February 2015, 22:08 | #9 |
Registered User
Join Date: Jan 2002
Location: Germany
Posts: 7,002
|
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 |
10 February 2015, 22:17 | #10 |
XoXo/Tasko Developer
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. |
11 February 2015, 05:09 | #11 |
XoXo/Tasko Developer
Join Date: Dec 2013
Location: Munich
Age: 48
Posts: 450
|
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 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 Last edited by AGS; 11 February 2015 at 07:41. |
11 February 2015, 07:14 | #12 |
Computer Nerd
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. |
11 February 2015, 07:51 | #13 |
XoXo/Tasko Developer
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.
|
11 February 2015, 11:20 | #14 |
XoXo/Tasko Developer
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. |
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 |
|
|