Author Topic: how do you handle RGB color?  (Read 1910 times)

0 Members and 1 Guest are viewing this topic.

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
how do you handle RGB color?
« on: September 12, 2018, 01:47:33 pm »
so, I am finalizing my rastering algorithm for solving this problem:

Code: [Select]
  | vertex0.x     vertex1.x     vertex3.x      0 | | weight0     |   | point.x |
  | vertex0.y     vertex1.y     vertex3.y      0 | | weight1     | = | point.y |
  | vertex0.color vertex1.color vertex3.color -1 | | weight2     |   | 0       |
  | 1             1             1              0 | | point.color |   | 1       |

and on the 4th equation

Code: [Select]
point.color = vertex0.color * weight0 + vertex1.color * weight1 + vertex2.color * weight2

I come across a doubt: how to handle the RGB_color?

you have a color associated with each vertex of the triangle, and the algorithm does a lot of computations with them

RGB color is defined as a 32bit-size array of bits, with three fields of 8bit each

R is 8bit
G is 8bit
B is 8bit

Code: [Select]
unused   R      G      B
______.______.______.______

---------- 32bit ----------

hence, formally it's a number with 12bit of information.


The doubt is: do I have to multiply this 12bit number by a weight factor, or do I have to multiply each component by the weight factor?


let's exemplify what I have to do in HDL by a piece of C (sort of proof of concept)

Code: [Select]
typedef union
{
    struct
    {
        uint8_t na;
        uint8_t R;
        uint8_t G;
        uint8_t B;
    }        as;
    sint32_t raw;
} color_rgb_t;

CaseA
Code: [Select]
color_rgb_t color_rgb_in0;
color_rgb_t color_rgb_in1;
color_rgb_t color_rgb_in2;
color_rgb_t color_rgb_out;

color_out.as.R = color_rgb_in0.as.R * weight + ... ;
color_out.as.G = color_rgb_in0.as.G * weight + ... ;
color_out.as.B = color_rgb_in0.as.B * weight + ... ;

or

CaseB
Code: [Select]
color_rgb_t color_rgb_in0;
color_rgb_t color_rgb_in1;
color_rgb_t color_rgb_in2;
color_rgb_t color_rgb_out;

color_out.raw = color_rgb_in0.raw * weight + ... ;


or is there a trick, something ... because with caseA I have to triplicate the numbers of MULs in my algorithm  :palm: :palm: :palm:


just to face to the system of equations, the caseA looks this way

Code: [Select]
  | vertex0.x     vertex1.x     vertex3.x      0              0              0  | | weight0       |   | point.x |
  | vertex0.y     vertex1.y     vertex3.y      0              0              0  | | weight1       | = | point.y |
  | vertex0.color vertex1.color vertex3.color  -1             0              0  | | weight2       |   | 0       |
  | vertex0.color vertex1.color vertex3.color  0              -1             0  | | point.color.R |   | 0       |
  | vertex0.color vertex1.color vertex3.color  0              0              -1 | | point.color.G |   | 0       |
  | 1             1             1              0              0              0  | | point.color.B |   | 1       |
 

Offline dmills

  • Super Contributor
  • ***
  • Posts: 2093
  • Country: gb
Re: how do you handle RGB color?
« Reply #1 on: September 12, 2018, 02:16:49 pm »
You have to do the first one.

Worse you really want to do the first one in a linear colourspace rather bigger then 8 bits per component, then convert back to the R'G'B' gamma corrected 8 bit space with appropriate dithering.

Fortunately FPGAs have usually got LOTS of DSP blocks.

One software possibility:

Given 8 bit inputs and an 8 bit gain value, pack the RGB components into 48 bits of a 64 bit unsigned type, with a 0x00 between each one.

Now do the multiply (64 bit * 8 bit) giving 3 16 bit RGB results packed into the low 48 bits of the result.

uint64_t input = 0x0000RR00GG00BB;
uint8_t weight = ...;

uint64_t result = input * weight;

uint16_t r = (result >> 32) & 0xffff;
uint16_t g = (result >> 16) & 0xffff;
uint16_t b = (result) & 0xffff;

The basic idea can be applied at other word lengths, so for example with 6 bit RGB and say 4 bit Alpha you could put the 6 bits in the lowest 6 bits of each 10 bit fragment of a 32 bit word and then multiply by the 4 bit alpha giving a 30 bit output.

Note that depending on your CPU architecture, barrel shifts can be anything from free to very expensive.

Regards, Dan.
 

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Re: how do you handle RGB color?
« Reply #2 on: September 12, 2018, 03:39:10 pm »
Note that depending on your CPU architecture, barrel shifts can be anything from free to very expensive.

The color-interpolation is performed by a dedicated blitter, the CPU is a softcore implemented in a second FPGA, and these two FPGAs communicate via a high-speed serial link, basically, the CPU t gives commands to the blitter, and the blitter executes them.


But I am consuming too many DSP slices for this algorithm ... 
 

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Re: how do you handle RGB color?
« Reply #3 on: September 12, 2018, 03:45:01 pm »
with n-bit of color's component, we have the following list of colors

Code: [Select]
color_RGB[0000]={0x0,0x0,0x0}=0x0000
color_RGB[0001]={0x0,0x0,0x1}=0x0001
color_RGB[0002]={0x0,0x0,0x2}=0x0002
color_RGB[0003]={0x0,0x0,0x3}=0x0003
color_RGB[0004]={0x0,0x1,0x0}=0x0004
color_RGB[0005]={0x0,0x1,0x1}=0x0005
color_RGB[0006]={0x0,0x1,0x2}=0x0006
color_RGB[0007]={0x0,0x1,0x3}=0x0007
color_RGB[0008]={0x0,0x2,0x0}=0x0008
color_RGB[0009]={0x0,0x2,0x1}=0x0009
color_RGB[0010]={0x0,0x2,0x2}=0x000a
color_RGB[0011]={0x0,0x2,0x3}=0x000b
color_RGB[0012]={0x0,0x3,0x0}=0x000c
color_RGB[0013]={0x0,0x3,0x1}=0x000d
color_RGB[0014]={0x0,0x3,0x2}=0x000e
color_RGB[0015]={0x0,0x3,0x3}=0x000f
color_RGB[0016]={0x1,0x0,0x0}=0x0010
color_RGB[0017]={0x1,0x0,0x1}=0x0011
color_RGB[0018]={0x1,0x0,0x2}=0x0012
color_RGB[0019]={0x1,0x0,0x3}=0x0013
color_RGB[0020]={0x1,0x1,0x0}=0x0014
color_RGB[0021]={0x1,0x1,0x1}=0x0015
color_RGB[0022]={0x1,0x1,0x2}=0x0016
color_RGB[0023]={0x1,0x1,0x3}=0x0017
color_RGB[0024]={0x1,0x2,0x0}=0x0018
color_RGB[0025]={0x1,0x2,0x1}=0x0019
color_RGB[0026]={0x1,0x2,0x2}=0x001a
color_RGB[0027]={0x1,0x2,0x3}=0x001b
color_RGB[0028]={0x1,0x3,0x0}=0x001c
color_RGB[0029]={0x1,0x3,0x1}=0x001d
color_RGB[0030]={0x1,0x3,0x2}=0x001e
color_RGB[0031]={0x1,0x3,0x3}=0x001f
color_RGB[0032]={0x2,0x0,0x0}=0x0020
color_RGB[0033]={0x2,0x0,0x1}=0x0021
color_RGB[0034]={0x2,0x0,0x2}=0x0022
color_RGB[0035]={0x2,0x0,0x3}=0x0023
color_RGB[0036]={0x2,0x1,0x0}=0x0024
color_RGB[0037]={0x2,0x1,0x1}=0x0025
color_RGB[0038]={0x2,0x1,0x2}=0x0026
color_RGB[0039]={0x2,0x1,0x3}=0x0027
color_RGB[0040]={0x2,0x2,0x0}=0x0028
color_RGB[0041]={0x2,0x2,0x1}=0x0029
color_RGB[0042]={0x2,0x2,0x2}=0x002a
color_RGB[0043]={0x2,0x2,0x3}=0x002b
color_RGB[0044]={0x2,0x3,0x0}=0x002c
color_RGB[0045]={0x2,0x3,0x1}=0x002d
color_RGB[0046]={0x2,0x3,0x2}=0x002e
color_RGB[0047]={0x2,0x3,0x3}=0x002f
color_RGB[0048]={0x3,0x0,0x0}=0x0030
color_RGB[0049]={0x3,0x0,0x1}=0x0031
color_RGB[0050]={0x3,0x0,0x2}=0x0032
color_RGB[0051]={0x3,0x0,0x3}=0x0033
color_RGB[0052]={0x3,0x1,0x0}=0x0034
color_RGB[0053]={0x3,0x1,0x1}=0x0035
color_RGB[0054]={0x3,0x1,0x2}=0x0036
color_RGB[0055]={0x3,0x1,0x3}=0x0037
color_RGB[0056]={0x3,0x2,0x0}=0x0038
color_RGB[0057]={0x3,0x2,0x1}=0x0039
color_RGB[0058]={0x3,0x2,0x2}=0x003a
color_RGB[0059]={0x3,0x2,0x3}=0x003b
color_RGB[0060]={0x3,0x3,0x0}=0x003c
color_RGB[0061]={0x3,0x3,0x1}=0x003d
color_RGB[0062]={0x3,0x3,0x2}=0x003e
color_RGB[0063]={0x3,0x3,0x3}=0x003f

in this example there 2 bit of colors

Code: [Select]
typedef union
{
    struct
    {
        uint8_t B:2;
        uint8_t G:2;
        uint8_t R:2;
        uint8_t na:2;
    }        as;
    uint8_t raw;
} color_rgb222_t;

maybe we can use a sort of array of colors? Hence when the interpolator says "color_out=x", we can access the array(x), that gives the RGB components ...  :-// :-// :-//

(maybe this can be calculated, to save precious LUTs area on the FPGA)

is it a good idea? bad idea?  :-//


ummm but another irritating detail is that white is 0xf...f, while black is 0x0...0, with a pretty inverted logic  :palm: :palm: :palm:

Code: [Select]
/*
 * Red    Green  Blue  Color
 * 255    0      0     Red
 * 0      255    0     Green
 * 0      0      255   Blue
 * 255    255    0     Yellow
 * 255    255    255   White
 * 100    100    100   Gray
 * 0      0      0     Black
 */
 

Offline ajb

  • Super Contributor
  • ***
  • Posts: 2599
  • Country: us
Re: how do you handle RGB color?
« Reply #4 on: September 12, 2018, 05:44:18 pm »
How much color resolution do you actually need in the end?  What's the weighting range?  How many processing steps must the colors go through?

To elaborate on dmills' post, the problem with packing the RGB values into a single integer and them multiplying the whole thing by the weight is that the color channels in the lower bits will overflow into the other color channels unless you space them out sufficiently.  If the weight only ranges from say 0...2, then you only need two bits of spacing.  If weight ranges from 0...64k, then you need 16 bits of spacing!  If your weights are fixed-point or fractional values then you need additional bits in the intermediate values.  Likewise, you need space for the carry bits when you add up all of the weighted values.  So with a large weighting range it quickly becomes sensible to use a 32-bit value for each color channel as an intermediate value and then truncate (and optionally pack) the channels after you're done.



ummm but another irritating detail is that white is 0xf...f, while black is 0x0...0, with a pretty inverted logic  :palm: :palm: :palm:

How is that inverted?  It maps directly to the additive model of color mixing. 
 

Offline dmills

  • Super Contributor
  • ***
  • Posts: 2093
  • Country: gb
Re: how do you handle RGB color?
« Reply #5 on: September 12, 2018, 05:51:24 pm »
-- 6 bits of RGB * 4 bits of factor, probably 1 or two DSP, needs some registers to pipeline the partial products if you want good speed.
-- Completely untested, almost certainly does not compile.

signal r : std_logic_vector (5 downto 0);
signal g : std_logic_vector (5 downto 0);
signal b : std_logic_vector (5 downto 0);
signal fac : std_logic_vector (3 downto 0);

signal multiplier_input : std_logic_vector (25 downto 0); -- 6 + 4 bits, three times with the top 4 bits chopped off
signal multiplier_output : std:logic:vector (29 downto 0);

multiplier_input <= "0000" & r & "0000" & g & "0000" & b;

multiplier_output = multiplier_input * fac;

r = multiplier_output(29 downto 23);
g = multiplier_output(22 downto 16);
b = multiplier_output(15 downto 9);

I take it you are fully pipeling your multipliers and using the appropriate sets of registers to get maximum speed out of the things? The usual limit on FPGA graphics is more in the line of memory bandwidth issues with the frame buffer then running out of logic.

Regards, Dan.
 

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Re: how do you handle RGB color?
« Reply #6 on: September 12, 2018, 08:17:43 pm »
@Dan
your idea is nice, but it tends to overflow the multiplier.
 

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Re: how do you handle RGB color?
« Reply #7 on: September 12, 2018, 08:19:08 pm »
How much color resolution do you actually need in the end?

8bits for R, 8bits for G, 8bits for B.

How many processing steps must the colors go through?

A lot. Each interaction in the loop.

fixed-point

the DSP engine is fixed-point 1616 with saturation

How is that inverted?  It maps directly to the additive model of color mixing.

With RGB_888, the white is 255, black is 0, I was expecting exactly the opposite, that makes more sense for me and for my algorithm. But it doesn't matter, it's not a problem.
 

Offline ajb

  • Super Contributor
  • ***
  • Posts: 2599
  • Country: us
Re: how do you handle RGB color?
« Reply #8 on: September 12, 2018, 08:43:37 pm »
To represent all possible values with no loss of precision, 8 bits per color channel with a 16.16 multiplier means your intermediate results would need to be 40 bits.  To add up n terms, you need an extra log2(n) bits to avoid overflow.  In the real world 16.16 is probably way more range than you actually need, so you can probably drop a number of bits in the intermediate calculations, but it depends on the nature of your algorithm.  If color channels get bounced way down and then way back up in the course of computation then you may well need all 40+ bits.  It's also possible that eight to twelve bit intermediate values will give you acceptable results.  I have no idea what you're actually doing so it's hard to say.  I would guess that you probably want in the neighborhood of 24-32 bits per channel for the intermediate values, though, if you want eight meaningful bits of output.
 

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Re: how do you handle RGB color?
« Reply #9 on: September 12, 2018, 09:04:32 pm »
it depends on the nature of your algorithm

The DSP engine is internally 40bit for both MULs and DIVs, and it's saturated. The algorithm of the color-interpolator tends to overflow with fewer bits.

This part is OK, and this NOT the problem, I know how to create and handle the math, my problem is how to handle RGB_colors, since I am not willing to touch the DSP-engine, neither I am not willing to triplicate the MULs and DIVs I need for the approach exposed in CaseA (see my first post).

The FPGA that implements the blitter unit can't consume all the DSP slices for the color-interpolator, it has other jobs to do.
 

Offline ajb

  • Super Contributor
  • ***
  • Posts: 2599
  • Country: us
Re: how do you handle RGB color?
« Reply #10 on: September 13, 2018, 02:05:05 am »
The DSP engine is internally 40bit for both MULs and DIVs, and it's saturated. The algorithm of the color-interpolator tends to overflow with fewer bits.

If you've already established that you need 40 bits per channel to make your algorithm work, then I'm afraid that you have your answer.  You'll need 3x40 bits one way or another.  The only other possibility is maybe moving to a different color space where you can sacrifice some resolution in one axis, but I doubt that would save you enough operations to be worthwhile, especially accounting for the fact that you'll have to translate between color spaces.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf