/** * Copyright 2014 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ package com.jogamp.graph.curve.opengl; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import javax.media.opengl.GL2ES2; import javax.media.opengl.GLException; import jogamp.graph.curve.opengl.RegionFactory; import jogamp.graph.geom.plane.AffineTransform; import com.jogamp.graph.curve.OutlineShape; import com.jogamp.graph.curve.Region; import com.jogamp.graph.font.Font; import com.jogamp.graph.font.Font.Glyph; import com.jogamp.graph.geom.Triangle; import com.jogamp.graph.geom.Vertex; import com.jogamp.graph.geom.Vertex.Factory; /** * * FIXME: Add VBO Vertex Factory for drawString3D ! * */ public class TextRenderUtil { public final Renderer renderer; public TextRenderUtil(final Renderer renderer) { this.renderer = renderer; } /** * Generate a Region to represent this Object. *
* Each glyph is cached and reused. *
* * @param renderModes bit-field of modes, e.g. {@link Region#VARIABLE_CURVE_WEIGHT_BIT}, {@link Region#VBAA_RENDERING_BIT} * @param vertexFactory vertex impl factory {@link Factory} * @param font the target {@link Font} * @param str string text * @param pixelSize */ public static GLRegion createRegion(final int renderModes, final Factory extends Vertex> vertexFactory, final Font font, final CharSequence str, final int pixelSize) { final int charCount = str.length(); final GLRegion region = RegionFactory.create(renderModes); // region.setFlipped(true); final Font.Metrics metrics = font.getMetrics(); final float lineGap = metrics.getLineGap(pixelSize) ; final float ascent = metrics.getAscent(pixelSize) ; final float descent = metrics.getDescent(pixelSize) ; final float advanceY = lineGap - descent + ascent; final float scale = metrics.getScale(pixelSize); final AffineTransform transform = new AffineTransform(vertexFactory); final AffineTransform t = new AffineTransform(vertexFactory); float y = 0; float advanceTotal = 0; int numVertices = region.getNumVertices(); for(int i=0; i< charCount; i++) { final char character = str.charAt(i); if( '\n' == character ) { y += advanceY; advanceTotal = 0; } else if (character == ' ') { advanceTotal += font.getAdvanceWidth(Glyph.ID_SPACE, pixelSize); } else { if(Region.DEBUG_INSTANCE) { System.err.println("XXXXXXXXXXXXXXx char: "+character+", scale: "+scale+"; translate: "+advanceTotal+", "+y); } t.setTransform(transform); // reset transform t.translate(advanceTotal, y); t.scale(scale, scale); final Font.Glyph glyph = font.getGlyph(character); final OutlineShape glyphShape = glyph.getShape(); if( null == glyphShape || glyphShape.getVertices().size() < 3 ) { continue; } // glyphShape.closeLastOutline(); if( false ) { region.addOutlineShape(glyphShape, t); } else { final ArrayListSets the cache limit for reusing GlyphString's and their Region. * Default is {@link #DEFAULT_CACHE_LIMIT}, -1 unlimited, 0 turns cache off, >0 limited
* *The cache will be validate when the next string rendering happens.
* * @param newLimit new cache size * * @see #DEFAULT_CACHE_LIMIT */ public final void setCacheLimit(int newLimit ) { stringCacheLimit = newLimit; } /** * Sets the cache limit, see {@link #setCacheLimit(int)} and validates the cache. * * @see #setCacheLimit(int) * * @param gl current GL used to remove cached objects if required * @param newLimit new cache size */ public final void setCacheLimit(GL2ES2 gl, int newLimit ) { stringCacheLimit = newLimit; validateCache(gl, 0); } /** * @return the current cache limit */ public final int getCacheLimit() { return stringCacheLimit; } /** * @return the current utilized cache size, <= {@link #getCacheLimit()} */ public final int getCacheSize() { return stringCacheArray.size(); } protected final void validateCache(GL2ES2 gl, int space) { if ( getCacheLimit() > 0 ) { while ( getCacheSize() + space > getCacheLimit() ) { removeCachedRegion(gl, 0); } } } protected final GLRegion getCachedRegion(Font font, CharSequence str, int fontSize) { return stringCacheMap.get(getKey(font, str, fontSize)); } protected final void addCachedRegion(GL2ES2 gl, Font font, CharSequence str, int fontSize, GLRegion glyphString) { if ( 0 != getCacheLimit() ) { final String key = getKey(font, str, fontSize); final GLRegion oldRegion = stringCacheMap.put(key, glyphString); if ( null == oldRegion ) { // new entry .. validateCache(gl, 1); stringCacheArray.add(stringCacheArray.size(), key); } /// else overwrite is nop .. } } protected final void removeCachedRegion(GL2ES2 gl, Font font, CharSequence str, int fontSize) { final String key = getKey(font, str, fontSize); GLRegion region = stringCacheMap.remove(key); if(null != region) { region.destroy(gl, renderer.getRenderState()); } stringCacheArray.remove(key); } protected final void removeCachedRegion(GL2ES2 gl, int idx) { final String key = stringCacheArray.remove(idx); final GLRegion region = stringCacheMap.remove(key); if(null != region) { region.destroy(gl, renderer.getRenderState()); } } protected final String getKey(Font font, CharSequence str, int fontSize) { final StringBuilder sb = new StringBuilder(); return font.getName(sb, Font.NAME_UNIQUNAME) .append(".").append(str.hashCode()).append(".").append(fontSize).toString(); } /** Default cache limit, see {@link #setCacheLimit(int)} */ public static final int DEFAULT_CACHE_LIMIT = 256; private final HashMap