001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.harmony.awt.gl.opengl;
019:
020: import java.awt.Color;
021: import java.awt.Font;
022: import java.awt.Graphics2D;
023: import java.awt.Paint;
024: import java.awt.Point;
025: import java.awt.Rectangle;
026: import java.awt.RenderingHints;
027: import java.awt.font.GlyphMetrics;
028: import java.awt.font.GlyphVector;
029: import java.awt.geom.AffineTransform;
030: import java.awt.geom.NoninvertibleTransformException;
031: import java.awt.image.BufferedImage;
032: import java.awt.image.DataBuffer;
033: import java.awt.image.DataBufferByte;
034: import java.awt.image.IndexColorModel;
035: import java.awt.image.Raster;
036: import java.awt.image.WritableRaster;
037: import java.lang.ref.SoftReference;
038: import java.util.HashSet;
039: import java.util.Hashtable;
040: import java.util.Iterator;
041: import java.util.Vector;
042:
043: import org.apache.harmony.awt.gl.Surface;
044: import org.apache.harmony.awt.gl.TextRenderer;
045: import org.apache.harmony.awt.gl.font.CommonGlyphVector;
046: import org.apache.harmony.awt.gl.font.FontPeerImpl;
047: import org.apache.harmony.awt.gl.font.Glyph;
048:
049: import org.apache.harmony.misc.HashCode;
050:
051: import org.apache.harmony.awt.internal.nls.Messages;
052:
053: public class OGLTextRenderer extends TextRenderer {
054:
055: private static final TextureCache tx = TextureCache.getInstance();
056: private static final GL gl = GL.getInstance();
057:
058: private static final HashSet ESCAPE = new HashSet<Character>();
059: static {
060: ESCAPE.add(new Character('\n'));
061: ESCAPE.add(new Character('\r'));
062: ESCAPE.add(new Character('\t'));
063: }
064:
065: private static final Hashtable intHash2glyphHash = new SoftHashtable();
066:
067: private static final Vector toDel = new Vector<Integer>();
068:
069: private static final Color INVISIBLE_COLOR = new Color(0, 0, 0,
070: (float) 0);
071:
072: public static final BufferedImage getBufImg(byte[] pixels,
073: int width, int height, Color color) {
074: if (pixels == null) {
075: return new BufferedImage(1, 1,
076: BufferedImage.TYPE_BYTE_BINARY);
077: }
078:
079: WritableRaster wr = Raster.createPackedRaster(
080: new DataBufferByte(pixels, pixels.length),
081: (pixels.length / height) << 3, height, 1, null);
082:
083: int[] masColors = new int[] { (int) 0x000000, color.getRGB() };
084:
085: IndexColorModel colorModel = new IndexColorModel(1, 2,
086: masColors, 0, true, 0, DataBuffer.TYPE_BYTE);
087:
088: BufferedImage bim = new BufferedImage(width, height,
089: BufferedImage.TYPE_BYTE_INDEXED, colorModel);
090:
091: bim.setData(wr.createWritableChild(0, 0, width, height, 0, 0,
092: null));
093:
094: return bim;
095: }
096:
097: /**
098: * Draws string on specified Graphics at desired position.
099: *
100: * @param g specified Graphics2D object
101: * @param str String object to draw
102: * @param x start X position to draw
103: * @param y start Y position to draw
104: */
105: public void drawString(Graphics2D g, String str, float x, float y) {
106: char[] input = str.toCharArray();
107: Character ch;
108: Glyph glyph;
109: DLInfo info;
110: GlyphMetrics glMetrics;
111: Color col = g.getColor();
112: Font font = g.getFont();
113: int length = str.length();
114: FontPeerImpl peer = ((FontPeerImpl) font.getPeer());
115: AffineTransform fontAT = (AffineTransform) font.getTransform()
116: .clone();
117: Point.Float pos = new Point.Float();
118: Paint paint = g.getPaint();
119: boolean isAntialias = g
120: .getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING) == RenderingHints.VALUE_TEXT_ANTIALIAS_ON;
121:
122: try {
123: fontAT.inverseTransform(new Point.Double(x
124: + fontAT.getTranslateX(), y
125: + fontAT.getTranslateY()), pos);
126: } catch (NoninvertibleTransformException e) {
127: // TODO determinant equals 0 => point or line
128: g.fill(font
129: .createGlyphVector(g.getFontRenderContext(), str)
130: .getOutline(x, y));
131: return;
132: }
133:
134: fontAT.translate(pos.x, pos.y);
135: g.transform(fontAT);
136:
137: HashCode hash = new HashCode();
138: hash.append(peer);
139: hash.append(getFactor(g.getTransform()));
140: hash.append(paint);
141: hash.append(isAntialias);
142: Integer intHash = new Integer(hash.hashCode());
143:
144: GlyphHashtable glyphHash = intHash2glyphHash
145: .containsKey(intHash) ? (GlyphHashtable) intHash2glyphHash
146: .get(intHash)
147: : null;
148: if (glyphHash == null) {
149: glyphHash = new GlyphHashtable();
150: intHash2glyphHash.put(intHash, glyphHash);
151: }
152:
153: activateVars();
154:
155: for (int i = 0; i - length < 0; i++) {
156: ch = new Character(input[i]);
157: if (ESCAPE.contains(ch))
158: continue;
159: glyph = peer.getGlyph(input[i]);
160:
161: if (ch == ' ') {
162: glMetrics = glyph.getGlyphPointMetrics();
163: gl.glTranslated(glMetrics.getAdvanceX(), glMetrics
164: .getAdvanceY(), 0);
165: continue;
166: }
167:
168: info = glyphHash.containsKey(ch) ? (DLInfo) glyphHash
169: .get(ch) : null;
170:
171: if (info == null || !info.isValid()) {
172: createColorGlyphDL(g, glyph, glyphHash, font, ch, col,
173: isAntialias);
174: } else {
175: gl.glCallList(info.getDL());
176: }
177:
178: glMetrics = glyph.getGlyphPointMetrics();
179: gl.glTranslated(glMetrics.getAdvanceX(), glMetrics
180: .getAdvanceY(), 0);
181:
182: }
183: deactivateVars();
184: cleanLists();
185:
186: }
187:
188: /**
189: * Draws GlyphVector on specified Graphics at desired position.
190: *
191: * @param g specified Graphics2D object
192: * @param glyphVector GlyphVector object to draw
193: * @param x start X position to draw
194: * @param y start Y position to draw
195: */
196: public void drawGlyphVector(Graphics2D g, GlyphVector gv, float x,
197: float y) {
198: Character ch;
199: DLInfo info;
200: Color col = g.getColor();
201: Glyph[] input = ((CommonGlyphVector) gv).vector;
202: Font font = gv.getFont();
203: int length = gv.getNumGlyphs();
204: FontPeerImpl peer = ((FontPeerImpl) font.getPeer());
205: AffineTransform fontAT = (AffineTransform) font.getTransform()
206: .clone();
207: Point.Float pos = new Point.Float();
208: boolean isAntialias = g
209: .getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING) == RenderingHints.VALUE_TEXT_ANTIALIAS_ON;
210:
211: try {
212: fontAT.inverseTransform(new Point.Double(x
213: + fontAT.getTranslateX(), y
214: + fontAT.getTranslateY()), pos);
215: } catch (NoninvertibleTransformException e) {
216: //TODO determinant equals 0 => point or line
217: g.fill(gv.getOutline(x, y));
218: return;
219: }
220:
221: fontAT.translate(pos.getX(), pos.getY());
222: g.transform(fontAT);
223:
224: HashCode hash = new HashCode();
225: hash.append(peer);
226: hash.append(getFactor(g.getTransform()));
227: hash.append(g.getPaint());
228: hash.append(isAntialias);
229: Integer intHash = new Integer(hash.hashCode());
230:
231: GlyphHashtable glyphHash = intHash2glyphHash
232: .containsKey(intHash) ? (GlyphHashtable) intHash2glyphHash
233: .get(intHash)
234: : null;
235: if (glyphHash == null) {
236: glyphHash = new GlyphHashtable();
237: intHash2glyphHash.put(intHash, glyphHash);
238: }
239:
240: fontAT = (AffineTransform) font.getTransform().clone();
241: activateVars();
242: for (int i = 0; i - length < 0; i++) {
243: ch = new Character(input[i].getChar());
244:
245: if (ch == ' ')
246: continue;
247:
248: info = glyphHash.containsKey(ch) ? (DLInfo) glyphHash
249: .get(ch) : null;
250: try {
251: fontAT.inverseTransform(gv.getGlyphPosition(i), pos);
252: } catch (NoninvertibleTransformException e) {
253: }
254:
255: gl.glTranslated(pos.x, pos.y, 0);
256: if (info == null || !info.isValid()) {
257: createColorGlyphDL(g, input[i], glyphHash, font, ch,
258: col, isAntialias);
259: } else {
260: gl.glCallList(info.getDL());
261: }
262: gl.glTranslated(-pos.x, -pos.y, 0);
263: }
264: deactivateVars();
265: cleanLists();
266: }
267:
268: /**
269: * @param ogl
270: * @param glyph
271: * @param glyphHash
272: * @param font
273: */
274: private void createColorGlyphDL(Graphics2D g, Glyph glyph,
275: GlyphHashtable glyphHash, Font font, Character ch,
276: Color col, boolean isAntialias) {
277: int base = gl.glGenLists(1);
278: if (base == 0) {
279: //awt.296 can't allocate memory on video card to create new display list
280: throw new NullPointerException(Messages
281: .getString("awt.296"));
282: }
283:
284: double texSize = getFactor(g.getTransform());
285:
286: Glyph newGlyph = ((FontPeerImpl) (font.deriveFont((float) (font
287: .getSize2D() * texSize))).getPeer()).getGlyph(ch
288: .charValue());
289:
290: byte[] pixels = newGlyph.getBitmap();
291:
292: BufferedImage bim = getBufImg(pixels, newGlyph.getPointWidth(),
293: newGlyph.getPointHeight(), col);
294:
295: Rectangle.Double rect = new Rectangle.Double(Math.round(glyph
296: .getGlyphPointMetrics().getLSB()), Math
297: .round(-newGlyph.bmp_top / texSize), glyph
298: .getPointWidth(), glyph.getPointHeight());
299:
300: Surface sur = Surface.getImageSurface(bim);
301:
302: OGLBlitter.OGLTextureParams tp = OGLBlitter.getInstance()
303: .blitImg2OGLTexCached(sur, bim.getWidth(),
304: bim.getHeight(), true);
305:
306: gl.glTexParameteri(GLDefs.GL_TEXTURE_2D,
307: GLDefs.GL_TEXTURE_WRAP_S, GLDefs.GL_CLAMP_TO_EDGE);
308: gl.glTexParameteri(GLDefs.GL_TEXTURE_2D,
309: GLDefs.GL_TEXTURE_WRAP_T, GLDefs.GL_CLAMP_TO_EDGE);
310:
311: if (isAntialias) {
312: gl.glTexParameteri(GLDefs.GL_TEXTURE_2D,
313: GLDefs.GL_TEXTURE_MAG_FILTER, GLDefs.GL_LINEAR);
314: gl.glTexParameteri(GLDefs.GL_TEXTURE_2D,
315: GLDefs.GL_TEXTURE_MIN_FILTER, GLDefs.GL_LINEAR);
316: } else {
317: gl.glTexParameteri(GLDefs.GL_TEXTURE_2D,
318: GLDefs.GL_TEXTURE_MAG_FILTER, GLDefs.GL_NEAREST);
319: gl.glTexParameteri(GLDefs.GL_TEXTURE_2D,
320: GLDefs.GL_TEXTURE_MIN_FILTER, GLDefs.GL_NEAREST);
321: }
322:
323: double widthFactor = rect.getWidth() / tp.width * tp.p2w;
324: double heightFactor = rect.getHeight() / tp.height * tp.p2h;
325:
326: gl.glNewList(base, GLDefs.GL_COMPILE_AND_EXECUTE);
327:
328: g.setColor(INVISIBLE_COLOR);
329:
330: gl.glBindTexture(GLDefs.GL_TEXTURE_2D, tp.textureName);
331:
332: gl.glBegin(GLDefs.GL_QUADS);
333:
334: gl.glTexCoord2d(0.0, 0.0);
335: gl.glVertex2d(rect.getX(), rect.getY());
336: gl.glTexCoord2d(0.0, rect.getHeight() / heightFactor);
337: gl.glVertex2d(rect.getX(), rect.getY() + rect.getHeight());
338: gl.glTexCoord2d(rect.getWidth() / widthFactor, rect.getHeight()
339: / heightFactor);
340: gl.glVertex2d(rect.getWidth() + rect.getX(), rect.getY()
341: + rect.getHeight());
342: gl.glTexCoord2d(rect.getWidth() / widthFactor, 0.0);
343: gl.glVertex2d(rect.getWidth() + rect.getX(), rect.getY());
344:
345: gl.glEnd();
346:
347: g.setColor(col);
348:
349: gl.glEndList();
350:
351: glyphHash.put(ch, new DLInfo(base, sur));
352:
353: // System.out.println("create new dl - " + base);
354: }
355:
356: private double getFactor(AffineTransform at) {
357: return Math.max(at.getScaleX(), at.getScaleY());
358: }
359:
360: private void cleanLists() {
361: synchronized (toDel) {
362: if (!toDel.isEmpty()) {
363: for (Iterator iter = toDel.iterator(); iter.hasNext();) {
364: int element = ((Integer) iter.next()).intValue();
365: if (gl.glIsList(element) == GLDefs.GL_TRUE) {
366:
367: gl.glDeleteLists(element, 1);
368: // System.err.println("delete dl - " + element);
369: }
370: }
371: toDel.clear();
372: }
373: }
374: }
375:
376: private void activateVars() {
377: // gl.glPixelStorei(GLDefs.GL_UNPACK_ALIGNMENT, 1);
378: gl.glTexEnvf(GLDefs.GL_TEXTURE_ENV, GLDefs.GL_TEXTURE_ENV_MODE,
379: GLDefs.GL_REPLACE);
380:
381: gl.glTexParameteri(GLDefs.GL_TEXTURE_2D,
382: GLDefs.GL_TEXTURE_WRAP_S, GLDefs.GL_CLAMP_TO_EDGE);
383: gl.glTexParameteri(GLDefs.GL_TEXTURE_2D,
384: GLDefs.GL_TEXTURE_WRAP_T, GLDefs.GL_CLAMP_TO_EDGE);
385:
386: gl.glTexGeni(GLDefs.GL_S, GLDefs.GL_TEXTURE_GEN_MODE,
387: GLDefs.GL_OBJECT_LINEAR);
388: gl.glTexGeni(GLDefs.GL_T, GLDefs.GL_TEXTURE_GEN_MODE,
389: GLDefs.GL_OBJECT_LINEAR);
390:
391: gl.glDisable(GLDefs.GL_TEXTURE_GEN_S);
392: gl.glDisable(GLDefs.GL_TEXTURE_GEN_T);
393:
394: gl.glEnable(GLDefs.GL_TEXTURE_2D);
395: }
396:
397: private void deactivateVars() {
398: gl.glDisable(GLDefs.GL_TEXTURE_2D);
399: }
400:
401: private static final class SoftHashtable extends Hashtable {
402:
403: public Object put(Object key, Object obj) {
404: SoftReference ref = (SoftReference) super .put(key,
405: new SoftReference(obj));
406: return ref == null ? null : ref.get();
407: }
408:
409: public Object get(Object key) {
410: SoftReference ref = (SoftReference) super .get(key);
411: return ref == null ? null : ref.get();
412: }
413:
414: public Object remove(Object key) {
415: SoftReference ref = (SoftReference) super .remove(key);
416: return ref == null ? null : ref.get();
417: }
418: }
419:
420: private static final class GlyphHashtable extends Hashtable {
421: public void finalize() throws Throwable {
422: super .finalize();
423: synchronized (toDel) {
424: for (Iterator i = this .values().iterator(); i.hasNext();) {
425: toDel.add(new Integer(((DLInfo) i.next()).getDL()));
426: }
427: }
428: }
429: }
430:
431: private static final class DLInfo {
432: private int dl;
433: private Surface srf;
434:
435: public DLInfo(int dl, Surface srf) {
436: this .dl = dl;
437: this .srf = srf;
438: }
439:
440: public int getDL() {
441: return dl;
442: }
443:
444: public Surface getSrf() {
445: return srf;
446: }
447:
448: public boolean isValid() {
449: if (tx.findTexture(srf) == null) {
450: synchronized (toDel) {
451: toDel.add(new Integer(dl));
452: }
453: return false;
454: }
455:
456: return gl.glIsList(dl) == GLDefs.GL_TRUE;
457: }
458: }
459:
460: }
|