40package com.jogamp.opengl.util.awt;
42import com.jogamp.common.nio.Buffers;
43import com.jogamp.common.util.InterruptSource;
44import com.jogamp.common.util.PropertyAccess;
45import com.jogamp.nativewindow.NativeSurface;
46import com.jogamp.nativewindow.ScalableSurface;
47import com.jogamp.opengl.util.*;
48import com.jogamp.opengl.util.packrect.*;
49import com.jogamp.opengl.util.texture.*;
51import java.awt.AlphaComposite;
55import java.awt.EventQueue;
58import java.awt.Graphics2D;
61import java.awt.RenderingHints;
62import java.awt.event.*;
63import java.awt.font.*;
64import java.awt.geom.*;
69import com.jogamp.opengl.*;
70import com.jogamp.opengl.fixedfunc.GLPointerFunc;
71import com.jogamp.opengl.glu.*;
72import com.jogamp.opengl.awt.*;
74import jogamp.opengl.Debug;
131 private static final boolean DEBUG;
134 Debug.initSingleton();
135 DEBUG = PropertyAccess.isPropertyDefined(
"jogl.debug.TextRenderer",
true);
139 private static final boolean DISABLE_GLYPH_CACHE =
false;
140 private static final boolean DRAW_BBOXES =
false;
142 static final int kSize = 256;
146 private static final int CYCLES_PER_FLUSH = 100;
150 private static final float MAX_VERTICAL_FRAGMENTATION = 0.7f;
151 static final int kQuadsPerBuffer = 100;
152 static final int kCoordsPerVertVerts = 3;
153 static final int kCoordsPerVertTex = 2;
154 static final int kVertsPerQuad = 4;
155 static final int kTotalBufferSizeVerts = kQuadsPerBuffer * kVertsPerQuad;
156 static final int kTotalBufferSizeCoordsVerts = kQuadsPerBuffer * kVertsPerQuad * kCoordsPerVertVerts;
157 static final int kTotalBufferSizeCoordsTex = kQuadsPerBuffer * kVertsPerQuad * kCoordsPerVertTex;
158 static final int kTotalBufferSizeBytesVerts = kTotalBufferSizeCoordsVerts * 4;
159 static final int kTotalBufferSizeBytesTex = kTotalBufferSizeCoordsTex * 4;
160 static final int kSizeInBytes_OneVertices_VertexData = kCoordsPerVertVerts * 4;
161 static final int kSizeInBytes_OneVertices_TexData = kCoordsPerVertTex * 4;
162 private final Font font;
163 private final boolean antialiased;
164 private final boolean useFractionalMetrics;
167 private boolean mipmap;
169 private boolean haveMaxSize;
172 private Graphics2D cachedGraphics;
173 private FontRenderContext cachedFontRenderContext;
174 private final Map<String, Rect> stringLocations =
new HashMap<String, Rect>();
175 private final GlyphProducer mGlyphProducer;
177 private int numRenderCycles;
182 private boolean inBeginEndPair;
183 private boolean isOrthoMode;
184 private int beginRenderingWidth;
185 private int beginRenderingHeight;
186 private boolean beginRenderingDepthTestDisabled;
189 private boolean haveCachedColor;
190 private float cachedR;
191 private float cachedG;
192 private float cachedB;
193 private float cachedA;
194 private Color cachedColor;
195 private boolean needToResetColor;
198 private Frame dbgFrame;
201 private boolean debugged;
202 Pipelined_QuadRenderer mPipelinedQuadRenderer;
205 private boolean useVertexArrays =
true;
208 private boolean isExtensionAvailable_GL_VERSION_1_5;
209 private boolean checkFor_isExtensionAvailable_GL_VERSION_1_5;
212 private boolean smoothing =
true;
222 this(font,
false,
false,
null,
false);
236 this(font,
false,
false,
null, mipmap);
253 final boolean useFractionalMetrics) {
254 this(font, antialiased, useFractionalMetrics,
null,
false);
272 final boolean useFractionalMetrics,
final RenderDelegate renderDelegate) {
273 this(font, antialiased, useFractionalMetrics, renderDelegate,
false);
294 final boolean useFractionalMetrics,
RenderDelegate renderDelegate,
295 final boolean mipmap) {
300 float[] surfaceScale =
new float[2];
302 float newSize = font.getSize() * surfaceScale[0];
303 this.font = font.deriveFont(newSize);
308 this.antialiased = antialiased;
309 this.useFractionalMetrics = useFractionalMetrics;
310 this.mipmap = mipmap;
316 if (renderDelegate ==
null) {
320 this.renderDelegate = renderDelegate;
322 mGlyphProducer =
new GlyphProducer(font.getNumGlyphs());
349 final Rect r = stringLocations.get(str);
355 return new Rectangle2D.Double(-data.origin().x, -data.origin().y,
361 return normalize(renderDelegate.
getBounds(str, font,
376 if (cachedFontRenderContext ==
null) {
377 cachedFontRenderContext = getGraphics2D().getFontRenderContext();
380 return cachedFontRenderContext;
419 public void beginRendering(
final int width,
final int height,
final boolean disableDepthTest)
447 final boolean noNeedForFlush = (haveCachedColor && (cachedColor !=
null) &&
448 color.equals(cachedColor));
450 if (!noNeedForFlush) {
451 flushGlyphPipeline();
455 haveCachedColor =
true;
474 public void setColor(
final float r,
final float g,
final float b,
final float a)
476 final boolean noNeedForFlush = (haveCachedColor && (cachedColor ==
null) &&
477 (r == cachedR) && (g == cachedG) && (b == cachedB) &&
480 if (!noNeedForFlush) {
481 flushGlyphPipeline();
484 getBackingStore().
setColor(r, g, b, a);
485 haveCachedColor =
true;
504 public void draw(
final CharSequence str,
final int x,
final int y)
throws GLException {
527 public void draw3D(
final CharSequence str,
final float x,
final float y,
final float z,
528 final float scaleFactor) {
529 internal_draw3D(str, x, y, z, scaleFactor);
536 public void draw3D(
final String str,
final float x,
final float y,
final float z,
final float scaleFactor) {
537 internal_draw3D(str, x, y, z, scaleFactor);
542 return mGlyphProducer.getGlyphPixelWidth(inChar);
551 flushGlyphPipeline();
581 if(
null != mPipelinedQuadRenderer ) {
582 mPipelinedQuadRenderer.dispose();
586 cachedBackingStore =
null;
587 cachedGraphics =
null;
588 cachedFontRenderContext =
null;
590 if (dbgFrame !=
null) {
599 private static Rectangle2D preNormalize(
final Rectangle2D src) {
604 final int minX = (int) Math.floor(src.getMinX()) - 1;
605 final int minY = (int) Math.floor(src.getMinY()) - 1;
606 final int maxX = (int) Math.ceil(src.getMaxX()) + 1;
607 final int maxY = (int) Math.ceil(src.getMaxY()) + 1;
608 return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
612 private Rectangle2D normalize(
final Rectangle2D src) {
620 final int boundary = (int) Math.max(1, 0.015 * font.getSize());
622 return new Rectangle2D.Double((
int) Math.floor(src.getMinX() - boundary),
623 (int) Math.floor(src.getMinY() - boundary),
624 (
int) Math.ceil(src.getWidth() + 2 * boundary),
625 (
int) Math.ceil(src.getHeight()) + 2 * boundary);
628 private TextureRenderer getBackingStore() {
629 final TextureRenderer renderer = (TextureRenderer) packer.
getBackingStore();
631 if (renderer != cachedBackingStore) {
633 if (cachedGraphics !=
null) {
634 cachedGraphics.dispose();
635 cachedGraphics =
null;
636 cachedFontRenderContext =
null;
639 cachedBackingStore = renderer;
642 return cachedBackingStore;
645 private Graphics2D getGraphics2D() {
646 final TextureRenderer renderer = getBackingStore();
648 if (cachedGraphics ==
null) {
649 cachedGraphics = renderer.createGraphics();
652 cachedGraphics.setComposite(AlphaComposite.Src);
653 cachedGraphics.setColor(Color.WHITE);
654 cachedGraphics.setFont(font);
655 cachedGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
656 (antialiased ? RenderingHints.VALUE_TEXT_ANTIALIAS_ON
657 : RenderingHints.VALUE_TEXT_ANTIALIAS_OFF));
658 cachedGraphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
659 (useFractionalMetrics
660 ? RenderingHints.VALUE_FRACTIONALMETRICS_ON
661 : RenderingHints.VALUE_FRACTIONALMETRICS_OFF));
664 return cachedGraphics;
667 private void beginRendering(
final boolean ortho,
final int width,
final int height,
668 final boolean disableDepthTestForOrtho) {
671 if (DEBUG && !debugged) {
675 inBeginEndPair =
true;
677 beginRenderingWidth = width;
678 beginRenderingHeight = height;
679 beginRenderingDepthTestDisabled = disableDepthTestForOrtho;
683 disableDepthTestForOrtho);
694 final int[] sz =
new int[1];
700 if (needToResetColor && haveCachedColor) {
701 if (cachedColor ==
null) {
702 getBackingStore().
setColor(cachedR, cachedG, cachedB, cachedA);
704 getBackingStore().
setColor(cachedColor);
707 needToResetColor =
false;
712 if (mipmap && !getBackingStore().isUsingAutoMipmapGeneration()) {
714 System.err.println(
"Disabled mipmapping in TextRenderer");
729 flushGlyphPipeline();
731 inBeginEndPair =
false;
744 }
catch (
final Exception e) {
745 isExtensionAvailable_GL_VERSION_1_5 =
false;
755 if (++numRenderCycles >= CYCLES_PER_FLUSH) {
759 System.err.println(
"Clearing unused entries in endRendering()");
762 clearUnusedEntries();
766 private void clearUnusedEntries() {
767 final java.util.List<
Rect> deadRects =
new ArrayList<Rect>();
773 public void visit(
final Rect rect) {
774 final TextData data = (TextData) rect.
getUserData();
784 for (
final Rect r : deadRects) {
786 stringLocations.remove(((TextData) r.getUserData()).
string());
788 final int unicodeToClearFromCache = ((TextData) r.getUserData()).unicodeID;
790 if (unicodeToClearFromCache > 0) {
791 mGlyphProducer.clearCacheEntry(unicodeToClearFromCache);
805 if (!deadRects.isEmpty() && (frag > MAX_VERTICAL_FRAGMENTATION)) {
808 "Compacting TextRenderer backing store due to vertical fragmentation " +
816 getBackingStore().
markDirty(0, 0, getBackingStore().getWidth(),
817 getBackingStore().getHeight());
821 private void internal_draw3D(
final CharSequence str,
float x,
final float y,
final float z,
822 final float scaleFactor) {
823 for (
final Glyph glyph : mGlyphProducer.getGlyphs(str)) {
824 final float advance = glyph.draw3D(x, y, z, scaleFactor);
825 x += advance * scaleFactor;
829 private void flushGlyphPipeline() {
830 if (mPipelinedQuadRenderer !=
null) {
831 mPipelinedQuadRenderer.draw();
835 private void draw3D_ROBUST(
final CharSequence str,
final float x,
final float y,
final float z,
836 final float scaleFactor) {
838 if (str instanceof String) {
839 curStr = (String) str;
841 curStr = str.toString();
845 Rect rect = stringLocations.get(curStr);
849 Graphics2D g = getGraphics2D();
851 final Rectangle2D bbox = normalize(origBBox);
852 final Point origin =
new Point((
int) -bbox.getMinX(),
853 (
int) -bbox.getMinY());
854 rect =
new Rect(0, 0, (
int) bbox.getWidth(),
855 (
int) bbox.getHeight(),
856 new TextData(curStr, origin, origBBox, -1));
859 stringLocations.put(curStr, rect);
867 final int strx = rect.x() + origin.x;
868 final int stry = rect.y() + origin.y;
871 g.setComposite(AlphaComposite.Clear);
872 g.fillRect(rect.x(), rect.y(), rect.w(), rect.h());
873 g.setComposite(AlphaComposite.Src);
876 renderDelegate.
draw(g, curStr, strx, stry);
879 final TextData data = (TextData) rect.
getUserData();
881 g.drawRect(strx - data.origOriginX(),
882 stry - data.origOriginY(),
883 (int) data.origRect().getWidth(),
884 (int) data.origRect().getHeight());
885 g.drawRect(strx - data.origin().x,
886 stry - data.origin().y,
892 getBackingStore().
markDirty(rect.x(), rect.y(), rect.w(),
897 final TextureRenderer renderer = getBackingStore();
902 final TextData data = (TextData) rect.
getUserData();
905 final Rectangle2D origRect = data.origRect();
908 renderer.draw3DRect(x - (scaleFactor * data.origOriginX()),
909 y - (scaleFactor * ((float) origRect.getHeight() - data.origOriginY())), z,
910 rect.x() + (data.origin().x - data.origOriginX()),
911 renderer.getHeight() - rect.y() - (int) origRect.getHeight() -
912 (data.origin().y - data.origOriginY()),
913 (
int) origRect.getWidth(), (int) origRect.getHeight(), scaleFactor);
919 private void debug(
final GL gl) {
920 dbgFrame =
new Frame(
"TextRenderer Debug Output");
925 dbgFrame.add(dbgCanvas);
928 dbgFrame.addWindowListener(
new WindowAdapter() {
930 public void windowClosing(
final WindowEvent e) {
934 new InterruptSource.Thread(
null,
new Runnable() {
942 dbgFrame.setSize(kSize, kSize);
943 dbgFrame.setVisible(
true);
965 FontRenderContext frc);
969 public Rectangle2D
getBounds(CharSequence str, Font font,
970 FontRenderContext frc);
975 public Rectangle2D
getBounds(GlyphVector gv, FontRenderContext frc);
990 public void draw(Graphics2D graphics, String str,
int x,
int y);
1008 private static class CharSequenceIterator
implements CharacterIterator {
1009 CharSequence mSequence;
1013 CharSequenceIterator() {
1016 CharSequenceIterator(
final CharSequence sequence) {
1017 initFromCharSequence(sequence);
1020 public void initFromCharSequence(
final CharSequence sequence) {
1021 mSequence = sequence;
1022 mLength = mSequence.length();
1027 public char last() {
1028 mCurrentIndex = Math.max(0, mLength - 1);
1034 public char current() {
1035 if ((mLength == 0) || (mCurrentIndex >= mLength)) {
1036 return CharacterIterator.DONE;
1039 return mSequence.charAt(mCurrentIndex);
1043 public char next() {
1050 public char previous() {
1051 mCurrentIndex = Math.max(mCurrentIndex - 1, 0);
1057 public char setIndex(
final int position) {
1058 mCurrentIndex = position;
1064 public int getBeginIndex() {
1069 public int getEndIndex() {
1074 public int getIndex() {
1075 return mCurrentIndex;
1079 public Object clone() {
1080 final CharSequenceIterator iter =
new CharSequenceIterator(mSequence);
1081 iter.mCurrentIndex = mCurrentIndex;
1087 public char first() {
1089 return CharacterIterator.DONE;
1099 static class TextData {
1102 private final String str;
1113 private final Point origin;
1121 private final Rectangle2D origRect;
1123 private boolean used;
1125 TextData(
final String str,
final Point origin,
final Rectangle2D origRect,
final int unicodeID) {
1127 this.origin = origin;
1128 this.origRect = origRect;
1129 this.unicodeID = unicodeID;
1143 return (
int) -origRect.getMinX();
1147 return (
int) -origRect.getMinY();
1150 Rectangle2D origRect() {
1168 private Graphics2D g;
1171 public Object allocateBackingStore(
final int w,
final int h) {
1176 TextureRenderer renderer;
1179 renderer = TextureRenderer.createAlphaOnlyRenderer(w, h, mipmap);
1181 renderer =
new TextureRenderer(w, h,
true, mipmap);
1183 renderer.setSmoothing(smoothing);
1186 System.err.println(
" TextRenderer allocating backing store " +
1194 public void deleteBackingStore(
final Object backingStore) {
1195 ((TextureRenderer) backingStore).dispose();
1199 public boolean preExpand(
final Rect cause,
final int attemptNumber) {
1214 if (attemptNumber == 0) {
1217 "Clearing unused entries in preExpand(): attempt number " +
1221 if (inBeginEndPair) {
1226 clearUnusedEntries();
1235 public boolean additionFailed(
final Rect cause,
final int attemptNumber) {
1238 stringLocations.clear();
1239 mGlyphProducer.clearAllCacheEntries();
1243 " *** Cleared all text because addition failed ***");
1246 if (attemptNumber == 0) {
1254 public boolean canCompact() {
1259 public void beginMovement(
final Object oldBackingStore,
final Object newBackingStore) {
1261 if (inBeginEndPair) {
1276 }
catch (
final Exception e) {
1277 isExtensionAvailable_GL_VERSION_1_5 =
false;
1282 ((TextureRenderer) oldBackingStore).endOrthoRendering();
1284 ((TextureRenderer) oldBackingStore).end3DRendering();
1288 final TextureRenderer newRenderer = (TextureRenderer) newBackingStore;
1289 g = newRenderer.createGraphics();
1293 public void move(
final Object oldBackingStore,
final Rect oldLocation,
1294 final Object newBackingStore,
final Rect newLocation) {
1295 final TextureRenderer oldRenderer = (TextureRenderer) oldBackingStore;
1296 final TextureRenderer newRenderer = (TextureRenderer) newBackingStore;
1298 if (oldRenderer == newRenderer) {
1300 g.copyArea(oldLocation.x(), oldLocation.y(), oldLocation.w(),
1301 oldLocation.h(), newLocation.x() - oldLocation.x(),
1302 newLocation.y() - oldLocation.y());
1305 final Image img = oldRenderer.getImage();
1306 g.drawImage(img, newLocation.x(), newLocation.y(),
1307 newLocation.x() + newLocation.w(),
1308 newLocation.y() + newLocation.h(), oldLocation.x(),
1309 oldLocation.y(), oldLocation.x() + oldLocation.w(),
1310 oldLocation.y() + oldLocation.h(),
null);
1315 public void endMovement(
final Object oldBackingStore,
final Object newBackingStore) {
1319 final TextureRenderer newRenderer = (TextureRenderer) newBackingStore;
1320 newRenderer.markDirty(0, 0, newRenderer.getWidth(),
1321 newRenderer.getHeight());
1324 if (inBeginEndPair) {
1326 ((TextureRenderer) newBackingStore).beginOrthoRendering(beginRenderingWidth,
1327 beginRenderingHeight, beginRenderingDepthTestDisabled);
1329 ((TextureRenderer) newBackingStore).begin3DRendering();
1336 if (haveCachedColor) {
1337 if (cachedColor ==
null) {
1338 ((TextureRenderer) newBackingStore).setColor(cachedR,
1339 cachedG, cachedB, cachedA);
1341 ((TextureRenderer) newBackingStore).setColor(cachedColor);
1345 needToResetColor =
true;
1357 public Rectangle2D
getBounds(
final CharSequence str,
final Font font,
1358 final FontRenderContext frc) {
1359 return getBounds(font.createGlyphVector(frc,
1360 new CharSequenceIterator(str)),
1365 public Rectangle2D
getBounds(
final String str,
final Font font,
1366 final FontRenderContext frc) {
1367 return getBounds(font.createGlyphVector(frc, str), frc);
1371 public Rectangle2D
getBounds(
final GlyphVector gv,
final FontRenderContext frc) {
1372 return gv.getVisualBounds();
1377 final int x,
final int y) {
1378 graphics.drawGlyphVector(str, x, y);
1382 public void draw(
final Graphics2D graphics,
final String str,
final int x,
final int y) {
1383 graphics.drawString(str, x, y);
1392 private final char[] singleUnicode =
new char[1];
1409 private int unicodeID;
1412 private int glyphCode;
1414 private GlyphProducer producer;
1416 private float advance;
1420 private GlyphVector singleUnicodeGlyphVector;
1423 private Rect glyphRectForTextureMapping;
1429 private boolean needAdvance;
1432 public Glyph(
final int unicodeID,
1433 final int glyphCode,
1434 final float advance,
1435 final GlyphVector singleUnicodeGlyphVector,
1436 final GlyphProducer producer) {
1437 this.unicodeID = unicodeID;
1438 this.glyphCode = glyphCode;
1439 this.advance = advance;
1440 this.singleUnicodeGlyphVector = singleUnicodeGlyphVector;
1441 this.producer = producer;
1447 public Glyph(
final String str,
final boolean needAdvance) {
1449 this.needAdvance = needAdvance;
1453 public int getUnicodeID() {
1458 public int getGlyphCode() {
1463 public float getAdvance() {
1468 public float draw3D(
final float inX,
final float inY,
final float z,
final float scaleFactor) {
1470 draw3D_ROBUST(str, inX, inY, z, scaleFactor);
1476 float totalAdvance = 0;
1477 for (
int i = 0; i < gv.getNumGlyphs(); i++) {
1478 totalAdvance += gv.getGlyphMetrics(i).getAdvance();
1480 return totalAdvance;
1484 if (glyphRectForTextureMapping ==
null) {
1489 if (mPipelinedQuadRenderer ==
null) {
1490 mPipelinedQuadRenderer =
new Pipelined_QuadRenderer();
1493 final TextureRenderer renderer = getBackingStore();
1495 final TextureCoords wholeImageTexCoords = renderer.getTexture().getImageTexCoords();
1496 final float xScale = wholeImageTexCoords.right();
1497 final float yScale = wholeImageTexCoords.bottom();
1499 final Rect rect = glyphRectForTextureMapping;
1500 final TextData data = (TextData) rect.
getUserData();
1503 final Rectangle2D origRect = data.origRect();
1505 final float x = inX - (scaleFactor * data.origOriginX());
1506 final float y = inY - (scaleFactor * ((float) origRect.getHeight() - data.origOriginY()));
1508 final int texturex = rect.x() + (data.origin().x - data.origOriginX());
1509 final int texturey = renderer.getHeight() - rect.y() - (int) origRect.getHeight() -
1510 (data.origin().y - data.origOriginY());
1511 final int width = (int) origRect.getWidth();
1512 final int height = (int) origRect.getHeight();
1514 final float tx1 = xScale * texturex / renderer.getWidth();
1515 final float ty1 = yScale * (1.0f -
1516 ((float) texturey / (
float) renderer.getHeight()));
1517 final float tx2 = xScale * (texturex + width) / renderer.getWidth();
1518 final float ty2 = yScale * (1.0f -
1519 ((float) (texturey + height) / (float) renderer.getHeight()));
1521 mPipelinedQuadRenderer.glTexCoord2f(tx1, ty1);
1522 mPipelinedQuadRenderer.glVertex3f(x, y, z);
1523 mPipelinedQuadRenderer.glTexCoord2f(tx2, ty1);
1524 mPipelinedQuadRenderer.glVertex3f(x + (width * scaleFactor), y,
1526 mPipelinedQuadRenderer.glTexCoord2f(tx2, ty2);
1527 mPipelinedQuadRenderer.glVertex3f(x + (width * scaleFactor),
1528 y + (height * scaleFactor), z);
1529 mPipelinedQuadRenderer.glTexCoord2f(tx1, ty2);
1530 mPipelinedQuadRenderer.glVertex3f(x,
1531 y + (height * scaleFactor), z);
1532 }
catch (
final Exception e) {
1533 e.printStackTrace();
1539 public void clear() {
1540 glyphRectForTextureMapping =
null;
1543 private void upload() {
1544 final GlyphVector gv = getGlyphVector();
1546 final Rectangle2D bbox = normalize(origBBox);
1547 final Point origin =
new Point((
int) -bbox.getMinX(),
1548 (
int) -bbox.getMinY());
1549 final Rect rect =
new Rect(0, 0, (
int) bbox.getWidth(),
1550 (
int) bbox.getHeight(),
1551 new TextData(
null, origin, origBBox, unicodeID));
1553 glyphRectForTextureMapping = rect;
1554 final Graphics2D g = getGraphics2D();
1557 final int strx = rect.x() + origin.x;
1558 final int stry = rect.y() + origin.y;
1561 g.setComposite(AlphaComposite.Clear);
1562 g.fillRect(rect.x(), rect.y(), rect.w(), rect.h());
1563 g.setComposite(AlphaComposite.Src);
1569 final TextData data = (TextData) rect.
getUserData();
1571 g.drawRect(strx - data.origOriginX(),
1572 stry - data.origOriginY(),
1573 (int) data.origRect().getWidth(),
1574 (int) data.origRect().getHeight());
1575 g.drawRect(strx - data.origin().x,
1576 stry - data.origin().y,
1582 getBackingStore().markDirty(rect.x(), rect.y(), rect.w(),
1585 producer.register(
this);
1588 private GlyphVector getGlyphVector() {
1589 final GlyphVector gv = singleUnicodeGlyphVector;
1591 singleUnicodeGlyphVector =
null;
1594 singleUnicode[0] = (char) unicodeID;
1599 class GlyphProducer {
1600 static final int undefined = -2;
1601 final FontRenderContext fontRenderContext =
null;
1602 List<Glyph> glyphsOutput =
new ArrayList<Glyph>();
1603 HashMap<String, GlyphVector> fullGlyphVectorCache =
new HashMap<String, GlyphVector>();
1604 HashMap<Character, GlyphMetrics> glyphMetricsCache =
new HashMap<Character, GlyphMetrics>();
1606 int[] unicodes2Glyphs;
1610 CharSequenceIterator iter =
new CharSequenceIterator();
1612 GlyphProducer(
final int fontLengthInGlyphs) {
1613 unicodes2Glyphs =
new int[512];
1614 glyphCache =
new Glyph[fontLengthInGlyphs];
1615 clearAllCacheEntries();
1618 public List<Glyph> getGlyphs(
final CharSequence inString) {
1619 glyphsOutput.clear();
1620 GlyphVector fullRunGlyphVector;
1621 fullRunGlyphVector = fullGlyphVectorCache.get(inString.toString());
1622 if (fullRunGlyphVector ==
null) {
1623 iter.initFromCharSequence(inString);
1625 fullGlyphVectorCache.put(inString.toString(), fullRunGlyphVector);
1627 final boolean complex = (fullRunGlyphVector.getLayoutFlags() != 0);
1628 if (complex || DISABLE_GLYPH_CACHE) {
1630 glyphsOutput.add(
new Glyph(inString.toString(),
false));
1631 return glyphsOutput;
1634 final int lengthInGlyphs = fullRunGlyphVector.getNumGlyphs();
1636 while (i < lengthInGlyphs) {
1637 final Character letter = CharacterCache.valueOf(inString.charAt(i));
1638 GlyphMetrics metrics = glyphMetricsCache.get(letter);
1639 if (metrics ==
null) {
1640 metrics = fullRunGlyphVector.getGlyphMetrics(i);
1641 glyphMetricsCache.put(letter, metrics);
1643 final Glyph glyph = getGlyph(inString, metrics, i);
1644 if (glyph !=
null) {
1645 glyphsOutput.add(glyph);
1650 final StringBuilder buf =
new StringBuilder();
1651 while (i < lengthInGlyphs &&
1652 getGlyph(inString, fullRunGlyphVector.getGlyphMetrics(i), i) ==
null) {
1653 buf.append(inString.charAt(i++));
1655 glyphsOutput.add(
new Glyph(buf.toString(),
1657 i < lengthInGlyphs));
1660 return glyphsOutput;
1663 public void clearCacheEntry(
final int unicodeID) {
1664 final int glyphID = unicodes2Glyphs[unicodeID];
1665 if (glyphID != undefined) {
1666 final Glyph glyph = glyphCache[glyphID];
1667 if (glyph !=
null) {
1670 glyphCache[glyphID] =
null;
1672 unicodes2Glyphs[unicodeID] = undefined;
1675 public void clearAllCacheEntries() {
1676 for (
int i = 0; i < unicodes2Glyphs.length; i++) {
1681 public void register(
final Glyph glyph) {
1682 unicodes2Glyphs[glyph.getUnicodeID()] = glyph.getGlyphCode();
1683 glyphCache[glyph.getGlyphCode()] = glyph;
1686 public float getGlyphPixelWidth(
final char unicodeID) {
1687 final Glyph glyph = getGlyph(unicodeID);
1688 if (glyph !=
null) {
1689 return glyph.getAdvance();
1693 singleUnicode[0] = unicodeID;
1694 if(
null == fontRenderContext ) {
1695 throw new InternalError(
"fontRenderContext never initialized!");
1697 final GlyphVector gv = font.createGlyphVector(fontRenderContext,
1699 return gv.getGlyphMetrics(0).getAdvance();
1705 private Glyph getGlyph(
final CharSequence inString,
1706 final GlyphMetrics glyphMetrics,
1708 final char unicodeID = inString.charAt(index);
1710 if (unicodeID >= unicodes2Glyphs.length) {
1714 final int glyphID = unicodes2Glyphs[unicodeID];
1715 if (glyphID != undefined) {
1716 return glyphCache[glyphID];
1720 singleUnicode[0] = unicodeID;
1722 return getGlyph(unicodeID, gv, glyphMetrics);
1728 private Glyph getGlyph(
final int unicodeID) {
1729 if (unicodeID >= unicodes2Glyphs.length) {
1733 final int glyphID = unicodes2Glyphs[unicodeID];
1734 if (glyphID != undefined) {
1735 return glyphCache[glyphID];
1737 singleUnicode[0] = (char) unicodeID;
1739 return getGlyph(unicodeID, gv, gv.getGlyphMetrics(0));
1742 private Glyph getGlyph(
final int unicodeID,
1743 final GlyphVector singleUnicodeGlyphVector,
1744 final GlyphMetrics metrics) {
1745 final int glyphCode = singleUnicodeGlyphVector.getGlyphCode(0);
1747 if (glyphCode >= glyphCache.length) {
1750 final Glyph glyph =
new Glyph(unicodeID,
1752 metrics.getAdvance(),
1753 singleUnicodeGlyphVector,
1760 private static class CharacterCache {
1761 private CharacterCache() {
1764 static final Character cache[] =
new Character[127 + 1];
1767 for (
int i = 0; i < cache.length; i++) {
1768 cache[i] = Character.valueOf((
char) i);
1772 public static Character valueOf(
final char c) {
1774 return CharacterCache.cache[c];
1776 return Character.valueOf(c);
1780 class Pipelined_QuadRenderer {
1781 int mOutstandingGlyphsVerticesPipeline = 0;
1782 FloatBuffer mTexCoords;
1783 FloatBuffer mVertCoords;
1785 int mVBO_For_ResuableTileVertices;
1786 int mVBO_For_ResuableTileTexCoords;
1788 Pipelined_QuadRenderer() {
1790 mVertCoords = Buffers.newDirectFloatBuffer(kTotalBufferSizeCoordsVerts);
1791 mTexCoords = Buffers.newDirectFloatBuffer(kTotalBufferSizeCoordsTex);
1797 final int[] vbos =
new int[2];
1800 mVBO_For_ResuableTileVertices = vbos[0];
1801 mVBO_For_ResuableTileTexCoords = vbos[1];
1804 mVBO_For_ResuableTileVertices);
1809 mVBO_For_ResuableTileTexCoords);
1812 }
catch (
final Exception e) {
1813 isExtensionAvailable_GL_VERSION_1_5 =
false;
1819 public void glTexCoord2f(
final float v,
final float v1) {
1824 public void glVertex3f(
final float inX,
final float inY,
final float inZ) {
1825 mVertCoords.put(inX);
1826 mVertCoords.put(inY);
1827 mVertCoords.put(inZ);
1829 mOutstandingGlyphsVerticesPipeline++;
1831 if (mOutstandingGlyphsVerticesPipeline >= kTotalBufferSizeVerts) {
1836 private void draw() {
1837 if (useVertexArrays) {
1844 private void drawVertexArrays() {
1845 if (mOutstandingGlyphsVerticesPipeline > 0) {
1848 final TextureRenderer renderer = getBackingStore();
1849 renderer.getTexture();
1851 mVertCoords.rewind();
1852 mTexCoords.rewind();
1858 mVBO_For_ResuableTileVertices);
1860 mOutstandingGlyphsVerticesPipeline * kSizeInBytes_OneVertices_VertexData,
1871 mVBO_For_ResuableTileTexCoords);
1873 mOutstandingGlyphsVerticesPipeline * kSizeInBytes_OneVertices_TexData,
1881 mOutstandingGlyphsVerticesPipeline);
1883 mVertCoords.rewind();
1884 mTexCoords.rewind();
1885 mOutstandingGlyphsVerticesPipeline = 0;
1889 private void drawIMMEDIATE() {
1890 if (mOutstandingGlyphsVerticesPipeline > 0) {
1891 final TextureRenderer renderer = getBackingStore();
1892 renderer.getTexture();
1898 final int numberOfQuads = mOutstandingGlyphsVerticesPipeline / 4;
1899 mVertCoords.rewind();
1900 mTexCoords.rewind();
1902 for (
int i = 0; i < numberOfQuads; i++) {
1904 gl.
glVertex3f(mVertCoords.get(), mVertCoords.get(),
1908 gl.
glVertex3f(mVertCoords.get(), mVertCoords.get(),
1912 gl.
glVertex3f(mVertCoords.get(), mVertCoords.get(),
1916 gl.
glVertex3f(mVertCoords.get(), mVertCoords.get(),
1919 }
catch (
final Exception e) {
1920 e.printStackTrace();
1923 mVertCoords.rewind();
1924 mTexCoords.rewind();
1925 mOutstandingGlyphsVerticesPipeline = 0;
1932 final int[] vbos =
new int[2];
1933 vbos[0] = mVBO_For_ResuableTileVertices;
1934 vbos[1] = mVBO_For_ResuableTileTexCoords;
1941 private Frame frame;
1943 DebugListener(
final GL gl,
final Frame frame) {
1953 if (packer ==
null) {
1957 final TextureRenderer rend = getBackingStore();
1958 final int w = rend.getWidth();
1959 final int h = rend.getHeight();
1960 rend.beginOrthoRendering(w, h);
1961 rend.drawOrthoRect(0, 0);
1962 rend.endOrthoRendering();
1964 if ((frame.getWidth() != w) || (frame.getHeight() != h)) {
1965 EventQueue.invokeLater(
new Runnable() {
1968 frame.setSize(w, h);
1976 mPipelinedQuadRenderer.dispose();
1988 public void reshape(
final GLAutoDrawable drawable,
final int x,
final int y,
final int width,
1993 final boolean modeChanged,
final boolean deviceChanged) {
2005 this.useVertexArrays = useVertexArrays;
2014 return useVertexArrays;
2024 this.smoothing = smoothing;
2038 private final boolean is15Available(
final GL gl) {
2039 if (!checkFor_isExtensionAvailable_GL_VERSION_1_5) {
2041 checkFor_isExtensionAvailable_GL_VERSION_1_5 =
true;
2043 return isExtensionAvailable_GL_VERSION_1_5;
Specifies a set of OpenGL capabilities.
Abstraction for an OpenGL rendering context.
abstract GLDrawable getGLDrawable()
Returns the write-drawable this context uses for framebuffer operations.
static GLContext getCurrent()
Returns this thread current context.
static GL getCurrentGL()
Returns the GL object bound to this thread current context.
A generic exception for OpenGL errors used throughout the binding as a substitute for RuntimeExceptio...
Class holding OpenGL extension strings, commonly used by JOGL's implementation.
static final String VERSION_1_5
A heavyweight AWT component which provides OpenGL rendering support.
final void setSharedContext(final GLContext sharedContext)
Specifies an OpenGL context, which shall be shared by this GLAutoDrawable's GLContext.
void addGLEventListener(final GLEventListener listener)
Adds the given listener to the end of this drawable queue.
Provides access to the OpenGL Utility Library (GLU).
static final GLU createGLU()
Instantiates a GLU implementation object in respect to the given GL profile of this thread current GL...
An Animator subclass which attempts to achieve a target frames-per-second rate to avoid using all CPU...
final synchronized boolean start()
Starts this animator, if not running.
final synchronized boolean stop()
Stops this FPSAnimator.
void draw(final Graphics2D graphics, final String str, final int x, final int y)
Render the passed character sequence at the designated location using the supplied Graphics2D instanc...
boolean intensityOnly()
Indicates whether the backing store of this TextRenderer should be intensity-only (the default) or fu...
Rectangle2D getBounds(final GlyphVector gv, final FontRenderContext frc)
Computes the bounds of the given GlyphVector, already assumed to have been created for a particular F...
Rectangle2D getBounds(final String str, final Font font, final FontRenderContext frc)
Computes the bounds of the given String relative to the origin.
Rectangle2D getBounds(final CharSequence str, final Font font, final FontRenderContext frc)
Computes the bounds of the given character sequence relative to the origin.
void drawGlyphVector(final Graphics2D graphics, final GlyphVector str, final int x, final int y)
Render the passed GlyphVector at the designated location using the supplied Graphics2D instance.
Renders bitmapped Java 2D text into an OpenGL window with high performance, full Unicode support,...
TextRenderer(final Font font, final boolean antialiased, final boolean useFractionalMetrics, final RenderDelegate renderDelegate)
Creates a new TextRenderer with the given Font, specified font properties, and given RenderDelegate.
void beginRendering(final int width, final int height, final boolean disableDepthTest)
Begins rendering with this TextRenderer into the current OpenGL drawable, pushing the projection and ...
void beginRendering(final int width, final int height)
Begins rendering with this TextRenderer into the current OpenGL drawable, pushing the projection and ...
void setColor(final float r, final float g, final float b, final float a)
Changes the current color of this TextRenderer to the supplied one, where each component ranges from ...
void endRendering()
Ends a render cycle with this TextRenderer.
void draw(final CharSequence str, final int x, final int y)
Draws the supplied CharSequence at the desired location using the renderer's current color.
void begin3DRendering()
Begins rendering of 2D text in 3D with this TextRenderer into the current OpenGL drawable.
void setColor(final Color color)
Changes the current color of this TextRenderer to the supplied one.
TextRenderer(final Font font)
Creates a new TextRenderer with the given font, using no antialiasing or fractional metrics,...
void draw3D(final CharSequence str, final float x, final float y, final float z, final float scaleFactor)
Draws the supplied CharSequence at the desired 3D location using the renderer's current color.
void setUseVertexArrays(final boolean useVertexArrays)
Sets whether vertex arrays are being used internally for rendering, or whether text is rendered using...
TextRenderer(final Font font, final boolean antialiased, final boolean useFractionalMetrics)
Creates a new TextRenderer with the given Font, specified font properties, and default RenderDelegate...
void end3DRendering()
Ends a 3D render cycle with this TextRenderer.
boolean getSmoothing()
Indicates whether smoothing is enabled in the backing TextureRenderer of this TextRenderer.
float getCharWidth(final char inChar)
Returns the pixel width of the given character.
Rectangle2D getBounds(final CharSequence str)
Returns the bounding rectangle of the given CharSequence, assuming it was rendered at the origin.
FontRenderContext getFontRenderContext()
Returns a FontRenderContext which can be used for external text-related size computations.
void setSmoothing(final boolean smoothing)
Sets whether smoothing (i.e., GL_LINEAR filtering) is enabled in the backing TextureRenderer of this ...
void draw3D(final String str, final float x, final float y, final float z, final float scaleFactor)
Draws the supplied String at the desired 3D location using the renderer's current color.
TextRenderer(final Font font, final boolean antialiased, final boolean useFractionalMetrics, RenderDelegate renderDelegate, final boolean mipmap)
Creates a new TextRenderer with the given Font, specified font properties, and given RenderDelegate.
TextRenderer(final Font font, final boolean mipmap)
Creates a new TextRenderer with the given font, using no antialiasing or fractional metrics,...
void flush()
Causes the TextRenderer to flush any internal caches it may be maintaining and draw its rendering res...
void dispose()
Disposes of all resources this TextRenderer is using.
final boolean getUseVertexArrays()
Indicates whether vertex arrays are being used internally for rendering, or whether text is rendered ...
Font getFont()
Returns the Font this renderer is using.
void draw(final String str, final int x, final int y)
Draws the supplied String at the desired location using the renderer's current color.
Rectangle2D getBounds(final String str)
Returns the bounding rectangle of the given String, assuming it was rendered at the origin.
Provides the ability to render into an OpenGL Texture using the Java 2D APIs.
void dispose()
Disposes all resources associated with this renderer.
void setSmoothing(final boolean smoothing)
Sets whether smoothing is enabled for the OpenGL texture; if so, uses GL_LINEAR interpolation for the...
void end3DRendering()
Convenience method which assists in rendering portions of the OpenGL texture to the screen as 2D quad...
void begin3DRendering()
Convenience method which assists in rendering portions of the OpenGL texture to the screen as 2D quad...
void markDirty(final int x, final int y, final int width, final int height)
Marks the given region of the TextureRenderer as dirty.
void setColor(final float r, final float g, final float b, final float a)
Changes the color of the polygons, and therefore the drawn images, this TextureRenderer produces.
void beginOrthoRendering(final int width, final int height)
Convenience method which assists in rendering portions of the OpenGL texture to the screen,...
void endOrthoRendering()
Convenience method which assists in rendering portions of the OpenGL texture to the screen,...
Represents a rectangular region on the backing store.
Packs rectangles supplied by the user (typically representing image regions) into a larger backing st...
void clear()
Clears all Rects contained in this RectanglePacker.
void dispose()
Disposes the backing store allocated by the BackingStoreManager.
void remove(final Rect rect)
Removes the given rectangle from this RectanglePacker.
void compact()
Forces a compaction cycle, which typically results in allocating a new backing store and copying all ...
void add(final Rect rect)
Decides upon an (x, y) position for the given rectangle (leaving its width and height unchanged) and ...
void setMaxSize(final int maxWidth, final int maxHeight)
Sets up a maximum width and height for the backing store.
float verticalFragmentationRatio()
Returns the vertical fragmentation ratio of this RectanglePacker.
void visit(final RectVisitor visitor)
Visits all Rects contained in this RectanglePacker.
Specifies texture coordinates for a rectangular area of a texture.
Provides low-level information required for hardware-accelerated rendering using a surface in a platf...
Adding mutable surface pixel scale property to implementing class, usually to a NativeSurface impleme...
static final int GL_STREAM_DRAW
GL_VERSION_1_5, GL_ES_VERSION_2_0, GL_ARB_vertex_buffer_object Alias for: GL_STREAM_DRAW_ARB Define ...
static final int GL_QUADS
GL_ES_VERSION_3_2, GL_VERSION_1_1, GL_VERSION_1_0, GL_OES_tessellation_shader, GL_EXT_tessellation_sh...
void glEnableClientState(int cap)
Entry point to C language function: void {@native glEnableClientState}(GLenum cap) Part of GL_NV_v...
void glTexCoord2f(float s, float t)
Entry point to C language function: void {@native glTexCoord2f}(GLfloat s, GLfloat t) Part of GL_V...
void glPopClientAttrib()
Entry point to C language function: void {@native glPopClientAttrib}() Part of GL_VERSION_1_1
void glBegin(int mode)
Entry point to C language function: void {@native glBegin}(GLenum mode) Part of GL_VERSION_1_0
void glVertex3f(float x, float y, float z)
Entry point to C language function: void {@native glVertex3f}(GLfloat x, GLfloat y,...
void glPushClientAttrib(int mask)
Entry point to C language function: void {@native glPushClientAttrib}(GLbitfield mask) Part of GL_...
void glEnd()
Entry point to C language function: void {@native glEnd}() Part of GL_VERSION_1_0
static final long GL_ALL_CLIENT_ATTRIB_BITS
GL_VERSION_1_1 Define "GL_ALL_CLIENT_ATTRIB_BITS" with expression '0xFFFFFFFF', CType: long
A higher-level abstraction than GLDrawable which supplies an event based mechanism (GLEventListener) ...
GLProfile getGLProfile()
Returns the GLProfile associated with this GL object.
boolean isExtensionAvailable(String glExtensionName)
Returns true if the specified OpenGL extension can be used successfully through this GL instance give...
GL2 getGL2()
Casts this object to the GL2 interface.
NativeSurface getNativeSurface()
Returns the associated NativeSurface of this NativeSurfaceHolder.
Declares events which client code can use to manage OpenGL rendering into a GLAutoDrawable.
void glGenBuffers(int n, IntBuffer buffers)
Entry point to C language function: void {@native glGenBuffers}(GLsizei n, GLuint * buffers) Part ...
void glGetIntegerv(int pname, IntBuffer data)
Entry point to C language function: void {@native glGetIntegerv}(GLenum pname, GLint * data) Part ...
void glDrawArrays(int mode, int first, int count)
Entry point to C language function: void {@native glDrawArrays}(GLenum mode, GLint first,...
static final int GL_FLOAT
GL_ES_VERSION_2_0, GL_VERSION_1_1, GL_VERSION_1_0, GL_VERSION_ES_1_0 Define "GL_FLOAT" with expressio...
void glBufferSubData(int target, long offset, long size, Buffer data)
Entry point to C language function: void {@native glBufferSubData}(GLenum target,...
static final int GL_COLOR_BUFFER_BIT
GL_ES_VERSION_2_0, GL_VERSION_1_1, GL_VERSION_1_0, GL_VERSION_ES_1_0 Define "GL_COLOR_BUFFER_BIT" wit...
void glClear(int mask)
Entry point to C language function: void {@native glClear}(GLbitfield mask) Part of GL_ES_VERSION_...
static final int GL_MAX_TEXTURE_SIZE
GL_ES_VERSION_2_0, GL_VERSION_1_1, GL_VERSION_1_0, GL_VERSION_ES_1_0 Define "GL_MAX_TEXTURE_SIZE" wit...
static final int GL_DEPTH_BUFFER_BIT
GL_ES_VERSION_2_0, GL_VERSION_1_1, GL_VERSION_1_0, GL_VERSION_ES_1_0 Define "GL_DEPTH_BUFFER_BIT" wit...
void glDeleteBuffers(int n, IntBuffer buffers)
Entry point to C language function: void {@native glDeleteBuffers}(GLsizei n, const GLuint * buffers...
void glBindBuffer(int target, int buffer)
Entry point to C language function: void {@native glBindBuffer}(GLenum target, GLuint buffer) Part...
void glBufferData(int target, long size, Buffer data, int usage)
Entry point to C language function: void {@native glBufferData}(GLenum target, GLsizeiptr size,...
static final int GL_ARRAY_BUFFER
GL_VERSION_1_5, GL_ES_VERSION_2_0, GL_VERSION_ES_1_0, GL_ARB_vertex_buffer_object Alias for: GL_ARRAY...
static final int GL_TEXTURE_COORD_ARRAY
void glTexCoordPointer(GLArrayData array)
static final int GL_VERTEX_ARRAY
void glVertexPointer(GLArrayData array)
Class supporting more full control over the process of rendering the bitmapped text.
void draw(Graphics2D graphics, String str, int x, int y)
Render the passed character sequence at the designated location using the supplied Graphics2D instanc...
void drawGlyphVector(Graphics2D graphics, GlyphVector str, int x, int y)
Render the passed GlyphVector at the designated location using the supplied Graphics2D instance.
Rectangle2D getBounds(CharSequence str, Font font, FontRenderContext frc)
Computes the bounds of the given character sequence relative to the origin.
Rectangle2D getBounds(String str, Font font, FontRenderContext frc)
Computes the bounds of the given String relative to the origin.
Rectangle2D getBounds(GlyphVector gv, FontRenderContext frc)
Computes the bounds of the given GlyphVector, already assumed to have been created for a particular F...
boolean intensityOnly()
Indicates whether the backing store of this TextRenderer should be intensity-only (the default) or fu...
This interface must be implemented by the end user and is called in response to events like addition ...
Iteration construct without exposing the internals of the RectanglePacker and without implementing a ...