fixed first image not HiDPI and ensure profiling info displayed are those of the ccurrent rendering (and not previous one)

This commit is contained in:
Martin Pernollet 2021-04-18 19:43:11 +02:00
parent ea75026dc1
commit f4fc69b8e9
16 changed files with 395 additions and 197 deletions

View File

@ -48,7 +48,7 @@ public class FrameAWT extends java.awt.Frame implements IFrame {
@Override
public void windowClosing(WindowEvent e) {
FrameAWT.this.remove((java.awt.Component) FrameAWT.this.chart.getCanvas());
FrameAWT.this.chart.stopAnimation();
FrameAWT.this.chart.stopAllThreads();
FrameAWT.this.chart.dispose();
FrameAWT.this.chart = null;
FrameAWT.this.dispose();

View File

@ -156,13 +156,18 @@ public class Chart {
}
}
}
public void startAnimation() {
setAnimated(true);
}
public void stopAnimation() {
setAnimated(false);
}
public void startAnimation() {
setAnimated(true);
public void stopAllThreads() {
getMouse().getSlaveThreadController().stop();
stopAnimation();
}
@ -194,31 +199,31 @@ public class Chart {
public ICameraMouseController addMouseCameraController() {
if(mouse==null) {
mouse = getFactory().getPainterFactory().newMouseCameraController(this);
}
CameraThreadController rotation = mouse.getSlaveThreadController();//new CameraThreadController(this);
rotation.setStep(0.025f);
// Always keep update view until the camera thread controller
// Has a timer to avoid rotating too fast (when no update view, thread can
// go much faster so rotation is to speedy!)
rotation.setUpdateViewDefault(true);
// later, should apply : !chart.getQuality().isAnimated());
// mouse.addSlaveThreadController(rotation);
// Switch between on demand/continuous rendering
// keep to false if animated to avoid double rendering
// keep to true otherwise the mouse does not update
mouse.setUpdateViewDefault(!getQuality().isAnimated());
CameraThreadController rotation = mouse.getSlaveThreadController();//new CameraThreadController(this);
rotation.setStep(0.025f);
// Always keep update view until the camera thread controller
// Has a timer to avoid rotating too fast (when no update view, thread can
// go much faster so rotation is to speedy!)
rotation.setUpdateViewDefault(true);
// later, should apply : !chart.getQuality().isAnimated());
// mouse.addSlaveThreadController(rotation);
// Switch between on demand/continuous rendering
// keep to false if animated to avoid double rendering
// keep to true otherwise the mouse does not update
mouse.setUpdateViewDefault(!getQuality().isAnimated());
}
return mouse;
}
public IMousePickingController addMousePickingController(int clickWidth) {
if(mousePicking==null) {
mousePicking = getFactory().getPainterFactory().newMousePickingController(this, clickWidth);
mousePicking = getFactory().getPainterFactory().newMousePickingController(this, clickWidth);
}
return mousePicking;
}
@ -226,11 +231,12 @@ public class Chart {
public ICameraKeyController addKeyboardCameraController() {
if(keyboard==null) {
keyboard = getFactory().getPainterFactory().newKeyboardCameraController(this);
// Switch between on demand/continuous rendering
// keep to false if animated to avoid double rendering
// keep to true otherwise the mouse does not update
keyboard.setUpdateViewDefault(!getQuality().isAnimated());
}
// Switch between on demand/continuous rendering
// keep to false if animated to avoid double rendering
// keep to true otherwise the mouse does not update
keyboard.setUpdateViewDefault(!getQuality().isAnimated());
return keyboard;
}

View File

@ -1,6 +1,8 @@
package org.jzy3d.events;
public interface IViewIsVerticalEventListener {
public interface IViewEventListener {
public void viewFirstRender();
public void viewVerticalReached(ViewIsVerticalEvent e);
public void viewVerticalLeft(ViewIsVerticalEvent e);

View File

@ -6,7 +6,7 @@ import org.apache.log4j.Logger;
import org.jzy3d.chart.Chart;
import org.jzy3d.chart.factories.IChartFactory;
import org.jzy3d.colors.Color;
import org.jzy3d.events.IViewIsVerticalEventListener;
import org.jzy3d.events.IViewEventListener;
import org.jzy3d.events.IViewLifecycleEventListener;
import org.jzy3d.events.IViewPointChangedListener;
import org.jzy3d.events.ViewIsVerticalEvent;
@ -73,8 +73,7 @@ public class View {
protected boolean squared = true;
protected float cameraRenderingSphereRadiusFactor = 1f;
protected float cameraRenderingSphereRadiusFactorOnTop = 0.25f;
// view objects
protected Camera cam;
protected IAxis axis;
@ -91,10 +90,13 @@ public class View {
// view listeners
protected List<IViewPointChangedListener> viewPointChangedListeners;
protected List<IViewIsVerticalEventListener> viewOnTopListeners;
protected List<IViewEventListener> viewOnTopListeners;
protected List<IViewLifecycleEventListener> viewLifecycleListeners;
protected boolean wasOnTopAtLastRendering;
// view states
protected boolean first = true;
// constants
public static final float PI_div2 = (float) Math.PI / 2;
public static final float DISTANCE_DEFAULT = 2000;
@ -657,11 +659,11 @@ public class View {
return annotations.getGraph();
}
public boolean addViewOnTopEventListener(IViewIsVerticalEventListener listener) {
public boolean addViewEventListener(IViewEventListener listener) {
return viewOnTopListeners.add(listener);
}
public boolean removeViewOnTopEventListener(IViewIsVerticalEventListener listener) {
public boolean removeViewOnTopEventListener(IViewEventListener listener) {
return viewOnTopListeners.remove(listener);
}
@ -669,13 +671,18 @@ public class View {
ViewIsVerticalEvent e = new ViewIsVerticalEvent(this);
if (isOnTop)
for (IViewIsVerticalEventListener listener : viewOnTopListeners)
for (IViewEventListener listener : viewOnTopListeners)
listener.viewVerticalReached(e);
else
for (IViewIsVerticalEventListener listener : viewOnTopListeners)
for (IViewEventListener listener : viewOnTopListeners)
listener.viewVerticalLeft(e);
}
protected void fireViewFirstRender() {
for (IViewEventListener listener : viewOnTopListeners)
listener.viewFirstRender();
}
public boolean addViewPointChangedListener(IViewPointChangedListener listener) {
return viewPointChangedListeners.add(listener);
}
@ -832,7 +839,7 @@ public class View {
public void render() {
fireViewLifecycleWillRender(null);
renderBackground(0f, 1f);
renderScene();
renderOverlay();
@ -862,8 +869,14 @@ public class View {
}
public void renderScene(ViewportConfiguration viewport) {
// updateQuality(); // REMOVED BECAUSE NOT NECESSARY
//synchronized(this) {
if(first) {
fireViewFirstRender();
first=false;
}
//}
BoundingBox3d scaling = computeScaledViewBounds();
updateCamera(viewport, scaling);
renderAxeBox();

View File

@ -0,0 +1,18 @@
package org.jzy3d.plot3d.rendering.view;
import org.jzy3d.events.IViewEventListener;
import org.jzy3d.events.ViewIsVerticalEvent;
public class ViewEventAdapter implements IViewEventListener{
@Override
public void viewFirstRender() {
}
@Override
public void viewVerticalReached(ViewIsVerticalEvent e) {
}
@Override
public void viewVerticalLeft(ViewIsVerticalEvent e) {
}
}

View File

@ -25,7 +25,7 @@ public class EmulGLAnimator implements IAnimator {
loop = true;
while (loop) {
canvas.doDisplay();
canvas.doRender();
try {
Thread.sleep(RENDERING_LOOP_PAUSE);

View File

@ -5,7 +5,12 @@ import org.jzy3d.chart.controllers.CameraThreadControllerWithTime;
import org.jzy3d.chart.controllers.keyboard.camera.AWTCameraKeyController;
import org.jzy3d.chart.controllers.keyboard.screenshot.AWTScreenshotKeyController;
import org.jzy3d.chart.controllers.mouse.picking.IMousePickingController;
import org.jzy3d.chart.factories.IFrame;
import org.jzy3d.maths.Rectangle;
import org.jzy3d.painters.IPainter.Font;
import org.jzy3d.plot3d.primitives.axis.AxisBox;
import org.jzy3d.plot3d.rendering.canvas.EmulGLCanvas;
import org.jzy3d.plot3d.text.renderers.TextBitmapRenderer;
/**
* {@link EmulGLSkin} is a chart facade that returns known subtypes of chart components already downcasted.
@ -41,6 +46,24 @@ public class EmulGLSkin {
return (EmulGLCanvas)chart.getCanvas();
}
public IFrame open(int width, int height) {
IFrame frame = chart.open(width, height);
triggerRenderAfterMili(30);
return frame;
}
public void triggerRenderAfterMili(long mili) {
if (!chart.getQuality().isAnimated()) {
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
chart.render();
}
}
public AdaptiveMouseController addMouseCameraController() {
return (AdaptiveMouseController)chart.addMouseCameraController();
@ -72,7 +95,12 @@ public class EmulGLSkin {
}
public CameraThreadControllerWithTime getSlaveThreadController() {
public CameraThreadControllerWithTime getThread() {
return getSlaveThreadController(getMouse());
}
public TextBitmapRenderer getAxisTextRenderer() {
return ((TextBitmapRenderer)((AxisBox)chart.getView().getAxis()).getTextRenderer());
}
}

View File

@ -147,7 +147,7 @@ public class AdaptiveMouseController extends AWTCameraMouseController {
gl.setAutoAdaptToHiDPI(currentHiDPI);
// this force the GL image to apply the new HiDPI setting immediatly
gl.updatePixelScale(canvas.getGraphics());
gl.resetViewport();
gl.applyViewport();
}
protected void disableWireframe(Chart chart) {

View File

@ -26,6 +26,8 @@ import org.jzy3d.plot3d.rendering.view.Camera;
* @author Martin Pernollet
*/
public class CameraThreadControllerWithTime extends CameraThreadController implements Runnable {
private static final int TIME_TO_SPIN_DEFAULT = 10;
protected double speed = 10; // seconds to make a complete revolution
/**
@ -40,19 +42,25 @@ public class CameraThreadControllerWithTime extends CameraThreadController imple
/**
* The interval between each rate limit verification in MS
*/
protected static final int RATE_CHECK_RATE = 100;
protected static final int RATE_CHECK_RATE = 40;
/**
* Rotation direction : Direction.LEFT makes negative azimuth increments, while Direction.RIGHT
* make positive azimuth increments.
*/
protected Direction direction = Direction.LEFT;
public enum Direction {
LEFT, RIGHT;
}
public CameraThreadControllerWithTime() {}
/**
* Defaults time to spin to 10
* @param chart
*/
public CameraThreadControllerWithTime(Chart chart) {
this(chart, 10);
this(chart, TIME_TO_SPIN_DEFAULT);
}
/**

View File

@ -1,7 +1,9 @@
package org.jzy3d.plot3d.rendering.canvas;
import java.awt.AWTEvent;
import java.awt.Canvas;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
@ -11,11 +13,14 @@ import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.imageio.ImageIO;
import org.apache.log4j.Logger;
import org.jzy3d.chart.IAnimator;
import org.jzy3d.chart.factories.IChartFactory;
import org.jzy3d.colors.AWTColor;
import org.jzy3d.colors.Color;
import org.jzy3d.maths.Coord2d;
import org.jzy3d.maths.TicToc;
@ -32,6 +37,15 @@ import jgl.GLCanvas;
import jgl.GLUT;
import jgl.context.gl_pointer;
/**
* This canvas allows rendering charts with jGL as OpenGL backend which perform in CPU.
*
* The below schema depicts how this canvas does painting :
*
* <img src="doc-files/emulgl-canvas.png"/>
*
* @author Martin Pernollet
*/
public class EmulGLCanvas extends GLCanvas implements IScreenCanvas, IMonitorable {
Logger log = Logger.getLogger(EmulGLCanvas.class);
@ -43,24 +57,29 @@ public class EmulGLCanvas extends GLCanvas implements IScreenCanvas, IMonitorabl
*/
public static final boolean TO_BE_CHOOSEN_REPAINT_WITH_FLUSH = false;
/** set to TRUE to overlay performance info on top left corner */
protected boolean profileDisplayMethod = false;
/** set to TRUE to show in console events of the component (to debug GLUT) */
protected boolean debugEvents = false;
protected TicToc profileDisplayTimer = new TicToc();
protected Font profileDisplayFont = new Font("Arial", Font.PLAIN, 12);
protected int profileDisplayCount = 0;
protected Monitor monitor;
// Fields used by the canvas to work
protected View view;
protected EmulGLPainter painter;
protected IAnimator animator;
protected AtomicBoolean isRenderingFlag = new AtomicBoolean(false);
// Profiling (display perf on screen)
/** set to TRUE to show in console events of the component (to debug GLUT) */
protected boolean debugEvents = false;
/** set to TRUE to overlay performance info on top left corner */
protected boolean profileDisplayMethod = false;
protected TicToc profileDisplayTimer = new TicToc();
protected Font profileDisplayFont = new Font("Arial", Font.PLAIN, 12);
protected int profileDisplayCount = 0;
protected List<ProfileInfo> profileInfo = new ArrayList<>();
// Monitor (export perf to something else, e.g. an XLS file)
protected Monitor monitor;
public EmulGLCanvas(IChartFactory factory, Scene scene, Quality quality) {
super();
view = scene.newView(this, quality);
@ -71,14 +90,11 @@ public class EmulGLCanvas extends GLCanvas implements IScreenCanvas, IMonitorabl
animator = factory.getPainterFactory().newAnimator(this);
if (quality.isPreserveViewportSize()) {
myGL.setAutoAdaptToHiDPI(false);
} else {
if (quality.isHiDPIEnabled()) {
myGL.setAutoAdaptToHiDPI(true);
} else {
myGL.setAutoAdaptToHiDPI(false);
}
// FROM NATIVE
// renderer = factory.newRenderer(view, traceGL, debugGL);
// addGLEventListener(renderer);
}
@Override
@ -103,7 +119,7 @@ public class EmulGLCanvas extends GLCanvas implements IScreenCanvas, IMonitorabl
@Override
public void processEvent(AWTEvent e) {
if (debugEvents && shouldPrintEvent(e)) {
System.out.println("EmulGLCanvas.processEvent:" + e);
System.err.println("EmulGLCanvas.processEvent:" + e);
}
super.processEvent(e);
}
@ -112,7 +128,9 @@ public class EmulGLCanvas extends GLCanvas implements IScreenCanvas, IMonitorabl
return !(e.getID() == MouseEvent.MOUSE_MOVED);
}
// ******************* INIT ******************* //
/* *********************************************************************** */
/* ******************************* INIT ********************************** */
/* *********************************************************************** */
/** Equivalent to registering a Renderer3d in native canvas. */
protected void init(int width, int height) {
@ -126,7 +144,7 @@ public class EmulGLCanvas extends GLCanvas implements IScreenCanvas, IMonitorabl
myUT.glutInitWindowPosition(getX(), getY());
myUT.glutCreateWindow(this); // this canvas GLUT register this canvas
myUT.glutDisplayFunc("doDisplay"); // on this canvas GLUT register this display method
myUT.glutDisplayFunc("doRender"); // on this canvas GLUT register this display method
myUT.glutReshapeFunc("doReshape"); // on ComponentEvent.RESIZE TODO: double render car
// GLUT.resize invoque
// reshape + display
@ -143,29 +161,90 @@ public class EmulGLCanvas extends GLCanvas implements IScreenCanvas, IMonitorabl
// pourquoi est il nécessaire de le faire pendant mouse dragged?
}
/* *********************************************************************** */
/* ***************************** DISPLAY ********************************* */
/* *********************************************************************** */
// ******************* DISPLAY ******************* //
/**
* This overrides the {@link GLCanvas} hence {@link Canvas} methods to copy the image of the 3D
* scene as generated while {@link GL#glFlush()}.
*
* It is called when the application needs to paint the canvas, which assume a rendering has
* already been process by {@link #doRender()} which produce an image that the canvas can use for
* fast pixel swap.
*
* {@link #doRender()} on its side is triggered when {@link GLUT} thinks it is relevant. This may
* occur because {@link EmulGLCanvas} triggered a {@link ComponentEvent.COMPONENT_RESIZED} event.
*/
@Override
public void paint(Graphics g) {
if (profileDisplayMethod) {
// Overrides GL swapping to retrieve the image and print performance info inside
BufferedImage glImage = myGL.getRenderedImage();
paintProfileInfo(glImage);
g.drawImage(glImage, myGL.getStartX(), myGL.getStartY(), myGL.getDesiredWidth(),
myGL.getDesiredHeight(), this);
}
// If not profiling invoke the default swapping method implemented in GLCanvas
else {
super.paint(g);
}
}
@Override
public void display() {
forceRepaint();
}
/**
* Can be used to update image if camera has changed position. (usually called by
* {@link View#shoot()})
*
* Warning if this is invoked by a thread external to AWT, this may redraw GL while GL is already
* used by AWT, which would turn GL into an inconsistent state.
*/
@Override
public void forceRepaint() {
// This makes GLUT invoke the myReshape function
// SHOULD NOT BE CALLED IF ANIMATOR IS ACTIVE
if (TO_BE_CHOOSEN_REPAINT_WITH_FLUSH) {
painter.getGL().glFlush();
// This triggers copy of newly generated picture to the GLCanvas
// repaint();
} else {
processEvent(new ComponentEvent(this, ComponentEvent.COMPONENT_RESIZED));
// equivalent to view.clear(), view.render(), glFlush(), glXSwapBuffers
}
// INTRODUCE A UNDESIRED RESIZE EVENT (WE ARE NOT RESHAPING VIEWPORT
// WAS JUST USED TO FORCE REPAINT
}
/**
* Triggers an atomic rendering of a frame, measure rendering performance and update the status of
* rendering (active or not).
* rendering (active or not). This method is callback registered in with
* {@link GLUT#glutDisplayFunc(String)} which will be called when OpenGL need to update display.
* OpenGL updates as soon as the component that GLUT listen to (which is this {@link EmulGLCanvas}
* triggers a {@link ComponentEvent.COMPONENT_RESIZED} event.
*
* Performance measurement can be seen on screen if {@link #setProfileDisplayMethod(boolean)} was
* set to true OR can be collected by a {@link Monitor} defined by {@link #add(Monitor)}.
*
* Method is synchronized to avoid multiple concurrent calls to doDisplay which might make jGL get
* crazy with GL state consistency : GL states must be consistent during a complete rendering
* pass, and should not be modified by a second rendering pass in the middle of the first one.
* This method is synchronized to prevent multiple concurrent calls to doDisplay which might make
* jGL get crazy with GL state consistency : GL states must be consistent during a complete
* rendering pass, and should not be modified by a second rendering pass in the middle of the
* first one. Consistency may be on drawing a complete geometry in appropriate order (glBegin,
* glVertex, glEnd) or in the way OpenGL 1.0 fixed pipeline is cleanly handled.
*
* In addition, the display method has a {@link #isRenderingFlag} so that external components may
* known that the canvas is working or not. This allows ignoring a rendering query in case the
* canvas is not ready for working. This is different from making use of <code>synchronized</code>
* (which lead to a queue of calls to be resolved) in that one may simply not append work to do
* according to the status of the canvas. This is used by mouse and thread controller which tend
* to send lot of rendering queries faster than the frame rate.
* known that the canvas is currently rendering or not. This allows ignoring a rendering query in
* case the canvas is not ready for working. This is different from making use of
* <code>synchronized</code> (which lead to a queue of calls to be resolved) in that one may
* simply not append work to do according to the status of the canvas.
*/
public synchronized void doDisplay() {
//System.out.println("IS RENDERING");
public synchronized void doRender() {
isRenderingFlag.set(true);
profileDisplayTimer.tic();
@ -192,11 +271,11 @@ public class EmulGLCanvas extends GLCanvas implements IScreenCanvas, IMonitorabl
// -------------------------------
// PROFILE
profileDisplayTimer.toc();
lastRenderingTimeMs = profileDisplayTimer.elapsedMilisecond();
if (profileDisplayMethod) {
postRenderProfiling(lastRenderingTimeMs);
profile(lastRenderingTimeMs);
}
if (monitor != null) {
@ -208,16 +287,16 @@ public class EmulGLCanvas extends GLCanvas implements IScreenCanvas, IMonitorabl
isRenderingFlag.set(false);
//System.out.println("DONE RENDERING");
// System.out.println("DONE RENDERING");
}
protected double lastRenderingTimeMs = LAST_RENDER_TIME_UNDEFINED;
public double getLastRenderingTime() {
return lastRenderingTimeMs;
}
public static final double LAST_RENDER_TIME_UNDEFINED = -1;
/*
@ -227,74 +306,15 @@ public class EmulGLCanvas extends GLCanvas implements IScreenCanvas, IMonitorabl
* } }
*/
@Override
public void display() {
forceRepaint();
}
/**
* Can be used to update image if camera has changed position. (usually called by
* {@link View#shoot()})
*
* Warning if this is invoked by a thread external to AWT, this may redraw
* GL while GL is already used by AWT, which would turn GL into an inconsistent state.
*/
@Override
public void forceRepaint() {
// This makes GLUT invoke the myReshape function
// SHOULD NOT BE CALLED IF ANIMATOR IS ACTIVE
if (TO_BE_CHOOSEN_REPAINT_WITH_FLUSH) {
painter.getGL().glFlush();
// This triggers copy of newly generated picture to the GLCanvas
// repaint();
}
else {
processEvent(new ComponentEvent(this, ComponentEvent.COMPONENT_RESIZED));
// equivalent to view.clear(), view.render(), glFlush(), glXSwapBuffers
}
// INTRODUCE A UNDESIRED RESIZE EVENT (WE ARE NOT RESHAPING VIEWPORT
// WAS JUST USED TO FORCE REPAINT
}
public AtomicBoolean getIsRenderingFlag() {
return isRenderingFlag;
}
protected void postRenderProfiling(double mili) {
int x = 05;
int y = 12;
int line = 1;
postRenderString("FrameID : " + profileDisplayCount, x, y*line++, Color.BLACK);
postRenderString("Render in : " + mili + "ms", x, y*line++, Color.BLACK);
postRenderString("Drawables : " + view.getScene().getGraph().getDecomposition().size(), x,
y*line++, Color.BLACK);
for(Drawable d: view.getScene().getGraph().getAll()) {
if(d instanceof Scatter) {
Scatter s = (Scatter)d;
postRenderString("Scatter : " + s.coordinates.length + " points", x,
y*(line++), Color.BLACK);
}
}
postRenderString("Canvas Size : " + getWidth() + "x" + getHeight(), x, y*line++, Color.BLACK);
GL gl = painter.getGL();
postRenderString("Viewport Size : " + gl.getContext().Viewport.Width + "x" + gl.getContext().Viewport.Height, x, y * (line++), Color.BLACK);
}
/** Draw a 2d text at the given position */
protected void postRenderString(String message, int x, int y, Color color) {
painter.getGL().appendTextToDraw(profileDisplayFont, message, x, y, color.r, color.g, color.b);
}
// ******************* RESIZE ******************* //
/* *********************************************************************** */
/* ****************************** RESIZE ********************************* */
/* *********************************************************************** */
/**
* Handle resize events emitted by GLUT.
@ -314,18 +334,18 @@ public class EmulGLCanvas extends GLCanvas implements IScreenCanvas, IMonitorabl
}
// ******************* MOUSE MOTION ******************* //
/* *************************** MOUSE MOTION ***************************** */
/**
* Handle mouse events emitted by GLUT. Most probably not registered as mouse already handled by
* Jzy3D.
*/
public synchronized void doMotion(int x, int y) {
doDisplay();
doRender();
System.out.println("EmulGLCanvas.doMotion!" + profileDisplayCount);
}
// ******************* SCREENSHOTS ******************* //
/* *************************** SCREENSHOTS ***************************** */
@Override
public BufferedImage screenshot() {
@ -411,7 +431,100 @@ public class EmulGLCanvas extends GLCanvas implements IScreenCanvas, IMonitorabl
return null;
}
/* ******************* DEBUG ********************* */
/* *********************************************************************** */
/* ************************** PROFILE AND DEBUG ************************** */
/* *********************************************************************** */
/**
* Render profile on top of an image (probably the image of the GL scene) previously collected
* while {@link EmulGLCanvas#doRender().
*
* Painting profile info is synchronized on the profile info list to ensure it is not modified
* while drawing (which occurs if synchronization is disabled). Despite we did not observed any
* lag due to such rendering, it is important to keep in mind that displaying profile information
* requires a synchronized access to this info list which is on the other side synchronized to
* protect exporting rendering info of the last call to {@link #doRender()}.
*
*/
protected void paintProfileInfo(BufferedImage glImage) {
Graphics2D g2d = (Graphics2D) glImage.getGraphics();
g2d.setFont(profileDisplayFont);
synchronized (profileInfo) {
for (ProfileInfo profile : profileInfo) {
java.awt.Color awtColor = AWTColor.toAWT(profile.color);
g2d.setColor(awtColor);
g2d.drawString(profile.message, profile.x, profile.y);
}
}
}
protected void profile(double mili) {
synchronized (profileInfo) {
profileClear();
int x = 10;
int y = 12;
int line = 1;
Color c = Color.BLACK;
// Rendering info
profile("FrameID : " + profileDisplayCount, x, y * line++, c);
profile("Render in : " + mili + "ms", x, y * line++, c);
// Drawables size
profile("Drawables : " + view.getScene().getGraph().getDecomposition().size(), x, y * line++,
c);
// Scatters sizes
for (Drawable d : view.getScene().getGraph().getAll()) {
if (d instanceof Scatter) {
Scatter s = (Scatter) d;
profile("Scatter : " + s.coordinates.length + " points", x, y * (line++), c);
}
}
// Canvas size
profile("Canvas Size : " + getWidth() + "x" + getHeight(), x, y * line++, c);
// Viewport size
GL gl = painter.getGL();
int viewportWidth = gl.getContext().Viewport.Width;
int viewportHeight = gl.getContext().Viewport.Height;
profile("Viewport Size : " + viewportWidth + "x" + viewportHeight, x, y * (line++), c);
}
}
/** Draw a 2d text at the given position */
protected void profile(String message, int x, int y, Color c) {
// painter.getGL().appendTextToDraw(profileDisplayFont, message, x, y, c.r, c.g, c.b);
profileInfo.add(new ProfileInfo(message, x, y, c));
}
protected void profileClear() {
profileInfo.clear();
}
class ProfileInfo {
String message;
int x;
int y;
Color color;
public ProfileInfo(String message, int x, int y, Color color) {
super();
this.message = message;
this.x = x;
this.y = y;
this.color = color;
}
}
public boolean isProfileDisplayMethod() {
return profileDisplayMethod;

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

View File

@ -83,8 +83,8 @@ public class TestContinuousAndOnDemandRendering {
if (false) {
canvas.processEvent(event); // 2.5s
canvas.processEvent(event); // 2.5s
canvas.doDisplay(); // 10
canvas.doDisplay(); // 10*/
canvas.doRender(); // 10
canvas.doRender(); // 10*/
// canvas.doDisplay(); // 10
}

View File

@ -40,6 +40,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.tools.JavaCompiler;
import jgl.context.gl_context;
import jgl.context.gl_list;
import jgl.context.gl_object;
@ -59,7 +60,7 @@ public class GL {
protected gl_list List;
protected Component canvas;
protected Image glImage;
protected BufferedImage glImage;
protected int StartX = 0;
protected int StartY = 0;
protected List<TextToDraw> textsToDraw = new ArrayList<>();
@ -103,7 +104,7 @@ public class GL {
return Context.CR;
}
public Image getRenderedImage() {
public BufferedImage getRenderedImage() {
return glImage;
}
@ -114,8 +115,10 @@ public class GL {
public double getPixelScaleY() {
return pixelScaleY;
}
/** the following functions are only for developpers **/
/* the following functions are only for developpers
public MemoryImageSource glJGetImageSource() {
return new MemoryImageSource(Context.Viewport.Width, Context.Viewport.Height, Context.ColorBuffer.Buffer, 0,
Context.Viewport.Width);
@ -123,9 +126,25 @@ public class GL {
public Image glJGetImage(MemoryImageSource imagesource) {
return canvas.createImage(imagesource);
}
}*/
public Component glJGetComponent() {
public int getStartX() {
return StartX;
}
public int getStartY() {
return StartY;
}
public int getDesiredWidth() {
return desiredWidth;
}
public int getDesiredHeight() {
return desiredHeight;
}
public Component glJGetComponent() {
return canvas;
}
@ -158,11 +177,10 @@ public class GL {
* <code>void glXSwapBuffers (Display *dpy, GLXDrawable drawable)</code>
*/
public void glXSwapBuffers(Graphics g, ImageObserver o) {
updatePixelScale(g);
//System.out.println("UPDATE PIX SCALE");
g.drawImage(glImage, StartX, StartY, desiredWidth, desiredHeight, o);
}
public void updatePixelScale(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
@ -1764,31 +1782,28 @@ public class GL {
desiredY = y;
desiredWidth = width;
desiredHeight = height;
// Update pixel scale to guess if HiDPI
if(canvas.getGraphics()!=null)
updatePixelScale(canvas.getGraphics());
resetViewport();
/*
* JavaImageSource = new MemoryImageSource (Context.Viewport.Width,
* Context.Viewport.Height, Context.ColorBuffer.Buffer, 0,
* Context.Viewport.Width); JavaImageSource.setAnimated (true);
* JavaImageSource.setFullBufferUpdates (true); JavaImage =
* JavaComponent.createImage (JavaImageSource);
*/
applyViewport();
}
/**
* Apply viewport according to the latest known expected width/height
* and the latest known pixel scales.
*/
public void resetViewport() {
//System.out.println("Viewport : " + autoAdaptToHiDPI + pixelScaleX);
actualWidth = (int)(desiredWidth * pixelScaleX);
actualHeight = (int)(desiredHeight * pixelScaleY);
if(pixelScaleX>0 && pixelScaleY>0)
CC.gl_viewport(desiredX, desiredY, actualWidth, actualHeight);
else
CC.gl_viewport(desiredX, desiredY, desiredWidth, desiredHeight);
public void applyViewport() {
if(autoAdaptToHiDPI) {
actualWidth = (int)(desiredWidth * pixelScaleX);
actualHeight = (int)(desiredHeight * pixelScaleY);
}
else {
actualWidth = desiredWidth;
actualHeight = desiredHeight;
}
CC.gl_viewport(desiredX, desiredY, actualWidth, actualHeight);
}
/** GLvoid glPushMatrix (GLvoid) */

View File

@ -39,7 +39,7 @@ public class FrameSwing extends JFrame implements IFrame {
@Override
public void windowClosing(WindowEvent e) {
FrameSwing.this.remove((java.awt.Component) FrameSwing.this.chart.getCanvas());
FrameSwing.this.chart.stopAnimation();
FrameSwing.this.chart.stopAllThreads();
FrameSwing.this.chart.dispose();
FrameSwing.this.chart = null;
FrameSwing.this.dispose();

View File

@ -2,11 +2,11 @@ package org.jzy3d.demos.scatter;
import java.util.Random;
import org.jzy3d.chart.Chart;
import org.jzy3d.chart.EmulGLSkin;
import org.jzy3d.chart.factories.EmulGLChartFactory;
import org.jzy3d.colors.Color;
import org.jzy3d.maths.Coord3d;
import org.jzy3d.plot3d.primitives.Scatter;
import org.jzy3d.plot3d.rendering.canvas.EmulGLCanvas;
import org.jzy3d.plot3d.rendering.canvas.Quality;
public class ScatterDemoEmulGL {
@ -16,15 +16,15 @@ public class ScatterDemoEmulGL {
q.setPreserveViewportSize(false); // need java 9+ to enable HiDPI & Retina displays
Chart chart = new EmulGLChartFactory().newChart(q);
chart.getScene().add(scatter());
chart.add(scatter(500000));
chart.open();
chart.addMouseCameraController();
((EmulGLCanvas) chart.getCanvas()).setProfileDisplayMethod(true); // to print frame rate
EmulGLSkin skin = EmulGLSkin.on(chart);
skin.getCanvas().setProfileDisplayMethod(true);
}
private static Scatter scatter() {
int size = 50000;
private static Scatter scatter(int size) {
float x;
float y;
float z;

View File

@ -3,6 +3,7 @@ package org.jzy3d.demos.surface;
import java.io.File;
import java.io.IOException;
import org.jzy3d.chart.Chart;
import org.jzy3d.chart.EmulGLSkin;
import org.jzy3d.chart.factories.EmulGLChartFactory;
import org.jzy3d.colors.Color;
import org.jzy3d.colors.ColorMapper;
@ -13,10 +14,7 @@ import org.jzy3d.plot3d.builder.Mapper;
import org.jzy3d.plot3d.builder.SurfaceBuilder;
import org.jzy3d.plot3d.builder.concrete.OrthonormalGrid;
import org.jzy3d.plot3d.primitives.Shape;
import org.jzy3d.plot3d.primitives.axis.AxisBox;
import org.jzy3d.plot3d.rendering.canvas.EmulGLCanvas;
import org.jzy3d.plot3d.rendering.canvas.Quality;
import org.jzy3d.plot3d.text.renderers.TextBitmapRenderer;
import jgl.GLCanvas;
@ -36,20 +34,17 @@ public class SurfaceDemoEmulGL {
EmulGLChartFactory factory = new EmulGLChartFactory();
Quality q = Quality.Advanced;
q.setAnimated(false); // leave CPU quiet if no need to re-render
q.setHiDPIEnabled(true); // need java 9+ to enable HiDPI & Retina displays
// (tutorials built with Java 8 for backward compatibility, update your runtime to get HiDPI)
Chart chart = factory.newChart(q);
chart.add(surface);
EmulGLCanvas c = (EmulGLCanvas) chart.getCanvas();
c.setProfileDisplayMethod(true);
c.getGL().setAutoAdaptToHiDPI(true); // need java 9+ to enable HiDPI & Retina displays
//chart.getAxisLayout().setFont(Font.Helvetica_18);
((TextBitmapRenderer)((AxisBox)chart.getView().getAxis()).getTextRenderer()).setFont(Font.TimesRoman_10);
chart.open();
EmulGLSkin skin = EmulGLSkin.on(chart);
skin.getCanvas().setProfileDisplayMethod(true);
skin.getAxisTextRenderer().setFont(Font.TimesRoman_10);
// --------------------------------
chart.setAnimated(true);
chart.open();
chart.addMouseCameraController();