JOGL v2.6.0-rc-20250706
JOGL, High-Performance Graphics Binding for Java™ (public API).
FontView01.java
Go to the documentation of this file.
1/**
2 * Copyright 2023-2024 JogAmp Community. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification, are
5 * permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * The views and conclusions contained in the software and documentation are those of the
25 * authors and should not be interpreted as representing official policies, either expressed
26 * or implied, of JogAmp Community.
27 */
28package com.jogamp.opengl.demos.graph.ui;
29
30import java.io.File;
31import java.io.IOException;
32import java.util.ArrayList;
33import java.util.List;
34import java.util.Locale;
35
36import com.jogamp.common.os.Clock;
37import com.jogamp.common.util.IOUtil;
38import com.jogamp.graph.curve.OutlineShape;
39import com.jogamp.graph.curve.Region;
40import com.jogamp.graph.font.Font;
41import com.jogamp.graph.font.Font.Glyph;
42import com.jogamp.graph.font.FontFactory;
43import com.jogamp.graph.font.FontScale;
44import com.jogamp.graph.ui.Group;
45import com.jogamp.graph.ui.Scene;
46import com.jogamp.graph.ui.Shape;
47import com.jogamp.graph.ui.TooltipShape;
48import com.jogamp.graph.ui.TooltipText;
49import com.jogamp.graph.ui.layout.Alignment;
50import com.jogamp.graph.ui.layout.BoxLayout;
51import com.jogamp.graph.ui.layout.Gap;
52import com.jogamp.graph.ui.layout.GridLayout;
53import com.jogamp.graph.ui.layout.Margin;
54import com.jogamp.graph.ui.layout.Padding;
55import com.jogamp.graph.ui.shapes.GlyphShape;
56import com.jogamp.graph.ui.shapes.Label;
57import com.jogamp.graph.ui.shapes.Rectangle;
58import com.jogamp.graph.ui.widgets.RangeSlider;
59import com.jogamp.graph.ui.widgets.RangedGroup;
60import com.jogamp.graph.ui.widgets.RangedGroup.SliderParam;
61import com.jogamp.math.FloatUtil;
62import com.jogamp.math.Vec2f;
63import com.jogamp.math.Vec3f;
64import com.jogamp.math.Vec4f;
65import com.jogamp.math.geom.AABBox;
66import com.jogamp.newt.event.KeyAdapter;
67import com.jogamp.newt.event.KeyEvent;
68import com.jogamp.newt.event.MouseEvent;
69import com.jogamp.newt.event.WindowAdapter;
70import com.jogamp.newt.event.WindowEvent;
71import com.jogamp.newt.opengl.GLWindow;
72import com.jogamp.opengl.GL;
73import com.jogamp.opengl.GLAutoDrawable;
74import com.jogamp.opengl.GLCapabilities;
75import com.jogamp.opengl.GLCapabilitiesImmutable;
76import com.jogamp.opengl.GLEventAdapter;
77import com.jogamp.opengl.GLProfile;
78import com.jogamp.opengl.demos.graph.FontSetDemos;
79import com.jogamp.opengl.demos.util.CommandlineOptions;
80import com.jogamp.opengl.demos.util.MiscUtils;
81import com.jogamp.opengl.util.Animator;
82
83/**
84 * This may become a little font viewer application, having FontForge as its role model.
85 * <p>
86 * Notable: The actual {@link GlyphShape} created for the glyph-grid {@link Group}
87 * is reused as-is in the top-right info-box as well as in the {@link TooltipShape}.
88 * </p>
89 * <p>
90 * This is possible only when not modifying the scale or position of the {@link GlyphShape},
91 * achieved by simply wrapping it in a {@link Group}.
92 * The latter gets scaled and translated when dropped
93 * into each target {@link Group} with a {@link Group.Layout}.<br/>
94 * </p>
95 * <p>
96 * This is also good example using GraphUI with a Directed Acyclic Graph (DAG) arrangement.
97 * </p>
98 */
99public class FontView01 {
100 private static final float GlyphGridWidth = 3/4f; // FBO AA: 3/4f = 0.75f dropped fine grid lines @ 0.2f thickness; 0.70f OK
101 private static final float GlyphGridBorderThickness = 0.02f; // thickness 0.2f dropping
102 private static final Vec4f GlyphGridBorderColorComplex = new Vec4f(0.2f, 0.2f, 0.2f, 1);
103 private static final Vec4f GlyphGridBorderColorSimple = new Vec4f(0.2f, 0.2f, 0.7f, 1);
104
105 // static CommandlineOptions options = new CommandlineOptions(1280, 720, Region.MSAA_RENDERING_BIT, Region.DEFAULT_AA_QUALITY, 4);
106 // static CommandlineOptions options = new CommandlineOptions(1280, 720, Region.VBAA_RENDERING_BIT);
107 static CommandlineOptions options = new CommandlineOptions(1280, 720, Region.NORM_RENDERING_BIT, 0, 0, 8);
108
109 static int max_glyph_count = 10000;
110
111 private static boolean VERBOSE_GLYPHS = false;
112 private static boolean VERBOSE_UI = false;
113
114 public static void main(final String[] args) throws IOException {
115 float mmPerCell = 8f;
116 String fontFilename = null;
117 int gridColumns = -1;
118 boolean showUnderline = false;
119 boolean showLabel = false;
120 boolean perfanal = false;
121
122 if( 0 != args.length ) {
123 final int[] idx = { 0 };
124 for (idx[0] = 0; idx[0] < args.length; ++idx[0]) {
125 if( options.parse(args, idx) ) {
126 continue;
127 } else if(args[idx[0]].equals("-font")) {
128 idx[0]++;
129 fontFilename = args[idx[0]];
130 } else if(args[idx[0]].equals("-mmPerCell")) {
131 idx[0]++;
132 mmPerCell = MiscUtils.atof(args[idx[0]], mmPerCell);
133 } else if(args[idx[0]].equals("-gridCols")) {
134 idx[0]++;
135 gridColumns = MiscUtils.atoi(args[idx[0]], gridColumns);
136 } else if(args[idx[0]].equals("-showUnderline")) {
137 showUnderline = true;
138 } else if(args[idx[0]].equals("-showLabel")) {
139 showLabel = true;
140 } else if(args[idx[0]].equals("-perf")) {
141 perfanal = true;
142 } else if(args[idx[0]].equals("-max")) {
143 idx[0]++;
144 max_glyph_count = MiscUtils.atoi(args[idx[0]], max_glyph_count);
145 }
146 }
147 }
148 System.err.println(options);
149
150 Font font;
151 if( null == fontFilename ) {
152 font = FontFactory.get(IOUtil.getResource("fonts/freefont/FreeSerif.ttf",
153 FontSetDemos.class.getClassLoader(), FontSetDemos.class).getInputStream(), true);
154 } else {
155 font = FontFactory.get( new File( fontFilename ) );
156 }
157 System.err.println("Font "+font.getFullFamilyName());
158
159 final Font fontStatus = FontFactory.get(IOUtil.getResource("fonts/freefont/FreeMono.ttf", FontSetDemos.class.getClassLoader(), FontSetDemos.class).getInputStream(), true);
160 final Font fontInfo = FontFactory.get(FontFactory.UBUNTU).getDefault();
161 System.err.println("Status Font "+fontStatus.getFullFamilyName());
162 System.err.println("Info Font "+fontInfo.getFullFamilyName());
163
164 final GLCapabilities reqCaps = options.getGLCaps();
165 System.out.println("Requested: " + reqCaps);
166
167 final Animator animator = new Animator(0 /* w/o AWT */);
168 animator.setUpdateFPSFrames(1*60, null); // System.err);
169 final GLWindow window = GLWindow.create(reqCaps);
170 window.invoke(false, (final GLAutoDrawable glad) -> {
171 glad.getGL().setSwapInterval(options.swapInterval);
172 return true;
173 } );
174 window.setSize(options.surface_width, options.surface_height);
175 window.setTitle(FontView01.class.getSimpleName()+": "+font.getFullFamilyName()+", "+window.getSurfaceWidth()+" x "+window.getSurfaceHeight());
176 window.setVisible(true);
177 System.out.println("Chosen: " + window.getChosenGLCapabilities());
178 window.addWindowListener(new WindowAdapter() {
179 @Override
180 public void windowResized(final WindowEvent e) {
181 window.setTitle(FontView01.class.getSimpleName()+": "+window.getSurfaceWidth()+" x "+window.getSurfaceHeight());
182 }
183 @Override
184 public void windowDestroyNotify(final WindowEvent e) {
185 animator.stop();
186 }
187 });
188 animator.add(window);
189
190 final Scene scene = new Scene(options.graphAASamples);
191 scene.setClearParams(new float[] { 1f, 1f, 1f, 1f}, GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
192 scene.setPMvCullingEnabled(true);
193
194 scene.attachInputListenerTo(window);
195 window.addGLEventListener(scene);
196
197 final float dpiV;
198 final int glyphGridRowsPerPage;
199 {
200 final float[] ppmm = window.getPixelsPerMM(new float[2]);
201 final float[] dpi = FontScale.ppmmToPPI( new float[] { ppmm[0], ppmm[1] } );
202 System.err.println("DPI "+dpi[0]+" x "+dpi[1]+", "+ppmm[0]+" x "+ppmm[1]+" pixel/mm");
203 dpiV = dpi[1];
204
205 final float[] hasSurfacePixelScale1 = window.getCurrentSurfaceScale(new float[2]);
206 System.err.println("HiDPI PixelScale: "+hasSurfacePixelScale1[0]+"x"+hasSurfacePixelScale1[1]+" (has)");
207 System.err.println("mmPerCell "+mmPerCell);
208 glyphGridRowsPerPage = (int)( ( window.getSurfaceHeight() / ppmm[1] ) / mmPerCell );
209 if( 0 >= gridColumns ) {
210 gridColumns = (int)( ( window.getSurfaceWidth() * GlyphGridWidth / ppmm[0] ) / mmPerCell );
211 }
212 }
213 final float glyphGridCellSize = GlyphGridWidth / gridColumns;
214 final Vec2f glyphGridSize = new Vec2f(GlyphGridWidth, glyphGridRowsPerPage * glyphGridCellSize);
215
216 if( perfanal ) {
217 MiscUtils.waitForKey("Start");
218 }
219 final long t0 = Clock.currentNanos();
220
221 final GridDim gridDim = new GridDim(font, gridColumns, glyphGridRowsPerPage, 1);
222 final Vec2f glyphGridTotalSize = new Vec2f(glyphGridSize.x(), gridDim.rows * glyphGridCellSize);
223 System.err.println(gridDim);
224 System.err.println("GlyphGrid[pgsz "+glyphGridSize+", totsz "+glyphGridTotalSize+", cellSz "+glyphGridCellSize+"]");
225
226 final int[] lastCodepoint = { gridDim.contourChars.get(0) };
227 final Group mainView;
228 final Shape.PointerListener glyphPointerListener;
229 {
230 final Group glyphShapeBox = new Group( new BoxLayout( 1f, 1f, Alignment.FillCenter, new Margin(0.01f) ) );
231 final Group glyphShapeHolder = new Group();
232 glyphShapeHolder.setName("GlyphShapeHolderInfo");
233 glyphShapeBox.addShape( glyphShapeHolder );
234
235 final Group glyphInfoBox = new Group( new BoxLayout( 1f, 1f, Alignment.FillCenter, new Margin(0.025f, 0.025f, 0.025f, 0.025f) ) );
236 final Label glyphInfo = new Label(options.renderModes, fontStatus, "Nothing there yet");
237 setGlyphInfo(fontStatus, glyphInfo, font.getGlyph( 'A' ));
238 glyphInfo.setColor(0.1f, 0.1f, 0.1f, 1.0f);
239 glyphInfoBox.addShape(glyphInfo);
240 glyphInfoBox.setRelayoutOnDirtyShapes(false); // avoid group re-validate on info text changes
241
242 glyphPointerListener = (final Shape s, final Vec3f pos, final MouseEvent e) -> {
243 // System.err.println("ShapeEvent "+shapeEvent);
244 final GlyphShape g0 = getGlyphShape(s);
245
246 e.setConsumed(true);
247
248 // Selected Glyph g0
249 scene.invoke(false, (final GLAutoDrawable d) -> {
250 // Handle old one
251 if( 1 == glyphShapeHolder.getShapeCount() ) {
252 final GlyphShape old = (GlyphShape) glyphShapeHolder.getShapeByIdx(0);
253 if( null != old ) {
254 if( old.getGlyph().getCodepoint() == g0.getGlyph().getCodepoint() ) {
255 // System.err.println("GlyphShape Same: "+old);
256 return true; // abort - no change
257 } else {
258 glyphShapeHolder.removeShape(old);
259 }
260 } else {
261 // System.err.println("GlyphShape Old: Null");
262 }
263 } else {
264 // System.err.println("GlyphShape Old: None");
265 }
266 // New Glyph
267 glyphShapeHolder.addShape(g0);
268 setGlyphInfo(fontStatus, glyphInfo, g0.getGlyph());
269 lastCodepoint[0] = g0.getGlyph().getCodepoint();
270 return true;
271 });
272 };
273
274 final Group glyphInfoView = new Group(new GridLayout(2, 0f, 0f, Alignment.None));
275 {
276 // final float gapSizeX = ( gridDim.rawSize.x() - 1 ) * cellSize * 0.1f;
277 final Group glyphGrid = new Group(new GridLayout(gridDim.columns, glyphGridCellSize*0.9f, glyphGridCellSize*0.9f, Alignment.FillCenter,
278 new Gap(glyphGridCellSize*0.1f)));
279 glyphGrid.setInteractive(true).setDragAndResizable(false).setToggleable(false).setName("GlyphGrid");
280 addGlyphs(reqCaps.getGLProfile(), font, glyphGrid, gridDim, showUnderline, showLabel, fontStatus, fontInfo, glyphPointerListener);
281 glyphGrid.setRelayoutOnDirtyShapes(false); // avoid group re-validate to ease load in Group.isShapeDirty() w/ thousands of glyphs
282 if( VERBOSE_UI ) {
283 glyphGrid.validate(reqCaps.getGLProfile());
284 System.err.println("GlyphGrid "+glyphGrid);
285 System.err.println("GlyphGrid "+glyphGrid.getLayout());
286 }
287 {
288 final GlyphShape gs = getGlyphShape( glyphGrid );
289 if( null != gs ) {
290 glyphShapeHolder.addShape(gs);
291 }
292 }
293 final RangedGroup glyphView = new RangedGroup( options.renderModes, glyphGrid, glyphGridSize,
294 null,
295 new SliderParam( new Vec2f(glyphGridCellSize/4f, glyphGridSize.y()), glyphGridCellSize/10f, true ) );
296 glyphView.getVertSlider().setColor(0.3f, 0.3f, 0.3f, 0.7f).setName("GlyphView");
297 if( VERBOSE_UI ) {
298 glyphView.getVertSlider().addChangeListener((final RangeSlider w, final float old_val, final float val, final float old_val_pct, final float val_pct, final Vec3f pos, final MouseEvent e) -> {
299 final Vec2f minmax = w.getMinMax();
300 final float row_f = val / glyphGridCellSize;
301 System.err.println("VertSlider: row["+row_f+".."+(row_f+gridDim.rowsPerPage-1)+"]/"+gridDim.rows+
302 ", val["+old_val+" -> "+val+"]/"+minmax.y()+", pct["+(100*old_val_pct)+"% -> "+(100*val_pct)+"%], cellSz "+glyphGridCellSize);
303 System.err.println("VertSlider: "+w.getDescription());
304 });
305 }
306 glyphView.getVertSlider().receiveKeyEvents(glyphGrid);
307 // glyphView.getVertSlider().receiveMouseEvents(glyphGrid);
308 if( VERBOSE_UI ) {
309 glyphView.validate(reqCaps.getGLProfile());
310 System.err.println("GlyphView "+glyphView);
311 }
312 glyphInfoView.addShape(glyphView);
313 }
314 {
315 final float infoCellWidth = ( 1f - glyphGridSize.x() ) * 1.15f; // FIXME: Layout issues to force 15% more width to use more size?
316 final float infoCellHeight = glyphGridSize.y() * 0.5f;
317 final Group infoGrid = new Group( new GridLayout(1, infoCellWidth, infoCellHeight * 1f, Alignment.FillCenter, new Gap(infoCellHeight*0.001f, 0)) );
318 infoGrid.setPaddding( new Padding(0, 0, 0, 0.01f) );
319 infoGrid.addShape(glyphShapeBox.setBorder(0.005f).setBorderColor(0, 0, 0, 1));
320 infoGrid.addShape(glyphInfoBox.setBorder(0.005f).setBorderColor(0, 0, 0, 1));
321 if( VERBOSE_UI ) {
322 infoGrid.validate(reqCaps.getGLProfile());
323 System.err.println("InfoGrid "+infoGrid);
324 System.err.println("InfoGrid "+infoGrid.getLayout());
325 System.err.println("GlyphShapeBox "+glyphShapeBox);
326 }
327 glyphInfoView.addShape(infoGrid);
328 }
329 glyphInfoView.setPaddding(new Padding(glyphGridCellSize/6f, 0, 0));
330 if( VERBOSE_UI ) {
331 glyphInfoView.validate(reqCaps.getGLProfile());
332 System.err.println("GlyphInfoGrid "+glyphInfoView);
333 System.err.println("GlyphInfoGrid "+glyphInfoView.getLayout());
334 }
335
336 mainView = new Group(new GridLayout(1, 0f, 0f, Alignment.None));
337 mainView.addShape(glyphInfoView);
338 {
339 final String infoHelp = "Click on a Glyph for a big tooltip view.\n"+
340 "Key-Up/Down or Slider-Mouse-Scroll to move through glyphs.\n"+
341 "Page-Up/Down or Control + Slider-Mouse-Scroll to page faster.\n"+
342 "Mouse-Scroll over left-half of Window rotates and holding control zooms.";
343 final Label infoLabel = new Label(options.renderModes, fontInfo, "Not yet");
344 infoLabel.setColor(0.1f, 0.1f, 0.1f, 1f);
345 infoLabel.setToolTip(new TooltipText(infoHelp, fontInfo, 8f));
346
347 final float h = glyphGridCellSize * 0.4f;
348 final Group labelBox = new Group(new BoxLayout(1.0f, h, new Alignment(Alignment.Bit.Fill.value | Alignment.Bit.CenterVert.value),
349 new Margin(0, 0.005f)));
350 labelBox.addShape(infoLabel);
352 @Override
353 public void display(final GLAutoDrawable drawable) {
354 infoLabel.setText( scene.getStatusText(drawable, options.renderModes, dpiV) + " (Hover over 1s for help)" );
355 }
356 });
357 mainView.addShape(labelBox);
358 }
360 @Override
361 public void mouseWheelMoved(final MouseEvent e) {
362 if( e.getX() >= window.getSurfaceWidth() / 2f ) {
363 return;
364 }
365 if( e.isControlDown() ) {
366 // Scale and move back to center
367 final float[] rot = e.getRotation();
368 final float r = e.isShiftDown() ? rot[0] : rot[1];
369 final float s = 1f+r/200f;
370 final AABBox b0 = mainView.getBounds();
371 final AABBox bs = new AABBox(b0).scale(s, s, 1);
372 final float dw = b0.getWidth() - bs.getWidth();
373 final float dh = b0.getHeight() - bs.getHeight();
374 mainView.scale(s, s, 1);
375 final Vec3f s1 = mainView.getScale();
376 mainView.move(s1.x()*dw/2f, s1.y()*dh/2f, 0);
377 System.err.println("scale +"+s+" = "+s1);
378 e.setConsumed(true);
379 } else {
380 final Vec3f rot = new Vec3f(e.getRotation()).scale( FloatUtil.PI / 180.0f );
381 // swap axis for onscreen rotation matching natural feel
382 final float tmp = rot.x(); rot.setX( rot.y() ); rot.setY( tmp );
383 mainView.setRotation( mainView.getRotation().rotateByEuler( rot.scale( 2f ) ) );
384 e.setConsumed(true);
385 }
386 }
387 });
388 if( VERBOSE_UI ) {
389 mainView.validate(reqCaps.getGLProfile());
390 System.err.println("MainView "+mainView);
391 System.err.println("MainView "+mainView.getLayout());
392 }
393 }
394 scene.addShape(mainView);
395 scene.setAAQuality(options.graphAAQuality);
396
397 window.addKeyListener(new KeyAdapter() {
398 @Override
399 public void keyReleased(final KeyEvent e) {
400 final short keySym = e.getKeySymbol();
401 if( keySym == KeyEvent.VK_F4 || keySym == KeyEvent.VK_ESCAPE || keySym == KeyEvent.VK_Q ) {
402 MiscUtils.destroyWindow(window);
403 } else if( keySym == KeyEvent.VK_S ) {
404 printScreenOnGLThread(scene, window.getChosenGLCapabilities(), font, lastCodepoint[0]);
405 }
406 }
407 });
408
409 animator.start();
410 scene.waitUntilDisplayed();
411 {
412 final AABBox sceneBox = scene.getBounds();
413 final AABBox mainViewBox = mainView.getBounds();
414 final float sx = sceneBox.getWidth() / mainViewBox.getWidth();
415 final float sy = sceneBox.getHeight() / mainViewBox.getHeight();
416 final float sxy = Math.min(sx, sy);
417 System.err.println("SceneBox "+sceneBox);
418 System.err.println("MainViewBox "+mainViewBox);
419 System.err.println("scale sx "+sx+", sy "+sy+", sxy "+sxy);
420 mainView.scale(sxy, sxy, 1f).moveTo(sceneBox.getLow());
421 final long t1 = Clock.currentNanos();
422 final long total = t1 - t0;
423 final float nsPerGlyph = total / gridDim.glyphCount;
424 System.err.println("PERF: Total took "+(total/1000000.0)+"ms, per-glyph "+(nsPerGlyph/1000000.0)+"ms, glyphs "+gridDim.glyphCount);
425 }
426 printScreenOnGLThread(scene, window.getChosenGLCapabilities(), font, lastCodepoint[0]);
427 // stay open ..
428 OutlineShape.printPerf(System.err);
429 }
430
431 static void printScreenOnGLThread(final Scene scene, final GLCapabilitiesImmutable caps, final Font font, final int codepoint) {
432 final String fn = font.getFullFamilyName().replace(' ', '_').replace('-', '_');
433 scene.screenshot(true, scene.nextScreenshotFile(null, FontView01.class.getSimpleName(), options.renderModes, caps, fn+"_cp"+Integer.toHexString(codepoint)));
434 }
435
436 static class GridDim {
437 final List<Character> contourChars;
438 final int glyphCount;
439 final int columns;
440 final int columnsNet;
441 final int rows;
442 final int rowsPerPage;
443 final int elemCount;
444 int complexGlyphCount;
445 int maxNameLen;
446
447 public GridDim(final Font font, final int columns, final int rowsPerPage, final int xReserved) {
448 this.contourChars = new ArrayList<Character>();
449 this.glyphCount = scanContourGlyphs(font);
450 this.columns = columns;
451 this.columnsNet = columns - xReserved;
452 this.rows = (int)Math.ceil((double)glyphCount / (double)columnsNet);
453 this.rowsPerPage = rowsPerPage;
454 this.elemCount = glyphCount + ( rows * xReserved );
455 this.maxNameLen=10;
456 }
457
458 public int reserverColumns() { return columns - columnsNet; }
459
460 private int scanContourGlyphs(final Font font) {
461 final long t0 = Clock.currentNanos();
462 contourChars.clear();
463 complexGlyphCount = 0;
464 maxNameLen = 1;
465 final int[] max = { max_glyph_count };
466 font.forAllGlyphs((final Glyph fg) -> {
467 if( !fg.isNonContour() && max[0]-- > 0 ) {
468 contourChars.add( fg.getCodepoint() );
469 if( null != fg.getShape() && fg.getShape().isComplex() ) {
470 ++complexGlyphCount;
471 }
472 maxNameLen = Math.max(maxNameLen, fg.getName().length());
473 }
474 });
475 final long t1 = Clock.currentNanos();
476 final long total = t1 - t0;
477 final float nsPerGlyph = total / contourChars.size();
478 System.err.println("PERF: GlyphScan took "+(total/1000000.0)+"ms, per-glyph "+(nsPerGlyph/1000000.0)+"ms, glyphs "+contourChars.size());
479 return contourChars.size();
480 }
481 @Override
482 public String toString() { return "GridDim[contours "+glyphCount+", complex "+complexGlyphCount+" ("+((float)complexGlyphCount/(float)glyphCount)*100+"%), "+columns+"x"+rows+"="+(columns*rows)+">="+elemCount+", rows/pg "+rowsPerPage+"]"; }
483 }
484
485 static Group getGlyphShapeHolder(final Shape shape0) {
486 if( !( shape0 instanceof Group ) ) {
487 return null;
488 }
489 return (Group)((Group)shape0).getShapeByName("GlyphHolder");
490 }
491 static GlyphShape getGlyphShape(final Shape shape0) {
492 final Group gsh = getGlyphShapeHolder(shape0);
493 if( null != gsh && gsh.getShapeCount() > 0 ) {
494 return (GlyphShape) gsh.getShapeByIdx(0);
495 }
496 return null;
497 }
498
499 /**
500 * Fill given Group sink with glyph shapes wrapped as {@code Group2[Group1[GlyphShape]]},
501 * with Group1 having the name 'GlyphHolder'.
502 */
503 static void addGlyphs(final GLProfile glp, final Font font, final Group sink,
504 final GridDim gridDim, final boolean showUnderline, final boolean showLabel,
505 final Font fontStatus, final Font fontInfo, final Shape.PointerListener glyphPointerListener) {
506 final AABBox tmpBox = new AABBox();
507 final long t0 = Clock.currentNanos();
508
509 for(int idx = 0; idx < gridDim.glyphCount; ++idx) {
510 final char codepoint = gridDim.contourChars.get(idx);
511 final Font.Glyph fg = font.getGlyph(codepoint);
512 final boolean isComplex = null != fg.getShape() ? fg.getShape().isComplex() : false;
513
514 final GlyphShape g = new GlyphShape(options.renderModes, fg, 0, 0);
515 g.setColor(0.1f, 0.1f, 0.1f, 1).setName("GlyphShape");
516 g.setInteractive(false).setDragAndResizable(false);
517 g.setName( "cp_0x"+Integer.toHexString(fg.getCodepoint()) );
518
519 final Group c0 = new Group("GlyphHolder", null, null, g);
520 c0.setInteractive(false).setDragAndResizable(false);
521
522 // Group each GlyphShape with its bounding box Rectangle
523 final AABBox gbox = fg.getBounds(tmpBox); // g.getBounds(glp);
524 final boolean addUnderline = showUnderline && gbox.getMinY() < 0f;
525 final Group c1 = new Group( new BoxLayout( 1f, 1f, addUnderline ? Alignment.None : Alignment.Center) );
526 c1.setBorder(GlyphGridBorderThickness).setBorderColor(isComplex ? GlyphGridBorderColorComplex : GlyphGridBorderColorSimple)
527 .setInteractive(true).setDragAndResizable(false).setName("GlyphHolder2");
528 if( addUnderline ) {
529 final Shape underline = new Rectangle(options.renderModes, 1f, gbox.getMinY(), 0.01f).setInteractive(false).setColor(0f, 0f, 1f, 0.25f);
530 c1.addShape(underline);
531 }
532
533 c1.addShape( c0 );
534 c1.onHover(glyphPointerListener);
535 sink.receiveKeyEvents(c1);
536 // sink.receiveMouseEvents(c1);
537 c1.setToolTip( new TooltipShape(new Vec4f(1, 1, 1, 1), new Vec4f(0, 0, 0, 1), 0.01f,
538 new Padding(0.05f), new Vec2f(14,14), 0, options.renderModes,
539 g, TooltipShape.NoOpDtor) );
540 c1.onClicked((final Shape s, final Vec3f pos, final MouseEvent e) -> {
541 c1.getTooltip().now();
542 });
543
544 if( 0 < gridDim.reserverColumns() && 0 == idx % gridDim.columnsNet ) {
545 addLabel(sink, fontStatus, String.format("%04x", (int)codepoint));
546 }
547 if( showLabel ) {
548 final Group c2 = new Group( new GridLayout( 1, 0, 0, Alignment.None) ); // Alignment(Alignment.Bit.CenterHoriz) ) );
549 c2.addShape(c1.setName("GlyphHolder3"));
550 {
551 final Label l = new Label(options.renderModes, fontInfo, fg.getName());
552 // final AABBox lbox = l.getUnscaledGlyphBounds();
553 final float sxy = 1f/7f; // gridDim.maxNameLen; // 0.10f; // Math.min(sx, sy);
554 c2.addShape( l.scale(sxy, sxy, 1).setColor(0, 0, 0, 1).setInteractive(false).setDragAndResizable(false) );
555 }
556 sink.addShape(c2);
557 // System.err.println("Add.2: "+c2);
558 } else {
559 sink.addShape(c1);
560 // System.err.println("Add.1: "+c1);
561 }
562 }
563 final long t1 = Clock.currentNanos();
564 final long total = t1 - t0;
565 final float nsPerGlyph = total / gridDim.glyphCount;
566 System.err.println("PERF: GlyphAdd took "+(total/1000000.0)+"ms, per-glyph "+(nsPerGlyph/1000000.0)+"ms, glyphs "+gridDim.glyphCount);
567 }
568 static void addLabel(final Group c, final Font font, final String text) {
569 c.addShape( new Label(options.renderModes, font, text).setColor(0, 0, 0, 1).setInteractive(false).setDragAndResizable(false) );
570 }
571
572 static void setGlyphInfo(final Font font, final Label label, final Font.Glyph g) {
573 label.setText( getGlyphInfo(g) );
574 if( VERBOSE_GLYPHS ) {
575 System.err.println( label.getText() );
576 }
577 }
578
579 static String getGlyphInfo(final Font.Glyph g) {
580 final OutlineShape os = g.getShape();
581 final boolean isComplex = null != os ? os.isComplex() : false;
582 final int osVertices = null != os ? os.getVertexCount() : 0;
583 final String name_s = null != g.getName() ? g.getName() : "";
584 final AABBox bounds = g.getBounds();
585 final String box_s = String.format("Box %+.3f/%+.3f%n %+.3f/%+.3f", bounds.getLow().x(), bounds.getLow().y(), bounds.getHigh().x(), bounds.getHigh().y());
586 return String.format((Locale)null, "%s%nHeight: %1.3f%nLine Height: %1.3f%n%nSymbol: %04x, id %04x%nName: '%s'%nDim %1.3f x %1.3f%n%s%nAdvance %1.3f%nLS Bearings: %1.3f%nVertices: %03d%n%s",
587 g.getFont().getFullFamilyName(),
588 g.getFont().getMetrics().getAscent() - g.getFont().getMetrics().getDescent(), // font hhea table
589 g.getFont().getLineHeight(), // font hhea table
590 (int)g.getCodepoint(), g.getID(), name_s,
591 bounds.getWidth(), bounds.getHeight(), box_s,
592 g.getAdvanceWidth(),
593 g.getLeftSideBearings(),
594 osVertices, isComplex?"Complex Shape":"Simple Shape");
595 }
596}
Abstract Outline shape representation define the method an OutlineShape(s) is bound and rendered.
Definition: Region.java:62
static final int NORM_RENDERING_BIT
Rendering-Mode bit for Region.
Definition: Region.java:79
The optional property jogamp.graph.font.ctor allows user to specify the FontConstructor implementatio...
static final FontSet get(final int font)
static final int UBUNTU
Ubuntu is the default font family, {@value}.
Simple static font scale methods for unit conversions.
Definition: FontScale.java:37
static float[] ppmmToPPI(final float[] ppmm)
Converts [1/mm] to [1/inch] in place.
Definition: FontScale.java:105
Group of Shapes, optionally utilizing a Group.Layout.
Definition: Group.java:61
int getShapeCount()
Returns number of Shapes, see getShapes().
Definition: Group.java:216
void addShape(final Shape s)
Adds a Shape.
Definition: Group.java:225
Layout getLayout()
Return current Group.Layout.
Definition: Group.java:150
AABBox getBounds(final PMVMatrix4f pmv, final Shape shape)
Returns AABBox dimension of given Shape from this container's perspective, i.e.
Definition: Group.java:686
void setRelayoutOnDirtyShapes(final boolean v)
Set relayout on dirty shapes mode, defaults to true.
Definition: Group.java:514
GraphUI Scene.
Definition: Scene.java:102
final void setClearParams(final float[] clearColor, final int clearMask)
Sets the clear parameter for glClearColor(..) and glClear(..) to be issued at display(GLAutoDrawable)...
Definition: Scene.java:221
final void setPMvCullingEnabled(final boolean v)
Enable or disable Project-Modelview (PMv) frustum culling per Shape for this container.
Definition: Scene.java:230
void addGLEventListener(final GLEventListener listener)
Definition: Scene.java:435
synchronized void attachInputListenerTo(final GLWindow window)
Definition: Scene.java:246
boolean invoke(final boolean wait, final GLRunnable glRunnable)
Enqueues a one-shot GLRunnable, which will be executed within the next GLAutoDrawable#display() call ...
Definition: Scene.java:428
String getStatusText(final GLAutoDrawable glad, final int renderModes, final float dpi)
Return a formatted status string containing avg fps and avg frame duration.
Definition: Scene.java:1381
Convenient adapter combining dummy implementation for MouseListener and GestureListener.
Definition: Shape.java:1884
Generic Shape, potentially using a Graph via GraphShape or other means of representing content.
Definition: Shape.java:87
Tooltip setToolTip(final Tooltip newTooltip)
Set's a new Tooltip for this shape.
Definition: Shape.java:1653
Shape setColor(final float r, final float g, final float b, final float a)
Set base color.
Definition: Shape.java:1389
Shape setName(final String name)
Set a symbolic name for this shape for identification.
Definition: Shape.java:339
final Shape addKeyListener(final KeyListener l)
Definition: Shape.java:1841
final Shape move(final float dtx, final float dty, final float dtz)
Move about scaled distance.
Definition: Shape.java:557
final Shape setInteractive(final boolean v)
Set whether this shape is interactive in general, i.e.
Definition: Shape.java:1711
final Vec3f getScale()
Returns scale Vec3f reference.
Definition: Shape.java:682
final AABBox getBounds()
Returns the unscaled bounding AABBox for this shape, borrowing internal instance.
Definition: Shape.java:732
final Quaternion getRotation()
Returns Quaternion for rotation.
Definition: Shape.java:595
final Shape setDragAndResizable(final boolean v)
Set whether this shape is draggable and resizable.
Definition: Shape.java:1801
final Shape validate(final GL2ES2 gl)
Validates the shape's underlying GLRegion.
Definition: Shape.java:850
final Shape setPaddding(final Padding padding)
Sets the unscaled padding for this shape, which is included in unscaled getBounds() and also includes...
Definition: Shape.java:376
final Shape setRotation(final Quaternion q)
Sets the rotation Quaternion.
Definition: Shape.java:604
final Shape scale(final Vec3f s)
Multiply current scale factor by given scale.
Definition: Shape.java:661
final Shape setToggleable(final boolean toggleable)
Set this shape toggleable, default is off.
Definition: Shape.java:1573
A round HUD text Tooltip for Shape, see Shape#setToolTip(Tooltip).
Immutable layout alignment options, including Bit#Fill.
Definition: Alignment.java:35
static final Alignment None
No alignment constant.
Definition: Alignment.java:37
static final Alignment FillCenter
Bit#Fill, Bit#CenterHoriz and Bit#CenterVert alignment constant.
Definition: Alignment.java:45
GraphUI Stack Group.Layout.
Definition: BoxLayout.java:53
GraphUI CSS property Gap, scaled spacing between (grid) cells not belonging to the cell element.
Definition: Gap.java:38
GraphUI Grid Group.Layout.
Definition: GridLayout.java:56
GraphUI CSS property Margin, scaled space between or around elements and not included in the element'...
Definition: Margin.java:41
GraphUI CSS property Padding, unscaled space belonging to the element and included in the element's s...
Definition: Padding.java:38
Representing a single Font.Glyph as a GraphShape.
Definition: GlyphShape.java:53
Glyph getGlyph()
Returns the Font.Glyph to be rendered.
A GraphUI text label GraphShape.
Definition: Label.java:50
boolean setText(final CharSequence text)
Set the text to be rendered.
Definition: Label.java:94
RangeSlider Widget either utilizing a simple positional round knob or a rectangular page-sized knob.
Vec2f getMinMax()
Returns slider value range, see setMinMax(Vec2f, float).
String getDescription()
Return string description of current slider setting.
final RangeSlider addChangeListener(final ChangeListener l)
final Shape setColor(final float r, final float g, final float b, final float a)
Sets the slider knob color.
void receiveKeyEvents(final Shape source)
Forward KeyListener events to this Shape from source using a ForwardKeyListener.
RangeSlider configuration parameter for RangedGroup.
Ranged Group Widget, displaying a clipped content Group with optional horizontal and/or vertical Rang...
RangeSlider getVertSlider()
Returns the used vertical RangeSlider or null.
Basic Float math utility functions.
Definition: FloatUtil.java:83
static final float PI
The value PI, i.e.
final Quaternion rotateByEuler(final Vec3f angradXYZ)
Rotates this quaternion from the given Euler rotation array angradXYZ in radians.
2D Vector based upon two float components.
Definition: Vec2f.java:37
3D Vector based upon three float components.
Definition: Vec3f.java:37
void setX(final float x)
Definition: Vec3f.java:158
Vec3f scale(final float s)
this = this * s, returns this.
Definition: Vec3f.java:218
void setY(final float y)
Definition: Vec3f.java:159
4D Vector based upon four float components.
Definition: Vec4f.java:37
Axis Aligned Bounding Box.
Definition: AABBox.java:54
final float getWidth()
Definition: AABBox.java:879
final float getHeight()
Definition: AABBox.java:883
final AABBox scale(final float s)
Scale this AABBox by a constant around fixed center.
Definition: AABBox.java:750
final boolean isShiftDown()
getModifiers() contains SHIFT_MASK.
final boolean isControlDown()
getModifiers() contains CTRL_MASK.
static final short VK_F4
Constant for the F4 function key.
Definition: KeyEvent.java:686
static final short VK_ESCAPE
Constant for the ESCAPE function key.
Definition: KeyEvent.java:485
final short getKeySymbol()
Returns the virtual key symbol reflecting the current keyboard layout.
Definition: KeyEvent.java:176
static final short VK_S
See VK_A.
Definition: KeyEvent.java:631
static final short VK_Q
See VK_A.
Definition: KeyEvent.java:627
Pointer event of type PointerType.
Definition: MouseEvent.java:74
final float[] getRotation()
Returns a 3-component float array filled with the values of the rotational axis in the following orde...
final int getX()
See details for multiple-pointer events.
final void setConsumed(final boolean consumed)
If consumed is true, this event is marked as consumed, ie.
Definition: NEWTEvent.java:126
NEWT Window events are provided for notification purposes ONLY.
An implementation of GLAutoDrawable and Window interface, using a delegated Window instance,...
Definition: GLWindow.java:121
final int getSurfaceHeight()
Returns the height of this GLDrawable's surface client area in pixel units.
Definition: GLWindow.java:466
final void addMouseListener(final MouseListener l)
Appends the given MouseListener to the end of the list.
Definition: GLWindow.java:927
final void setTitle(final String title)
Definition: GLWindow.java:297
final float[] getPixelsPerMM(final float[] ppmmStore)
Returns the pixels per millimeter of this window's NativeSurface according to the main monitor's curr...
Definition: GLWindow.java:520
final float[] getCurrentSurfaceScale(final float[] result)
Returns the current pixel scale of the associated NativeSurface.
Definition: GLWindow.java:505
final int getSurfaceWidth()
Returns the width of this GLDrawable's surface client area in pixel units.
Definition: GLWindow.java:461
final void setSize(final int width, final int height)
Sets the size of the window's client area in window units, excluding decorations.
Definition: GLWindow.java:625
final void setVisible(final boolean visible)
Calls setVisible(true, visible), i.e.
Definition: GLWindow.java:615
final void addWindowListener(final WindowListener l)
Appends the given com.jogamp.newt.event.WindowListener to the end of the list.
Definition: GLWindow.java:882
static GLWindow create(final GLCapabilitiesImmutable caps)
Creates a new GLWindow attaching a new Window referencing a new default Screen and default Display wi...
Definition: GLWindow.java:169
Specifies a set of OpenGL capabilities.
final GLProfile getGLProfile()
Returns the GL profile you desire or used by the drawable.
This may become a little font viewer application, having FontForge as its role model.
Definition: FontView01.java:99
static void main(final String[] args)
int graphAASamples
Sample count for Graph Region AA render-modes: Region#VBAA_RENDERING_BIT or Region#MSAA_RENDERING_BIT...
int graphAAQuality
Pass2 AA-quality rendering for Graph Region AA render-modes: VBAA_RENDERING_BIT.
static void waitForKey(final String preMessage)
Definition: MiscUtils.java:167
static int atoi(final String str, final int def)
Definition: MiscUtils.java:60
static void destroyWindow(final GLAutoDrawable glad)
Definition: MiscUtils.java:269
static float atof(final String str, final float def)
Definition: MiscUtils.java:78
final synchronized void add(final GLAutoDrawable drawable)
Adds a drawable to this animator's list of rendering drawables.
final void setUpdateFPSFrames(final int frames, final PrintStream out)
final synchronized boolean stop()
Stops this animator.
Definition: Animator.java:368
CenterVert
Vertical center alignment.
Definition: Alignment.java:67
Fill
Scale object to parent size, e.g.
Definition: Alignment.java:61
Font getDefault()
Returns the family FAMILY_REGULAR with STYLE_NONE as retrieved with get(int, int).
char getCodepoint()
Returns this glyph's mapped (unicode) codepoint symbol.
Interface wrapper for font implementation.
Definition: Font.java:60
Glyph getGlyph(final String name)
Returns the Glyph mapped to given name.
String getFullFamilyName()
Shall return the family and subfamily name, separated a dash.
A higher-level abstraction than GLDrawable which supplies an event based mechanism (GLEventListener) ...
boolean invoke(boolean wait, GLRunnable glRunnable)
Enqueues a one-shot GLRunnable, which will be executed within the next display() call after all regis...
GL getGL()
Returns the GL pipeline object this GLAutoDrawable uses.
void addGLEventListener(GLEventListener listener)
Adds the given listener to the end of this drawable queue.
GLCapabilitiesImmutable getChosenGLCapabilities()
Fetches the GLCapabilitiesImmutable corresponding to the chosen OpenGL capabilities (pixel format / v...
static final int GL_DEPTH_BUFFER_BIT
GL_ES_VERSION_2_0, GL_VERSION_1_1, GL_VERSION_1_0, GL_VERSION_ES_1_0 Define "GL_DEPTH_BUFFER_BIT" wit...
Definition: GL.java:738