New file |
| | |
| | | package org.tw.pi.framebuffer; |
| | | |
| | | /* |
| | | * This file is the JNI Java part of a Raspberry Pi FrameBuffer project. |
| | | * |
| | | * Created 2013 by Thomas Welsch (ttww@gmx.de). |
| | | * |
| | | * Do whatever you want to do with it :-) |
| | | * |
| | | **/ |
| | | |
| | | |
| | | |
| | | import java.awt.Dimension; |
| | | import java.awt.Graphics; |
| | | import java.awt.image.BufferedImage; |
| | | import java.awt.image.DataBufferInt; |
| | | |
| | | import javax.swing.JPanel; |
| | | |
| | | |
| | | /** |
| | | * This class is the Java front end for a simple to use FrameBuffer driver. |
| | | * Simple draw in the BufferedImage and all changes are transfered to the FrameBuffer device.<p> |
| | | * For testing purpose a dummy device is supported (via the devicename "dummy_160x128" instead of "/dev/fb1").<p< |
| | | * The Java process needs write access to the frame buffer device file. |
| | | * <p> |
| | | * It's used to drive small bit mapped screens connected via SPI, see |
| | | * http://www.sainsmart.com/blog/ada/ |
| | | * <p> |
| | | * <p> |
| | | * My Linux kernel config for SPI display was: |
| | | * <pre> |
| | | * CONFIG_FB_ST7735=y |
| | | * CONFIG_FB_ST7735_PANEL_TYPE_RED_TAB=y |
| | | * CONFIG_FB_ST7735_RGB_ORDER_REVERSED=y |
| | | * CONFIG_FB_ST7735_MAP=y |
| | | * CONFIG_FB_ST7735_MAP_RST_GPIO=25 |
| | | * CONFIG_FB_ST7735_MAP_DC_GPIO=24 |
| | | * CONFIG_FB_ST7735_MAP_SPI_BUS_NUM=0 |
| | | * CONFIG_FB_ST7735_MAP_SPI_BUS_CS=0 |
| | | * CONFIG_FB_ST7735_MAP_SPI_BUS_SPEED=16000000 |
| | | * CONFIG_FB_ST7735_MAP_SPI_BUS_MODE=0 |
| | | * </pre> |
| | | * CONFIG_FB_ST7735_MAP_SPI_BUS_SPEED gives faster updates :-) |
| | | * <p> |
| | | * If you get the wrong colors, try the CONFIG_FB_ST7735_RGB_ORDER_REVERSED option ! |
| | | */ |
| | | public class FrameBuffer { |
| | | |
| | | private static final int FPS = 60; // Max. update rate |
| | | |
| | | private String deviceName; |
| | | |
| | | private long deviceInfo; // Private data from JNI C |
| | | |
| | | private int width,height; |
| | | private int bits; |
| | | |
| | | private BufferedImage img; |
| | | private int[] imgBuffer; |
| | | |
| | | // ----------------------------------------------------------------------------------------------------------------- |
| | | |
| | | private native long openDevice(String device); |
| | | private native void closeDevice(long di); |
| | | private native int getDeviceWidth(long di); |
| | | private native int getDeviceHeight(long di); |
| | | private native int getDeviceBitsPerPixel(long di); |
| | | private native boolean updateDeviceBuffer(long di,int[] buffer); |
| | | |
| | | static { |
| | | System.loadLibrary("FrameBufferJNI"); // FrameBufferJNI.dll (Windows) or FrameBufferJNI.so (Unixes) |
| | | } |
| | | |
| | | // ----------------------------------------------------------------------------------------------------------------- |
| | | |
| | | /** |
| | | * Open the named frame buffer device and starts the automatic update thread between the internal |
| | | * BufferedImage and the device. |
| | | * |
| | | * @param deviceName e.g. /dev/fb1 or dummy_320x200 |
| | | */ |
| | | public FrameBuffer(String deviceName) { |
| | | this(deviceName,true); |
| | | } |
| | | |
| | | // ----------------------------------------------------------------------------------------------------------------- |
| | | |
| | | /** |
| | | * Open the named frame buffer device. |
| | | * |
| | | * @param deviceName e.g. /dev/fb1 or dummy_320x200 |
| | | * @param autoUpdate if true, starts the automatic update thread between the internal |
| | | * BufferedImage and the device. |
| | | */ |
| | | public FrameBuffer(String deviceName, boolean autoUpdate) { |
| | | |
| | | this.deviceName = deviceName; |
| | | |
| | | deviceInfo = openDevice(deviceName); |
| | | |
| | | if (deviceInfo < 10) { |
| | | throw new IllegalArgumentException("Init. for frame buffer "+deviceName+" failed with error code "+deviceInfo); |
| | | } |
| | | |
| | | this.width = getDeviceWidth(deviceInfo); |
| | | this.height = getDeviceHeight(deviceInfo); |
| | | |
| | | System.err.println("Open with "+deviceName+" ("+deviceInfo+")"); |
| | | System.err.println(" width "+getDeviceWidth(deviceInfo)); |
| | | System.err.println(" height "+getDeviceHeight(deviceInfo)); |
| | | System.err.println(" bpp "+getDeviceBitsPerPixel(deviceInfo)); |
| | | |
| | | // We always use ARGB image type. |
| | | img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); |
| | | imgBuffer = ((DataBufferInt) img.getRaster().getDataBuffer()).getBankData()[0]; |
| | | |
| | | if (autoUpdate) new UpdateThread().start(); |
| | | } |
| | | |
| | | // ----------------------------------------------------------------------------------------------------------------- |
| | | |
| | | private ScreenPanel screenPanel; |
| | | |
| | | /** |
| | | * Returns a JPanel which represents the actual frame buffer device. |
| | | * |
| | | * @return JPanel... |
| | | */ |
| | | public JPanel getScreenPanel() { |
| | | synchronized (deviceName) { |
| | | if (screenPanel != null) throw new IllegalStateException("Only one screen panel supported"); |
| | | |
| | | screenPanel = new ScreenPanel(); |
| | | |
| | | return screenPanel; |
| | | } |
| | | } |
| | | |
| | | // ----------------------------------------------------------------------------------------------------------------- |
| | | |
| | | /** |
| | | * Internal helper class for displaying the current frame buffer image via a JPanel. |
| | | */ |
| | | @SuppressWarnings("serial") |
| | | private class ScreenPanel extends JPanel { |
| | | public ScreenPanel() { |
| | | setPreferredSize(new Dimension(FrameBuffer.this.width,FrameBuffer.this.height)); |
| | | } |
| | | |
| | | @Override |
| | | protected void paintComponent(Graphics g) { |
| | | super.paintComponent(g); |
| | | g.drawImage(img, 0, 0, null); |
| | | } |
| | | } |
| | | |
| | | // ----------------------------------------------------------------------------------------------------------------- |
| | | |
| | | /** |
| | | * Internal helper class for refreshing the frame buffer display and/or JPanel. |
| | | */ |
| | | private class UpdateThread extends Thread { |
| | | |
| | | UpdateThread() { |
| | | setDaemon(true); |
| | | setName("FB "+deviceName+ " update"); |
| | | } |
| | | |
| | | @Override |
| | | public void run() { |
| | | final int SLEEP_TIME = 1000 / FPS; |
| | | |
| | | while (deviceInfo != 0) { |
| | | |
| | | if (updateScreen()) { |
| | | if (screenPanel != null) { |
| | | screenPanel.repaint(); |
| | | } |
| | | } |
| | | |
| | | try { |
| | | sleep(SLEEP_TIME); |
| | | } catch (InterruptedException e) { |
| | | break; |
| | | } |
| | | |
| | | } // while |
| | | |
| | | } |
| | | |
| | | } // class UpdateThread |
| | | |
| | | // ----------------------------------------------------------------------------------------------------------------- |
| | | |
| | | /** |
| | | * Returns the BufferedImage for drawing. Anything your draw here is synchronizet to the frame buffer. |
| | | * |
| | | * @return BufferedImage of type ARGB. |
| | | */ |
| | | public BufferedImage getScreen() { |
| | | return img; |
| | | } |
| | | |
| | | // ----------------------------------------------------------------------------------------------------------------- |
| | | |
| | | /** |
| | | * Close the device. |
| | | */ |
| | | public void close() { |
| | | synchronized (deviceName) { |
| | | closeDevice(deviceInfo); |
| | | deviceInfo = 0; |
| | | img = null; |
| | | imgBuffer = null; |
| | | } |
| | | } |
| | | |
| | | // ----------------------------------------------------------------------------------------------------------------- |
| | | |
| | | /** |
| | | * Update the screen if no automatic sync is used (see constructor autoUpdate flag). |
| | | * This method is normally called by the autoUpdate thread. |
| | | * |
| | | * @return true if the BufferedImage was changed since the last call. |
| | | */ |
| | | public boolean updateScreen() { |
| | | synchronized (deviceName) { |
| | | if (deviceInfo == 0) return false; |
| | | return updateDeviceBuffer(deviceInfo,imgBuffer); |
| | | } |
| | | } |
| | | |
| | | |
| | | } // of class |