how do you handle RGB color? - Page 1
Forum section: electronics / general

author: legacy
author_rank: Super Contributor
timestamp: September 12, 2018, 01:47:33 pm
content: so, I am finalizing my rastering algorithm for solving this problem:Code:| 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 equationCode:point.color = vertex0.color * weight0 + vertex1.color * weight1 + vertex2.color * weight2I 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 themRGB color is defined as a 32bit-size array of bits, with three fields of 8bit eachR is 8bitG is 8bitB is 8bitCode: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:typedef union{struct{uint8_t na;uint8_t R;uint8_t G;uint8_t B;} as;sint32_t raw;} color_rgb_t;CaseACode: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 + ... ;orCaseBCode: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 withcaseAI have to triplicate the numbers of MULs in my algorithmjust to face to the system of equations, the caseA looks this wayCode:| 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 |

author: dmills
author_rank: Super Contributor
timestamp: September 12, 2018, 02:16:49 pm
content: 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.

author: legacy
author_rank: Super Contributor
timestamp: September 12, 2018, 03:39:10 pm
content: Quote from: dmills on September 12, 2018, 02:16:49 pmThe 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 ...

author: legacy
author_rank: Super Contributor
timestamp: September 12, 2018, 03:45:01 pm
content: with n-bit of color's component, we have the following list of colorsCode:color_RGB={0x0,0x0,0x0}=0x0000color_RGB={0x0,0x0,0x1}=0x0001color_RGB={0x0,0x0,0x2}=0x0002color_RGB={0x0,0x0,0x3}=0x0003color_RGB={0x0,0x1,0x0}=0x0004color_RGB={0x0,0x1,0x1}=0x0005color_RGB={0x0,0x1,0x2}=0x0006color_RGB={0x0,0x1,0x3}=0x0007color_RGB={0x0,0x2,0x0}=0x0008color_RGB={0x0,0x2,0x1}=0x0009color_RGB={0x0,0x2,0x2}=0x000acolor_RGB={0x0,0x2,0x3}=0x000bcolor_RGB={0x0,0x3,0x0}=0x000ccolor_RGB={0x0,0x3,0x1}=0x000dcolor_RGB={0x0,0x3,0x2}=0x000ecolor_RGB={0x0,0x3,0x3}=0x000fcolor_RGB={0x1,0x0,0x0}=0x0010color_RGB={0x1,0x0,0x1}=0x0011color_RGB={0x1,0x0,0x2}=0x0012color_RGB={0x1,0x0,0x3}=0x0013color_RGB={0x1,0x1,0x0}=0x0014color_RGB={0x1,0x1,0x1}=0x0015color_RGB={0x1,0x1,0x2}=0x0016color_RGB={0x1,0x1,0x3}=0x0017color_RGB={0x1,0x2,0x0}=0x0018color_RGB={0x1,0x2,0x1}=0x0019color_RGB={0x1,0x2,0x2}=0x001acolor_RGB={0x1,0x2,0x3}=0x001bcolor_RGB={0x1,0x3,0x0}=0x001ccolor_RGB={0x1,0x3,0x1}=0x001dcolor_RGB={0x1,0x3,0x2}=0x001ecolor_RGB={0x1,0x3,0x3}=0x001fcolor_RGB={0x2,0x0,0x0}=0x0020color_RGB={0x2,0x0,0x1}=0x0021color_RGB={0x2,0x0,0x2}=0x0022color_RGB={0x2,0x0,0x3}=0x0023color_RGB={0x2,0x1,0x0}=0x0024color_RGB={0x2,0x1,0x1}=0x0025color_RGB={0x2,0x1,0x2}=0x0026color_RGB={0x2,0x1,0x3}=0x0027color_RGB={0x2,0x2,0x0}=0x0028color_RGB={0x2,0x2,0x1}=0x0029color_RGB={0x2,0x2,0x2}=0x002acolor_RGB={0x2,0x2,0x3}=0x002bcolor_RGB={0x2,0x3,0x0}=0x002ccolor_RGB={0x2,0x3,0x1}=0x002dcolor_RGB={0x2,0x3,0x2}=0x002ecolor_RGB={0x2,0x3,0x3}=0x002fcolor_RGB={0x3,0x0,0x0}=0x0030color_RGB={0x3,0x0,0x1}=0x0031color_RGB={0x3,0x0,0x2}=0x0032color_RGB={0x3,0x0,0x3}=0x0033color_RGB={0x3,0x1,0x0}=0x0034color_RGB={0x3,0x1,0x1}=0x0035color_RGB={0x3,0x1,0x2}=0x0036color_RGB={0x3,0x1,0x3}=0x0037color_RGB={0x3,0x2,0x0}=0x0038color_RGB={0x3,0x2,0x1}=0x0039color_RGB={0x3,0x2,0x2}=0x003acolor_RGB={0x3,0x2,0x3}=0x003bcolor_RGB={0x3,0x3,0x0}=0x003ccolor_RGB={0x3,0x3,0x1}=0x003dcolor_RGB={0x3,0x3,0x2}=0x003ecolor_RGB={0x3,0x3,0x3}=0x003fin this example there 2 bit of colorsCode: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 logicCode:/** 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*/

author: ajb
author_rank: Super Contributor
timestamp: September 12, 2018, 05:44:18 pm
content: 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.Quote from: legacy on September 12, 2018, 03:45:01 pmHow is that inverted? It maps directly to the additive model of color mixing.

author: dmills
author_rank: Super Contributor
timestamp: September 12, 2018, 05:51:24 pm
content: -- 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 offsignal 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.

author: legacy
author_rank: Super Contributor
timestamp: September 12, 2018, 08:17:43 pm
content: @Danyour idea is nice, but it tends to overflow the multiplier.

author: legacy
author_rank: Super Contributor
timestamp: September 12, 2018, 08:19:08 pm
content: Quote from: ajb on September 12, 2018, 05:44:18 pm8bits for R, 8bits for G, 8bits for B.Quote from: ajb on September 12, 2018, 05:44:18 pmA lot. Each interaction in the loop.Quote from: ajb on September 12, 2018, 05:44:18 pmthe DSP engine is fixed-point 1616 with saturationQuote from: legacy on September 12, 2018, 03:45:01 pmWith 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.

author: ajb
author_rank: Super Contributor
timestamp: September 12, 2018, 08:43:37 pm
content: 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.

author: legacy
author_rank: Super Contributor
timestamp: September 12, 2018, 09:04:32 pm
content: Quote from: ajb on September 12, 2018, 08:43:37 pmThe 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 inCaseA(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.

author: ajb
author_rank: Super Contributor
timestamp: September 13, 2018, 02:05:05 am
content: Quote from: legacy on September 12, 2018, 09:04:32 pmIf 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.