/******************************************************************************* * Copyright (c) 2011, 2016 Eurotech and/or its affiliates * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Eurotech *******************************************************************************/ package org.eclipse.kura.raspberrypi.sensehat.ledmatrix; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.ByteOrder; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FrameBuffer { private static final Logger s_logger = LoggerFactory.getLogger(FrameBuffer.class); private static final String SENSE_HAT_FB_NAME = "RPi-Sense FB"; private static FrameBuffer frameBuffer = new FrameBuffer(); private static File graphicsFolder = new File("/sys/class/graphics/"); private static File FrameBufferFile = null; private static RandomAccessFile raf = null; private static int rotation = 0; private static Alphabet alphabet; private FrameBuffer() { } public static FrameBuffer getFrameBuffer(ComponentContext ctx) { rotation = 0; alphabet = new Alphabet( ctx.getBundleContext().getBundle().getResource("src/main/resources/sense_hat_text.pbm")); BufferedReader br = null; String currentLine; for (final File fbFolder : graphicsFolder.listFiles()) { if (fbFolder.getName().contains("fb")) { try { br = new BufferedReader(new FileReader(fbFolder + "/name")); currentLine = br.readLine(); if (null != currentLine && currentLine.equals(SENSE_HAT_FB_NAME)) { String eventFolderPath = fbFolder.getAbsolutePath(); FrameBufferFile = new File("/dev/fb" + eventFolderPath.substring(eventFolderPath.length() - 1)); br.close(); break; } } catch (IOException e) { s_logger.error("Error in opening file.", e); } finally { if (br != null) { try { br.close(); } catch (IOException e) { s_logger.error("Error in closing file.", e); } } } } } try { raf = new RandomAccessFile(FrameBufferFile, "rw"); } catch (FileNotFoundException e) { s_logger.error("FrameBuffer not found!", e); } return frameBuffer; } public static void setRotation(int rotate) { rotation = rotate; } public int getRotation() { return rotation; } public void flipVertical(short[][][] image) { if (image.length != 8 || image[0].length != 8 || image[0][0].length != 3) { s_logger.error("Image is a 8x8 matrix of RGB pixels."); return; } int coordinates[] = new int[2]; for (int x = 0; x < 8; x++) { for (int y = 0; y < 8; y++) { coordinates[0] = x; coordinates[1] = y; if (rotation == 0 || rotation == 180) { setPixel(rotate(flipH(coordinates)), image[x][y]); } else { setPixel(rotate(flipV(coordinates)), image[x][y]); } } } } public void flipHorizontal(short[][][] image) { if (image.length != 8 || image[0].length != 8 || image[0][0].length != 3) { s_logger.error("Image is a 8x8 matrix of RGB pixels."); return; } int coordinates[] = new int[2]; for (int x = 0; x < 8; x++) { for (int y = 0; y < 8; y++) { coordinates[0] = x; coordinates[1] = y; if (rotation == 90 || rotation == 270) { setPixel(rotate(flipH(coordinates)), image[x][y]); } else { setPixel(rotate(flipV(coordinates)), image[x][y]); } } } } public void setPixels(short[][][] image) { if (image.length != 8 || image[0].length != 8 || image[0][0].length != 3) { s_logger.error("Image is a 8x8 matrix of RGB pixels."); return; } int coordinates[] = new int[2]; for (int x = 0; x < 8; x++) { for (int y = 0; y < 8; y++) { coordinates[0] = x; coordinates[1] = y; setPixel(rotate(coordinates), image[x][y]); } } } public void setPixel(int[] coordinates, short[] pixel) { int x = coordinates[0]; int y = coordinates[1]; if (x < 0 || x > 7 || y < 0 || y > 7) { s_logger.error("Invalid pixel position."); return; } if (pixel.length != 3) { s_logger.error("Pixel has to be in rgb format."); return; } byte[] packedPixel = packPixel(pixel); try { raf.seek(2 * (x * 8 + y)); raf.write(packedPixel[0]); raf.write(packedPixel[1]); } catch (IOException e) { s_logger.error("Error in writing on framebuffer."); } } public short[][][] getPixels() { short[][][] image = new short[8][8][3]; int[] coordinates = new int[2]; for (int x = 0; x < 8; x++) { for (int y = 0; y < 8; y++) { coordinates[0] = x; coordinates[1] = y; image[x][y] = getPixel(coordinates); } } return image; } public short[] getPixel(int[] coordinates) { int x = coordinates[0]; int y = coordinates[1]; if (x < 0 || x > 7 || y < 0 || y > 7) { s_logger.error("Invalid pixel position."); return null; } byte[] pixel = new byte[2]; try { raf.seek(2 * (x * 8 + y)); raf.read(pixel); } catch (IOException e) { s_logger.error("Error in writing on framebuffer."); } return unpackPixel(pixel); } public void loadImage() { } public void getCharPixels() { } public void showMessage(String text, short[] textColor, short[] backColor) { short[][][] message = new short[(text.length() + 2) * 8][8][3]; System.arraycopy(alphabet.getLetter(" "), 0, message, 0, 8); for (int i = 0; i < text.length(); i++) { if (!alphabet.isAvailable(String.valueOf(text.charAt(i)))) { s_logger.warn("Letter not available"); clearFrameBuffer(); return; } System.arraycopy(alphabet.getLetter(String.valueOf(text.charAt(i))), 0, message, (i + 1) * 8, 8); } System.arraycopy(alphabet.getLetter(" "), 0, message, message.length - 8, 8); short[][][] currentFrame = new short[8][8][3]; for (int i = 0; i < message.length - 8; i++) { for (int x = 0; x < 8; x++) { for (int y = 0; y < 8; y++) { currentFrame[x][y][0] = message[x + i][y][0] == 0 ? backColor[0] : textColor[0]; currentFrame[x][y][1] = message[x + i][y][1] == 0 ? backColor[1] : textColor[1]; currentFrame[x][y][2] = message[x + i][y][2] == 0 ? backColor[2] : textColor[2]; } } frameBuffer.setPixels(currentFrame); try { Thread.sleep(100); } catch (InterruptedException e) { s_logger.error(e.toString()); } } } public void showLetter(String letter, short[] textColor, short[] backColor) { if (!alphabet.isAvailable(letter)) { s_logger.warn("Letter not available"); clearFrameBuffer(); return; } short[][][] letterPixel = new short[8][8][3]; letterPixel = alphabet.getLetter(letter); for (int x = 0; x < 8; x++) { for (int y = 0; y < 8; y++) { letterPixel[x][y][0] = letterPixel[x][y][0] == 0 ? backColor[0] : textColor[0]; letterPixel[x][y][1] = letterPixel[x][y][1] == 0 ? backColor[1] : textColor[1]; letterPixel[x][y][2] = letterPixel[x][y][2] == 0 ? backColor[2] : textColor[2]; } } frameBuffer.setPixels(letterPixel); } public void clearFrameBuffer() { try { raf.seek(0); for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { raf.write((char) 0x00); raf.write((char) 0x00); } } } catch (IOException e) { s_logger.error("Error in writing on framebuffer", e); } } public static void closeFrameBuffer() { if (raf != null) { try { raf.close(); } catch (IOException e) { s_logger.error("Error in closing framebuffer.", e); } } } private byte[] packPixel(short[] pixel) { byte[] pixelByte = new byte[3]; byte[] outPixel = new byte[2]; pixelByte[0] = (byte) (pixel[0] >> 3 & 0x1F); pixelByte[1] = (byte) (pixel[1] >> 2 & 0x3F); pixelByte[2] = (byte) (pixel[2] >> 3 & 0x1F); outPixel[0] = (byte) ((pixelByte[1] << 5) + pixelByte[0]); outPixel[1] = (byte) ((pixelByte[2] << 3) + (pixelByte[1] >> 3)); return outPixel; } private short[] unpackPixel(byte[] pixel) { short[] unpackedPixel = new short[3]; ByteBuffer bb = ByteBuffer.allocate(2); bb.order(ByteOrder.LITTLE_ENDIAN); bb.put(pixel[0]); bb.put(pixel[1]); short shortPixel = bb.getShort(0); unpackedPixel[2] = (short) ((shortPixel & 0xF800) >> 8); unpackedPixel[1] = (short) ((shortPixel & 0x07E0) >> 3); unpackedPixel[0] = (short) ((shortPixel & 0x001F) << 3); return unpackedPixel; } private int[] rotate(int[] coordinates) { int[] rotated = new int[2]; if (rotation == 0) { rotated = coordinates; } else if (rotation == 90) { rotated = transpose(coordinates); rotated[0] = 8 - 1 - rotated[0]; } else if (rotation == 180) { rotated[0] = 8 - 1 - coordinates[0]; rotated[1] = 8 - 1 - coordinates[1]; } else if (rotation == 270) { rotated = transpose(coordinates); rotated[1] = 8 - 1 - rotated[1]; } else { s_logger.warn("Incorrect rotation value. Only 0, 90, 180, 270 values are allowed."); } return rotated; } private int[] transpose(int[] coordinates) { int[] transposed = new int[2]; transposed[0] = coordinates[1]; transposed[1] = coordinates[0]; return transposed; } private int[] flipV(int[] coordinates) { int[] flipped = new int[2]; flipped[0] = coordinates[0]; flipped[1] = 8 - 1 - coordinates[1]; return flipped; } private int[] flipH(int[] coordinates) { int[] flipped = new int[2]; flipped[0] = 8 - 1 - coordinates[0]; flipped[1] = coordinates[1]; return flipped; } }