001// Copyright (c) FIRST and other WPILib contributors. 002// Open Source Software; you can modify and/or share it under the terms of 003// the WPILib BSD license file in the root directory of this project. 004 005package edu.wpi.first.wpilibj; 006 007import edu.wpi.first.wpilibj.util.Color; 008import edu.wpi.first.wpilibj.util.Color8Bit; 009 010/** Buffer storage for Addressable LEDs. */ 011public class AddressableLEDBuffer { 012 byte[] m_buffer; 013 014 /** 015 * Constructs a new LED buffer with the specified length. 016 * 017 * @param length The length of the buffer in pixels 018 */ 019 public AddressableLEDBuffer(int length) { 020 m_buffer = new byte[length * 4]; 021 } 022 023 /** 024 * Sets a specific led in the buffer. 025 * 026 * @param index the index to write 027 * @param r the r value [0-255] 028 * @param g the g value [0-255] 029 * @param b the b value [0-255] 030 */ 031 public void setRGB(int index, int r, int g, int b) { 032 m_buffer[index * 4] = (byte) b; 033 m_buffer[(index * 4) + 1] = (byte) g; 034 m_buffer[(index * 4) + 2] = (byte) r; 035 m_buffer[(index * 4) + 3] = 0; 036 } 037 038 /** 039 * Sets a specific led in the buffer. 040 * 041 * @param index the index to write 042 * @param h the h value [0-180) 043 * @param s the s value [0-255] 044 * @param v the v value [0-255] 045 */ 046 public void setHSV(final int index, final int h, final int s, final int v) { 047 if (s == 0) { 048 setRGB(index, v, v, v); 049 return; 050 } 051 052 // The below algorithm is copied from Color.fromHSV and moved here for 053 // performance reasons. 054 055 // Loosely based on 056 // https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB 057 // The hue range is split into 60 degree regions where in each region there 058 // is one rgb component at a low value (m), one at a high value (v) and one 059 // that changes (X) from low to high (X+m) or high to low (v-X) 060 061 // Difference between highest and lowest value of any rgb component 062 final int chroma = (s * v) / 255; 063 064 // Because hue is 0-180 rather than 0-360 use 30 not 60 065 final int region = (h / 30) % 6; 066 067 // Remainder converted from 0-30 to 0-255 068 final int remainder = (int) Math.round((h % 30) * (255 / 30.0)); 069 070 // Value of the lowest rgb component 071 final int m = v - chroma; 072 073 // Goes from 0 to chroma as hue increases 074 final int X = (chroma * remainder) >> 8; 075 076 switch (region) { 077 case 0: 078 setRGB(index, v, X + m, m); 079 break; 080 case 1: 081 setRGB(index, v - X, v, m); 082 break; 083 case 2: 084 setRGB(index, m, v, X + m); 085 break; 086 case 3: 087 setRGB(index, m, v - X, v); 088 break; 089 case 4: 090 setRGB(index, X + m, m, v); 091 break; 092 default: 093 setRGB(index, v, m, v - X); 094 break; 095 } 096 } 097 098 /** 099 * Sets a specific LED in the buffer. 100 * 101 * @param index The index to write 102 * @param color The color of the LED 103 */ 104 public void setLED(int index, Color color) { 105 setRGB(index, (int) (color.red * 255), (int) (color.green * 255), (int) (color.blue * 255)); 106 } 107 108 /** 109 * Sets a specific LED in the buffer. 110 * 111 * @param index The index to write 112 * @param color The color of the LED 113 */ 114 public void setLED(int index, Color8Bit color) { 115 setRGB(index, color.red, color.green, color.blue); 116 } 117 118 /** 119 * Gets the buffer length. 120 * 121 * @return the buffer length 122 */ 123 public int getLength() { 124 return m_buffer.length / 4; 125 } 126 127 /** 128 * Gets the color at the specified index. 129 * 130 * @param index the index to get 131 * @return the LED color at the specified index 132 */ 133 public Color8Bit getLED8Bit(int index) { 134 return new Color8Bit(getRed(index), getGreen(index), getBlue(index)); 135 } 136 137 /** 138 * Gets the color at the specified index. 139 * 140 * @param index the index to get 141 * @return the LED color at the specified index 142 */ 143 public Color getLED(int index) { 144 return new Color(getRed(index) / 255.0, getGreen(index) / 255.0, getBlue(index) / 255.0); 145 } 146 147 /** 148 * Gets the red channel of the color at the specified index. 149 * 150 * @param index the index of the LED to read 151 * @return the value of the red channel, from [0, 255] 152 */ 153 public int getRed(int index) { 154 return m_buffer[index * 4 + 2] & 0xFF; 155 } 156 157 /** 158 * Gets the green channel of the color at the specified index. 159 * 160 * @param index the index of the LED to read 161 * @return the value of the green channel, from [0, 255] 162 */ 163 public int getGreen(int index) { 164 return m_buffer[index * 4 + 1] & 0xFF; 165 } 166 167 /** 168 * Gets the blue channel of the color at the specified index. 169 * 170 * @param index the index of the LED to read 171 * @return the value of the blue channel, from [0, 255] 172 */ 173 public int getBlue(int index) { 174 return m_buffer[index * 4] & 0xFF; 175 } 176 177 /** 178 * A functional interface that allows for iteration over an LED buffer without manually writing an 179 * indexed for-loop. 180 */ 181 @FunctionalInterface 182 public interface IndexedColorIterator { 183 /** 184 * Accepts an index of an LED in the buffer and the red, green, and blue components of the 185 * currently stored color for that LED. 186 * 187 * @param index the index of the LED in the buffer that the red, green, and blue channels 188 * corresponds to 189 * @param r the value of the red channel of the color currently in the buffer at index {@code i} 190 * @param g the value of the green channel of the color currently in the buffer at index {@code 191 * i} 192 * @param b the value of the blue channel of the color currently in the buffer at index {@code 193 * i} 194 */ 195 void accept(int index, int r, int g, int b); 196 } 197 198 /** 199 * Iterates over the LEDs in the buffer, starting from index 0. The iterator function is passed 200 * the current index of iteration, along with the values for the red, green, and blue components 201 * of the color written to the LED at that index. 202 * 203 * @param iterator the iterator function to call for each LED in the buffer. 204 */ 205 public void forEach(IndexedColorIterator iterator) { 206 for (int i = 0; i < getLength(); i++) { 207 iterator.accept(i, getRed(i), getGreen(i), getBlue(i)); 208 } 209 } 210}