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.
* For testing purpose a dummy device is supported (via the devicename "dummy_160x128" instead of "/dev/fb1").
* It's used to drive small bit mapped screens connected via SPI, see * http://www.sainsmart.com/blog/ada/ *
*
* My Linux kernel config for SPI display was: *
* 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 ** CONFIG_FB_ST7735_MAP_SPI_BUS_SPEED gives faster updates :-) *
* 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