* @author Brian Paul (converted to Java by Ron Cemer and Sven Gothel)
diff --git a/src/demos/com/jogamp/opengl/demos/es2/GearsObjectES2.java b/src/demos/com/jogamp/opengl/demos/es2/GearsObjectES2.java
index 5c48a5df1..3aa6696df 100644
--- a/src/demos/com/jogamp/opengl/demos/es2/GearsObjectES2.java
+++ b/src/demos/com/jogamp/opengl/demos/es2/GearsObjectES2.java
@@ -27,13 +27,11 @@ import com.jogamp.opengl.GL2ES2;
import com.jogamp.opengl.GLBufferStorage;
import com.jogamp.opengl.GLException;
import com.jogamp.opengl.GLUniformData;
-
+import com.jogamp.opengl.demos.GearsObject;
import com.jogamp.opengl.util.GLArrayDataServer;
import com.jogamp.opengl.util.PMVMatrix;
import com.jogamp.opengl.util.glsl.ShaderState;
-import com.jogamp.opengl.demos.GearsObject;
-
/**
* GearsObjectES2.java
* @author Brian Paul (converted to Java by Ron Cemer and Sven Gothel)
diff --git a/src/demos/com/jogamp/opengl/demos/es2/TextureSequenceCubeES2.java b/src/demos/com/jogamp/opengl/demos/es2/TextureSequenceCubeES2.java
new file mode 100644
index 000000000..6a7115143
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/es2/TextureSequenceCubeES2.java
@@ -0,0 +1,491 @@
+/**
+ * Copyright 2012 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.demos.es2;
+
+import java.nio.FloatBuffer;
+
+import com.jogamp.opengl.GL;
+import com.jogamp.opengl.GL2ES2;
+import com.jogamp.opengl.GLAutoDrawable;
+import com.jogamp.opengl.GLES2;
+import com.jogamp.opengl.GLEventListener;
+import com.jogamp.opengl.GLException;
+import com.jogamp.opengl.GLProfile;
+import com.jogamp.opengl.GLUniformData;
+import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
+
+import com.jogamp.common.os.Platform;
+import com.jogamp.newt.Window;
+import com.jogamp.newt.event.MouseAdapter;
+import com.jogamp.newt.event.MouseEvent;
+import com.jogamp.newt.event.MouseListener;
+import com.jogamp.opengl.GLExtensions;
+import com.jogamp.opengl.JoglVersion;
+import com.jogamp.opengl.util.GLArrayDataServer;
+import com.jogamp.opengl.util.PMVMatrix;
+import com.jogamp.opengl.util.glsl.ShaderCode;
+import com.jogamp.opengl.util.glsl.ShaderProgram;
+import com.jogamp.opengl.util.glsl.ShaderState;
+import com.jogamp.opengl.util.texture.Texture;
+import com.jogamp.opengl.util.texture.TextureCoords;
+import com.jogamp.opengl.util.texture.TextureSequence;
+import com.jogamp.opengl.util.texture.TextureSequence.TextureFrame;
+
+public class TextureSequenceCubeES2 implements GLEventListener {
+ public TextureSequenceCubeES2 (final TextureSequence texSource, final boolean innerCube, final float zoom0, final float rotx, final float roty) {
+ this.texSeq = texSource;
+ this.innerCube = innerCube;
+ this.zoom = zoom0;
+ this.view_rotx = rotx;
+ this.view_roty = roty;
+ }
+
+ private TextureSequence texSeq;
+ public ShaderState st;
+ public PMVMatrix pmvMatrix;
+ private GLUniformData pmvMatrixUniform;
+ // private TextureCoords[] textureCoords = null;
+ private float nearPlaneNormalized;
+ // private float zoom0=-5.0f, zoom=zoom0;
+ // private float view_rotx = 20.0f, view_roty = 30.0f, view_rotz = 0.0f;
+ public float zoom=-2.3f;
+ private float view_rotx = 0.0f, view_roty = 0.0f;
+ private final float view_rotz = 0.0f;
+ int[] vboNames = new int[4];
+ boolean innerCube;
+
+ private final MouseListener mouseAction = new MouseAdapter() {
+ int lx = 0;
+ int ly = 0;
+ boolean first = false;
+
+ public void mousePressed(final MouseEvent e) {
+ first = true;
+ }
+ public void mouseMoved(final MouseEvent e) {
+ first = false;
+ }
+ public void mouseDragged(final MouseEvent e) {
+ int width, height;
+ final Object source = e.getSource();
+ Window window = null;
+ if(source instanceof Window) {
+ window = (Window) source;
+ width=window.getSurfaceWidth();
+ height=window.getSurfaceHeight();
+ } else if (source instanceof GLAutoDrawable) {
+ final GLAutoDrawable glad = (GLAutoDrawable) source;
+ width = glad.getSurfaceWidth();
+ height = glad.getSurfaceHeight();
+ } else if (GLProfile.isAWTAvailable() && source instanceof java.awt.Component) {
+ final java.awt.Component comp = (java.awt.Component) source;
+ width=comp.getWidth(); // FIXME HiDPI: May need to convert window units -> pixel units!
+ height=comp.getHeight();
+ } else {
+ throw new RuntimeException("Event source neither Window nor Component: "+source);
+ }
+ if(e.getPointerCount()==2) {
+ // 2 pointers zoom ..
+ if(first) {
+ lx = Math.abs(e.getY(0)-e.getY(1));
+ first=false;
+ return;
+ }
+ final int nv = Math.abs(e.getY(0)-e.getY(1));
+ final int dy = nv - lx;
+
+ {
+ final float o = zoom;
+ final float d = 40f*Math.signum(dy)/height;
+ zoom += d;
+ System.err.println("zoom.d: "+o+" + "+d+" -> "+zoom);
+ }
+
+ lx = nv;
+ } else {
+ // 1 pointer rotate
+ if(first) {
+ lx = e.getX();
+ ly = e.getY();
+ first=false;
+ return;
+ }
+ final int nx = e.getX();
+ final int ny = e.getY();
+ view_roty += 360f * ( (float)( nx - lx ) / (float)width );
+ view_rotx += 360f * ( (float)( ny - ly ) / (float)height );
+ lx = nx;
+ ly = ny;
+ }
+ }
+ public void mouseWheelMoved(final MouseEvent e) {
+ // System.err.println("XXX "+e);
+ if( !e.isShiftDown() ) {
+ final float o = zoom;
+ final float d = e.getRotation()[1]/10f; // vertical: wheel
+ zoom += d;
+ System.err.println("zoom.w: "+o+" + "+d+" -> "+zoom);
+ }
+ }
+ };
+
+ static final String shaderBasename = "texsequence_xxx";
+ static final String myTextureLookupName = "myTexture2D";
+
+ private void initShader(final GL2ES2 gl) {
+ // Create & Compile the shader objects
+ final ShaderCode rsVp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, this.getClass(),
+ "shader", "shader/bin", shaderBasename, true);
+ final ShaderCode rsFp = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, this.getClass(),
+ "shader", "shader/bin", shaderBasename, true);
+
+ boolean preludeGLSLVersion = true;
+ if( GLES2.GL_TEXTURE_EXTERNAL_OES == texSeq.getTextureTarget() ) {
+ if( !gl.isExtensionAvailable(GLExtensions.OES_EGL_image_external) ) {
+ throw new GLException(GLExtensions.OES_EGL_image_external+" requested but not available");
+ }
+ if( Platform.OSType.ANDROID == Platform.getOSType() && gl.isGLES3() ) {
+ // Bug on Nexus 10, ES3 - Android 4.3, where
+ // GL_OES_EGL_image_external extension directive leads to a failure _with_ '#version 300 es' !
+ // P0003: Extension 'GL_OES_EGL_image_external' not supported
+ preludeGLSLVersion = false;
+ }
+ }
+ rsVp.defaultShaderCustomization(gl, preludeGLSLVersion, true);
+
+ int rsFpPos = preludeGLSLVersion ? rsFp.addGLSLVersion(gl) : 0;
+ rsFpPos = rsFp.insertShaderSource(0, rsFpPos, texSeq.getRequiredExtensionsShaderStub());
+ rsFp.addDefaultShaderPrecision(gl, rsFpPos);
+
+ final String texLookupFuncName = texSeq.getTextureLookupFunctionName(myTextureLookupName);
+ rsFp.replaceInShaderSource(myTextureLookupName, texLookupFuncName);
+
+ // Inject TextureSequence shader details
+ final StringBuilder sFpIns = new StringBuilder();
+ sFpIns.append("uniform ").append(texSeq.getTextureSampler2DType()).append(" mgl_ActiveTexture;\n");
+ sFpIns.append(texSeq.getTextureLookupFragmentShaderImpl());
+ rsFp.insertShaderSource(0, "TEXTURE-SEQUENCE-CODE-BEGIN", 0, sFpIns);
+
+ // Create & Link the shader program
+ final ShaderProgram sp = new ShaderProgram();
+ sp.add(rsVp);
+ sp.add(rsFp);
+ if(!sp.link(gl, System.err)) {
+ throw new GLException("Couldn't link program: "+sp);
+ }
+
+ // Let's manage all our states using ShaderState.
+ st = new ShaderState();
+ st.attachShaderProgram(gl, sp, false);
+ }
+
+ GLArrayDataServer interleavedVBO, cubeIndicesVBO;
+
+ public void init(final GLAutoDrawable drawable) {
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+ System.err.println(JoglVersion.getGLInfo(gl, null));
+ final TextureFrame frame = texSeq.getLastTexture();
+ if( null == frame ) {
+ return;
+ }
+ final Texture tex= frame.getTexture();
+
+ initShader(gl);
+
+ // Push the 1st uniform down the path
+ st.useProgram(gl, true);
+
+ pmvMatrix = new PMVMatrix();
+ reshapePMV(drawable.getSurfaceWidth(), drawable.getSurfaceHeight());
+ pmvMatrixUniform = new GLUniformData("mgl_PMVMatrix", 4, 4, pmvMatrix.glGetPMvMatrixf()); // P, Mv
+ if(!st.uniform(gl, pmvMatrixUniform)) {
+ throw new GLException("Error setting PMVMatrix in shader: "+st);
+ }
+ if(!st.uniform(gl, new GLUniformData("mgl_ActiveTexture", texSeq.getTextureUnit()))) {
+ throw new GLException("Error setting mgl_ActiveTexture in shader: "+st);
+ }
+
+
+ // calculate centered tex coords w/ aspect ratio
+ final float[] fixedCubeTexCoords = new float[s_cubeTexCoords.length];
+ {
+ final float aspect = tex.getAspectRatio();
+ final TextureCoords tc = tex.getImageTexCoords();
+ System.err.println("XXX0: aspect: "+aspect);
+ System.err.println("XXX0: y-flip: "+tex.getMustFlipVertically());
+ System.err.println("XXX0: "+tc);
+ final float tc_x1 = Math.max(tc.left(), tc.right());
+ final float tc_y1 = Math.max(tc.bottom(), tc.top());
+ final float ss=1f, ts=aspect; // scale tex-coord
+ final float dy = ( 1f - aspect ) / 2f ;
+ for(int i=0; i outlineShapes = new ArrayList();
+
+ public GPURegionGLListener10 (final RenderState rs, final int renderModes, final int sampleCount, final boolean debug, final boolean trace) {
+ super(RegionRenderer.create(rs, RegionRenderer.defaultBlendEnable, RegionRenderer.defaultBlendDisable), renderModes, debug, trace);
+ rs.setHintMask(RenderState.BITHINT_GLOBAL_DEPTH_TEST_ENABLED);
+ setMatrix(-20, 00, -50, 0f, sampleCount);
+ }
+
+ private void createTestOutline(final GLProfile glp){
+ OutlineShape shape = new OutlineShape(getRenderer().getRenderState().getVertexFactory());
+ outlineShapes.add(shape);
+ shape.addVertex(0.0f,-10.0f,true);
+ shape.addVertex(15.0f,-10.0f, true);
+ shape.addVertex(10.0f,5.0f, false);
+ shape.addVertex(15.0f,10.0f, true);
+ shape.addVertex(6.0f,15.0f, false);
+ shape.addVertex(5.0f,8.0f, false);
+ shape.addVertex(0.0f,10.0f,true);
+ shape.closeLastOutline(true);
+ shape.addEmptyOutline();
+ shape.addVertex(5.0f,-5.0f,true);
+ shape.addVertex(10.0f,-5.0f, false);
+ shape.addVertex(10.0f,0.0f, true);
+ shape.addVertex(5.0f,0.0f, false);
+ shape.closeLastOutline(true);
+
+ /** Same shape as above but without any off-curve vertices */
+ shape = new OutlineShape(getRenderer().getRenderState().getVertexFactory());
+ outlineShapes.add(shape);
+ final float offset = 30;
+ shape.addVertex(offset+0.0f,-10.0f, true);
+ shape.addVertex(offset+17.0f,-10.0f, true);
+ shape.addVertex(offset+11.0f,5.0f, true);
+ shape.addVertex(offset+16.0f,10.0f, true);
+ shape.addVertex(offset+7.0f,15.0f, true);
+ shape.addVertex(offset+6.0f,8.0f, true);
+ shape.addVertex(offset+0.0f,10.0f, true);
+ shape.closeLastOutline(true);
+ shape.addEmptyOutline();
+ shape.addVertex(offset+5.0f,0.0f, true);
+ shape.addVertex(offset+5.0f,-5.0f, true);
+ shape.addVertex(offset+10.0f,-5.0f, true);
+ shape.addVertex(offset+10.0f,0.0f, true);
+ shape.closeLastOutline(true);
+
+ region = GLRegion.create(glp, getRenderModes(), null);
+ region.addOutlineShapes(outlineShapes, null, null);
+ }
+
+ @Override
+ public void init(final GLAutoDrawable drawable) {
+ super.init(drawable);
+
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+
+ final RenderState rs = getRenderer().getRenderState();
+
+ gl.setSwapInterval(1);
+ gl.glEnable(GL.GL_DEPTH_TEST);
+ gl.glEnable(GL.GL_BLEND);
+ rs.setColorStatic(0.0f, 0.0f, 0.0f, 1.0f);
+
+ createTestOutline(gl.getGLProfile());
+ }
+
+ @Override
+ public void display(final GLAutoDrawable drawable) {
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+
+ gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+ gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
+
+ final RegionRenderer regionRenderer = getRenderer();
+
+ final PMVMatrix pmv = regionRenderer.getMatrix();
+ pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
+ pmv.glLoadIdentity();
+ pmv.glTranslatef(getXTran(), getYTran(), getZTran());
+ pmv.glRotatef(getAngle(), 0, 1, 0);
+ if( weight != regionRenderer.getRenderState().getWeight() ) {
+ regionRenderer.getRenderState().setWeight(weight);
+ }
+ regionRenderer.enable(gl, true);
+ region.draw(gl, regionRenderer, getSampleCount());
+ regionRenderer.enable(gl, false);
+
+ }
+}
diff --git a/src/demos/com/jogamp/opengl/demos/graph/GPURegionNewtDemo.java b/src/demos/com/jogamp/opengl/demos/graph/GPURegionNewtDemo.java
new file mode 100644
index 000000000..59292c7be
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/graph/GPURegionNewtDemo.java
@@ -0,0 +1,162 @@
+/**
+ * Copyright 2010 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.demos.graph;
+
+import com.jogamp.opengl.GLCapabilities;
+import com.jogamp.opengl.GLProfile;
+import com.jogamp.opengl.demos.util.MiscUtils;
+import com.jogamp.graph.curve.Region;
+import com.jogamp.graph.curve.opengl.RenderState;
+import com.jogamp.graph.geom.SVertex;
+import com.jogamp.newt.event.KeyAdapter;
+import com.jogamp.newt.event.KeyEvent;
+import com.jogamp.newt.event.WindowAdapter;
+import com.jogamp.newt.event.WindowEvent;
+import com.jogamp.newt.opengl.GLWindow;
+import com.jogamp.opengl.util.Animator;
+
+/** Demonstrate the rendering of multiple outlines into one region/OutlineShape
+ * These Outlines are not necessary connected or contained.
+ * The output of this demo shows two identical shapes but the left one
+ * has some vertices with off-curve flag set to true, and the right allt he vertices
+ * are on the curve. Demos the Res. Independent Nurbs based Curve rendering
+ *
+ */
+public class GPURegionNewtDemo {
+ static final boolean DEBUG = false;
+ static final boolean TRACE = false;
+
+ static int shape_ctor_mode = 1;
+ static int SceneMSAASamples = 0;
+ static int GraphVBAASamples = 4;
+ static int GraphMSAASamples = 0;
+ static boolean GraphUseWeight = true;
+
+ public static void main(final String[] args) {
+ int width = 800, height = 400;
+ int x = 10, y = 10;
+ if( 0 != args.length ) {
+ SceneMSAASamples = 0;
+ GraphMSAASamples = 0;
+ GraphVBAASamples = 0;
+ GraphUseWeight = false;
+
+ for(int i=0; i 0 ) {
+ caps.setSampleBuffers(true);
+ caps.setNumSamples(SceneMSAASamples);
+ }
+ System.out.println("Requested: " + caps);
+
+ int rmode = GraphUseWeight ? Region.VARWEIGHT_RENDERING_BIT : 0;
+ int sampleCount = 0;
+ if( GraphVBAASamples > 0 ) {
+ rmode |= Region.VBAA_RENDERING_BIT;
+ sampleCount += GraphVBAASamples;
+ } else if( GraphMSAASamples > 0 ) {
+ rmode |= Region.MSAA_RENDERING_BIT;
+ sampleCount += GraphMSAASamples;
+ }
+
+ final GLWindow window = GLWindow.create(caps);
+ window.setPosition(x, y);
+ window.setSize(width, height);
+ window.setTitle("GPU Curve Region Newt Demo - graph[vbaa"+GraphVBAASamples+" msaa"+GraphMSAASamples+"], msaa "+SceneMSAASamples);
+
+ final RenderState rs = RenderState.createRenderState(SVertex.factory());
+ final GPURegionGLListener01 regionGLListener = new GPURegionGLListener01 (shape_ctor_mode, rs, rmode, sampleCount, DEBUG, TRACE);
+ regionGLListener.attachInputListenerTo(window);
+ window.addGLEventListener(regionGLListener);
+ window.setVisible(true);
+
+ //FPSAnimator animator = new FPSAnimator(60);
+ final Animator animator = new Animator();
+ animator.setUpdateFPSFrames(60, System.err);
+ animator.add(window);
+
+ window.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyPressed(final KeyEvent arg0) {
+ if(arg0.getKeyCode() == KeyEvent.VK_F4) {
+ window.destroy();
+ }
+ }
+ });
+ window.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowDestroyed(final WindowEvent e) {
+ animator.stop();
+ }
+ });
+
+ animator.start();
+ }
+}
diff --git a/src/demos/com/jogamp/opengl/demos/graph/GPURendererListenerBase01.java b/src/demos/com/jogamp/opengl/demos/graph/GPURendererListenerBase01.java
new file mode 100644
index 000000000..185699d55
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/graph/GPURendererListenerBase01.java
@@ -0,0 +1,361 @@
+/**
+ * Copyright 2010 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.demos.graph;
+
+import java.io.File;
+import java.io.IOException;
+
+import com.jogamp.opengl.FPSCounter;
+import com.jogamp.opengl.GL;
+import com.jogamp.opengl.GL2ES2;
+import com.jogamp.opengl.GLAnimatorControl;
+import com.jogamp.opengl.GLAutoDrawable;
+import com.jogamp.opengl.GLEventListener;
+import com.jogamp.opengl.GLException;
+import com.jogamp.opengl.GLPipelineFactory;
+import com.jogamp.opengl.GLRunnable;
+import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
+
+import com.jogamp.graph.curve.Region;
+import com.jogamp.graph.curve.opengl.GLRegion;
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.curve.opengl.RenderState;
+import com.jogamp.graph.font.FontScale;
+import com.jogamp.newt.Window;
+import com.jogamp.newt.event.KeyEvent;
+import com.jogamp.newt.event.KeyListener;
+import com.jogamp.newt.opengl.GLWindow;
+import com.jogamp.opengl.math.geom.AABBox;
+import com.jogamp.opengl.util.GLReadBufferUtil;
+import com.jogamp.opengl.util.PMVMatrix;
+
+/**
+ *
+ * Action Keys:
+ * - 1/2: zoom in/out
+ * - 6/7: 2nd pass texture size
+ * - 0/9: rotate
+ * - Q/W: change weight
+ * - v: toggle v-sync
+ * - s: screenshot
+ */
+public abstract class GPURendererListenerBase01 implements GLEventListener {
+ private final RegionRenderer renderer;
+ private final int renderModes;
+ private final boolean debug;
+ private final boolean trace;
+
+ protected GLRegion region;
+
+ private final GLReadBufferUtil screenshot;
+
+ private KeyAction keyAction;
+
+ private volatile GLAutoDrawable autoDrawable = null;
+
+ private final float[] position = new float[] {0,0,0};
+
+ protected final float zNear = 0.1f, zFar = 7000f;
+ /** Describing the bounding box in model-coordinates of the near-plane parallel at distance one. */
+ protected final AABBox nearPlane1Box;
+
+ private float xTran = -10;
+ private float yTran = 10;
+ private float ang = 0f;
+ private float zTran = -70f;
+ private final int[] sampleCount = new int[] { 4 };
+
+ protected volatile float weight = 1.0f;
+ boolean ignoreInput = false;
+
+ public GPURendererListenerBase01(final RegionRenderer renderer, final int renderModes, final boolean debug, final boolean trace) {
+ this.renderer = renderer;
+ this.renderModes = renderModes;
+ this.debug = debug;
+ this.trace = trace;
+ this.screenshot = new GLReadBufferUtil(false, false);
+ nearPlane1Box = new AABBox();
+ }
+
+ public final RegionRenderer getRenderer() { return renderer; }
+ public final int getRenderModes() { return renderModes; }
+ public final float getZTran() { return zTran; }
+ public final float getXTran() { return xTran; }
+ public final float getYTran() { return yTran; }
+ public final float getAngle() { return ang; }
+ public final int[] getSampleCount() { return sampleCount; }
+ public final float[] getPosition() { return position; }
+
+ public void setMatrix(final float xtrans, final float ytrans, final float zTran, final float angle, final int sampleCount) {
+ this.xTran = xtrans;
+ this.yTran = ytrans;
+ this.zTran = zTran;
+ this.ang = angle;
+ this.sampleCount[0] = sampleCount;
+ }
+
+ @Override
+ public void init(final GLAutoDrawable drawable) {
+ final Object upObj = drawable.getUpstreamWidget();
+ if( upObj instanceof Window ) {
+ final Window window = (Window) upObj;
+ final float[] sPpMM = window.getPixelsPerMM(new float[2]);
+ final float[] sDPI = FontScale.perMMToPerInch( new float[] { sPpMM[0], sPpMM[1] } );
+ System.err.println("DPI "+sDPI[0]+" x "+sDPI[1]+", "+sPpMM[0]+" x "+sPpMM[1]+" pixel/mm");
+
+ final float[] hasSurfacePixelScale1 = window.getCurrentSurfaceScale(new float[2]);
+ System.err.println("HiDPI PixelScale: "+hasSurfacePixelScale1[0]+"x"+hasSurfacePixelScale1[1]+" (has)");
+ }
+ autoDrawable = drawable;
+ GL2ES2 gl = drawable.getGL().getGL2ES2();
+ if(debug) {
+ gl = gl.getContext().setGL( GLPipelineFactory.create("com.jogamp.opengl.Debug", null, gl, null) ).getGL2ES2();
+ }
+ if(trace) {
+ gl = gl.getContext().setGL( GLPipelineFactory.create("com.jogamp.opengl.Trace", null, gl, new Object[] { System.err } ) ).getGL2ES2();
+ }
+ System.err.println("*** "+gl.getContext().getGLVersion());
+ System.err.println("*** GLDebugMessage "+gl.getContext().isGLDebugMessageEnabled());
+ MSAATool.dump(drawable);
+ gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+ getRenderer().init(gl);
+ }
+
+ public static void mapWin2ObjectCoords(final PMVMatrix pmv, final int[] view,
+ final float zNear, final float zFar,
+ final float orthoX, final float orthoY, final float orthoDist,
+ final float[] winZ, final float[] objPos) {
+ winZ[0] = (1f/zNear-1f/orthoDist)/(1f/zNear-1f/zFar);
+ pmv.gluUnProject(orthoX, orthoY, winZ[0], view, 0, objPos, 0);
+ }
+
+ @Override
+ public void reshape(final GLAutoDrawable drawable, final int xstart, final int ystart, final int width, final int height) {
+ final PMVMatrix pmv = renderer.getMatrix();
+ renderer.reshapePerspective(45.0f, width, height, zNear, zFar);
+ pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
+ pmv.glLoadIdentity();
+ System.err.printf("Reshape: zNear %f, zFar %f%n", zNear, zFar);
+ System.err.printf("Reshape: Frustum: %s%n", pmv.glGetFrustum());
+ {
+ final float orthoDist = 1f;
+ final float[] obj00Coord = new float[3];
+ final float[] obj11Coord = new float[3];
+ final float[] winZ = new float[1];
+ final int[] view = new int[] { 0, 0, width, height };
+
+ mapWin2ObjectCoords(pmv, view, zNear, zFar, 0f, 0f, orthoDist, winZ, obj00Coord);
+ System.err.printf("Reshape: mapped.00: [%f, %f, %f], winZ %f -> [%f, %f, %f]%n", 0f, 0f, orthoDist, winZ[0], obj00Coord[0], obj00Coord[1], obj00Coord[2]);
+
+ mapWin2ObjectCoords(pmv, view, zNear, zFar, width, height, orthoDist, winZ, obj11Coord);
+ System.err.printf("Reshape: mapped.11: [%f, %f, %f], winZ %f -> [%f, %f, %f]%n", (float)width, (float)height, orthoDist, winZ[0], obj11Coord[0], obj11Coord[1], obj11Coord[2]);
+
+ nearPlane1Box.setSize( obj00Coord[0], // lx
+ obj00Coord[1], // ly
+ obj00Coord[2], // lz
+ obj11Coord[0], // hx
+ obj11Coord[1], // hy
+ obj11Coord[2] );// hz
+ System.err.printf("Reshape: dist1Box: %s%n", nearPlane1Box);
+ }
+
+ dumpMatrix();
+ // System.err.println("Reshape: "+renderer.getRenderState());
+ }
+
+ @Override
+ public void dispose(final GLAutoDrawable drawable) {
+ autoDrawable = null;
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+ if(null != region) {
+ region.destroy(gl);
+ }
+ screenshot.dispose(gl);
+ renderer.destroy(gl);
+ }
+
+ public void zoom(final int v){
+ zTran += v;
+ dumpMatrix();
+ }
+
+ public void move(final float x, final float y){
+ xTran += x;
+ yTran += y;
+ dumpMatrix();
+ }
+ public void rotate(final float delta){
+ ang += delta;
+ ang %= 360.0f;
+ dumpMatrix();
+ }
+ public void editGlobalWeight(final float delta) {
+ if( !RenderState.isWeightValid(weight+delta) ) {
+ return;
+ }
+ weight += delta;
+ System.err.println("Global Weight: "+ weight);
+ }
+
+ void dumpMatrix() {
+ System.err.println("Matrix: " + xTran + " / " + yTran + " / "+zTran + " @ "+ang);
+ }
+
+ /** Attach the input listener to the window */
+ public void attachInputListenerTo(final GLWindow window) {
+ if ( null == keyAction ) {
+ keyAction = new KeyAction();
+ window.addKeyListener(keyAction);
+ }
+ }
+
+ public void detachInputListenerFrom(final GLWindow window) {
+ if ( null == keyAction ) {
+ return;
+ }
+ window.removeKeyListener(keyAction);
+ }
+
+ public void printScreen(final GLAutoDrawable drawable, final String dir, final String tech, final String objName, final boolean exportAlpha) throws GLException, IOException {
+ final String sw = String.format("_s%02d-%s-Z%04d-snap%02d-%03dx%03d", sampleCount[0], objName, (int)Math.abs(zTran), screenshot_num++, drawable.getSurfaceWidth(), drawable.getSurfaceHeight());
+ final String filename = dir + tech + sw +".png";
+ if(screenshot.readPixels(drawable.getGL(), false)) {
+ screenshot.write(new File(filename));
+ }
+ }
+ private int screenshot_num = 0;
+
+ public void printScreenOnGLThread(final GLAutoDrawable drawable, final String dir, final String tech, final String objName, final boolean exportAlpha) {
+ drawable.invoke(false, new GLRunnable() {
+ @Override
+ public boolean run(final GLAutoDrawable drawable) {
+ try {
+ printScreen(drawable, dir, tech, objName, exportAlpha);
+ } catch (final GLException e) {
+ e.printStackTrace();
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+ return true;
+ }
+ });
+ }
+
+ public void setIgnoreInput(final boolean v) {
+ ignoreInput = v;
+ }
+ public boolean getIgnoreInput() {
+ return ignoreInput;
+ }
+
+ public class KeyAction implements KeyListener {
+ @Override
+ public void keyPressed(final KeyEvent arg0) {
+ if(ignoreInput) {
+ return;
+ }
+
+ if(arg0.getKeyCode() == KeyEvent.VK_1){
+ zoom(10);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_2){
+ zoom(-10);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_UP){
+ move(0, -1);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_DOWN){
+ move(0, 1);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_LEFT){
+ move(-1, 0);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_RIGHT){
+ move(1, 0);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_6){
+ sampleCount[0] -= 1;
+ System.err.println("Sample Count: " + sampleCount[0]);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_7){
+ sampleCount[0] += 1;
+ System.err.println("Sample Count: " + sampleCount[0]);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_0){
+ rotate(1);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_9){
+ rotate(-1);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_Q){
+ editGlobalWeight(-0.1f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_W){
+ editGlobalWeight(0.1f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_V) {
+ if(null != autoDrawable) {
+ autoDrawable.invoke(false, new GLRunnable() {
+ @Override
+ public boolean run(final GLAutoDrawable drawable) {
+ final GL gl = drawable.getGL();
+ final int _i = gl.getSwapInterval();
+ final int i;
+ switch(_i) {
+ case 0: i = -1; break;
+ case -1: i = 1; break;
+ case 1: i = 0; break;
+ default: i = 1; break;
+ }
+ gl.setSwapInterval(i);
+
+ final GLAnimatorControl a = drawable.getAnimator();
+ if( null != a ) {
+ a.resetFPSCounter();
+ }
+ if(drawable instanceof FPSCounter) {
+ ((FPSCounter)drawable).resetFPSCounter();
+ }
+ System.err.println("Swap Interval: "+_i+" -> "+i+" -> "+gl.getSwapInterval());
+ return true;
+ }
+ });
+ }
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_S){
+ if(null != autoDrawable) {
+ final String modeS = Region.getRenderModeString(renderModes);
+ final String type = modeS + ( Region.hasVariableWeight(renderModes) ? "-vc" : "-uc" ) ;
+ printScreenOnGLThread(autoDrawable, "./", "demo-"+type, "", false);
+ }
+ }
+ }
+ @Override
+ public void keyReleased(final KeyEvent arg0) {}
+ }
+}
diff --git a/src/demos/com/jogamp/opengl/demos/graph/GPUTextGLListener0A.java b/src/demos/com/jogamp/opengl/demos/graph/GPUTextGLListener0A.java
new file mode 100644
index 000000000..8a8a49869
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/graph/GPUTextGLListener0A.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright 2010 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.demos.graph;
+
+
+import com.jogamp.opengl.GL;
+import com.jogamp.opengl.GL2ES2;
+import com.jogamp.opengl.GLAutoDrawable;
+import com.jogamp.opengl.GLProfile;
+import com.jogamp.graph.curve.Region;
+import com.jogamp.graph.curve.opengl.RenderState;
+import com.jogamp.graph.geom.SVertex;
+import com.jogamp.newt.opengl.GLWindow;
+
+public class GPUTextGLListener0A extends GPUTextRendererListenerBase01 {
+
+ public GPUTextGLListener0A(final GLProfile glp, final RenderState rs, final int renderModes, final int sampleCount, final boolean blending, final boolean debug, final boolean trace) {
+ super(glp, rs, renderModes, sampleCount, blending, debug, trace);
+ }
+
+ @Override
+ public void init(final GLAutoDrawable drawable) {
+ if(drawable instanceof GLWindow) {
+ final GLWindow glw = (GLWindow) drawable;
+ attachInputListenerTo(glw);
+ }
+ super.init(drawable);
+
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+
+ final RenderState rs = getRenderer().getRenderState();
+
+ gl.setSwapInterval(1);
+ gl.glEnable(GL.GL_DEPTH_TEST);
+ gl.glEnable(GL.GL_BLEND);
+ rs.setColorStatic(0.1f, 0.1f, 0.1f, 1.0f);
+ }
+
+ @Override
+ public void dispose(final GLAutoDrawable drawable) {
+ if(drawable instanceof GLWindow) {
+ final GLWindow glw = (GLWindow) drawable;
+ detachInputListenerFrom(glw);
+ }
+ super.dispose(drawable);
+ }
+}
diff --git a/src/demos/com/jogamp/opengl/demos/graph/GPUTextNewtDemo.java b/src/demos/com/jogamp/opengl/demos/graph/GPUTextNewtDemo.java
new file mode 100644
index 000000000..b4e91e01f
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/graph/GPUTextNewtDemo.java
@@ -0,0 +1,193 @@
+/**
+ * Copyright 2010 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.demos.graph;
+
+import com.jogamp.opengl.GLCapabilities;
+import com.jogamp.opengl.GLProfile;
+import com.jogamp.opengl.demos.util.MiscUtils;
+import com.jogamp.opengl.math.geom.AABBox;
+
+import java.io.File;
+import java.io.IOException;
+
+import com.jogamp.common.util.InterruptSource;
+import com.jogamp.graph.curve.Region;
+import com.jogamp.graph.curve.opengl.RenderState;
+import com.jogamp.graph.font.Font;
+import com.jogamp.graph.font.FontFactory;
+import com.jogamp.graph.font.FontScale;
+import com.jogamp.graph.geom.SVertex;
+import com.jogamp.newt.MonitorDevice;
+import com.jogamp.newt.event.KeyAdapter;
+import com.jogamp.newt.event.KeyEvent;
+import com.jogamp.newt.event.WindowAdapter;
+import com.jogamp.newt.event.WindowEvent;
+import com.jogamp.newt.opengl.GLWindow;
+import com.jogamp.opengl.util.Animator;
+
+public class GPUTextNewtDemo {
+ /**
+ * FIXME:
+ *
+ * If DEBUG is enabled:
+ *
+ * Caused by: com.jogamp.opengl.GLException: Thread[main-Display-X11_:0.0-1-EDT-1,5,main] glGetError() returned the following error codes after a call to glFramebufferRenderbuffer( 0x8D40, 0x1902, 0x8D41, 0x1): GL_INVALID_ENUM ( 1280 0x500),
+ * at com.jogamp.opengl.DebugGL4bc.checkGLGetError(DebugGL4bc.java:33961)
+ * at com.jogamp.opengl.DebugGL4bc.glFramebufferRenderbuffer(DebugGL4bc.java:33077)
+ * at jogamp.graph.curve.opengl.VBORegion2PGL3.initFBOTexture(VBORegion2PGL3.java:295)
+ */
+ static final boolean DEBUG = false;
+ static final boolean TRACE = false;
+
+ static int SceneMSAASamples = 0;
+ static int GraphVBAASamples = 4;
+ static int GraphMSAASamples = 0;
+
+ public static void main(final String[] args) throws IOException {
+ Font opt_font = null;
+ int opt_fontSizeHead = -1;
+ int width = 800, height = 400;
+ int x = 10, y = 10;
+ if( 0 != args.length ) {
+ SceneMSAASamples = 0;
+ GraphMSAASamples = 0;
+ GraphVBAASamples = 0;
+
+ for(int i=0; i 0 ) {
+ caps.setSampleBuffers(true);
+ caps.setNumSamples(SceneMSAASamples);
+ }
+ System.out.println("Requested: " + caps);
+
+ int rmode = 0; // Region.VARIABLE_CURVE_WEIGHT_BIT;
+ int sampleCount = 0;
+ if( GraphVBAASamples > 0 ) {
+ rmode |= Region.VBAA_RENDERING_BIT;
+ sampleCount += GraphVBAASamples;
+ } else if( GraphMSAASamples > 0 ) {
+ rmode |= Region.MSAA_RENDERING_BIT;
+ sampleCount += GraphMSAASamples;
+ }
+
+ final GLWindow window = GLWindow.create(caps);
+ window.setPosition(x, y);
+ window.setSize(width, height);
+ window.setTitle("GPU Text Newt Demo - graph[vbaa"+GraphVBAASamples+" msaa"+GraphMSAASamples+"], msaa "+SceneMSAASamples);
+
+ final RenderState rs = RenderState.createRenderState(SVertex.factory());
+ final GPUTextGLListener0A textGLListener = new GPUTextGLListener0A(glp, rs, rmode, sampleCount, true, DEBUG, TRACE);
+ textGLListener.setFont(opt_font);
+ textGLListener.setFontHeadSize(opt_fontSizeHead);
+ // ((TextRenderer)textGLListener.getRenderer()).setCacheLimit(32);
+ window.addGLEventListener(textGLListener);
+ window.setVisible(true);
+
+ {
+ final Font font2 = textGLListener.getFont();
+ final float[] sDPI = FontScale.perMMToPerInch( window.getPixelsPerMM(new float[2]) );
+ final float font_ptpi = 12f;
+ final float font_ppi = FontScale.toPixels(font_ptpi, sDPI[1]);
+ final AABBox fontNameBox = font2.getMetricBounds(GPUTextRendererListenerBase01.textX1);
+ System.err.println("GPU Text Newt Demo: "+font2.fullString());
+ System.err.println("GPU Text Newt Demo: screen-dpi: "+sDPI[0]+"x"+sDPI[1]+", font "+font_ptpi+" pt, "+font_ppi+" pixel");
+ System.err.println("GPU Text Newt Demo: textX2: "+fontNameBox+" em, "+fontNameBox.scale(font_ppi, new float[3])+" px");
+ final MonitorDevice monitor = window.getMainMonitor();
+ System.err.println("GPU Text Newt Demo: "+monitor);
+ // window.setSurfaceSize((int)(fontNameBox.getWidth()*1.1f), (int)(fontNameBox.getHeight()*2f));
+ }
+
+ // FPSAnimator animator = new FPSAnimator(60);
+ final Animator animator = new Animator();
+ animator.setUpdateFPSFrames(60, null);
+ animator.add(window);
+
+ window.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyPressed(final KeyEvent arg0) {
+ if(arg0.getKeyCode() == KeyEvent.VK_F4) {
+ new InterruptSource.Thread() {
+ @Override
+ public void run() {
+ window.destroy();
+ } }.start();
+ }
+ }
+ });
+ window.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowDestroyed(final WindowEvent e) {
+ animator.stop();
+ }
+ });
+
+ animator.start();
+ }
+}
diff --git a/src/demos/com/jogamp/opengl/demos/graph/GPUTextRendererListenerBase01.java b/src/demos/com/jogamp/opengl/demos/graph/GPUTextRendererListenerBase01.java
new file mode 100644
index 000000000..f23548b8a
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/graph/GPUTextRendererListenerBase01.java
@@ -0,0 +1,588 @@
+/**
+ * Copyright 2010 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.demos.graph;
+
+import java.io.IOException;
+
+import com.jogamp.opengl.GL;
+import com.jogamp.opengl.GL2ES2;
+import com.jogamp.opengl.GLAnimatorControl;
+import com.jogamp.opengl.GLAutoDrawable;
+import com.jogamp.opengl.GLException;
+import com.jogamp.opengl.GLProfile;
+import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
+import com.jogamp.common.util.InterruptSource;
+import com.jogamp.graph.curve.Region;
+import com.jogamp.graph.curve.opengl.GLRegion;
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.curve.opengl.RenderState;
+import com.jogamp.graph.curve.opengl.TextRegionUtil;
+import com.jogamp.graph.font.Font;
+import com.jogamp.graph.font.FontFactory;
+import com.jogamp.graph.font.FontScale;
+import com.jogamp.graph.font.FontSet;
+import com.jogamp.graph.geom.plane.AffineTransform;
+import com.jogamp.newt.Window;
+import com.jogamp.newt.event.KeyEvent;
+import com.jogamp.newt.event.KeyListener;
+import com.jogamp.newt.opengl.GLWindow;
+import com.jogamp.opengl.math.geom.AABBox;
+import com.jogamp.opengl.util.PMVMatrix;
+
+/**
+ *
+ * GPURendererListenerBase01 Keys:
+ * - 1/2: zoom in/out
+ * - 6/7: 2nd pass texture size
+ * - 0/9: rotate
+ * - v: toggle v-sync
+ * - s: screenshot
+ *
+ * Additional Keys:
+ * - 3/4: font +/-
+ * - h: toogle draw 'font set'
+ * - f: toggle draw fps
+ * - space: toggle font (ubuntu/java)
+ * - i: live input text input (CR ends it, backspace supported)
+ */
+public abstract class GPUTextRendererListenerBase01 extends GPURendererListenerBase01 {
+ public final TextRegionUtil textRegionUtil;
+ private final GLRegion regionFPS, regionHead, regionBottom;
+ int fontSet = FontFactory.UBUNTU;
+ Font font;
+
+ int headType = 1;
+ boolean drawFPS = true;
+ final float fontSizeFName = 10f;
+ final float fontSizeFPS = 10f;
+ final int[] sampleCountFPS = new int[] { 8 };
+ float fontSizeHead = 12f;
+ float fontSizeCenter = 16f;
+ float dpiV = 96;
+ float ppmmV = 1;
+ final int fontSizeModulo = 100;
+ String fontName;
+ AABBox fontNameBox;
+ String headtext;
+ AABBox headbox;
+
+ protected final AffineTransform tempT1 = new AffineTransform();
+ protected final AffineTransform tempT2 = new AffineTransform();
+
+ static final String text2 = "The quick brown fox jumps over the lazy dog";
+ public static final String text_help =
+ "JOGL: Java™ Binding for OpenGL®, providing hardware-accelerated 3D graphics.\n\n"+
+ "JOGAMP graph demo using Resolution Independent NURBS\n"+
+ "JOGAMP JOGL - OpenGL ES2 profile\n"+
+ "Press 1/2 to zoom in/out the below text\n"+
+ "Press 3/4 to incr/decs font size (alt: head, w/o bottom)\n"+
+ "Press 6/7 to edit texture size if using VBAA\n"+
+ "Press 0/9 to rotate the below string\n"+
+ "Press s to screenshot\n"+
+ "Press v to toggle vsync\n"+
+ "Press i for live input text input (CR ends it, backspace supported)\n"+
+ "Press f to toggle fps. H for different text, space for font type\n";
+
+ public static final String textX1 =
+ "JOGL: Java™ Binding for OpenGL®, providing hardware-accelerated 3D graphics.\n\n"+
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec nec sapien tellus. \n"+
+ "Ut purus odio, rhoncus sit amet commodo eget, ullamcorper vel urna. Mauris ultricies \n"+
+ "quam iaculis urna cursus ornare. Nullam ut felis a ante ultrices ultricies nec a elit. \n"+
+ "In hac habitasse platea dictumst. Vivamus et mi a quam lacinia pharetra at venenatis est. \n"+
+ "Morbi quis bibendum nibh. Donec lectus orci, sagittis in consequat nec, volutpat nec nisi. \n"+
+ "Donec ut dolor et nulla tristique varius. In nulla magna, fermentum id tempus quis, semper \n"+
+ "in lorem. Maecenas in ipsum ac justo scelerisque sollicitudin. Quisque sit amet neque lorem, \n" +
+ "-------Press H to change text---------";
+
+ public static final String textX2 =
+ "I “Ask Jeff” or ‘Ask Jeff’. Take the chef d’œuvre! Two of [of] (of) ‘of’ “of” of? of! of*.\n"+
+ "Les Woëvres, the Fôret de Wœvres, the Voire and Vauvise. Yves is in heaven; D’Amboise is in jail.\n"+
+ "Lyford’s in Texas & L’Anse-aux-Griffons in Québec; the Łyna in Poland. Yriarte, Yciar and Ysaÿe are at Yale.\n"+
+ "Kyoto and Ryotsu are both in Japan, Kwikpak on the Yukon delta, Kvæven in Norway, Kyulu in Kenya, not in Rwanda.…\n"+
+ "Von-Vincke-Straße in Münster, Vdovino in Russia, Ytterbium in the periodic table. Are Toussaint L’Ouverture, Wölfflin, Wolfe,\n"+
+ "Miłosz and Wū Wŭ all in the library? 1510–1620, 11:00 pm, and the 1980s are over. X\n"+
+ "-------Press H to change text---------";
+
+ public static final String textX20 =
+ "I “Ask Jeff” or ‘Ask Jeff’. Take the chef d’œuvre! Two of [of] (of) ‘of’ “of” of? of! of*.\n"+
+ "Two of [of] (of) ‘of’ “of” of? of! of*. Ydes, Yffignac and Ygrande are in France: so are Ypres,\n"+
+ "Les Woëvres, the Fôret de Wœvres, the Voire and Vauvise. Yves is in heaven; D’Amboise is in jail.\n"+
+ "Lyford’s in Texas & L’Anse-aux-Griffons in Québec; the Łyna in Poland. Yriarte, Yciar and Ysaÿe are at Yale.\n"+
+ "Kyoto and Ryotsu are both in Japan, Kwikpak on the Yukon delta, Kvæven in Norway, Kyulu in Kenya, not in Rwanda.…\n"+
+ "Walton’s in West Virginia, but «Wren» is in Oregon. Tlálpan is near Xochimilco in México.\n"+
+ "The Zygos & Xylophagou are in Cyprus, Zwettl in Austria, Fænø in Denmark, the Vøringsfossen and Værøy in Norway.\n"+
+ "Tchula is in Mississippi, the Tittabawassee in Michigan. Twodot is here in Montana, Ywamun in Burma.\n"+
+ "Yggdrasil and Ymir, Yngvi and Vóden, Vídrið and Skeggjöld and Týr are all in the Eddas.\n"+
+ "Tørberget and Våg, of course, are in Norway, Ktipas and Tmolos in Greece, but Vázquez is in Argentina, Vreden in Germany,\n"+
+ "Von-Vincke-Straße in Münster, Vdovino in Russia, Ytterbium in the periodic table. Are Toussaint L’Ouverture, Wölfflin, Wolfe,\n"+
+ "Miłosz and Wū Wŭ all in the library? 1510–1620, 11:00 pm, and the 1980s are over.\n"+
+ "Ut purus odio, rhoncus sit amet commodo eget, ullamcorper vel urna. Mauris ultricies \n"+
+ "-------Press H to change text---------";
+
+ static final String textXLast = "abcdefghijklmnopqrstuvwxyz\nABCDEFGHIJKLMNOPQRSTUVWXYZ\n0123456789.:,;(*!?/\\\")$%^&-+@~#<>{}[]";
+
+ Window upstream_window = null;
+ StringBuilder userString = new StringBuilder(textX1);
+ boolean userInput = false;
+ public GPUTextRendererListenerBase01(final GLProfile glp, final RenderState rs, final int renderModes, final int sampleCount, final boolean blending, final boolean debug, final boolean trace) {
+ // NOTE_ALPHA_BLENDING: We use alpha-blending
+ super(RegionRenderer.create(rs, blending ? RegionRenderer.defaultBlendEnable : null,
+ blending ? RegionRenderer.defaultBlendDisable : null),
+ renderModes, debug, trace);
+ rs.setHintMask(RenderState.BITHINT_GLOBAL_DEPTH_TEST_ENABLED);
+ this.textRegionUtil = new TextRegionUtil(renderModes);
+ this.regionFPS = GLRegion.create(glp, renderModes, null);
+ this.regionHead = GLRegion.create(glp, renderModes, null);
+ this.regionBottom = GLRegion.create(glp, renderModes, null);
+ setFontSet(fontSet, FontSet.FAMILY_LIGHT, FontSet.STYLE_NONE);
+ setMatrix(0, 0, 0, 0f, sampleCount);
+ }
+
+ void switchHeadBox() {
+ setHeadBox( ( headType + 1 ) % 5, true );
+ }
+ public int getHeadBoxType() { return headType; }
+ public AABBox getHeadBox() { return headbox; }
+ public void setHeadBox(final int choice, final boolean resize) {
+ headType = choice % 5 ;
+ switch(headType) {
+ case 0:
+ headtext = null;
+ break;
+
+ case 1:
+ headtext= textX1;
+ break;
+ case 2:
+ headtext= textX2;
+ break;
+ case 3:
+ headtext= text_help;
+ break;
+
+ default:
+ headtext = textXLast;
+ }
+ if(resize && null != headtext) {
+ headbox = font.getMetricBounds(headtext);
+ if( headtext != text_help ) {
+ final float pxSz = FontScale.toPixels(fontSizeHead, dpiV);
+ upsizeWindowSurface(upstream_window, true, (int)(headbox.getWidth()*pxSz*1.1f), (int)(headbox.getHeight()*pxSz*2f));
+ }
+ }
+ }
+
+ public void setHeadBox(final String text, final boolean resize) {
+ headtext = text;
+ if(resize && null != headtext) {
+ headbox = font.getMetricBounds(headtext);
+ if( headtext != text_help ) {
+ final float pxSz = FontScale.toPixels(fontSizeHead, dpiV);
+ upsizeWindowSurface(upstream_window, true, (int)(headbox.getWidth()*pxSz*1.1f), (int)(headbox.getHeight()*pxSz*2f));
+ }
+ }
+ }
+
+ public static void upsizeWindowSurface(final Window window, final boolean off_thread, final int w, final int h) {
+ if( null == window ) {
+ return;
+ }
+ final int w2 = Math.max(window.getSurfaceWidth(), w);
+ final int h2 = Math.max(window.getSurfaceHeight(), h);
+ System.err.println("upsizeWindowSurface: "+window.getSurfaceWidth()+"x"+window.getSurfaceHeight()+" -> "+w+"x"+h+" -> "+w2+"x"+h2);
+ if( off_thread ) {
+ new InterruptSource.Thread() {
+ @Override
+ public void run() {
+ window.setSurfaceSize(w2, h2);
+ } }.start();
+ } else {
+ window.setSurfaceSize(w2, h2);
+ }
+ }
+
+ @Override
+ public void init(final GLAutoDrawable drawable) {
+ super.init(drawable);
+ final Object upObj = drawable.getUpstreamWidget();
+ if( upObj instanceof Window ) {
+ upstream_window = (Window) upObj;
+ final float[] sPpMM = upstream_window.getPixelsPerMM(new float[2]);
+ final float[] sDPI = FontScale.perMMToPerInch( new float[] { sPpMM[0], sPpMM[1] } );
+ dpiV = sDPI[1];
+ ppmmV = sPpMM[1];
+ System.err.println("Using vertical screen DPI of "+dpiV+", "+ppmmV+" pixel/mm");
+ } else {
+ System.err.println("Using vertical default DPI of "+dpiV+", "+ppmmV+" pixel/mm");
+ }
+ fontNameBox = font.getGlyphBounds(fontName, tempT1, tempT2);
+ setHeadBox(headType, true);
+ {
+ final float pixelSizeFName = FontScale.toPixels(fontSizeFName, dpiV);
+ System.err.println("XXX: fontName size "+fontSizeFName+"pt, dpiV "+dpiV+" -> "+pixelSizeFName+"px");
+ System.err.println("XXX: fontName boxM fu "+font.getMetricBoundsFU(fontName));
+ System.err.println("XXX: fontName boxG fu "+font.getGlyphBoundsFU(fontName, tempT1, tempT2));
+ System.err.println("XXX: fontName boxM em "+font.getMetricBounds(fontName));
+ System.err.println("XXX: fontName boxG em "+font.getGlyphBounds(fontName, tempT1, tempT2));
+ System.err.println("XXX: fontName box height px "+(fontNameBox.getHeight() * pixelSizeFName));
+ }
+ }
+
+ @Override
+ public void reshape(final GLAutoDrawable drawable, final int xstart, final int ystart, final int width, final int height) {
+ super.reshape(drawable, xstart, ystart, width, height);
+ final Object upObj = drawable.getUpstreamWidget();
+ if( upObj instanceof Window ) {
+ upstream_window = (Window) upObj;
+ }
+ final float dist = 100f;
+ nearPlaneX0 = nearPlane1Box.getMinX() * dist;
+ nearPlaneY0 = nearPlane1Box.getMinY() * dist;
+ nearPlaneZ0 = nearPlane1Box.getMinZ() * dist;
+ final float xd = nearPlane1Box.getWidth() * dist;
+ final float yd = nearPlane1Box.getHeight() * dist;
+ nearPlaneSx = xd / width;
+ nearPlaneSy = yd / height;
+ nearPlaneS = nearPlaneSy;
+ System.err.printf("Scale: [%f x %f] / [%d x %d] = [%f, %f] -> %f%n", xd, yd, width, height, nearPlaneSx, nearPlaneSy, nearPlaneS);
+ }
+ float nearPlaneX0, nearPlaneY0, nearPlaneZ0, nearPlaneSx, nearPlaneSy, nearPlaneS;
+
+ @Override
+ public void dispose(final GLAutoDrawable drawable) {
+ upstream_window = null;
+ regionFPS.destroy(drawable.getGL().getGL2ES2());
+ regionHead.destroy(drawable.getGL().getGL2ES2());
+ regionBottom.destroy(drawable.getGL().getGL2ES2());
+ super.dispose(drawable);
+ }
+
+ @Override
+ public void display(final GLAutoDrawable drawable) {
+ final Object upObj = drawable.getUpstreamWidget();
+ if( upObj instanceof Window ) {
+ upstream_window = (Window) upObj;
+ }
+ final int width = drawable.getSurfaceWidth();
+ final int height = drawable.getSurfaceHeight();
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+
+ gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+ gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
+
+ // final float zDistance0 = 500f;
+ // final float zDistance1 = 400f;
+ // final float[] objPos = new float[3];
+ // final float[] winZ = new float[1];
+ // final int[] view = new int[] { 0, 0, drawable.getWidth(), drawable.getHeight() };
+
+ final RegionRenderer renderer = getRenderer();
+ final RenderState rs = renderer.getRenderState();
+ final PMVMatrix pmv = renderer.getMatrix();
+ pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
+ pmv.glLoadIdentity();
+ rs.setColorStatic(0.1f, 0.1f, 0.1f, 1.0f);
+ final float pixelSizeFName = FontScale.toPixels(fontSizeFName, dpiV);
+ final float pixelSizeHead = FontScale.toPixels(fontSizeHead, dpiV);
+ final float mmSizeHead = pixelSizeHead / ppmmV;
+ final float pixelSizeCenter = FontScale.toPixels(fontSizeCenter, dpiV);
+ final float mmSizeCenter = pixelSizeCenter / ppmmV;
+
+ renderer.enable(gl, true);
+
+ if( drawFPS ) {
+ pmv.glPushMatrix();
+ final float pixelSizeFPS = FontScale.toPixels(fontSizeFPS, dpiV);
+ final float lfps, tfps, td;
+ final GLAnimatorControl animator = drawable.getAnimator();
+ if( null != animator ) {
+ lfps = animator.getLastFPS();
+ tfps = animator.getTotalFPS();
+ td = animator.getTotalFPSDuration()/1000f;
+ } else {
+ lfps = 0f;
+ tfps = 0f;
+ td = 0f;
+ }
+ final String modeS = Region.getRenderModeString(regionFPS.getRenderModes());
+ final String text = String.format("%03.1f/%03.1f fps, v-sync %d, dpiV %.2f %.2f px/mm, font[head %.1fpt %.2fpx %.2fmm, center %.1fpt %.2fpx %.2fmm], %s-samples[%d, this %d], blend %b, alpha %d",
+ lfps, tfps, gl.getSwapInterval(), dpiV, ppmmV,
+ fontSizeHead, pixelSizeHead, mmSizeHead,
+ fontSizeCenter, pixelSizeCenter, mmSizeCenter,
+ modeS, getSampleCount()[0], sampleCountFPS[0],
+ renderer.getRenderState().isHintMaskSet(RenderState.BITHINT_BLENDING_ENABLED),
+ drawable.getChosenGLCapabilities().getAlphaBits());
+
+ // bottom, half line up
+ pmv.glTranslatef(nearPlaneX0, nearPlaneY0+(nearPlaneS * pixelSizeFPS / 2f), nearPlaneZ0);
+ {
+ final float sxy = nearPlaneS * pixelSizeFPS;
+ pmv.glScalef(sxy, sxy, 1.0f);
+ }
+ // No cache, keep region alive!
+ TextRegionUtil.drawString3D(gl, regionFPS.clear(gl), renderer, font, text, null, sampleCountFPS, tempT1, tempT2);
+ pmv.glPopMatrix();
+ }
+
+ // float dx = width - ( fontNameBox.getWidth() + font.getAdvanceWidth( Glyph.ID_SPACE ) ) * pixelSizeFName;
+ float dx = width - ( fontNameBox.getWidth() + 2 * font.getAdvanceWidth( font.getGlyphID('X') ) ) * pixelSizeFName;
+ float dy = height - fontNameBox.getHeight() * pixelSizeFName;
+ {
+ pmv.glPushMatrix();
+ pmv.glTranslatef(nearPlaneX0+(dx*nearPlaneSx), nearPlaneY0+(dy*nearPlaneSy), nearPlaneZ0);
+ {
+ final float sxy = nearPlaneS * pixelSizeFName;
+ pmv.glScalef(sxy, sxy, 1.0f);
+ }
+ // System.err.printf("FontN: [%f %f] -> [%f %f]%n", dx, dy, nearPlaneX0+(dx*nearPlaneSx), nearPlaneY0+(dy*nearPlaneSy));
+ textRegionUtil.drawString3D(gl, renderer, font, fontName, null, getSampleCount());
+ pmv.glPopMatrix();
+ }
+
+ dx = 10f;
+ dy += -fontNameBox.getHeight() * pixelSizeFName - 10f;
+
+ if(null != headtext) {
+ pmv.glPushMatrix();
+ // System.err.printf("Head: [%f %f] -> [%f %f]%n", dx, dy, nearPlaneX0+(dx*nearPlaneSx), nearPlaneY0+(dy*nearPlaneSy));
+ pmv.glTranslatef(nearPlaneX0+(dx*nearPlaneSx), nearPlaneY0+(dy*nearPlaneSy), nearPlaneZ0);
+ {
+ final float sxy = nearPlaneS * pixelSizeHead;
+ pmv.glScalef(sxy, sxy, 1.0f);
+ }
+ // pmv.glTranslatef(x0, y1, z0);
+ textRegionUtil.drawString3D(gl, renderer, font, headtext, null, getSampleCount());
+ pmv.glPopMatrix();
+ }
+
+ dy += ( -headbox.getHeight() - font.getLineHeight() ) * pixelSizeCenter;
+
+ {
+ pmv.glPushMatrix();
+ pmv.glTranslatef(nearPlaneX0+(dx*nearPlaneSx), nearPlaneY0+(dy*nearPlaneSy), nearPlaneZ0);
+ // System.err.printf("Bottom: [%f %f] -> [%f %f]%n", dx, dy, nearPlaneX0+(dx*nearPlaneSx), nearPlaneY0+(dy*nearPlaneSy));
+ pmv.glTranslatef(getXTran(), getYTran(), getZTran());
+ pmv.glRotatef(getAngle(), 0, 1, 0);
+ {
+ final float sxy = nearPlaneS * pixelSizeCenter;
+ pmv.glScalef(sxy, sxy, 1.0f);
+ }
+ rs.setColorStatic(0.9f, 0.0f, 0.0f, 1.0f);
+
+ if( bottomTextUseFrustum ) {
+ regionBottom.setFrustum(pmv.glGetFrustum());
+ }
+ if(!userInput) {
+ if( bottomTextUseFrustum ) {
+ TextRegionUtil.drawString3D(gl, regionBottom.clear(gl), renderer, font, text2, null, getSampleCount(), tempT1, tempT2);
+ } else {
+ textRegionUtil.drawString3D(gl, renderer, font, text2, null, getSampleCount());
+ }
+ } else {
+ if( bottomTextUseFrustum ) {
+ TextRegionUtil.drawString3D(gl, regionBottom.clear(gl), renderer, font, userString.toString(), null, getSampleCount(), tempT1, tempT2);
+ } else {
+ textRegionUtil.drawString3D(gl, renderer, font, userString.toString(), null, getSampleCount());
+ }
+ }
+ pmv.glPopMatrix();
+ }
+ renderer.enable(gl, false);
+ }
+ final boolean bottomTextUseFrustum = true;
+
+ public Font getFont() { return font; }
+ public float getFontSizeHead() { return fontSizeHead; }
+
+ public void fontBottomIncr(final int v) {
+ fontSizeCenter = Math.abs((fontSizeCenter + v) % fontSizeModulo) ;
+ dumpMatrix(true);
+ }
+
+ public void fontHeadIncr(final int v) {
+ fontSizeHead = Math.abs((fontSizeHead + v) % fontSizeModulo) ;
+ updateFontNameBox();
+ if(null != headtext) {
+ headbox = font.getMetricBounds(headtext);
+ }
+ }
+
+ public void setFontHeadSize(final int v) {
+ if( 0 < v ) {
+ fontSizeHead = v % fontSizeModulo;
+ updateFontNameBox();
+ if(null != headtext) {
+ headbox = font.getMetricBounds(headtext);
+ }
+ }
+ }
+
+ public boolean nextFontSet() {
+ try {
+ final int set = ( fontSet == FontFactory.UBUNTU ) ? FontFactory.JAVA : FontFactory.UBUNTU ;
+ final Font _font = FontFactory.get(set).getDefault();
+ if(null != _font) {
+ fontSet = set;
+ font = _font;
+ updateFontNameBox();
+ return true;
+ }
+ } catch (final IOException ex) {
+ System.err.println("Caught: "+ex.getMessage());
+ }
+ return false;
+ }
+
+ public boolean setFontSet(final int set, final int family, final int stylebits) {
+ try {
+ final Font _font = FontFactory.get(set).get(family, stylebits);
+ if(null != _font) {
+ fontSet = set;
+ font = _font;
+ updateFontNameBox();
+ return true;
+ }
+ } catch (final IOException ex) {
+ System.err.println("Caught: "+ex.getMessage());
+ }
+ return false;
+ }
+
+ public boolean setFont(final Font _font) {
+ if(null != _font) {
+ // fontSet = ???
+ font = _font;
+ updateFontNameBox();
+ return true;
+ }
+ return false;
+ }
+
+ private void updateFontNameBox() {
+ fontName = font.getFullFamilyName()+" (head "+fontSizeHead+"pt)";
+ fontNameBox = font.getMetricBounds(fontName);
+ }
+
+ public boolean isUserInputMode() { return userInput; }
+
+ void dumpMatrix(final boolean bbox) {
+ System.err.println("Matrix: " + getXTran() + "/" + getYTran() + " x"+getZTran() + " @"+getAngle() +" fontSize "+fontSizeCenter);
+ if(bbox) {
+ System.err.println("bbox em: "+font.getMetricBounds(text2));
+ System.err.println("bbox px: "+font.getMetricBounds(text2).scale(nearPlaneS * FontScale.toPixels(fontSizeCenter, dpiV), new float[3]));
+ }
+ }
+
+ KeyAction keyAction = null;
+
+ @Override
+ public void attachInputListenerTo(final GLWindow window) {
+ if ( null == keyAction ) {
+ keyAction = new KeyAction();
+ window.addKeyListener(keyAction);
+ super.attachInputListenerTo(window);
+ }
+ }
+
+ @Override
+ public void detachInputListenerFrom(final GLWindow window) {
+ super.detachInputListenerFrom(window);
+ if ( null == keyAction ) {
+ return;
+ }
+ window.removeKeyListener(keyAction);
+ }
+
+ @Override
+ public void printScreen(final GLAutoDrawable drawable, final String dir, final String tech, final String objName, final boolean exportAlpha) throws GLException, IOException {
+ final String fn = font.getFullFamilyName().replace(' ', '_').replace('-', '_');
+ final String modes = Region.getRenderModeString(getRenderModes());
+ final String fsaa = "fsaa"+drawable.getChosenGLCapabilities().getNumSamples();
+ super.printScreen(drawable, dir, tech+"-"+modes, fsaa+"-"+fn+"-text"+getHeadBoxType()+"-"+objName, exportAlpha);
+ }
+
+ float fontHeadScale = 1f;
+
+ public class KeyAction implements KeyListener {
+ @Override
+ public void keyPressed(final KeyEvent e) {
+ if(userInput) {
+ return;
+ }
+ final short s = e.getKeySymbol();
+ if(s == KeyEvent.VK_3) {
+ if( e.isAltDown() ) {
+ fontHeadIncr(1);
+ } else {
+ fontBottomIncr(1);
+ }
+ }
+ else if(s == KeyEvent.VK_4) {
+ if( e.isAltDown() ) {
+ fontHeadIncr(-1);
+ } else {
+ fontBottomIncr(-1);
+ }
+ }
+ else if(s == KeyEvent.VK_H) {
+ switchHeadBox();
+ }
+ else if(s == KeyEvent.VK_F) {
+ drawFPS = !drawFPS;
+ }
+ else if(s == KeyEvent.VK_SPACE) {
+ nextFontSet();
+ }
+ else if(s == KeyEvent.VK_I) {
+ userInput = true;
+ setIgnoreInput(true);
+ }
+ }
+
+ @Override
+ public void keyReleased(final KeyEvent e) {
+ if( !e.isPrintableKey() || e.isAutoRepeat() ) {
+ return;
+ }
+ if(userInput) {
+ final short k = e.getKeySymbol();
+ if( KeyEvent.VK_ENTER == k ) {
+ userInput = false;
+ setIgnoreInput(false);
+ } else if( KeyEvent.VK_BACK_SPACE == k && userString.length()>0) {
+ userString.deleteCharAt(userString.length()-1);
+ } else {
+ final char c = e.getKeyChar();
+ if( font.isPrintableChar( c ) ) {
+ userString.append(c);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/demos/com/jogamp/opengl/demos/graph/MSAATool.java b/src/demos/com/jogamp/opengl/demos/graph/MSAATool.java
new file mode 100644
index 000000000..e0815b70a
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/graph/MSAATool.java
@@ -0,0 +1,96 @@
+/**
+ * Copyright 2010 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.demos.graph;
+
+import com.jogamp.opengl.GL;
+import com.jogamp.opengl.GL2ES2;
+import com.jogamp.opengl.GL2GL3;
+import com.jogamp.opengl.GLAutoDrawable;
+import com.jogamp.opengl.GLCapabilitiesImmutable;
+
+public class MSAATool {
+ public static boolean glIsEnabled(final GL gl, final int name) {
+ boolean isEnabled = false;
+ try {
+ isEnabled = gl.glIsEnabled(name);
+ final int glerr = gl.glGetError();
+ if(GL.GL_NO_ERROR != glerr) {
+ System.err.println("glIsEnabled(0x"+Integer.toHexString(name)+") -> error 0x"+Integer.toHexString(glerr));
+ }
+ } catch (final Exception e) {
+ System.err.println("Caught exception: "+e.getMessage());
+ // e.printStackTrace();
+ }
+ return isEnabled;
+ }
+ public static void dump(final GLAutoDrawable drawable) {
+ final float[] vf = new float[] { 0f };
+ final byte[] vb = new byte[] { 0 };
+ final int[] vi = new int[] { 0, 0 };
+
+ System.out.println("GL MSAA SETUP:");
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+ final GLCapabilitiesImmutable caps = drawable.getChosenGLCapabilities();
+ System.out.println(" Caps realised "+caps);
+ System.out.println(" Caps sample buffers "+caps.getSampleBuffers()+", samples "+caps.getNumSamples());
+
+ System.out.println(" GL MULTISAMPLE "+glIsEnabled(gl, GL.GL_MULTISAMPLE));
+ // sample buffers min 0, same as GLX_SAMPLE_BUFFERS_ARB or WGL_SAMPLE_BUFFERS_ARB
+ gl.glGetIntegerv(GL.GL_SAMPLE_BUFFERS, vi, 0);
+ // samples min 0
+ gl.glGetIntegerv(GL.GL_SAMPLES, vi, 1);
+ System.out.println(" GL SAMPLE_BUFFERS "+vi[0]+", SAMPLES "+vi[1]);
+
+ System.out.println("GL CSAA SETUP:");
+ // default FALSE
+ System.out.println(" GL SAMPLE COVERAGE "+glIsEnabled(gl, GL.GL_SAMPLE_COVERAGE));
+ // default FALSE
+ System.out.println(" GL SAMPLE_ALPHA_TO_COVERAGE "+glIsEnabled(gl, GL.GL_SAMPLE_ALPHA_TO_COVERAGE));
+ // default FALSE
+ System.out.println(" GL SAMPLE_ALPHA_TO_ONE "+glIsEnabled(gl, GL.GL_SAMPLE_ALPHA_TO_ONE));
+ // default FALSE, value 1, invert false
+ gl.glGetFloatv(GL.GL_SAMPLE_COVERAGE_VALUE, vf, 0);
+ gl.glGetBooleanv(GL.GL_SAMPLE_COVERAGE_INVERT, vb, 0);
+ System.out.println(" GL SAMPLE_COVERAGE "+glIsEnabled(gl, GL.GL_SAMPLE_COVERAGE) +
+ ": SAMPLE_COVERAGE_VALUE "+vf[0]+
+ ", SAMPLE_COVERAGE_INVERT "+vb[0]);
+ dumpBlend(gl);
+ }
+ public static void dumpBlend(final GL gl) {
+ final int[] vi = new int[] { 0, 0, 0, 0 };
+ gl.glGetIntegerv(GL.GL_BLEND, vi, 0);
+ gl.glGetIntegerv(GL.GL_BLEND_SRC_ALPHA, vi, 1);
+ gl.glGetIntegerv(GL.GL_BLEND_SRC_RGB, vi, 2);
+ gl.glGetIntegerv(GL.GL_BLEND_DST_RGB, vi, 3);
+ final boolean blendEnabled = vi[0] == GL.GL_TRUE;
+ System.out.println("GL_BLEND "+blendEnabled+"/"+glIsEnabled(gl, GL.GL_BLEND) +
+ " GL_SRC_ALPHA 0x"+Integer.toHexString(vi[1])+
+ " GL_SRC_RGB 0x"+Integer.toHexString(vi[2])+
+ " GL_DST_RGB 0x"+Integer.toHexString(vi[3]));
+ }
+}
diff --git a/src/demos/com/jogamp/opengl/demos/graph/TextRendererGLELBase.java b/src/demos/com/jogamp/opengl/demos/graph/TextRendererGLELBase.java
new file mode 100644
index 000000000..61240e4a2
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/graph/TextRendererGLELBase.java
@@ -0,0 +1,291 @@
+/**
+ * Copyright 2014 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.demos.graph;
+
+import java.io.IOException;
+
+import com.jogamp.opengl.GL2ES2;
+import com.jogamp.opengl.GLAutoDrawable;
+import com.jogamp.opengl.GLEventListener;
+import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
+
+import com.jogamp.graph.curve.opengl.GLRegion;
+import com.jogamp.graph.curve.opengl.RenderState;
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.curve.opengl.TextRegionUtil;
+import com.jogamp.graph.font.Font;
+import com.jogamp.graph.font.FontFactory;
+import com.jogamp.graph.font.FontScale;
+import com.jogamp.graph.font.FontSet;
+import com.jogamp.graph.geom.SVertex;
+import com.jogamp.graph.geom.plane.AffineTransform;
+import com.jogamp.newt.Window;
+import com.jogamp.opengl.util.PMVMatrix;
+
+public abstract class TextRendererGLELBase implements GLEventListener {
+ public final int renderModes;
+
+ protected final int[] vbaaSampleCount;
+ protected final float[] staticRGBAColor = new float[] { 1f, 1f, 1f, 1f };
+
+ private boolean exclusivePMVMatrix = true;
+ private PMVMatrix sharedPMVMatrix = null;
+ private RenderState rs = null;
+ private RegionRenderer.GLCallback enableCallback=null, disableCallback=null;
+ protected RegionRenderer renderer = null;
+ protected TextRegionUtil textRenderUtil = null;
+
+ protected final AffineTransform tempT1 = new AffineTransform();
+ protected final AffineTransform tempT2 = new AffineTransform();
+
+ /** scale pixel, default is 1f */
+ protected float pixelScale = 1.0f;
+
+ /** dpi display resolution, queried at {@link #init(GLAutoDrawable)} if NEWT, otherwise 96. */
+ protected float dpiH = 96;
+
+ boolean flipVerticalInGLOrientation = false;
+
+ /**
+ * @param fontSet e.g. default is {@link FontFactory#UBUNTU}
+ * @param fontFamily e.g. default is {@link FontSet#FAMILY_REGULAR}
+ * @param fontStylebits e.g. default is {@link FontSet#STYLE_NONE}
+ * @return the resulting font.
+ */
+ public static Font getFont(final int fontSet, final int fontFamily, final int fontStylebits) {
+ try {
+ return FontFactory.get(fontSet).get(fontFamily, fontStylebits);
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * @param renderModes
+ * @param sampleCount desired multisampling sample count for msaa-rendering.
+ * @see #setRendererCallbacks(com.jogamp.graph.curve.opengl.RegionRenderer.GLCallback, com.jogamp.graph.curve.opengl.RegionRenderer.GLCallback)
+ */
+ public TextRendererGLELBase(final int renderModes, final int[] sampleCount) {
+ this.renderModes = renderModes;
+ this.vbaaSampleCount = sampleCount;
+ }
+
+ /**
+ *
+ * Must be called before {@link #init(GLAutoDrawable)}.
+ *
+ * @param rs
+ */
+ public void setRenderState(final RenderState rs) { this.rs = rs; }
+
+ /**
+ * In exclusive mode, impl. uses a pixelScale of 1f and orthogonal PMV on window dimensions
+ * and renderString uses 'height' for '1'.
+ *
+ * In non-exclusive mode, i.e. shared w/ custom PMV (within another 3d scene),
+ * it uses the custom pixelScale and renderString uses normalized 'height', i.e. '1'.
+ *
+ *
+ * Must be called before {@link #init(GLAutoDrawable)}.
+ *
+ */
+ public void setSharedPMVMatrix(final PMVMatrix pmv) {
+ this.sharedPMVMatrix = pmv;
+ }
+
+ /**
+ * See {@link RegionRenderer#create(RenderState, com.jogamp.graph.curve.opengl.RegionRenderer.GLCallback, com.jogamp.graph.curve.opengl.RegionRenderer.GLCallback)}.
+ *
+ * Must be called before {@link #init(GLAutoDrawable)}.
+ *
+ */
+ public void setRendererCallbacks(final RegionRenderer.GLCallback enable, final RegionRenderer.GLCallback disable) {
+ this.enableCallback = enable;
+ this.disableCallback = disable;
+ }
+
+ public void setFlipVerticalInGLOrientation(final boolean v) { flipVerticalInGLOrientation=v; }
+ public final RegionRenderer getRenderer() { return renderer; }
+ public final TextRegionUtil getTextRenderUtil() { return textRenderUtil; }
+
+ @Override
+ public void init(final GLAutoDrawable drawable) {
+ if( null == this.rs ) {
+ exclusivePMVMatrix = null == sharedPMVMatrix;
+ this.rs = RenderState.createRenderState(SVertex.factory(), sharedPMVMatrix);
+ }
+ this.renderer = RegionRenderer.create(rs, enableCallback, disableCallback);
+ rs.setHintMask(RenderState.BITHINT_GLOBAL_DEPTH_TEST_ENABLED);
+ this.textRenderUtil = new TextRegionUtil(renderModes);
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+ renderer.init(gl);
+ rs.setColorStatic(staticRGBAColor[0], staticRGBAColor[1], staticRGBAColor[2], staticRGBAColor[3]);
+ renderer.enable(gl, false);
+
+ final Object upObj = drawable.getUpstreamWidget();
+ if( upObj instanceof Window ) {
+ final float[] dpi = FontScale.perMMToPerInch( ((Window)upObj).getPixelsPerMM(new float[2]) );
+ dpiH = dpi[1];
+ }
+ }
+
+ @Override
+ public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) {
+ if( null != renderer ) {
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+ renderer.enable(gl, true);
+ if( exclusivePMVMatrix ) {
+ // renderer.reshapePerspective(gl, 45.0f, width, height, 0.1f, 1000.0f);
+ renderer.reshapeOrtho(width, height, 0.1f, 1000.0f);
+ pixelScale = 1.0f;
+ } else {
+ renderer.reshapeNotify(width, height);
+ }
+ renderer.enable(gl, false);
+ }
+ }
+
+ @Override
+ public abstract void display(GLAutoDrawable drawable);
+
+ @Override
+ public void dispose(final GLAutoDrawable drawable) {
+ if( null != renderer ) {
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+ renderer.destroy(gl);
+ }
+ }
+
+ int lastRow = -1;
+
+ /**
+ *
+ * @param drawable
+ * @param font
+ * @param pixelSize Use {@link Font#toPixels(float, float)} for resolution correct pixel-size.
+ * @param text
+ * @param column
+ * @param tx
+ * @param ty
+ * @param tz
+ * @param cacheRegion
+ */
+ public void renderString(final GLAutoDrawable drawable,
+ final Font font, final float pixelSize, final String text,
+ final int column, final float tx, final float ty, final float tz, final boolean cacheRegion) {
+ final int row = lastRow + 1;
+ renderStringImpl(drawable, font, pixelSize, text, column, row, tx, ty, tz, cacheRegion, null);
+ }
+
+ public void renderString(final GLAutoDrawable drawable,
+ final Font font, final float pixelSize, final String text,
+ final int column, final float tx, final float ty, final float tz, final GLRegion region) {
+ final int row = lastRow + 1;
+ renderStringImpl(drawable, font, pixelSize, text, column, row, tx, ty, tz, false, region);
+ }
+
+ /**
+ *
+ * @param drawable
+ * @param font
+ * @param pixelSize Use {@link Font#toPixels(float, float)} for resolution correct pixel-size.
+ * @param text
+ * @param column
+ * @param row
+ * @param tx
+ * @param ty
+ * @param tz
+ * @param cacheRegion
+ */
+ public void renderString(final GLAutoDrawable drawable,
+ final Font font, final float pixelSize, final String text,
+ final int column, final int row,
+ final float tx, final float ty, final float tz, final boolean cacheRegion) {
+ renderStringImpl(drawable, font, pixelSize, text, column, row, tx, ty, tz, cacheRegion, null);
+ }
+
+ public void renderString(final GLAutoDrawable drawable,
+ final Font font, final float pixelSize, final String text,
+ final int column, final int row,
+ final float tx, final float ty, final float tz, final GLRegion region) {
+ renderStringImpl(drawable, font, pixelSize, text, column, row, tx, ty, tz, false, region);
+ }
+
+ private void renderStringImpl(final GLAutoDrawable drawable,
+ final Font font, final float pixelSize, final String text,
+ final int column, final int row,
+ final float tx, final float ty, final float tz, final boolean cacheRegion, final GLRegion region) {
+ if( null != renderer ) {
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+
+ float dx = tx;
+ float dy;
+
+ if( !exclusivePMVMatrix ) {
+ dy = 1f-ty;
+ } else {
+ final int height = drawable.getSurfaceHeight();
+ dy = height-ty;
+ }
+ final float sxy = pixelScale * pixelSize;
+ final int newLineCount = TextRegionUtil.getCharCount(text, '\n');
+ final float lineHeight = font.getLineHeight();
+ dx += sxy * font.getAdvanceWidth('X') * column;
+ dy -= sxy * lineHeight * ( row + 1 );
+
+ final PMVMatrix pmvMatrix = rs.getMatrix();
+ pmvMatrix.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
+ if( !exclusivePMVMatrix ) {
+ pmvMatrix.glPushMatrix();
+ } else {
+ pmvMatrix.glLoadIdentity();
+ }
+ pmvMatrix.glTranslatef(dx, dy, tz);
+ if( flipVerticalInGLOrientation && drawable.isGLOriented() ) {
+ pmvMatrix.glScalef(sxy, -1f*sxy, 1.0f);
+ } else {
+ pmvMatrix.glScalef(sxy, sxy, 1.0f);
+ }
+ renderer.enable(gl, true);
+ if( cacheRegion ) {
+ textRenderUtil.drawString3D(gl, renderer, font, text, null, vbaaSampleCount);
+ } else if( null != region ) {
+ TextRegionUtil.drawString3D(gl, region, renderer, font, text, null, vbaaSampleCount, tempT1, tempT1);
+ } else {
+ TextRegionUtil.drawString3D(gl, renderModes, renderer, font, text, null, vbaaSampleCount, tempT1, tempT1);
+ }
+ renderer.enable(gl, false);
+
+ if( !exclusivePMVMatrix ) {
+ pmvMatrix.glPopMatrix();
+ }
+ lastRow = row + newLineCount;
+ }
+ }
+}
diff --git a/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeMono.ttf b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeMono.ttf
new file mode 100644
index 000000000..c4200565a
Binary files /dev/null and b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeMono.ttf differ
diff --git a/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeMonoBold.ttf b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeMonoBold.ttf
new file mode 100644
index 000000000..0bee057ec
Binary files /dev/null and b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeMonoBold.ttf differ
diff --git a/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeMonoBoldOblique.ttf b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeMonoBoldOblique.ttf
new file mode 100644
index 000000000..91bbc0e8a
Binary files /dev/null and b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeMonoBoldOblique.ttf differ
diff --git a/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeMonoOblique.ttf b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeMonoOblique.ttf
new file mode 100644
index 000000000..3252bdda6
Binary files /dev/null and b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeMonoOblique.ttf differ
diff --git a/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSans.ttf b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSans.ttf
new file mode 100644
index 000000000..e56dc6e90
Binary files /dev/null and b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSans.ttf differ
diff --git a/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSansBold.ttf b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSansBold.ttf
new file mode 100644
index 000000000..66e19ecb0
Binary files /dev/null and b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSansBold.ttf differ
diff --git a/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSansBoldOblique.ttf b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSansBoldOblique.ttf
new file mode 100644
index 000000000..de8a9e153
Binary files /dev/null and b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSansBoldOblique.ttf differ
diff --git a/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSansOblique.ttf b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSansOblique.ttf
new file mode 100644
index 000000000..b0357eabb
Binary files /dev/null and b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSansOblique.ttf differ
diff --git a/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSerif.ttf b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSerif.ttf
new file mode 100644
index 000000000..dffa1aedb
Binary files /dev/null and b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSerif.ttf differ
diff --git a/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSerifBold.ttf b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSerifBold.ttf
new file mode 100644
index 000000000..e2393ad22
Binary files /dev/null and b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSerifBold.ttf differ
diff --git a/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSerifBoldItalic.ttf b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSerifBoldItalic.ttf
new file mode 100644
index 000000000..46bc4695f
Binary files /dev/null and b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSerifBoldItalic.ttf differ
diff --git a/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSerifItalic.ttf b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSerifItalic.ttf
new file mode 100644
index 000000000..d173e3566
Binary files /dev/null and b/src/demos/com/jogamp/opengl/demos/graph/fonts/freefont/FreeSerifItalic.ttf differ
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/GPUUISceneGLListener0A.java b/src/demos/com/jogamp/opengl/demos/graph/ui/GPUUISceneGLListener0A.java
new file mode 100644
index 000000000..990143de8
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/GPUUISceneGLListener0A.java
@@ -0,0 +1,973 @@
+/**
+ * Copyright 2010-2023 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.demos.graph.ui;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+import com.jogamp.opengl.GL;
+import com.jogamp.opengl.GL2ES2;
+import com.jogamp.opengl.GLAnimatorControl;
+import com.jogamp.opengl.GLAutoDrawable;
+import com.jogamp.opengl.GLEventListener;
+import com.jogamp.opengl.GLPipelineFactory;
+import com.jogamp.opengl.GLRunnable;
+import com.jogamp.opengl.JoglVersion;
+import com.jogamp.opengl.demos.es2.GearsES2;
+import com.jogamp.opengl.demos.graph.FontSetDemos;
+import com.jogamp.opengl.demos.graph.MSAATool;
+import com.jogamp.common.net.Uri;
+import com.jogamp.common.util.IOUtil;
+import com.jogamp.common.util.InterruptSource;
+import com.jogamp.graph.curve.Region;
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.curve.opengl.RenderState;
+import com.jogamp.graph.font.Font;
+import com.jogamp.graph.font.FontFactory;
+import com.jogamp.graph.font.FontScale;
+import com.jogamp.graph.geom.SVertex;
+import com.jogamp.graph.ui.gl.Scene;
+import com.jogamp.graph.ui.gl.Shape;
+import com.jogamp.graph.ui.gl.shapes.Button;
+import com.jogamp.graph.ui.gl.shapes.GLButton;
+import com.jogamp.graph.ui.gl.shapes.ImageButton;
+import com.jogamp.graph.ui.gl.shapes.Label;
+import com.jogamp.graph.ui.gl.shapes.MediaButton;
+import com.jogamp.graph.ui.gl.shapes.RoundButton;
+import com.jogamp.newt.MonitorDevice;
+import com.jogamp.newt.Window;
+import com.jogamp.newt.event.InputEvent;
+import com.jogamp.newt.event.MouseEvent;
+import com.jogamp.newt.event.PinchToZoomGesture;
+import com.jogamp.newt.event.GestureHandler.GestureEvent;
+import com.jogamp.newt.event.MouseEvent.PointerClass;
+import com.jogamp.newt.opengl.GLWindow;
+import com.jogamp.opengl.math.FloatUtil;
+import com.jogamp.opengl.math.VectorUtil;
+import com.jogamp.opengl.util.GLReadBufferUtil;
+import com.jogamp.opengl.util.av.GLMediaPlayer;
+import com.jogamp.opengl.util.av.GLMediaPlayerFactory;
+import com.jogamp.opengl.util.texture.ImageSequence;
+import com.jogamp.opengl.util.texture.TextureIO;
+
+public class GPUUISceneGLListener0A implements GLEventListener {
+ static private final String defaultMediaURL = "http://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4";
+
+ private boolean debug = false;
+ private boolean trace = false;
+
+ private final float noAADPIThreshold;
+ private final Scene sceneUICntrl;
+
+ /** -1 == AUTO, TBD @ init(..) */
+ private int renderModes;
+
+ private final Font font;
+ private final Font fontFPS;
+ private final Uri filmURL;
+
+ private final float sceneDist = 3000f;
+ private final float zNear = 0.1f, zFar = 7000f;
+
+ private final float relTop = 80f/100f;
+ private final float relMiddle = 22f/100f;
+ private final float relLeft = 11f/100f;
+
+ /** Proportional Button Size to Window Height, per-vertical-pixels [PVP] */
+ private final float buttonYSizePVP = 0.084f;
+ private final float buttonXSizePVP = 0.084f; // 0.105f;
+ private final float fontSizePt = 10f;
+ /** Proportional Font Size to Window Height for Main Text, per-vertical-pixels [PVP] */
+ private final float fontSizeFixedPVP = 0.04f;
+ /** Proportional Font Size to Window Height for FPS Status Line, per-vertical-pixels [PVP] */
+ private final float fontSizeFpsPVP = 0.03f;
+ private float dpiV = 96;
+
+ /**
+ * Default DPI threshold value to disable {@link Region#VBAA_RENDERING_BIT VBAA}: {@value} dpi
+ * @see #GPUUISceneGLListener0A(float)
+ * @see #GPUUISceneGLListener0A(float, boolean, boolean)
+ */
+ public static final float DefaultNoAADPIThreshold = 200f;
+
+ private int currentText = 0;
+
+ private String actionText = null;
+ private Label[] labels = null;
+ private String[] strings = null;
+ private final List buttons = new ArrayList();
+ private int buttonsLeftCount = 0;
+ private Label truePtSizeLabel = null;
+ private Label jogampLabel = null;
+ private Label fpsLabel = null;
+
+ private GLAutoDrawable cDrawable;
+
+ private final GLReadBufferUtil screenshot;
+
+ private final String jogamp = "JogAmp - Jogl Graph Module Demo";
+ private final String truePtSize = fontSizePt+" pt font size label - true scale!";
+
+ private final String longText = "JOGL: Java™ Binding for the OpenGL® API.\n\n"+
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec \n"+
+ "Ut purus odio, rhoncus sit amet commodo eget, ullamcorper vel\n"+
+ "quam iaculis urna cursus ornare. Nullam ut felis a ante ultrices\n"+
+ "In hac habitasse platea dictumst. Vivamus et mi a quam lacinia\n"+
+ "Morbi quis bibendum nibh. Donec lectus orci, sagittis in consequat\n"+
+ "Donec ut dolor et nulla tristique varius. In nulla magna, fermentum\n"+
+ "in lorem. Maecenas in ipsum ac justo scelerisque sollicitudin.\n"+
+ "\n"+
+ "Lyford’s in Texas & L’Anse-aux-Griffons in Québec;\n"+
+ "Kwikpak on the Yukon delta, Kvæven in Norway, Kyulu in Kenya, not Rwanda.…\n"+
+ "Ytterbium in the periodic table. Are Toussaint L’Ouverture, Wölfflin, Wolfe,\n"+
+ "\n"+
+ "The quick brown fox jumps over the lazy dog\n";
+
+ /**
+ * @param renderModes
+ */
+ public GPUUISceneGLListener0A(final int renderModes) {
+ this(null, null, renderModes, false, false);
+ }
+
+ /**
+ * @param filmURL TODO
+ * @param renderModes
+ * @param debug
+ * @param trace
+ */
+ public GPUUISceneGLListener0A(final String fontfilename, final String filmURL, final int renderModes, final boolean debug, final boolean trace) {
+ this(fontfilename, filmURL, 0f, renderModes, debug, trace);
+ }
+
+ /**
+ * @param filmURL TODO
+ * @param noAADPIThreshold see {@link #DefaultNoAADPIThreshold}
+ * @param debug
+ * @param trace
+ */
+ public GPUUISceneGLListener0A(final String fontfilename, final String filmURL, final float noAADPIThreshold, final boolean debug, final boolean trace) {
+ this(fontfilename, filmURL, noAADPIThreshold, 0, debug, trace);
+ }
+
+ private GPUUISceneGLListener0A(final String fontfilename, final String filmURL, final float noAADPIThreshold, final int renderModes, final boolean debug, final boolean trace) {
+ this.noAADPIThreshold = noAADPIThreshold;
+ this.debug = debug;
+ this.trace = trace;
+
+ this.renderModes = renderModes;
+
+ try {
+ if( null == fontfilename ) {
+ font = FontFactory.get(IOUtil.getResource("fonts/freefont/FreeSerif.ttf",
+ FontSetDemos.class.getClassLoader(), FontSetDemos.class).getInputStream(), true);
+ } else {
+ font = FontFactory.get( new File( fontfilename ) );
+ }
+ System.err.println("Font "+font.getFullFamilyName());
+
+ fontFPS = FontFactory.get(IOUtil.getResource("fonts/freefont/FreeMonoBold.ttf",
+ FontSetDemos.class.getClassLoader(), FontSetDemos.class).getInputStream(), true);
+ System.err.println("Font FPS "+fontFPS.getFullFamilyName());
+
+ } catch (final IOException ioe) {
+ throw new RuntimeException(ioe);
+ }
+ try {
+ this.filmURL = Uri.cast( null != filmURL ? filmURL : defaultMediaURL );
+ } catch (final URISyntaxException e1) {
+ throw new RuntimeException(e1);
+ }
+ {
+ final RenderState rs = RenderState.createRenderState(SVertex.factory());
+ final RegionRenderer renderer = RegionRenderer.create(rs, RegionRenderer.defaultBlendEnable, RegionRenderer.defaultBlendDisable);
+ rs.setHintMask(RenderState.BITHINT_GLOBAL_DEPTH_TEST_ENABLED);
+ // renderer = RegionRenderer.create(rs, null, null);
+
+ sceneUICntrl = new Scene(renderer, sceneDist, zNear, zFar);
+ // sceneUIController.setSampleCount(3); // easy on embedded devices w/ just 3 samples (default is 4)?
+ }
+ screenshot = new GLReadBufferUtil(false, false);
+ }
+
+ private void rotateButtons(float[] angdeg) {
+ angdeg = VectorUtil.scaleVec3(angdeg, angdeg, FloatUtil.PI / 180.0f);
+ for(int i=0; i "+buttonXSize+" x "+buttonYSize);
+ final float xStartLeft = 0f; // aligned to left edge w/ space via reshape
+ final float yStartTop = 0f; // aligned to top edge w/ space via reshape
+ final float diffX = 1.2f * buttonXSize;
+ final float diffY = 1.5f * buttonYSize;
+
+ Button button = new Button(SVertex.factory(), renderModes, font, "Next Text", buttonXSize, buttonYSize);
+ button.setName(BUTTON_NEXTTEXT);
+ button.move(xStartLeft,yStartTop-diffY*buttons.size(), 0f);
+ button.addMouseListener(new Shape.MouseGestureAdapter() {
+ @Override
+ public void mouseClicked(final MouseEvent e) {
+ if( null != labels[currentText] ) {
+ labels[currentText].setEnabled(false);
+ }
+ currentText = (currentText+1)%labels.length;
+ if( null != labels[currentText] ) {
+ labels[currentText].setEnabled(true);
+ }
+ } } );
+ button.addMouseListener(dragZoomRotateListener);
+ buttons.add(button);
+
+ button = new Button(SVertex.factory(), renderModes, font, "Show FPS", buttonXSize, buttonYSize);
+ button.setName(BUTTON_FPS);
+ button.move(xStartLeft,yStartTop - diffY*buttons.size(), 0f);
+ button.setToggleable(true);
+ button.setToggle(fpsLabel.isEnabled());
+ button.addMouseListener(new Shape.MouseGestureAdapter() {
+ @Override
+ public void mouseClicked(final MouseEvent e) {
+ final GLAnimatorControl a = cDrawable.getAnimator();
+ if( null != a ) {
+ a.resetFPSCounter();
+ }
+ fpsLabel.setEnabled(!fpsLabel.isEnabled());
+ } } );
+ button.addMouseListener(dragZoomRotateListener);
+ buttons.add(button);
+
+ button = new Button(SVertex.factory(), renderModes, font, "V-Sync", buttonXSize, buttonYSize);
+ button.setName(BUTTON_VSYNC);
+ button.move(xStartLeft,yStartTop - diffY*buttons.size(), 0f);
+ button.setToggleable(true);
+ button.setToggle(gl.getSwapInterval()>0);
+ button.addMouseListener(new Shape.MouseGestureAdapter() {
+ @Override
+ public void mouseClicked(final MouseEvent e) {
+ cDrawable.invoke(false, new GLRunnable() {
+ @Override
+ public boolean run(final GLAutoDrawable drawable) {
+ final GL gl = drawable.getGL();
+ gl.setSwapInterval(gl.getSwapInterval()<=0?1:0);
+ final GLAnimatorControl a = drawable.getAnimator();
+ if( null != a ) {
+ a.resetFPSCounter();
+ }
+ return true;
+ }
+ });
+ } } );
+ button.addMouseListener(dragZoomRotateListener);
+ buttons.add(button);
+
+ button = new Button(SVertex.factory(), renderModes, font, "< Tilt >", buttonXSize, buttonYSize);
+ button.move(xStartLeft,yStartTop - diffY*buttons.size(), 0f);
+ button.addMouseListener(new Shape.MouseGestureAdapter() {
+ @Override
+ public void mouseClicked(final MouseEvent e) {
+ final Shape.EventInfo shapeEvent = (Shape.EventInfo) e.getAttachment();
+ if( shapeEvent.objPos[0] < shapeEvent.shape.getBounds().getCenter()[0] ) {
+ rotateButtons(new float[] { 0f, -5f, 0f}); // left-half pressed
+ } else {
+ rotateButtons(new float[] { 0f, 5f, 0f}); // right-half pressed
+ }
+ }
+ @Override
+ public void mouseWheelMoved(final MouseEvent e) {
+ rotateButtons(new float[] { 0f, e.getRotation()[1], 0f});
+ } } );
+ buttons.add(button);
+
+ if( pass2Mode ) { // second column to the left
+ button = new Button(SVertex.factory(), renderModes, font, "< Samples >", buttonXSize, buttonYSize);
+ button.move(xStartLeft,yStartTop - diffY*buttons.size(), 0f);
+ button.addMouseListener(new Shape.MouseGestureAdapter() {
+ @Override
+ public void mouseClicked(final MouseEvent e) {
+ final Shape.EventInfo shapeEvent = (Shape.EventInfo) e.getAttachment();
+ int sampleCount = sceneUICntrl.getSampleCount();
+ if( shapeEvent.objPos[0] < shapeEvent.shape.getBounds().getCenter()[0] ) {
+ // left-half pressed
+ sampleCount--;
+ } else {
+ // right-half pressed
+ sampleCount++;
+ }
+ sampleCount = sceneUICntrl.setSampleCount(sampleCount); // validated / clipped
+ } } );
+ button.addMouseListener(dragZoomRotateListener);
+ buttons.add(button);
+
+ button = new Button(SVertex.factory(), renderModes, font, "< Quality >", buttonXSize, buttonYSize);
+ button.move(xStartLeft,yStartTop - diffY*buttons.size(), 0f);
+ button.addMouseListener(new Shape.MouseGestureAdapter() {
+ @Override
+ public void mouseClicked(final MouseEvent e) {
+ final Shape.EventInfo shapeEvent = (Shape.EventInfo) e.getAttachment();
+ int quality = shapeEvent.shape.getQuality();
+
+ if( shapeEvent.objPos[0] < shapeEvent.shape.getBounds().getCenter()[0] ) {
+ // left-half pressed
+ if( quality > 0 ) {
+ quality--;
+ }
+ } else {
+ // right-half pressed
+ if( quality < Region.MAX_QUALITY ) {
+ quality++;
+ }
+ }
+ sceneUICntrl.setAllShapesQuality(quality);
+ } } );
+ button.addMouseListener(dragZoomRotateListener);
+ buttons.add(button);
+ }
+
+ button = new Button(SVertex.factory(), renderModes, font, "Quit", buttonXSize, buttonYSize);
+ button.setName(BUTTON_QUIT);
+ button.move(xStartLeft,yStartTop - diffY*buttons.size(), 0f);
+ button.setColor(0.7f, 0.0f, 0.0f, 1.0f);
+ button.setLabelColor(1.2f, 1.2f, 1.2f);
+ button.setPressedColorMod(1.1f, 0.0f, 0.0f, 1.0f);
+ button.addMouseListener(new Shape.MouseGestureAdapter() {
+ @Override
+ public void mouseClicked(final MouseEvent e) {
+ new InterruptSource.Thread() {
+ @Override
+ public void run() {
+ if( null != cDrawable ) {
+ final GLAnimatorControl actrl = cDrawable.getAnimator();
+ if( null != actrl ) {
+ actrl.stop();
+ }
+ cDrawable.destroy();
+ }
+ } }.start();
+ } } );
+ button.addMouseListener(dragZoomRotateListener);
+ buttons.add(button);
+
+ // second column to the left
+ {
+ final int j = 1; // column
+ int k = 0; // row
+ button = new Button(SVertex.factory(), renderModes, font, "Y Flip", buttonXSize, buttonYSize);
+ button.move(xStartLeft - diffX*j,yStartTop - diffY*k, 0f);
+ button.addMouseListener(new Shape.MouseGestureAdapter() {
+ @Override
+ public void mouseClicked(final MouseEvent e) {
+ rotateButtons(new float[] { 0f, 180f, 0f});
+ } } );
+ button.addMouseListener(dragZoomRotateListener);
+ buttons.add(button);
+
+ k++;
+ button = new Button(SVertex.factory(), renderModes, font, "X Flip", buttonXSize, buttonYSize);
+ button.move(xStartLeft - diffX*j,yStartTop - diffY*k, 0f);
+ button.addMouseListener(new Shape.MouseGestureAdapter() {
+ @Override
+ public void mouseClicked(final MouseEvent e) {
+ rotateButtons(new float[] { 180f, 0f, 0f});
+ } } );
+ button.addMouseListener(dragZoomRotateListener);
+ buttons.add(button);
+ k++;
+
+ button = new Button(SVertex.factory(), renderModes, font, "+", buttonXSize, buttonYSize);
+ button.move(xStartLeft - diffX*j,yStartTop - diffY*k, 0f);
+ button.addMouseListener(new Shape.MouseGestureAdapter() {
+ @Override
+ public void mouseClicked(final MouseEvent e) {
+ final Shape.EventInfo shapeEvent = (Shape.EventInfo) e.getAttachment();
+ // rel position to center
+ final float dx = shapeEvent.objPos[0] - shapeEvent.shape.getBounds().getCenter()[0] ;
+ final float dy = shapeEvent.objPos[1] - shapeEvent.shape.getBounds().getCenter()[1] ;
+ // per-cent position to center (remove dependency on dimension)
+ final float awdx = Math.abs(dx)/shapeEvent.shape.getBounds().getWidth();
+ final float awdy = Math.abs(dy)/shapeEvent.shape.getBounds().getHeight();
+ float tx = 0, ty = 0;
+ if ( awdx > awdy ) {
+ tx = dx < 0 ? -5 : 5;
+ } else {
+ ty = dy < 0 ? -5 : 5;
+ }
+ translateButtons(tx, ty, 0f);
+ } } );
+ button.addMouseListener(dragZoomRotateListener);
+ buttons.add(button);
+ k++;
+
+ button = new Button(SVertex.factory(), renderModes, font, "< Space >", buttonXSize, buttonYSize);
+ button.move(xStartLeft - diffX*j,yStartTop - diffY*k, 0f);
+ button.addMouseListener(new Shape.MouseGestureAdapter() {
+ @Override
+ public void mouseClicked(final MouseEvent e) {
+ final Shape.EventInfo shapeEvent = (Shape.EventInfo) e.getAttachment();
+ final float dx, dy;
+ if( shapeEvent.objPos[0] < shapeEvent.shape.getBounds().getCenter()[0] ) {
+ dx=-0.01f; dy=-0.005f;
+ } else {
+ dx=0.01f; dy=0.005f;
+ }
+ setButtonsSpacing(dx, dy);
+ }
+ @Override
+ public void mouseWheelMoved(final MouseEvent e) {
+ setButtonsSpacing(e.getRotation()[0]/100f, e.getRotation()[1]/200f);
+ } } );
+ buttons.add(button);
+ k++;
+
+ button = new Button(SVertex.factory(), renderModes, font, "< Corner >", buttonXSize, buttonYSize);
+ button.move(xStartLeft - diffX*j,yStartTop - diffY*k, 0f);
+ button.addMouseListener(new Shape.MouseGestureAdapter() {
+ @Override
+ public void mouseClicked(final MouseEvent e) {
+ final Shape.EventInfo shapeEvent = (Shape.EventInfo) e.getAttachment();
+ final float dc;
+ if( shapeEvent.objPos[0] < shapeEvent.shape.getBounds().getCenter()[0] ) {
+ dc=-0.1f;
+ } else {
+ dc=0.1f;
+ }
+ setButtonsCorner(dc);
+ }
+ @Override
+ public void mouseWheelMoved(final MouseEvent e) {
+ setButtonsCorner(e.getRotation()[1]/20f);
+ } } );
+ buttons.add(button);
+ k++;
+
+ button = new Button(SVertex.factory(), renderModes, font, "Reset", buttonXSize, buttonYSize);
+ button.move(xStartLeft - diffX*j,yStartTop - diffY*k, 0f);
+ button.addMouseListener(new Shape.MouseGestureAdapter() {
+ @Override
+ public void mouseClicked(final MouseEvent e) {
+ resetButtons();
+ } } );
+ button.addMouseListener(dragZoomRotateListener);
+ buttons.add(button);
+ k++;
+
+ button = new Button(SVertex.factory(), renderModes, font, "Snapshot", buttonXSize, buttonYSize);
+ button.move(xStartLeft - diffX*j,yStartTop - diffY*k, 0f);
+ button.addMouseListener(new Shape.MouseGestureAdapter() {
+ @Override
+ public void mouseClicked(final MouseEvent e) {
+ cDrawable.invoke(false, new GLRunnable() {
+ @Override
+ public boolean run(final GLAutoDrawable drawable) {
+ printScreen(drawable.getGL());
+ return true;
+ }
+ });
+ } } );
+ button.addMouseListener(dragZoomRotateListener);
+ buttons.add(button);
+ k++;
+ }
+
+ buttonsLeftCount = buttons.size();
+
+ final float button2XSize = 2f*buttonXSize;
+ final float button2YSize = 2f*buttonYSize;
+ final float xStartRight = -button2XSize - 8f; // aligned to right edge via reshape
+ final int texUnitMediaPlayer, texUnitImageButton, texUnitGLELButton;
+ {
+ // works - but not required ..
+ texUnitMediaPlayer=1;
+ texUnitImageButton=2;
+ texUnitGLELButton=3;
+ }
+
+ if( true ) {
+ final GLMediaPlayer mPlayer = GLMediaPlayerFactory.createDefault();
+ mPlayer.setTextureUnit(texUnitMediaPlayer);
+ final MediaButton mPlayerButton = new MediaButton(sceneUICntrl.getVertexFactory(), renderModes,
+ button2XSize, button2YSize, mPlayer);
+ mPlayerButton.setName(BUTTON_MOVIE);
+ mPlayerButton.setVerbose(true);
+ mPlayerButton.addDefaultEventListener();
+ mPlayerButton.move(xStartRight, yStartTop - diffY*1, 0f);
+ mPlayerButton.setToggleable(true);
+ mPlayerButton.setToggle(false); // toggle == false -> mute audio
+ mPlayerButton.setToggleOffColorMod(0f, 1f, 0f, 1.0f);
+ mPlayerButton.addMouseListener(dragZoomRotateListener);
+ mPlayerButton.addMouseListener(new Shape.MouseGestureAdapter() {
+ @Override
+ public void mouseClicked(final MouseEvent e) {
+ mPlayer.setAudioVolume( mPlayerButton.isToggleOn() ? 1f : 0f );
+ } } );
+ buttons.add(mPlayerButton);
+ mPlayer.initStream(filmURL, GLMediaPlayer.STREAM_ID_AUTO, GLMediaPlayer.STREAM_ID_AUTO, GLMediaPlayer.TEXTURE_COUNT_DEFAULT);
+ }
+ if( true ) {
+ final ImageSequence imgSeq = new ImageSequence(texUnitImageButton, true);
+ final ImageButton imgButton = new ImageButton(sceneUICntrl.getVertexFactory(), renderModes,
+ button2XSize, button2YSize, imgSeq);
+ try {
+ imgSeq.addFrame(gl, GPUUISceneGLListener0A.class, "button-released-145x53.png", TextureIO.PNG);
+ imgSeq.addFrame(gl, GPUUISceneGLListener0A.class, "button-pressed-145x53.png", TextureIO.PNG);
+ } catch (final IOException e2) {
+ e2.printStackTrace();
+ }
+ imgSeq.setManualStepping(true);
+ imgButton.move(xStartRight, yStartTop - diffY*2.5f, 0f);
+ imgButton.addMouseListener(dragZoomRotateListener);
+ imgButton.addMouseListener(new Shape.MouseGestureAdapter() {
+ @Override
+ public void mousePressed(final MouseEvent e) {
+ imgButton.setCurrentIdx(1);
+ System.err.println("XXX: "+imgButton);
+ }
+ @Override
+ public void mouseReleased(final MouseEvent e) {
+ imgButton.setCurrentIdx(0);
+ } } );
+ buttons.add(imgButton);
+ }
+ if( true ) {
+ // Issues w/ OSX and NewtCanvasAWT when rendering / animating
+ // Probably related to CALayer - FBO - FBO* (of this button)
+ final GLEventListener glel;
+ {
+ final GearsES2 gears = new GearsES2(0);
+ gears.setVerbose(false);
+ gears.setClearColor(new float[] { 0.9f, 0.9f, 0.9f, 1f } );
+ glel = gears;
+ }
+ final GLButton glelButton = new GLButton(sceneUICntrl.getVertexFactory(), renderModes,
+ button2XSize, button2YSize,
+ texUnitGLELButton, glel, false /* useAlpha */,
+ (int)(button2XSize), (int)(button2YSize));
+ glelButton.setName(BUTTON_GLEL);
+ glelButton.setToggleable(true);
+ glelButton.setToggle(false); // toggle == true -> animation
+ glelButton.setAnimate(false);
+ glelButton.move(xStartRight, yStartTop - diffY*4f, 0f);
+ glelButton.addMouseListener(dragZoomRotateListener);
+ glelButton.addMouseListener(new Shape.MouseGestureAdapter() {
+ @Override
+ public void mouseClicked(final MouseEvent e) {
+ glelButton.setAnimate( glelButton.isToggleOn() );
+ } } );
+ buttons.add(glelButton);
+ }
+ }
+
+ private void initTexts() {
+ strings = new String[4];
+ int i = 0;
+
+ strings[i++] = "- Mouse Scroll Over Object\n"+
+ " - General\n"+
+ " - Z Translation\n"+
+ " - Ctrl: Y-Rotation (Shift: X-Rotation)\n"+
+ " - Tilt, Space and Corner\n"+
+ " - Their respective action via wheel\n"+
+ " (shift = other value)\n"+
+ "\n"+
+ "- Mouse Drag On Object\n"+
+ " - Click on Object and drag mouse\n"+
+ " - Current postion in status line at bottom\n"+
+ "\n"+
+ "- Tilt Button Rotate Whole Button Group";
+
+ strings[i++] = "abcdefghijklmn\nopqrstuvwxyz\n"+
+ "ABCDEFGHIJKL\n"+
+ "MNOPQRSTUVWXYZ\n"+
+ "0123456789.:,;(*!?/\\\")$%^&-+@~#<>{}[]";
+
+ strings[i++] = "The quick brown fox jumps over the lazy dog";
+
+ strings[i++] = longText;
+
+ labels = new Label[i];
+
+ currentText = strings.length - 1;
+ }
+
+
+ private static final boolean enableOthers = true;
+ private static final boolean reshape_ui = false; // incomplete: button positioning
+
+
+ private void setupUI(final GLAutoDrawable drawable) {
+ final float pixelSizeFixed = fontSizeFixedPVP * drawable.getSurfaceHeight();
+ jogampLabel = new Label(sceneUICntrl.getVertexFactory(), renderModes, font, pixelSizeFixed, jogamp);
+ jogampLabel.addMouseListener(dragZoomRotateListener);
+ sceneUICntrl.addShape(jogampLabel);
+ jogampLabel.setEnabled(enableOthers);
+
+ final float pixelSize10Pt = FontScale.toPixels(fontSizePt, dpiV);
+ System.err.println("10Pt PixelSize: Display "+dpiV+" dpi, fontSize "+fontSizePt+" ppi -> "+pixelSize10Pt+" pixel-size");
+ truePtSizeLabel = new Label(sceneUICntrl.getVertexFactory(), renderModes, font, pixelSize10Pt, truePtSize);
+ sceneUICntrl.addShape(truePtSizeLabel);
+ truePtSizeLabel.setEnabled(enableOthers);
+ truePtSizeLabel.move(0, - 1.5f * jogampLabel.getLineHeight(), 0f);
+ truePtSizeLabel.setColor(0.1f, 0.1f, 0.1f, 1.0f);
+
+ /**
+ *
+ * [Label] Display 112.88889 dpi, fontSize 12.0 ppi -> pixelSize 18.814816
+ * [FPS] Display 112.88889 dpi, fontSize 12.0 ppi -> pixelSize 15.679012
+ */
+ final float pixelSizeFPS = fontSizeFpsPVP * drawable.getSurfaceHeight();
+ fpsLabel = new Label(sceneUICntrl.getVertexFactory(), renderModes, fontFPS, pixelSizeFPS, "Nothing there yet");
+ fpsLabel.addMouseListener(dragZoomRotateListener);
+ sceneUICntrl.addShape(fpsLabel);
+ fpsLabel.setEnabled(enableOthers);
+ fpsLabel.setColor(0.1f, 0.1f, 0.1f, 1.0f);
+ fpsLabel.move(0f, pixelSizeFPS * (fontFPS.getMetrics().getLineGap() - fontFPS.getMetrics().getDescent()), 0f);
+
+ initButtons(drawable.getGL().getGL2ES2(), drawable.getSurfaceWidth(), drawable.getSurfaceHeight());
+ for(int i=0; i "+pixelSize10Pt+" pixel-size");
+ truePtSizeLabel.setPixelSize(pixelSize10Pt);
+
+ /**
+ *
+ * [Label] Display 112.88889 dpi, fontSize 12.0 ppi -> pixelSize 18.814816
+ * [FPS] Display 112.88889 dpi, fontSize 12.0 ppi -> pixelSize 15.679012
+ */
+ final float pixelSizeFPS = fontSizeFpsPVP * drawable.getSurfaceHeight();
+ fpsLabel.setPixelSize(pixelSizeFPS);
+
+ final float buttonXSize = buttonXSizePVP * drawable.getSurfaceWidth();
+ // final float buttonYSize = buttonYSizePVP * height;
+ final float buttonYSize = buttonXSize / 2.5f;
+ final float button2XSize = 2f*buttonXSize;
+ final float button2YSize = 2f*buttonYSize;
+
+ for(int i=0; i= noAADPIThreshold;
+ final String noAAs = noAA ? " >= " : " < ";
+ System.err.println("AUTO RenderMode: dpi "+dpiV+noAAs+noAADPIThreshold+" -> noAA "+noAA);
+ renderModes = noAA ? 0 : Region.VBAA_RENDERING_BIT;
+ }
+ if(drawable instanceof GLWindow) {
+ System.err.println("GPUUISceneGLListener0A: init (1)");
+ final GLWindow glw = (GLWindow) drawable;
+ sceneUICntrl.attachInputListenerTo(glw);
+ } else {
+ System.err.println("GPUUISceneGLListener0A: init (0)");
+ }
+ cDrawable = drawable;
+ GL2ES2 gl = drawable.getGL().getGL2ES2();
+ if(debug) {
+ gl = gl.getContext().setGL( GLPipelineFactory.create("com.jogamp.opengl.Debug", null, gl, null) ).getGL2ES2();
+ }
+ if(trace) {
+ gl = gl.getContext().setGL( GLPipelineFactory.create("com.jogamp.opengl.Trace", null, gl, new Object[] { System.err } ) ).getGL2ES2();
+ }
+ System.err.println(JoglVersion.getGLInfo(gl, null, false /* withCapsAndExts */).toString());
+ System.err.println("VSync Swap Interval: "+gl.getSwapInterval());
+ System.err.println("Chosen: "+drawable.getChosenGLCapabilities());
+ MSAATool.dump(drawable);
+
+ gl.setSwapInterval(1);
+ gl.glEnable(GL.GL_DEPTH_TEST);
+ gl.glEnable(GL.GL_BLEND);
+
+ initTexts();
+
+ sceneUICntrl.init(drawable);
+
+ final GLAnimatorControl a = drawable.getAnimator();
+ if( null != a ) {
+ a.resetFPSCounter();
+ }
+
+ setupUI(drawable);
+ }
+
+ @Override
+ public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) {
+ System.err.println("GPUUISceneGLListener0A: reshape");
+
+ //
+ // Layout all shapes: Relational move regarding window coordinates
+ //
+ final float dw = width - lastWidth;
+ final float dh = height - lastHeight;
+
+ final float dz = 0f;
+ final float dyTop = dh * relTop;
+ final float dxLeft = dw * relLeft;
+ final float dxRight = dw;
+
+ if( reshape_ui ) {
+ reshapeUI(drawable);
+ }
+ for(int i=0; i 0 ) {
+ caps.setSampleBuffers(true);
+ caps.setNumSamples(SceneMSAASamples);
+ }
+ System.out.println("Requested: " + caps);
+
+ final int rmode;
+ if( GraphVBAAMode ) {
+ rmode = Region.VBAA_RENDERING_BIT;
+ } else if( GraphMSAAMode ) {
+ rmode = Region.MSAA_RENDERING_BIT;
+ } else {
+ rmode = 0;
+ }
+
+ final GLWindow window = GLWindow.create(screen, caps);
+ if( 0 == SceneMSAASamples ) {
+ window.setCapabilitiesChooser(new NonFSAAGLCapsChooser(true));
+ }
+ window.setSize(width, height);
+ window.setTitle("GraphUI Newt Demo: graph["+Region.getRenderModeString(rmode)+"], msaa "+SceneMSAASamples);
+ window.setSurfaceScale(reqSurfacePixelScale);
+ // final float[] valReqSurfacePixelScale = window.getRequestedSurfaceScale(new float[2]);
+
+ final GPUUISceneGLListener0A scene = 0 < GraphAutoMode ? new GPUUISceneGLListener0A(fontfilename, filmURL, GraphAutoMode, DEBUG, TRACE) :
+ new GPUUISceneGLListener0A(fontfilename, filmURL, rmode, DEBUG, TRACE);
+
+ window.addGLEventListener(scene);
+ scene.attachInputListenerTo(window);
+
+ final Animator animator = new Animator();
+ animator.setUpdateFPSFrames(5*60, null);
+ animator.add(window);
+
+ window.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowDestroyed(final WindowEvent e) {
+ animator.stop();
+ }
+ });
+
+ window.setVisible(true);
+ animator.start();
+
+ // sleep(3000);
+ // final UIShape movie = scene.getWidget(GPUUISceneGLListener0A.BUTTON_MOVIE);
+ }
+
+}
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/GPUUISceneTextAnim01.java b/src/demos/com/jogamp/opengl/demos/graph/ui/GPUUISceneTextAnim01.java
new file mode 100644
index 000000000..45bfd53f1
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/GPUUISceneTextAnim01.java
@@ -0,0 +1,351 @@
+/**
+ * Copyright 2010-2023 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.demos.graph.ui;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Locale;
+
+import com.jogamp.common.util.IOUtil;
+import com.jogamp.graph.curve.Region;
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.curve.opengl.RenderState;
+import com.jogamp.graph.font.Font;
+import com.jogamp.graph.font.FontFactory;
+import com.jogamp.graph.font.FontScale;
+import com.jogamp.graph.geom.SVertex;
+import com.jogamp.graph.ui.gl.Scene;
+import com.jogamp.graph.ui.gl.Shape;
+import com.jogamp.graph.ui.gl.shapes.Label;
+import com.jogamp.newt.MonitorDevice;
+import com.jogamp.newt.Window;
+import com.jogamp.newt.event.GestureHandler.GestureEvent;
+import com.jogamp.newt.event.InputEvent;
+import com.jogamp.newt.event.MouseEvent;
+import com.jogamp.newt.event.MouseEvent.PointerClass;
+import com.jogamp.newt.event.PinchToZoomGesture;
+import com.jogamp.newt.opengl.GLWindow;
+import com.jogamp.opengl.GL;
+import com.jogamp.opengl.GL2ES2;
+import com.jogamp.opengl.GLAnimatorControl;
+import com.jogamp.opengl.GLAutoDrawable;
+import com.jogamp.opengl.GLEventListener;
+import com.jogamp.opengl.GLPipelineFactory;
+import com.jogamp.opengl.JoglVersion;
+import com.jogamp.opengl.demos.graph.FontSetDemos;
+import com.jogamp.opengl.demos.graph.MSAATool;
+import com.jogamp.opengl.math.FloatUtil;
+import com.jogamp.opengl.math.VectorUtil;
+import com.jogamp.opengl.util.GLReadBufferUtil;
+
+public class GPUUISceneTextAnim01 implements GLEventListener {
+
+ private boolean debug = false;
+ private boolean trace = false;
+
+ private final float noAADPIThreshold;
+ private final Scene sceneUICntrl;
+
+ /** -1 == AUTO, TBD @ init(..) */
+ private int renderModes;
+
+ private final Font font;
+ private final Font fontFPS;
+
+ private final float sceneDist = 3000f;
+ private final float zNear = 0.1f, zFar = 7000f;
+
+ // private final float relTop = 80f/100f;
+ private final float relMiddle = 22f/100f;
+ // private final float relLeft = 11f/100f;
+
+ private final float fontSizePt = 10f;
+ /** Proportional Font Size to Window Height for Main Text, per-vertical-pixels [PVP] */
+ private final float fontSizeFixedPVP = 0.04f;
+ /** Proportional Font Size to Window Height for FPS Status Line, per-vertical-pixels [PVP] */
+ private final float fontSizeFpsPVP = 0.03f;
+ private float dpiV = 96;
+
+ /**
+ * Default DPI threshold value to disable {@link Region#VBAA_RENDERING_BIT VBAA}: {@value} dpi
+ * @see #GPUUISceneGLListener0A(float)
+ * @see #GPUUISceneGLListener0A(float, boolean, boolean)
+ */
+ public static final float DefaultNoAADPIThreshold = 200f;
+
+ private String actionText = null;
+ private Label jogampLabel = null;
+ private Label fpsLabel = null;
+
+ // private GLAutoDrawable cDrawable;
+
+ private final GLReadBufferUtil screenshot;
+
+ private final String jogamp = "JogAmp - Jogl Graph Module Demo";
+
+ // private final String longText = "JOGL: Java™ Binding for the OpenGL® API.";
+
+ public GPUUISceneTextAnim01(final String fontfilename, final float noAADPIThreshold, final int renderModes, final boolean debug, final boolean trace) {
+ this.noAADPIThreshold = noAADPIThreshold;
+ this.debug = debug;
+ this.trace = trace;
+
+ this.renderModes = renderModes;
+
+ try {
+ if( null == fontfilename ) {
+ font = FontFactory.get(IOUtil.getResource("fonts/freefont/FreeSerif.ttf",
+ FontSetDemos.class.getClassLoader(), FontSetDemos.class).getInputStream(), true);
+ } else {
+ font = FontFactory.get( new File( fontfilename ) );
+ }
+ System.err.println("Font "+font.getFullFamilyName());
+
+ fontFPS = FontFactory.get(IOUtil.getResource("fonts/freefont/FreeMonoBold.ttf",
+ FontSetDemos.class.getClassLoader(), FontSetDemos.class).getInputStream(), true);
+ System.err.println("Font FPS "+fontFPS.getFullFamilyName());
+
+ } catch (final IOException ioe) {
+ throw new RuntimeException(ioe);
+ }
+
+ {
+ final RenderState rs = RenderState.createRenderState(SVertex.factory());
+ final RegionRenderer renderer = RegionRenderer.create(rs, RegionRenderer.defaultBlendEnable, RegionRenderer.defaultBlendDisable);
+ rs.setHintMask(RenderState.BITHINT_GLOBAL_DEPTH_TEST_ENABLED);
+ // renderer = RegionRenderer.create(rs, null, null);
+
+ sceneUICntrl = new Scene(renderer, sceneDist, zNear, zFar);
+ // sceneUIController.setSampleCount(3); // easy on embedded devices w/ just 3 samples (default is 4)?
+ }
+ screenshot = new GLReadBufferUtil(false, false);
+ }
+
+ private void setupUI(final GLAutoDrawable drawable) {
+ final float pixelSizeFixed = fontSizeFixedPVP * drawable.getSurfaceHeight();
+ jogampLabel = new Label(sceneUICntrl.getVertexFactory(), renderModes, font, pixelSizeFixed, jogamp);
+ jogampLabel.addMouseListener(dragZoomRotateListener);
+ sceneUICntrl.addShape(jogampLabel);
+ jogampLabel.setEnabled(true);
+
+ final float pixelSize10Pt = FontScale.toPixels(fontSizePt, dpiV);
+ System.err.println("10Pt PixelSize: Display "+dpiV+" dpi, fontSize "+fontSizePt+" ppi -> "+pixelSize10Pt+" pixel-size");
+
+ /**
+ *
+ * [Label] Display 112.88889 dpi, fontSize 12.0 ppi -> pixelSize 18.814816
+ * [FPS] Display 112.88889 dpi, fontSize 12.0 ppi -> pixelSize 15.679012
+ */
+ final float pixelSizeFPS = fontSizeFpsPVP * drawable.getSurfaceHeight();
+ fpsLabel = new Label(sceneUICntrl.getVertexFactory(), renderModes, fontFPS, pixelSizeFPS, "Nothing there yet");
+ fpsLabel.addMouseListener(dragZoomRotateListener);
+ sceneUICntrl.addShape(fpsLabel);
+ fpsLabel.setEnabled(true);
+ fpsLabel.setColor(0.1f, 0.1f, 0.1f, 1.0f);
+ fpsLabel.move(0f, pixelSizeFPS * (fontFPS.getMetrics().getLineGap() - fontFPS.getMetrics().getDescent()), 0f);
+ }
+
+ @Override
+ public void init(final GLAutoDrawable drawable) {
+ final Object upObj = drawable.getUpstreamWidget();
+ if( upObj instanceof Window ) {
+ final Window upWin = (Window)upObj;
+ final MonitorDevice monitor = upWin.getMainMonitor();
+ final float[] monitorDPI = MonitorDevice.perMMToPerInch( monitor.getPixelsPerMM(new float[2]) );
+ final float[] sDPI = MonitorDevice.perMMToPerInch( upWin.getPixelsPerMM(new float[2]) );
+ dpiV = sDPI[1];
+ System.err.println("Monitor detected: "+monitor);
+ System.err.println("Monitor dpi: "+monitorDPI[0]+" x "+monitorDPI[1]);
+ System.err.println("Surface scale: native "+Arrays.toString(upWin.getMaximumSurfaceScale(new float[2]))+", current "+Arrays.toString(upWin.getCurrentSurfaceScale(new float[2])));
+ System.err.println("Surface dpi "+sDPI[0]+" x "+sDPI[1]);
+ } else {
+ System.err.println("Using default DPI of "+dpiV);
+ }
+ if( 0 == renderModes && !FloatUtil.isZero(noAADPIThreshold, FloatUtil.EPSILON)) {
+ final boolean noAA = dpiV >= noAADPIThreshold;
+ final String noAAs = noAA ? " >= " : " < ";
+ System.err.println("AUTO RenderMode: dpi "+dpiV+noAAs+noAADPIThreshold+" -> noAA "+noAA);
+ renderModes = noAA ? 0 : Region.VBAA_RENDERING_BIT;
+ }
+ if(drawable instanceof GLWindow) {
+ System.err.println("GPUUISceneGLListener0A: init (1)");
+ final GLWindow glw = (GLWindow) drawable;
+ sceneUICntrl.attachInputListenerTo(glw);
+ } else {
+ System.err.println("GPUUISceneGLListener0A: init (0)");
+ }
+ // cDrawable = drawable;
+ GL2ES2 gl = drawable.getGL().getGL2ES2();
+ if(debug) {
+ gl = gl.getContext().setGL( GLPipelineFactory.create("com.jogamp.opengl.Debug", null, gl, null) ).getGL2ES2();
+ }
+ if(trace) {
+ gl = gl.getContext().setGL( GLPipelineFactory.create("com.jogamp.opengl.Trace", null, gl, new Object[] { System.err } ) ).getGL2ES2();
+ }
+ System.err.println(JoglVersion.getGLInfo(gl, null, false /* withCapsAndExts */).toString());
+ System.err.println("VSync Swap Interval: "+gl.getSwapInterval());
+ System.err.println("Chosen: "+drawable.getChosenGLCapabilities());
+ MSAATool.dump(drawable);
+
+ gl.setSwapInterval(1);
+ gl.glEnable(GL.GL_DEPTH_TEST);
+ gl.glEnable(GL.GL_BLEND);
+
+ sceneUICntrl.init(drawable);
+
+ final GLAnimatorControl a = drawable.getAnimator();
+ if( null != a ) {
+ a.resetFPSCounter();
+ }
+
+ setupUI(drawable);
+ }
+
+ @Override
+ public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) {
+ System.err.println("GPUUISceneGLListener0A: reshape");
+
+ //
+ // Layout all shapes: Relational move regarding window coordinates
+ //
+ final float dz = 0f;
+
+ final float dxMiddleAbs = width * relMiddle;
+ final float dyTopLabelAbs = drawable.getSurfaceHeight() - 2f*jogampLabel.getLineHeight();
+ jogampLabel.setPosition(dxMiddleAbs, dyTopLabelAbs, dz);
+ fpsLabel.move(0f, 0f, 0f);
+
+ sceneUICntrl.reshape(drawable, x, y, width, height);
+
+ lastWidth = width;
+ lastHeight = height;
+ }
+ float lastWidth = 0f, lastHeight = 0f;
+
+ @Override
+ public void dispose(final GLAutoDrawable drawable) {
+ System.err.println("GPUUISceneGLListener0A: dispose");
+
+ sceneUICntrl.dispose(drawable); // disposes all registered UIShapes
+
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+ screenshot.dispose(gl);
+ }
+
+ private int shotCount = 0;
+
+ public void printScreen(final GL gl) {
+ final RegionRenderer renderer = sceneUICntrl.getRenderer();
+ final String modeS = Region.getRenderModeString(jogampLabel.getRenderModes());
+ final String filename = String.format((Locale)null, "GraphUIDemo-shot%03d-%03dx%03d-S_%s_%02d.png",
+ shotCount++, renderer.getWidth(), renderer.getHeight(),
+ modeS, sceneUICntrl.getSampleCount());
+ gl.glFinish(); // just make sure rendering finished ..
+ if(screenshot.readPixels(gl, false)) {
+ screenshot.write(new File(filename));
+ System.err.println("Wrote: "+filename);
+ }
+ }
+
+ @Override
+ public void display(final GLAutoDrawable drawable) {
+ if( fpsLabel.isEnabled() ) {
+ final String text;
+ if( null == actionText ) {
+ text = sceneUICntrl.getStatusText(drawable, renderModes, fpsLabel.getQuality(), dpiV);
+ } else if( null != drawable.getAnimator() ) {
+ text = Scene.getStatusText(drawable.getAnimator())+", "+actionText;
+ } else {
+ text = actionText;
+ }
+ if( fpsLabel.setText(text) ) { // marks dirty only if text differs.
+ System.err.println(text);
+ }
+ }
+ sceneUICntrl.display(drawable);
+ }
+
+ public void attachInputListenerTo(final GLWindow window) {
+ sceneUICntrl.attachInputListenerTo(window);
+ }
+
+ public void detachInputListenerFrom(final GLWindow window) {
+ sceneUICntrl.detachInputListenerFrom(window);
+ }
+
+ /**
+ * We can share this instance w/ all UI elements,
+ * since only mouse action / gesture is complete for a single one (press, drag, released and click).
+ */
+ private final Shape.MouseGestureAdapter dragZoomRotateListener = new Shape.MouseGestureAdapter() {
+ @Override
+ public void mouseReleased(final MouseEvent e) {
+ actionText = null;
+ }
+
+ @Override
+ public void mouseDragged(final MouseEvent e) {
+ final Shape.EventInfo shapeEvent = (Shape.EventInfo) e.getAttachment();
+ if( e.getPointerCount() == 1 ) {
+ final float[] tx = shapeEvent.shape.getPosition();
+ actionText = String.format((Locale)null, "Pos %6.2f / %6.2f / %6.2f", tx[0], tx[1], tx[2]);
+ }
+ }
+
+ @Override
+ public void mouseWheelMoved(final MouseEvent e) {
+ final Shape.EventInfo shapeEvent = (Shape.EventInfo) e.getAttachment();
+ final boolean isOnscreen = PointerClass.Onscreen == e.getPointerType(0).getPointerClass();
+ if( 0 == ( ~InputEvent.BUTTONALL_MASK & e.getModifiers() ) && !isOnscreen ) {
+ // offscreen vertical mouse wheel zoom
+ final float tz = 100f*e.getRotation()[1]; // vertical: wheel
+ System.err.println("Rotate.Zoom.W: "+tz);
+ shapeEvent.shape.move(0f, 0f, tz);
+ } else if( isOnscreen || e.isControlDown() ) {
+ final float[] rot = VectorUtil.scaleVec3(e.getRotation(), e.getRotation(), FloatUtil.PI / 180.0f);
+ if( isOnscreen ) {
+ System.err.println("XXX: "+e);
+ // swap axis for onscreen rotation matching natural feel
+ final float tmp = rot[0]; rot[0] = rot[1]; rot[1] = tmp;
+ VectorUtil.scaleVec3(rot, rot, 2f);
+ }
+ shapeEvent.shape.getRotation().rotateByEuler( rot );
+ }
+ }
+ @Override
+ public void gestureDetected(final GestureEvent e) {
+ final Shape.EventInfo shapeEvent = (Shape.EventInfo) e.getAttachment();
+ if( e instanceof PinchToZoomGesture.ZoomEvent ) {
+ final PinchToZoomGesture.ZoomEvent ze = (PinchToZoomGesture.ZoomEvent) e;
+ final float tz = ze.getDelta() * ze.getScale();
+ System.err.println("Rotate.Zoom.G: "+tz);
+ shapeEvent.shape.move(0f, 0f, tz);
+ }
+ } };
+}
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/UIShapeDemo01.java b/src/demos/com/jogamp/opengl/demos/graph/ui/UIShapeDemo01.java
new file mode 100644
index 000000000..b89b3e2b9
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/UIShapeDemo01.java
@@ -0,0 +1,569 @@
+/**
+ * Copyright 2010-2023 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.demos.graph.ui;
+
+import java.io.File;
+import java.io.IOException;
+
+import com.jogamp.opengl.FPSCounter;
+import com.jogamp.opengl.GL;
+import com.jogamp.opengl.GL2ES2;
+import com.jogamp.opengl.GLAnimatorControl;
+import com.jogamp.opengl.GLAutoDrawable;
+import com.jogamp.opengl.GLCapabilities;
+import com.jogamp.opengl.GLEventListener;
+import com.jogamp.opengl.GLException;
+import com.jogamp.opengl.GLPipelineFactory;
+import com.jogamp.opengl.GLProfile;
+import com.jogamp.opengl.GLRunnable;
+import com.jogamp.opengl.demos.graph.MSAATool;
+import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
+import com.jogamp.opengl.math.FloatUtil;
+import com.jogamp.opengl.math.geom.AABBox;
+import com.jogamp.common.util.InterruptSource;
+import com.jogamp.graph.curve.Region;
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.curve.opengl.RenderState;
+import com.jogamp.graph.curve.opengl.TextRegionUtil;
+import com.jogamp.graph.font.Font;
+import com.jogamp.graph.font.FontFactory;
+import com.jogamp.graph.font.FontSet;
+import com.jogamp.graph.geom.SVertex;
+import com.jogamp.graph.geom.plane.AffineTransform;
+import com.jogamp.graph.ui.gl.Shape;
+import com.jogamp.graph.ui.gl.shapes.Button;
+import com.jogamp.graph.ui.gl.shapes.CrossHair;
+import com.jogamp.newt.Window;
+import com.jogamp.newt.event.KeyAdapter;
+import com.jogamp.newt.event.KeyEvent;
+import com.jogamp.newt.event.KeyListener;
+import com.jogamp.newt.event.MouseEvent;
+import com.jogamp.newt.event.MouseListener;
+import com.jogamp.newt.event.WindowAdapter;
+import com.jogamp.newt.event.WindowEvent;
+import com.jogamp.newt.opengl.GLWindow;
+import com.jogamp.opengl.util.Animator;
+import com.jogamp.opengl.util.GLReadBufferUtil;
+import com.jogamp.opengl.util.PMVMatrix;
+
+/**
+ * Basic UIShape and Type Rendering demo.
+ *
+ * Action Keys:
+ * - 1/2: zoom in/out
+ * - 4/5: increase/decrease shape/text spacing
+ * - 6/7: increase/decrease corner size
+ * - 0/9: rotate
+ * - v: toggle v-sync
+ * - s: screenshot
+ */
+public class UIShapeDemo01 implements GLEventListener {
+ static final boolean DEBUG = false;
+ static final boolean TRACE = false;
+
+ public static void main(final String[] args) throws IOException {
+ Font font = null;
+ if( 0 != args.length ) {
+ for(int i=0; i obj [%f, %f, %f]%n", glWinX, glWinY, winZ, objCoord0[0], objCoord0[1], objCoord0[2]);
+ }
+ }
+ glWinX = drawable.getSurfaceWidth();
+ glWinY = drawable.getSurfaceHeight();
+ if( pmv.gluUnProject(glWinX, glWinY, winZ, renderer.getViewport(), 0, objCoord1, 0) ) {
+ if( once ) {
+ System.err.printf("winToObjCoord: win [%f, %f, %f] -> obj [%f, %f, %f]%n", glWinX, glWinY, winZ, objCoord1[0], objCoord1[1], objCoord1[2]);
+ }
+ }
+ full_width_o = objCoord1[0] - objCoord0[0];
+ }
+ final AABBox txt_box_em = font.getGlyphBounds(text, tempT1, tempT2);
+ final float full_width_s = full_width_o / txt_box_em.getWidth();
+ final float txt_scale = full_width_s/2f;
+ pmv.glPushMatrix();
+ pmv.glScalef(txt_scale, txt_scale, 1f);
+ pmv.glTranslatef(-txt_box_em.getWidth(), 0f, 0f);
+ final AABBox txt_box_r = TextRegionUtil.drawString3D(gl, renderModes, renderer, font, text, new float[] { 0, 0, 0, 1 }, sampleCount, tempT1, tempT2);
+ if( once ) {
+ final AABBox txt_box_em2 = font.getGlyphShapeBounds(null, text);
+ System.err.println("XXX: full_width: "+full_width_o+" / "+txt_box_em.getWidth()+" -> "+full_width_s);
+ System.err.println("XXX: txt_box_em "+txt_box_em);
+ System.err.println("XXX: txt_box_e2 "+txt_box_em2);
+ System.err.println("XXX: txt_box_rg "+txt_box_r);
+ once = false;
+ }
+ pmv.glPopMatrix();
+ }
+ renderer.enable(gl, false);
+ }
+ static boolean once = true;
+
+ @Override
+ public void dispose(final GLAutoDrawable drawable) {
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+ button.destroy(gl, getRegionRenderer());
+ crossHair.destroy(gl, getRegionRenderer());
+
+ autoDrawable = null;
+ screenshot.dispose(gl);
+ rRenderer.destroy(gl);
+ }
+
+ /** Attach the input listener to the window */
+ public void attachInputListenerTo(final GLWindow window) {
+ if ( null == keyAction ) {
+ keyAction = new KeyAction();
+ window.addKeyListener(keyAction);
+ }
+ if ( null == mouseAction ) {
+ mouseAction = new MouseAction();
+ window.addMouseListener(mouseAction);
+ }
+ }
+
+ public void detachFrom(final GLWindow window) {
+ if ( null == keyAction ) {
+ return;
+ }
+ if ( null == mouseAction ) {
+ return;
+ }
+ window.removeGLEventListener(this);
+ window.removeKeyListener(keyAction);
+ window.removeMouseListener(mouseAction);
+ }
+
+ public void printScreen(final GLAutoDrawable drawable, final String dir, final String tech, final String objName, final boolean exportAlpha) throws GLException, IOException {
+ final String sw = String.format("-%03dx%03d-Z%04d-T%04d-%s", drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), (int)Math.abs(zTran), 0, objName);
+
+ final String filename = dir + tech + sw +".png";
+ if(screenshot.readPixels(drawable.getGL(), false)) {
+ screenshot.write(new File(filename));
+ }
+ }
+
+ int screenshot_num = 0;
+
+ public void setIgnoreInput(final boolean v) {
+ ignoreInput = v;
+ }
+ public boolean getIgnoreInput() {
+ return ignoreInput;
+ }
+
+ public class MouseAction implements MouseListener{
+
+ @Override
+ public void mouseClicked(final MouseEvent e) {
+
+ }
+
+ @Override
+ public void mouseEntered(final MouseEvent e) {
+ }
+
+ @Override
+ public void mouseExited(final MouseEvent e) {
+ }
+
+ @Override
+ public void mousePressed(final MouseEvent e) {
+ autoDrawable.invoke(false, new GLRunnable() { // avoid data-race
+ @Override
+ public boolean run(final GLAutoDrawable drawable) {
+ System.err.println("\n\nMouse: "+e);
+
+ final RegionRenderer renderer = getRegionRenderer();
+ final PMVMatrix pmv = renderer.getMatrix();
+ pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
+ pmv.glLoadIdentity();
+ pmv.glTranslatef(xTran, yTran, zTran);
+
+ // flip to GL window coordinates, origin bottom-left
+ final int[] viewport = renderer.getViewport(new int[4]);
+ final int glWinX = e.getX();
+ final int glWinY = viewport[3] - e.getY() - 1;
+
+ {
+ pmv.glPushMatrix();
+ button.setTransform(pmv);
+
+ final float[] objPos = new float[3];
+ System.err.println("\n\nButton: "+button);
+ button.winToObjCoord(renderer, glWinX, glWinY, objPos);
+ System.err.println("Button: Click: Win "+glWinX+"/"+glWinY+" -> Obj "+objPos[0]+"/"+objPos[1]+"/"+objPos[1]);
+
+ final int[] surfaceSize = new int[2];
+ button.getSurfaceSize(renderer, surfaceSize);
+ System.err.println("Button: Size: Pixel "+surfaceSize[0]+" x "+surfaceSize[1]);
+
+ pmv.glPopMatrix();
+ }
+ {
+ pmv.glPushMatrix();
+ crossHair.setTransform(pmv);
+
+ final float[] objPosC = crossHair.getBounds().getCenter();
+ final int[] objWinPos = new int[2];
+ System.err.println("\n\nCrossHair: "+crossHair);
+ if( crossHair.objToWinCoord(renderer, objPosC, objWinPos) ) {
+ System.err.println("CrossHair: Obj: Obj "+objPosC[0]+"/"+objPosC[1]+"/"+objPosC[1]+" -> Win "+objWinPos[0]+"/"+objWinPos[1]);
+ }
+
+ final float[] objPos2 = new float[3];
+ crossHair.winToObjCoord(renderer, objWinPos[0], objWinPos[1], objPos2);
+ System.err.println("CrossHair: Obj: Win "+objWinPos[0]+"/"+objWinPos[1]+" -> Obj "+objPos2[0]+"/"+objPos2[1]+"/"+objPos2[1]);
+
+ final float[] winObjPos = new float[3];
+ if( crossHair.winToObjCoord(renderer, glWinX, glWinY, winObjPos) ) {
+ // final float[] translate = crossHair.getTranslate();
+ // final float[] objPosT = new float[] { objPosC[0]+translate[0], objPosC[1]+translate[1], objPosC[2]+translate[2] };
+ final float dx = winObjPos[0] - objPosC[0];
+ final float dy = winObjPos[1] - objPosC[1];
+ // final float dz = winObjPos[2] - objPosT[2];
+ if( !FloatUtil.isZero(dx, FloatUtil.EPSILON) || !FloatUtil.isZero(dy, FloatUtil.EPSILON) ) {
+ System.err.println("CrossHair: Move.1: Win "+glWinX+"/"+glWinY+" -> Obj "+winObjPos[0]+"/"+winObjPos[1]+"/"+winObjPos[1]+" -> diff "+dx+" / "+dy);
+ crossHair.move(dx, dy, 0f);
+ } else {
+ System.err.println("CrossHair: Move.0: Win "+glWinX+"/"+glWinY+" -> Obj "+winObjPos[0]+"/"+winObjPos[1]+"/"+winObjPos[1]+" -> diff "+dx+" / "+dy);
+ }
+ }
+
+ final int[] surfaceSize = new int[2];
+ crossHair.getSurfaceSize(renderer, surfaceSize);
+ System.err.println("CrossHair: Size: Pixel "+surfaceSize[0]+" x "+surfaceSize[1]);
+
+ pmv.glPopMatrix();
+ }
+ return true;
+ } } );
+
+ }
+
+ @Override
+ public void mouseReleased(final MouseEvent e) {
+ }
+
+ @Override
+ public void mouseMoved(final MouseEvent e) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void mouseDragged(final MouseEvent e) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void mouseWheelMoved(final MouseEvent e) {
+ // TODO Auto-generated method stub
+
+ }
+
+ }
+
+ public class KeyAction implements KeyListener {
+ @Override
+ public void keyPressed(final KeyEvent arg0) {
+ if(ignoreInput) {
+ return;
+ }
+
+ if(arg0.getKeyCode() == KeyEvent.VK_1){
+ button.move(0f, 0f, -zTran/10f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_2){
+ button.move(0f, 0f, zTran/10f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_UP){
+ button.move(0f, button.getHeight()/10f, 0f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_DOWN){
+ button.move(0f, -button.getHeight()/10f, 0f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_LEFT){
+ button.move(-button.getWidth()/10f, 0f, 0f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_RIGHT){
+ button.move(button.getWidth()/10f, 0f, 0f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_4){
+ button.setSpacing(button.getSpacingX()-0.01f, button.getSpacingY()-0.005f);
+ System.err.println("Button Spacing: " + button.getSpacingX());
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_5){
+ button.setSpacing(button.getSpacingX()+0.01f, button.getSpacingY()+0.005f);
+ System.err.println("Button Spacing: " + button.getSpacingX());
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_6){
+ button.setCorner(button.getCorner()-0.01f);
+ System.err.println("Button Corner: " + button.getCorner());
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_7){
+ button.setCorner(button.getCorner()+0.01f);
+ System.err.println("Button Corner: " + button.getCorner());
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_0){
+ // rotate(1);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_9){
+ // rotate(-1);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_V) {
+ if(null != autoDrawable) {
+ autoDrawable.invoke(false, new GLRunnable() {
+ @Override
+ public boolean run(final GLAutoDrawable drawable) {
+ final GL gl = drawable.getGL();
+ final int _i = gl.getSwapInterval();
+ final int i;
+ switch(_i) {
+ case 0: i = 1; break;
+ case 1: i = -1; break;
+ case -1: i = 0; break;
+ default: i = 1; break;
+ }
+ gl.setSwapInterval(i);
+
+ final GLAnimatorControl a = drawable.getAnimator();
+ if( null != a ) {
+ a.resetFPSCounter();
+ }
+ if(drawable instanceof FPSCounter) {
+ ((FPSCounter)drawable).resetFPSCounter();
+ }
+ System.err.println("Swap Interval: "+_i+" -> "+i+" -> "+gl.getSwapInterval());
+ return true;
+ }
+ });
+ }
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_S){
+ if(null != autoDrawable) {
+ autoDrawable.invoke(false, new GLRunnable() {
+ @Override
+ public boolean run(final GLAutoDrawable drawable) {
+ try {
+ final String type = Region.getRenderModeString(renderModes);
+ printScreen(drawable, "./", "demo-"+type, "snap"+screenshot_num, false);
+ screenshot_num++;
+ } catch (final GLException e) {
+ e.printStackTrace();
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+ return true;
+ }
+ });
+ }
+ }
+ }
+ @Override
+ public void keyReleased(final KeyEvent arg0) {}
+ }
+}
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/UITypeDemo01.java b/src/demos/com/jogamp/opengl/demos/graph/ui/UITypeDemo01.java
new file mode 100644
index 000000000..56962e110
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/UITypeDemo01.java
@@ -0,0 +1,617 @@
+/**
+ * Copyright 2010-2023 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.demos.graph.ui;
+
+import java.io.File;
+import java.io.IOException;
+
+import com.jogamp.opengl.FPSCounter;
+import com.jogamp.opengl.GL;
+import com.jogamp.opengl.GL2ES2;
+import com.jogamp.opengl.GLAnimatorControl;
+import com.jogamp.opengl.GLAutoDrawable;
+import com.jogamp.opengl.GLCapabilities;
+import com.jogamp.opengl.GLEventListener;
+import com.jogamp.opengl.GLException;
+import com.jogamp.opengl.GLPipelineFactory;
+import com.jogamp.opengl.GLProfile;
+import com.jogamp.opengl.GLRunnable;
+import com.jogamp.opengl.demos.graph.MSAATool;
+import com.jogamp.opengl.demos.graph.ui.testshapes.Glyph03FreeMonoRegular_M;
+import com.jogamp.opengl.demos.graph.ui.testshapes.Glyph04FreeSans_0;
+import com.jogamp.opengl.demos.graph.ui.testshapes.Glyph05FreeSerifBoldItalic_ae;
+import com.jogamp.opengl.demos.util.MiscUtils;
+import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
+import com.jogamp.opengl.math.FloatUtil;
+import com.jogamp.opengl.math.geom.AABBox;
+import com.jogamp.common.util.InterruptSource;
+import com.jogamp.graph.curve.Region;
+import com.jogamp.graph.curve.opengl.GLRegion;
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.curve.opengl.RenderState;
+import com.jogamp.graph.curve.opengl.TextRegionUtil;
+import com.jogamp.graph.font.Font;
+import com.jogamp.graph.font.FontFactory;
+import com.jogamp.graph.font.FontSet;
+import com.jogamp.graph.font.Font.Glyph;
+import com.jogamp.graph.geom.SVertex;
+import com.jogamp.graph.geom.plane.AffineTransform;
+import com.jogamp.graph.ui.gl.Shape;
+import com.jogamp.graph.ui.gl.shapes.CrossHair;
+import com.jogamp.graph.ui.gl.shapes.Rectangle;
+import com.jogamp.newt.Window;
+import com.jogamp.newt.event.KeyAdapter;
+import com.jogamp.newt.event.KeyEvent;
+import com.jogamp.newt.event.KeyListener;
+import com.jogamp.newt.event.MouseEvent;
+import com.jogamp.newt.event.MouseListener;
+import com.jogamp.newt.event.WindowAdapter;
+import com.jogamp.newt.event.WindowEvent;
+import com.jogamp.newt.opengl.GLWindow;
+import com.jogamp.opengl.util.Animator;
+import com.jogamp.opengl.util.GLReadBufferUtil;
+import com.jogamp.opengl.util.PMVMatrix;
+
+/**
+ * Basic UIShape and Type Rendering demo.
+ *
+ * Action Keys:
+ * - 1/2: zoom in/out
+ * - 4/5: increase/decrease shape/text spacing
+ * - 6/7: increase/decrease corner size
+ * - 0/9: rotate
+ * - v: toggle v-sync
+ * - s: screenshot
+ */
+public class UITypeDemo01 implements GLEventListener {
+ static final boolean DEBUG = false;
+ static final boolean TRACE = false;
+
+ public static void main(final String[] args) throws IOException {
+ Font font = null;
+ String text = "Hello Origin.";
+ int glyph_id = Glyph.ID_UNKNOWN;
+ if( 0 != args.length ) {
+ for(int i=0; i obj [%f, %f, %f]%n", glWinX, glWinY, winZ, objCoord0[0], objCoord0[1], objCoord0[2]);
+ }
+ }
+ glWinX = drawable.getSurfaceWidth();
+ glWinY = drawable.getSurfaceHeight();
+ if( pmv.gluUnProject(glWinX, glWinY, winZ, renderer.getViewport(), 0, objCoord1, 0) ) {
+ if( once ) {
+ System.err.printf("winToObjCoord: win [%f, %f, %f] -> obj [%f, %f, %f]%n", glWinX, glWinY, winZ, objCoord1[0], objCoord1[1], objCoord1[2]);
+ }
+ }
+ full_width_o = objCoord1[0] - objCoord0[0];
+ full_height_o = objCoord1[1] - objCoord0[1];
+ }
+ pmv.glPushMatrix();
+
+ final Font.Glyph glyph;
+ if( Glyph.ID_UNKNOWN < glyph_id ) {
+ glyph = font.getGlyph(glyph_id);
+ if( once ) {
+ System.err.println("glyph_id "+glyph_id+": "+glyph);
+ }
+ } else {
+ glyph = null;
+ }
+ if( null != glyph && glyph.getID() != Glyph.ID_UNKNOWN ) {
+ final AABBox txt_box_em = glyph.getBBox();
+ final float full_width_s = full_width_o / txt_box_em.getWidth();
+ final float full_height_s = full_height_o / txt_box_em.getHeight();
+ final float txt_scale = full_width_s < full_height_s ? full_width_s/2f : full_height_s/2f;
+ pmv.glScalef(txt_scale, txt_scale, 1f);
+ pmv.glTranslatef(-txt_box_em.getWidth(), 0f, 0f);
+ if( null != glyph.getShape() ) {
+ final GLRegion region = GLRegion.create(gl.getGLProfile(), renderModes, null);
+ region.addOutlineShape(glyph.getShape(), null, region.hasColorChannel() ? fg_color : null);
+ region.draw(gl, renderer, sampleCount);
+ region.destroy(gl);
+ }
+ if( once ) {
+ final AABBox txt_box_em2 = font.getGlyphShapeBounds(null, text);
+ System.err.println("XXX: full_width: "+full_width_o+" / "+txt_box_em.getWidth()+" -> "+full_width_s);
+ System.err.println("XXX: full_height: "+full_height_o+" / "+txt_box_em.getHeight()+" -> "+full_height_s);
+ System.err.println("XXX: txt_scale: "+txt_scale);
+ System.err.println("XXX: txt_box_em "+txt_box_em);
+ System.err.println("XXX: txt_box_e2 "+txt_box_em2);
+ }
+ } else if( Glyph.ID_UNKNOWN == glyph_id ) {
+ final AABBox txt_box_em = font.getGlyphBounds(text, tempT1, tempT2);
+ final float full_width_s = full_width_o / txt_box_em.getWidth();
+ final float full_height_s = full_height_o / txt_box_em.getHeight();
+ final float txt_scale = full_width_s < full_height_s ? full_width_s/2f : full_height_s/2f;
+ pmv.glScalef(txt_scale, txt_scale, 1f);
+ pmv.glTranslatef(-txt_box_em.getWidth(), 0f, 0f);
+ final AABBox txt_box_r = TextRegionUtil.drawString3D(gl, renderModes, renderer, font, text, fg_color, sampleCount, tempT1, tempT2);
+ if( once ) {
+ final AABBox txt_box_em2 = font.getGlyphShapeBounds(null, text);
+ System.err.println("XXX: full_width: "+full_width_o+" / "+txt_box_em.getWidth()+" -> "+full_width_s);
+ System.err.println("XXX: full_height: "+full_height_o+" / "+txt_box_em.getHeight()+" -> "+full_height_s);
+ System.err.println("XXX: txt_scale: "+txt_scale);
+ System.err.println("XXX: txt_box_em "+txt_box_em);
+ System.err.println("XXX: txt_box_e2 "+txt_box_em2);
+ System.err.println("XXX: txt_box_rg "+txt_box_r);
+ }
+ }
+ pmv.glPopMatrix();
+ if( once ) {
+ try {
+ printScreen(drawable);
+ } catch (GLException | IOException e) {
+ e.printStackTrace();
+ }
+ }
+ once = false;
+ }
+ renderer.enable(gl, false);
+ }
+ private boolean once = true;
+
+ @Override
+ public void dispose(final GLAutoDrawable drawable) {
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+ crossHair.destroy(gl, getRegionRenderer());
+ testObj.destroy(gl, getRegionRenderer());
+
+ autoDrawable = null;
+ screenshot.dispose(gl);
+ rRenderer.destroy(gl);
+ }
+
+ /** Attach the input listener to the window */
+ public void attachInputListenerTo(final GLWindow window) {
+ if ( null == keyAction ) {
+ keyAction = new KeyAction();
+ window.addKeyListener(keyAction);
+ }
+ if ( null == mouseAction ) {
+ mouseAction = new MouseAction();
+ window.addMouseListener(mouseAction);
+ }
+ }
+
+ public void detachFrom(final GLWindow window) {
+ if ( null == keyAction ) {
+ return;
+ }
+ if ( null == mouseAction ) {
+ return;
+ }
+ window.removeGLEventListener(this);
+ window.removeKeyListener(keyAction);
+ window.removeMouseListener(mouseAction);
+ }
+
+ public void printScreen(final GLAutoDrawable drawable) throws GLException, IOException {
+ final String dir = "./";
+ final String tech="demo-"+Region.getRenderModeString(renderModes);
+ final String objName = "snap"+screenshot_num;
+ {
+ final String sw = String.format("-%03dx%03d-Z%04d-T%04d-%s", drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), (int)Math.abs(zTran), 0, objName);
+
+ final String filename = dir + tech + sw +".png";
+ if(screenshot.readPixels(drawable.getGL(), false)) {
+ screenshot.write(new File(filename));
+ }
+ }
+ screenshot_num++;
+ }
+ int screenshot_num = 0;
+
+ public void setIgnoreInput(final boolean v) {
+ ignoreInput = v;
+ }
+ public boolean getIgnoreInput() {
+ return ignoreInput;
+ }
+
+ public class MouseAction implements MouseListener{
+
+ @Override
+ public void mouseClicked(final MouseEvent e) {
+
+ }
+
+ @Override
+ public void mouseEntered(final MouseEvent e) {
+ }
+
+ @Override
+ public void mouseExited(final MouseEvent e) {
+ }
+
+ @Override
+ public void mousePressed(final MouseEvent e) {
+ autoDrawable.invoke(false, new GLRunnable() { // avoid data-race
+ @Override
+ public boolean run(final GLAutoDrawable drawable) {
+ System.err.println("\n\nMouse: "+e);
+
+ final RegionRenderer renderer = getRegionRenderer();
+ final PMVMatrix pmv = renderer.getMatrix();
+ pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
+ pmv.glLoadIdentity();
+ pmv.glTranslatef(xTran, yTran, zTran);
+
+ // flip to GL window coordinates, origin bottom-left
+ final int[] viewport = renderer.getViewport(new int[4]);
+ final int glWinX = e.getX();
+ final int glWinY = viewport[3] - e.getY() - 1;
+
+ {
+ pmv.glPushMatrix();
+ crossHair.setTransform(pmv);
+
+ final float[] objPosC = crossHair.getBounds().getCenter();
+ final int[] objWinPos = new int[2];
+ System.err.println("\n\nCrossHair: "+crossHair);
+ if( crossHair.objToWinCoord(renderer, objPosC, objWinPos) ) {
+ System.err.println("CrossHair: Obj: Obj "+objPosC[0]+"/"+objPosC[1]+"/"+objPosC[1]+" -> Win "+objWinPos[0]+"/"+objWinPos[1]);
+ }
+
+ final float[] objPos2 = new float[3];
+ crossHair.winToObjCoord(renderer, objWinPos[0], objWinPos[1], objPos2);
+ System.err.println("CrossHair: Obj: Win "+objWinPos[0]+"/"+objWinPos[1]+" -> Obj "+objPos2[0]+"/"+objPos2[1]+"/"+objPos2[1]);
+
+ final float[] winObjPos = new float[3];
+ if( crossHair.winToObjCoord(renderer, glWinX, glWinY, winObjPos) ) {
+ // final float[] translate = crossHair.getTranslate();
+ // final float[] objPosT = new float[] { objPosC[0]+translate[0], objPosC[1]+translate[1], objPosC[2]+translate[2] };
+ final float dx = winObjPos[0] - objPosC[0];
+ final float dy = winObjPos[1] - objPosC[1];
+ // final float dz = winObjPos[2] - objPosT[2];
+ if( !FloatUtil.isZero(dx, FloatUtil.EPSILON) || !FloatUtil.isZero(dy, FloatUtil.EPSILON) ) {
+ System.err.println("CrossHair: Move.1: Win "+glWinX+"/"+glWinY+" -> Obj "+winObjPos[0]+"/"+winObjPos[1]+"/"+winObjPos[1]+" -> diff "+dx+" / "+dy);
+ crossHair.move(dx, dy, 0f);
+ } else {
+ System.err.println("CrossHair: Move.0: Win "+glWinX+"/"+glWinY+" -> Obj "+winObjPos[0]+"/"+winObjPos[1]+"/"+winObjPos[1]+" -> diff "+dx+" / "+dy);
+ }
+ }
+
+ final int[] surfaceSize = new int[2];
+ crossHair.getSurfaceSize(renderer, surfaceSize);
+ System.err.println("CrossHair: Size: Pixel "+surfaceSize[0]+" x "+surfaceSize[1]);
+
+ pmv.glPopMatrix();
+ }
+ return true;
+ } } );
+
+ }
+
+ @Override
+ public void mouseReleased(final MouseEvent e) {
+ }
+
+ @Override
+ public void mouseMoved(final MouseEvent e) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void mouseDragged(final MouseEvent e) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void mouseWheelMoved(final MouseEvent e) {
+ // TODO Auto-generated method stub
+
+ }
+
+ }
+
+ public class KeyAction implements KeyListener {
+ @Override
+ public void keyPressed(final KeyEvent arg0) {
+ if(ignoreInput) {
+ return;
+ }
+
+ if(arg0.getKeyCode() == KeyEvent.VK_1){
+ crossHair.move(0f, 0f, -zTran/10f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_2){
+ crossHair.move(0f, 0f, zTran/10f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_UP){
+ crossHair.move(0f, crossHair.getHeight()/10f, 0f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_DOWN){
+ crossHair.move(0f, -crossHair.getHeight()/10f, 0f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_LEFT){
+ crossHair.move(-crossHair.getWidth()/10f, 0f, 0f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_RIGHT){
+ crossHair.move(crossHair.getWidth()/10f, 0f, 0f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_0){
+ // rotate(1);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_9){
+ // rotate(-1);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_V) {
+ if(null != autoDrawable) {
+ autoDrawable.invoke(false, new GLRunnable() {
+ @Override
+ public boolean run(final GLAutoDrawable drawable) {
+ final GL gl = drawable.getGL();
+ final int _i = gl.getSwapInterval();
+ final int i;
+ switch(_i) {
+ case 0: i = 1; break;
+ case 1: i = -1; break;
+ case -1: i = 0; break;
+ default: i = 1; break;
+ }
+ gl.setSwapInterval(i);
+
+ final GLAnimatorControl a = drawable.getAnimator();
+ if( null != a ) {
+ a.resetFPSCounter();
+ }
+ if(drawable instanceof FPSCounter) {
+ ((FPSCounter)drawable).resetFPSCounter();
+ }
+ System.err.println("Swap Interval: "+_i+" -> "+i+" -> "+gl.getSwapInterval());
+ return true;
+ }
+ });
+ }
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_S){
+ if(null != autoDrawable) {
+ autoDrawable.invoke(false, new GLRunnable() {
+ @Override
+ public boolean run(final GLAutoDrawable drawable) {
+ try {
+ printScreen(drawable);
+ } catch (final GLException e) {
+ e.printStackTrace();
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+ return true;
+ }
+ });
+ }
+ }
+ }
+ @Override
+ public void keyReleased(final KeyEvent arg0) {}
+ }
+}
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/button-pressed-145x53.png b/src/demos/com/jogamp/opengl/demos/graph/ui/button-pressed-145x53.png
new file mode 100644
index 000000000..1eba3a09e
Binary files /dev/null and b/src/demos/com/jogamp/opengl/demos/graph/ui/button-pressed-145x53.png differ
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/button-released-145x53.png b/src/demos/com/jogamp/opengl/demos/graph/ui/button-released-145x53.png
new file mode 100644
index 000000000..fe223c6d3
Binary files /dev/null and b/src/demos/com/jogamp/opengl/demos/graph/ui/button-released-145x53.png differ
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/testshapes/Glyph01UbuntuLight_o.java b/src/demos/com/jogamp/opengl/demos/graph/ui/testshapes/Glyph01UbuntuLight_o.java
new file mode 100644
index 000000000..4171eeda8
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/testshapes/Glyph01UbuntuLight_o.java
@@ -0,0 +1,316 @@
+/**
+ * Copyright 2023 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.demos.graph.ui.testshapes;
+
+import com.jogamp.opengl.GL2ES2;
+import com.jogamp.graph.curve.OutlineShape;
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.geom.Vertex;
+import com.jogamp.graph.geom.Vertex.Factory;
+import com.jogamp.graph.geom.plane.Winding;
+import com.jogamp.graph.ui.gl.Shape;
+
+/**
+ * GPU based resolution independent test object
+ * - Ubuntu-Light, lower case 'o'
+ * - TTF Shape for Glyph 82
+ */
+public class Glyph01UbuntuLight_o extends Shape {
+
+ public Glyph01UbuntuLight_o(final Factory extends Vertex> factory, final int renderModes) {
+ super(factory, renderModes);
+ }
+
+ @Override
+ protected void clearImpl(final GL2ES2 gl, final RegionRenderer renderer) {
+ }
+
+ @Override
+ protected void destroyImpl(final GL2ES2 gl, final RegionRenderer renderer) {
+ }
+
+ @SuppressWarnings("unused")
+ @Override
+ protected void addShapeToRegion(final GL2ES2 gl, final RegionRenderer renderer) {
+ final OutlineShape shape = new OutlineShape(renderer.getRenderState().getVertexFactory());
+
+ // Ubuntu-Light, lower case 'o'
+ // Start TTF Shape for Glyph 82
+ if( false ) {
+ // Original Outer shape: Winding.CW
+ // Moved into OutlineShape reverse -> Winding.CCW -> OK
+ //
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0, 0.527000f, 0.258000f, true);
+ // 000: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.527000f, 0.197000f, false);
+ shape.addVertex(0, 0.510000f, 0.147000f, true);
+ // 002: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.492000f, 0.097000f, false);
+ shape.addVertex(0, 0.461000f, 0.062000f, true);
+ // 003: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.429000f, 0.027000f, false);
+ shape.addVertex(0, 0.386000f, 0.008000f, true);
+ // 004: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.343000f, -0.012000f, false);
+ shape.addVertex(0, 0.291000f, -0.012000f, true);
+ // 005: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.239000f, -0.012000f, false);
+ shape.addVertex(0, 0.196000f, 0.007000f, true);
+ // 007: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.153000f, 0.027000f, false);
+ shape.addVertex(0, 0.122000f, 0.062000f, true);
+ // 008: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.090000f, 0.097000f, false);
+ shape.addVertex(0, 0.073000f, 0.147000f, true);
+ // 009: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.055000f, 0.197000f, false);
+ shape.addVertex(0, 0.055000f, 0.258000f, true);
+ // 010: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.055000f, 0.319000f, false);
+ shape.addVertex(0, 0.072000f, 0.369000f, true);
+ // 012: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.090000f, 0.419000f, false);
+ shape.addVertex(0, 0.121000f, 0.454000f, true);
+ // 013: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.153000f, 0.490000f, false);
+ shape.addVertex(0, 0.196000f, 0.509000f, true);
+ // 014: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.239000f, 0.529000f, false);
+ shape.addVertex(0, 0.291000f, 0.529000f, true);
+ // 015: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.343000f, 0.529000f, false);
+ shape.addVertex(0, 0.386000f, 0.510000f, true);
+ // 017: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.429000f, 0.490000f, false);
+ shape.addVertex(0, 0.460000f, 0.455000f, true);
+ // 018: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.492000f, 0.419000f, false);
+ shape.addVertex(0, 0.509000f, 0.369000f, true);
+ // 019: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.527000f, 0.319000f, false);
+ shape.addVertex(0, 0.527000f, 0.258000f, true);
+ System.err.println("Glyph01UbuntuLight_o.shape01a.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ } else {
+ // Outer shape: Winding.CW
+ // Moved into OutlineShape same-order -> Winding.CW -> ERROR (so we fix it in the end, see below)
+ //
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0.527000f, 0.258000f, true);
+ // 000: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.527000f, 0.197000f, false);
+ shape.addVertex(0.510000f, 0.147000f, true);
+ // 002: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.492000f, 0.097000f, false);
+ shape.addVertex(0.461000f, 0.062000f, true);
+ // 003: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.429000f, 0.027000f, false);
+ shape.addVertex(0.386000f, 0.008000f, true);
+ // 004: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.343000f, -0.012000f, false);
+ shape.addVertex(0.291000f, -0.012000f, true);
+ // 005: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.239000f, -0.012000f, false);
+ shape.addVertex(0.196000f, 0.007000f, true);
+ // 007: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.153000f, 0.027000f, false);
+ shape.addVertex(0.122000f, 0.062000f, true);
+ // 008: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.090000f, 0.097000f, false);
+ shape.addVertex(0.073000f, 0.147000f, true);
+ // 009: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.055000f, 0.197000f, false);
+ shape.addVertex(0.055000f, 0.258000f, true);
+ // 010: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.055000f, 0.319000f, false);
+ shape.addVertex(0.072000f, 0.369000f, true);
+ // 012: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.090000f, 0.419000f, false);
+ shape.addVertex(0.121000f, 0.454000f, true);
+ // 013: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.153000f, 0.490000f, false);
+ shape.addVertex(0.196000f, 0.509000f, true);
+ // 014: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.239000f, 0.529000f, false);
+ shape.addVertex(0.291000f, 0.529000f, true);
+ // 015: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.343000f, 0.529000f, false);
+ shape.addVertex(0.386000f, 0.510000f, true);
+ // 017: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.429000f, 0.490000f, false);
+ shape.addVertex(0.460000f, 0.455000f, true);
+ // 018: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.492000f, 0.419000f, false);
+ shape.addVertex(0.509000f, 0.369000f, true);
+ // 019: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.527000f, 0.319000f, false);
+ shape.addVertex(0.527000f, 0.258000f, true);
+ System.err.println("Glyph01UbuntuLight_o.shape01b.1.winding_area: "+shape.getWindingOfLastOutline());
+ shape.setWindingOfLastOutline(Winding.CCW);
+ System.err.println("Glyph01UbuntuLight_o.shape01b.2.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ }
+
+ if( true ) {
+ // Original Inner shape: Winding.CCW
+ // Moved into OutlineShape reverse -> Winding.CW -> OK
+ //
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0, 0.458000f, 0.258000f, true);
+ // 020: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.458000f, 0.355000f, false);
+ shape.addVertex(0, 0.413000f, 0.412000f, true);
+ // 022: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.368000f, 0.470000f, false);
+ shape.addVertex(0, 0.291000f, 0.470000f, true);
+ // 023: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.214000f, 0.470000f, false);
+ shape.addVertex(0, 0.169000f, 0.413000f, true);
+ // 025: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.124000f, 0.355000f, false);
+ shape.addVertex(0, 0.124000f, 0.258000f, true);
+ // 026: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.124000f, 0.161000f, false);
+ shape.addVertex(0, 0.169000f, 0.104000f, true);
+ // 028: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.214000f, 0.047000f, false);
+ shape.addVertex(0, 0.291000f, 0.047000f, true);
+ // 029: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.368000f, 0.047000f, false);
+ shape.addVertex(0, 0.413000f, 0.104000f, true);
+ // 031: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.458000f, 0.161000f, false);
+ shape.addVertex(0, 0.458000f, 0.258000f, true);
+ System.err.println("Glyph01UbuntuLight_o.shape02a.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ } else {
+ // Inner shape: Winding.CCW
+ // Moved into OutlineShape same-order -> Winding.CCW -> OK
+ //
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+
+ shape.addVertex(0.458000f, 0.258000f, true);
+ // 020: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.458000f, 0.355000f, false);
+ shape.addVertex(0.413000f, 0.412000f, true);
+ // 022: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.368000f, 0.470000f, false);
+ shape.addVertex(0.291000f, 0.470000f, true);
+ // 023: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.214000f, 0.470000f, false);
+ shape.addVertex(0.169000f, 0.413000f, true);
+ // 025: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.124000f, 0.355000f, false);
+ shape.addVertex(0.124000f, 0.258000f, true);
+ // 026: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.124000f, 0.161000f, false);
+ shape.addVertex(0.169000f, 0.104000f, true);
+ // 028: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.214000f, 0.047000f, false);
+ shape.addVertex(0.291000f, 0.047000f, true);
+ // 029: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.368000f, 0.047000f, false);
+ shape.addVertex(0.413000f, 0.104000f, true);
+ // 031: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.458000f, 0.161000f, false);
+ shape.addVertex(0.458000f, 0.258000f, true);
+
+ System.err.println("Glyph01UbuntuLight_o.shape02b.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ }
+ // End Shape for Glyph 82
+
+ shape.setIsQuadraticNurbs();
+ shape.setSharpness(shapesSharpness);
+ region.addOutlineShape(shape, null, rgbaColor);
+
+ box.resize(shape.getBounds());
+ }
+
+ @Override
+ public String getSubString() {
+ return super.getSubString();
+ }
+}
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/testshapes/Glyph02UbuntuLight_ae.java b/src/demos/com/jogamp/opengl/demos/graph/ui/testshapes/Glyph02UbuntuLight_ae.java
new file mode 100644
index 000000000..28d6f6390
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/testshapes/Glyph02UbuntuLight_ae.java
@@ -0,0 +1,652 @@
+/**
+ * Copyright 2023 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.demos.graph.ui.testshapes;
+
+import com.jogamp.opengl.GL2ES2;
+import com.jogamp.graph.curve.OutlineShape;
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.geom.Vertex;
+import com.jogamp.graph.geom.Vertex.Factory;
+import com.jogamp.graph.ui.gl.Shape;
+
+/**
+ * GPU based resolution independent test object
+ * - Ubuntu-Light, lower case 'æ'
+ * - TTF Shape for Glyph 193
+ */
+public class Glyph02UbuntuLight_ae extends Shape {
+
+ public Glyph02UbuntuLight_ae(final Factory extends Vertex> factory, final int renderModes) {
+ super(factory, renderModes);
+ }
+
+ @Override
+ protected void clearImpl(final GL2ES2 gl, final RegionRenderer renderer) {
+ }
+
+ @Override
+ protected void destroyImpl(final GL2ES2 gl, final RegionRenderer renderer) {
+ }
+
+ @SuppressWarnings("unused")
+ @Override
+ protected void addShapeToRegion(final GL2ES2 gl, final RegionRenderer renderer) {
+ final OutlineShape shape = new OutlineShape(renderer.getRenderState().getVertexFactory());
+
+ // Ubuntu-Light, lower case 'æ'
+
+ // Start TTF Shape for Glyph 193
+ if( true ) {
+ // Original Inner e-shape: Winding.CCW
+ // Moved into OutlineShape reverse -> Winding.CW -> OK
+ //
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0, 0.728000f, 0.300000f, true);
+ // 000: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.726000f, 0.381000f, false);
+ shape.addVertex(0, 0.690000f, 0.426000f, true);
+ // 002: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.654000f, 0.471000f, false);
+ shape.addVertex(0, 0.588000f, 0.471000f, true);
+ // 003: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.553000f, 0.471000f, false);
+ shape.addVertex(0, 0.526000f, 0.457000f, true);
+ // 005: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.498000f, 0.443000f, false);
+ shape.addVertex(0, 0.478000f, 0.420000f, true);
+ // 006: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.457000f, 0.396000f, false);
+ shape.addVertex(0, 0.446000f, 0.365000f, true);
+ // 007: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.434000f, 0.334000f, false);
+ shape.addVertex(0, 0.432000f, 0.300000f, true);
+ // 008: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.728000f, 0.300000f, true);
+ System.err.println("Glyph02UbuntuLight_ae.shape01a.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ } else {
+ // Inner e-shape: Winding.CCW
+ // Moved into OutlineShape same-order -> Winding.CCW -> ??
+ //
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0.728000f, 0.300000f, true);
+ // 000: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.726000f, 0.381000f, false);
+ shape.addVertex(0.690000f, 0.426000f, true);
+ // 002: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.654000f, 0.471000f, false);
+ shape.addVertex(0.588000f, 0.471000f, true);
+ // 003: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.553000f, 0.471000f, false);
+ shape.addVertex(0.526000f, 0.457000f, true);
+ // 005: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.498000f, 0.443000f, false);
+ shape.addVertex(0.478000f, 0.420000f, true);
+ // 006: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.457000f, 0.396000f, false);
+ shape.addVertex(0.446000f, 0.365000f, true);
+ // 007: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.434000f, 0.334000f, false);
+ shape.addVertex(0.432000f, 0.300000f, true);
+ // 008: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0.728000f, 0.300000f, true);
+ System.err.println("Glyph02UbuntuLight_ae.shape01b.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ }
+
+ if( true ) {
+ // Original Outer shape: Winding.CW
+ // Moved into OutlineShape reverse -> Winding.CCW -> OK
+ //
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0, 0.252000f, -0.011000f, true);
+ // 009: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.208000f, -0.011000f, false);
+ shape.addVertex(0, 0.171000f, -0.002000f, true);
+ // 011: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.133000f, 0.007000f, false);
+ shape.addVertex(0, 0.106000f, 0.026000f, true);
+ // 012: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.079000f, 0.046000f, false);
+ shape.addVertex(0, 0.064000f, 0.076000f, true);
+ // 013: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.048000f, 0.107000f, false);
+ shape.addVertex(0, 0.048000f, 0.151000f, true);
+ // 014: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.048000f, 0.193000f, false);
+ shape.addVertex(0, 0.064000f, 0.223000f, true);
+ // 016: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.080000f, 0.253000f, false);
+ shape.addVertex(0, 0.109000f, 0.272000f, true);
+ // 017: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.138000f, 0.292000f, false);
+ shape.addVertex(0, 0.178000f, 0.301000f, true);
+ // 018: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.218000f, 0.310000f, false);
+ shape.addVertex(0, 0.265000f, 0.310000f, true);
+ // 019: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.279000f, 0.310000f, false);
+ shape.addVertex(0, 0.294000f, 0.309000f, true);
+ // 021: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.310000f, 0.307000f, false);
+ shape.addVertex(0, 0.324000f, 0.305000f, true);
+ // 022: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.339000f, 0.302000f, false);
+ shape.addVertex(0, 0.349000f, 0.300000f, true);
+ // 023: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.360000f, 0.297000f, false);
+ shape.addVertex(0, 0.364000f, 0.295000f, true);
+ // 024: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.364000f, 0.327000f, true);
+ // 025: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.364000f, 0.354000f, false);
+ shape.addVertex(0, 0.360000f, 0.379000f, true);
+ // 027: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.356000f, 0.405000f, false);
+ shape.addVertex(0, 0.343000f, 0.425000f, true);
+ // 028: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.329000f, 0.446000f, false);
+ shape.addVertex(0, 0.305000f, 0.458000f, true);
+ // 029: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.280000f, 0.471000f, false);
+ shape.addVertex(0, 0.240000f, 0.471000f, true);
+ // 030: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.186000f, 0.471000f, false);
+ shape.addVertex(0, 0.156000f, 0.464000f, true);
+ // 032: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.126000f, 0.456000f, false);
+ shape.addVertex(0, 0.113000f, 0.451000f, true);
+ // 033: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.105000f, 0.507000f, true);
+ // 034: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.122000f, 0.515000f, false);
+ shape.addVertex(0, 0.158000f, 0.522000f, true);
+ // 036: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.194000f, 0.529000f, false);
+ shape.addVertex(0, 0.243000f, 0.529000f, true);
+ // 037: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.314000f, 0.529000f, false);
+ shape.addVertex(0, 0.354000f, 0.503000f, true);
+ // 039: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.395000f, 0.476000f, false);
+ shape.addVertex(0, 0.412000f, 0.431000f, true);
+ // 040: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.445000f, 0.480000f, false);
+ shape.addVertex(0, 0.491000f, 0.504000f, true);
+ // 042: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.537000f, 0.529000f, false);
+ shape.addVertex(0, 0.587000f, 0.529000f, true);
+ // 043: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.682000f, 0.529000f, false);
+ shape.addVertex(0, 0.738000f, 0.467000f, true);
+ // 045: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.795000f, 0.405000f, false);
+ shape.addVertex(0, 0.795000f, 0.276000f, true);
+ // 046: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.795000f, 0.268000f, false);
+ shape.addVertex(0, 0.795000f, 0.260000f, true);
+ // 048: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.794000f, 0.252000f, false);
+ shape.addVertex(0, 0.793000f, 0.245000f, true);
+ // 049: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.430000f, 0.245000f, true);
+ // 050: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.433000f, 0.150000f, false);
+ shape.addVertex(0, 0.477000f, 0.099000f, true);
+ // 052: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.521000f, 0.048000f, false);
+ shape.addVertex(0, 0.617000f, 0.048000f, true);
+ // 053: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.670000f, 0.048000f, false);
+ shape.addVertex(0, 0.701000f, 0.058000f, true);
+ // 055: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.732000f, 0.068000f, false);
+ shape.addVertex(0, 0.746000f, 0.075000f, true);
+ // 056: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.758000f, 0.019000f, true);
+ // 057: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.744000f, 0.011000f, false);
+ shape.addVertex(0, 0.706000f, 0.000000f, true);
+ // 059: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.667000f, -0.011000f, false);
+ shape.addVertex(0, 0.615000f, -0.011000f, true);
+ // 060: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.558000f, -0.011000f, false);
+ shape.addVertex(0, 0.514000f, 0.003000f, true);
+ // 062: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.470000f, 0.017000f, false);
+ shape.addVertex(0, 0.437000f, 0.049000f, true);
+ // 063: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.426000f, 0.040000f, false);
+ shape.addVertex(0, 0.410000f, 0.030000f, true);
+ // 065: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.393000f, 0.019000f, false);
+ shape.addVertex(0, 0.370000f, 0.010000f, true);
+ // 066: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.347000f, 0.001000f, false);
+ shape.addVertex(0, 0.318000f, -0.005000f, true);
+ // 067: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.289000f, -0.011000f, false);
+ shape.addVertex(0, 0.252000f, -0.011000f, true);
+ System.err.println("Glyph02UbuntuLight_ae.shape02a.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ } else {
+ // Outer shape: Winding.CW
+ // Moved into OutlineShape same-order -> Winding.CW -> OK now
+ //
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0.252000f, -0.011000f, true);
+ // 009: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.208000f, -0.011000f, false);
+ shape.addVertex(0.171000f, -0.002000f, true);
+ // 011: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.133000f, 0.007000f, false);
+ shape.addVertex(0.106000f, 0.026000f, true);
+ // 012: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.079000f, 0.046000f, false);
+ shape.addVertex(0.064000f, 0.076000f, true);
+ // 013: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.048000f, 0.107000f, false);
+ shape.addVertex(0.048000f, 0.151000f, true);
+ // 014: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.048000f, 0.193000f, false);
+ shape.addVertex(0.064000f, 0.223000f, true);
+ // 016: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.080000f, 0.253000f, false);
+ shape.addVertex(0.109000f, 0.272000f, true);
+ // 017: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.138000f, 0.292000f, false);
+ shape.addVertex(0.178000f, 0.301000f, true);
+ // 018: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.218000f, 0.310000f, false);
+ shape.addVertex(0.265000f, 0.310000f, true);
+ // 019: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.279000f, 0.310000f, false);
+ shape.addVertex(0.294000f, 0.309000f, true);
+ // 021: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.310000f, 0.307000f, false);
+ shape.addVertex(0.324000f, 0.305000f, true);
+ // 022: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.339000f, 0.302000f, false);
+ shape.addVertex(0.349000f, 0.300000f, true);
+ // 023: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.360000f, 0.297000f, false);
+ shape.addVertex(0.364000f, 0.295000f, true);
+ // 024: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0.364000f, 0.327000f, true);
+ // 025: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.364000f, 0.354000f, false);
+ shape.addVertex(0.360000f, 0.379000f, true);
+ // 027: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.356000f, 0.405000f, false);
+ shape.addVertex(0.343000f, 0.425000f, true);
+ // 028: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.329000f, 0.446000f, false);
+ shape.addVertex(0.305000f, 0.458000f, true);
+ // 029: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.280000f, 0.471000f, false);
+ shape.addVertex(0.240000f, 0.471000f, true);
+ // 030: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.186000f, 0.471000f, false);
+ shape.addVertex(0.156000f, 0.464000f, true);
+ // 032: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.126000f, 0.456000f, false);
+ shape.addVertex(0.113000f, 0.451000f, true);
+ // 033: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0.105000f, 0.507000f, true);
+ // 034: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.122000f, 0.515000f, false);
+ shape.addVertex(0.158000f, 0.522000f, true);
+ // 036: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.194000f, 0.529000f, false);
+ shape.addVertex(0.243000f, 0.529000f, true);
+ // 037: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.314000f, 0.529000f, false);
+ shape.addVertex(0.354000f, 0.503000f, true);
+ // 039: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.395000f, 0.476000f, false);
+ shape.addVertex(0.412000f, 0.431000f, true);
+ // 040: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.445000f, 0.480000f, false);
+ shape.addVertex(0.491000f, 0.504000f, true);
+ // 042: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.537000f, 0.529000f, false);
+ shape.addVertex(0.587000f, 0.529000f, true);
+ // 043: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.682000f, 0.529000f, false);
+ shape.addVertex(0.738000f, 0.467000f, true);
+ // 045: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.795000f, 0.405000f, false);
+ shape.addVertex(0.795000f, 0.276000f, true);
+ // 046: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.795000f, 0.268000f, false);
+ shape.addVertex(0.795000f, 0.260000f, true);
+ // 048: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.794000f, 0.252000f, false);
+ shape.addVertex(0.793000f, 0.245000f, true);
+ // 049: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0.430000f, 0.245000f, true);
+ // 050: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.433000f, 0.150000f, false);
+ shape.addVertex(0.477000f, 0.099000f, true);
+ // 052: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.521000f, 0.048000f, false);
+ shape.addVertex(0.617000f, 0.048000f, true);
+ // 053: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.670000f, 0.048000f, false);
+ shape.addVertex(0.701000f, 0.058000f, true);
+ // 055: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.732000f, 0.068000f, false);
+ shape.addVertex(0.746000f, 0.075000f, true);
+ // 056: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0.758000f, 0.019000f, true);
+ // 057: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.744000f, 0.011000f, false);
+ shape.addVertex(0.706000f, 0.000000f, true);
+ // 059: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.667000f, -0.011000f, false);
+ shape.addVertex(0.615000f, -0.011000f, true);
+ // 060: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.558000f, -0.011000f, false);
+ shape.addVertex(0.514000f, 0.003000f, true);
+ // 062: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.470000f, 0.017000f, false);
+ shape.addVertex(0.437000f, 0.049000f, true);
+ // 063: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.426000f, 0.040000f, false);
+ shape.addVertex(0.410000f, 0.030000f, true);
+ // 065: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.393000f, 0.019000f, false);
+ shape.addVertex(0.370000f, 0.010000f, true);
+ // 066: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.347000f, 0.001000f, false);
+ shape.addVertex(0.318000f, -0.005000f, true);
+ // 067: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.289000f, -0.011000f, false);
+ shape.addVertex(0.252000f, -0.011000f, true);
+ System.err.println("Glyph02UbuntuLight_ae.shape02b.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ }
+
+ if( true ) {
+ // Original Inner a-shape: Winding.CCW
+ // Moved into OutlineShape reverse -> Winding.CW -> OK now
+ //
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0, 0.365000f, 0.238000f, true);
+ // 068: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.354000f, 0.243000f, false);
+ shape.addVertex(0, 0.330000f, 0.248000f, true);
+ // 070: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.305000f, 0.254000f, false);
+ shape.addVertex(0, 0.263000f, 0.254000f, true);
+ // 071: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.239000f, 0.254000f, false);
+ shape.addVertex(0, 0.213000f, 0.251000f, true);
+ // 073: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.187000f, 0.247000f, false);
+ shape.addVertex(0, 0.165000f, 0.236000f, true);
+ // 074: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.143000f, 0.224000f, false);
+ shape.addVertex(0, 0.129000f, 0.204000f, true);
+ // 075: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.115000f, 0.184000f, false);
+ shape.addVertex(0, 0.115000f, 0.151000f, true);
+ // 076: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.115000f, 0.122000f, false);
+ shape.addVertex(0, 0.125000f, 0.102000f, true);
+ // 078: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.135000f, 0.082000f, false);
+ shape.addVertex(0, 0.153000f, 0.070000f, true);
+ // 079: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.172000f, 0.058000f, false);
+ shape.addVertex(0, 0.197000f, 0.053000f, true);
+ // 080: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.222000f, 0.047000f, false);
+ shape.addVertex(0, 0.252000f, 0.047000f, true);
+ // 081: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.314000f, 0.047000f, false);
+ shape.addVertex(0, 0.350000f, 0.063000f, true);
+ // 083: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.386000f, 0.080000f, false);
+ shape.addVertex(0, 0.400000f, 0.093000f, true);
+ // 084: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.384000f, 0.119000f, false);
+ shape.addVertex(0, 0.375000f, 0.154000f, true);
+ // 086: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.366000f, 0.190000f, false);
+ shape.addVertex(0, 0.365000f, 0.238000f, true);
+ System.err.println("Glyph02UbuntuLight_ae.shape03a.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ } else {
+ // Inner a-shape: Winding.CCW
+ // Moved into OutlineShape same-order -> Winding.CCW -> OK
+ //
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0.365000f, 0.238000f, true);
+ // 068: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.354000f, 0.243000f, false);
+ shape.addVertex(0.330000f, 0.248000f, true);
+ // 070: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.305000f, 0.254000f, false);
+ shape.addVertex(0.263000f, 0.254000f, true);
+ // 071: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.239000f, 0.254000f, false);
+ shape.addVertex(0.213000f, 0.251000f, true);
+ // 073: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.187000f, 0.247000f, false);
+ shape.addVertex(0.165000f, 0.236000f, true);
+ // 074: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.143000f, 0.224000f, false);
+ shape.addVertex(0.129000f, 0.204000f, true);
+ // 075: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.115000f, 0.184000f, false);
+ shape.addVertex(0.115000f, 0.151000f, true);
+ // 076: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.115000f, 0.122000f, false);
+ shape.addVertex(0.125000f, 0.102000f, true);
+ // 078: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.135000f, 0.082000f, false);
+ shape.addVertex(0.153000f, 0.070000f, true);
+ // 079: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.172000f, 0.058000f, false);
+ shape.addVertex(0.197000f, 0.053000f, true);
+ // 080: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.222000f, 0.047000f, false);
+ shape.addVertex(0.252000f, 0.047000f, true);
+ // 081: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.314000f, 0.047000f, false);
+ shape.addVertex(0.350000f, 0.063000f, true);
+ // 083: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.386000f, 0.080000f, false);
+ shape.addVertex(0.400000f, 0.093000f, true);
+ // 084: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.384000f, 0.119000f, false);
+ shape.addVertex(0.375000f, 0.154000f, true);
+ // 086: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.366000f, 0.190000f, false);
+ shape.addVertex(0.365000f, 0.238000f, true);
+ System.err.println("Glyph02UbuntuLight_ae.shape03b.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ }
+ // End Shape for Glyph 193
+
+ shape.setIsQuadraticNurbs();
+ shape.setSharpness(shapesSharpness);
+ region.addOutlineShape(shape, null, rgbaColor);
+
+ box.resize(shape.getBounds());
+ }
+
+ @Override
+ public String getSubString() {
+ return super.getSubString();
+ }
+}
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/testshapes/Glyph03FreeMonoRegular_M.java b/src/demos/com/jogamp/opengl/demos/graph/ui/testshapes/Glyph03FreeMonoRegular_M.java
new file mode 100644
index 000000000..45868762c
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/testshapes/Glyph03FreeMonoRegular_M.java
@@ -0,0 +1,804 @@
+/**
+ * Copyright 2023 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.demos.graph.ui.testshapes;
+
+import com.jogamp.opengl.GL2ES2;
+import com.jogamp.graph.curve.OutlineShape;
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.geom.Vertex;
+import com.jogamp.graph.geom.Vertex.Factory;
+import com.jogamp.graph.ui.gl.Shape;
+
+/**
+ * GPU based resolution independent test object
+ * - FreeMono-Regular, capital case 'M'
+ * - TTF Shape for Glyph 48
+ */
+public class Glyph03FreeMonoRegular_M extends Shape {
+
+ public Glyph03FreeMonoRegular_M(final Factory extends Vertex> factory, final int renderModes) {
+ super(factory, renderModes);
+ }
+
+ @Override
+ protected void clearImpl(final GL2ES2 gl, final RegionRenderer renderer) {
+ }
+
+ @Override
+ protected void destroyImpl(final GL2ES2 gl, final RegionRenderer renderer) {
+ }
+
+ @SuppressWarnings("unused")
+ @Override
+ protected void addShapeToRegion(final GL2ES2 gl, final RegionRenderer renderer) {
+ final OutlineShape shape = new OutlineShape(renderer.getRenderState().getVertexFactory());
+
+ if( false ) {
+ // Start TTF Shape for Glyph 48
+ // GlyphShape<48>: offset 0 of 45/45 points
+ // pM[044] P[483/522, on true, end true]
+ // p0[000] P[326/169, on true, end false]
+ // p1[001] P[280/169, on true, end false]
+ // p2[002] P[121/522, on true, end false]
+ // 000: B0a: move-to p0
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0, 0.326000f, 0.169000f, true);
+ // 000: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.280000f, 0.169000f, true);
+ // GlyphShape<48>: offset 1 of 45/45 points
+ // pM[000] P[326/169, on true, end false]
+ // p0[001] P[280/169, on true, end false]
+ // p1[002] P[121/522, on true, end false]
+ // p2[003] P[113/522, on true, end false]
+ // 001: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.121000f, 0.522000f, true);
+ // GlyphShape<48>: offset 2 of 45/45 points
+ // pM[001] P[280/169, on true, end false]
+ // p0[002] P[121/522, on true, end false]
+ // p1[003] P[113/522, on true, end false]
+ // p2[004] P[113/41, on true, end false]
+ // 002: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.113000f, 0.522000f, true);
+ // GlyphShape<48>: offset 3 of 45/45 points
+ // pM[002] P[121/522, on true, end false]
+ // p0[003] P[113/522, on true, end false]
+ // p1[004] P[113/41, on true, end false]
+ // p2[005] P[187/41, on true, end false]
+ // 003: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.113000f, 0.041000f, true);
+ // GlyphShape<48>: offset 4 of 45/45 points
+ // pM[003] P[113/522, on true, end false]
+ // p0[004] P[113/41, on true, end false]
+ // p1[005] P[187/41, on true, end false]
+ // p2[006] P[215/41, on false, end false]
+ // 004: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.187000f, 0.041000f, true);
+ // GlyphShape<48>: offset 5 of 45/45 points
+ // pM[004] P[113/41, on true, end false]
+ // p0[005] P[187/41, on true, end false]
+ // p1[006] P[215/41, on false, end false]
+ // p2[007] P[215/21, on true, end false]
+ // 005: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.215000f, 0.041000f, false);
+ shape.addVertex(0, 0.215000f, 0.021000f, true);
+ // GlyphShape<48>: offset 7 of 45/45 points
+ // pM[006] P[215/41, on false, end false]
+ // p0[007] P[215/21, on true, end false]
+ // p1[008] P[215/0, on false, end false]
+ // p2[009] P[187/0, on true, end false]
+ // 007: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.215000f, 0.000000f, false);
+ shape.addVertex(0, 0.187000f, 0.000000f, true);
+ // GlyphShape<48>: offset 9 of 45/45 points
+ // pM[008] P[215/0, on false, end false]
+ // p0[009] P[187/0, on true, end false]
+ // p1[010] P[38/0, on true, end false]
+ // p2[011] P[11/0, on false, end false]
+ // 009: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.038000f, 0.000000f, true);
+ // GlyphShape<48>: offset 10 of 45/45 points
+ // pM[009] P[187/0, on true, end false]
+ // p0[010] P[38/0, on true, end false]
+ // p1[011] P[11/0, on false, end false]
+ // p2[012] P[11/21, on true, end false]
+ // 010: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.011000f, 0.000000f, false);
+ shape.addVertex(0, 0.011000f, 0.021000f, true);
+ // GlyphShape<48>: offset 12 of 45/45 points
+ // pM[011] P[11/0, on false, end false]
+ // p0[012] P[11/21, on true, end false]
+ // p1[013] P[11/41, on false, end false]
+ // p2[014] P[38/41, on true, end false]
+ // 012: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.011000f, 0.041000f, false);
+ shape.addVertex(0, 0.038000f, 0.041000f, true);
+ // GlyphShape<48>: offset 14 of 45/45 points
+ // pM[013] P[11/41, on false, end false]
+ // p0[014] P[38/41, on true, end false]
+ // p1[015] P[72/41, on true, end false]
+ // p2[016] P[72/522, on true, end false]
+ // 014: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.072000f, 0.041000f, true);
+ // GlyphShape<48>: offset 15 of 45/45 points
+ // pM[014] P[38/41, on true, end false]
+ // p0[015] P[72/41, on true, end false]
+ // p1[016] P[72/522, on true, end false]
+ // p2[017] P[47/522, on true, end false]
+ // 015: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.072000f, 0.522000f, true);
+ // GlyphShape<48>: offset 16 of 45/45 points
+ // pM[015] P[72/41, on true, end false]
+ // p0[016] P[72/522, on true, end false]
+ // p1[017] P[47/522, on true, end false]
+ // p2[018] P[20/522, on false, end false]
+ // 016: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.047000f, 0.522000f, true);
+ // GlyphShape<48>: offset 17 of 45/45 points
+ // pM[016] P[72/522, on true, end false]
+ // p0[017] P[47/522, on true, end false]
+ // p1[018] P[20/522, on false, end false]
+ // p2[019] P[20/543, on true, end false]
+ // 017: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.020000f, 0.522000f, false);
+ shape.addVertex(0, 0.020000f, 0.543000f, true);
+ // GlyphShape<48>: offset 19 of 45/45 points
+ // pM[018] P[20/522, on false, end false]
+ // p0[019] P[20/543, on true, end false]
+ // p1[020] P[20/563, on false, end false]
+ // p2[021] P[47/563, on true, end false]
+ // 019: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.020000f, 0.563000f, false);
+ shape.addVertex(0, 0.047000f, 0.563000f, true);
+ // GlyphShape<48>: offset 21 of 45/45 points
+ // pM[020] P[20/563, on false, end false]
+ // p0[021] P[47/563, on true, end false]
+ // p1[022] P[146/563, on true, end false]
+ // p2[023] P[303/215, on true, end false]
+ // 021: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.146000f, 0.563000f, true);
+ // GlyphShape<48>: offset 22 of 45/45 points
+ // pM[021] P[47/563, on true, end false]
+ // p0[022] P[146/563, on true, end false]
+ // p1[023] P[303/215, on true, end false]
+ // p2[024] P[457/563, on true, end false]
+ // 022: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.303000f, 0.215000f, true);
+ // GlyphShape<48>: offset 23 of 45/45 points
+ // pM[022] P[146/563, on true, end false]
+ // p0[023] P[303/215, on true, end false]
+ // p1[024] P[457/563, on true, end false]
+ // p2[025] P[557/563, on true, end false]
+ // 023: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.457000f, 0.563000f, true);
+ // GlyphShape<48>: offset 24 of 45/45 points
+ // pM[023] P[303/215, on true, end false]
+ // p0[024] P[457/563, on true, end false]
+ // p1[025] P[557/563, on true, end false]
+ // p2[026] P[584/563, on false, end false]
+ // 024: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.557000f, 0.563000f, true);
+ // GlyphShape<48>: offset 25 of 45/45 points
+ // pM[024] P[457/563, on true, end false]
+ // p0[025] P[557/563, on true, end false]
+ // p1[026] P[584/563, on false, end false]
+ // p2[027] P[584/543, on true, end false]
+ // 025: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.584000f, 0.563000f, false);
+ shape.addVertex(0, 0.584000f, 0.543000f, true);
+ // GlyphShape<48>: offset 27 of 45/45 points
+ // pM[026] P[584/563, on false, end false]
+ // p0[027] P[584/543, on true, end false]
+ // p1[028] P[584/522, on false, end false]
+ // p2[029] P[557/522, on true, end false]
+ // 027: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.584000f, 0.522000f, false);
+ shape.addVertex(0, 0.557000f, 0.522000f, true);
+ // GlyphShape<48>: offset 29 of 45/45 points
+ // pM[028] P[584/522, on false, end false]
+ // p0[029] P[557/522, on true, end false]
+ // p1[030] P[532/522, on true, end false]
+ // p2[031] P[532/41, on true, end false]
+ // 029: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.532000f, 0.522000f, true);
+ // GlyphShape<48>: offset 30 of 45/45 points
+ // pM[029] P[557/522, on true, end false]
+ // p0[030] P[532/522, on true, end false]
+ // p1[031] P[532/41, on true, end false]
+ // p2[032] P[566/41, on true, end false]
+ // 030: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.532000f, 0.041000f, true);
+ // GlyphShape<48>: offset 31 of 45/45 points
+ // pM[030] P[532/522, on true, end false]
+ // p0[031] P[532/41, on true, end false]
+ // p1[032] P[566/41, on true, end false]
+ // p2[033] P[593/41, on false, end false]
+ // 031: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.566000f, 0.041000f, true);
+ // GlyphShape<48>: offset 32 of 45/45 points
+ // pM[031] P[532/41, on true, end false]
+ // p0[032] P[566/41, on true, end false]
+ // p1[033] P[593/41, on false, end false]
+ // p2[034] P[593/21, on true, end false]
+ // 032: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.593000f, 0.041000f, false);
+ shape.addVertex(0, 0.593000f, 0.021000f, true);
+ // GlyphShape<48>: offset 34 of 45/45 points
+ // pM[033] P[593/41, on false, end false]
+ // p0[034] P[593/21, on true, end false]
+ // p1[035] P[593/0, on false, end false]
+ // p2[036] P[566/0, on true, end false]
+ // 034: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.593000f, 0.000000f, false);
+ shape.addVertex(0, 0.566000f, 0.000000f, true);
+ // GlyphShape<48>: offset 36 of 45/45 points
+ // pM[035] P[593/0, on false, end false]
+ // p0[036] P[566/0, on true, end false]
+ // p1[037] P[417/0, on true, end false]
+ // p2[038] P[390/0, on false, end false]
+ // 036: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.417000f, 0.000000f, true);
+ // GlyphShape<48>: offset 37 of 45/45 points
+ // pM[036] P[566/0, on true, end false]
+ // p0[037] P[417/0, on true, end false]
+ // p1[038] P[390/0, on false, end false]
+ // p2[039] P[390/21, on true, end false]
+ // 037: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.390000f, 0.000000f, false);
+ shape.addVertex(0, 0.390000f, 0.021000f, true);
+ // GlyphShape<48>: offset 39 of 45/45 points
+ // pM[038] P[390/0, on false, end false]
+ // p0[039] P[390/21, on true, end false]
+ // p1[040] P[390/41, on false, end false]
+ // p2[041] P[417/41, on true, end false]
+ // 039: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.390000f, 0.041000f, false);
+ shape.addVertex(0, 0.417000f, 0.041000f, true);
+ // GlyphShape<48>: offset 41 of 45/45 points
+ // pM[040] P[390/41, on false, end false]
+ // p0[041] P[417/41, on true, end false]
+ // p1[042] P[491/41, on true, end false]
+ // p2[043] P[491/522, on true, end false]
+ // 041: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.491000f, 0.041000f, true);
+ // GlyphShape<48>: offset 42 of 45/45 points
+ // pM[041] P[417/41, on true, end false]
+ // p0[042] P[491/41, on true, end false]
+ // p1[043] P[491/522, on true, end false]
+ // p2[044] P[483/522, on true, end true]
+ // 042: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.491000f, 0.522000f, true);
+ // GlyphShape<48>: offset 43 of 45/45 points
+ // pM[042] P[491/41, on true, end false]
+ // p0[043] P[491/522, on true, end false]
+ // p1[044] P[483/522, on true, end true]
+ // p2[000] P[326/169, on true, end false]
+ // 043: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.483000f, 0.522000f, true);
+ // GlyphShape<48>: offset 44 of 45/45 points
+ // pM[043] P[491/522, on true, end false]
+ // p0[044] P[483/522, on true, end true]
+ // p1[000] P[326/169, on true, end false]
+ // p2[001] P[280/169, on true, end false]
+ // 044: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.326000f, 0.169000f, true);
+ System.err.println("Glyph03FreeMonoRegular_M.shape01a.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+
+ // End Shape for Glyph 48
+ } else if( false ) {
+ // Start TTF Shape for Glyph 48
+ // GlyphShape<48>: offset 0 of 45/45 points
+ // pM[044] P[483/522, on true, end true]
+ // p0[000] P[326/169, on true, end false]
+ // p1[001] P[280/169, on true, end false]
+ // p2[002] P[121/522, on true, end false]
+ // 000: B0a: move-to p0
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0, 0.326000f, 0.169000f, true);
+ // 000: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.280000f, 0.169000f, true);
+ // GlyphShape<48>: offset 1 of 45/45 points
+ // pM[000] P[326/169, on true, end false]
+ // p0[001] P[280/169, on true, end false]
+ // p1[002] P[121/522, on true, end false]
+ // p2[003] P[113/522, on true, end false]
+ // 001: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.121000f, 0.522000f, true);
+ // GlyphShape<48>: offset 2 of 45/45 points
+ // pM[001] P[280/169, on true, end false]
+ // p0[002] P[121/522, on true, end false]
+ // p1[003] P[113/522, on true, end false]
+ // p2[004] P[113/41, on true, end false]
+ // 002: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.113000f, 0.522000f, true);
+ // GlyphShape<48>: offset 3 of 45/45 points
+ // pM[002] P[121/522, on true, end false]
+ // p0[003] P[113/522, on true, end false]
+ // p1[004] P[113/41, on true, end false]
+ // p2[005] P[187/41, on true, end false]
+ // 003: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.113000f, 0.041000f, true);
+ // GlyphShape<48>: offset 4 of 45/45 points
+ // pM[003] P[113/522, on true, end false]
+ // p0[004] P[113/41, on true, end false]
+ // p1[005] P[187/41, on true, end false]
+ // p2[006] P[215/41, on false, end false]
+ // 004: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.187000f, 0.041000f, true);
+ // GlyphShape<48>: offset 5 of 45/45 points
+ // pM[004] P[113/41, on true, end false]
+ // p0[005] P[187/41, on true, end false]
+ // p1[006] P[215/41, on false, end false]
+ // p2[007] P[215/21, on true, end false]
+ // 005: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.215000f, 0.041000f, true); // curve -> line
+ shape.addVertex(0, 0.215000f, 0.021000f, true);
+ // GlyphShape<48>: offset 7 of 45/45 points
+ // pM[006] P[215/41, on false, end false]
+ // p0[007] P[215/21, on true, end false]
+ // p1[008] P[215/0, on false, end false]
+ // p2[009] P[187/0, on true, end false]
+ // 007: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.215000f, 0.000000f, true); // curve -> line
+ shape.addVertex(0, 0.187000f, 0.000000f, true);
+ // GlyphShape<48>: offset 9 of 45/45 points
+ // pM[008] P[215/0, on false, end false]
+ // p0[009] P[187/0, on true, end false]
+ // p1[010] P[38/0, on true, end false]
+ // p2[011] P[11/0, on false, end false]
+ // 009: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.038000f, 0.000000f, true);
+ // GlyphShape<48>: offset 10 of 45/45 points
+ // pM[009] P[187/0, on true, end false]
+ // p0[010] P[38/0, on true, end false]
+ // p1[011] P[11/0, on false, end false]
+ // p2[012] P[11/21, on true, end false]
+ // 010: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.011000f, 0.000000f, true); // curve -> line
+ shape.addVertex(0, 0.011000f, 0.021000f, true);
+ // GlyphShape<48>: offset 12 of 45/45 points
+ // pM[011] P[11/0, on false, end false]
+ // p0[012] P[11/21, on true, end false]
+ // p1[013] P[11/41, on false, end false]
+ // p2[014] P[38/41, on true, end false]
+ // 012: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.011000f, 0.041000f, true); // curve -> line
+ shape.addVertex(0, 0.038000f, 0.041000f, true);
+ // GlyphShape<48>: offset 14 of 45/45 points
+ // pM[013] P[11/41, on false, end false]
+ // p0[014] P[38/41, on true, end false]
+ // p1[015] P[72/41, on true, end false]
+ // p2[016] P[72/522, on true, end false]
+ // 014: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.072000f, 0.041000f, true);
+ // GlyphShape<48>: offset 15 of 45/45 points
+ // pM[014] P[38/41, on true, end false]
+ // p0[015] P[72/41, on true, end false]
+ // p1[016] P[72/522, on true, end false]
+ // p2[017] P[47/522, on true, end false]
+ // 015: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.072000f, 0.522000f, true);
+ // GlyphShape<48>: offset 16 of 45/45 points
+ // pM[015] P[72/41, on true, end false]
+ // p0[016] P[72/522, on true, end false]
+ // p1[017] P[47/522, on true, end false]
+ // p2[018] P[20/522, on false, end false]
+ // 016: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.047000f, 0.522000f, true);
+ // GlyphShape<48>: offset 17 of 45/45 points
+ // pM[016] P[72/522, on true, end false]
+ // p0[017] P[47/522, on true, end false]
+ // p1[018] P[20/522, on false, end false]
+ // p2[019] P[20/543, on true, end false]
+ // 017: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.020000f, 0.522000f, true); // curve -> line
+ shape.addVertex(0, 0.020000f, 0.543000f, true);
+ // GlyphShape<48>: offset 19 of 45/45 points
+ // pM[018] P[20/522, on false, end false]
+ // p0[019] P[20/543, on true, end false]
+ // p1[020] P[20/563, on false, end false]
+ // p2[021] P[47/563, on true, end false]
+ // 019: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.020000f, 0.563000f, true); // curve -> line
+ shape.addVertex(0, 0.047000f, 0.563000f, true);
+ // GlyphShape<48>: offset 21 of 45/45 points
+ // pM[020] P[20/563, on false, end false]
+ // p0[021] P[47/563, on true, end false]
+ // p1[022] P[146/563, on true, end false]
+ // p2[023] P[303/215, on true, end false]
+ // 021: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.146000f, 0.563000f, true);
+ // GlyphShape<48>: offset 22 of 45/45 points
+ // pM[021] P[47/563, on true, end false]
+ // p0[022] P[146/563, on true, end false]
+ // p1[023] P[303/215, on true, end false]
+ // p2[024] P[457/563, on true, end false]
+ // 022: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.303000f, 0.215000f, true);
+ // GlyphShape<48>: offset 23 of 45/45 points
+ // pM[022] P[146/563, on true, end false]
+ // p0[023] P[303/215, on true, end false]
+ // p1[024] P[457/563, on true, end false]
+ // p2[025] P[557/563, on true, end false]
+ // 023: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.457000f, 0.563000f, true);
+ // GlyphShape<48>: offset 24 of 45/45 points
+ // pM[023] P[303/215, on true, end false]
+ // p0[024] P[457/563, on true, end false]
+ // p1[025] P[557/563, on true, end false]
+ // p2[026] P[584/563, on false, end false]
+ // 024: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.557000f, 0.563000f, true);
+ // GlyphShape<48>: offset 25 of 45/45 points
+ // pM[024] P[457/563, on true, end false]
+ // p0[025] P[557/563, on true, end false]
+ // p1[026] P[584/563, on false, end false]
+ // p2[027] P[584/543, on true, end false]
+ // 025: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.584000f, 0.563000f, true); // curve -> line
+ shape.addVertex(0, 0.584000f, 0.543000f, true);
+ // GlyphShape<48>: offset 27 of 45/45 points
+ // pM[026] P[584/563, on false, end false]
+ // p0[027] P[584/543, on true, end false]
+ // p1[028] P[584/522, on false, end false]
+ // p2[029] P[557/522, on true, end false]
+ // 027: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.584000f, 0.522000f, true); // curve -> line
+ shape.addVertex(0, 0.557000f, 0.522000f, true);
+ // GlyphShape<48>: offset 29 of 45/45 points
+ // pM[028] P[584/522, on false, end false]
+ // p0[029] P[557/522, on true, end false]
+ // p1[030] P[532/522, on true, end false]
+ // p2[031] P[532/41, on true, end false]
+ // 029: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.532000f, 0.522000f, true);
+ // GlyphShape<48>: offset 30 of 45/45 points
+ // pM[029] P[557/522, on true, end false]
+ // p0[030] P[532/522, on true, end false]
+ // p1[031] P[532/41, on true, end false]
+ // p2[032] P[566/41, on true, end false]
+ // 030: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.532000f, 0.041000f, true);
+ // GlyphShape<48>: offset 31 of 45/45 points
+ // pM[030] P[532/522, on true, end false]
+ // p0[031] P[532/41, on true, end false]
+ // p1[032] P[566/41, on true, end false]
+ // p2[033] P[593/41, on false, end false]
+ // 031: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.566000f, 0.041000f, true);
+ // GlyphShape<48>: offset 32 of 45/45 points
+ // pM[031] P[532/41, on true, end false]
+ // p0[032] P[566/41, on true, end false]
+ // p1[033] P[593/41, on false, end false]
+ // p2[034] P[593/21, on true, end false]
+ // 032: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.593000f, 0.041000f, true); // curve -> line
+ shape.addVertex(0, 0.593000f, 0.021000f, true);
+ // GlyphShape<48>: offset 34 of 45/45 points
+ // pM[033] P[593/41, on false, end false]
+ // p0[034] P[593/21, on true, end false]
+ // p1[035] P[593/0, on false, end false]
+ // p2[036] P[566/0, on true, end false]
+ // 034: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.593000f, 0.000000f, true); // curve -> line
+ shape.addVertex(0, 0.566000f, 0.000000f, true);
+ // GlyphShape<48>: offset 36 of 45/45 points
+ // pM[035] P[593/0, on false, end false]
+ // p0[036] P[566/0, on true, end false]
+ // p1[037] P[417/0, on true, end false]
+ // p2[038] P[390/0, on false, end false]
+ // 036: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.417000f, 0.000000f, true);
+ // GlyphShape<48>: offset 37 of 45/45 points
+ // pM[036] P[566/0, on true, end false]
+ // p0[037] P[417/0, on true, end false]
+ // p1[038] P[390/0, on false, end false]
+ // p2[039] P[390/21, on true, end false]
+ // 037: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.390000f, 0.000000f, true); // curve -> line
+ shape.addVertex(0, 0.390000f, 0.021000f, true);
+ // GlyphShape<48>: offset 39 of 45/45 points
+ // pM[038] P[390/0, on false, end false]
+ // p0[039] P[390/21, on true, end false]
+ // p1[040] P[390/41, on false, end false]
+ // p2[041] P[417/41, on true, end false]
+ // 039: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.390000f, 0.041000f, true); // curve -> line
+ shape.addVertex(0, 0.417000f, 0.041000f, true);
+ // GlyphShape<48>: offset 41 of 45/45 points
+ // pM[040] P[390/41, on false, end false]
+ // p0[041] P[417/41, on true, end false]
+ // p1[042] P[491/41, on true, end false]
+ // p2[043] P[491/522, on true, end false]
+ // 041: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.491000f, 0.041000f, true);
+ // GlyphShape<48>: offset 42 of 45/45 points
+ // pM[041] P[417/41, on true, end false]
+ // p0[042] P[491/41, on true, end false]
+ // p1[043] P[491/522, on true, end false]
+ // p2[044] P[483/522, on true, end true]
+ // 042: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.491000f, 0.522000f, true);
+ // GlyphShape<48>: offset 43 of 45/45 points
+ // pM[042] P[491/41, on true, end false]
+ // p0[043] P[491/522, on true, end false]
+ // p1[044] P[483/522, on true, end true]
+ // p2[000] P[326/169, on true, end false]
+ // 043: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.483000f, 0.522000f, true);
+ // GlyphShape<48>: offset 44 of 45/45 points
+ // pM[043] P[491/522, on true, end false]
+ // p0[044] P[483/522, on true, end true]
+ // p1[000] P[326/169, on true, end false]
+ // p2[001] P[280/169, on true, end false]
+ // 044: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.326000f, 0.169000f, true);
+ System.err.println("Glyph03FreeMonoRegular_M.shape01a.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ } else {
+ final boolean with_left_leg = true; // ERROR
+ final boolean with_right_leg = false; // OK
+
+ // Start TTF Shape for Glyph 48
+ // GlyphShape<48>: offset 0 of 45/45 points
+ // pM[044] P[483/522, on true, end true]
+ // p0[000] P[326/169, on true, end false]
+ // p1[001] P[280/169, on true, end false]
+ // p2[002] P[121/522, on true, end false]
+ // 000: B0a: move-to p0
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0, 0.326000f, 0.169000f, true);
+ // 000: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.280000f, 0.169000f, true);
+ // GlyphShape<48>: offset 1 of 45/45 points
+ // pM[000] P[326/169, on true, end false]
+ // p0[001] P[280/169, on true, end false]
+ // p1[002] P[121/522, on true, end false]
+ // p2[003] P[113/522, on true, end false]
+ // 001: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.121000f, 0.522000f, true); // ID 11
+
+ // GlyphShape<48>: offset 2 of 45/45 points
+ // pM[001] P[280/169, on true, end false]
+ // p0[002] P[121/522, on true, end false]
+ // p1[003] P[113/522, on true, end false]
+ // p2[004] P[113/41, on true, end false]
+ // 002: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.113000f, 0.522000f, true);
+
+ if( with_left_leg ) {
+ // GlyphShape<48>: offset 3 of 45/45 points
+ // pM[002] P[121/522, on true, end false]
+ // p0[003] P[113/522, on true, end false]
+ // p1[004] P[113/41, on true, end false]
+ // p2[005] P[187/41, on true, end false]
+ // 003: B1: line-to p0-p1
+ // Shape.LineTo:
+ // shape.addVertex(0, 0.113000f, 0.041000f, true);
+
+ shape.addVertex(0, 0.113000f, 0.000000f, true);
+ shape.addVertex(0, 0.072000f, 0.000000f, true);
+
+ // GlyphShape<48>: offset 14 of 45/45 points
+ // pM[013] P[11/41, on false, end false]
+ // p0[014] P[38/41, on true, end false]
+ // p1[015] P[72/41, on true, end false]
+ // p2[016] P[72/522, on true, end false]
+ // 014: B1: line-to p0-p1
+ // Shape.LineTo:
+ // shape.addVertex(0, 0.072000f, 0.041000f, true);
+
+ // GlyphShape<48>: offset 15 of 45/45 points
+ // pM[014] P[38/41, on true, end false]
+ // p0[015] P[72/41, on true, end false]
+ // p1[016] P[72/522, on true, end false]
+ // p2[017] P[47/522, on true, end false]
+ // 015: B1: line-to p0-p1
+ // Shape.LineTo:
+ // shape.addVertex(0, 0.072000f, 0.522000f, true);
+
+ shape.addVertex(0, 0.072000f, 0.563000f, true); // ID 7
+ } else {
+ shape.addVertex(0, 0.113000f, 0.563000f, true);
+ }
+
+ // GlyphShape<48>: offset 21 of 45/45 points
+ // pM[020] P[20/563, on false, end false]
+ // p0[021] P[47/563, on true, end false]
+ // p1[022] P[146/563, on true, end false]
+ // p2[023] P[303/215, on true, end false]
+ // 021: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.146000f, 0.563000f, true);
+ // GlyphShape<48>: offset 22 of 45/45 points
+ // pM[021] P[47/563, on true, end false]
+ // p0[022] P[146/563, on true, end false]
+ // p1[023] P[303/215, on true, end false]
+ // p2[024] P[457/563, on true, end false]
+ // 022: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.303000f, 0.215000f, true);
+ // GlyphShape<48>: offset 23 of 45/45 points
+ // pM[022] P[146/563, on true, end false]
+ // p0[023] P[303/215, on true, end false]
+ // p1[024] P[457/563, on true, end false]
+ // p2[025] P[557/563, on true, end false]
+ // 023: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.457000f, 0.563000f, true); // ID 4
+
+ if( with_right_leg ) {
+ shape.addVertex(0, 0.532000f, 0.563000f, true);
+
+ // GlyphShape<48>: offset 29 of 45/45 points
+ // pM[028] P[584/522, on false, end false]
+ // p0[029] P[557/522, on true, end false]
+ // p1[030] P[532/522, on true, end false]
+ // p2[031] P[532/41, on true, end false]
+ // 029: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.532000f, 0.522000f, true);
+ // GlyphShape<48>: offset 30 of 45/45 points
+ // pM[029] P[557/522, on true, end false]
+ // p0[030] P[532/522, on true, end false]
+ // p1[031] P[532/41, on true, end false]
+ // p2[032] P[566/41, on true, end false]
+ // 030: B1: line-to p0-p1
+ // Shape.LineTo:
+ // shape.addVertex(0, 0.532000f, 0.041000f, true);
+
+ shape.addVertex(0, 0.532000f, 0.000000f, true);
+ shape.addVertex(0, 0.491000f, 0.000000f, true);
+ } else {
+ shape.addVertex(0, 0.491000f, 0.563000f, true); // ID 3
+ }
+
+ // GlyphShape<48>: offset 41 of 45/45 points
+ // pM[040] P[390/41, on false, end false]
+ // p0[041] P[417/41, on true, end false]
+ // p1[042] P[491/41, on true, end false]
+ // p2[043] P[491/522, on true, end false]
+ // 041: B1: line-to p0-p1
+ // Shape.LineTo:
+ // shape.addVertex(0, 0.491000f, 0.041000f, true);
+
+ // GlyphShape<48>: offset 42 of 45/45 points
+ // pM[041] P[417/41, on true, end false]
+ // p0[042] P[491/41, on true, end false]
+ // p1[043] P[491/522, on true, end false]
+ // p2[044] P[483/522, on true, end true]
+ // 042: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.491000f, 0.522000f, true); // ID 2
+ // GlyphShape<48>: offset 43 of 45/45 points
+ // pM[042] P[491/41, on true, end false]
+ // p0[043] P[491/522, on true, end false]
+ // p1[044] P[483/522, on true, end true]
+ // p2[000] P[326/169, on true, end false]
+ // 043: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.483000f, 0.522000f, true); // ID 1
+ // GlyphShape<48>: offset 44 of 45/45 points
+ // pM[043] P[491/522, on true, end false]
+ // p0[044] P[483/522, on true, end true]
+ // p1[000] P[326/169, on true, end false]
+ // p2[001] P[280/169, on true, end false]
+ // 044: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.326000f, 0.169000f, true);
+ System.err.println("Glyph03FreeMonoRegular_M.shape01a.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ }
+
+ shape.setIsQuadraticNurbs();
+ shape.setSharpness(shapesSharpness);
+ region.addOutlineShape(shape, null, rgbaColor);
+
+ box.resize(shape.getBounds());
+ }
+
+ @Override
+ public String getSubString() {
+ return super.getSubString();
+ }
+}
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/testshapes/Glyph04FreeSans_0.java b/src/demos/com/jogamp/opengl/demos/graph/ui/testshapes/Glyph04FreeSans_0.java
new file mode 100644
index 000000000..63bc0b5b5
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/testshapes/Glyph04FreeSans_0.java
@@ -0,0 +1,150 @@
+/**
+ * Copyright 2023 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.demos.graph.ui.testshapes;
+
+import com.jogamp.opengl.GL2ES2;
+import com.jogamp.graph.curve.OutlineShape;
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.geom.Vertex;
+import com.jogamp.graph.geom.Vertex.Factory;
+import com.jogamp.graph.ui.gl.Shape;
+
+/**
+ * GPU based resolution independent test object
+ * - FreeSans, '0'
+ * - TTF Shape for Glyph 19
+ */
+public class Glyph04FreeSans_0 extends Shape {
+
+ public Glyph04FreeSans_0(final Factory extends Vertex> factory, final int renderModes) {
+ super(factory, renderModes);
+ }
+
+ @Override
+ protected void clearImpl(final GL2ES2 gl, final RegionRenderer renderer) {
+ }
+
+ @Override
+ protected void destroyImpl(final GL2ES2 gl, final RegionRenderer renderer) {
+ }
+
+ @Override
+ protected void addShapeToRegion(final GL2ES2 gl, final RegionRenderer renderer) {
+ final OutlineShape shape = new OutlineShape(renderer.getRenderState().getVertexFactory());
+
+ // Start TTF Shape for Glyph 19
+ // 000: B0a: move-to p0
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0, 0.043000f, 0.343000f, true);
+ // 000: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.043000f, 0.432000f, false);
+ shape.addVertex(0, 0.058000f, 0.500000f, true);
+ // 002: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.073000f, 0.568000f, false);
+ shape.addVertex(0, 0.096000f, 0.606000f, true);
+ // 003: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.119000f, 0.645000f, false);
+ shape.addVertex(0, 0.151000f, 0.669000f, true);
+ // 004: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.183000f, 0.693000f, false);
+ shape.addVertex(0, 0.212000f, 0.701000f, true);
+ // 005: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.242000f, 0.709000f, false);
+ shape.addVertex(0, 0.275000f, 0.709000f, true);
+ // 006: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.507000f, 0.709000f, false);
+ shape.addVertex(0, 0.507000f, 0.337000f, true);
+ // 008: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.507000f, 0.162000f, false);
+ shape.addVertex(0, 0.448000f, 0.070000f, true);
+ // 010: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.388000f, -0.023000f, false);
+ shape.addVertex(0, 0.275000f, -0.023000f, true);
+ // 011: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.161000f, -0.023000f, false);
+ shape.addVertex(0, 0.102000f, 0.070000f, true);
+ // 013: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.043000f, 0.164000f, false);
+ shape.addVertex(0, 0.043000f, 0.343000f, true);
+ System.err.println("Glyph04FreeSans_0.shape01a.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+
+ // 021: B0b: move-to pM
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0, 0.417000f, 0.345000f, true);
+ // 021: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.417000f, 0.631000f, false);
+ shape.addVertex(0, 0.275000f, 0.631000f, true);
+ // 015: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.133000f, 0.631000f, false);
+ shape.addVertex(0, 0.133000f, 0.342000f, true);
+ // 016: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.133000f, 0.050000f, false);
+ shape.addVertex(0, 0.273000f, 0.050000f, true);
+ // 018: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.347000f, 0.050000f, false);
+ shape.addVertex(0, 0.382000f, 0.122000f, true);
+ // 020: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.417000f, 0.194000f, false);
+ shape.addVertex(0, 0.417000f, 0.345000f, true);
+ System.err.println("Glyph04FreeSans_0.shape02a.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+
+ // End Shape for Glyph 19
+
+ shape.setIsQuadraticNurbs();
+ shape.setSharpness(shapesSharpness);
+ region.addOutlineShape(shape, null, rgbaColor);
+
+ box.resize(shape.getBounds());
+ }
+
+ @Override
+ public String getSubString() {
+ return super.getSubString();
+ }
+}
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/testshapes/Glyph05FreeSerifBoldItalic_ae.java b/src/demos/com/jogamp/opengl/demos/graph/ui/testshapes/Glyph05FreeSerifBoldItalic_ae.java
new file mode 100644
index 000000000..dd92228ff
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/testshapes/Glyph05FreeSerifBoldItalic_ae.java
@@ -0,0 +1,287 @@
+/**
+ * Copyright 2023 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.demos.graph.ui.testshapes;
+
+import com.jogamp.opengl.GL2ES2;
+import com.jogamp.graph.curve.OutlineShape;
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.geom.Vertex;
+import com.jogamp.graph.geom.Vertex.Factory;
+import com.jogamp.graph.ui.gl.Shape;
+
+/**
+ * GPU based resolution independent test object
+ * - FreeSans, '0'
+ * - TTF Shape for Glyph 19
+ */
+public class Glyph05FreeSerifBoldItalic_ae extends Shape {
+
+ public Glyph05FreeSerifBoldItalic_ae(final Factory extends Vertex> factory, final int renderModes) {
+ super(factory, renderModes);
+ }
+
+ @Override
+ protected void clearImpl(final GL2ES2 gl, final RegionRenderer renderer) {
+ }
+
+ @Override
+ protected void destroyImpl(final GL2ES2 gl, final RegionRenderer renderer) {
+ }
+
+ @Override
+ protected void addShapeToRegion(final GL2ES2 gl, final RegionRenderer renderer) {
+ final OutlineShape shape = new OutlineShape(renderer.getRenderState().getVertexFactory());
+
+ // Start TTF Shape for Glyph 168
+ // 000: B0a: move-to p0
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0, 0.450000f, -0.013000f, true);
+ // 000: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.386000f, -0.013000f, false);
+ shape.addVertex(0, 0.353000f, 0.018000f, true);
+ // 002: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.319000f, 0.049000f, false);
+ shape.addVertex(0, 0.307000f, 0.118000f, true);
+ // 003: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.265000f, 0.049000f, false);
+ shape.addVertex(0, 0.225000f, 0.019000f, true);
+ // 005: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.184000f, -0.012000f, false);
+ shape.addVertex(0, 0.134000f, -0.012000f, true);
+ // 006: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.085000f, -0.012000f, false);
+ shape.addVertex(0, 0.053000f, 0.021000f, true);
+ // 008: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.020000f, 0.055000f, false);
+ shape.addVertex(0, 0.020000f, 0.106000f, true);
+ // 009: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.020000f, 0.185000f, false);
+ shape.addVertex(0, 0.062000f, 0.269000f, true);
+ // 011: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.105000f, 0.353000f, false);
+ shape.addVertex(0, 0.170000f, 0.407000f, true);
+ // 012: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.235000f, 0.462000f, false);
+ shape.addVertex(0, 0.296000f, 0.462000f, true);
+ // 013: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.328000f, 0.462000f, false);
+ shape.addVertex(0, 0.346000f, 0.448000f, true);
+ // 015: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.364000f, 0.433000f, false);
+ shape.addVertex(0, 0.377000f, 0.396000f, true);
+ // 016: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.395000f, 0.454000f, true);
+ // 017: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.498000f, 0.459000f, true);
+ // 018: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.478000f, 0.394000f, true);
+ // 019: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.510000f, 0.431000f, false);
+ shape.addVertex(0, 0.535000f, 0.445000f, true);
+ // 021: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.561000f, 0.459000f, false);
+ shape.addVertex(0, 0.598000f, 0.459000f, true);
+ // 022: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.645000f, 0.459000f, false);
+ shape.addVertex(0, 0.671000f, 0.436000f, true);
+ // 024: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.698000f, 0.413000f, false);
+ shape.addVertex(0, 0.698000f, 0.372000f, true);
+ // 025: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.698000f, 0.310000f, false);
+ shape.addVertex(0, 0.639000f, 0.263000f, true);
+ // 027: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.579000f, 0.215000f, false);
+ shape.addVertex(0, 0.470000f, 0.190000f, true);
+ // 028: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.431000f, 0.181000f, true);
+ // 029: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.426000f, 0.156000f, false);
+ shape.addVertex(0, 0.426000f, 0.134000f, true);
+ // 031: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.426000f, 0.096000f, false);
+ shape.addVertex(0, 0.444000f, 0.073000f, true);
+ // 033: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.462000f, 0.050000f, false);
+ shape.addVertex(0, 0.493000f, 0.050000f, true);
+ // 034: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.565000f, 0.050000f, false);
+ shape.addVertex(0, 0.616000f, 0.139000f, true);
+ // 036: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.644000f, 0.122000f, true);
+ // 037: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.578000f, -0.013000f, false);
+ shape.addVertex(0, 0.450000f, -0.013000f, true);
+ System.err.println("Glyph05FreeSerifBoldItalic_ae.shape01a.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+
+ // 039: B0a: move-to p0
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0, 0.194000f, 0.058000f, true);
+ // 039: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.238000f, 0.058000f, false);
+ shape.addVertex(0, 0.278000f, 0.122000f, true);
+ // 041: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.319000f, 0.187000f, false);
+ shape.addVertex(0, 0.338000f, 0.256000f, true);
+ // 042: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.358000f, 0.326000f, false);
+ shape.addVertex(0, 0.358000f, 0.363000f, true);
+ // 043: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.358000f, 0.387000f, false);
+ shape.addVertex(0, 0.345000f, 0.403000f, true);
+ // 045: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.331000f, 0.419000f, false);
+ shape.addVertex(0, 0.310000f, 0.419000f, true);
+ // 046: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.267000f, 0.419000f, false);
+ shape.addVertex(0, 0.227000f, 0.356000f, true);
+ // 048: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.187000f, 0.293000f, false);
+ shape.addVertex(0, 0.167000f, 0.225000f, true);
+ // 049: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.146000f, 0.156000f, false);
+ shape.addVertex(0, 0.146000f, 0.119000f, true);
+ // 050: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.146000f, 0.092000f, false);
+ shape.addVertex(0, 0.159000f, 0.075000f, true);
+ // 052: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.172000f, 0.058000f, false);
+ shape.addVertex(0, 0.194000f, 0.058000f, true);
+ System.err.println("Glyph05FreeSerifBoldItalic_ae.shape02a.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+
+ if( true ) {
+ // GlyphShape<168>: offset 0 of 8/61 points
+ // pM[060] P[443/231, on true, end true]
+ // p0[053] P[438/214, on true, end false]
+ // p1[054] P[498/223, on false, end false]
+ // p2[055] P[608/320, on false, end false]
+ // 053: B0a: move-to p0
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0, 0.438000f, 0.214000f, true);
+ // 053: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.498000f, 0.223000f, false);
+ shape.addVertex(0, 0.553000f, 0.271000f, true);
+ // GlyphShape<168>: offset 2 of 8/61 points
+ // pM[054] P[498/223, on false, end false]
+ // p0[055] P[608/320, on false, end false]
+ // p1[056] P[608/388, on true, end false]
+ // p2[057] P[608/429, on false, end false]
+ // 055: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.608000f, 0.320000f, false);
+ shape.addVertex(0, 0.608000f, 0.388000f, true);
+ // GlyphShape<168>: offset 3 of 8/61 points
+ // pM[055] P[608/320, on false, end false]
+ // p0[056] P[608/388, on true, end false]
+ // p1[057] P[608/429, on false, end false]
+ // p2[058] P[575/429, on true, end false]
+ // 056: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.608000f, 0.429000f, false);
+ shape.addVertex(0, 0.575000f, 0.429000f, true);
+ // GlyphShape<168>: offset 5 of 8/61 points
+ // pM[057] P[608/429, on false, end false]
+ // p0[058] P[575/429, on true, end false]
+ // p1[059] P[502/429, on false, end false]
+ // p2[060] P[443/231, on true, end true]
+ // 058: B2: quad-to p0-p1-p2
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.502000f, 0.429000f, false);
+ shape.addVertex(0, 0.443000f, 0.231000f, true);
+ // GlyphShape<168>: offset 7 of 8/61 points
+ // pM[059] P[502/429, on false, end false]
+ // p0[060] P[443/231, on true, end true]
+ // p1[053] P[438/214, on true, end false]
+ // p2[054] P[498/223, on false, end false]
+ // 060: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.438000f, 0.214000f, true);
+ System.err.println("Glyph05FreeSerifBoldItalic_ae.shape03a.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ }
+
+ // End Shape for Glyph 168
+
+ shape.setIsQuadraticNurbs();
+ shape.setSharpness(shapesSharpness);
+ region.addOutlineShape(shape, null, rgbaColor);
+
+ box.resize(shape.getBounds());
+ }
+
+ @Override
+ public String getSubString() {
+ return super.getSubString();
+ }
+}
diff --git a/src/demos/com/jogamp/opengl/demos/util/MiscUtils.java b/src/demos/com/jogamp/opengl/demos/util/MiscUtils.java
new file mode 100644
index 000000000..f1c367e95
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/util/MiscUtils.java
@@ -0,0 +1,259 @@
+/**
+ * Copyright 2010 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.demos.util;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.lang.reflect.*;
+import java.nio.FloatBuffer;
+import java.util.Iterator;
+import java.util.List;
+
+import com.jogamp.opengl.GLContext;
+
+import com.jogamp.common.os.Platform;
+import com.jogamp.common.util.InterruptSource;
+
+public class MiscUtils {
+ public static boolean atob(final String str, final boolean def) {
+ try {
+ return Boolean.parseBoolean(str);
+ } catch (final Exception ex) {
+ ex.printStackTrace();
+ }
+ return def;
+ }
+
+ public static int atoi(final String str, final int def) {
+ try {
+ return Integer.parseInt(str);
+ } catch (final Exception ex) {
+ ex.printStackTrace();
+ }
+ return def;
+ }
+
+ public static long atol(final String str, final long def) {
+ try {
+ return Long.parseLong(str);
+ } catch (final Exception ex) {
+ ex.printStackTrace();
+ }
+ return def;
+ }
+
+ public static float atof(final String str, final float def) {
+ try {
+ return Float.parseFloat(str);
+ } catch (final Exception ex) {
+ ex.printStackTrace();
+ }
+ return def;
+ }
+
+ public static String toHexString(final byte hex) {
+ return "0x" + Integer.toHexString( hex & 0x000000FF );
+ }
+
+ public static String toHexString(final short hex) {
+ return "0x" + Integer.toHexString( hex & 0x0000FFFF );
+ }
+
+ public static String toHexString(final int hex) {
+ return "0x" + Integer.toHexString( hex );
+ }
+
+ public static String toHexString(final long hex) {
+ return "0x" + Long.toHexString( hex );
+ }
+
+ public static void assertFloatBufferEquals(final String errmsg, final FloatBuffer expected, final FloatBuffer actual, final float delta) {
+ if(null == expected && null == actual) {
+ return;
+ }
+ final String msg = null != errmsg ? errmsg + " " : "";
+ if(null == expected) {
+ throw new AssertionError(msg+"; Expected is null, but actual not: "+actual);
+ }
+ if(null == actual) {
+ throw new AssertionError(msg+"; Actual is null, but expected not: "+expected);
+ }
+ if(expected.remaining() != actual.remaining()) {
+ throw new AssertionError(msg+"; Expected has "+expected.remaining()+" remaining, but actual has "+actual.remaining());
+ }
+ final int a0 = expected.position();
+ final int b0 = actual.position();
+ for(int i=0; i delta ) {
+ throw new AssertionError(msg+"; Expected @ ["+a0+"+"+i+"] has "+ai+", but actual @ ["+b0+"+"+i+"] has "+bi+", it's delta "+daibi+" > "+delta);
+ }
+ }
+ }
+
+ public static void assertFloatBufferNotEqual(final String errmsg, final FloatBuffer expected, final FloatBuffer actual, final float delta) {
+ if(null == expected || null == actual) {
+ return;
+ }
+ if(expected.remaining() != actual.remaining()) {
+ return;
+ }
+ final String msg = null != errmsg ? errmsg + " " : "";
+ final int a0 = expected.position();
+ final int b0 = actual.position();
+ for(int i=0; i delta ) {
+ return;
+ }
+ }
+ throw new AssertionError(msg+"; Expected and actual are equal.");
+ }
+
+ public static boolean setFieldIfExists(final Object instance, final String fieldName, final Object value) {
+ try {
+ final Field f = instance.getClass().getField(fieldName);
+ if(value instanceof Boolean || f.getType().isInstance(value)) {
+ f.set(instance, value);
+ return true;
+ } else {
+ System.out.println(instance.getClass()+" '"+fieldName+"' field not assignable with "+value.getClass()+", it's a: "+f.getType());
+ }
+ } catch (final IllegalAccessException ex) {
+ throw new RuntimeException(ex);
+ } catch (final NoSuchFieldException nsfe) {
+ // OK - throw new RuntimeException(instance.getClass()+" has no '"+fieldName+"' field", nsfe);
+ }
+ return false;
+ }
+
+ public static void waitForKey(final String preMessage) {
+ final BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
+ System.err.println(preMessage+"> Press enter to continue");
+ try {
+ System.err.println(stdin.readLine());
+ } catch (final IOException e) { e.printStackTrace(); }
+ }
+
+
+ public static class StreamDump extends InterruptSource.Thread {
+ final InputStream is;
+ final StringBuilder outString;
+ final OutputStream outStream;
+ final String prefix;
+ final Object sync;
+ volatile boolean eos = false;
+
+ public StreamDump(final OutputStream out, final String prefix, final InputStream is, final Object sync) {
+ this.is = is;
+ this.outString = null;
+ this.outStream = out;
+ this.prefix = prefix;
+ this.sync = sync;
+ }
+ public StreamDump(final StringBuilder sb, final String prefix, final InputStream is, final Object sync) {
+ this.is = is;
+ this.outString = sb;
+ this.outStream = null;
+ this.prefix = prefix;
+ this.sync = sync;
+ }
+ public StreamDump(final StringBuilder sb, final InputStream is, final Object sync) {
+ this.is = is;
+ this.outString = sb;
+ this.outStream = null;
+ this.prefix = null;
+ this.sync = sync;
+ }
+
+ public final boolean eos() { return eos; }
+
+ @Override
+ public void run() {
+ synchronized ( sync ) {
+ try {
+ final BufferedReader in = new BufferedReader( new InputStreamReader(is) );
+ String line = null;
+ while ((line = in.readLine()) != null) {
+ if( null != outString ) {
+ outString.append(line).append(Platform.getNewline());
+ } else if( null != outStream ) {
+ if( null != prefix ) {
+ outStream.write(prefix.getBytes());
+ }
+ outStream.write(line.getBytes());
+ outStream.write(Platform.getNewline().getBytes());
+ outStream.flush();
+ }
+ }
+ } catch (final IOException ioe) {
+ System.err.println("Caught "+ioe.getClass().getName()+": "+ioe.getMessage());
+ ioe.printStackTrace();
+ } finally {
+ eos = true;
+ sync.notifyAll();
+ }
+ }
+ }
+ }
+
+ public static void dumpSharedGLContext(final String prefix, final GLContext self) {
+ int i = 0, j = 0;
+ final GLContext master = self.getSharedMaster();
+ final int masterHash = null != master ? master.hashCode() : 0;
+ System.err.println(prefix+": hash 0x"+Integer.toHexString(self.hashCode())+", \t(isShared "+self.isShared()+", created "+self.isCreated()+", master 0x"+Integer.toHexString(masterHash)+")");
+ {
+ final List set = self.getCreatedShares();
+ for (final Iterator iter = set.iterator(); iter.hasNext(); ) {
+ final GLContext c = iter.next();
+ System.err.println(" Created Ctx #"+(i++)+": hash 0x"+Integer.toHexString(c.hashCode())+", \t(created "+c.isCreated()+")");
+ }
+ }
+ {
+ final List set = self.getDestroyedShares();
+ for (final Iterator iter = set.iterator(); iter.hasNext(); ) {
+ final GLContext c = iter.next();
+ System.err.println(" Destroyed Ctx #"+(j++)+": hash 0x"+Integer.toHexString(c.hashCode())+", \t(created "+c.isCreated()+")");
+ }
+ }
+ System.err.println("\t Total created "+i+" + destroyed "+j+" = "+(i+j));
+ System.err.println();
+ }
+}
+
+
+
diff --git a/src/demos/com/jogamp/opengl/demos/util/QuitAdapter.java b/src/demos/com/jogamp/opengl/demos/util/QuitAdapter.java
new file mode 100644
index 000000000..b7c3a6c22
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/util/QuitAdapter.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright 2010 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.demos.util;
+
+import com.jogamp.newt.event.*;
+
+public class QuitAdapter extends WindowAdapter implements WindowListener, KeyListener {
+ boolean shouldQuit = false;
+ boolean enabled = true;
+
+ public void enable(final boolean v) { enabled = v; }
+
+ public void clear() { shouldQuit = false; }
+
+ public boolean shouldQuit() { return shouldQuit; }
+ public void doQuit() { shouldQuit=true; }
+
+ public void windowDestroyNotify(final WindowEvent e) {
+ if( enabled ) {
+ System.err.println("QUIT Window "+Thread.currentThread());
+ shouldQuit = true;
+ }
+ }
+
+ public void keyReleased(final KeyEvent e) {
+ if( !e.isPrintableKey() || e.isAutoRepeat() ) {
+ return;
+ }
+ if( enabled ) {
+ if(e.getKeyChar()=='q') {
+ System.err.println("QUIT Key "+Thread.currentThread());
+ shouldQuit = true;
+ }
+ }
+ }
+ public void keyPressed(final KeyEvent e) {}
+}
+
--
cgit v1.2.3