All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jogamp.graph.curve.opengl.TextRegionUtil Maven / Gradle / Ivy

/**
 * 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 com.jogamp.opengl.GL2ES2;
import com.jogamp.opengl.GLException;

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.Vertex;
import com.jogamp.graph.geom.Vertex.Factory;

/**
 * Text {@link GLRegion} Utility Class
 */
public class TextRegionUtil {

    public final int renderModes;

    public TextRegionUtil(final int renderModes) {
        this.renderModes = renderModes;
    }

    public static interface ShapeVisitor {
        /**
         * Visiting the given {@link OutlineShape} with it's corresponding {@link AffineTransform}.
         * @param shape may be used as is, otherwise a copy shall be made if intended to be modified.
         * @param t may be used immediately as is, otherwise a copy shall be made if stored.
         */
        public void visit(final OutlineShape shape, final AffineTransform t);
    }

    public static int getCharCount(final String s, final char c) {
        final int sz = s.length();
        int count = 0;
        for(int i=0; i vertexFactory,
                                         final Font font, final float pixelSize, final CharSequence str, final float[] rgbaColor,
                                         final AffineTransform temp1, final AffineTransform temp2) {
        final ShapeVisitor visitor = new ShapeVisitor() {
            public final void visit(final OutlineShape shape, final AffineTransform t) {
                region.addOutlineShape(shape, t, region.hasColorChannel() ? rgbaColor : null);
            } };
        processString(visitor, null, font, pixelSize, str, temp1, temp2);
    }

    /**
     * Render the string in 3D space w.r.t. the font and pixelSize
     * using a cached {@link GLRegion} for reuse.
     * 

* Cached {@link GLRegion}s will be destroyed w/ {@link #clear(GL2ES2)} or to free memory. *

* @param gl the current GL state * @param renderer TODO * @param font {@link Font} to be used * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size. * @param str text to be rendered * @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored. * @param sampleCount desired multisampling sample count for msaa-rendering. * The actual used scample-count is written back when msaa-rendering is enabled, otherwise the store is untouched. * @throws Exception if TextRenderer not initialized */ public void drawString3D(final GL2ES2 gl, final RegionRenderer renderer, final Font font, final float pixelSize, final CharSequence str, final float[] rgbaColor, final int[/*1*/] sampleCount) { if( !renderer.isInitialized() ) { throw new GLException("TextRendererImpl01: not initialized!"); } final int special = 0; GLRegion region = getCachedRegion(font, str, pixelSize, special); if(null == region) { region = GLRegion.create(renderModes, null); addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, pixelSize, str, rgbaColor, tempT1, tempT2); addCachedRegion(gl, font, str, pixelSize, special, region); } region.draw(gl, renderer, sampleCount); } /** * Render the string in 3D space w.r.t. the font and pixelSize * using a temporary {@link GLRegion}, which will be destroyed afterwards. *

* In case of a multisampling region renderer, i.e. {@link Region#VBAA_RENDERING_BIT}, recreating the {@link GLRegion} * is a huge performance impact. * In such case better use {@link #drawString3D(GL2ES2, GLRegion, RegionRenderer, Font, float, CharSequence, float[], int[], AffineTransform, AffineTransform)} * instead. *

* @param gl the current GL state * @param renderModes TODO * @param font {@link Font} to be used * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size. * @param str text to be rendered * @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored. * @param sampleCount desired multisampling sample count for msaa-rendering. * The actual used scample-count is written back when msaa-rendering is enabled, otherwise the store is untouched. * @param temp1 temporary AffineTransform storage, mandatory * @param temp2 temporary AffineTransform storage, mandatory * @throws Exception if TextRenderer not initialized */ public static void drawString3D(final GL2ES2 gl, final int renderModes, final RegionRenderer renderer, final Font font, final float pixelSize, final CharSequence str, final float[] rgbaColor, final int[/*1*/] sampleCount, final AffineTransform temp1, final AffineTransform temp2) { if(!renderer.isInitialized()){ throw new GLException("TextRendererImpl01: not initialized!"); } final GLRegion region = GLRegion.create(renderModes, null); addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, pixelSize, str, rgbaColor, temp1, temp2); region.draw(gl, renderer, sampleCount); region.destroy(gl); } /** * Render the string in 3D space w.r.t. the font and pixelSize * using the given {@link GLRegion}, which will {@link GLRegion#clear(GL2ES2) cleared} beforehand. * @param gl the current GL state * @param font {@link Font} to be used * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size. * @param str text to be rendered * @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored. * @param sampleCount desired multisampling sample count for msaa-rendering. * The actual used scample-count is written back when msaa-rendering is enabled, otherwise the store is untouched. * @param temp1 temporary AffineTransform storage, mandatory * @param temp2 temporary AffineTransform storage, mandatory * @throws Exception if TextRenderer not initialized */ public static void drawString3D(final GL2ES2 gl, final GLRegion region, final RegionRenderer renderer, final Font font, final float pixelSize, final CharSequence str, final float[] rgbaColor, final int[/*1*/] sampleCount, final AffineTransform temp1, final AffineTransform temp2) { if(!renderer.isInitialized()){ throw new GLException("TextRendererImpl01: not initialized!"); } region.clear(gl); addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, pixelSize, str, rgbaColor, temp1, temp2); region.draw(gl, renderer, sampleCount); } /** * Clear all cached {@link GLRegions}. */ public void clear(final GL2ES2 gl) { // fluchCache(gl) already called final Iterator iterator = stringCacheMap.values().iterator(); while(iterator.hasNext()){ final GLRegion region = iterator.next(); region.destroy(gl); } stringCacheMap.clear(); stringCacheArray.clear(); } /** *

Sets 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(final 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(final GL2ES2 gl, final 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(final GL2ES2 gl, final int space) { if ( getCacheLimit() > 0 ) { while ( getCacheSize() + space > getCacheLimit() ) { removeCachedRegion(gl, 0); } } } protected final GLRegion getCachedRegion(final Font font, final CharSequence str, final float pixelSize, final int special) { return stringCacheMap.get(getKey(font, str, pixelSize, special)); } protected final void addCachedRegion(final GL2ES2 gl, final Font font, final CharSequence str, final float pixelSize, final int special, final GLRegion glyphString) { if ( 0 != getCacheLimit() ) { final String key = getKey(font, str, pixelSize, special); 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(final GL2ES2 gl, final Font font, final CharSequence str, final int pixelSize, final int special) { final String key = getKey(font, str, pixelSize, special); final GLRegion region = stringCacheMap.remove(key); if(null != region) { region.destroy(gl); } stringCacheArray.remove(key); } protected final void removeCachedRegion(final GL2ES2 gl, final int idx) { final String key = stringCacheArray.remove(idx); if( null != key ) { final GLRegion region = stringCacheMap.remove(key); if(null != region) { region.destroy(gl); } } } protected final String getKey(final Font font, final CharSequence str, final float pixelSize, final int special) { final StringBuilder sb = new StringBuilder(); return font.getName(sb, Font.NAME_UNIQUNAME) .append(".").append(str.hashCode()).append(".").append(Float.floatToIntBits(pixelSize)).append(special).toString(); } /** Default cache limit, see {@link #setCacheLimit(int)} */ public static final int DEFAULT_CACHE_LIMIT = 256; public final AffineTransform tempT1 = new AffineTransform(); public final AffineTransform tempT2 = new AffineTransform(); private final HashMap stringCacheMap = new HashMap(DEFAULT_CACHE_LIMIT); private final ArrayList stringCacheArray = new ArrayList(DEFAULT_CACHE_LIMIT); private int stringCacheLimit = DEFAULT_CACHE_LIMIT; }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy