/* JLaTeXMathCache.java * ========================================================================= * This file is part of the JLaTeXMath Library - http://forge.scilab.org/p/jlatexmath * * Copyright (C) 2010 DENIZET Calixte * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * A copy of the GNU General Public License can be found in the file * LICENSE.txt provided with the source distribution of this program (see * the META-INF directory in the source jar). This license can also be * found on the GNU website at http://www.gnu.org/licenses/gpl.html. * * If you did not receive a copy of the GNU General Public License along * with this program, contact the lead developer, or write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Linking this library statically or dynamically with other modules * is making a combined work based on this library. Thus, the terms * and conditions of the GNU General Public License cover the whole * combination. * * As a special exception, the copyright holders of this library give you * permission to link this library with independent modules to produce * an executable, regardless of the license terms of these independent * modules, and to copy and distribute the resulting executable under terms * of your choice, provided that you also meet, for each linked independent * module, the terms and conditions of the license of that module. * An independent module is a module which is not derived from or based * on this library. If you modify this library, you may extend this exception * to your version of the library, but you are not obliged to do so. * If you do not wish to do so, delete this exception statement from your * version. * */ package com.himamis.retex.renderer.share.cache; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import com.himamis.retex.renderer.share.TeXFormula; import com.himamis.retex.renderer.share.TeXIcon; import com.himamis.retex.renderer.share.exception.ParseException; import com.himamis.retex.renderer.share.platform.Graphics; import com.himamis.retex.renderer.share.platform.graphics.Color; import com.himamis.retex.renderer.share.platform.graphics.Graphics2DInterface; import com.himamis.retex.renderer.share.platform.graphics.Image; import com.himamis.retex.renderer.share.platform.graphics.Insets; import com.himamis.retex.renderer.share.platform.graphics.Transform; /** * Class to cache generated image from formulas * * @author Calixte DENIZET */ public final class JLaTeXMathCache { private static final Transform identity = new Graphics().createTransform(); private static ConcurrentMap<CachedTeXFormula, SoftReference<CachedImage>> cache = new ConcurrentHashMap<CachedTeXFormula, SoftReference<CachedImage>>( 128); private static int max = Integer.MAX_VALUE; private static ReferenceQueue queue = new ReferenceQueue(); private JLaTeXMathCache() { } /** * Set max size. Take care the cache will be reinitialized * * @param max the max size */ public static void setMaxCachedObjects(int max) { JLaTeXMathCache.max = Math.max(max, 1); cache.clear(); cache = new ConcurrentHashMap<CachedTeXFormula, SoftReference<CachedImage>>(JLaTeXMathCache.max); } /** * @param f a formula * @param style a style like TeXConstants.STYLE_DISPLAY * @param size the size of font * @param inset the inset to add on the top, bottom, left and right * @return an array of length 3 containing width, height and depth */ public static int[] getCachedTeXFormulaDimensions(String f, int style, int type, int size, int inset, Color fgcolor) throws ParseException { return getCachedTeXFormulaDimensions(new CachedTeXFormula(f, style, type, size, inset, fgcolor)); } public static int[] getCachedTeXFormulaDimensions(String f, int style, int size, int inset) throws ParseException { return getCachedTeXFormulaDimensions(f, style, 0, size, inset, null); } /** * @param o an Object to identify the image in the cache * @return an array of length 3 containing width, height and depth */ public static int[] getCachedTeXFormulaDimensions(Object o) throws ParseException { if (o == null || !(o instanceof CachedTeXFormula)) { return new int[] { 0, 0, 0 }; } CachedTeXFormula cached = (CachedTeXFormula) o; SoftReference<CachedImage> img = cache.get(cached); if (img == null || img.get() == null) { img = makeImage(cached); } return new int[] { cached.width, cached.height, cached.depth }; } /** * Get a cached formula * * @param f a formula * @param style a style like TeXConstants.STYLE_DISPLAY * @param size the size of font * @param inset the inset to add on the top, bottom, left and right * @return the key in the map */ public static Object getCachedTeXFormula(String f, int style, int type, int size, int inset, Color fgcolor) throws ParseException { CachedTeXFormula cached = new CachedTeXFormula(f, style, type, size, inset, fgcolor); SoftReference<CachedImage> img = cache.get(cached); if (img == null || img.get() == null) { img = makeImage(cached); } return cached; } public static Object getCachedTeXFormula(String f, int style, int size, int inset) throws ParseException { return getCachedTeXFormula(f, style, 0, size, inset, null); } /** * Clear the cache */ public static void clearCache() { cache.clear(); } /** * Remove a formula from the cache * * @param f a formula * @param style a style like TeXConstants.STYLE_DISPLAY * @param size the size of font * @param inset the inset to add on the top, bottom, left and right */ public static void removeCachedTeXFormula(String f, int style, int type, int size, int inset, Color fgcolor) throws ParseException { cache.remove(new CachedTeXFormula(f, style, type, size, inset, fgcolor)); } public static void removeCachedTeXFormula(String f, int style, int size, int inset) throws ParseException { removeCachedTeXFormula(f, style, 0, size, inset, null); } /** * Remove a formula from the cache. Take care, remove the Object o, invalidate it ! * * @param o an Object to identify the image in the cache */ public static void removeCachedTeXFormula(Object o) throws ParseException { if (o != null && o instanceof CachedTeXFormula) { cache.remove(o); } } /** * Paint a cached formula * * @param f a formula * @param style a style like TeXConstants.STYLE_DISPLAY * @param size the size of font * @param inset the inset to add on the top, bottom, left and right * @return the key in the map */ // public static Object paintCachedTeXFormula(String f, int style, int type, // int size, int inset, // Color fgcolor, Graphics2DInterface g) throws ParseException { // return paintCachedTeXFormula(new CachedTeXFormula(f, style, type, size, // inset, fgcolor), g); // } // public static Object paintCachedTeXFormula(String f, int style, int size, // int inset, Graphics2DInterface g) // throws ParseException { // return paintCachedTeXFormula(f, style, 0, size, inset, null, g); // } /** * Paint a cached formula * * @param o an Object to identify the image in the cache * @param g the graphics where to paint the image * @return the key in the map */ // public static Object paintCachedTeXFormula(Object o, Graphics2DInterface // g) throws ParseException { // if (o == null || !(o instanceof CachedTeXFormula)) { // return null; // } // CachedTeXFormula cached = (CachedTeXFormula) o; // SoftReference<CachedImage> img = cache.get(cached); // if (img == null || img.get() == null) { // img = makeImage(cached); // } // g.drawImage(img.get().image, identity); // // return cached; // } /** * Get a cached formula * * @param f a formula * @param style a style like TeXConstants.STYLE_DISPLAY * @param size the size of font * @param inset the inset to add on the top, bottom, left and right * @return the cached image */ public static Image getCachedTeXFormulaImage(String f, int style, int type, int size, int inset, Color fgcolor) throws ParseException { return getCachedTeXFormulaImage(new CachedTeXFormula(f, style, type, size, inset, fgcolor)); } public static Image getCachedTeXFormulaImage(String f, int style, int size, int inset) throws ParseException { return getCachedTeXFormulaImage(f, style, 0, size, inset, null); } /** * Get a cached formula * * @param o an Object to identify the image in the cache * @return the cached image */ public static Image getCachedTeXFormulaImage(Object o) throws ParseException { if (o == null || !(o instanceof CachedTeXFormula)) { return null; } CachedTeXFormula cached = (CachedTeXFormula) o; SoftReference<CachedImage> img = cache.get(cached); if (img == null || img.get() == null) { img = makeImage(cached); } return img.get().image; } private static SoftReference<CachedImage> makeImage(CachedTeXFormula cached) throws ParseException { TeXFormula formula = new TeXFormula(cached.f); TeXIcon icon = formula.createTeXIcon(cached.style, cached.size, cached.type, cached.fgcolor); icon.setInsets(new Insets(cached.inset, cached.inset, cached.inset, cached.inset)); Image image = new Graphics().createImage(icon.getIconWidth(), icon.getIconHeight(), Image.TYPE_INT_ARGB); Graphics2DInterface g2 = image.createGraphics2D(); icon.paintIcon(null, g2, 0, 0); g2.dispose(); cached.setDimensions(icon.getIconWidth(), icon.getIconHeight(), icon.getIconDepth()); SoftReference<CachedImage> img = new SoftReference<CachedImage>(new CachedImage(image, cached), queue); if (cache.size() >= max) { Reference soft; while ((soft = queue.poll()) != null) { CachedImage ci = (CachedImage) soft.get(); if (ci != null) { cache.remove(ci.cachedTf); } } Iterator<CachedTeXFormula> iter = cache.keySet().iterator(); if (iter.hasNext()) { CachedTeXFormula c = iter.next(); SoftReference<CachedImage> cachedImage = cache.get(c); if (cachedImage != null) { cachedImage.clear(); } cache.remove(c); } } cache.put(cached, img); return img; } private static class CachedImage { Image image; CachedTeXFormula cachedTf; CachedImage(Image image, CachedTeXFormula cachedTf) { this.image = image; this.cachedTf = cachedTf; } } private static class CachedTeXFormula { String f; int style; int type; int size; int inset; int width = -1; int height; int depth; Color fgcolor; CachedTeXFormula(String f, int style, int type, int size, int inset, Color fgcolor) { this.f = f; this.style = style; this.type = type; this.size = size; this.inset = inset; this.fgcolor = fgcolor; } void setDimensions(int width, int height, int depth) { this.width = width; this.height = height; this.depth = depth; } /** * {@inheritDoc} */ @Override public boolean equals(Object o) { if (o != null && o instanceof CachedTeXFormula) { CachedTeXFormula c = (CachedTeXFormula) o; boolean b = (c.f.equals(f) && c.style == style && c.type == type && c.size == size && c.inset == inset && c.fgcolor.equals(fgcolor)); if (b) { if (c.width == -1) { c.width = width; c.height = height; c.depth = depth; } else if (width == -1) { width = c.width; height = c.height; depth = c.depth; } } return b; } return false; } /** * {@inheritDoc} */ @Override public int hashCode() { return f.hashCode(); } } }