From b388a5decf5957cbb119b8c020baef200760d6e1 Mon Sep 17 00:00:00 2001 From: ulrich Date: Fri, 11 Feb 2022 13:03:59 +0000 Subject: [PATCH] Bildbetrachter hinzugefuegt --- src/de/uhilger/calypso/Betrachter.java | 143 ++++++++++++++++++++ src/org/tw/pi/framebuffer/FrameBuffer.java | 236 +++++++++++++++++++++++++++++++++ 2 files changed, 379 insertions(+), 0 deletions(-) diff --git a/src/de/uhilger/calypso/Betrachter.java b/src/de/uhilger/calypso/Betrachter.java new file mode 100644 index 0000000..3890072 --- /dev/null +++ b/src/de/uhilger/calypso/Betrachter.java @@ -0,0 +1,143 @@ +package de.uhilger.calypso; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.net.URL; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.imageio.ImageIO; +import org.tw.pi.framebuffer.FrameBuffer; + +/** + * Klasse zum Betrachten von Fotografien auf dem Fernseher + * mit dem Raspberry Pi + * + * Diese Klasse verwendet die Klasse JavaFrameBuffer + * von Thomas Welsch, die die Ausgabe direkt in den + * FrameBuffer des Raspberry Pi erlaubt. + * + * Der JavaFrameBuffer ist mit Calypso + * enthalten und erfordert, dass beim Start die native + * Programmbibliothek namens 'libFrameBuffer.so' eingebunden wird, die im Verteilpaket + * von Calypso enthalten ist. Zum Einbinden der nativen Biliothek genuegt die + * Angabe von -Djava.library.path=/pfad/zum/lib-ordner im Startskript von + * Calypso. + * + * Durch die Verwendung des Framebuffer kann das Betrachten von + * Bildern auf dem Fernseher erfolgen ohne, dass auf dem Raspberry Pi + * eine grafische Bedienoberflaeche wie z.B. das X Windowing System, + * XBMC oder OpenELEC, usw. laufen muss. Es genuegt Java + * im 'headless mode', was zudem die Bedienbarkeit aus der Ferne via + * HTTP hinzufuegt. + * + * @author Ulrich Hilger, https://uhilger.de + * @author Published under the terms and conditions of + * the <a href="http://www.gnu.org/licenses/agpl-3.0" target="_blank">GNU Affero General Public License</a> + * + * @version 2 vom 11. Februar 2022, Version 1 war vom 15.1.2014 + */ +public class Betrachter { + + public static final long serialVersionUID = 42L; + + private static final Logger logger = Logger.getLogger(Betrachter.class.getName()); + + public static final String P_FBDEV = "fbdev"; + public static final String STANDARD_FBDEV = "/dev/fb0"; + + public static final String P_URL = "u"; + public static final String P_X = "x"; + public static final String P_Y = "y"; + public static final String P_WIDTH = "w"; + public static final String P_HEIGHT = "h"; + + //public static final String PATH_ZEIGEN = "/zeigen"; + //public static final String PATH_LOESCHEN = "/loeschen"; + + private FrameBuffer jfb; + private BufferedImage img; + private Graphics2D g; + + public void init(String devName) { + String device = devName; + if(device == null) { + device = STANDARD_FBDEV; + } + jfb = new FrameBuffer(device, false); + img = jfb.getScreen(); + g = img.createGraphics(); + } + + public void close() { + jfb.close(); + } + + public void jfbHelloWorld() { + g.setColor(Color.RED); + g.drawString("Hello World !", 50, 50); + jfb.updateScreen(); + } + + /** + * Anzeigen eines Bildes mit Hilfe des JavaFrameBuffers + * + * Diese Methode laedt ein Bild via HTTP oder HTTPS und + * schreibt es in den Framebuffer zur Anzeige + * + * @param urlStr Adresse des Bildes, das angezeigt werden soll + * @param x waagerechte Koordinate der gewuenschten Position der linken oberen Ecke des Bildes + * @param y senkrechte Koordinate der gewuenschten Position der linken oberen Ecke des Bildes + * @param w Breite in Bildpunkten, in der das Bild angezeigt werden soll + * @param h Hoehe in Bildpunkten, in der das Bild angezeigt werden soll + */ + public String jfbBildZeigen(String urlStr, int x, int y, int w, int h) { + String antwort = "jfbBildZeigen hat nicht geklappt"; + BufferedImage webimg = null; + try { + /* + vgl. http://java.kompf.de/java2d.html + */ + //System.setProperty("sun.java2d.opengl","true"); + URL url = new URL(urlStr); + webimg = ImageIO.read(url); + g.drawImage(webimg, x, y, w, h, null); + jfb.updateScreen(); + antwort = urlStr + " geladen"; + } catch (Exception e) { + antwort = e.getMessage(); + } + return antwort; + } + + /** + * Leeren des FrameBuffers + * + * Hier wird einfach ein Linux-Prozess eroeffnet, der das + * Kommando dd if=/dev/zero of=/dev/fb0 ausfuehrt und + * den Framebuffer mit Nullwerten fuellt + */ + public String fbLeeren(String deviceName) { + String antwort = "fbLeeren hat nicht geklappt"; + try { + String cmd = "dd if=/dev/zero of=" + deviceName; + Process leeren = Runtime.getRuntime().exec(cmd); + /*MeldeThread t = new MeldeThread(); + t.setProcess(leeren); + t.lauscherHinzufuegen(new ProzessLauscherImpl() { + @Override + public void prozessBeendet() { + // etwas tun, wenn fertig... + } + }); + t.start();*/ + antwort = "Prozess gestartet"; + } catch(Exception ex) { + logger.log(Level.FINE, ex.getMessage(), ex); + } + return antwort; + } + + + +} diff --git a/src/org/tw/pi/framebuffer/FrameBuffer.java b/src/org/tw/pi/framebuffer/FrameBuffer.java new file mode 100644 index 0000000..45afda2 --- /dev/null +++ b/src/org/tw/pi/framebuffer/FrameBuffer.java @@ -0,0 +1,236 @@ +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 \ No newline at end of file -- Gitblit v1.9.3