Skip to content

Instantly share code, notes, and snippets.

@abachman
Created October 17, 2020 15:04
Show Gist options
  • Save abachman/d110efa55339d9de1224cca71488f4e3 to your computer and use it in GitHub Desktop.
Save abachman/d110efa55339d9de1224cca71488f4e3 to your computer and use it in GitHub Desktop.
turning RGB into GRB

pieces you asked about:

// translate the 24 bit color from RGB to the actual
// order used by the LED wiring.  GRB is the most common.
int colorWiring(int c) {
  int red = (c & 0xFF0000) >> 16;
  int green = (c & 0x00FF00) >> 8;
  int blue = (c & 0x0000FF);
  red = gammatable[red];
  green = gammatable[green];
  blue = gammatable[blue];
  return (green << 16) | (red << 8) | (blue); // GRB - most common wiring
}

This is taking an integer and flipping the order of the bits and calibrating the colors to go from screen brightness to LED brightness.

gammatable is calculated once and then reused. It always looks like this:

000 000 000 000 000 000 000 001 001 001 001 001 001 002 002 002
002 003 003 003 003 004 004 004 005 005 005 006 006 006 007 007
007 008 008 009 009 010 010 010 011 011 012 012 013 013 014 014
015 015 016 017 017 018 018 019 019 020 021 021 022 022 023 024
024 025 026 026 027 028 028 029 030 030 031 032 033 033 034 035
036 036 037 038 039 039 040 041 042 043 043 044 045 046 047 048
048 049 050 051 052 053 054 055 056 056 057 058 059 060 061 062
063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078
079 080 081 082 083 084 085 086 088 089 090 091 092 093 094 095
097 098 099 100 101 102 103 105 106 107 108 109 111 112 113 114
115 117 118 119 120 122 123 124 125 127 128 129 131 132 133 134
136 137 138 140 141 142 144 145 146 148 149 151 152 153 155 156
157 159 160 162 163 164 166 167 169 170 172 173 174 176 177 179
180 182 183 185 186 188 189 191 192 194 195 197 198 200 201 203
205 206 208 209 211 212 214 216 217 219 220 222 224 225 227 228
230 232 233 235 237 238 240 242 243 245 247 248 250 252 253 255

longer explanation

this will be handy when we get to the wire protocol

In Processing / Java an int, under the hood, is always a 24 digit binary number. We think of numbers like, 1 or 1000 as having a different number of digits. To the computer, though, all numbers of a particular type have the same number of digits. Also under the hood, color values in the array of pixels that constitute an image are not actually three separate values (R G B), but one int. This works out because the 24 bits of storage in an int split evenly into three 8-bit chunks. Each 8-bit (or 8 digit) binary number can represent a value between 0 and 255.

That means in Processing code like color(212, 99, 155) where 212 is the red part of the color, 99 is the green part, and 155 is the blue part--what Processing actually stores is a 24 digit binary number equal to (212 * 2^16) + (99 * 2^8) + 155 where ^ means, "to the power of". In the code given, << 16 is a "binary left shift", which is the same thing as * 2^16 ("times two to the sixteenth"). That calculation is done enough that computer people just made a symbol for it.

The >> 16 at the top of the function is the same thing in reverse.

In binary, you could think of it like this:

Let's say I start with color(212, 99, 155)

  212 = 11010100
  99  = 01100011
  155 = 10011011

That's stored as a 24 digit binary number:

  red     | green  | blue
  11010100 01100011 10011011

whose actual int value is:

  binary:  110101000110001110011011
  decimal: 13919131

So colorWiring(color(212, 99, 155)) is actually colorWiring(13919131).

The & and | symbols are binary "and" and "or" operations, respectively.

"and" means something like, "compare these two binary numbers and keep every 1 from the first number where there's also a 1 in the second number". 0xFF0000 is a hexidecimal number with all 1s in the first 8 bits of the 24 bit binary number. (it turns out hexidecimal-to-binary is a much easier conversion than decimal-to-binary, so computer people use it when they're doing bit-logic math like this)

Using & 0xFF0000 with an int is called a "bitmask" and it's used to keep all the 1s and set every other bit to 0. We can think of it like this:

i start with

  11010100 01100011 10011011

i & it with

  11111111 00000000 00000000

which leaves me with

    11010100 01100011 10011011
  & 11111111 00000000 00000000
  --------------------------
    11010100 00000000 00000000

then it's immediately >> 16 (right shifted 16 places) to leave

                      11010100

which, because it's stored as an int still, is actually

    00000000 00000000 11010100

which, in decimal, is

    212

And that's a bitmask plus bitshift operation that pulls our 24 bit, RGB value back out into three separate R G and B values, each of which is between 0 and 255--or 0x00 and 0xFF in hexidecimal.

It's safe to think of the colorWiring function as taking an int (which is a 24 bit binary number), splitting it into three equal parts, adjusting each part according to the gamma table, and then shuffling those parts. The shuffle, according to the comment, has to do with the physical wiring of the LEDs. So our computer gets video pixel values in RGB, but our LEDs want pixel values in GRB.

The | (read as "binary or"), symbol we see at the end reconstructs a 24 bit number out of the three 8 bit parts. "or" here means, "when you compare multiple numbers, keep the 1 in that digit if any of the numbers has it."

I'm not going to bother looking up the gamma-shifted values, so I'll just reuse the binary numbers from before in this example:

i start with

  red     | green  | blue
  11010100 01100011 10011011

i bit mask and right shift to get

  red = 11010100
  grn = 01100011
  blu = 10011011

i left shift each

  grn << 16 = 01100011 00000000 00000000
  red << 8  =          11010100 00000000
  blu       =                   10011011

and | the resulting ints together

    01100011 00000000 00000000
    00000000 11010100 00000000
  | 00000000 00000000 10011011
  ----------------------------
    01100011 11010100 10011011

to get a GRB value for my LED

And that's how colorWiring shuffles an RGB value to give us a GRB value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment