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

com.jme3.system.AWTComponentRenderer Maven / Gradle / Ivy

There is a newer version: 3.7.0-stable
Show newest version
/*
 * Copyright (c) 2009-2021 jMonkeyEngine
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.jme3.system;

import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.awt.image.SinglePixelPackedSampleModel;
import java.awt.image.WritableRaster;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;

import com.jme3.renderer.RenderManager;
import com.jme3.renderer.Renderer;
import com.jme3.system.AWTFrameProcessor.TransferMode;
import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Image;
import com.jme3.util.BufferUtils;


/**
 * 

* This class enables to update graphics of an AWT component with the result of JMonkey 3D rendering. *

*

* This class is based on the JavaFX original code provided by Alexander Brui (see JME3-FX) *

* @author Julien Seinturier - COMEX SA - http://www.seinturier.fr * @author Alexander Brui (JavaSaBr) * */ public class AWTComponentRenderer { /** * The constant RUNNING_STATE. */ protected static final int RUNNING_STATE = 1; /** * The constant WAITING_STATE. */ protected static final int WAITING_STATE = 2; /** * The constant DISPOSING_STATE. */ protected static final int DISPOSING_STATE = 3; /** * The constant DISPOSED_STATE. */ protected static final int DISPOSED_STATE = 4; /** * The Frame state. */ protected final AtomicInteger frameState; /** * The Image state. */ protected final AtomicInteger imageState; /** * The Frame buffer. */ protected final FrameBuffer frameBuffer; /** * The Pixel writer. */ protected final Graphics pixelWriter; /** * The Frame byte buffer. */ protected final ByteBuffer frameByteBuffer; /** * The transfer mode. */ protected final TransferMode transferMode; /** * The byte buffer. */ protected final byte[] byteBuffer; /** * The image byte buffer. */ protected final int[] imageByteBuffer; /** * The prev image byte buffer. */ protected final byte[] prevImageByteBuffer; /** * How many frames need to write else. */ protected int frameCount; /** * The width. */ private final int width; /** * The height. */ private final int height; private ColorModel colorModel = null; private Component component = null; private Graphics2D offGraphics = null; /** * Create a new component renderer attached to the given {@link Component destination}. * The graphics of the destination are updated with the JMonkeyEngine rendering result. * @param destination the AWT component to use as target of the JMonkeyEngine rendering. * @param width the width of the component in pixels. * @param height the height of the component in pixels. * @param transferMode the rendering mode that can be {@link TransferMode#ALWAYS} if the component has to be rendered at each update * or {@link TransferMode#ON_CHANGES} if the component has to be rendered only when changes are occurring. */ public AWTComponentRenderer(Component destination, final int width, final int height, TransferMode transferMode) { this(destination, transferMode, null, width, height); } /** * Create a new component renderer attached to the given {@link Component destination}. * The graphics of the destination are updated with the JMonkeyEngine rendering result. * @param destination the AWT component to use as target of the JMonkeyEngine rendering. * @param transferMode the rendering mode that can be {@link TransferMode#ALWAYS} if the component has to be rendered at each update or {@link TransferMode#ON_CHANGES} if the component has to be rendered only when changes are occurring. * @param frameBuffer the JMonkey frame buffer to use (if null is passed, a new default frame buffer is created) * @param width the width of the component in pixels. * @param height the height of the component in pixels. */ public AWTComponentRenderer(Component destination, TransferMode transferMode, FrameBuffer frameBuffer, int width, int height) { this.transferMode = transferMode; this.frameState = new AtomicInteger(WAITING_STATE); this.imageState = new AtomicInteger(WAITING_STATE); this.width = frameBuffer != null ? frameBuffer.getWidth() : width; this.height = frameBuffer != null ? frameBuffer.getHeight() : height; this.frameCount = 0; if (frameBuffer != null) { this.frameBuffer = frameBuffer; } else { this.frameBuffer = new FrameBuffer(width, height, 1); this.frameBuffer.setDepthBuffer(Image.Format.Depth); this.frameBuffer.setColorBuffer(Image.Format.RGBA8); this.frameBuffer.setSrgb(true); } colorModel = ColorModel.getRGBdefault(); frameByteBuffer = BufferUtils.createByteBuffer(getWidth() * getHeight() * 4); byteBuffer = new byte[getWidth() * getHeight() * 4]; prevImageByteBuffer = new byte[getWidth() * getHeight() * 4]; imageByteBuffer = new int[getWidth() * getHeight()]; pixelWriter = getGraphics(destination); this.component = destination; } /** * Initialize the component renderer. * @param renderer the JMonkey {@link Renderer renderer} to use. * @param main true if the attached frame buffer is the main one or false otherwise. */ public void init(Renderer renderer, boolean main) { if (main) { renderer.setMainFrameBufferOverride(frameBuffer); } } /** * Get the graphics context of the given component. * @param destination the AWT component used for rendering (not null) * @return the graphics context of the given component. */ protected Graphics getGraphics(Component destination) { if (destination != null) { if (destination.getGraphics() != null) { return destination.getGraphics(); } else { System.out.println("AWT component "+destination.getClass().getSimpleName()+" does not provide 2D graphics capabilities."); return null; //throw new IllegalArgumentException("AWT component "+destination.getClass().getSimpleName()+" does not provide 2D graphics capabilities."); } } else { throw new IllegalArgumentException("destination component cannot be null"); } } /** * Get the width of the area to render. * @return the width of the area to render. * @see #getHeight() */ public int getWidth() { return width; } /** * Get the height of the area to render. * @return the height of the area to render. * @see #getWidth() */ public int getHeight() { return height; } /** * Copy the JMonkey frame buffer that has been rendered by the JMonkey engine and schedule the rendering of the component. * @param renderManager the JMonkey render manager. */ public void copyFrameBufferToImage(RenderManager renderManager) { while (!frameState.compareAndSet(WAITING_STATE, RUNNING_STATE)) { if (frameState.get() == DISPOSED_STATE) { return; } } // Convert screenshot. try { frameByteBuffer.clear(); final Renderer renderer = renderManager.getRenderer(); renderer.readFrameBufferWithFormat(frameBuffer, frameByteBuffer, Image.Format.RGBA8); } finally { if (!frameState.compareAndSet(RUNNING_STATE, WAITING_STATE)) { throw new RuntimeException("unknown problem with the frame state"); } } synchronized (byteBuffer) { frameByteBuffer.get(byteBuffer); if (transferMode == TransferMode.ON_CHANGES) { final byte[] prevBuffer = getPrevImageByteBuffer(); if (Arrays.equals(prevBuffer, byteBuffer)) { if (frameCount == 0) return; } else { frameCount = 2; System.arraycopy(byteBuffer, 0, prevBuffer, 0, byteBuffer.length); } frameByteBuffer.position(0); frameCount--; } } EventQueue.invokeLater(new Runnable() { @Override public void run() { writeFrame(); }}); } /** * Write the current rendered frame to the component graphics context. */ protected void writeFrame() { if (pixelWriter != null) { while (!imageState.compareAndSet(WAITING_STATE, RUNNING_STATE)) { if (imageState.get() == DISPOSED_STATE) { return; } } try { final int[] imageDataBuffer = getImageByteBuffer(); synchronized (byteBuffer) { for(int i = 0; i < width * height; i++) { imageDataBuffer[i] = ((0xff & byteBuffer[i*4+3]) << 24) // Alpha | ((0xff & byteBuffer[i*4]) << 16) // Red | ((0xff & byteBuffer[i*4+1]) << 8) // Green | ((0xff & byteBuffer[i*4+2])); // BLue } } DataBuffer buffer = new DataBufferInt(imageDataBuffer, imageDataBuffer.length); SampleModel sm = new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, getWidth(), getHeight(), new int[] {0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000}); WritableRaster raster = Raster.createWritableRaster(sm, buffer, null); BufferedImage img = new BufferedImage(colorModel, raster, false, null); BufferedImage img2 = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); offGraphics = img2.createGraphics(); offGraphics.setColor(component.getBackground()); img2.createGraphics().fillRect(0, 0, getWidth(), getHeight()); img2.createGraphics().drawImage(img, null, null); component.getGraphics().drawImage(img2, 0, 0, null); } finally { if (!imageState.compareAndSet(RUNNING_STATE, WAITING_STATE)) { throw new RuntimeException("unknown problem with the image state"); } } } else { System.out.println("No graphics context available for rendering."); } } /** * Get the image byte buffer. * @return the image byte buffer. */ protected int[] getImageByteBuffer() { return imageByteBuffer; } /** * Get the previous image byte buffer. * @return the previous image byte buffer. */ protected byte[] getPrevImageByteBuffer() { return prevImageByteBuffer; } /** * Dispose this renderer. The underlying frame buffer is also disposed. */ public void dispose() { while (!frameState.compareAndSet(WAITING_STATE, DISPOSING_STATE)) ; while (!imageState.compareAndSet(WAITING_STATE, DISPOSING_STATE)) ; frameBuffer.dispose(); BufferUtils.destroyDirectBuffer(frameByteBuffer); frameState.compareAndSet(DISPOSING_STATE, DISPOSED_STATE); imageState.compareAndSet(DISPOSING_STATE, DISPOSED_STATE); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy