| 1 | package kaka.cakelight; |
| 2 | |
| 3 | import java.util.Arrays; |
| 4 | |
| 5 | public class LedFrame { |
| 6 | private Configuration config; |
| 7 | private byte[] bytes; |
| 8 | private int stride; |
| 9 | private int roff, goff, boff; // RGB offsets |
| 10 | |
| 11 | /** |
| 12 | * @return a frame initiated to black |
| 13 | */ |
| 14 | public static LedFrame from(Configuration config) { |
| 15 | LedFrame frame = new LedFrame(); |
| 16 | frame.config = config; |
| 17 | switch (config.leds.type) { |
| 18 | /* |
| 19 | * The WS2801 strip takes its input as a plain list of 24-bit colors in RBG order (at least mine did). |
| 20 | */ |
| 21 | case WS2801: |
| 22 | frame.stride = 3; |
| 23 | frame.roff = 0; |
| 24 | frame.goff = 2; |
| 25 | frame.boff = 1; |
| 26 | frame.bytes = new byte[config.leds.getCount() * frame.stride]; |
| 27 | break; |
| 28 | |
| 29 | /* |
| 30 | * The APA102 strip takes its input as: |
| 31 | * <ol> |
| 32 | * <li>a start frame of 4 bytes (all zeroes)</li> |
| 33 | * <li>a frame of 4 bytes for each LED (111 (3 bits) + global illumination (5 bits) + BGR)</li> |
| 34 | * <li>an (optional) end frame of 4 bytes (all ones)</li> |
| 35 | * </ol> |
| 36 | */ |
| 37 | case APA102: |
| 38 | frame.stride = 4; |
| 39 | frame.roff = 3 + 4; |
| 40 | frame.goff = 2 + 4; |
| 41 | frame.boff = 1 + 4; |
| 42 | frame.bytes = new byte[4 + config.leds.getCount() * frame.stride + 4]; |
| 43 | Arrays.fill(frame.bytes, 4, frame.bytes.length - 5, (byte)(0b11100000 | config.leds.brightness)); // Initiate the first byte of each LED |
| 44 | Arrays.fill(frame.bytes, frame.bytes.length - 5, frame.bytes.length - 1, (byte)0xff); // Initiate the end frame with ones |
| 45 | break; |
| 46 | } |
| 47 | return frame; |
| 48 | } |
| 49 | |
| 50 | public void fillColor(int r, int g, int b) { |
| 51 | fillColor(Color.rgb(r, g, b)); |
| 52 | } |
| 53 | |
| 54 | public void fillColor(Color color) { |
| 55 | byte r = (byte)color.r(), g = (byte)color.g(), b = (byte)color.b(); // Gamma corrected values |
| 56 | for (int i = 0; i < bytes.length; i += stride) { |
| 57 | bytes[i + roff] = r; |
| 58 | bytes[i + goff] = g; |
| 59 | bytes[i + boff] = b; |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | public void setLedColor(int led, Color color) { |
| 64 | int offset = led * stride; |
| 65 | bytes[offset + roff] = (byte)color.r(); |
| 66 | bytes[offset + goff] = (byte)color.g(); |
| 67 | bytes[offset + boff] = (byte)color.b(); |
| 68 | } |
| 69 | |
| 70 | public Color getLedColor(int led) { |
| 71 | int offset = led * stride; |
| 72 | return Color.rgb( |
| 73 | bytes[offset + roff] & 0xff, |
| 74 | bytes[offset + goff] & 0xff, |
| 75 | bytes[offset + boff] & 0xff |
| 76 | ); |
| 77 | } |
| 78 | |
| 79 | public byte[] getBytes() { |
| 80 | return bytes; |
| 81 | } |
| 82 | |
| 83 | // TODO this needs to be improved |
| 84 | /** The x position of the led from 0.0-1.0. */ |
| 85 | public double xOf(int led) { |
| 86 | /* left */ if (led >= config.leds.cols * 2 + config.leds.rows) return 0; |
| 87 | /* top */ if (led >= config.leds.cols + config.leds.rows) return 1 - (double)(led - config.leds.cols - config.leds.rows) / config.leds.cols; |
| 88 | /* right */ if (led >= config.leds.cols) return 1; |
| 89 | /* bottom */ return (double)led / config.leds.cols; |
| 90 | } |
| 91 | |
| 92 | /** The y position of the led from 0.0-1.0. */ |
| 93 | public double yOf(int led) { |
| 94 | /* left */ if (led >= config.leds.cols * 2 + config.leds.rows) return (double)(led - config.leds.cols * 2 - config.leds.rows) / config.leds.rows; |
| 95 | /* top */ if (led >= config.leds.cols + config.leds.rows) return 0; |
| 96 | /* right */ if (led >= config.leds.cols) return 1 - (double)(led - config.leds.cols) / config.leds.rows; |
| 97 | /* bottom */ return 1; |
| 98 | } |
| 99 | } |