Fix bug 2540
[scilab.git] / scilab / modules / renderer / src / java / org / scilab / modules / renderer / utils / textRendering / SciTextRenderer.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2007 - INRIA - Jean-Baptiste Silvy
4  * desc : TextRender to use with Scilab. Provides text rendering without aliasing
5  * 
6  * This file must be used under the terms of the CeCILL.
7  * This source file is licensed as described in the file COPYING, which
8  * you should have received as part of this distribution.  The terms
9  * are also available at    
10  * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
11  *
12  */
13
14 package org.scilab.modules.renderer.utils.textRendering;
15
16 import java.awt.Font;
17 import java.awt.geom.Rectangle2D;
18
19 import javax.media.opengl.GL;
20
21 import java.lang.reflect.Field;
22 import com.sun.opengl.util.j2d.TextRenderer;
23
24 /**
25  * Scilab adapted JOGL TextRenderer. Provides text rendering without aliasing.
26  * @author Jean-Baptiste Silvy
27  */
28 public class SciTextRenderer {
29
30         private static final float EPSILON = 1.0e-4f; 
31
32         private static boolean areMipmapsAvailable;
33         
34         private static boolean areMMsAvailableUpToDate;
35         
36         /** Size of the font to use */
37         private float fontSize;
38         
39         /** Actual object used for text rendering */
40         private TextRenderer renderer;
41         
42         /** font size of the renderer object */
43         private float scaleFactor;
44         
45         private boolean useFractionalMetrics;
46         
47         /**
48          * Constructor from a Font to use.
49          * @param fontSize font Size of the font
50          * @param renderer mapped text renderer
51          */
52         public SciTextRenderer(TextRenderer renderer, float fontSize) {
53                 this.fontSize = fontSize;
54                 this.renderer = renderer;
55                 setUseFractionalMetrics(true);
56                 updateScaleFactor();
57         }
58         
59         /**
60          * Update the scale factor to use
61          */
62         private void updateScaleFactor() {
63                 this.scaleFactor = fontSize / renderer.getFont().getSize2D();
64         }
65         
66         
67         
68         /**
69          * Display a string at the desired 3D location.
70          * (x,y,z) is the baseline of the leftmost character.
71          * @param gl current gl pipeline
72          * @param str string to draw
73          * @param x X coordinate of the text
74          * @param y Y coordinate of the text
75          * @param z Z coordinate of the text
76          * @param angle angle of the text to draw
77          */
78         public void draw3D(GL gl, String str, double x, double y, double z, double angle) {
79                 // with OpenGL strings, angle is already set
80                 if (useFractionalMetrics) {
81                         renderer.draw3D(str, (float) x, (float) y, (float) z, scaleFactor);
82                 } else {
83                         // we need to add a little offset othrwise texture interpolation
84                         // sometimes (especially for title) leads to jaggy text.
85                         renderer.draw3D(str, (float) x, (float) y, (float) z, 1.0f + EPSILON);
86                 }
87         }
88         
89         /**
90          * Specify wether the font size must stuck with Scilab font Size (integer from 0 to 6) or
91          * if intermediate sizes can be used.
92          * @param useFractionalMetrics if ture useFractionnal metrics
93          */
94         public void setUseFractionalMetrics(boolean useFractionalMetrics) {
95                 this.useFractionalMetrics = useFractionalMetrics;
96                 if (useFractionalMetrics) {
97                         renderer.setSmoothing(true);
98                 } else {
99                         renderer.setSmoothing(false);
100                 }
101                 updateScaleFactor();
102         }
103         
104         /**
105          * Begin a rendering process
106          * @param gl OpenGL pipeline
107          */
108         public void begin3DRendering(GL gl) {
109                 
110                 renderer.begin3DRendering();
111                 
112                 // HACK HACK HACK for Intel drivers
113                 // When text is rendered using normal texture mapping (no mipmap)
114                 // the result is sometime totally fuzzy or the text is simply not displayed.
115                 // Apparently setting mipmap use solves the problem on this cards.
116                 // Normally it should not break display on other GC, so let use it.
117                 fuzzyTextHack(gl);
118                 // END OF HACK
119         }
120         
121         /**
122          * Terminate a drawing sequence
123          * @param gl OpenGL pipeline
124          */
125         public void end3DRendering(GL gl) {
126                 renderer.end3DRendering();
127         }
128         
129         /**
130          * @return font used by the renderer.
131          */
132         public Font getFont() {
133                 // renderer font is from the same family but does not have the same size.
134                 return renderer.getFont().deriveFont(fontSize);
135         }
136         
137         /**
138          * @return real size of text produced by the renderer
139          */
140         public double getDisplayedFontSize() {
141                 if (useFractionalMetrics) {
142                         return fontSize;
143                 } else {
144                         return renderer.getFont().getSize2D();
145                 }
146         }
147         
148         /**
149          * modify the font size of the renderer
150          * To change family it is needed to create a new SciTextRenderer instance
151          * @param newFontSize font size to use
152          */
153         public void setFontSize(float newFontSize) {
154                 this.fontSize = newFontSize;
155                 updateScaleFactor();
156         }
157         
158         /**
159          * Redefine setColor with only three channels
160          * @param red red channel
161          * @param green green channel
162          * @param blue blue channel
163          */
164         public void setColor(double red, double green, double blue) {
165                 renderer.setColor((float) red, (float) green, (float) blue, 1.0f);
166         }
167         
168         /**
169          * Redefine setColor with only three channels
170          * @param color array of size 3 containing the channels
171          */
172         public void setColor(double[] color) {
173                 renderer.setColor((float) color[0], (float) color[1], (float) color[2], 1.0f);
174         }
175         
176         /**
177          * Get the bounding box of the t
178          * @param str String of which we want the bounding box
179          * @return rectangle with the position, width and height.
180          */
181         public Rectangle2D getBounds(String str) {
182                 Rectangle2D res = renderer.getBounds(str);
183                 
184                 // apply scale factor to the bounds
185                 res.setRect(res.getX(), res.getY(),
186                                         res.getWidth() * scaleFactor,
187                                         res.getHeight() * scaleFactor);
188                 return res;
189         }
190         
191         /**
192          * @return true if mipmaps are enabled for the text renderer.
193          */
194         private boolean getMipmapAvailability() {
195                 // The result of the function is actually the mipmap field
196                 // of the text renderer.
197                 // The result is the same for each renderers
198                 // So we just need to compute it once
199                 
200                 // The problem here is that the mipmap field is private
201                 // This following lines retrieve the field even if it is private.
202                 // This technique is inspired from the TextRenderHack:
203                 // http://www.javagaming.org/index.php/topic,19239.0.html
204                 if (!areMMsAvailableUpToDate) {
205                         // search for the mipmap field inside the renderer object
206                         Field mipmapField = null;
207                         try {
208                                 mipmapField = renderer.getClass().getDeclaredField("mipmap");
209                         } catch (SecurityException e) {
210                                 e.printStackTrace();
211                         } catch (NoSuchFieldException e) {
212                                 e.printStackTrace();
213                         }
214                         
215                         // the field is private, we need to enable it
216                         mipmapField.setAccessible(true);
217                         
218                         // get the field value
219                         try {
220                                 areMipmapsAvailable = mipmapField.getBoolean(renderer);
221                         } catch (IllegalArgumentException e) {
222                                 e.printStackTrace();
223                         } catch (IllegalAccessException e) {
224                                 e.printStackTrace();
225                         }
226                         areMMsAvailableUpToDate = true;
227                 }
228                 return areMipmapsAvailable;
229         }
230         
231         /**
232          * HACK function for buggy drivers on Intel graphics.
233          * Always try to enable mipmaps since they seems less buggy than normal mode
234          * @param gl current GL pipeline
235          */
236         private void fuzzyTextHack(GL gl) {
237                 // try to use mimaps if possible
238                 if (!useFractionalMetrics && getMipmapAvailability()) {
239                         gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST_MIPMAP_NEAREST);
240                 }
241         }
242         
243
244 }