Frames | No Frames |
1: /* GNUGlyphVector.java -- The GNU implementation of GlyphVector. 2: Copyright (C) 2006 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package gnu.java.awt.font; 39: 40: import java.awt.Font; 41: import java.awt.font.FontRenderContext; 42: import java.awt.font.GlyphMetrics; 43: import java.awt.font.GlyphJustificationInfo; 44: import java.awt.font.GlyphVector; 45: 46: import java.awt.Shape; 47: import java.awt.geom.AffineTransform; 48: import java.awt.geom.GeneralPath; 49: import java.awt.geom.Point2D; 50: import java.awt.geom.Rectangle2D; 51: 52: 53: /** 54: * The GNU implementation of the abstract GlyphVector class, which 55: * uses the services provided by a FontDelegate for its functionality. 56: * 57: * @author Sascha Brawer (brawer@dandelis.ch) 58: */ 59: public class GNUGlyphVector 60: extends GlyphVector 61: { 62: private FontDelegate fontDelegate; 63: private Font font; 64: private FontRenderContext renderContext; 65: private int[] glyphs; 66: private float fontSize; 67: private AffineTransform transform; 68: private boolean valid; 69: 70: 71: /** 72: * The position of each glyph. The horizontal position of the 73: * <code>i</code>-th glyph is at <code>pos[i * 2]</code>, its 74: * vertical position at <code>pos[i * 2 + 1]</code>. The total 75: * advance width of the entire vector is stored at 76: * <code>pos[numGlyphs]</code>, the total advance height at 77: * <code>pos[numGlyphs + 1]</code>. 78: */ 79: private float[] pos; 80: 81: 82: private AffineTransform[] transforms; 83: private int layoutFlags; 84: 85: 86: /** 87: * Constructs a new GNUGlyphVector. 88: * 89: * @param fontDelegate the FontDelegate that creates this vector. 90: * 91: * @param font the Font that this GlyphVector will return for {@link 92: * #getFont()}. That object is also used to determine the point 93: * size, which affects the affine transformation used by the font 94: * scaler. 95: * 96: * @param renderContext an object with parameters for font 97: * rendering, such as whether anti-aliasing is enabled. 98: * 99: * @param glyphs the glyphs in this vector. 100: */ 101: public GNUGlyphVector(FontDelegate fontDelegate, 102: Font font, 103: FontRenderContext renderContext, 104: int[] glyphs) 105: { 106: this.fontDelegate = fontDelegate; 107: this.font = font; 108: this.renderContext = renderContext; 109: this.glyphs = glyphs; 110: 111: fontSize = font.getSize2D(); 112: transform = font.getTransform(); // returns a modifiable copy 113: //transform.concatenate(renderContext.getTransform()); 114: } 115: 116: 117: 118: /** 119: * Returns the font of the glyphs in this GlyphVector. 120: */ 121: public Font getFont() 122: { 123: return font; 124: } 125: 126: 127: /** 128: * Returns the FontRenderContext that is used to calculate the 129: * extent and position of the glyphs. 130: */ 131: public FontRenderContext getFontRenderContext() 132: { 133: return renderContext; 134: } 135: 136: 137: /** 138: * Moves each glyph in the vector to its default position. 139: */ 140: public void performDefaultLayout() 141: { 142: float x, y, advanceWidth, advanceHeight; 143: int i, p; 144: AffineTransform tx; 145: Point2D.Float advance = new Point2D.Float(); 146: 147: pos = new float[(glyphs.length + 1) * 2]; 148: x = y = 0.0f; 149: p = 0; 150: for (i = p = 0; i < glyphs.length; i++) 151: { 152: p += 2; 153: 154: if ((transforms == null) || (tx = transforms[i]) == null) 155: tx = this.transform; 156: else 157: { 158: tx = new AffineTransform(tx); 159: tx.concatenate(this.transform); 160: } 161: 162: fontDelegate.getAdvance(glyphs[i], fontSize, tx, 163: renderContext.isAntiAliased(), 164: renderContext.usesFractionalMetrics(), 165: /* horizontal */ true, 166: advance); 167: pos[p] = x += advance.x; 168: pos[p + 1] = y += advance.y; 169: } 170: valid = true; 171: } 172: 173: 174: /** 175: * Determines the number of glyphs in this GlyphVector. 176: */ 177: public int getNumGlyphs() 178: { 179: return glyphs.length; 180: } 181: 182: 183: /** 184: * Determines the glyph number by index in this vector. 185: * Glyph numbers are specific to each font, so two fonts 186: * will likely assign different numbers to the same glyph. 187: * 188: * @param glyphIndex the index of the glyph whose glyph number is to 189: * be retrieved. 190: * 191: * @throws IndexOutOfBoundsException if <code>glyphIndex</code> 192: * is not in the range <code[0 .. getNumGlyphs() - 1]</code>. 193: */ 194: public int getGlyphCode(int glyphIndex) 195: { 196: /* The exception is thrown automatically if the index is out 197: * of the valid bounds. 198: */ 199: return glyphs[glyphIndex]; 200: } 201: 202: 203: /** 204: * Returns a slice of this GlyphVector. 205: * 206: * @param firstGlyphIndex the index of the first glyph in the 207: * returned slice. 208: * 209: * @param numEntries the size of the returned slice. 210: * 211: * @param outCodes a pre-allocated array for storing the slice, 212: * or <code>null</code> to cause allocation of a new array. 213: * 214: * @return a slice of this GlyphVector. If <code>outCodes</code> 215: * is <code>null</code>, the slice will be stored into a freshly 216: * allocated array; otherwise, the result will be stored into 217: * <code>outCodes</code>. 218: */ 219: public int[] getGlyphCodes(int firstGlyphIndex, 220: int numEntries, 221: int[] outCodes) 222: { 223: if (numEntries < 0) 224: throw new IllegalArgumentException(); 225: if (outCodes == null) 226: outCodes = new int[numEntries]; 227: System.arraycopy(glyphs, firstGlyphIndex, outCodes, 0, numEntries); 228: return outCodes; 229: } 230: 231: 232: public Rectangle2D getLogicalBounds() 233: { 234: float ascent, descent; 235: 236: validate(); 237: 238: return new Rectangle2D.Float(0, 0, 239: pos[pos.length - 2], 240: getAscent() - getDescent()); 241: } 242: 243: 244: public Rectangle2D getVisualBounds() 245: { 246: validate(); 247: 248: // FIXME: Not yet implemented. 249: return getLogicalBounds(); 250: } 251: 252: 253: /** 254: * Returns the shape of this GlyphVector. 255: */ 256: public Shape getOutline() 257: { 258: validate(); 259: return getOutline(0.0f, 0.0f); 260: } 261: 262: 263: /** 264: * Returns the shape of this GlyphVector, translated to the 265: * specified position. 266: * 267: * @param x the horizontal position for rendering this vector. 268: * @param y the vertical position for rendering this vector. 269: */ 270: public Shape getOutline(float x, float y) 271: { 272: validate(); 273: 274: GeneralPath outline = new GeneralPath(); 275: int len = glyphs.length; 276: for (int i = 0; i < len; i++) 277: { 278: GeneralPath p = new GeneralPath(getGlyphOutline(i)); 279: outline.append(p, false); 280: } 281: AffineTransform t = new AffineTransform(); 282: t.translate(x, y); 283: outline.transform(t); 284: return outline; 285: } 286: 287: 288: /** 289: * Determines the shape of the specified glyph. 290: * 291: * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is 292: * not in the range <code[0 .. getNumGlyphs()]</code>. 293: */ 294: public Shape getGlyphOutline(int glyphIndex) 295: { 296: AffineTransform tx, glyphTx; 297: GeneralPath path; 298: 299: validate(); 300: 301: if ((transforms != null) 302: && ((glyphTx = transforms[glyphIndex]) != null)) 303: { 304: tx = new AffineTransform(transform); 305: tx.concatenate(glyphTx); 306: } 307: else 308: tx = transform; 309: 310: path = fontDelegate.getGlyphOutline(glyphs[glyphIndex], fontSize, tx, 311: renderContext.isAntiAliased(), 312: renderContext.usesFractionalMetrics()); 313: 314: tx = new AffineTransform(); 315: tx.translate(pos[glyphIndex * 2], pos[glyphIndex * 2 + 1]); 316: path.transform(tx); 317: return path; 318: } 319: 320: 321: /** 322: * Determines the position of the specified glyph, or the 323: * total advance width and height of the vector. 324: * 325: * @param glyphIndex the index of the glyph in question. 326: * If this value equals <code>getNumGlyphs()</code>, the 327: * position <i>after</i> the last glyph will be returned, 328: * which is the total advance width and height of the vector. 329: * 330: * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is 331: * not in the range <code[0 .. getNumGlyphs()]</code>. 332: */ 333: public Point2D getGlyphPosition(int glyphIndex) 334: { 335: validate(); 336: return new Point2D.Float(pos[glyphIndex * 2], 337: pos[glyphIndex * 2 + 1]); 338: } 339: 340: 341: /** 342: * Moves the specified glyph to a new position, or changes the 343: * advance width and height of the entire glyph vector. 344: * 345: * <p>Note that the position of an individual glyph may also 346: * affected by its affine transformation. 347: * 348: * @param glyphIndex the index of the moved glyph. If 349: * <code>glyphIndex</code> equals the total number of glyphs in this 350: * vector, the advance width and height of the vector is changed. 351: * 352: * @param position the new position of the glyph. 353: * 354: * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is 355: * not in the range <code[0 .. getNumGlyphs()]</code>. 356: */ 357: public void setGlyphPosition(int glyphIndex, Point2D position) 358: { 359: validate(); 360: pos[glyphIndex * 2] = (float) position.getX(); 361: pos[glyphIndex * 2 + 1] = (float) position.getY(); 362: } 363: 364: 365: /** 366: * Returns the affine transformation that is applied to the 367: * glyph at the specified index. 368: * 369: * @param glyphIndex the index of the glyph whose transformation 370: * is to be retrieved. 371: * 372: * @return an affine transformation, or <code>null</code> 373: * for the identity transformation. 374: * 375: * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is 376: * not in the range <code[0 .. getNumGlyphs() - 1]</code>. 377: */ 378: public AffineTransform getGlyphTransform(int glyphIndex) 379: { 380: if (transforms == null) 381: return null; 382: else 383: return transforms[glyphIndex]; 384: } 385: 386: 387: /** 388: * Applies an affine transformation to the glyph at the specified 389: * index. 390: * 391: * @param glyphIndex the index of the glyph to which the 392: * transformation is applied. 393: * 394: * @param transform the affine transformation for the glyph, or 395: * <code>null</code> for an identity transformation. 396: */ 397: public void setGlyphTransform(int glyphIndex, 398: AffineTransform transform) 399: { 400: if (transforms == null) 401: transforms = new AffineTransform[glyphs.length]; 402: transforms[glyphIndex] = transform; 403: 404: /* If the GlyphVector has only a transform for a single glyph, and 405: * the caller clears its transform, the FLAG_HAS_TRANSFORMS bit 406: * should be cleared in layoutFlags. However, this would require 407: * that we keep track of the number of transformed glyphs, or that 408: * we count them when a transform is cleared. This would 409: * complicate the code quite a bit. Note that the only drawback of 410: * wrongly setting FLAG_HAS_TRANSFORMS is that a slower code path 411: * might be taken for rendering the vector. Right now, we never 412: * really look at the flag, so it does not make any difference. 413: */ 414: if (transform != null) 415: layoutFlags |= FLAG_HAS_TRANSFORMS; 416: valid = false; 417: } 418: 419: 420: /** 421: * Returns flags that can be used for optimizing the rendering 422: * of this GlyphVector. 423: * 424: * @return a bit mask with the applicable flags set. 425: * 426: * @since 1.4 427: * 428: * @see GlyphVector#FLAG_HAS_POSITION_ADJUSTMENTS 429: * @see GlyphVector#FLAG_HAS_TRANSFORMS 430: * @see GlyphVector#FLAG_RUN_RTL 431: * @see GlyphVector#FLAG_COMPLEX_GLYPHS 432: * @see GlyphVector#FLAG_MASK 433: */ 434: public int getLayoutFlags() 435: { 436: return layoutFlags; 437: } 438: 439: 440: /** 441: * Returns the positions of a range of glyphs in this vector. 442: * 443: * @param firstGlyphIndex the index of the first glyph whose 444: * position is retrieved. 445: * 446: * @param numGlyphs the number of glyphs whose positions 447: * are retrieved. 448: * 449: * @param outPositions an array for storing the results 450: * (the length must be at least twice <code>numGlyphs</code>), 451: * or <code>null</code> for freshly allocating an array. 452: * 453: * @return an array with the glyph positions. The horizontal 454: * position of the <code>i</code>-th glyph is at index <code>2 * 455: * i</code>, the vertical position at index <code>2 * i + 1</code>. 456: * 457: * @throws IllegalArgumentException if <code>numGlyphs</code> 458: * is less than zero. 459: * 460: * @throws IndexOutOfBoundsException if either 461: * <code>firstGlyphIndex</code> or <code>(firstGlyphIndex + 462: * numGlyphs)</code> is not in the range <code>[0 .. getNumGlyphs() - 463: * 1]</code>. 464: */ 465: public float[] getGlyphPositions(int firstGlyphIndex, 466: int numGlyphs, 467: float[] outPositions) 468: { 469: if (numGlyphs < 0) 470: throw new IllegalArgumentException(); 471: 472: validate(); 473: if (outPositions == null) 474: outPositions = new float[numGlyphs * 2]; 475: 476: System.arraycopy(/*src */ pos, /* srcStart */ firstGlyphIndex * 2, 477: /* dest */ outPositions, /* destStart */ 0, 478: /* length */ numGlyphs * 2); 479: return outPositions; 480: } 481: 482: 483: private float getAscent() 484: { 485: return fontDelegate.getAscent(fontSize, transform, 486: renderContext.isAntiAliased(), 487: renderContext.usesFractionalMetrics(), 488: /* horizontal */ true); 489: } 490: 491: 492: private float getDescent() 493: { 494: return fontDelegate.getDescent(fontSize, transform, 495: renderContext.isAntiAliased(), 496: renderContext.usesFractionalMetrics(), 497: /* horizontal */ true); 498: } 499: 500: 501: public Shape getGlyphLogicalBounds(int glyphIndex) 502: { 503: float x, y, ascent; 504: 505: validate(); 506: ascent = getAscent(); 507: x = pos[glyphIndex * 2]; 508: y = pos[glyphIndex * 2 + 1]; 509: 510: return new Rectangle2D.Float(x, y - ascent, 511: pos[(glyphIndex + 1) * 2] - x, 512: ascent - getDescent()); 513: } 514: 515: 516: public Shape getGlyphVisualBounds(int glyphIndex) 517: { 518: return getGlyphOutline(glyphIndex).getBounds2D(); 519: } 520: 521: 522: /** 523: * Determines the metrics of the glyph at the specified index. 524: * 525: * @param glyphIndex the index of the glyph whose metrics is to be 526: * retrieved. 527: * 528: * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is 529: * not in the range <code[0 .. getNumGlyphs() - 1]</code>. 530: */ 531: public GlyphMetrics getGlyphMetrics(int glyphIndex) 532: { 533: // FIXME: Not yet implemented. 534: throw new UnsupportedOperationException(); 535: } 536: 537: 538: /** 539: * Determines the justification information for the glyph at the 540: * specified index. 541: * 542: * @param glyphIndex the index of the glyph whose justification 543: * information is to be retrieved. 544: * 545: * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is 546: * not in the range <code[0 .. getNumGlyphs() - 1]</code>. 547: */ 548: public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex) 549: { 550: // FIXME: Not yet implemented. 551: throw new UnsupportedOperationException(); 552: } 553: 554: 555: /** 556: * Determines whether another GlyphVector is for the same font and 557: * rendering context, uses the same glyphs and positions them to the 558: * same location. 559: * 560: * @param other the GlyphVector to compare with. 561: * 562: * @return <code>true</code> if the two vectors are equal, 563: * <code>false</code> otherwise. 564: */ 565: public boolean equals(GlyphVector other) 566: { 567: GNUGlyphVector o; 568: if (!(other instanceof GNUGlyphVector)) 569: return false; 570: 571: o = (GNUGlyphVector) other; 572: if ((this.font != o.font) 573: || (this.fontDelegate != o.fontDelegate) 574: || (this.renderContext != o.renderContext) 575: || (this.glyphs.length != o.glyphs.length)) 576: return false; 577: 578: for (int i = 0; i < glyphs.length; i++) 579: if (this.glyphs[i] != o.glyphs[i]) 580: return false; 581: 582: validate(); 583: o.validate(); 584: for (int i = 0; i < pos.length; i++) 585: if (this.pos[i] != o.pos[i]) 586: return false; 587: 588: return true; 589: } 590: 591: private void validate() 592: { 593: if (!valid) 594: performDefaultLayout(); 595: } 596: }