All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bytedeco.javacv.CanvasFrame Maven / Gradle / Ivy

There is a newer version: 1.5.11
Show newest version
/*
 * Copyright (C) 2009-2015 Samuel Audet
 *
 * Licensed either under the Apache License, Version 2.0, or (at your option)
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation (subject to the "Classpath" exception),
 * either version 2, or any later version (collectively, the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *     http://www.gnu.org/licenses/
 *     http://www.gnu.org/software/classpath/license.html
 *
 * or as provided in the LICENSE.txt file that accompanied this code.
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.bytedeco.javacv;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.DisplayMode;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_ProfileRGB;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JRootPane;

/**
 *
 * @author Samuel Audet
 * 
 * Make sure OpenGL or XRender is enabled to get low latency, something like
 *      export _JAVA_OPTIONS=-Dsun.java2d.opengl=True
 *      export _JAVA_OPTIONS=-Dsun.java2d.xrender=True
 */
public class CanvasFrame extends JFrame {

    public static class Exception extends java.lang.Exception {
        public Exception(String message) { super(message); }
        public Exception(String message, Throwable cause) { super(message, cause); }
    }

    public static String[] getScreenDescriptions() {
        GraphicsDevice[] screens = getScreenDevices();
        String[] descriptions = new String[screens.length];
        for (int i = 0; i < screens.length; i++) {
            descriptions[i] = screens[i].getIDstring();
        }
        return descriptions;
    }
    public static DisplayMode getDisplayMode(int screenNumber) {
        GraphicsDevice[] screens = getScreenDevices();
        if (screenNumber >= 0 && screenNumber < screens.length) {
            return screens[screenNumber].getDisplayMode();
        } else {
            return null;
        }
    }
    public static double getGamma(int screenNumber) {
        GraphicsDevice[] screens = getScreenDevices();
        if (screenNumber >= 0 && screenNumber < screens.length) {
            return getGamma(screens[screenNumber]);
        } else {
            return 0.0;
        }
    }
    public static double getDefaultGamma() {
        return getGamma(getDefaultScreenDevice());
    }

    public static double getGamma(GraphicsDevice screen) {
        ColorSpace cs = screen.getDefaultConfiguration().getColorModel().getColorSpace();
        if (cs.isCS_sRGB()) {
            return 2.2;
        } else {
            try {
                return ((ICC_ProfileRGB)((ICC_ColorSpace)cs).getProfile()).getGamma(0);
            } catch (RuntimeException e) { }
        }
        return 0.0;
    }
    public static GraphicsDevice getScreenDevice(int screenNumber) throws Exception {
        GraphicsDevice[] screens = getScreenDevices();
        if (screenNumber >= screens.length) {
            throw new Exception("CanvasFrame Error: Screen number " + screenNumber + " not found. " +
                                "There are only " + screens.length + " screens.");
        }
        return screens[screenNumber];//.getDefaultConfiguration();
    }
    public static GraphicsDevice[] getScreenDevices() {
        return GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices();
    }
    public static GraphicsDevice getDefaultScreenDevice() {
        return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
    }

    public CanvasFrame(String title) {
        this(title, 0.0);
    }
    public CanvasFrame(String title, double gamma) {
        super(title);
        init(false, null, gamma);
    }

    public CanvasFrame(String title, GraphicsConfiguration gc) {
        this(title, gc, 0.0);
    }
    public CanvasFrame(String title, GraphicsConfiguration gc, double gamma) {
        super(title, gc);
        init(false, null, gamma);
    }

    public CanvasFrame(String title, int screenNumber, DisplayMode displayMode) throws Exception {
        this(title, screenNumber, displayMode, 0.0);
    }
    public CanvasFrame(String title, int screenNumber, DisplayMode displayMode, double gamma) throws Exception {
        super(title, getScreenDevice(screenNumber).getDefaultConfiguration());
        init(true, displayMode, gamma);
    }

    private void init(final boolean fullScreen, final DisplayMode displayMode, final double gamma) {
        Runnable r = new Runnable() { public void run() {
            KeyboardFocusManager.getCurrentKeyboardFocusManager().
                    addKeyEventDispatcher(keyEventDispatch);

            GraphicsDevice gd = getGraphicsConfiguration().getDevice();
            DisplayMode d = gd.getDisplayMode(), d2 = null;
            if (displayMode != null && d != null) {
                int w = displayMode.getWidth();
                int h = displayMode.getHeight();
                int b = displayMode.getBitDepth();
                int r = displayMode.getRefreshRate();
                d2 = new DisplayMode(w > 0 ? w : d.getWidth(),    h > 0 ? h : d.getHeight(),
                                     b > 0 ? b : d.getBitDepth(), r > 0 ? r : d.getRefreshRate());
            }
            if (fullScreen) {
                setUndecorated(true);
                getRootPane().setWindowDecorationStyle(JRootPane.NONE);
                setResizable(false);
                gd.setFullScreenWindow(CanvasFrame.this);
            } else {
                setLocationByPlatform(true);
            }
            if (d2 != null && !d2.equals(d)) {
                gd.setDisplayMode(d2);
            }
            double g = gamma == 0.0 ? getGamma(gd) : gamma;
            inverseGamma = g == 0.0 ? 1.0 : 1.0/g;

            // Must be called after the fullscreen stuff, but before
            // getting our BufferStrategy or even creating our Canvas
            setVisible(true);

            initCanvas(fullScreen, displayMode, gamma);
        }};

        if (EventQueue.isDispatchThread()) {
            r.run();
        } else {
            try {
                EventQueue.invokeAndWait(r);
            } catch (java.lang.Exception ex) { }
        }
    }

    protected void initCanvas(boolean fullScreen, DisplayMode displayMode, double gamma) {
        canvas = new Canvas() {
            @Override public void update(Graphics g) {
                paint(g);
            }
            @Override public void paint(Graphics g) {
                // Calling BufferStrategy.show() here sometimes throws
                // NullPointerException or IllegalStateException,
                // but otherwise seems to work fine.
                try {
                    if (canvas.getWidth() <= 0 || canvas.getHeight() <= 0) {
                        return;
                    }
                    BufferStrategy strategy = canvas.getBufferStrategy();
                    do {
                        do {
                            g = strategy.getDrawGraphics();
                            if (color != null) {
                                g.setColor(color);
                                g.fillRect(0, 0, getWidth(), getHeight());
                            }
                            if (image != null) {
                                g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
                            }
                            if (buffer != null) {
                                g.drawImage(buffer, 0, 0, getWidth(), getHeight(), null);
                            }
                            g.dispose();
                        } while (strategy.contentsRestored());
                        strategy.show();
                    } while (strategy.contentsLost());
                } catch (NullPointerException e) {
                } catch (IllegalStateException e) { }
            }
        };
        if (fullScreen) {
            canvas.setSize(getSize());
            needInitialResize = false;
        } else {
            canvas.setSize(10,10); // mac bug
            needInitialResize = true;
        }
        getContentPane().add(canvas);
        canvas.setVisible(true);
        canvas.createBufferStrategy(2);
        //canvas.setIgnoreRepaint(true);
    }

    // used for example as debugging console...
    public static CanvasFrame global = null;

    // Latency is about 60 ms on Metacity and Windows XP, and 90 ms on Compiz Fusion,
    // but we set the default to twice as much to take into account the roundtrip
    // camera latency as well, just to be sure
    public static final long DEFAULT_LATENCY = 200;
    private long latency = DEFAULT_LATENCY;

    private KeyEvent keyEvent = null;
    private KeyEventDispatcher keyEventDispatch = new KeyEventDispatcher() {
        public boolean dispatchKeyEvent(KeyEvent e) {
            if (e.getID() == KeyEvent.KEY_PRESSED) {
                synchronized (CanvasFrame.this) {
                    keyEvent = e;
                    CanvasFrame.this.notify();
                }
            }
            return false;
        }
    };

    protected Canvas canvas = null;
    protected boolean needInitialResize = false;
    protected double initialScale = 1.0;
    protected double inverseGamma = 1.0;
    private Color color = null;
    private Image image = null;
    private BufferedImage buffer = null;
    private Java2DFrameConverter converter = new Java2DFrameConverter();

    public long getLatency() {
        // if there exists some way to estimate the latency in real time,
        // add it here
        return latency;
    }
    public void setLatency(long latency) {
        this.latency = latency;
    }
    public void waitLatency() throws InterruptedException {
        Thread.sleep(getLatency());
    }

    public KeyEvent waitKey() throws InterruptedException {
        return waitKey(0);
    }
    public synchronized KeyEvent waitKey(int delay) throws InterruptedException {
        if (delay >= 0) {
            keyEvent = null;
            wait(delay);
        }
        KeyEvent e = keyEvent;
        keyEvent = null;
        return e;
    }

    public Canvas getCanvas() {
        return canvas;
    }

    public Dimension getCanvasSize() {
        return canvas.getSize();
    }
    public void setCanvasSize(final int width, final int height) {
        Dimension d = getCanvasSize();
        if (d.width == width && d.height == height) {
            return;
        }

        Runnable r = new Runnable() { public void run() {
            // There is apparently a bug in Java code for Linux, and what happens goes like this:
            // 1. Canvas gets resized, checks the visible area (has not changed) and updates
            // BufferStrategy with the same size. 2. pack() resizes the frame and changes
            // the visible area 3. We call Canvas.setSize() with different dimensions, to make
            // it check the visible area and reallocate the BufferStrategy almost correctly
            // 4. Finally, we resize the Canvas to the desired size... phew!
            setExtendedState(NORMAL); // force unmaximization
            canvas.setSize(width, height);
            pack();
            canvas.setSize(width+1, height+1);
            canvas.setSize(width, height);
            needInitialResize = false;
        }};

        if (EventQueue.isDispatchThread()) {
            r.run();
        } else {
            try {
                EventQueue.invokeAndWait(r);
            } catch (java.lang.Exception ex) { }
        }
    }

    public double getCanvasScale() {
        return initialScale;
    }
    public void setCanvasScale(double initialScale) {
        this.initialScale = initialScale;
        this.needInitialResize = true;
    }

    public Graphics2D createGraphics() {
        if (buffer == null || buffer.getWidth() != canvas.getWidth() || buffer.getHeight() != canvas.getHeight()) {
            BufferedImage newbuffer = canvas.getGraphicsConfiguration().createCompatibleImage(
                    canvas.getWidth(), canvas.getHeight(), Transparency.TRANSLUCENT);
            if (buffer != null) {
                Graphics g = newbuffer.getGraphics();
                g.drawImage(buffer, 0, 0, null);
                g.dispose();
            }
            buffer = newbuffer;
        }
        return buffer.createGraphics();
    }
    public void releaseGraphics(Graphics2D g) {
        g.dispose();
        canvas.paint(null);
    }

    public void showColor(Color color) {
        this.color = color;
        this.image = null;
        canvas.paint(null);
    }

    // Java2D will do gamma correction for TYPE_CUSTOM BufferedImage, but
    // not for the standard types, so we need to do it manually.
    public void showImage(Frame image) {
        showImage(image, false);
    }
    public void showImage(Frame image, boolean flipChannels) {
        showImage(converter.getBufferedImage(image, converter.getBufferedImageType(image) ==
                BufferedImage.TYPE_CUSTOM ? 1.0 : inverseGamma, flipChannels, null));
    }
    public void showImage(Image image) {
        if (image == null) {
            return;
        } else if (isResizable() && needInitialResize) {
            int w = (int)Math.round(image.getWidth (null)*initialScale);
            int h = (int)Math.round(image.getHeight(null)*initialScale);
            setCanvasSize(w, h);
        }
        this.color = null;
        this.image = image;
        canvas.paint(null);
    }

    // This should not be called from the event dispatch thread (EDT),
    // but if it is, it should not totally crash... In the worst case,
    // it will simply timeout waiting for the moved events.
    public static void tile(final CanvasFrame[] frames) {

        class MovedListener extends ComponentAdapter {
            boolean moved = false;
            @Override public void componentMoved(ComponentEvent e) {
                moved = true;
                Component c = e.getComponent();
                synchronized (c) {
                    c.notify();
                }
            }
        }
        final MovedListener movedListener = new MovedListener();

        // layout the canvas frames for the cameras in tiles
        int canvasCols = (int)Math.round(Math.sqrt(frames.length));
        if (canvasCols*canvasCols < frames.length) {
            // if we don't get a square, favor horizontal layouts
            // since screens are usually wider than cameras...
            // and we also have title bars, tasks bar, menus, etc that
            // takes up vertical space
            canvasCols++;
        }
        int canvasX = 0, canvasY = 0;
        int canvasMaxY = 0;
        for (int i = 0; i < frames.length; i++) {
            final int n = i;
            final int x = canvasX;
            final int y = canvasY;
            try {
                movedListener.moved = false;
                EventQueue.invokeLater(new Runnable() {
                    public void run() {
                        frames[n].addComponentListener(movedListener);
                        frames[n].setLocation(x, y);
                    }
                });
                int count = 0;
                while (!movedListener.moved && count < 5) {
                    // wait until the window manager actually places our window...
                    // wait a maximum of 500 ms since this does not work if
                    // we are on the event dispatch thread. also some window
                    // managers like Windows do not always send us the event...
                    synchronized (frames[n]) {
                        frames[n].wait(100);
                    }
                    count++;
                }
                EventQueue.invokeLater(new Runnable() {
                    public void run() {
                        frames[n].removeComponentListener(movedListener);
                    }
                });
            } catch (java.lang.Exception ex) { }
            canvasX = frames[i].getX()+frames[i].getWidth();
            canvasMaxY = Math.max(canvasMaxY, frames[i].getY()+frames[i].getHeight());
            if ((i+1)%canvasCols == 0) {
                canvasX = 0;
                canvasY = canvasMaxY;
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy