package org.vorthmann.zome.render.java3d; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.GraphicsConfiguration; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.awt.image.renderable.ParameterBlock; import java.util.HashMap; import java.util.Map; import javax.media.j3d.Canvas3D; import javax.media.j3d.GraphicsContext3D; import javax.media.j3d.ImageComponent; import javax.media.j3d.ImageComponent2D; import javax.media.j3d.Raster; import javax.media.jai.JAI; import javax.vecmath.Point3f; import com.vzome.desktop.controller.RenderingViewer; // some of this is from http://www.j3d.org/faq/capturing.html public class CapturingCanvas3D extends Canvas3D { public CapturingCanvas3D( GraphicsConfiguration gc, boolean isOffscreen ) { super( gc, isOffscreen ); postSwapCount_ = 0; } public transient RenderingViewer.ImageCapture m_imageHandler = null; public transient int maxImageSize = -1; private int postSwapCount_; public CapturingCanvas3D( GraphicsConfiguration gc ) { super( gc ); postSwapCount_ = 0; } // public void setBounds( int x, int y, int width, int height ) // { // super .setBounds( x, y, width, height ); // System .out .println( "canvas3d " + x + " " + y + " " + width + " " + height ); // } @Override public void postSwap() { if( m_imageHandler != null ) { GraphicsContext3D ctx = getGraphicsContext3D(); Dimension size = this.getSize(); int sizeX = size .width; int sizeY = size .height; // float aspectRatio = ((float) sizeX) / ((float) sizeY); // int max = m_imageHandler .getMaxSize(); // if ( aspectRatio > 1.0f ) // { // if ( sizeX > max ) // { // sizeX = max; // sizeY = (int) Math .floor( max / aspectRatio ); // } // } // else // { // if ( sizeY > max ) // { // sizeY = max; // sizeX = (int) Math .floor( max * aspectRatio ); // } // } // The raster components need all be set! Raster ras = new Raster( new Point3f(-1.0f,-1.0f,-1.0f), Raster.RASTER_COLOR, 0,0, sizeX, sizeY, new ImageComponent2D( ImageComponent.FORMAT_RGB, new BufferedImage( sizeX, sizeY, BufferedImage.TYPE_INT_RGB)), null); ctx.readRaster(ras); // Now strip out the image info BufferedImage img = ras.getImage().getImage(); if ( maxImageSize > 0 ) { float scale = 1.0f; int h = img .getHeight(); int w = img .getWidth(); if ( h > w ) w = h; if ( w > maxImageSize ) { scale = ((float) maxImageSize) / ((float) w); } ParameterBlock params = new ParameterBlock(); params.addSource( img ); params.add( scale );// x scale factor params.add( scale );// y scale factor params.add( 0.0f );// x translate params.add( 0.0f );// y translate Map<RenderingHints.Key, Object> map = new HashMap<>(); map.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); map.put( RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY ); map.put( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC ); map.put( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY ); RenderingHints hints = new RenderingHints( map ); // Here's the important bit - use "SubsampleAverage" instead of "scale" img = JAI .create( "scale", params, hints ) .getAsBufferedImage(); } // since we're currently running on some Java3d rendering thread, we want to make sure // that we do the capture (which triggers a repaint) on the AWT event handler thread. final BufferedImage bufImg = img; final RenderingViewer.ImageCapture capturer = this .m_imageHandler; EventQueue .invokeLater( new Runnable(){ @Override public void run() { capturer .captureImage( bufImg ); }} ); postSwapCount_++; m_imageHandler = null; maxImageSize = -1; } } }