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}