WorldWindJava
-
diff --git a/src/gov/nasa/worldwind/awt/AWTInputHandler.java b/src/gov/nasa/worldwind/awt/AWTInputHandler.java
index 564ed3fcb3..4915cd6d77 100644
--- a/src/gov/nasa/worldwind/awt/AWTInputHandler.java
+++ b/src/gov/nasa/worldwind/awt/AWTInputHandler.java
@@ -404,24 +404,26 @@ public void mouseClicked(final MouseEvent awtMouseEvent)
if (pickedObjects != null && pickedObjects.getTopPickedObject() != null
&& !pickedObjects.getTopPickedObject().isTerrain())
{
- // Something is under the cursor, so it's deemed "selected".
+ Point awtPt = awtMouseEvent.getPoint(); // AWT screen coordinates
+
+ // Something is under the cursor, so it's deemed "selected".
if (MouseEvent.BUTTON1 == mouseEvent.getButton())
{
if (mouseEvent.getClickCount() <= 1)
{
this.callSelectListeners(new SelectEvent(this.wwd, SelectEvent.LEFT_CLICK,
- mouseEvent, pickedObjects));
+ awtPt, mouseEvent, pickedObjects));
}
else
{
this.callSelectListeners(new SelectEvent(this.wwd, SelectEvent.LEFT_DOUBLE_CLICK,
- mouseEvent, pickedObjects));
+ awtPt, mouseEvent, pickedObjects));
}
}
else if (MouseEvent.BUTTON3 == mouseEvent.getButton())
{
this.callSelectListeners(new SelectEvent(this.wwd, SelectEvent.RIGHT_CLICK,
- mouseEvent, pickedObjects));
+ awtPt, mouseEvent, pickedObjects));
}
this.wwd.getView().firePropertyChange(AVKey.VIEW, null, this.wwd.getView());
@@ -472,16 +474,18 @@ public void mousePressed(MouseEvent awtMouseEvent)
if (this.objectsAtButtonPress != null && objectsAtButtonPress.getTopPickedObject() != null
&& !this.objectsAtButtonPress.getTopPickedObject().isTerrain())
{
+ Point awtPt = awtMouseEvent.getPoint(); // AWT screen coordinates
+
// Something is under the cursor, so it's deemed "selected".
if (MouseEvent.BUTTON1 == mouseEvent.getButton())
{
this.callSelectListeners(new SelectEvent(this.wwd, SelectEvent.LEFT_PRESS,
- mouseEvent, this.objectsAtButtonPress));
+ awtPt, mouseEvent, this.objectsAtButtonPress));
}
else if (MouseEvent.BUTTON3 == mouseEvent.getButton())
{
this.callSelectListeners(new SelectEvent(this.wwd, SelectEvent.RIGHT_PRESS,
- mouseEvent, this.objectsAtButtonPress));
+ awtPt, mouseEvent, this.objectsAtButtonPress));
}
// Initiate a repaint.
@@ -600,8 +604,9 @@ public void mouseDragged(MouseEvent awtMouseEvent)
&& !pickedObjects.getTopPickedObject().isTerrain()))
{
this.isDragging = true;
- DragSelectEvent selectEvent = new DragSelectEvent(this.wwd, SelectEvent.DRAG, mouseEvent, pickedObjects,
- prevMousePoint);
+ DragSelectEvent selectEvent = new DragSelectEvent(this.wwd, SelectEvent.DRAG,
+ awtMouseEvent.getPoint(), mouseEvent,
+ pickedObjects, prevMousePoint);
this.callSelectListeners(selectEvent);
// If no listener consumed the event, then cancel the drag.
@@ -790,7 +795,7 @@ protected void cancelDrag()
if (this.isDragging)
{
this.callSelectListeners(new DragSelectEvent(this.wwd, SelectEvent.DRAG_END, null,
- this.objectsAtButtonPress, this.mousePoint));
+ null, this.objectsAtButtonPress, this.mousePoint));
}
this.isDragging = false;
diff --git a/src/gov/nasa/worldwind/awt/AbstractViewInputHandler.java b/src/gov/nasa/worldwind/awt/AbstractViewInputHandler.java
index b0783cd1f4..012457796b 100644
--- a/src/gov/nasa/worldwind/awt/AbstractViewInputHandler.java
+++ b/src/gov/nasa/worldwind/awt/AbstractViewInputHandler.java
@@ -954,7 +954,7 @@ public double computeDragSlope(Point point1, Point point2, Vec4 vec1, Vec4 vec2)
return slope - 1.0;
}
- protected static Point constrainToSourceBounds(Point point, Object source)
+ protected static Point constrainToSourceBounds(Point point, WorldWindow source)
{
if (point == null)
return null;
@@ -962,19 +962,24 @@ protected static Point constrainToSourceBounds(Point point, Object source)
if (!(source instanceof Component))
return point;
- Component c = (Component) source;
+ // source.getHeight(), source.getWidth() are AWT coords height,
+ // but the 'point' is MouseEvent GL surface coords.
+ // Clamp to GL viewport size.
+ int glWidth = source.getView().getViewport().width;
+ int glHeight = source.getView().getViewport().height;
int x = (int) point.getX();
if (x < 0)
x = 0;
- if (x > c.getWidth())
- x = c.getWidth();
+ if (x >= glWidth)
+ x = glWidth - 1;
int y = (int) point.getY();
if (y < 0)
y = 0;
- if (y > c.getHeight())
- y = c.getHeight();
+
+ if (y >= glHeight)
+ y = glHeight - 1;
return new Point(x, y);
}
diff --git a/src/gov/nasa/worldwind/awt/BasicViewInputHandler.java b/src/gov/nasa/worldwind/awt/BasicViewInputHandler.java
index e7af6f0ca6..2008517ca6 100644
--- a/src/gov/nasa/worldwind/awt/BasicViewInputHandler.java
+++ b/src/gov/nasa/worldwind/awt/BasicViewInputHandler.java
@@ -219,10 +219,10 @@ public boolean inputActionPerformed(KeyEventState keys, String target,
Point movement = ViewUtil.subtract(point, lastPoint);
int headingInput = movement.x;
- int pitchInput = movement.y;
+ int pitchInput = -movement.y;
Point totalMovement = ViewUtil.subtract(point, mouseDownPoint);
int totalHeadingInput = totalMovement.x;
- int totalPitchInput = totalMovement.y;
+ int totalPitchInput = -totalMovement.y;
ViewInputAttributes.DeviceAttributes deviceAttributes =
getAttributes().getDeviceAttributes(ViewInputAttributes.DEVICE_MOUSE);
@@ -258,16 +258,16 @@ public boolean inputActionPerformed(AbstractViewInputHandler inputHandler,
return false;
}
+ // 'mouseEvent' is in GL surface coords, (0,0) in lower left of canvas
+ // Make down mouse movement increase the pitch.
Point movement = ViewUtil.subtract(point, lastPoint);
int headingInput = movement.x;
- int pitchInput = movement.y;
+ int pitchInput = -movement.y;
if (mouseDownPoint == null)
mouseDownPoint = lastPoint;
Point totalMovement = ViewUtil.subtract(point, mouseDownPoint);
int totalHeadingInput = totalMovement.x;
- int totalPitchInput = totalMovement.y;
-
-
+ int totalPitchInput = -totalMovement.y;
ViewInputAttributes.DeviceAttributes deviceAttributes =
getAttributes().getDeviceAttributes(ViewInputAttributes.DEVICE_MOUSE);
diff --git a/src/gov/nasa/worldwind/drag/DragContext.java b/src/gov/nasa/worldwind/drag/DragContext.java
index 6623902e88..935f2c31b4 100644
--- a/src/gov/nasa/worldwind/drag/DragContext.java
+++ b/src/gov/nasa/worldwind/drag/DragContext.java
@@ -41,16 +41,16 @@
public class DragContext
{
/**
- * In accordance with the AWT screen coordinates the top left point of the window is the origin.
+ * In accordance with the GL surface coordinates the top left point of the window is the origin.
*/
protected Point point;
/**
- * In accordance with the AWT screen coordinates the top left point of the window is the origin. This point is the
+ * In accordance with the GL surface coordinates the top left point of the window is the origin. This point is the
* previous screen point.
*/
protected Point previousPoint;
/**
- * In accordance with the AWT screen coordinates the top left point of the window is the origin. This point refers
+ * In accordance with the GL surface coordinates the top left point of the window is the origin. This point refers
* to the initial point of the drag event.
*/
protected Point initialPoint;
@@ -81,9 +81,9 @@ public DragContext()
}
/**
- * Returns the current screen point with the origin at the top left corner of the window.
+ * Returns the current GL surface point with the origin at the top left corner of the window.
*
- * @return the current screen point.
+ * @return the current GL surface point.
*/
public Point getPoint()
{
@@ -91,9 +91,9 @@ public Point getPoint()
}
/**
- * Set the {@link DragContext} current screen point.
+ * Set the {@link DragContext} current GL surface point.
*
- * @param point the point to assign to the current screen point.
+ * @param point the point to assign to the current GL surface point.
*
* @throws IllegalArgumentException if the point is null.
*/
@@ -110,7 +110,7 @@ public void setPoint(Point point)
}
/**
- * Returns the previous screen point with the origin at the top left corner of the window.
+ * Returns the previous GL surface point with the origin at the top left corner of the window.
*
* @return the previous point.
*/
@@ -120,9 +120,9 @@ public Point getPreviousPoint()
}
/**
- * Set the {@link DragContext} previous screen point.
+ * Set the {@link DragContext} previous GL surface point.
*
- * @param previousPoint the screen point to assign to the previous screen point.
+ * @param previousPoint the GL surface point to assign to the previous screen point.
*
* @throws IllegalArgumentException if the previousPoint is null.
*/
@@ -139,10 +139,10 @@ public void setPreviousPoint(Point previousPoint)
}
/**
- * Returns the initial screen point with the origin at the top left corner of the window. The initial point is the
- * screen point at the initiation of the drag event.
+ * Returns the initial GL surface point with the origin at the top left corner of the window. The initial point is the
+ * GL surface point at the initiation of the drag event.
*
- * @return the initial screen point.
+ * @return the initial GL surface point.
*/
public Point getInitialPoint()
{
@@ -150,9 +150,9 @@ public Point getInitialPoint()
}
/**
- * Set the {@link DragContext} initial screen point.
+ * Set the {@link DragContext} initial GL surface point.
*
- * @param initialPoint the screen point to assign to the initial screen point.
+ * @param initialPoint the GL surface point to assign to the initial screen point.
*
* @throws IllegalArgumentException if the initialPoint is null.
*/
diff --git a/src/gov/nasa/worldwind/drag/DraggableSupport.java b/src/gov/nasa/worldwind/drag/DraggableSupport.java
index 3a5e7b52a5..7af85f8eb6 100644
--- a/src/gov/nasa/worldwind/drag/DraggableSupport.java
+++ b/src/gov/nasa/worldwind/drag/DraggableSupport.java
@@ -428,9 +428,7 @@ protected Vec4 computeScreenOffsetFromReferencePosition(Position dragObjectRefer
Vec4 screenPointOffset = new Vec4(
dragContext.getInitialPoint().getX() - dragObjectScreenPoint.getX(),
- dragContext.getInitialPoint().getY() - (
- dragContext.getView().getViewport().getHeight()
- - dragObjectScreenPoint.getY() - 1.0)
+ dragContext.getInitialPoint().getY() - dragObjectScreenPoint.getY()
);
return screenPointOffset;
diff --git a/src/gov/nasa/worldwind/event/DragSelectEvent.java b/src/gov/nasa/worldwind/event/DragSelectEvent.java
index fcd36fe33a..c694b58f1b 100644
--- a/src/gov/nasa/worldwind/event/DragSelectEvent.java
+++ b/src/gov/nasa/worldwind/event/DragSelectEvent.java
@@ -42,10 +42,11 @@ public class DragSelectEvent extends SelectEvent
{
private final java.awt.Point previousPickPoint;
- public DragSelectEvent(Object source, String eventAction, MouseEvent mouseEvent, PickedObjectList pickedObjects,
- java.awt.Point previousPickPoint)
+ public DragSelectEvent(Object source, String eventAction, java.awt.Point awtPt, MouseEvent mouseEvent,
+ PickedObjectList pickedObjects,
+ java.awt.Point previousPickPoint)
{
- super(source, eventAction, mouseEvent, pickedObjects);
+ super(source, eventAction, awtPt, mouseEvent, pickedObjects);
this.previousPickPoint = previousPickPoint;
}
diff --git a/src/gov/nasa/worldwind/event/SelectEvent.java b/src/gov/nasa/worldwind/event/SelectEvent.java
index 0100deae12..1804356111 100644
--- a/src/gov/nasa/worldwind/event/SelectEvent.java
+++ b/src/gov/nasa/worldwind/event/SelectEvent.java
@@ -104,15 +104,17 @@ public class SelectEvent extends WWEvent
private final Point pickPoint; // GL surface coordinates
private final Rectangle pickRect; // GL surface coordinates
private final MouseEvent mouseEvent; // GL surface coordinates
+ private final Point awtMousePt; // AWT screen coordinates
private final PickedObjectList pickedObjects;
- public SelectEvent(Object source, String eventAction, MouseEvent mouseEvent, PickedObjectList pickedObjects)
+ public SelectEvent(Object source, String eventAction, Point awtPt, MouseEvent mouseEvent, PickedObjectList pickedObjects)
{
super(source);
this.eventAction = eventAction;
this.pickPoint = mouseEvent != null ? mouseEvent.getPoint() : null;
this.pickRect = null;
this.mouseEvent = mouseEvent;
+ this.awtMousePt = awtPt;
this.pickedObjects = pickedObjects;
}
@@ -123,6 +125,7 @@ public SelectEvent(Object source, String eventAction, Point pickPoint, PickedObj
this.pickPoint = pickPoint;
this.pickRect = null;
this.mouseEvent = null;
+ this.awtMousePt = null;
this.pickedObjects = pickedObjects;
}
@@ -133,6 +136,7 @@ public SelectEvent(Object source, String eventAction, Rectangle pickRectangle, P
this.pickPoint = null;
this.pickRect = pickRectangle;
this.mouseEvent = null;
+ this.awtMousePt = null;
this.pickedObjects = pickedObjects;
}
@@ -150,6 +154,10 @@ public String getEventAction()
return this.eventAction != null ? this.eventAction : "gov.nasa.worldwind.SelectEvent.UnknownEventAction";
}
+ public Point getAwtMousePt() {
+ return awtMousePt;
+ }
+
public Point getPickPoint()
{
return this.pickPoint;
diff --git a/src/gov/nasa/worldwind/globes/EllipsoidalGlobe.java b/src/gov/nasa/worldwind/globes/EllipsoidalGlobe.java
index 479cf7fbe6..d5ccf17d09 100644
--- a/src/gov/nasa/worldwind/globes/EllipsoidalGlobe.java
+++ b/src/gov/nasa/worldwind/globes/EllipsoidalGlobe.java
@@ -2,25 +2,25 @@
* Copyright 2006-2009, 2017, 2020 United States Government, as represented by the
* Administrator of the National Aeronautics and Space Administration.
* All rights reserved.
- *
+ *
* The NASA World Wind Java (WWJ) platform is licensed under the Apache License,
* Version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
- *
+ *
* NASA World Wind Java (WWJ) also contains the following 3rd party Open Source
* software:
- *
+ *
* Jackson Parser – Licensed under Apache 2.0
* GDAL – Licensed under MIT
* JOGL – Licensed under Berkeley Software Distribution (BSD)
* Gluegen – Licensed under Berkeley Software Distribution (BSD)
- *
+ *
* A complete listing of 3rd Party software notices and licenses included in
* NASA World Wind Java (WWJ) can be found in the WorldWindJava-v2.2 3rd-party
* notices and licenses PDF found in code directory.
@@ -132,7 +132,10 @@ public Globe getGlobe()
return this.globe;
}
- @SuppressWarnings({"RedundantIfStatement"})
+ @SuppressWarnings(
+ {
+ "RedundantIfStatement"
+ })
@Override
public boolean equals(Object o)
{
@@ -145,8 +148,8 @@ public boolean equals(Object o)
if (Double.compare(stateKey.verticalExaggeration, verticalExaggeration) != 0)
return false;
- if (elevationModel != null ? !elevationModel.equals(stateKey.elevationModel) :
- stateKey.elevationModel != null)
+ if (elevationModel != null ? !elevationModel.equals(stateKey.elevationModel)
+ : stateKey.elevationModel != null)
return false;
if (globe != null ? !globe.equals(stateKey.globe) : stateKey.globe != null)
return false;
@@ -238,7 +241,6 @@ public double getRadiusAt(Angle latitude, Angle longitude)
// observing that the length of the ellipsoidal point at the specified latitude and longitude indicates the
// radius at that location. The formula for the length of the ellipsoidal point was then converted into the
// simplified form below.
-
double sinLat = Math.sin(latitude.radians);
double rpm = this.equatorialRadius / Math.sqrt(1.0 - this.es * sinLat * sinLat);
@@ -294,7 +296,10 @@ public double[] getMinAndMaxElevations(Angle latitude, Angle longitude)
}
return this.elevationModel != null ? this.elevationModel.getExtremeElevations(latitude, longitude)
- : new double[] {0, 0};
+ : new double[]
+ {
+ 0, 0
+ };
}
public double[] getMinAndMaxElevations(Sector sector)
@@ -306,7 +311,10 @@ public double[] getMinAndMaxElevations(Sector sector)
throw new IllegalArgumentException(message);
}
- return this.elevationModel != null ? this.elevationModel.getExtremeElevations(sector) : new double[] {0, 0};
+ return this.elevationModel != null ? this.elevationModel.getExtremeElevations(sector) : new double[]
+ {
+ 0, 0
+ };
}
public Extent getExtent()
@@ -347,7 +355,6 @@ protected Intersection[] intersect(Line line, double equRadius, double polRadius
return null;
// Taken from Lengyel, 2Ed., Section 5.2.3, page 148.
-
double m = equRadius / polRadius; // "ratio of the x semi-axis length to the y semi-axis length"
double n = 1d; // "ratio of the x semi-axis length to the z semi-axis length"
double m2 = m * m;
@@ -373,16 +380,25 @@ protected Intersection[] intersect(Line line, double equRadius, double polRadius
if (discriminant == 0)
{
Vec4 p = line.getPointAt((-b - discriminantRoot) / (2 * a));
- return new Intersection[] {new Intersection(p, true)};
+ return new Intersection[]
+ {
+ new Intersection(p, true)
+ };
}
else // (discriminant > 0)
{
Vec4 near = line.getPointAt((-b - discriminantRoot) / (2 * a));
Vec4 far = line.getPointAt((-b + discriminantRoot) / (2 * a));
if (c >= 0) // Line originates outside the Globe.
- return new Intersection[] {new Intersection(near, false), new Intersection(far, false)};
+ return new Intersection[]
+ {
+ new Intersection(near, false), new Intersection(far, false)
+ };
else // Line originates inside the Globe.
- return new Intersection[] {new Intersection(far, false)};
+ return new Intersection[]
+ {
+ new Intersection(far, false)
+ };
}
}
@@ -492,7 +508,10 @@ public double[] getElevations(Sector sector, List extends LatLon> latLons, dou
double[] elevations)
{
if (this.elevationModel == null)
- return new double[] {0};
+ return new double[]
+ {
+ 0
+ };
double[] resolution = this.elevationModel.getElevations(sector, latLons, targetResolution, elevations);
@@ -508,6 +527,16 @@ public double[] getElevations(Sector sector, List extends LatLon> latLons, dou
return resolution;
}
+ /**
+ * Get the EGM96 offset class for this globe.
+ *
+ * @return The EGM96 offset class for this globe.
+ */
+ public EGM96 getEGM96()
+ {
+ return this.egm96;
+ }
+
public double getElevation(Angle latitude, Angle longitude)
{
if (latitude == null || longitude == null)
@@ -909,7 +938,8 @@ protected Vec4 geodeticToEllipsoidal(Angle latitude, Angle longitude, double met
double cosLon = Math.cos(longitude.radians);
double sinLon = Math.sin(longitude.radians);
- double rpm = // getRadius (in meters) of vertical in prime meridian
+ double rpm
+ = // getRadius (in meters) of vertical in prime meridian
this.equatorialRadius / Math.sqrt(1.0 - this.es * sinLat * sinLat);
double x = (rpm + metersElevation) * cosLat * sinLon;
@@ -1035,7 +1065,6 @@ protected void geodeticToCartesian(Sector sector, int numLat, int numLon, double
//
// return Position.fromRadians(lat, lon, elevation);
// }
-
/**
* Compute the geographic position to corresponds to a Cartesian point.
*
@@ -1059,7 +1088,10 @@ protected Position cartesianToGeodetic(Vec4 cart)
*
* @see #geodeticToEllipsoidal(gov.nasa.worldwind.geom.Angle, gov.nasa.worldwind.geom.Angle, double)
*/
- @SuppressWarnings({"SuspiciousNameCombination"})
+ @SuppressWarnings(
+ {
+ "SuspiciousNameCombination"
+ })
protected Position ellipsoidalToGeodetic(Vec4 cart)
{
// Contributed by Nathan Kronenfeld. Integrated 1/24/2011. Brings this calculation in line with Vermeille's
@@ -1370,4 +1402,4 @@ public static ElevationModel makeElevationModel(String key, String defaultValue)
Object configSource = Configuration.getStringValue(key, defaultValue);
return (ElevationModel) BasicFactory.create(AVKey.ELEVATION_MODEL_FACTORY, configSource);
}
-}
\ No newline at end of file
+}
diff --git a/src/gov/nasa/worldwind/layers/ScalebarLayer.java b/src/gov/nasa/worldwind/layers/ScalebarLayer.java
index 03f495b886..6cfd8af978 100644
--- a/src/gov/nasa/worldwind/layers/ScalebarLayer.java
+++ b/src/gov/nasa/worldwind/layers/ScalebarLayer.java
@@ -34,6 +34,8 @@
import gov.nasa.worldwind.util.*;
import com.jogamp.opengl.*;
+import com.jogamp.opengl.util.awt.TextRenderer;
+
import java.awt.*;
import java.awt.geom.*;
diff --git a/src/gov/nasa/worldwind/layers/TerrainProfileLayer.java b/src/gov/nasa/worldwind/layers/TerrainProfileLayer.java
index 8f1303ff6e..149ab8c2eb 100644
--- a/src/gov/nasa/worldwind/layers/TerrainProfileLayer.java
+++ b/src/gov/nasa/worldwind/layers/TerrainProfileLayer.java
@@ -37,6 +37,8 @@
import gov.nasa.worldwind.view.orbit.OrbitView;
import com.jogamp.opengl.*;
+import com.jogamp.opengl.util.awt.TextRenderer;
+
import java.awt.*;
import java.awt.geom.*;
import java.beans.PropertyChangeEvent;
diff --git a/src/gov/nasa/worldwind/layers/TiledImageLayer.java b/src/gov/nasa/worldwind/layers/TiledImageLayer.java
index 67fba48604..1a19ce23e7 100644
--- a/src/gov/nasa/worldwind/layers/TiledImageLayer.java
+++ b/src/gov/nasa/worldwind/layers/TiledImageLayer.java
@@ -39,6 +39,8 @@
import javax.imageio.ImageIO;
import com.jogamp.opengl.*;
+import com.jogamp.opengl.util.awt.TextRenderer;
+
import javax.xml.xpath.XPath;
import java.awt.image.*;
import java.io.*;
diff --git a/src/gov/nasa/worldwind/layers/mercator/MercatorTiledImageLayer.java b/src/gov/nasa/worldwind/layers/mercator/MercatorTiledImageLayer.java
index 9ba7c0ff76..9d6f527329 100644
--- a/src/gov/nasa/worldwind/layers/mercator/MercatorTiledImageLayer.java
+++ b/src/gov/nasa/worldwind/layers/mercator/MercatorTiledImageLayer.java
@@ -38,6 +38,8 @@
import javax.imageio.ImageIO;
import com.jogamp.opengl.*;
+import com.jogamp.opengl.util.awt.TextRenderer;
+
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
diff --git a/src/gov/nasa/worldwind/render/AbstractAnnotation.java b/src/gov/nasa/worldwind/render/AbstractAnnotation.java
index 82cbad8bff..72d1571bfc 100644
--- a/src/gov/nasa/worldwind/render/AbstractAnnotation.java
+++ b/src/gov/nasa/worldwind/render/AbstractAnnotation.java
@@ -34,6 +34,8 @@
import gov.nasa.worldwind.util.*;
import com.jogamp.opengl.*;
+import com.jogamp.opengl.util.awt.TextRenderer;
+
import java.awt.*;
/**
diff --git a/src/gov/nasa/worldwind/render/DeclutteringTextRenderer.java b/src/gov/nasa/worldwind/render/DeclutteringTextRenderer.java
index 81faf5ce86..394e81e879 100644
--- a/src/gov/nasa/worldwind/render/DeclutteringTextRenderer.java
+++ b/src/gov/nasa/worldwind/render/DeclutteringTextRenderer.java
@@ -36,6 +36,8 @@
import com.jogamp.opengl.*;
import com.jogamp.opengl.glu.GLU;
import com.jogamp.opengl.glu.gl2.GLUgl2;
+import com.jogamp.opengl.util.awt.TextRenderer;
+
import java.awt.*;
import java.awt.geom.*;
import java.io.IOException;
diff --git a/src/gov/nasa/worldwind/render/DrawContext.java b/src/gov/nasa/worldwind/render/DrawContext.java
index ffc935a719..c6e8567a5e 100644
--- a/src/gov/nasa/worldwind/render/DrawContext.java
+++ b/src/gov/nasa/worldwind/render/DrawContext.java
@@ -1051,4 +1051,11 @@ public interface DrawContext extends WWObject, Disposable
* Convert AWT effective screen location to GL surface location using DPI scaling.
*/
int [] awtPointToGLpoint(Point pt);
+
+ /**
+ * Convert GL surface coordinate point to AWT device point using DPI scaling.
+ * @param glPoint
+ * @return
+ */
+ public Point glPointToAwtPoint(Point glPoint);
}
diff --git a/src/gov/nasa/worldwind/render/DrawContextImpl.java b/src/gov/nasa/worldwind/render/DrawContextImpl.java
index eaed5cc1c7..c24100b162 100644
--- a/src/gov/nasa/worldwind/render/DrawContextImpl.java
+++ b/src/gov/nasa/worldwind/render/DrawContextImpl.java
@@ -1752,7 +1752,17 @@ else if (altitudeMode == WorldWind.RELATIVE_TO_GROUND)
// Convert to GL surface coordinates
int [] glSurfacePt = drawable.getNativeSurface().convertToPixelUnits(awtPt);
int glSurfaceHeight = drawable.getSurfaceHeight();
- glSurfacePt[1] = glSurfaceHeight - glSurfacePt[1] - 1;
+ glSurfacePt[1] = glSurfaceHeight-1 - glSurfacePt[1];
return glSurfacePt;
}
+
+ public Point glPointToAwtPoint(Point glPoint) {
+ GLDrawable drawable = glContext.getGLDrawable();
+ if (drawable == null) return glPoint;
+
+ final int viewportHeight = getView().getViewport().height;
+ int [] glPt = { glPoint.x, viewportHeight-1 - glPoint.y };
+ getGLDrawable().getNativeSurface().convertToWindowUnits(glPt);
+ return new Point(glPt[0], glPt[1]);
+ }
}
diff --git a/src/gov/nasa/worldwind/render/GeographicTextRenderer.java b/src/gov/nasa/worldwind/render/GeographicTextRenderer.java
index a362b387dc..d3aa21fa5d 100644
--- a/src/gov/nasa/worldwind/render/GeographicTextRenderer.java
+++ b/src/gov/nasa/worldwind/render/GeographicTextRenderer.java
@@ -38,6 +38,8 @@
import com.jogamp.opengl.*;
import com.jogamp.opengl.glu.GLU;
import com.jogamp.opengl.glu.gl2.GLUgl2;
+import com.jogamp.opengl.util.awt.TextRenderer;
+
import java.awt.*;
import java.awt.geom.*;
import java.io.IOException;
diff --git a/src/gov/nasa/worldwind/render/MultiLineTextRenderer.java b/src/gov/nasa/worldwind/render/MultiLineTextRenderer.java
index d8677398b8..201b35eb64 100644
--- a/src/gov/nasa/worldwind/render/MultiLineTextRenderer.java
+++ b/src/gov/nasa/worldwind/render/MultiLineTextRenderer.java
@@ -33,6 +33,8 @@
import gov.nasa.worldwind.util.*;
import com.jogamp.opengl.*;
+import com.jogamp.opengl.util.awt.TextRenderer;
+
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
diff --git a/src/gov/nasa/worldwind/render/PointPlacemark.java b/src/gov/nasa/worldwind/render/PointPlacemark.java
index 93c6e2d5c8..9ac04e0b57 100644
--- a/src/gov/nasa/worldwind/render/PointPlacemark.java
+++ b/src/gov/nasa/worldwind/render/PointPlacemark.java
@@ -40,6 +40,8 @@
import gov.nasa.worldwind.util.*;
import com.jogamp.opengl.*;
+import com.jogamp.opengl.util.awt.TextRenderer;
+
import javax.xml.stream.*;
import java.awt.*;
import java.awt.geom.*;
diff --git a/src/gov/nasa/worldwind/render/ScreenImage.java b/src/gov/nasa/worldwind/render/ScreenImage.java
index 69c33b5cb7..488d283002 100644
--- a/src/gov/nasa/worldwind/render/ScreenImage.java
+++ b/src/gov/nasa/worldwind/render/ScreenImage.java
@@ -78,13 +78,6 @@ public class ScreenImage extends WWObjectImpl implements Renderable, Exportable
* is computed in computeOffsets and used in draw Initially null.
*/
protected Point screenLocation;
- /**
- * Indicates the location of this screen image in the viewport (on the screen) in AWT coordinates. This property is
- * assigned in setScreenLocation and computeOffsets. In computeOffsets, this
- * is computed by converting the screenLocation from OpenGL coordinates to AWT coordinates. Initially
- * null.
- */
- protected Point awtScreenLocation;
protected double dx;
protected double dy;
protected Layer pickLayer;
@@ -122,7 +115,7 @@ public void render(DrawContext dc)
*/
public Point getScreenLocation()
{
- return this.awtScreenLocation;
+ return this.screenLocation;
}
/**
@@ -136,7 +129,7 @@ public Point getScreenLocation()
public Point getScreenLocation(DrawContext dc)
{
this.computeOffsets(dc);
- return this.awtScreenLocation;
+ return this.screenLocation;
}
/**
@@ -151,17 +144,16 @@ public Point getScreenLocation(DrawContext dc)
*/
public void setScreenLocation(Point screenLocation)
{
- // Use units PIXELS for the X screen offset, and and INSET_PIXELS for the Y screen offset. The Offset is in
- // OpenGL coordinates with the origin in the lower-left corner, but the Point is in AWT coordinates with the
- // origin in the upper-left corner. This offset translates the origin from the lower-left to the upper-left
- // corner.
- this.screenOffset = new Offset(screenLocation.getX(), screenLocation.getY(), AVKey.PIXELS, AVKey.INSET_PIXELS);
+ // Use units PIXELS for the X screen offset, and and PIXELS for the Y screen offset. The Offset is in
+ // OpenGL coordinates with the origin in the lower-left corner, as is the Point. This offset
+ // translates the origin from the lower-left to the upper-left corner.
+ this.screenOffset = new Offset(screenLocation.getX(), screenLocation.getY(), AVKey.PIXELS, AVKey.PIXELS);
this.imageOffset = new Offset(0.5, 0.5, AVKey.FRACTION, AVKey.FRACTION);
// Set cached screen location to the initial screen location so that it can be retrieved if getScreenLocation()
// is called before the image is rendered. This maintains backward compatibility with the previous behavior of
// ScreenImage.
- this.awtScreenLocation = new Point(screenLocation);
+ this.screenLocation = new Point(screenLocation);
}
/**
@@ -544,12 +536,7 @@ else if (this.getImageSource() == null) // If no image source is set, draw a rec
{
this.screenLocation = new Point(viewportWidth / 2, viewportHeight / 2);
}
-
- // Convert the screen location from OpenGL to AWT coordinates and store the result in awtScreenLocation. The
- // awtScreenLocation property is used in getScreenLocation to indicate the screen location in AWT
- // coordinates.
- this.awtScreenLocation = new Point(this.screenLocation.x, viewportHeight - this.screenLocation.y);
-
+
Point.Double overlayPoint;
if (this.imageOffset != null)
overlayPoint = this.imageOffset.computeOffset(this.width, this.height, null, null);
diff --git a/src/gov/nasa/worldwind/render/SurfaceText.java b/src/gov/nasa/worldwind/render/SurfaceText.java
index bdb5490b3a..74386e9aae 100644
--- a/src/gov/nasa/worldwind/render/SurfaceText.java
+++ b/src/gov/nasa/worldwind/render/SurfaceText.java
@@ -36,6 +36,8 @@
import gov.nasa.worldwind.util.*;
import com.jogamp.opengl.GL2;
+import com.jogamp.opengl.util.awt.TextRenderer;
+
import java.awt.*;
import java.awt.geom.*;
import java.util.Arrays;
diff --git a/src/gov/nasa/worldwind/render/TextRenderer.java b/src/gov/nasa/worldwind/render/TextRenderer.java
deleted file mode 100644
index d25c14f4fc..0000000000
--- a/src/gov/nasa/worldwind/render/TextRenderer.java
+++ /dev/null
@@ -1,2108 +0,0 @@
-/*
- * Copyright 2006-2009, 2017, 2020 United States Government, as represented by the
- * Administrator of the National Aeronautics and Space Administration.
- * All rights reserved.
- *
- * The NASA World Wind Java (WWJ) platform is licensed under the Apache License,
- * Version 2.0 (the "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed
- * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
- * CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- *
- * NASA World Wind Java (WWJ) also contains the following 3rd party Open Source
- * software:
- *
- * Jackson Parser – Licensed under Apache 2.0
- * GDAL – Licensed under MIT
- * JOGL – Licensed under Berkeley Software Distribution (BSD)
- * Gluegen – Licensed under Berkeley Software Distribution (BSD)
- *
- * A complete listing of 3rd Party software notices and licenses included in
- * NASA World Wind Java (WWJ) can be found in the WorldWindJava-v2.2 3rd-party
- * notices and licenses PDF found in code directory.
- */
-/*
- * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
- * Copyright (c) 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:
- *
- * - Redistribution of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistribution 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.
- *
- * Neither the name of Sun Microsystems, Inc. or the names of
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * This software is provided "AS IS," without a warranty of any kind. ALL
- * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
- * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
- * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
- * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
- * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
- * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
- * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
- * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
- * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
- * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
- * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
- *
- * You acknowledge that this software is not designed or intended for use
- * in the design, construction, operation or maintenance of any nuclear
- * facility.
- *
- * Sun gratefully acknowledges that this software was originally authored
- * and developed by Kenneth Bradley Russell and Christopher John Kline.
- */
-/**
- * @version $Id: TextRenderer.java 2387 2014-10-15 20:25:02Z tgaskins $
- */
-
-package gov.nasa.worldwind.render;
-
-import com.jogamp.common.nio.Buffers;
-import com.jogamp.nativewindow.NativeSurface;
-import com.jogamp.nativewindow.ScalableSurface;
-import com.jogamp.opengl.GLExtensions;
-import com.jogamp.opengl.util.FPSAnimator;
-import com.jogamp.opengl.util.awt.TextureRenderer;
-import com.jogamp.opengl.util.packrect.*;
-import com.jogamp.opengl.util.texture.TextureCoords;
-import jogamp.opengl.Debug;
-
-import com.jogamp.opengl.*;
-import com.jogamp.opengl.awt.GLCanvas;
-import com.jogamp.opengl.glu.GLU;
-import java.awt.*;
-import java.awt.event.*;
-import java.awt.font.*;
-import java.awt.geom.*;
-import java.nio.*;
-import java.text.CharacterIterator;
-import java.util.*;
-import java.util.List;
-
-// For debugging purposes
-
-
-/** Renders bitmapped Java 2D text into an OpenGL window with high
- performance, full Unicode support, and a simple API. Performs
- appropriate caching of text rendering results in an OpenGL texture
- internally to avoid repeated font rasterization. The caching is
- completely automatic, does not require any user intervention, and
- has no visible controls in the public API.
-
- Using the {@link TextRenderer TextRenderer} is simple. Add a
- "TextRenderer renderer;" field to your {@link
- com.jogamp.opengl.GLEventListener GLEventListener}. In your {@link
- com.jogamp.opengl.GLEventListener#init init} method, add:
-
-
- renderer = new TextRenderer(new Font("SansSerif", Font.BOLD, 36));
-
-
- In the {@link com.jogamp.opengl.GLEventListener#display display} method of your
- {@link com.jogamp.opengl.GLEventListener GLEventListener}, add:
-
- renderer.beginRendering(drawable.getWidth(), drawable.getHeight());
- // optionally set the color
- renderer.setColor(1.0f, 0.2f, 0.2f, 0.8f);
- renderer.draw("Text to draw", xPosition, yPosition);
- // ... more draw commands, color changes, etc.
- renderer.endRendering();
-
-
- Unless you are sharing textures and display lists between OpenGL
- contexts, you do not need to call the {@link #dispose dispose}
- method of the TextRenderer; the OpenGL resources it uses
- internally will be cleaned up automatically when the OpenGL
- context is destroyed.
-
- Note that the TextRenderer may cause the vertex and texture
- coordinate array buffer bindings to change, or to be unbound. This
- is important to note if you are using Vertex Buffer Objects (VBOs)
- in your application.
-
- Internally, the renderer uses a rectangle packing algorithm to
- pack both glyphs and full Strings' rendering results (which are
- variable size) onto a larger OpenGL texture. The internal backing
- store is maintained using a {@link
- com.jogamp.opengl.util.awt.TextureRenderer TextureRenderer}. A least
- recently used (LRU) algorithm is used to discard previously
- rendered strings; the specific algorithm is undefined, but is
- currently implemented by flushing unused Strings' rendering
- results every few hundred rendering cycles, where a rendering
- cycle is defined as a pair of calls to {@link #beginRendering
- beginRendering} / {@link #endRendering endRendering}.
-
- @author John Burkey
- @author Kenneth Russell
-*/
-public class TextRenderer {
- private static final boolean DEBUG;
-
- static {
- Debug.initSingleton();
- DEBUG = Debug.isPropertyDefined("jogl.debug.TextRenderer", true);
- }
-
- // These are occasionally useful for more in-depth debugging
- private static boolean DISABLE_GLYPH_CACHE = false;
- private static final boolean DRAW_BBOXES = false;
- static
- {
- String arg = System.getProperty("gov.nasa.worldwind.textrender.useglyphcache");
- if (arg != null && arg.toLowerCase().startsWith("f"))
- DISABLE_GLYPH_CACHE = true;
- }
-
- static final int kSize = 256;
-
- // Every certain number of render cycles, flush the strings which
- // haven't been used recently
- private static final int CYCLES_PER_FLUSH = 100;
-
- // The amount of vertical dead space on the backing store before we
- // force a compaction
- private static final float MAX_VERTICAL_FRAGMENTATION = 0.7f;
- static final int kQuadsPerBuffer = 100;
- static final int kCoordsPerVertVerts = 3;
- static final int kCoordsPerVertTex = 2;
- static final int kVertsPerQuad = 4;
- static final int kTotalBufferSizeVerts = kQuadsPerBuffer * kVertsPerQuad;
- static final int kTotalBufferSizeCoordsVerts = kQuadsPerBuffer * kVertsPerQuad * kCoordsPerVertVerts;
- static final int kTotalBufferSizeCoordsTex = kQuadsPerBuffer * kVertsPerQuad * kCoordsPerVertTex;
- static final int kTotalBufferSizeBytesVerts = kTotalBufferSizeCoordsVerts * 4;
- static final int kTotalBufferSizeBytesTex = kTotalBufferSizeCoordsTex * 4;
- static final int kSizeInBytes_OneVertices_VertexData = kCoordsPerVertVerts * 4;
- static final int kSizeInBytes_OneVertices_TexData = kCoordsPerVertTex * 4;
- private final Font font;
- private final boolean antialiased;
- private final boolean useFractionalMetrics;
-
- // Whether we're attempting to use automatic mipmap generation support
- private boolean mipmap;
- private RectanglePacker packer;
- private boolean haveMaxSize;
- private final RenderDelegate renderDelegate;
- private TextureRenderer cachedBackingStore;
- private Graphics2D cachedGraphics;
- private FontRenderContext cachedFontRenderContext;
- private final Map stringLocations = new HashMap();
- private final GlyphProducer mGlyphProducer;
-
- private int numRenderCycles;
-
- // Need to keep track of whether we're in a beginRendering() /
- // endRendering() cycle so we can re-enter the exact same state if
- // we have to reallocate the backing store
- private boolean inBeginEndPair;
- private boolean isOrthoMode;
- private int beginRenderingWidth;
- private int beginRenderingHeight;
- private boolean beginRenderingDepthTestDisabled;
-
- // For resetting the color after disposal of the old backing store
- private boolean haveCachedColor;
- private float cachedR;
- private float cachedG;
- private float cachedB;
- private float cachedA;
- private Color cachedColor;
- private boolean needToResetColor;
-
- // For debugging only
- private Frame dbgFrame;
-
- // Debugging purposes only
- private boolean debugged;
- Pipelined_QuadRenderer mPipelinedQuadRenderer;
-
- //emzic: added boolean flag
- private boolean useVertexArrays = true;
-
- //emzic: added boolean flag
- private boolean isExtensionAvailable_GL_VERSION_1_5;
- private boolean checkFor_isExtensionAvailable_GL_VERSION_1_5;
-
- // Whether GL_LINEAR filtering is enabled for the backing store
- private boolean smoothing = true;
-
- /** Creates a new TextRenderer with the given font, using no
- antialiasing or fractional metrics, and the default
- RenderDelegate. Equivalent to TextRenderer(font, false,
- false).
-
- @param font the font to render with
- */
- public TextRenderer(Font font) {
- this(font, false, false, null, false);
- }
-
- /** Creates a new TextRenderer with the given font, using no
- antialiasing or fractional metrics, and the default
- RenderDelegate. If mipmap is true, attempts to use
- OpenGL's automatic mipmap generation for better smoothing when
- rendering the TextureRenderer's contents at a distance.
- Equivalent to TextRenderer(font, false, false).
-
- @param font the font to render with
- @param mipmap whether to attempt use of automatic mipmap generation
- */
- public TextRenderer(Font font, boolean mipmap) {
- this(font, false, false, null, mipmap);
- }
-
- /** Creates a new TextRenderer with the given Font, specified font
- properties, and default RenderDelegate. The
- antialiased and useFractionalMetrics
- flags provide control over the same properties at the Java 2D
- level. No mipmap support is requested. Equivalent to
- TextRenderer(font, antialiased, useFractionalMetrics,
- null).
-
- @param font the font to render with
- @param antialiased whether to use antialiased fonts
- @param useFractionalMetrics whether to use fractional font
- metrics at the Java 2D level
- */
- public TextRenderer(Font font, boolean antialiased,
- boolean useFractionalMetrics) {
- this(font, antialiased, useFractionalMetrics, null, false);
- }
-
- /** Creates a new TextRenderer with the given Font, specified font
- properties, and given RenderDelegate. The
- antialiased and useFractionalMetrics
- flags provide control over the same properties at the Java 2D
- level. The renderDelegate provides more control
- over the text rendered. No mipmap support is requested.
-
- @param font the font to render with
- @param antialiased whether to use antialiased fonts
- @param useFractionalMetrics whether to use fractional font
- metrics at the Java 2D level
- @param renderDelegate the render delegate to use to draw the
- text's bitmap, or null to use the default one
- */
- public TextRenderer(Font font, boolean antialiased,
- boolean useFractionalMetrics, RenderDelegate renderDelegate) {
- this(font, antialiased, useFractionalMetrics, renderDelegate, false);
- }
-
- /** Creates a new TextRenderer with the given Font, specified font
- properties, and given RenderDelegate. The
- antialiased and useFractionalMetrics
- flags provide control over the same properties at the Java 2D
- level. The renderDelegate provides more control
- over the text rendered. If mipmap is true, attempts
- to use OpenGL's automatic mipmap generation for better smoothing
- when rendering the TextureRenderer's contents at a distance.
-
- @param font the font to render with
- @param antialiased whether to use antialiased fonts
- @param useFractionalMetrics whether to use fractional font
- metrics at the Java 2D level
- @param renderDelegate the render delegate to use to draw the
- text's bitmap, or null to use the default one
- @param mipmap whether to attempt use of automatic mipmap generation
- */
- public TextRenderer(Font font, boolean antialiased,
- boolean useFractionalMetrics, RenderDelegate renderDelegate,
- boolean mipmap) {
-
- NativeSurface surface = GLContext.getCurrent().getGLDrawable().getNativeSurface();
- if (surface instanceof ScalableSurface) {
- // DPI scaling for surface
- float[] surfaceScale = new float[2];
- ((ScalableSurface) surface).getCurrentSurfaceScale(surfaceScale);
- float newSize = font.getSize() * surfaceScale[0];
- this.font = font.deriveFont(newSize);
- } else {
- this.font = font;
- }
-
- this.antialiased = antialiased;
- this.useFractionalMetrics = useFractionalMetrics;
- this.mipmap = mipmap;
-
- // FIXME: consider adjusting the size based on font size
- // (it will already automatically resize if necessary)
- packer = new RectanglePacker(new Manager(), kSize, kSize);
-
- if (renderDelegate == null) {
- renderDelegate = new DefaultRenderDelegate();
- }
-
- this.renderDelegate = renderDelegate;
-
- mGlyphProducer = new GlyphProducer(font.getNumGlyphs());
- }
-
- /**
- * Returns the bounding rectangle of the given String, assuming it was rendered at the origin.See
- * {@link #getBounds(CharSequence) getBounds(CharSequence)}.
- *
- * @param str The string.
- * @return The bounding rectangle for str.
- */
- public Rectangle2D getBounds(String str) {
- return getBounds((CharSequence) str);
- }
-
- /**
- * Returns the bounding rectangle of the given CharSequence, assuming it was rendered at the origin.The coordinate
- * system of the returned rectangle is Java 2D's, with increasing Y coordinates in the downward direction.The
- * relative coordinate (0, 0) in the returned rectangle corresponds to the baseline of the leftmost character of the
- * rendered string, in similar fashion to the results returned by, for example,
- * {@link java.awt.font.GlyphVector#getVisualBounds}. Most applications will use only the width and height of the
- * returned Rectangle for the purposes of centering or justifying the String. It is not specified which Java 2D
- * bounds ({@link
- * java.awt.font.GlyphVector#getVisualBounds getVisualBounds},
- * {@link java.awt.font.GlyphVector#getPixelBounds getPixelBounds}, etc.) the returned bounds correspond to,
- * although every effort is made to ensure an accurate bound.
- *
- * @param str The string.
- * @return The bounds of the string.
- */
- public Rectangle2D getBounds(CharSequence str) {
- // FIXME: this should be more optimized and use the glyph cache
- Rect r = stringLocations.get(str);
-
- if (r != null) {
- TextData data = (TextData) r.getUserData();
-
- // Reconstitute the Java 2D results based on the cached values
- return new Rectangle2D.Double(-data.origin().x, -data.origin().y,
- r.w(), r.h());
- }
-
- // Must return a Rectangle compatible with the layout algorithm --
- // must be idempotent
- return normalize(renderDelegate.getBounds(str, font,
- getFontRenderContext()));
- }
-
- /**
- * Returns the Font this renderer is using.
- *
- * @return The Font.
- */
- public Font getFont() {
- return font;
- }
-
- /**
- * * Returns a FontRenderContext which can be used for external text-related size computations.This object should be
- * considered transient and may become invalidated between
- * {@link #beginRendering beginRendering} / {@link #endRendering endRendering} pairs.
- *
- * @return A FontRenderContext.
- */
- public FontRenderContext getFontRenderContext() {
- if (cachedFontRenderContext == null) {
- cachedFontRenderContext = getGraphics2D().getFontRenderContext();
- }
-
- return cachedFontRenderContext;
- }
-
- /** Begins rendering with this {@link TextRenderer TextRenderer}
- into the current OpenGL drawable, pushing the projection and
- modelview matrices and some state bits and setting up a
- two-dimensional orthographic projection with (0, 0) as the
- lower-left coordinate and (width, height) as the upper-right
- coordinate. Binds and enables the internal OpenGL texture
- object, sets the texture environment mode to GL_MODULATE, and
- changes the current color to the last color set with this
- TextRenderer via {@link #setColor setColor}. This method
- disables the depth test and is equivalent to
- beginRendering(width, height, true).
-
- @param width the width of the current on-screen OpenGL drawable
- @param height the height of the current on-screen OpenGL drawable
- @throws com.jogamp.opengl.GLException If an OpenGL context is not current when this method is called
- */
- public void beginRendering(int width, int height) throws GLException {
- beginRendering(width, height, true);
- }
-
- /** Begins rendering with this {@link TextRenderer TextRenderer}
- into the current OpenGL drawable, pushing the projection and
- modelview matrices and some state bits and setting up a
- two-dimensional orthographic projection with (0, 0) as the
- lower-left coordinate and (width, height) as the upper-right
- coordinate. Binds and enables the internal OpenGL texture
- object, sets the texture environment mode to GL_MODULATE, and
- changes the current color to the last color set with this
- TextRenderer via {@link #setColor setColor}. Disables the depth
- test if the disableDepthTest argument is true.
-
- @param width the width of the current on-screen OpenGL drawable
- @param height the height of the current on-screen OpenGL drawable
- @param disableDepthTest whether to disable the depth test
- @throws GLException If an OpenGL context is not current when this method is called
- */
- public void beginRendering(int width, int height, boolean disableDepthTest)
- throws GLException {
- beginRendering(true, width, height, disableDepthTest);
- }
-
- /** Begins rendering of 2D text in 3D with this {@link TextRenderer
- TextRenderer} into the current OpenGL drawable. Assumes the end
- user is responsible for setting up the modelview and projection
- matrices, and will render text using the {@link #draw3D draw3D}
- method. This method pushes some OpenGL state bits, binds and
- enables the internal OpenGL texture object, sets the texture
- environment mode to GL_MODULATE, and changes the current color
- to the last color set with this TextRenderer via {@link
- #setColor setColor}.
-
- @throws GLException If an OpenGL context is not current when this method is called
- */
- public void begin3DRendering() throws GLException {
- beginRendering(false, 0, 0, false);
- }
-
- /** Changes the current color of this TextRenderer to the supplied
- one. The default color is opaque white.
-
- @param color the new color to use for rendering text
- @throws GLException If an OpenGL context is not current when this method is called
- */
- public void setColor(Color color) throws GLException {
- boolean noNeedForFlush = (haveCachedColor && (cachedColor != null) &&
- color.equals(cachedColor));
-
- if (!noNeedForFlush) {
- flushGlyphPipeline();
- }
-
- getBackingStore().setColor(color);
- haveCachedColor = true;
- cachedColor = color;
- }
-
- /** Changes the current color of this TextRenderer to the supplied
- one, where each component ranges from 0.0f - 1.0f. The alpha
- component, if used, does not need to be premultiplied into the
- color channels as described in the documentation for {@link
- com.jogamp.opengl.util.texture.Texture Texture}, although
- premultiplied colors are used internally. The default color is
- opaque white.
-
- @param r the red component of the new color
- @param g the green component of the new color
- @param b the blue component of the new color
- @param a the alpha component of the new color, 0.0f = completely
- transparent, 1.0f = completely opaque
- @throws GLException If an OpenGL context is not current when this method is called
- */
- public void setColor(float r, float g, float b, float a)
- throws GLException {
- boolean noNeedForFlush = (haveCachedColor && (cachedColor == null) &&
- (r == cachedR) && (g == cachedG) && (b == cachedB) &&
- (a == cachedA));
-
- if (!noNeedForFlush) {
- flushGlyphPipeline();
- }
-
- getBackingStore().setColor(r, g, b, a);
- haveCachedColor = true;
- cachedR = r;
- cachedG = g;
- cachedB = b;
- cachedA = a;
- cachedColor = null;
- }
-
- /** Draws the supplied CharSequence at the desired location using
- the renderer's current color. The baseline of the leftmost
- character is at position (x, y) specified in OpenGL coordinates,
- where the origin is at the lower-left of the drawable and the Y
- coordinate increases in the upward direction.
-
- @param str the string to draw
- @param x the x coordinate at which to draw
- @param y the y coordinate at which to draw
- @throws GLException If an OpenGL context is not current when this method is called
- */
- public void draw(CharSequence str, int x, int y) throws GLException {
- draw3D(str, x, y, 0, 1);
- }
-
- /**
- * * Draws the supplied String at the desired location using the renderer's current color.See
- * {@link #draw(CharSequence, int, int) draw(CharSequence, int, int)}.
- *
- * @param str The string to draw.
- * @param x The desired x location.
- * @param y The desired y location.
- */
- public void draw(String str, int x, int y) throws GLException {
- draw3D(str, x, y, 0, 1);
- }
-
- /** Draws the supplied CharSequence at the desired 3D location using
- the renderer's current color. The baseline of the leftmost
- character is placed at position (x, y, z) in the current
- coordinate system.
-
- @param str the string to draw
- @param x the x coordinate at which to draw
- @param y the y coordinate at which to draw
- @param z the z coordinate at which to draw
- @param scaleFactor a uniform scale factor applied to the width and height of the drawn rectangle
- @throws GLException If an OpenGL context is not current when this method is called
- */
- public void draw3D(CharSequence str, float x, float y, float z,
- float scaleFactor) {
- internal_draw3D(str, x, y, z, scaleFactor);
- }
-
- /**
- * * Draws the supplied String at the desired 3D location using the renderer's current color.See {@link #draw3D(CharSequence, float, float, float, float) draw3D(CharSequence, float, float,
- * float, float)}.
- *
- * @param str The string to draw.
- * @param x The x location.
- * @param y The y location.
- * @param z The z location.
- * @param scaleFactor The scale factor.
- */
- public void draw3D(String str, float x, float y, float z, float scaleFactor) {
- internal_draw3D(str, x, y, z, scaleFactor);
- }
-
- /**
- * Returns the pixel width of the given character.
- *
- * @param inChar The character to measure.
- * @return The pixel width.
- */
- public float getCharWidth(char inChar) {
- return mGlyphProducer.getGlyphPixelWidth(inChar);
- }
-
- /** Causes the TextRenderer to flush any internal caches it may be
- maintaining and draw its rendering results to the screen. This
- should be called after each call to draw() if you are setting
- OpenGL state such as the modelview matrix between calls to
- draw(). */
- public void flush() {
- flushGlyphPipeline();
- }
-
- /** Ends a render cycle with this {@link TextRenderer TextRenderer}.
- Restores the projection and modelview matrices as well as
- several OpenGL state bits. Should be paired with {@link
- #beginRendering beginRendering}.
-
- @throws GLException If an OpenGL context is not current when this method is called
- */
- public void endRendering() throws GLException {
- endRendering(true);
- }
-
- /** Ends a 3D render cycle with this {@link TextRenderer TextRenderer}.
- Restores several OpenGL state bits. Should be paired with {@link
- #begin3DRendering begin3DRendering}.
-
- @throws GLException If an OpenGL context is not current when this method is called
- */
- public void end3DRendering() throws GLException {
- endRendering(false);
- }
-
- /** Disposes of all resources this TextRenderer is using. It is not
- valid to use the TextRenderer after this method is called.
-
- @throws GLException If an OpenGL context is not current when this method is called
- */
- public void dispose() throws GLException {
- packer.dispose();
- packer = null;
- cachedBackingStore = null;
- cachedGraphics = null;
- cachedFontRenderContext = null;
-
- if (dbgFrame != null) {
- dbgFrame.dispose();
- }
- }
-
- //----------------------------------------------------------------------
- // Internals only below this point
- //
-
- private static Rectangle2D preNormalize(Rectangle2D src) {
- // Need to round to integer coordinates
- // Also give ourselves a little slop around the reported
- // bounds of glyphs because it looks like neither the visual
- // nor the pixel bounds works perfectly well
- int minX = (int) Math.floor(src.getMinX()) - 1;
- int minY = (int) Math.floor(src.getMinY()) - 1;
- int maxX = (int) Math.ceil(src.getMaxX()) + 1;
- int maxY = (int) Math.ceil(src.getMaxY()) + 1;
- return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
- }
-
-
- private Rectangle2D normalize(Rectangle2D src) {
- // Give ourselves a boundary around each entity on the backing
- // store in order to prevent bleeding of nearby Strings due to
- // the fact that we use linear filtering
-
- // NOTE that this boundary is quite heuristic and is related
- // to how far away in 3D we may view the text --
- // heuristically, 1.5% of the font's height
- int boundary = (int) Math.max(1, 0.015 * font.getSize());
-
- return new Rectangle2D.Double((int) Math.floor(src.getMinX() - boundary),
- (int) Math.floor(src.getMinY() - boundary),
- (int) Math.ceil(src.getWidth() + 2 * boundary),
- (int) Math.ceil(src.getHeight()) + 2 * boundary);
- }
-
- private TextureRenderer getBackingStore() {
- TextureRenderer renderer = (TextureRenderer) packer.getBackingStore();
-
- if (renderer != cachedBackingStore) {
- // Backing store changed since last time; discard any cached Graphics2D
- if (cachedGraphics != null) {
- cachedGraphics.dispose();
- cachedGraphics = null;
- cachedFontRenderContext = null;
- }
-
- cachedBackingStore = renderer;
- }
-
- return cachedBackingStore;
- }
-
- private Graphics2D getGraphics2D() {
- TextureRenderer renderer = getBackingStore();
-
- if (cachedGraphics == null) {
- cachedGraphics = renderer.createGraphics();
-
- // Set up composite, font and rendering hints
- cachedGraphics.setComposite(AlphaComposite.Src);
- cachedGraphics.setColor(Color.WHITE);
- cachedGraphics.setFont(font);
- cachedGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
- (antialiased ? RenderingHints.VALUE_TEXT_ANTIALIAS_ON
- : RenderingHints.VALUE_TEXT_ANTIALIAS_OFF));
- cachedGraphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
- (useFractionalMetrics
- ? RenderingHints.VALUE_FRACTIONALMETRICS_ON
- : RenderingHints.VALUE_FRACTIONALMETRICS_OFF));
- }
-
- return cachedGraphics;
- }
-
- private void beginRendering(boolean ortho, int width, int height,
- boolean disableDepthTestForOrtho) {
- GL2 gl = GLContext.getCurrentGL().getGL2();
-
- if (DEBUG && !debugged) {
- debug(gl);
- }
-
- inBeginEndPair = true;
- isOrthoMode = ortho;
- beginRenderingWidth = width;
- beginRenderingHeight = height;
- beginRenderingDepthTestDisabled = disableDepthTestForOrtho;
-
- if (ortho) {
- getBackingStore().beginOrthoRendering(width, height,
- disableDepthTestForOrtho);
- } else {
- getBackingStore().begin3DRendering();
- }
-
- // Push client attrib bits used by the pipelined quad renderer
- gl.glPushClientAttrib((int) GL2.GL_ALL_CLIENT_ATTRIB_BITS);
-
- if (!haveMaxSize) {
- // Query OpenGL for the maximum texture size and set it in the
- // RectanglePacker to keep it from expanding too large
- int[] sz = new int[1];
- gl.glGetIntegerv(GL2.GL_MAX_TEXTURE_SIZE, sz, 0);
- packer.setMaxSize(sz[0], sz[0]);
- haveMaxSize = true;
- }
-
- if (needToResetColor && haveCachedColor) {
- if (cachedColor == null) {
- getBackingStore().setColor(cachedR, cachedG, cachedB, cachedA);
- } else {
- getBackingStore().setColor(cachedColor);
- }
-
- needToResetColor = false;
- }
-
- // Disable future attempts to use mipmapping if TextureRenderer
- // doesn't support it
- if (mipmap && !getBackingStore().isUsingAutoMipmapGeneration()) {
- if (DEBUG) {
- System.err.println("Disabled mipmapping in TextRenderer");
- }
-
- mipmap = false;
- }
- }
-
- /**
- * emzic: here the call to glBindBuffer crashes on certain graphicscard/driver combinations
- * this is why the ugly try-catch block has been added, which falls back to the old textrenderer
- *
- * @param ortho
- * @throws GLException
- */
- private void endRendering(boolean ortho) throws GLException {
- flushGlyphPipeline();
-
- inBeginEndPair = false;
-
- GL2 gl = GLContext.getCurrentGL().getGL2();
-
- // Pop client attrib bits used by the pipelined quad renderer
- gl.glPopClientAttrib();
-
- // The OpenGL spec is unclear about whether this changes the
- // buffer bindings, so preemptively zero out the GL_ARRAY_BUFFER
- // binding
- if (getUseVertexArrays() && is15Available(gl)) {
- try {
- gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0);
- } catch (Exception e) {
- isExtensionAvailable_GL_VERSION_1_5 = false;
- }
- }
-
- if (ortho) {
- getBackingStore().endOrthoRendering();
- } else {
- getBackingStore().end3DRendering();
- }
-
- if (++numRenderCycles >= CYCLES_PER_FLUSH) {
- numRenderCycles = 0;
-
- if (DEBUG) {
- System.err.println("Clearing unused entries in endRendering()");
- }
-
- clearUnusedEntries();
- }
- }
-
- private void clearUnusedEntries() {
- final java.util.List deadRects = new ArrayList();
-
- // Iterate through the contents of the backing store, removing
- // text strings that haven't been used recently
- packer.visit(new RectVisitor() {
- @Override
- public void visit(Rect rect) {
- TextData data = (TextData) rect.getUserData();
-
- if (data.used()) {
- data.clearUsed();
- } else {
- deadRects.add(rect);
- }
- }
- });
-
- for (Rect r : deadRects) {
- packer.remove(r);
- stringLocations.remove(((TextData) r.getUserData()).string());
-
- int unicodeToClearFromCache = ((TextData) r.getUserData()).unicodeID;
-
- if (unicodeToClearFromCache > 0) {
- mGlyphProducer.clearCacheEntry(unicodeToClearFromCache);
- }
-
- // if (DEBUG) {
- // Graphics2D g = getGraphics2D();
- // g.setComposite(AlphaComposite.Clear);
- // g.fillRect(r.x(), r.y(), r.w(), r.h());
- // g.setComposite(AlphaComposite.Src);
- // }
- }
-
- // If we removed dead rectangles this cycle, try to do a compaction
- float frag = packer.verticalFragmentationRatio();
-
- if (!deadRects.isEmpty() && (frag > MAX_VERTICAL_FRAGMENTATION)) {
- if (DEBUG) {
- System.err.println(
- "Compacting TextRenderer backing store due to vertical fragmentation " +
- frag);
- }
-
- packer.compact();
- }
-
- if (DEBUG) {
- getBackingStore().markDirty(0, 0, getBackingStore().getWidth(),
- getBackingStore().getHeight());
- }
- }
-
- private void internal_draw3D(CharSequence str, float x, float y, float z,
- float scaleFactor) {
- for (Glyph glyph : mGlyphProducer.getGlyphs(str)) {
- float advance = glyph.draw3D(x, y, z, scaleFactor);
- x += advance * scaleFactor;
- }
- }
-
- private void flushGlyphPipeline() {
- if (mPipelinedQuadRenderer != null) {
- mPipelinedQuadRenderer.draw();
- }
- }
-
- private void draw3D_ROBUST(CharSequence str, float x, float y, float z,
- float scaleFactor) {
- String curStr;
- if (str instanceof String) {
- curStr = (String) str;
- } else {
- curStr = str.toString();
- }
-
- // Look up the string on the backing store
- Rect rect = stringLocations.get(curStr);
-
- if (rect == null) {
- // Rasterize this string and place it on the backing store
- Graphics2D g = getGraphics2D();
- Rectangle2D origBBox = preNormalize(renderDelegate.getBounds(curStr, font, getFontRenderContext()));
- Rectangle2D bbox = normalize(origBBox);
- Point origin = new Point((int) -bbox.getMinX(),
- (int) -bbox.getMinY());
- rect = new Rect(0, 0, (int) bbox.getWidth(),
- (int) bbox.getHeight(),
- new TextData(curStr, origin, origBBox, -1));
-
- packer.add(rect);
- stringLocations.put(curStr, rect);
-
- // Re-fetch the Graphics2D in case the addition of the rectangle
- // caused the old backing store to be thrown away
- g = getGraphics2D();
-
- // OK, should now have an (x, y) for this rectangle; rasterize
- // the String
- int strx = rect.x() + origin.x;
- int stry = rect.y() + origin.y;
-
- // Clear out the area we're going to draw into
- g.setComposite(AlphaComposite.Clear);
- g.fillRect(rect.x(), rect.y(), rect.w(), rect.h());
- g.setComposite(AlphaComposite.Src);
-
- // Draw the string
- renderDelegate.draw(g, curStr, strx, stry);
-
- if (DRAW_BBOXES) {
- TextData data = (TextData) rect.getUserData();
- // Draw a bounding box on the backing store
- g.drawRect(strx - data.origOriginX(),
- stry - data.origOriginY(),
- (int) data.origRect().getWidth(),
- (int) data.origRect().getHeight());
- g.drawRect(strx - data.origin().x,
- stry - data.origin().y,
- rect.w(),
- rect.h());
- }
-
- // Mark this region of the TextureRenderer as dirty
- getBackingStore().markDirty(rect.x(), rect.y(), rect.w(),
- rect.h());
- }
-
- // OK, now draw the portion of the backing store to the screen
- TextureRenderer renderer = getBackingStore();
-
- // NOTE that the rectangles managed by the packer have their
- // origin at the upper-left but the TextureRenderer's origin is
- // at its lower left!!!
- TextData data = (TextData) rect.getUserData();
- data.markUsed();
-
- Rectangle2D origRect = data.origRect();
-
- // Align the leftmost point of the baseline to the (x, y, z) coordinate requested
- renderer.draw3DRect(x - (scaleFactor * data.origOriginX()),
- y - (scaleFactor * ((float) origRect.getHeight() - data.origOriginY())), z,
- rect.x() + (data.origin().x - data.origOriginX()),
- renderer.getHeight() - rect.y() - (int) origRect.getHeight() -
- (data.origin().y - data.origOriginY()),
- (int) origRect.getWidth(), (int) origRect.getHeight(), scaleFactor);
- }
-
- //----------------------------------------------------------------------
- // Debugging functionality
- //
- private void debug(GL gl) {
- dbgFrame = new Frame("TextRenderer Debug Output");
-
- GLCanvas dbgCanvas = new GLCanvas(new GLCapabilities(gl.getGLProfile()));
- dbgCanvas.setSharedContext(GLContext.getCurrent());
- dbgCanvas.addGLEventListener(new DebugListener(gl, dbgFrame));
- dbgFrame.add(dbgCanvas);
-
- final FPSAnimator anim = new FPSAnimator(dbgCanvas, 10);
- dbgFrame.addWindowListener(new WindowAdapter() {
- @Override
- public void windowClosing(WindowEvent e) {
- // Run this on another thread than the AWT event queue to
- // make sure the call to Animator.stop() completes before
- // exiting
- new Thread(new Runnable() {
- @Override
- public void run() {
- anim.stop();
- }
- }).start();
- }
- });
- dbgFrame.setSize(kSize, kSize);
- dbgFrame.setVisible(true);
- anim.start();
- debugged = true;
- }
-
- /** Class supporting more full control over the process of rendering
- the bitmapped text. Allows customization of whether the backing
- store text bitmap is full-color or intensity only, the size of
- each individual rendered text rectangle, and the contents of
- each individual rendered text string. The default implementation
- of this interface uses an intensity-only texture, a
- closely-cropped rectangle around the text, and renders text
- using the color white, which is modulated by the set color
- during the rendering process. */
- public static interface RenderDelegate {
- /**
- * Indicates whether the backing store of this TextRenderer should be intensity-only (the default) or
- * full-color.
- *
- * @return whether or not intensity only is on.
- */
- public boolean intensityOnly();
-
- /**
- * Computes the bounds of the given String relative to the origin.
- *
- * @param str The string to process.
- * @param font The font to use.
- * @param frc The render context to use.
- * @return The bounds given the parameters.
- */
- public Rectangle2D getBounds(String str, Font font,
- FontRenderContext frc);
-
- /**
- * Computes the bounds of the given character sequence relative to the origin.
- *
- * @param str The string to process.
- * @param font The font to use.
- * @param frc The render context to use.
- * @return The bounds given the parameters.
- */
- public Rectangle2D getBounds(CharSequence str, Font font,
- FontRenderContext frc);
-
- /**
- * Computes the bounds of the given GlyphVector, already assumed to have been created for a particular Font,
- * relative to the origin.
- *
- * @param gv The string to process.
- * @param frc The render context to use.
- * @return The bounds given the parameters.
- */
- public Rectangle2D getBounds(GlyphVector gv, FontRenderContext frc);
-
- /**
- * * Render the passed character sequence at the designated location using the supplied Graphics2D instance.The
- * surrounding region will already have been cleared to the RGB color (0, 0, 0) with zero alpha.The initial
- * drawing context of the passed Graphics2D will be set to use AlphaComposite.Src, the color white, the Font
- * specified in the TextRenderer's constructor, and the rendering hints specified in the TextRenderer
- * constructor.Changes made by the end user may be visible in successive calls to this method, but are not
- * guaranteed to be preserved.Implementors of this method should reset the Graphics2D's state to that desired
- * each time this method is called, in particular those states which are not the defaults.
- *
- * @param graphics The canvas to draw on.
- * @param str The string to draw.
- * @param x The x location.
- * @param y The y location.
- */
- public void draw(Graphics2D graphics, String str, int x, int y);
-
- /**
- * * Render the passed GlyphVector at the designated location using the supplied Graphics2D instance.The
- * surrounding region will already have been cleared to the RGB color (0, 0, 0) with zero alpha.The initial
- * drawing context of the passed Graphics2D will be set to use AlphaComposite.Src, the color white, the Font
- * specified in the TextRenderer's constructor, and the rendering hints specified in the TextRenderer
- * constructor.Changes made by the end user may be visible in successive calls to this method, but are not
- * guaranteed to be preserved.Implementors of this method should reset the Graphics2D's state to that desired
- * each time this method is called, in particular those states which are not the defaults.
- *
- * @param graphics The canvas to draw on.
- * @param str The string to draw.
- * @param x The x location.
- * @param y The y location.
- */
- public void drawGlyphVector(Graphics2D graphics, GlyphVector str,
- int x, int y);
- }
-
- private static class CharSequenceIterator implements CharacterIterator {
- CharSequence mSequence;
- int mLength;
- int mCurrentIndex;
-
- CharSequenceIterator() {
- }
-
- CharSequenceIterator(CharSequence sequence) {
- initFromCharSequence(sequence);
- }
-
- public void initFromCharSequence(CharSequence sequence) {
- mSequence = sequence;
- mLength = mSequence.length();
- mCurrentIndex = 0;
- }
-
- @Override
- public char last() {
- mCurrentIndex = Math.max(0, mLength - 1);
-
- return current();
- }
-
- @Override
- public char current() {
- if ((mLength == 0) || (mCurrentIndex >= mLength)) {
- return CharacterIterator.DONE;
- }
-
- return mSequence.charAt(mCurrentIndex);
- }
-
- @Override
- public char next() {
- mCurrentIndex++;
-
- return current();
- }
-
- @Override
- public char previous() {
- mCurrentIndex = Math.max(mCurrentIndex - 1, 0);
-
- return current();
- }
-
- @Override
- public char setIndex(int position) {
- mCurrentIndex = position;
-
- return current();
- }
-
- @Override
- public int getBeginIndex() {
- return 0;
- }
-
- @Override
- public int getEndIndex() {
- return mLength;
- }
-
- @Override
- public int getIndex() {
- return mCurrentIndex;
- }
-
- @Override
- public Object clone() {
- CharSequenceIterator iter = new CharSequenceIterator(mSequence);
- iter.mCurrentIndex = mCurrentIndex;
-
- return iter;
- }
-
- @Override
- public char first() {
- if (mLength == 0) {
- return CharacterIterator.DONE;
- }
-
- mCurrentIndex = 0;
-
- return current();
- }
- }
-
- // Data associated with each rectangle of text
- static class TextData {
- // Back-pointer to String this TextData describes, if it
- // represents a String rather than a single glyph
- private final String str;
-
- // If this TextData represents a single glyph, this is its
- // unicode ID
- int unicodeID;
-
- // The following must be defined and used VERY precisely. This is
- // the offset from the upper-left corner of this rectangle (Java
- // 2D coordinate system) at which the string must be rasterized in
- // order to fit within the rectangle -- the leftmost point of the
- // baseline.
- private final Point origin;
-
- // This represents the pre-normalized rectangle, which fits
- // within the rectangle on the backing store. We keep a
- // one-pixel border around entries on the backing store to
- // prevent bleeding of adjacent letters when using GL_LINEAR
- // filtering for rendering. The origin of this rectangle is
- // equivalent to the origin above.
- private final Rectangle2D origRect;
-
- private boolean used; // Whether this text was used recently
-
- TextData(String str, Point origin, Rectangle2D origRect, int unicodeID) {
- this.str = str;
- this.origin = origin;
- this.origRect = origRect;
- this.unicodeID = unicodeID;
- }
-
- String string() {
- return str;
- }
-
- Point origin() {
- return origin;
- }
-
- // The following three methods are used to locate the glyph
- // within the expanded rectangle coming from normalize()
- int origOriginX() {
- return (int) -origRect.getMinX();
- }
-
- int origOriginY() {
- return (int) -origRect.getMinY();
- }
-
- Rectangle2D origRect() {
- return origRect;
- }
-
- boolean used() {
- return used;
- }
-
- void markUsed() {
- used = true;
- }
-
- void clearUsed() {
- used = false;
- }
- }
-
- class Manager implements BackingStoreManager {
- private Graphics2D g;
-
- @Override
- public Object allocateBackingStore(int w, int h) {
- // FIXME: should consider checking Font's attributes to see
- // whether we're likely to need to support a full RGBA backing
- // store (i.e., non-default Paint, foreground color, etc.), but
- // for now, let's just be more efficient
- TextureRenderer renderer;
-
- if (renderDelegate.intensityOnly()) {
- renderer = TextureRenderer.createAlphaOnlyRenderer(w, h, mipmap);
- } else {
- renderer = new TextureRenderer(w, h, true, mipmap);
- }
- renderer.setSmoothing(smoothing);
-
- if (DEBUG) {
- System.err.println(" TextRenderer allocating backing store " +
- w + " x " + h);
- }
-
- return renderer;
- }
-
- @Override
- public void deleteBackingStore(Object backingStore) {
- ((TextureRenderer) backingStore).dispose();
- }
-
- @Override
- public boolean preExpand(Rect cause, int attemptNumber) {
- // Only try this one time; clear out potentially obsolete entries
- // NOTE: this heuristic and the fact that it clears the used bit
- // of all entries seems to cause cycling of entries in some
- // situations, where the backing store becomes small compared to
- // the amount of text on the screen (see the TextFlow demo) and
- // the entries continually cycle in and out of the backing
- // store, decreasing performance. If we added a little age
- // information to the entries, and only cleared out entries
- // above a certain age, this behavior would be eliminated.
- // However, it seems the system usually stabilizes itself, so
- // for now we'll just keep things simple. Note that if we don't
- // clear the used bit here, the backing store tends to increase
- // very quickly to its maximum size, at least with the TextFlow
- // demo when the text is being continually re-laid out.
- if (attemptNumber == 0) {
- if (DEBUG) {
- System.err.println(
- "Clearing unused entries in preExpand(): attempt number " +
- attemptNumber);
- }
-
- if (inBeginEndPair) {
- // Draw any outstanding glyphs
- flush();
- }
-
- clearUnusedEntries();
-
- return true;
- }
-
- return false;
- }
-
- @Override
- public boolean additionFailed(Rect cause, int attemptNumber) {
- // Heavy hammer -- might consider doing something different
- packer.clear();
- stringLocations.clear();
- mGlyphProducer.clearAllCacheEntries();
-
- if (DEBUG) {
- System.err.println(
- " *** Cleared all text because addition failed ***");
- }
-
- if (attemptNumber == 0) {
- return true;
- }
-
- return false;
- }
-
- @Override
- public boolean canCompact() {
- return true;
- }
-
- @Override
- public void beginMovement(Object oldBackingStore, Object newBackingStore) {
- // Exit the begin / end pair if necessary
- if (inBeginEndPair) {
- // Draw any outstanding glyphs
- flush();
-
- GL2 gl = GLContext.getCurrentGL().getGL2();
-
- // Pop client attrib bits used by the pipelined quad renderer
- gl.glPopClientAttrib();
-
- // The OpenGL spec is unclear about whether this changes the
- // buffer bindings, so preemptively zero out the GL_ARRAY_BUFFER
- // binding
- if (getUseVertexArrays() && is15Available(gl)) {
- try {
- gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0);
- } catch (Exception e) {
- isExtensionAvailable_GL_VERSION_1_5 = false;
- }
- }
-
- if (isOrthoMode) {
- ((TextureRenderer) oldBackingStore).endOrthoRendering();
- } else {
- ((TextureRenderer) oldBackingStore).end3DRendering();
- }
- }
-
- TextureRenderer newRenderer = (TextureRenderer) newBackingStore;
- g = newRenderer.createGraphics();
- }
-
- @Override
- public void move(Object oldBackingStore, Rect oldLocation,
- Object newBackingStore, Rect newLocation) {
- TextureRenderer oldRenderer = (TextureRenderer) oldBackingStore;
- TextureRenderer newRenderer = (TextureRenderer) newBackingStore;
-
- if (oldRenderer == newRenderer) {
- // Movement on the same backing store -- easy case
- g.copyArea(oldLocation.x(), oldLocation.y(), oldLocation.w(),
- oldLocation.h(), newLocation.x() - oldLocation.x(),
- newLocation.y() - oldLocation.y());
- } else {
- // Need to draw from the old renderer's image into the new one
- Image img = oldRenderer.getImage();
- g.drawImage(img, newLocation.x(), newLocation.y(),
- newLocation.x() + newLocation.w(),
- newLocation.y() + newLocation.h(), oldLocation.x(),
- oldLocation.y(), oldLocation.x() + oldLocation.w(),
- oldLocation.y() + oldLocation.h(), null);
- }
- }
-
- @Override
- public void endMovement(Object oldBackingStore, Object newBackingStore) {
- g.dispose();
-
- // Sync the whole surface
- TextureRenderer newRenderer = (TextureRenderer) newBackingStore;
- newRenderer.markDirty(0, 0, newRenderer.getWidth(),
- newRenderer.getHeight());
-
- // Re-enter the begin / end pair if necessary
- if (inBeginEndPair) {
- if (isOrthoMode) {
- ((TextureRenderer) newBackingStore).beginOrthoRendering(beginRenderingWidth,
- beginRenderingHeight, beginRenderingDepthTestDisabled);
- } else {
- ((TextureRenderer) newBackingStore).begin3DRendering();
- }
-
- // Push client attrib bits used by the pipelined quad renderer
- GL2 gl = GLContext.getCurrentGL().getGL2();
- gl.glPushClientAttrib((int) GL2.GL_ALL_CLIENT_ATTRIB_BITS);
-
- if (haveCachedColor) {
- if (cachedColor == null) {
- ((TextureRenderer) newBackingStore).setColor(cachedR,
- cachedG, cachedB, cachedA);
- } else {
- ((TextureRenderer) newBackingStore).setColor(cachedColor);
- }
- }
- } else {
- needToResetColor = true;
- }
- }
- }
-
- public static class DefaultRenderDelegate implements RenderDelegate {
- @Override
- public boolean intensityOnly() {
- return true;
- }
-
- @Override
- public Rectangle2D getBounds(CharSequence str, Font font,
- FontRenderContext frc) {
- return getBounds(font.createGlyphVector(frc,
- new CharSequenceIterator(str)),
- frc);
- }
-
- @Override
- public Rectangle2D getBounds(String str, Font font,
- FontRenderContext frc) {
- return getBounds(font.createGlyphVector(frc, str), frc);
- }
-
- @Override
- public Rectangle2D getBounds(GlyphVector gv, FontRenderContext frc) {
- return gv.getVisualBounds();
- }
-
- @Override
- public void drawGlyphVector(Graphics2D graphics, GlyphVector str,
- int x, int y) {
- graphics.drawGlyphVector(str, x, y);
- }
-
- @Override
- public void draw(Graphics2D graphics, String str, int x, int y) {
- graphics.drawString(str, x, y);
- }
- }
-
- //----------------------------------------------------------------------
- // Glyph-by-glyph rendering support
- //
-
- // A temporary to prevent excessive garbage creation
- private final char[] singleUnicode = new char[1];
-
- /** A Glyph represents either a single unicode glyph or a
- substring of characters to be drawn. The reason for the dual
- behavior is so that we can take in a sequence of unicode
- characters and partition them into runs of individual glyphs,
- but if we encounter complex text and/or unicode sequences we
- don't understand, we can render them using the
- string-by-string method.
-
- Glyphs need to be able to re-upload themselves to the backing
- store on demand as we go along in the render sequence.
- */
-
- class Glyph {
- // If this Glyph represents an individual unicode glyph, this
- // is its unicode ID. If it represents a String, this is -1.
- private int unicodeID;
- // If the above field isn't -1, then these fields are used.
- // The glyph code in the font
- private int glyphCode;
- // The GlyphProducer which created us
- private GlyphProducer producer;
- // The advance of this glyph
- private float advance;
- // The GlyphVector for this single character; this is passed
- // in during construction but cleared during the upload
- // process
- private GlyphVector singleUnicodeGlyphVector;
- // The rectangle of this glyph on the backing store, or null
- // if it has been cleared due to space pressure
- private Rect glyphRectForTextureMapping;
- // If this Glyph represents a String, this is the sequence of
- // characters
- private String str;
- // Whether we need a valid advance when rendering this string
- // (i.e., whether it has other single glyphs coming after it)
- private boolean needAdvance;
-
- // Creates a Glyph representing an individual Unicode character
- public Glyph(int unicodeID,
- int glyphCode,
- float advance,
- GlyphVector singleUnicodeGlyphVector,
- GlyphProducer producer) {
- this.unicodeID = unicodeID;
- this.glyphCode = glyphCode;
- this.advance = advance;
- this.singleUnicodeGlyphVector = singleUnicodeGlyphVector;
- this.producer = producer;
- }
-
- // Creates a Glyph representing a sequence of characters, with
- // an indication of whether additional single glyphs are being
- // rendered after it
- public Glyph(String str, boolean needAdvance) {
- this.str = str;
- this.needAdvance = needAdvance;
- }
-
- /** Returns this glyph's unicode ID */
- public int getUnicodeID() {
- return unicodeID;
- }
-
- /** Returns this glyph's (font-specific) glyph code */
- public int getGlyphCode() {
- return glyphCode;
- }
-
- /** Returns the advance for this glyph */
- public float getAdvance() {
- return advance;
- }
-
- /** Draws this glyph and returns the (x) advance for this glyph */
- public float draw3D(float inX, float inY, float z, float scaleFactor) {
- if (str != null) {
- draw3D_ROBUST(str, inX, inY, z, scaleFactor);
- if (!needAdvance) {
- return 0;
- }
- // Compute and return the advance for this string
- GlyphVector gv = font.createGlyphVector(getFontRenderContext(), str);
- float totalAdvance = 0;
- for (int i = 0; i < gv.getNumGlyphs(); i++) {
- totalAdvance += gv.getGlyphMetrics(i).getAdvance();
- }
- return totalAdvance;
- }
-
- // This is the code path taken for individual glyphs
- if (glyphRectForTextureMapping == null) {
- upload();
- }
-
- try {
- if (mPipelinedQuadRenderer == null) {
- mPipelinedQuadRenderer = new Pipelined_QuadRenderer();
- }
-
- TextureRenderer renderer = getBackingStore();
- // Handles case where NPOT texture is used for backing store
- TextureCoords wholeImageTexCoords = renderer.getTexture().getImageTexCoords();
- float xScale = wholeImageTexCoords.right();
- float yScale = wholeImageTexCoords.bottom();
-
- Rect rect = glyphRectForTextureMapping;
- TextData data = (TextData) rect.getUserData();
- data.markUsed();
-
- Rectangle2D origRect = data.origRect();
-
- float x = inX - (scaleFactor * data.origOriginX());
- float y = inY - (scaleFactor * ((float) origRect.getHeight() - data.origOriginY()));
-
- int texturex = rect.x() + (data.origin().x - data.origOriginX());
- int texturey = renderer.getHeight() - rect.y() - (int) origRect.getHeight() -
- (data.origin().y - data.origOriginY());
- int width = (int) origRect.getWidth();
- int height = (int) origRect.getHeight();
-
- float tx1 = xScale * texturex / renderer.getWidth();
- float ty1 = yScale * (1.0f -
- ((float) texturey / (float) renderer.getHeight()));
- float tx2 = xScale * (texturex + width) / renderer.getWidth();
- float ty2 = yScale * (1.0f -
- ((float) (texturey + height) / (float) renderer.getHeight()));
-
- mPipelinedQuadRenderer.glTexCoord2f(tx1, ty1);
- mPipelinedQuadRenderer.glVertex3f(x, y, z);
- mPipelinedQuadRenderer.glTexCoord2f(tx2, ty1);
- mPipelinedQuadRenderer.glVertex3f(x + (width * scaleFactor), y,
- z);
- mPipelinedQuadRenderer.glTexCoord2f(tx2, ty2);
- mPipelinedQuadRenderer.glVertex3f(x + (width * scaleFactor),
- y + (height * scaleFactor), z);
- mPipelinedQuadRenderer.glTexCoord2f(tx1, ty2);
- mPipelinedQuadRenderer.glVertex3f(x,
- y + (height * scaleFactor), z);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return advance;
- }
-
- /** Notifies this glyph that it's been cleared out of the cache */
- public void clear() {
- glyphRectForTextureMapping = null;
- }
-
- private void upload() {
- GlyphVector gv = getGlyphVector();
- Rectangle2D origBBox = preNormalize(renderDelegate.getBounds(gv, getFontRenderContext()));
- Rectangle2D bbox = normalize(origBBox);
- Point origin = new Point((int) -bbox.getMinX(),
- (int) -bbox.getMinY());
- Rect rect = new Rect(0, 0, (int) bbox.getWidth(),
- (int) bbox.getHeight(),
- new TextData(null, origin, origBBox, unicodeID));
- packer.add(rect);
- glyphRectForTextureMapping = rect;
- Graphics2D g = getGraphics2D();
- // OK, should now have an (x, y) for this rectangle; rasterize
- // the glyph
- int strx = rect.x() + origin.x;
- int stry = rect.y() + origin.y;
-
- // Clear out the area we're going to draw into
- g.setComposite(AlphaComposite.Clear);
- g.fillRect(rect.x(), rect.y(), rect.w(), rect.h());
- g.setComposite(AlphaComposite.Src);
-
- // Draw the string
- renderDelegate.drawGlyphVector(g, gv, strx, stry);
-
- if (DRAW_BBOXES) {
- TextData data = (TextData) rect.getUserData();
- // Draw a bounding box on the backing store
- g.drawRect(strx - data.origOriginX(),
- stry - data.origOriginY(),
- (int) data.origRect().getWidth(),
- (int) data.origRect().getHeight());
- g.drawRect(strx - data.origin().x,
- stry - data.origin().y,
- rect.w(),
- rect.h());
- }
-
- // Mark this region of the TextureRenderer as dirty
- getBackingStore().markDirty(rect.x(), rect.y(), rect.w(),
- rect.h());
- // Re-register ourselves with our producer
- producer.register(this);
- }
-
- private GlyphVector getGlyphVector() {
- GlyphVector gv = singleUnicodeGlyphVector;
- if (gv != null) {
- singleUnicodeGlyphVector = null; // Don't need this anymore
- return gv;
- }
- singleUnicode[0] = (char) unicodeID;
- return font.createGlyphVector(getFontRenderContext(), singleUnicode);
- }
- }
-
- class GlyphProducer {
- final int undefined = -2;
- FontRenderContext fontRenderContext;
- List glyphsOutput = new ArrayList();
- HashMap fullGlyphVectorCache = new HashMap();
- HashMap glyphMetricsCache = new HashMap();
- // The mapping from unicode character to font-specific glyph ID
- int[] unicodes2Glyphs;
- // The mapping from glyph ID to Glyph
- Glyph[] glyphCache;
- // We re-use this for each incoming string
- CharSequenceIterator iter = new CharSequenceIterator();
-
- GlyphProducer(int fontLengthInGlyphs) {
- unicodes2Glyphs = new int[512];
- glyphCache = new Glyph[fontLengthInGlyphs];
- clearAllCacheEntries();
- }
-
- public List getGlyphs(CharSequence inString) {
- glyphsOutput.clear();
- GlyphVector fullRunGlyphVector;
- fullRunGlyphVector = fullGlyphVectorCache.get(inString.toString());
- if (fullRunGlyphVector == null) {
- iter.initFromCharSequence(inString);
- fullRunGlyphVector = font.createGlyphVector(getFontRenderContext(), iter);
- fullGlyphVectorCache.put(inString.toString(), fullRunGlyphVector);
- }
- boolean complex = (fullRunGlyphVector.getLayoutFlags() != 0);
- if (complex || DISABLE_GLYPH_CACHE) {
- // Punt to the robust version of the renderer
- glyphsOutput.add(new Glyph(inString.toString(), false));
- return glyphsOutput;
- }
-
- int lengthInGlyphs = fullRunGlyphVector.getNumGlyphs();
- int i = 0;
- while (i < lengthInGlyphs) {
- Character letter = CharacterCache.valueOf(inString.charAt(i));
- GlyphMetrics metrics = glyphMetricsCache.get(letter);
- if (metrics == null) {
- metrics = fullRunGlyphVector.getGlyphMetrics(i);
- glyphMetricsCache.put(letter, metrics);
- }
- Glyph glyph = getGlyph(inString, metrics, i);
- if (glyph != null) {
- glyphsOutput.add(glyph);
- i++;
- } else {
- // Assemble a run of characters that don't fit in
- // the cache
- StringBuilder buf = new StringBuilder();
- while (i < lengthInGlyphs &&
- getGlyph(inString, fullRunGlyphVector.getGlyphMetrics(i), i) == null) {
- buf.append(inString.charAt(i++));
- }
- glyphsOutput.add(new Glyph(buf.toString(),
- // Any more glyphs after this run?
- i < lengthInGlyphs));
- }
- }
- return glyphsOutput;
- }
-
- public void clearCacheEntry(int unicodeID) {
- int glyphID = unicodes2Glyphs[unicodeID];
- if (glyphID != undefined) {
- Glyph glyph = glyphCache[glyphID];
- if (glyph != null) {
- glyph.clear();
- }
- glyphCache[glyphID] = null;
- }
- unicodes2Glyphs[unicodeID] = undefined;
- }
-
- public void clearAllCacheEntries() {
- for (int i = 0; i < unicodes2Glyphs.length; i++) {
- clearCacheEntry(i);
- }
- }
-
- public void register(Glyph glyph) {
- unicodes2Glyphs[glyph.getUnicodeID()] = glyph.getGlyphCode();
- glyphCache[glyph.getGlyphCode()] = glyph;
- }
-
- public float getGlyphPixelWidth(char unicodeID) {
- Glyph glyph = getGlyph(unicodeID);
- if (glyph != null) {
- return glyph.getAdvance();
- }
-
- // Have to do this the hard / uncached way
- singleUnicode[0] = unicodeID;
- GlyphVector gv = font.createGlyphVector(fontRenderContext,
- singleUnicode);
- return gv.getGlyphMetrics(0).getAdvance();
- }
-
- // Returns a glyph object for this single glyph. Returns null
- // if the unicode or glyph ID would be out of bounds of the
- // glyph cache.
- private Glyph getGlyph(CharSequence inString,
- GlyphMetrics glyphMetrics,
- int index) {
- char unicodeID = inString.charAt(index);
-
- if (unicodeID >= unicodes2Glyphs.length) {
- return null;
- }
-
- int glyphID = unicodes2Glyphs[unicodeID];
- if (glyphID != undefined) {
- return glyphCache[glyphID];
- }
-
- // Must fabricate the glyph
- singleUnicode[0] = unicodeID;
- GlyphVector gv = font.createGlyphVector(getFontRenderContext(), singleUnicode);
- return getGlyph(unicodeID, gv, glyphMetrics);
- }
-
- // It's unclear whether this variant might produce less
- // optimal results than if we can see the entire GlyphVector
- // for the incoming string
- private Glyph getGlyph(int unicodeID) {
- if (unicodeID >= unicodes2Glyphs.length) {
- return null;
- }
-
- int glyphID = unicodes2Glyphs[unicodeID];
- if (glyphID != undefined) {
- return glyphCache[glyphID];
- }
- singleUnicode[0] = (char) unicodeID;
- GlyphVector gv = font.createGlyphVector(getFontRenderContext(), singleUnicode);
- return getGlyph(unicodeID, gv, gv.getGlyphMetrics(0));
- }
-
- private Glyph getGlyph(int unicodeID,
- GlyphVector singleUnicodeGlyphVector,
- GlyphMetrics metrics) {
- int glyphCode = singleUnicodeGlyphVector.getGlyphCode(0);
- // Have seen huge glyph codes (65536) coming out of some fonts in some Unicode situations
- if (glyphCode >= glyphCache.length) {
- return null;
- }
- Glyph glyph = new Glyph(unicodeID,
- glyphCode,
- metrics.getAdvance(),
- singleUnicodeGlyphVector,
- this);
- register(glyph);
- return glyph;
- }
- }
-
- private static class CharacterCache {
- private CharacterCache() {
- }
-
- static final Character CACHE[] = new Character[127 + 1];
-
- static {
- for (int i = 0; i < CACHE.length; i++) {
- CACHE[i] = (char) i;
- }
- }
-
- public static Character valueOf(char c) {
- if (c <= 127) { // must cache
- return CharacterCache.CACHE[c];
- }
- return c;
- }
- }
-
- class Pipelined_QuadRenderer {
- int mOutstandingGlyphsVerticesPipeline = 0;
- FloatBuffer mTexCoords;
- FloatBuffer mVertCoords;
- boolean usingVBOs;
- int mVBO_For_ResuableTileVertices;
- int mVBO_For_ResuableTileTexCoords;
-
- Pipelined_QuadRenderer() {
- GL2 gl = GLContext.getCurrentGL().getGL2();
- mVertCoords = Buffers.newDirectFloatBuffer(kTotalBufferSizeCoordsVerts);
- mTexCoords = Buffers.newDirectFloatBuffer(kTotalBufferSizeCoordsTex);
-
- usingVBOs = getUseVertexArrays() && is15Available(gl);
-
- if (usingVBOs) {
- try {
- int[] vbos = new int[2];
- gl.glGenBuffers(2, IntBuffer.wrap(vbos));
-
- mVBO_For_ResuableTileVertices = vbos[0];
- mVBO_For_ResuableTileTexCoords = vbos[1];
-
- gl.glBindBuffer(GL2.GL_ARRAY_BUFFER,
- mVBO_For_ResuableTileVertices);
- gl.glBufferData(GL2.GL_ARRAY_BUFFER, kTotalBufferSizeBytesVerts,
- null, GL2.GL_STREAM_DRAW); // stream draw because this is a single quad use pipeline
-
- gl.glBindBuffer(GL2.GL_ARRAY_BUFFER,
- mVBO_For_ResuableTileTexCoords);
- gl.glBufferData(GL2.GL_ARRAY_BUFFER, kTotalBufferSizeBytesTex,
- null, GL2.GL_STREAM_DRAW); // stream draw because this is a single quad use pipeline
- } catch (Exception e) {
- isExtensionAvailable_GL_VERSION_1_5 = false;
- usingVBOs = false;
- }
- }
- }
-
- public void glTexCoord2f(float v, float v1) {
- mTexCoords.put(v);
- mTexCoords.put(v1);
- }
-
- public void glVertex3f(float inX, float inY, float inZ) {
- mVertCoords.put(inX);
- mVertCoords.put(inY);
- mVertCoords.put(inZ);
-
- mOutstandingGlyphsVerticesPipeline++;
-
- if (mOutstandingGlyphsVerticesPipeline >= kTotalBufferSizeVerts) {
- this.draw();
- }
- }
-
- private void draw() {
- if (useVertexArrays) {
- drawVertexArrays();
- } else {
- drawIMMEDIATE();
- }
- }
-
- private void drawVertexArrays() {
- if (mOutstandingGlyphsVerticesPipeline > 0) {
- GL2 gl = GLContext.getCurrentGL().getGL2();
-
- TextureRenderer renderer = getBackingStore();
- renderer.getTexture(); // triggers texture uploads. Maybe this should be more obvious?
-
- mVertCoords.rewind();
- mTexCoords.rewind();
-
- gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
-
- if (usingVBOs) {
- gl.glBindBuffer(GL2.GL_ARRAY_BUFFER,
- mVBO_For_ResuableTileVertices);
- gl.glBufferSubData(GL2.GL_ARRAY_BUFFER, 0,
- mOutstandingGlyphsVerticesPipeline * kSizeInBytes_OneVertices_VertexData,
- mVertCoords); // upload only the new stuff
- gl.glVertexPointer(3, GL2.GL_FLOAT, 0, 0);
- } else {
- gl.glVertexPointer(3, GL2.GL_FLOAT, 0, mVertCoords);
- }
-
- gl.glEnableClientState(GL2.GL_TEXTURE_COORD_ARRAY);
-
- if (usingVBOs) {
- gl.glBindBuffer(GL2.GL_ARRAY_BUFFER,
- mVBO_For_ResuableTileTexCoords);
- gl.glBufferSubData(GL2.GL_ARRAY_BUFFER, 0,
- mOutstandingGlyphsVerticesPipeline * kSizeInBytes_OneVertices_TexData,
- mTexCoords); // upload only the new stuff
- gl.glTexCoordPointer(2, GL2.GL_FLOAT, 0, 0);
- } else {
- gl.glTexCoordPointer(2, GL2.GL_FLOAT, 0, mTexCoords);
- }
-
- gl.glDrawArrays(GL2.GL_QUADS, 0,
- mOutstandingGlyphsVerticesPipeline);
-
- mVertCoords.rewind();
- mTexCoords.rewind();
- mOutstandingGlyphsVerticesPipeline = 0;
- }
- }
-
- private void drawIMMEDIATE() {
- if (mOutstandingGlyphsVerticesPipeline > 0) {
- TextureRenderer renderer = getBackingStore();
- renderer.getTexture(); // triggers texture uploads. Maybe this should be more obvious?
-
- GL2 gl = GLContext.getCurrentGL().getGL2();
- gl.glBegin(GL2.GL_QUADS);
-
- try {
- int numberOfQuads = mOutstandingGlyphsVerticesPipeline / 4;
- mVertCoords.rewind();
- mTexCoords.rewind();
-
- for (int i = 0; i < numberOfQuads; i++) {
- gl.glTexCoord2f(mTexCoords.get(), mTexCoords.get());
- gl.glVertex3f(mVertCoords.get(), mVertCoords.get(),
- mVertCoords.get());
-
- gl.glTexCoord2f(mTexCoords.get(), mTexCoords.get());
- gl.glVertex3f(mVertCoords.get(), mVertCoords.get(),
- mVertCoords.get());
-
- gl.glTexCoord2f(mTexCoords.get(), mTexCoords.get());
- gl.glVertex3f(mVertCoords.get(), mVertCoords.get(),
- mVertCoords.get());
-
- gl.glTexCoord2f(mTexCoords.get(), mTexCoords.get());
- gl.glVertex3f(mVertCoords.get(), mVertCoords.get(),
- mVertCoords.get());
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- gl.glEnd();
- mVertCoords.rewind();
- mTexCoords.rewind();
- mOutstandingGlyphsVerticesPipeline = 0;
- }
- }
- }
- }
-
- class DebugListener implements GLEventListener {
- private GLU glu;
- private Frame frame;
-
- DebugListener(GL gl, Frame frame) {
- this.glu = GLU.createGLU(gl);
- this.frame = frame;
- }
-
- @Override
- public void display(GLAutoDrawable drawable) {
- GL2 gl = GLContext.getCurrentGL().getGL2();
- gl.glClear(GL2.GL_DEPTH_BUFFER_BIT | GL2.GL_COLOR_BUFFER_BIT);
-
- if (packer == null) {
- return;
- }
-
- TextureRenderer rend = getBackingStore();
- final int w = rend.getWidth();
- final int h = rend.getHeight();
- rend.beginOrthoRendering(w, h);
- rend.drawOrthoRect(0, 0);
- rend.endOrthoRendering();
-
- if ((frame.getWidth() != w) || (frame.getHeight() != h)) {
- EventQueue.invokeLater(new Runnable() {
- @Override
- public void run() {
- frame.setSize(w, h);
- }
- });
- }
- }
-
- @Override
- public void dispose(GLAutoDrawable drawable) {
- glu=null;
- frame=null;
- }
-
- // Unused methods
- @Override
- public void init(GLAutoDrawable drawable) {
- }
-
- @Override
- public void reshape(GLAutoDrawable drawable, int x, int y, int width,
- int height) {
- }
-
- public void displayChanged(GLAutoDrawable drawable,
- boolean modeChanged, boolean deviceChanged) {
- }
- }
-
- /**
- * Sets whether vertex arrays are being used internally for rendering, or whether text is rendered using the OpenGL
- * immediate mode commands.This is provided as a concession for certain graphics cards which have poor vertex array
- * performance. Defaults to true.
- *
- * @param useVertexArrays The desired value.
- */
- public void setUseVertexArrays(boolean useVertexArrays) {
- this.useVertexArrays = useVertexArrays;
- }
-
- /**
- * Indicates whether vertex arrays are being used internally for
- * rendering, or whether text is rendered using the OpenGL
- * immediate mode commands.Defaults to true.
- * @return whether userVertexArrays is on or off.
- */
- public final boolean getUseVertexArrays() {
- return useVertexArrays;
- }
-
- /**
- * Sets whether smoothing (i.e., GL_LINEAR filtering) is enabled in the backing TextureRenderer of this
- * TextRenderer.A few graphics cards do not behave well when this is enabled, resulting in fuzzy text. Defaults to
- * true.
- *
- * @param smoothing The new smoothing setting.
- */
- public void setSmoothing(boolean smoothing) {
- this.smoothing = smoothing;
- getBackingStore().setSmoothing(smoothing);
- }
-
- /**
- * Indicates whether smoothing is enabled in the backing TextureRenderer of this TextRenderer.A few graphics cards
- * do not behave well when this is enabled, resulting in fuzzy text. Defaults to true.
- *
- * @return The current smoothing setting.
- */
- public boolean getSmoothing() {
- return smoothing;
- }
-
- private boolean is15Available(GL gl) {
- if (!checkFor_isExtensionAvailable_GL_VERSION_1_5) {
- isExtensionAvailable_GL_VERSION_1_5 = gl.isExtensionAvailable(GLExtensions.VERSION_1_5);
- checkFor_isExtensionAvailable_GL_VERSION_1_5 = true;
- }
- return isExtensionAvailable_GL_VERSION_1_5;
- }
-}
diff --git a/src/gov/nasa/worldwind/render/TextRendererCache.java b/src/gov/nasa/worldwind/render/TextRendererCache.java
index 5f670f784e..82a710bbe6 100644
--- a/src/gov/nasa/worldwind/render/TextRendererCache.java
+++ b/src/gov/nasa/worldwind/render/TextRendererCache.java
@@ -27,6 +27,8 @@
*/
package gov.nasa.worldwind.render;
+import com.jogamp.opengl.util.awt.TextRenderer;
+
import gov.nasa.worldwind.Disposable;
import gov.nasa.worldwind.util.Logging;
diff --git a/src/gov/nasa/worldwind/render/ToolTipRenderer.java b/src/gov/nasa/worldwind/render/ToolTipRenderer.java
index b9e4b7af0f..13fb44fb69 100644
--- a/src/gov/nasa/worldwind/render/ToolTipRenderer.java
+++ b/src/gov/nasa/worldwind/render/ToolTipRenderer.java
@@ -30,6 +30,8 @@
import gov.nasa.worldwind.util.*;
import com.jogamp.opengl.*;
+import com.jogamp.opengl.util.awt.TextRenderer;
+
import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
diff --git a/src/gov/nasa/worldwind/symbology/AbstractTacticalSymbol.java b/src/gov/nasa/worldwind/symbology/AbstractTacticalSymbol.java
index af167bd955..7c410ec413 100644
--- a/src/gov/nasa/worldwind/symbology/AbstractTacticalSymbol.java
+++ b/src/gov/nasa/worldwind/symbology/AbstractTacticalSymbol.java
@@ -28,6 +28,7 @@
package gov.nasa.worldwind.symbology;
+import com.jogamp.opengl.util.awt.TextRenderer;
import com.jogamp.opengl.util.texture.*;
import com.jogamp.opengl.util.texture.awt.AWTTextureIO;
import gov.nasa.worldwind.*;
diff --git a/src/gov/nasa/worldwind/symbology/TacticalGraphicLabel.java b/src/gov/nasa/worldwind/symbology/TacticalGraphicLabel.java
index d24b0dcfb9..0837534eaa 100644
--- a/src/gov/nasa/worldwind/symbology/TacticalGraphicLabel.java
+++ b/src/gov/nasa/worldwind/symbology/TacticalGraphicLabel.java
@@ -37,6 +37,8 @@
import gov.nasa.worldwind.util.*;
import com.jogamp.opengl.*;
+import com.jogamp.opengl.util.awt.TextRenderer;
+
import java.awt.*;
import java.awt.geom.*;
diff --git a/src/gov/nasa/worldwind/terrain/RectangularTessellator.java b/src/gov/nasa/worldwind/terrain/RectangularTessellator.java
index 869e99424a..07ea6ba500 100644
--- a/src/gov/nasa/worldwind/terrain/RectangularTessellator.java
+++ b/src/gov/nasa/worldwind/terrain/RectangularTessellator.java
@@ -39,6 +39,8 @@
import gov.nasa.worldwind.util.*;
import com.jogamp.opengl.*;
+import com.jogamp.opengl.util.awt.TextRenderer;
+
import java.awt.*;
import java.nio.*;
import java.util.*;
diff --git a/src/gov/nasa/worldwind/util/EGM2008.java b/src/gov/nasa/worldwind/util/EGM2008.java
new file mode 100644
index 0000000000..f8e64d9900
--- /dev/null
+++ b/src/gov/nasa/worldwind/util/EGM2008.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2006-2009, 2017, 2020 United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The NASA World Wind Java (WWJ) platform is licensed under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed
+ * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ *
+ * NASA World Wind Java (WWJ) also contains the following 3rd party Open Source
+ * software:
+ *
+ * Jackson Parser – Licensed under Apache 2.0
+ * GDAL – Licensed under MIT
+ * JOGL – Licensed under Berkeley Software Distribution (BSD)
+ * Gluegen – Licensed under Berkeley Software Distribution (BSD)
+ *
+ * A complete listing of 3rd Party software notices and licenses included in
+ * NASA World Wind Java (WWJ) can be found in the WorldWindJava-v2.2 3rd-party
+ * notices and licenses PDF found in code directory.
+ */
+
+package gov.nasa.worldwind.util;
+
+import gov.nasa.worldwind.cache.BasicMemoryCache;
+import gov.nasa.worldwind.geom.Angle;
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+public class EGM2008
+{
+ public static final int N_ROW_MARKERS = 2; // The beginning and end of each row of latitude data is a flag of some sort
+ public static final int N_LONGITUDE_COLS = 8640 + N_ROW_MARKERS; // Number of float32s in a row of data in the data file.
+ public static final int N_LATITUDE_ROWS = 4321; // Number of rows.
+ public static final double GRID_RESOLUTION = 2.5d / 60d; // 2.5 minute grid
+ public static final double CELL_AREA = GRID_RESOLUTION * GRID_RESOLUTION;
+ protected static final long CACHE_SIZE = EGM2008.N_LONGITUDE_COLS * 4 * 45 * 15; // Cache 15 degrees worth of offsets.
+ public static final int N_LAT_ROW_BYTES = N_LONGITUDE_COLS * 4; // Offsets are float32
+
+ protected String offsetsFilePath;
+ protected BufferWrapper deltas;
+ protected final BasicMemoryCache offsetCache;
+
+ protected class GridCell
+ {
+ public double x1;
+ public double y1;
+ public double x2;
+ public double y2;
+
+ public GridCell(double x1, double y1)
+ {
+ this.x1 = x1;
+ this.y1 = y1;
+ this.x2 = x1 + EGM2008.GRID_RESOLUTION;
+ this.y2 = y1 + EGM2008.GRID_RESOLUTION;
+ }
+
+ public GridCell()
+ {
+ this(0, 0);
+ }
+
+ public GridCell intersect(GridCell that)
+ {
+ GridCell intersection = new GridCell();
+ intersection.x1 = Math.max(this.x1, that.x1);
+ intersection.x2 = Math.min(this.x2, that.x2);
+ intersection.y1 = Math.max(this.y1, that.y1);
+ intersection.y2 = Math.min(this.y2, that.y2);
+ return intersection;
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("%5.2f,%5.2f,%5.2f,%5.2f", x1, y1, x2, y2);
+ }
+
+ public double area()
+ {
+ return (this.x2 - this.x1) * (this.y2 - this.y1);
+ }
+ }
+
+ /**
+ * Allows the retrieval of geoid offsets from the EGM2008 2.5 Minute Interpolation Grid sourced from the
+ * National Geospatial-Intelligence Agency Office of Geomatics (https://earth-info.nga.mil/).
+ *
+ * The EGM2008 data path. This data file is not included in the SDK due to its size. The data may be downloaded here:
+ * https://builds.worldwind.arc.nasa.gov/artifactory/EGM2008-Data/egm2008_25.dat
+ *
+ * @param offsetsFilePath a path pointing to a file with the geoid offsets.
+ */
+ public EGM2008(String offsetsFilePath)
+ {
+ if (offsetsFilePath == null)
+ {
+ String msg = Logging.getMessage("nullValue.PathIsNull");
+ Logging.logger().severe(msg);
+ throw new IllegalArgumentException(msg);
+ }
+ File test = new File(offsetsFilePath);
+ if (test.exists())
+ {
+ this.offsetsFilePath = offsetsFilePath;
+ }
+ else
+ {
+ Class c = EGM2008.class;
+ URL url = c.getResource("/" + offsetsFilePath);
+ if (url != null)
+ {
+ test = WWIO.getFileForLocalAddress(url);
+ this.offsetsFilePath = test.getAbsolutePath();
+ }
+ else
+ {
+ this.offsetsFilePath = null;
+ }
+ }
+ this.offsetCache = new BasicMemoryCache((EGM2008.CACHE_SIZE * 8) / 10, EGM2008.CACHE_SIZE);
+ this.offsetCache.setName(EGM2008.class.getName());
+ }
+
+ public float getOffset(Angle lat, Angle lon) throws IOException
+ {
+ return this.getOffset((float) lat.degrees, (float) lon.degrees);
+ }
+
+ public int getLatRow(double lat)
+ {
+ // Compute the row in the data file corresponding to a given latitude.
+ // Latitude row zero in the data corresponds to 90 degrees latitude (north pole) and increases southward
+ // Longitude column zero in the data corresponds to 0 degrees of longitude and increases eastward
+ float lat180 = 90f - (float) lat;
+ return (int) Math.floor(lat180 / EGM2008.GRID_RESOLUTION);
+ }
+
+ public int getLonCol(double lon)
+ {
+ // Compute the column in the data file corresponding to a given latitude and longitude.
+ // Latitude row zero in the data corresponds to 90 degrees latitude (north pole) and increases southward
+ // Longitude column zero in the data corresponds to 0 degrees of longitude and increases eastward
+ float lon360 = (float) lon;
+ if (lon < 0)
+ {
+ lon360 = lon360 + 360;
+ }
+ return (int) Math.floor(lon360 / EGM2008.GRID_RESOLUTION);
+ }
+
+ public float[][] getLatRows(int latRow) throws IOException
+ {
+ int[] interpRowIndices =
+ {
+ latRow, latRow + 1
+ };
+ float[][] latDataArray = new float[2][];
+ boolean retrievalRequired = false;
+ for (int i = 0; i < interpRowIndices.length; i++)
+ {
+ if (interpRowIndices[i] < EGM2008.N_LATITUDE_ROWS)
+ {
+ float[] latData = (float[]) this.offsetCache.getObject(interpRowIndices[i]);
+ latDataArray[i] = latData;
+ if (latData == null)
+ {
+ retrievalRequired = true;
+ }
+ }
+ }
+ if (retrievalRequired)
+ {
+ try (RandomAccessFile offsetFile = new RandomAccessFile(this.offsetsFilePath, "r"))
+ {
+ for (int i = 0; i < interpRowIndices.length; i++)
+ {
+ if (interpRowIndices[i] < EGM2008.N_LATITUDE_ROWS && latDataArray[i] == null)
+ {
+ offsetFile.seek(interpRowIndices[i] * EGM2008.N_LAT_ROW_BYTES);
+ byte[] latByteData = new byte[EGM2008.N_LAT_ROW_BYTES];
+ offsetFile.read(latByteData);
+ ByteBuffer latByteBuffer = ByteBuffer.wrap(latByteData).order(ByteOrder.LITTLE_ENDIAN);
+ FloatBuffer latFloatBuffer = latByteBuffer.asFloatBuffer();
+ float[] latData = new float[EGM2008.N_LONGITUDE_COLS];
+ latFloatBuffer.get(latData);
+ this.offsetCache.add(interpRowIndices[i], latData, EGM2008.N_LAT_ROW_BYTES);
+ latDataArray[i] = latData;
+ }
+ }
+ }
+ }
+ return latDataArray;
+ }
+
+ public float getOffset(double lat, double lon) throws IOException
+ {
+ if (this.offsetsFilePath == null)
+ {
+ return 0f;
+ }
+ int latRow = this.getLatRow(lat);
+ int lonCol = this.getLonCol(lon);
+
+ float[][] latDataArray = getLatRows(latRow);
+
+ float baseOffset = latDataArray[0][lonCol + EGM2008.N_ROW_MARKERS / 2];
+ if (latDataArray[1] == null)
+ {
+ return baseOffset;
+ }
+
+ // Interpolate with surrounding offset cells
+ float lat180 = 90f - (float) lat;
+ float lon360 = (float) lon;
+ if (lon < 0)
+ {
+ lon360 = lon360 + 360;
+ }
+ GridCell offsetCell = new GridCell(lon360, lat180);
+ double baseLat = ((double) latRow) * EGM2008.GRID_RESOLUTION;
+ double baseLon = ((double) lonCol) * EGM2008.GRID_RESOLUTION;
+ float interpOffset = 0;
+ for (int x = 0; x < 2; x++)
+ {
+ double cellLon = baseLon + ((double) x) * EGM2008.GRID_RESOLUTION;
+ for (int y = 0; y < 2; y++)
+ {
+ float cellOffset = latDataArray[y][lonCol + EGM2008.N_ROW_MARKERS / 2 + x];
+ double cellLat = baseLat + ((double) y) * EGM2008.GRID_RESOLUTION;
+ GridCell interpCell = new GridCell(cellLon, cellLat);
+ GridCell intersection = offsetCell.intersect(interpCell);
+ interpOffset += cellOffset * (intersection.area() / EGM2008.CELL_AREA);
+ }
+ }
+ return interpOffset;
+ }
+
+ public boolean isEGMDataAvailable()
+ {
+ return this.offsetsFilePath != null;
+ }
+}
diff --git a/src/gov/nasa/worldwind/util/OGLTextRenderer.java b/src/gov/nasa/worldwind/util/OGLTextRenderer.java
index 271568cc94..a5b77a291d 100644
--- a/src/gov/nasa/worldwind/util/OGLTextRenderer.java
+++ b/src/gov/nasa/worldwind/util/OGLTextRenderer.java
@@ -27,6 +27,8 @@
*/
package gov.nasa.worldwind.util;
+import com.jogamp.opengl.util.awt.TextRenderer;
+
import gov.nasa.worldwind.render.*;
/**
diff --git a/src/gov/nasa/worldwind/util/PlacemarkClutterFilter.java b/src/gov/nasa/worldwind/util/PlacemarkClutterFilter.java
index e41b6a8100..0d785e5e3f 100644
--- a/src/gov/nasa/worldwind/util/PlacemarkClutterFilter.java
+++ b/src/gov/nasa/worldwind/util/PlacemarkClutterFilter.java
@@ -34,6 +34,8 @@
import gov.nasa.worldwind.render.*;
import com.jogamp.opengl.*;
+import com.jogamp.opengl.util.awt.TextRenderer;
+
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
diff --git a/src/gov/nasa/worldwind/util/tree/BasicTreeLayout.java b/src/gov/nasa/worldwind/util/tree/BasicTreeLayout.java
index f87da8f212..d9325e46f8 100644
--- a/src/gov/nasa/worldwind/util/tree/BasicTreeLayout.java
+++ b/src/gov/nasa/worldwind/util/tree/BasicTreeLayout.java
@@ -28,6 +28,7 @@
package gov.nasa.worldwind.util.tree;
+import com.jogamp.opengl.util.awt.TextRenderer;
import com.jogamp.opengl.util.texture.TextureCoords;
import gov.nasa.worldwind.WWObjectImpl;
import gov.nasa.worldwind.avlist.AVKey;
diff --git a/src/gov/nasa/worldwind/util/tree/ScrollFrame.java b/src/gov/nasa/worldwind/util/tree/ScrollFrame.java
index 6111066332..057fd378fa 100644
--- a/src/gov/nasa/worldwind/util/tree/ScrollFrame.java
+++ b/src/gov/nasa/worldwind/util/tree/ScrollFrame.java
@@ -28,6 +28,7 @@
package gov.nasa.worldwind.util.tree;
+import com.jogamp.opengl.util.awt.TextRenderer;
import com.jogamp.opengl.util.texture.*;
import gov.nasa.worldwind.*;
import gov.nasa.worldwind.avlist.AVKey;
diff --git a/src/gov/nasa/worldwindx/applications/antenna/AntennaAxes.java b/src/gov/nasa/worldwindx/applications/antenna/AntennaAxes.java
index de6ad819d8..23dc2412e0 100644
--- a/src/gov/nasa/worldwindx/applications/antenna/AntennaAxes.java
+++ b/src/gov/nasa/worldwindx/applications/antenna/AntennaAxes.java
@@ -37,6 +37,8 @@
import gov.nasa.worldwind.util.*;
import com.jogamp.opengl.*;
+import com.jogamp.opengl.util.awt.TextRenderer;
+
import javax.xml.stream.*;
import java.awt.*;
import java.io.IOException;
diff --git a/src/gov/nasa/worldwindx/applications/sar/render/TrackSegmentInfo.java b/src/gov/nasa/worldwindx/applications/sar/render/TrackSegmentInfo.java
index 5455ebed9f..3503bd4c49 100644
--- a/src/gov/nasa/worldwindx/applications/sar/render/TrackSegmentInfo.java
+++ b/src/gov/nasa/worldwindx/applications/sar/render/TrackSegmentInfo.java
@@ -34,6 +34,8 @@
import gov.nasa.worldwindx.applications.sar.*;
import com.jogamp.opengl.*;
+import com.jogamp.opengl.util.awt.TextRenderer;
+
import java.awt.*;
/**
diff --git a/src/gov/nasa/worldwindx/applications/sar/segmentplane/SegmentPlaneRenderer.java b/src/gov/nasa/worldwindx/applications/sar/segmentplane/SegmentPlaneRenderer.java
index 9444f7228d..b99607e25a 100644
--- a/src/gov/nasa/worldwindx/applications/sar/segmentplane/SegmentPlaneRenderer.java
+++ b/src/gov/nasa/worldwindx/applications/sar/segmentplane/SegmentPlaneRenderer.java
@@ -40,6 +40,8 @@
import gov.nasa.worldwind.util.*;
import com.jogamp.opengl.*;
+import com.jogamp.opengl.util.awt.TextRenderer;
+
import java.awt.*;
import java.nio.*;
import java.util.*;
diff --git a/src/gov/nasa/worldwindx/examples/Annotations.java b/src/gov/nasa/worldwindx/examples/Annotations.java
index cd0e8972dc..866ba403c6 100644
--- a/src/gov/nasa/worldwindx/examples/Annotations.java
+++ b/src/gov/nasa/worldwindx/examples/Annotations.java
@@ -38,6 +38,8 @@
import gov.nasa.worldwindx.examples.util.PowerOfTwoPaddedImage;
import com.jogamp.opengl.*;
+import com.jogamp.opengl.util.awt.TextRenderer;
+
import javax.swing.*;
import javax.swing.Box;
import javax.swing.border.*;
diff --git a/src/gov/nasa/worldwindx/examples/ContextMenusOnShapes.java b/src/gov/nasa/worldwindx/examples/ContextMenusOnShapes.java
index c432b47db4..9ace62fed7 100644
--- a/src/gov/nasa/worldwindx/examples/ContextMenusOnShapes.java
+++ b/src/gov/nasa/worldwindx/examples/ContextMenusOnShapes.java
@@ -72,7 +72,6 @@ public void selected(SelectEvent event) {
}
}
- @SuppressWarnings({"UnusedDeclaration"})
protected void highlight(SelectEvent event, Object o) {
if (this.lastPickedPlacemark == o) {
return; // same thing selected
@@ -110,7 +109,7 @@ protected void showContextMenu(SelectEvent event) {
}
ContextMenu menu = new ContextMenu((Component) event.getSource(), menuInfo);
- menu.show(event.getMouseEvent());
+ menu.show(event.getAwtMousePt());
}
}
}
@@ -145,7 +144,7 @@ protected void makeMenuItems() {
}
}
- public void show(final MouseEvent event) {
+ public void show(final Point screenPt) {
JPopupMenu popup = new JPopupMenu();
popup.add(this.menuTitleItem);
@@ -156,7 +155,7 @@ public void show(final MouseEvent event) {
popup.add(subMenu);
}
- popup.show(sourceComponent, event.getX(), event.getY());
+ popup.show(sourceComponent, (int)screenPt.getX(), (int)screenPt.getY());
}
}
diff --git a/src/gov/nasa/worldwindx/examples/EGM2008Offsets.java b/src/gov/nasa/worldwindx/examples/EGM2008Offsets.java
new file mode 100644
index 0000000000..f2b403ca0d
--- /dev/null
+++ b/src/gov/nasa/worldwindx/examples/EGM2008Offsets.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2006-2009, 2017, 2020 United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The NASA World Wind Java (WWJ) platform is licensed under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed
+ * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ *
+ * NASA World Wind Java (WWJ) also contains the following 3rd party Open Source
+ * software:
+ *
+ * Jackson Parser – Licensed under Apache 2.0
+ * GDAL – Licensed under MIT
+ * JOGL – Licensed under Berkeley Software Distribution (BSD)
+ * Gluegen – Licensed under Berkeley Software Distribution (BSD)
+ *
+ * A complete listing of 3rd Party software notices and licenses included in
+ * NASA World Wind Java (WWJ) can be found in the WorldWindJava-v2.2 3rd-party
+ * notices and licenses PDF found in code directory.
+ */
+package gov.nasa.worldwindx.examples;
+
+import gov.nasa.worldwind.Model;
+import gov.nasa.worldwind.avlist.AVKey;
+import gov.nasa.worldwind.geom.LatLon;
+import gov.nasa.worldwind.geom.Position;
+import gov.nasa.worldwind.geom.Sector;
+import gov.nasa.worldwind.globes.Earth;
+import gov.nasa.worldwind.globes.Globe;
+import gov.nasa.worldwind.layers.RenderableLayer;
+import gov.nasa.worldwind.render.PointPlacemark;
+import gov.nasa.worldwind.util.EGM2008;
+import java.awt.Dimension;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import javax.swing.SwingUtilities;
+
+/**
+ * Shows how to apply EGM2008 offsets to Earth elevations.
+ *
+ * This EGM2008 data file is not included in the SDK due to its size. The data may be downloaded here:
+ * https://builds.worldwind.arc.nasa.gov/artifactory/EGM2008-Data/egm2008_25.dat
+ *
+ * This example looks for the EGM2008 data in the WorldWind src/config folder by default.
+ */
+public class EGM2008Offsets extends ApplicationTemplate
+{
+ public static class AppFrame extends ApplicationTemplate.AppFrame
+ {
+ /**
+ * Attempt to retrieve the best elevations for a specified list of locations. The elevations returned are the best currently
+ * available for the data set and the area bounding the locations. Since the necessary elevation data might not
+ * be in memory at the time of the call, this method iterates until the necessary elevation data is in memory
+ * and can be used to determine the locations elevations.
+ *
+ * @param locations a list of locations to determine elevations for
+ */
+ public void loadBestElevations(ArrayList locations)
+ {
+ Globe globe = this.getWwd().getModel().getGlobe();
+ ArrayList sectors = new ArrayList<>();
+ ArrayList> locationsList = new ArrayList<>();
+ double delta = 0.0001;
+ for (LatLon ll : locations)
+ {
+ double lat = ll.latitude.degrees;
+ double lon = ll.longitude.degrees;
+ sectors.add(Sector.fromDegrees(lat, lat + delta, lon, lon + delta));
+ ArrayList sectorLocations = new ArrayList<>();
+ sectorLocations.add(ll);
+ sectorLocations.add(LatLon.fromDegrees(lat + delta, lon + delta));
+ locationsList.add(sectorLocations);
+ }
+
+ double[] targetResolutions = new double[sectors.size()];
+ double[] actualResolutions = new double[sectors.size()];
+ for (int i = 0, len = sectors.size(); i < len; i++)
+ {
+ targetResolutions[i] = globe.getElevationModel().getBestResolution(sectors.get(i));
+ }
+ boolean resolutionsAchieved = false;
+ double[] elevations = new double[2];
+ while (!resolutionsAchieved)
+ {
+ for (int i = 0, len = sectors.size(); i < len; i++)
+ {
+ actualResolutions[i] = globe.getElevations(sectors.get(i), locationsList.get(i), targetResolutions[i], elevations);
+ }
+
+ resolutionsAchieved = true;
+ for (int i = 0, len = actualResolutions.length; i < len && resolutionsAchieved; i++)
+ {
+ resolutionsAchieved = actualResolutions[i] <= targetResolutions[i];
+ }
+ if (!resolutionsAchieved)
+ {
+ try
+ {
+ Thread.sleep(200);
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ public AppFrame()
+ {
+ Model m = this.wwjPanel.getWwd().getModel();
+ Earth earth = (Earth) m.getGlobe();
+ final RenderableLayer layer = new RenderableLayer();
+ double[] locations = new double[]
+ {
+ 37.0, -119.0,
+ 36.0, -117.016667,
+ 89.0, 0.0,
+ -80.0, 0.0,
+ -90.0, 0.0
+ };
+ EGM2008 egm2008Offsets = new EGM2008("config/egm2008_25.dat");
+ boolean egmAvailable = egm2008Offsets.isEGMDataAvailable();
+ if (!egmAvailable)
+ {
+ System.out.println("*** EGM 2008 data not available.");
+ }
+
+ // Run the elevation query in a separate thread to avoid locking up the user interface
+ Thread t = new Thread(() ->
+ {
+ ArrayList elevLocations = new ArrayList<>();
+ for (int i = 0; i < locations.length; i += 2)
+ {
+ elevLocations.add(LatLon.fromDegrees(locations[i], locations[i + 1]));
+ }
+
+ loadBestElevations(elevLocations);
+
+ try
+ {
+ for (int i = 0; i < locations.length; i += 2)
+ {
+ Position pos = Position.fromDegrees(locations[i], locations[i + 1], 0);
+ PointPlacemark placemark = new PointPlacemark(pos);
+ String label = String.format("lat: %7.4f, lon: %7.4f", locations[i], locations[i + 1]);
+ float egmOffset = egm2008Offsets.getOffset(pos.latitude, pos.longitude);
+ double elevation = earth.getElevation(pos.latitude, pos.longitude);
+ if (egmAvailable)
+ {
+ placemark.setValue(AVKey.DISPLAY_NAME, String.format("EGM2008 Offset: %7.4f\nEllipsoid elevation:%7.4f\nEGM2008 Adjusted elevation: %7.4f",
+ egmOffset, elevation, elevation - egmOffset));
+ }
+ else
+ {
+ placemark.setValue(AVKey.DISPLAY_NAME, String.format("EGM2008 Offset: N/A\nEllipsoid elevation:%7.4f\nEGM2008 Adjusted elevation: N/A",
+ elevation));
+ }
+ placemark.setLabelText(label);
+ layer.addRenderable(placemark);
+ }
+ }
+ catch (IOException iex)
+ {
+ iex.printStackTrace();
+ }
+
+ SwingUtilities.invokeLater(() ->
+ {
+ System.out.println("Elevations retrieved");
+ getWwd().redraw();
+ });
+ });
+ t.start();
+
+ try
+ {
+ // Test offsets for some coordinates
+ float lat = 47;
+ float lon = -94;
+ System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon));
+
+ lat = 37;
+ lon = -119;
+ System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon));
+
+ // Try previous coordinates to verify caching
+ lat = 47;
+ lon = -94;
+ System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon));
+
+ lat = 37;
+ lon = -119;
+ System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon));
+
+ lat = 47.02f;
+ lon = -94.02f;
+ System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon));
+
+ float gridResolution = (float) EGM2008.GRID_RESOLUTION;
+ lat = 47 + gridResolution;
+ lon = -94 - gridResolution;
+ System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon));
+
+ lat = 36.0f;
+ lon = -117.0f;
+ System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon));
+
+ lat = 36.0f;
+ lon = -117.041666666667f;
+ System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon));
+
+ System.out.println();
+ }
+ catch (IOException iex)
+ {
+ iex.printStackTrace();
+ }
+
+ this.wwjPanel.toolTipController.setAnnotationSize(new Dimension(500, 0));
+ insertBeforeCompass(getWwd(), layer);
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ ApplicationTemplate.start("WorldWind EGM2008 Offsets", AppFrame.class);
+ }
+}
diff --git a/src/gov/nasa/worldwindx/examples/EGM96Offsets.java b/src/gov/nasa/worldwindx/examples/EGM96Offsets.java
index a64d11b1bc..39b7adf6b0 100644
--- a/src/gov/nasa/worldwindx/examples/EGM96Offsets.java
+++ b/src/gov/nasa/worldwindx/examples/EGM96Offsets.java
@@ -2,39 +2,50 @@
* Copyright 2006-2009, 2017, 2020 United States Government, as represented by the
* Administrator of the National Aeronautics and Space Administration.
* All rights reserved.
- *
+ *
* The NASA World Wind Java (WWJ) platform is licensed under the Apache License,
* Version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
- *
+ *
* NASA World Wind Java (WWJ) also contains the following 3rd party Open Source
* software:
- *
+ *
* Jackson Parser – Licensed under Apache 2.0
* GDAL – Licensed under MIT
* JOGL – Licensed under Berkeley Software Distribution (BSD)
* Gluegen – Licensed under Berkeley Software Distribution (BSD)
- *
+ *
* A complete listing of 3rd Party software notices and licenses included in
* NASA World Wind Java (WWJ) can be found in the WorldWindJava-v2.2 3rd-party
* notices and licenses PDF found in code directory.
*/
-
package gov.nasa.worldwindx.examples;
import gov.nasa.worldwind.Model;
+import gov.nasa.worldwind.avlist.AVKey;
+import gov.nasa.worldwind.geom.LatLon;
+import gov.nasa.worldwind.geom.Position;
+import gov.nasa.worldwind.geom.Sector;
import gov.nasa.worldwind.globes.Earth;
+import gov.nasa.worldwind.globes.Globe;
+import gov.nasa.worldwind.layers.RenderableLayer;
+import gov.nasa.worldwind.render.PointPlacemark;
+import gov.nasa.worldwind.util.EGM96;
+import java.awt.Dimension;
import java.io.IOException;
+import java.util.ArrayList;
+import javax.swing.SwingUtilities;
/**
* Shows how to apply EGM96 offsets to the Earth.
+ *
* @author tag
* @version $Id: EGM96Offsets.java 1501 2013-07-11 15:59:11Z tgaskins $
*/
@@ -42,17 +53,118 @@ public class EGM96Offsets extends ApplicationTemplate
{
public static class AppFrame extends ApplicationTemplate.AppFrame
{
+ /**
+ * Attempt to retrieve the best elevations for a specified list of locations. The elevations returned are the best currently
+ * available for the data set and the area bounding the locations. Since the necessary elevation data might not
+ * be in memory at the time of the call, this method iterates until the necessary elevation data is in memory
+ * and can be used to determine the locations elevations.
+ *
+ * @param locations a list of locations to determine elevations for
+ */
+ public void loadBestElevations(ArrayList locations)
+ {
+ Globe globe = this.getWwd().getModel().getGlobe();
+ ArrayList sectors = new ArrayList<>();
+ ArrayList> locationsList = new ArrayList<>();
+ double delta = 0.0001;
+ for (LatLon ll : locations)
+ {
+ double lat = ll.latitude.degrees;
+ double lon = ll.longitude.degrees;
+ sectors.add(Sector.fromDegrees(lat, lat + delta, lon, lon + delta));
+ ArrayList sectorLocations = new ArrayList<>();
+ sectorLocations.add(ll);
+ sectorLocations.add(LatLon.fromDegrees(lat + delta, lon + delta));
+ locationsList.add(sectorLocations);
+ }
+
+ double[] targetResolutions = new double[sectors.size()];
+ double[] actualResolutions = new double[sectors.size()];
+ for (int i = 0, len = sectors.size(); i < len; i++)
+ {
+ targetResolutions[i] = globe.getElevationModel().getBestResolution(sectors.get(i));
+ }
+ boolean resolutionsAchieved = false;
+ double[] elevations = new double[2];
+ while (!resolutionsAchieved)
+ {
+ for (int i = 0, len = sectors.size(); i < len; i++)
+ {
+ actualResolutions[i] = globe.getElevations(sectors.get(i), locationsList.get(i), targetResolutions[i], elevations);
+ }
+
+ resolutionsAchieved = true;
+ for (int i = 0, len = actualResolutions.length; i < len && resolutionsAchieved; i++)
+ {
+ resolutionsAchieved = actualResolutions[i] <= targetResolutions[i];
+ }
+ if (!resolutionsAchieved)
+ {
+ try
+ {
+ Thread.sleep(200);
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
public AppFrame()
{
Model m = this.wwjPanel.getWwd().getModel();
+ Earth earth = (Earth) m.getGlobe();
try
{
- ((Earth) m.getGlobe()).applyEGMA96Offsets("config/EGM96.dat");
+ earth.applyEGMA96Offsets("config/EGM96.dat");
}
catch (IOException e)
{
e.printStackTrace();
}
+ final RenderableLayer layer = new RenderableLayer();
+ double[] locations = new double[]
+ {
+ 37.0, -119.0,
+ 36.0, -117.016667,
+ 89.0, 0.0,
+ -80.0, 0.0,
+ -90.0, 0.0
+ };
+ Thread t = new Thread(() ->
+ {
+ ArrayList elevLocations = new ArrayList<>();
+ for (int i = 0; i < locations.length; i += 2)
+ {
+ elevLocations.add(LatLon.fromDegrees(locations[i], locations[i + 1]));
+ }
+
+ loadBestElevations(elevLocations);
+
+ EGM96 egm96Offsets = earth.getEGM96();
+ for (int i = 0; i < locations.length; i += 2)
+ {
+ Position pos = Position.fromDegrees(locations[i], locations[i + 1], 0);
+ PointPlacemark placemark = new PointPlacemark(pos);
+ String label = String.format("lat: %7.4f, lon: %7.4f", locations[i], locations[i + 1]);
+ placemark.setValue(AVKey.DISPLAY_NAME, String.format("EGM96 Offset: %7.4f\nEGM96 Adjusted elevation: %7.4f",
+ egm96Offsets.getOffset(pos.latitude, pos.longitude),
+ earth.getElevation(pos.latitude, pos.longitude)));
+ placemark.setLabelText(label);
+ layer.addRenderable(placemark);
+ }
+ SwingUtilities.invokeLater(() ->
+ {
+ System.out.println("Elevations retrieved");
+ getWwd().redraw();
+ });
+ });
+ t.start();
+
+ this.wwjPanel.toolTipController.setAnnotationSize(new Dimension(500, 0));
+ insertBeforeCompass(getWwd(), layer);
}
}
diff --git a/src/gov/nasa/worldwindx/examples/analytics/AnalyticSurfaceLegend.java b/src/gov/nasa/worldwindx/examples/analytics/AnalyticSurfaceLegend.java
index 1daa69884b..69b42dfc81 100644
--- a/src/gov/nasa/worldwindx/examples/analytics/AnalyticSurfaceLegend.java
+++ b/src/gov/nasa/worldwindx/examples/analytics/AnalyticSurfaceLegend.java
@@ -37,6 +37,8 @@
import java.text.Format;
import java.util.ArrayList;
+import com.jogamp.opengl.util.awt.TextRenderer;
+
/**
* @author dcollins
* @version $Id: AnalyticSurfaceLegend.java 2053 2014-06-10 20:16:57Z tgaskins $
diff --git a/src/gov/nasa/worldwindx/examples/util/StatusLayer.java b/src/gov/nasa/worldwindx/examples/util/StatusLayer.java
index 9544a97cee..76f9d4c6e5 100644
--- a/src/gov/nasa/worldwindx/examples/util/StatusLayer.java
+++ b/src/gov/nasa/worldwindx/examples/util/StatusLayer.java
@@ -27,6 +27,7 @@
*/
package gov.nasa.worldwindx.examples.util;
+import com.jogamp.opengl.util.awt.TextRenderer;
import com.jogamp.opengl.util.texture.*;
import gov.nasa.worldwind.*;
import gov.nasa.worldwind.event.*;
diff --git a/src/gov/nasa/worldwindx/examples/util/ToolTipController.java b/src/gov/nasa/worldwindx/examples/util/ToolTipController.java
index 0cadd531d9..05e58a7e63 100644
--- a/src/gov/nasa/worldwindx/examples/util/ToolTipController.java
+++ b/src/gov/nasa/worldwindx/examples/util/ToolTipController.java
@@ -2,25 +2,25 @@
* Copyright 2006-2009, 2017, 2020 United States Government, as represented by the
* Administrator of the National Aeronautics and Space Administration.
* All rights reserved.
- *
+ *
* The NASA World Wind Java (WWJ) platform is licensed under the Apache License,
* Version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
- *
+ *
* NASA World Wind Java (WWJ) also contains the following 3rd party Open Source
* software:
- *
+ *
* Jackson Parser – Licensed under Apache 2.0
* GDAL – Licensed under MIT
* JOGL – Licensed under Berkeley Software Distribution (BSD)
* Gluegen – Licensed under Berkeley Software Distribution (BSD)
- *
+ *
* A complete listing of 3rd Party software notices and licenses included in
* NASA World Wind Java (WWJ) can be found in the WorldWindJava-v2.2 3rd-party
* notices and licenses PDF found in code directory.
@@ -34,6 +34,7 @@
import gov.nasa.worldwindx.examples.ApplicationTemplate;
import gov.nasa.worldwind.layers.*;
import gov.nasa.worldwind.util.*;
+import java.awt.Dimension;
/**
* Controls display of tool tips on picked objects. Any shape implementing {@link AVList} can participate. Shapes
@@ -52,6 +53,7 @@ public class ToolTipController implements SelectListener, Disposable
protected Object lastHoverObject;
protected AnnotationLayer layer;
protected ToolTipAnnotation annotation;
+ protected Dimension annotationSize = null;
/**
* Create a controller for a specified {@link WorldWindow} that displays tool tips on hover and/or rollover.
@@ -84,6 +86,16 @@ public ToolTipController(WorldWindow wwd)
this.wwd.addSelectListener(this);
}
+ public Dimension getAnnotationSize()
+ {
+ return this.annotationSize;
+ }
+
+ public void setAnnotationSize(Dimension value)
+ {
+ this.annotationSize = value;
+ }
+
public void dispose()
{
this.wwd.removeSelectListener(this);
@@ -91,14 +103,14 @@ public void dispose()
protected String getHoverText(SelectEvent event)
{
- return event.getTopObject() != null && event.getTopObject() instanceof AVList ?
- ((AVList) event.getTopObject()).getStringValue(this.hoverKey) : null;
+ return event.getTopObject() != null && event.getTopObject() instanceof AVList
+ ? ((AVList) event.getTopObject()).getStringValue(this.hoverKey) : null;
}
protected String getRolloverText(SelectEvent event)
{
- return event.getTopObject() != null && event.getTopObject() instanceof AVList ?
- ((AVList) event.getTopObject()).getStringValue(this.rolloverKey) : null;
+ return event.getTopObject() != null && event.getTopObject() instanceof AVList
+ ? ((AVList) event.getTopObject()).getStringValue(this.rolloverKey) : null;
}
public void selected(SelectEvent event)
@@ -167,6 +179,7 @@ protected void showToolTip(SelectEvent event, String text)
else
{
annotation = new ToolTipAnnotation(text);
+ annotation.getAttributes().setSize(this.annotationSize);
}
if (layer == null)