diff options
4 files changed, 538 insertions, 3 deletions
diff --git a/make/scripts/tests.sh b/make/scripts/tests.sh index db267816e..9e2a06dac 100644 --- a/make/scripts/tests.sh +++ b/make/scripts/tests.sh @@ -330,8 +330,9 @@ function testawtswt() { #testnoawt com.jogamp.opengl.test.junit.jogl.tile.TestRandomTiledRendering2GL2NEWT $* #testawt com.jogamp.opengl.test.junit.jogl.tile.TestRandomTiledRendering3GL2AWT $* #testawt com.jogamp.opengl.test.junit.jogl.tile.TestTiledPrintingGearsAWT $* -testawt com.jogamp.opengl.test.junit.jogl.tile.TestTiledPrintingGearsSwingAWT $* +#testawt com.jogamp.opengl.test.junit.jogl.tile.TestTiledPrintingGearsSwingAWT $* #testawt com.jogamp.opengl.test.junit.jogl.tile.TestTiledPrintingGearsNewtAWT $* +testawt com.jogamp.opengl.test.junit.jogl.tile.TestTiledPrintingNIOImageSwingAWT $* # # core/newt (testnoawt and testawt) diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/awt/DirectDataBufferInt.java b/src/nativewindow/classes/com/jogamp/nativewindow/awt/DirectDataBufferInt.java new file mode 100644 index 000000000..d9c3c3791 --- /dev/null +++ b/src/nativewindow/classes/com/jogamp/nativewindow/awt/DirectDataBufferInt.java @@ -0,0 +1,238 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``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 JogAmp Community 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. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.nativewindow.awt; + +import java.awt.Point; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.DirectColorModel; +import java.awt.image.SampleModel; +import java.awt.image.SinglePixelPackedSampleModel; +import java.awt.image.WritableRaster; +import java.nio.IntBuffer; + +import com.jogamp.common.nio.Buffers; + +/** + * {@link DataBuffer} specialization using NIO direct buffer of type {@link DataBuffer#TYPE_INT} as storage. + */ +public final class DirectDataBufferInt extends DataBuffer { + + public static class DirectWritableRaster extends WritableRaster { + protected DirectWritableRaster(SampleModel sampleModel, DirectDataBufferInt dataBuffer, Point origin) { + super(sampleModel, dataBuffer, origin); + } + } + + /** + * Creates a {@link BufferedImage} using an RGB[A] {@link DirectColorModel} + * with an {@link DirectWritableRaster} utilizing a {@link DirectDataBufferInt} as storage. + * + * @param width + * @param height + * @param numComponents 3 or 4 components, i.e. {@link BufferedImage#TYPE_INT_RGB} or {@link BufferedImage#TYPE_INT_RGBA}. + * @param location origin, if <code>null</code> 0/0 is assumed. + * @return + */ + public static BufferedImage createBufferedImage(int width, int height, int numComponents, Point location) { + final int[] bandOffsets = new int[numComponents]; + for (int i=0; i < numComponents; i++) { + bandOffsets[i] = i; + } + final int bpp, rmask, gmask, bmask, amask; + final ColorModel cm; + if( 4 == numComponents ) { + bpp = 32; + rmask = 0x00ff0000; + gmask = 0x0000ff00; + bmask = 0x000000ff; + amask = 0xff000000; + cm = ColorModel.getRGBdefault(); + } else if( 3 == numComponents ) { + bpp = 24; + rmask = 0x00ff0000; + gmask = 0x0000ff00; + bmask = 0x000000ff; + amask = 0x0; + cm = new DirectColorModel(bpp, rmask, gmask, bmask, amask); + } else { + throw new IllegalArgumentException("numComponents must be [3..4], has "+numComponents); + } + final int[] bandMasks; + if ( 0 != amask ) { + bandMasks = new int[4]; + bandMasks[3] = amask; + } + else { + bandMasks = new int[3]; + } + bandMasks[0] = rmask; + bandMasks[1] = gmask; + bandMasks[2] = bmask; + + final DirectDataBufferInt dataBuffer = new DirectDataBufferInt(width*height); + if( null == location ) { + location = new Point(0,0); + } + final SinglePixelPackedSampleModel sppsm = new SinglePixelPackedSampleModel(dataBuffer.getDataType(), + width, height, width /* scanLineStride */, bandMasks); + // IntegerComponentRasters must haveinteger DataBuffers: + // final WritableRaster raster = new IntegerInterleavedRaster(sppsm, dataBuffer, location); + // Not public: + // final WritableRaster raster = new SunWritableRaster(sppsm, dataBuffer, location); + final WritableRaster raster = new DirectWritableRaster(sppsm, dataBuffer, location); + + return new BufferedImage (cm, raster, false /* isRasterPremultiplied */, null /* properties */); + } + + /** Default data bank. */ + private IntBuffer data; + + /** All data banks */ + private IntBuffer bankdata[]; + + /** + * Constructs an nio integer-based {@link DataBuffer} with a single bank + * and the specified size. + * + * @param size The size of the {@link DataBuffer}. + */ + public DirectDataBufferInt(int size) { + super(TYPE_INT, size); + data = Buffers.newDirectIntBuffer(size); + bankdata = new IntBuffer[1]; + bankdata[0] = data; + } + + /** + * Constructs an nio integer-based {@link DataBuffer} with the specified number of + * banks, all of which are the specified size. + * + * @param size The size of the banks in the {@link DataBuffer}. + * @param numBanks The number of banks in the a{@link DataBuffer}. + */ + public DirectDataBufferInt(int size, int numBanks) { + super(TYPE_INT,size,numBanks); + bankdata = new IntBuffer[numBanks]; + for (int i= 0; i < numBanks; i++) { + bankdata[i] = Buffers.newDirectIntBuffer(size); + } + data = bankdata[0]; + } + + /** + * Constructs an nio integer-based {@link DataBuffer} with a single bank using the + * specified array. + * <p> + * Only the first <code>size</code> elements should be used by accessors of + * this {@link DataBuffer}. <code>dataArray</code> must be large enough to + * hold <code>size</code> elements. + * </p> + * + * @param dataArray The integer array for the {@link DataBuffer}. + * @param size The size of the {@link DataBuffer} bank. + */ + public DirectDataBufferInt(IntBuffer dataArray, int size) { + super(TYPE_INT,size); + data = dataArray; + bankdata = new IntBuffer[1]; + bankdata[0] = data; + } + + /** + * Returns the default (first) int data array in {@link DataBuffer}. + * + * @return The first integer data array. + */ + public IntBuffer getData() { + return data; + } + + /** + * Returns the data array for the specified bank. + * + * @param bank The bank whose data array you want to get. + * @return The data array for the specified bank. + */ + public IntBuffer getData(int bank) { + return bankdata[bank]; + } + + /** + * Returns the requested data array element from the first (default) bank. + * + * @param i The data array element you want to get. + * @return The requested data array element as an integer. + * @see #setElem(int, int) + * @see #setElem(int, int, int) + */ + public int getElem(int i) { + return data.get(i+offset); + } + + /** + * Returns the requested data array element from the specified bank. + * + * @param bank The bank from which you want to get a data array element. + * @param i The data array element you want to get. + * @return The requested data array element as an integer. + * @see #setElem(int, int) + * @see #setElem(int, int, int) + */ + public int getElem(int bank, int i) { + return bankdata[bank].get(i+offsets[bank]); + } + + /** + * Sets the requested data array element in the first (default) bank + * to the specified value. + * + * @param i The data array element you want to set. + * @param val The integer value to which you want to set the data array element. + * @see #getElem(int) + * @see #getElem(int, int) + */ + public void setElem(int i, int val) { + data.put(i+offset, val); + } + + /** + * Sets the requested data array element in the specified bank + * to the integer value <code>i</code>. + * @param bank The bank in which you want to set the data array element. + * @param i The data array element you want to set. + * @param val The integer value to which you want to set the specified data array element. + * @see #getElem(int) + * @see #getElem(int, int) + */ + public void setElem(int bank, int i, int val) { + bankdata[bank].put(i+offsets[bank], val); + } +} + diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/tile/TestTiledPrintingNIOImageSwingAWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/tile/TestTiledPrintingNIOImageSwingAWT.java new file mode 100644 index 000000000..ad699c0f5 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/tile/TestTiledPrintingNIOImageSwingAWT.java @@ -0,0 +1,296 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``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 JogAmp Community 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. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.test.junit.jogl.tile; + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.Label; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.reflect.InvocationTargetException; + +import javax.imageio.ImageIO; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLProfile; +import javax.media.opengl.awt.GLJPanel; +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JLayeredPane; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +import com.jogamp.common.util.awt.AWTEDTExecutor; +import com.jogamp.nativewindow.awt.AWTPrintLifecycle; +import com.jogamp.nativewindow.awt.DirectDataBufferInt; +import com.jogamp.opengl.test.junit.jogl.demos.es2.RedSquareES2; +import com.jogamp.opengl.test.junit.jogl.demos.gl2.Gears; +import com.jogamp.opengl.test.junit.util.AWTRobotUtil; +import com.jogamp.opengl.test.junit.util.UITestCase; +import com.jogamp.opengl.util.texture.TextureIO; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestTiledPrintingNIOImageSwingAWT extends UITestCase { + + static boolean waitForKey = false; + /** only when run manually .. */ + static boolean allow600dpi = false; + static GLProfile glp; + static int width, height; + + @BeforeClass + public static void initClass() { + if(GLProfile.isAvailable(GLProfile.GL2)) { + glp = GLProfile.get(GLProfile.GL2); + Assert.assertNotNull(glp); + width = 640; + height = 480; + } else { + setTestSupported(false); + } + // Runtime.getRuntime().traceInstructions(true); + // Runtime.getRuntime().traceMethodCalls(true); + } + + @AfterClass + public static void releaseClass() { + } + + protected void printOffscreenToFile(final BufferedImage image, final Frame frame, final GLCapabilities caps, final int num, final String detail) { + final Insets frameInsets = frame.getInsets(); + final int frameWidth = frame.getWidth(); + final int frameHeight= frame.getHeight(); + final int imageWidth = image.getWidth(); + final int imageHeight= image.getHeight(); + final double scaleComp72; + { + final double frameBorderW = frameInsets.left + frameInsets.right; + final double frameBorderH = frameInsets.top + frameInsets.bottom; + final double sx = (double)imageWidth / ( frameWidth + frameBorderW ); + final double sy = (double)imageHeight / ( frameHeight + frameBorderH ); + scaleComp72 = Math.min(sx, sy); + } + System.err.println("PRINT DPI: scaleComp72 "+scaleComp72+", image-size "+imageWidth+"x"+imageHeight+", frame[border "+frameInsets+", size "+frameWidth+"x"+frameHeight+"]"); + + System.err.println("XXX: image "+image); + System.err.println("XXX: cm "+image.getColorModel()); + System.err.println("XXX: raster "+image.getRaster()); + System.err.println("XXX: dataBuffer "+image.getRaster().getDataBuffer()); + + AWTEDTExecutor.singleton.invoke(true, new Runnable() { + public void run() { + final Graphics2D g2d = (Graphics2D) image.getGraphics(); + g2d.setClip(0, 0, image.getWidth(), image.getHeight()); + g2d.scale(scaleComp72, scaleComp72); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + + // frame.paintAll(g2d); + final AWTPrintLifecycle.Context ctx = AWTPrintLifecycle.Context.setupPrint(frame, 1.0/scaleComp72, 1.0/scaleComp72, 0); + try { + frame.printAll(g2d); + } finally { + ctx.releasePrint(); + } + // to file + final String fname = getSnapshotFilename(num, detail, caps, image.getWidth(), image.getHeight(), false, TextureIO.PNG, null); + System.err.println("XXX file "+fname); + final File fout = new File(fname); + try { + ImageIO.write(image, "png", fout); + } catch (IOException e) { + e.printStackTrace(); + } + } }); + } + + protected void runTestGL(final GLCapabilities caps, final boolean layered) throws InterruptedException, InvocationTargetException { + final int layerStepX = width/6, layerStepY = height/6; + final Dimension glc_sz = new Dimension(layered ? width - 2*layerStepX : width/2, layered ? height - 2*layerStepY : height); + final GLJPanel glJPanel1 = new GLJPanel(caps); + Assert.assertNotNull(glJPanel1); + glJPanel1.setMinimumSize(glc_sz); + glJPanel1.setPreferredSize(glc_sz); + if( layered ) { + glJPanel1.setBounds(layerStepX/2, layerStepY/2, glc_sz.width, glc_sz.height); + } else { + glJPanel1.setBounds(0, 0, glc_sz.width, glc_sz.height); + } + glJPanel1.addGLEventListener(new Gears()); + + final GLJPanel glJPanel2 = new GLJPanel(caps); + Assert.assertNotNull(glJPanel2); + glJPanel2.setMinimumSize(glc_sz); + glJPanel2.setPreferredSize(glc_sz); + if( layered ) { + glJPanel2.setBounds(3*layerStepY, 2*layerStepY, glc_sz.width, glc_sz.height); + } else { + glJPanel2.setBounds(0, 0, glc_sz.width, glc_sz.height); + } + glJPanel2.addGLEventListener(new RedSquareES2()); + // glJPanel2.addGLEventListener(new Gears()); + + final JComponent demoPanel; + if( layered ) { + glJPanel1.setOpaque(true); + glJPanel2.setOpaque(false); + final Dimension lsz = new Dimension(width, height); + demoPanel = new JLayeredPane(); + demoPanel.setMinimumSize(lsz); + demoPanel.setPreferredSize(lsz); + demoPanel.setBounds(0, 0, lsz.width, lsz.height); + demoPanel.setBorder(BorderFactory.createTitledBorder("Layered Pane")); + demoPanel.add(glJPanel1, JLayeredPane.DEFAULT_LAYER); + demoPanel.add(glJPanel2, Integer.valueOf(1)); + final JButton tb = new JButton("On Top"); + tb.setBounds(4*layerStepY, 3*layerStepY, 100, 50); + demoPanel.add(tb, Integer.valueOf(2)); + } else { + demoPanel = new JPanel(); + demoPanel.add(glJPanel1); + demoPanel.add(glJPanel2); + } + + final JFrame frame = new JFrame("Swing Print"); + Assert.assertNotNull(frame); + + final Button print72DPIButton = new Button("72dpi"); // dummy + final Button print300DPIButton = new Button("300dpi"); // dummy + final Button print600DPIButton = new Button("600dpi"); // dummy + + final JPanel printPanel = new JPanel(); + printPanel.add(print72DPIButton); + printPanel.add(print300DPIButton); + printPanel.add(print600DPIButton); + final JPanel southPanel = new JPanel(); + southPanel.add(new Label("South")); + final JPanel eastPanel = new JPanel(); + eastPanel.add(new Label("East")); + final JPanel westPanel = new JPanel(); + westPanel.add(new Label("West")); + + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + final Container fcont = frame.getContentPane(); + fcont.setLayout(new BorderLayout()); + fcont.add(printPanel, BorderLayout.NORTH); + fcont.add(demoPanel, BorderLayout.CENTER); + fcont.add(southPanel, BorderLayout.SOUTH); + fcont.add(eastPanel, BorderLayout.EAST); + fcont.add(westPanel, BorderLayout.WEST); + fcont.validate(); + frame.pack(); + frame.setVisible(true); + } } ) ; + + Assert.assertEquals(true, AWTRobotUtil.waitForVisible(frame, true)); + Assert.assertEquals(true, AWTRobotUtil.waitForRealized(glJPanel1, true)); + Assert.assertEquals(true, AWTRobotUtil.waitForRealized(glJPanel2, true)); + + // paint offscreen: array 72dpi + { + final BufferedImage image = new BufferedImage(frame.getWidth(), frame.getHeight(), BufferedImage.TYPE_INT_ARGB); + printOffscreenToFile(image, frame, caps, 0, "array_072dpi"); + } + // paint offscreen: NIO 72dpi + { + final BufferedImage image = DirectDataBufferInt.createBufferedImage(frame.getWidth(), frame.getHeight(), 4, null /* location */); + printOffscreenToFile(image, frame, caps, 1, "newio_072dpi"); + } + // paint offscreen: NIO 600dpi + { + final int scale = (int) ( 600.0 / 72.0 + 0.5 ); + final BufferedImage image = DirectDataBufferInt.createBufferedImage(frame.getWidth()*scale, frame.getHeight()*scale, 4, null /* location */); + printOffscreenToFile(image, frame, caps, 2, "newio_600dpi"); + } + + Assert.assertNotNull(frame); + Assert.assertNotNull(glJPanel1); + Assert.assertNotNull(glJPanel2); + + javax.swing.SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + frame.setVisible(false); + }}); + Assert.assertEquals(false, frame.isVisible()); + javax.swing.SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + final Frame _frame = frame; + _frame.remove(demoPanel); + _frame.dispose(); + }}); + } + + @Test + public void test01_Offscreen_aa0() throws InterruptedException, InvocationTargetException { + GLCapabilities caps = new GLCapabilities(glp); + runTestGL(caps, false); + } + + static long duration = 500; // ms + + public static void main(String args[]) { + allow600dpi = true; + for(int i=0; i<args.length; i++) { + if(args[i].equals("-time")) { + i++; + try { + duration = Integer.parseInt(args[i]); + } catch (Exception ex) { ex.printStackTrace(); } + } else if(args[i].equals("-wait")) { + waitForKey = true; + } + } + if(waitForKey) { + BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); + System.err.println("Press enter to continue"); + try { + System.err.println(stdin.readLine()); + } catch (IOException e) { } + } + org.junit.runner.JUnitCore.main(TestTiledPrintingNIOImageSwingAWT.class.getName()); + } +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/tile/TiledPrintingAWTBase.java b/src/test/com/jogamp/opengl/test/junit/jogl/tile/TiledPrintingAWTBase.java index c23d51c51..10d8b186d 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/tile/TiledPrintingAWTBase.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/tile/TiledPrintingAWTBase.java @@ -155,8 +155,8 @@ public abstract class TiledPrintingAWTBase extends UITestCase implements Printab final int frameHeight= printContainer.getHeight(); final double scaleComp72; { - final int frameBorderW = frameInsets.left + frameInsets.right; - final int frameBorderH = frameInsets.top + frameInsets.bottom; + final double frameBorderW = frameInsets.left + frameInsets.right; + final double frameBorderH = frameInsets.top + frameInsets.bottom; final double sx = pf.getImageableWidth() / ( frameWidth + frameBorderW ); final double sy = pf.getImageableHeight() / ( frameHeight + frameBorderH ); scaleComp72 = Math.min(sx, sy); |