package net.sf.fmj.media.renderer.video;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.util.logging.Logger;
import javax.media.Buffer;
import javax.media.Format;
import javax.media.format.RGBFormat;
import javax.media.format.VideoFormat;
import javax.media.renderer.VideoRenderer;
import net.sf.fmj.media.AbstractVideoRenderer;
import net.sf.fmj.media.util.BufferToImage;
import net.sf.fmj.utility.FPSCounter;
import net.sf.fmj.utility.LoggerSingleton;
/**
*
* The simplest possible AWT Renderer.
* @author Ken Larson
*
*/
public class SimpleAWTRenderer extends AbstractVideoRenderer implements VideoRenderer
{
private static final Logger logger = LoggerSingleton.logger;
private final Format[] supportedInputFormats = new Format[] {
//RGB, 32-bit, Masks=16711680:65280:255, LineStride=-1, class [I
new RGBFormat(null, -1, Format.intArray, -1.0f, 32, 0xff0000, 0xff00, 0xff, 1, -1, 0, -1),
//RGB, 32-bit, Masks=255:65280:16711680, LineStride=-1, class [I
new RGBFormat(null, -1, Format.intArray, -1.0f, 32, 0xff, 0xff00, 0xff0000, 1, -1, 0, -1),
new RGBFormat(null, -1, Format.byteArray, -1.0f, 32, 1, 2, 3),
new RGBFormat(null, -1, Format.byteArray, -1.0f, 32, 3, 2, 1),
new RGBFormat(null, -1, Format.byteArray, -1.0f, 24, 1, 2, 3),
new RGBFormat(null, -1, Format.byteArray, -1.0f, 24, 3, 2, 1),
// rgb565, rgb555, bgr565, bgr555
new RGBFormat(null, -1, Format.shortArray, -1.0f, 16, -1, -1, -1, 1, -1, 0, -1),
// rgb8,bgr8
new RGBFormat(null, -1, Format.byteArray, -1.0f, 8, -1, -1, -1, 1, -1, 0, -1),
};
//@Override
@Override
public String getName()
{
return "Simple AWT Renderer";
}
//@Override
@Override
public Format[] getSupportedInputFormats()
{
return supportedInputFormats;
}
private AwtVideoComponent component = new AwtVideoComponent();
@Override
public Component getComponent()
{ return component;
}
private Object[] controls = new Object[] {this};
@Override
public Object[] getControls()
{ return controls;
}
private BufferToImage bufferToImage;
//@Override
@Override
public Format setInputFormat(Format format)
{
// logger.fine("FORMAT: " + MediaCGUtils.formatToStr(format));
// TODO: check VideoFormat and compatibility
bufferToImage = new BufferToImage((VideoFormat) format);
return super.setInputFormat(format);
}
private final FPSCounter fpsCounter = new FPSCounter();
@Override
public int doProcess(Buffer buffer)
{
if (buffer.isEOM())
{ logger.warning(this.getClass().getSimpleName() + "passed buffer with EOM flag"); // normally not supposed to happen, is it?
return BUFFER_PROCESSED_OK;
}
// if (buffer.isDiscard())
// return BUFFER_PROCESSED_OK; // TODO: where do we check for this?
if (buffer.getData() == null)
{ return BUFFER_PROCESSED_FAILED; // TODO: check for EOM?
}
java.awt.Image image = bufferToImage.createImage(buffer);
component.setImage(image);
// fpsCounter.nextFrame();
// if (fpsCounter.getNumFrames() >= 50)
// { System.out.println(fpsCounter);
// fpsCounter.reset();
// }
return BUFFER_PROCESSED_OK;
}
/**
* Component used for rendering video images.
*/
private class AwtVideoComponent extends Component {
private Image image;
public void setImage(Image image) {
this.image = image;
repaint();
}
@Override
public Dimension getPreferredSize() {
if (inputFormat == null) {
return super.getPreferredSize();
}
VideoFormat videoFormat = (VideoFormat) inputFormat;
return videoFormat.getSize();
}
private Rectangle getVideoRect(final boolean scale)
{
final int x, y;
final int w, h;
final Dimension preferredSize = getPreferredSize();
final Dimension size = getSize();
if (!scale)
{
if (preferredSize.width <= size.width)
{ x = (size.width - preferredSize.width) / 2;
w = preferredSize.width;
}
else
{ x = 0;
w = preferredSize.width;
}
if (preferredSize.height <= size.height)
{ y = (size.height - preferredSize.height) / 2;
h = preferredSize.height;
}
else
{ y = 0;
h = preferredSize.height;
}
}
else
{
if (scaleKeepAspectRatio)
{
if ((float)size.width / preferredSize.width < (float)size.height / preferredSize.height) {
w = size.width;
h = size.width * preferredSize.height / preferredSize.width;
x = 0;
y = (size.height-h)/2;
}
else {
w = size.height * preferredSize.width / preferredSize.height;
h = size.height;
x = (size.width-w)/2;
y = 0;
}
}
else
{
x = 0;
y = 0;
w = size.width;
h = size.height;
}
}
return new Rectangle(x, y, w, h);
}
// jmf scales without keeping the aspect ratio, TODO: make scale and scaleKeepAspectRatio accessible by application
private boolean scaleKeepAspectRatio = false;
private boolean scale = true;
private BufferedImage biCompatible;
@Override
public void paint(Graphics g) {
if (image != null) {
Rectangle rect = getVideoRect(scale);
// long start = System.currentTimeMillis();
try
{
if (biCompatible == null) // try drawing directly, unless we already know that won't work (biCompatible is set)
{ g.drawImage(image, rect.x, rect.y, rect.width, rect.height, null);
return;
}
}
catch (java.awt.image.ImagingOpException e)
{
// some images do not seem to be able to be scaled directly.
// this appears to only happen when the image has a ComponentColorModel.
// the graphics destination appears to use a DirectColorModel, and for scaling,
// they are incompatible. Civil uses ComponentColorModel in many cases, so
// this is generally when we have this problem.
// fall through and convert manually.
// no idea why AWT/Java2d doesn't just do this for us behind the scenes.
}
getCompatibleBufferedImage();
biCompatible.getGraphics().drawImage(image, 0, 0, image.getWidth(null), image.getHeight(null), null);
g.drawImage(biCompatible, rect.x, rect.y, rect.width, rect.height, null);
}
// else {
// Dimension size = getSize();
// g.setColor(getBackground());
// g.fillRect(0, 0, size.width, size.height);
// }
}
private BufferedImage getCompatibleBufferedImage()
{
if (biCompatible == null || biCompatible.getWidth() != image.getWidth(null) || biCompatible.getHeight() != image.getHeight(null))
biCompatible = this.getGraphicsConfiguration().createCompatibleImage(image.getWidth(null), image.getHeight(null));
return biCompatible;
}
@Override
public void update(Graphics g) {
paint(g);
}
}
}