1 /*
   2  * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.font;
  27 
  28 import java.awt.Font;
  29 import java.awt.Graphics2D;
  30 import java.awt.Point;
  31 import java.awt.Rectangle;
  32 import static java.awt.RenderingHints.*;
  33 import java.awt.Shape;
  34 import java.awt.font.FontRenderContext;
  35 import java.awt.font.GlyphMetrics;
  36 import java.awt.font.GlyphJustificationInfo;
  37 import java.awt.font.GlyphVector;
  38 import java.awt.font.LineMetrics;
  39 import java.awt.font.TextAttribute;
  40 import java.awt.geom.AffineTransform;
  41 import java.awt.geom.GeneralPath;
  42 import java.awt.geom.NoninvertibleTransformException;
  43 import java.awt.geom.PathIterator;
  44 import java.awt.geom.Point2D;
  45 import java.awt.geom.Rectangle2D;
  46 import java.lang.ref.SoftReference;
  47 import java.text.CharacterIterator;
  48 
  49 import sun.awt.SunHints;
  50 import sun.java2d.loops.FontInfo;
  51 
  52 /**
  53  * Standard implementation of GlyphVector used by Font, GlyphList, and
  54  * SunGraphics2D.
  55  *
  56  * The main issues involve the semantics of the various transforms
  57  * (font, glyph, device) and their effect on rendering and metrics.
  58  *
  59  * Very, very unfortunately, the translation component of the font
  60  * transform affects where the text gets rendered.  It offsets the
  61  * rendering origin.  None of the other metrics of the glyphvector
  62  * are affected, making them inconsistent with the rendering behavior.
  63  * I think the translation component of the font would be better
  64  * interpreted as the translation component of a per-glyph transform,
  65  * but I don't know if this is possible to change.
  66  *
  67  * After the font transform is applied, the glyph transform is
  68  * applied.  This makes glyph transforms relative to font transforms,
  69  * if the font transform changes, the glyph transform will have the
  70  * same (relative) effect on the outline of the glyph.  The outline
  71  * and logical bounds are passed through the glyph transform before
  72  * being returned.  The glyph metrics ignore the glyph transform, but
  73  * provide the outline bounds and the advance vector of the glyph (the
  74  * latter will be rotated if the font is rotated).  The default layout
  75  * places each glyph at the end of the advance vector of the previous
  76  * glyph, and since the glyph transform translates the advance vector,
  77  * this means a glyph transform affects the positions of all
  78  * subsequent glyphs if defaultLayout is called after setting a glyph
  79  * transform.  In the glyph info array, the bounds are the outline
  80  * bounds including the glyph transform, and the positions are as
  81  * computed, and the advances are the deltas between the positions.
  82  *
  83  * (There's a bug in the logical bounds of a rotated glyph for
  84  * composite fonts, it's not to spec (in 1.4.0, 1.4.1, 1.4.2).  The
  85  * problem is that the rotated composite doesn't handle the multiple
  86  * ascents and descents properly in both x and y.  You end up with
  87  * a rotated advance vector but an unrotated ascent and descent.)
  88  *
  89  * Finally, the whole thing is transformed by the device transform to
  90  * position it on the page.
  91  *
  92  * Another bug: The glyph outline seems to ignore fractional point
  93  * size information, but the images (and advances) don't ignore it.
  94  *
  95  * Small fonts drawn at large magnification have odd advances when
  96  * fractional metrics is off-- that's because the advances depend on
  97  * the frc.  When the frc is scaled appropriately, the advances are
  98  * fine.  FM or a large frc (high numbers) make the advances right.
  99  *
 100  * The buffer aa flag doesn't affect rendering, the glyph vector
 101  * renders as AA if aa is set in its frc, and as non-aa if aa is not
 102  * set in its frc.
 103  *
 104  * font rotation, baseline, vertical etc.
 105  *
 106  * Font rotation and baseline Line metrics should be measured along a
 107  * unit vector pi/4 cc from the baseline vector.  For 'horizontal'
 108  * fonts the baseline vector is the x vector passed through the font
 109  * transform (ignoring translation), for 'vertical' it is the y
 110  * vector.  This definition makes ascent, descent, etc independent of
 111  * shear, so shearing can be used to simulate italic. This means no
 112  * fonts have 'negative ascents' or 'zero ascents' etc.
 113  *
 114  * Having a coordinate system with orthogonal axes where one is
 115  * parallel to the baseline means we could use rectangles and interpret
 116  * them in terms of this coordinate system.  Unfortunately there
 117  * is support for rotated fonts in the jdk already so maintaining
 118  * the semantics of existing code (getlogical bounds, etc) might
 119  * be difficult.
 120  *
 121  * A font transform transforms both the baseline and all the glyphs
 122  * in the font, so it does not rotate the glyph w.r.t the baseline.
 123  * If you do want to rotate individual glyphs, you need to apply a
 124  * glyph transform.  If performDefaultLayout is called after this,
 125  * the transformed glyph advances will affect the glyph positions.
 126  *
 127  * useful additions
 128  * - select vertical metrics - glyphs are rotated pi/4 cc and vertical
 129  * metrics are used to align them to the baseline.
 130  * - define baseline for font (glyph rotation not linked to baseline)
 131  * - define extra space (delta between each glyph along baseline)
 132  * - define offset (delta from 'true' baseline, impacts ascent and
 133  * descent as these are still computed from true basline and pinned
 134  * to zero, used in superscript).
 135  */
 136 public class StandardGlyphVector extends GlyphVector {
 137     private Font font;
 138     private FontRenderContext frc;
 139     private int[] glyphs; // always
 140     private int[] userGlyphs; // used to return glyphs to the client.
 141     private float[] positions; // only if not default advances
 142     private int[] charIndices;  // only if interesting
 143     private int flags; // indicates whether positions, charIndices is interesting
 144 
 145     private static final int UNINITIALIZED_FLAGS = -1;
 146 
 147     // transforms information
 148     private GlyphTransformInfo gti; // information about per-glyph transforms
 149 
 150     // !!! can we get rid of any of this extra stuff?
 151     private AffineTransform ftx;   // font transform without translation
 152     private AffineTransform dtx;   // device transform used for strike calculations, no translation
 153     private AffineTransform invdtx; // inverse of dtx or null if dtx is identity
 154     private AffineTransform frctx; // font render context transform, wish we could just share it
 155     private Font2D font2D;         // basic strike-independent stuff
 156     private SoftReference<GlyphStrike> fsref;   // font strike reference for glyphs with no per-glyph transform
 157 
 158     /////////////////////////////
 159     // Constructors and Factory methods
 160     /////////////////////////////
 161 
 162     public StandardGlyphVector(Font font, String str, FontRenderContext frc) {
 163         init(font, str.toCharArray(), 0, str.length(), frc, UNINITIALIZED_FLAGS);
 164     }
 165 
 166     public StandardGlyphVector(Font font, char[] text, FontRenderContext frc) {
 167         init(font, text, 0, text.length, frc, UNINITIALIZED_FLAGS);
 168     }
 169 
 170     public StandardGlyphVector(Font font, char[] text, int start, int count,
 171                                FontRenderContext frc) {
 172         init(font, text, start, count, frc, UNINITIALIZED_FLAGS);
 173     }
 174 
 175     private float getTracking(Font font) {
 176         if (font.hasLayoutAttributes()) {
 177             AttributeValues values = ((AttributeMap)font.getAttributes()).getValues();
 178             return values.getTracking();
 179         }
 180         return 0;
 181     }
 182 
 183      // used by GlyphLayout to construct a glyphvector
 184     public StandardGlyphVector(Font font, FontRenderContext frc, int[] glyphs, float[] positions,
 185                                int[] indices, int flags) {
 186         initGlyphVector(font, frc, glyphs, positions, indices, flags);
 187 
 188         // this code should go into layout
 189         float track = getTracking(font);
 190         if (track != 0) {
 191             track *= font.getSize2D();
 192             Point2D.Float trackPt = new Point2D.Float(track, 0); // advance delta
 193             if (font.isTransformed()) {
 194                 AffineTransform at = font.getTransform();
 195                 at.deltaTransform(trackPt, trackPt);
 196             }
 197 
 198             // how do we know its a base glyph
 199             // for now, it is if the natural advance of the glyph is non-zero
 200             Font2D f2d = FontUtilities.getFont2D(font);
 201             FontStrike strike = f2d.getStrike(font, frc);
 202 
 203             float[] deltas = { trackPt.x, trackPt.y };
 204             for (int j = 0; j < deltas.length; ++j) {
 205                 float inc = deltas[j];
 206                 if (inc != 0) {
 207                     float delta = 0;
 208                     for (int i = j, n = 0; n < glyphs.length; i += 2) {
 209                         if (strike.getGlyphAdvance(glyphs[n++]) != 0) { // might be an inadequate test
 210                             positions[i] += delta;
 211                             delta += inc;
 212                         }
 213                     }
 214                     positions[positions.length-2+j] += delta;
 215                 }
 216             }
 217         }
 218     }
 219 
 220     public void initGlyphVector(Font font, FontRenderContext frc, int[] glyphs, float[] positions,
 221                                 int[] indices, int flags) {
 222         this.font = font;
 223         this.frc = frc;
 224         this.glyphs = glyphs;
 225         this.userGlyphs = glyphs; // no need to check
 226         this.positions = positions;
 227         this.charIndices = indices;
 228         this.flags = flags;
 229 
 230         initFontData();
 231     }
 232 
 233     public StandardGlyphVector(Font font, CharacterIterator iter, FontRenderContext frc) {
 234         int offset = iter.getBeginIndex();
 235         char[] text = new char [iter.getEndIndex() - offset];
 236         for(char c = iter.first();
 237             c != CharacterIterator.DONE;
 238             c = iter.next()) {
 239             text[iter.getIndex() - offset] = c;
 240         }
 241         init(font, text, 0, text.length, frc, UNINITIALIZED_FLAGS);
 242     }
 243 
 244     public StandardGlyphVector(Font font, int[] glyphs, FontRenderContext frc) {
 245         // !!! find callers of this
 246         // should be able to fully init from raw data, e.g. charmap, flags too.
 247         this.font = font;
 248         this.frc = frc;
 249         this.flags = UNINITIALIZED_FLAGS;
 250 
 251         initFontData();
 252         this.userGlyphs = glyphs;
 253         this.glyphs = getValidatedGlyphs(this.userGlyphs);
 254     }
 255 
 256     /* This is called from the rendering loop. FontInfo is supplied
 257      * because a GV caches a strike and glyph images suitable for its FRC.
 258      * LCD text isn't currently supported on all surfaces, in which case
 259      * standard AA must be used. This is most likely to occur when LCD text
 260      * is requested and the surface is some non-standard type or hardward
 261      * surface for which there are no accelerated loops.
 262      * We can detect this as being AA=="ON" in the FontInfo and AA!="ON"
 263      * and AA!="GASP" in the FRC - since this only occurs for LCD text we don't
 264      * need to check any more precisely what value is in the FRC.
 265      */
 266     public static StandardGlyphVector getStandardGV(GlyphVector gv,
 267                                                     FontInfo info) {
 268         if (info.aaHint == SunHints.INTVAL_TEXT_ANTIALIAS_ON) {
 269             Object aaHint = gv.getFontRenderContext().getAntiAliasingHint();
 270             if (aaHint != VALUE_TEXT_ANTIALIAS_ON &&
 271                 aaHint != VALUE_TEXT_ANTIALIAS_GASP) {
 272                 /* We need to create a new GV with AA==ON for rendering */
 273                 FontRenderContext frc = gv.getFontRenderContext();
 274                 frc = new FontRenderContext(frc.getTransform(),
 275                                             VALUE_TEXT_ANTIALIAS_ON,
 276                                             frc.getFractionalMetricsHint());
 277                 return new StandardGlyphVector(gv, frc);
 278             }
 279         }
 280         if (gv instanceof StandardGlyphVector) {
 281             return (StandardGlyphVector)gv;
 282         }
 283         return new StandardGlyphVector(gv, gv.getFontRenderContext());
 284     }
 285 
 286     /////////////////////////////
 287     // GlyphVector API
 288     /////////////////////////////
 289 
 290     public Font getFont() {
 291         return this.font;
 292     }
 293 
 294     public FontRenderContext getFontRenderContext() {
 295         return this.frc;
 296     }
 297 
 298     public void performDefaultLayout() {
 299         positions = null;
 300         if (getTracking(font) == 0) {
 301             clearFlags(FLAG_HAS_POSITION_ADJUSTMENTS);
 302         }
 303     }
 304 
 305     public int getNumGlyphs() {
 306         return glyphs.length;
 307     }
 308 
 309     public int getGlyphCode(int glyphIndex) {
 310         return userGlyphs[glyphIndex];
 311     }
 312 
 313     public int[] getGlyphCodes(int start, int count, int[] result) {
 314         if (count < 0) {
 315             throw new IllegalArgumentException("count = " + count);
 316         }
 317         if (start < 0) {
 318             throw new IndexOutOfBoundsException("start = " + start);
 319         }
 320         if (start > glyphs.length - count) { // watch out for overflow if index + count overlarge
 321             throw new IndexOutOfBoundsException("start + count = " + (start + count));
 322         }
 323 
 324         if (result == null) {
 325             result = new int[count];
 326         }
 327 
 328         // if arraycopy were faster, we wouldn't code this
 329         for (int i = 0; i < count; ++i) {
 330             result[i] = userGlyphs[i + start];
 331         }
 332 
 333         return result;
 334     }
 335 
 336     public int getGlyphCharIndex(int ix) {
 337         if (ix < 0 && ix >= glyphs.length) {
 338             throw new IndexOutOfBoundsException("" + ix);
 339         }
 340         if (charIndices == null) {
 341             if ((getLayoutFlags() & FLAG_RUN_RTL) != 0) {
 342                 return glyphs.length - 1 - ix;
 343             }
 344             return ix;
 345         }
 346         return charIndices[ix];
 347     }
 348 
 349     public int[] getGlyphCharIndices(int start, int count, int[] result) {
 350         if (start < 0 || count < 0 || (count > glyphs.length - start)) {
 351             throw new IndexOutOfBoundsException("" + start + ", " + count);
 352         }
 353         if (result == null) {
 354             result = new int[count];
 355         }
 356         if (charIndices == null) {
 357             if ((getLayoutFlags() & FLAG_RUN_RTL) != 0) {
 358                 for (int i = 0, n = glyphs.length - 1 - start;
 359                      i < count; ++i, --n) {
 360                          result[i] = n;
 361                      }
 362             } else {
 363                 for (int i = 0, n = start; i < count; ++i, ++n) {
 364                     result[i] = n;
 365                 }
 366             }
 367         } else {
 368             for (int i = 0; i < count; ++i) {
 369                 result[i] = charIndices[i + start];
 370             }
 371         }
 372         return result;
 373     }
 374 
 375     // !!! not cached, assume TextLayout will cache if necessary
 376     // !!! reexamine for per-glyph-transforms
 377     // !!! revisit for text-on-a-path, vertical
 378     public Rectangle2D getLogicalBounds() {
 379         setFRCTX();
 380         initPositions();
 381 
 382         LineMetrics lm = font.getLineMetrics("", frc);
 383 
 384         float minX, minY, maxX, maxY;
 385         // horiz only for now...
 386         minX = 0;
 387         minY = -lm.getAscent();
 388         maxX = 0;
 389         maxY = lm.getDescent() + lm.getLeading();
 390         if (glyphs.length > 0) {
 391             maxX = positions[positions.length - 2];
 392         }
 393 
 394         return new Rectangle2D.Float(minX, minY, maxX - minX, maxY - minY);
 395     }
 396 
 397     // !!! not cached, assume TextLayout will cache if necessary
 398     public Rectangle2D getVisualBounds() {
 399         Rectangle2D result = null;
 400         for (int i = 0; i < glyphs.length; ++i) {
 401             Rectangle2D glyphVB = getGlyphVisualBounds(i).getBounds2D();
 402             if (!glyphVB.isEmpty()) {
 403                 if (result == null) {
 404                     result = glyphVB;
 405                 } else {
 406                     Rectangle2D.union(result, glyphVB, result);
 407                 }
 408             }
 409         }
 410         if (result == null) {
 411             result = new Rectangle2D.Float(0, 0, 0, 0);
 412         }
 413         return result;
 414     }
 415 
 416     // !!! not cached, assume TextLayout will cache if necessary
 417     // !!! fontStrike needs a method for this
 418     public Rectangle getPixelBounds(FontRenderContext renderFRC, float x, float y) {
 419       return getGlyphsPixelBounds(renderFRC, x, y, 0, glyphs.length);
 420     }
 421 
 422     public Shape getOutline() {
 423         return getGlyphsOutline(0, glyphs.length, 0, 0);
 424     }
 425 
 426     public Shape getOutline(float x, float y) {
 427         return getGlyphsOutline(0, glyphs.length, x, y);
 428     }
 429 
 430     // relative to gv origin
 431     public Shape getGlyphOutline(int ix) {
 432         return getGlyphsOutline(ix, 1, 0, 0);
 433     }
 434 
 435     // relative to gv origin offset by x, y
 436     public Shape getGlyphOutline(int ix, float x, float y) {
 437         return getGlyphsOutline(ix, 1, x, y);
 438     }
 439 
 440     public Point2D getGlyphPosition(int ix) {
 441         initPositions();
 442 
 443         ix *= 2;
 444         return new Point2D.Float(positions[ix], positions[ix + 1]);
 445     }
 446 
 447     public void setGlyphPosition(int ix, Point2D pos) {
 448         initPositions();
 449 
 450         int ix2 = ix << 1;
 451         positions[ix2] = (float)pos.getX();
 452         positions[ix2 + 1] = (float)pos.getY();
 453 
 454         clearCaches(ix);
 455         addFlags(FLAG_HAS_POSITION_ADJUSTMENTS);
 456     }
 457 
 458     public AffineTransform getGlyphTransform(int ix) {
 459         if (ix < 0 || ix >= glyphs.length) {
 460             throw new IndexOutOfBoundsException("ix = " + ix);
 461         }
 462         if (gti != null) {
 463             return gti.getGlyphTransform(ix);
 464         }
 465         return null; // spec'd as returning null
 466     }
 467 
 468     public void setGlyphTransform(int ix, AffineTransform newTX) {
 469         if (ix < 0 || ix >= glyphs.length) {
 470             throw new IndexOutOfBoundsException("ix = " + ix);
 471         }
 472 
 473         if (gti == null) {
 474             if (newTX == null || newTX.isIdentity()) {
 475                 return;
 476             }
 477             gti = new GlyphTransformInfo(this);
 478         }
 479         gti.setGlyphTransform(ix, newTX); // sets flags
 480         if (gti.transformCount() == 0) {
 481             gti = null;
 482         }
 483     }
 484 
 485     public int getLayoutFlags() {
 486         if (flags == UNINITIALIZED_FLAGS) {
 487             flags = 0;
 488 
 489             if (charIndices != null && glyphs.length > 1) {
 490                 boolean ltr = true;
 491                 boolean rtl = true;
 492 
 493                 int rtlix = charIndices.length; // rtl index
 494                 for (int i = 0; i < charIndices.length && (ltr || rtl); ++i) {
 495                     int cx = charIndices[i];
 496 
 497                     ltr = ltr && (cx == i);
 498                     rtl = rtl && (cx == --rtlix);
 499                 }
 500 
 501                 if (rtl) flags |= FLAG_RUN_RTL;
 502                 if (!rtl && !ltr) flags |= FLAG_COMPLEX_GLYPHS;
 503             }
 504         }
 505 
 506         return flags;
 507     }
 508 
 509     public float[] getGlyphPositions(int start, int count, float[] result) {
 510         if (count < 0) {
 511             throw new IllegalArgumentException("count = " + count);
 512         }
 513         if (start < 0) {
 514             throw new IndexOutOfBoundsException("start = " + start);
 515         }
 516         if (start > glyphs.length + 1 - count) { // watch for overflow
 517             throw new IndexOutOfBoundsException("start + count = " + (start + count));
 518         }
 519 
 520         return internalGetGlyphPositions(start, count, 0, result);
 521     }
 522 
 523     public Shape getGlyphLogicalBounds(int ix) {
 524         if (ix < 0 || ix >= glyphs.length) {
 525             throw new IndexOutOfBoundsException("ix = " + ix);
 526         }
 527 
 528         Shape[] lbcache;
 529         if (lbcacheRef == null || (lbcache = lbcacheRef.get()) == null) {
 530             lbcache = new Shape[glyphs.length];
 531             lbcacheRef = new SoftReference<>(lbcache);
 532         }
 533 
 534         Shape result = lbcache[ix];
 535         if (result == null) {
 536             setFRCTX();
 537             initPositions();
 538 
 539             // !!! ought to return a rectangle2d for simple cases, though the following works for all
 540 
 541             // get the position, the tx offset, and the x,y advance and x,y adl.  The
 542             // shape is the box formed by adv (width) and adl (height) offset by
 543             // the position plus the tx offset minus the ascent.
 544 
 545             ADL adl = new ADL();
 546             GlyphStrike gs = getGlyphStrike(ix);
 547             gs.getADL(adl);
 548 
 549             Point2D.Float adv = gs.strike.getGlyphMetrics(glyphs[ix]);
 550 
 551             float wx = adv.x;
 552             float wy = adv.y;
 553             float hx = adl.descentX + adl.leadingX + adl.ascentX;
 554             float hy = adl.descentY + adl.leadingY + adl.ascentY;
 555             float x = positions[ix*2] + gs.dx - adl.ascentX;
 556             float y = positions[ix*2+1] + gs.dy - adl.ascentY;
 557 
 558             GeneralPath gp = new GeneralPath();
 559             gp.moveTo(x, y);
 560             gp.lineTo(x + wx, y + wy);
 561             gp.lineTo(x + wx + hx, y + wy + hy);
 562             gp.lineTo(x + hx, y + hy);
 563             gp.closePath();
 564 
 565             result = new DelegatingShape(gp);
 566             lbcache[ix] = result;
 567         }
 568 
 569         return result;
 570     }
 571     private SoftReference<Shape[]> lbcacheRef;
 572 
 573     public Shape getGlyphVisualBounds(int ix) {
 574         if (ix < 0 || ix >= glyphs.length) {
 575             throw new IndexOutOfBoundsException("ix = " + ix);
 576         }
 577 
 578         Shape[] vbcache;
 579         if (vbcacheRef == null || (vbcache = vbcacheRef.get()) == null) {
 580             vbcache = new Shape[glyphs.length];
 581             vbcacheRef = new SoftReference<>(vbcache);
 582         }
 583 
 584         Shape result = vbcache[ix];
 585         if (result == null) {
 586             result = new DelegatingShape(getGlyphOutlineBounds(ix));
 587             vbcache[ix] = result;
 588         }
 589 
 590         return result;
 591     }
 592     private SoftReference<Shape[]> vbcacheRef;
 593 
 594     public Rectangle getGlyphPixelBounds(int index, FontRenderContext renderFRC, float x, float y) {
 595       return getGlyphsPixelBounds(renderFRC, x, y, index, 1);
 596     }
 597 
 598     public GlyphMetrics getGlyphMetrics(int ix) {
 599         if (ix < 0 || ix >= glyphs.length) {
 600             throw new IndexOutOfBoundsException("ix = " + ix);
 601         }
 602 
 603         Rectangle2D vb = getGlyphVisualBounds(ix).getBounds2D();
 604         Point2D pt = getGlyphPosition(ix);
 605         vb.setRect(vb.getMinX() - pt.getX(),
 606                    vb.getMinY() - pt.getY(),
 607                    vb.getWidth(),
 608                    vb.getHeight());
 609         Point2D.Float adv =
 610             getGlyphStrike(ix).strike.getGlyphMetrics(glyphs[ix]);
 611         GlyphMetrics gm = new GlyphMetrics(true, adv.x, adv.y,
 612                                            vb,
 613                                            GlyphMetrics.STANDARD);
 614         return gm;
 615     }
 616 
 617     public GlyphJustificationInfo getGlyphJustificationInfo(int ix) {
 618         if (ix < 0 || ix >= glyphs.length) {
 619             throw new IndexOutOfBoundsException("ix = " + ix);
 620         }
 621 
 622         // currently we don't have enough information to do this right.  should
 623         // get info from the font and use real OT/GX justification.  Right now
 624         // sun/font/ExtendedTextSourceLabel assigns one of three infos
 625         // based on whether the char is kanji, space, or other.
 626 
 627         return null;
 628     }
 629 
 630     public boolean equals(GlyphVector rhs) {
 631         if (this == rhs) {
 632             return true;
 633         }
 634         if (rhs == null) {
 635             return false;
 636         }
 637 
 638         try {
 639             StandardGlyphVector other = (StandardGlyphVector)rhs;
 640 
 641             if (glyphs.length != other.glyphs.length) {
 642                 return false;
 643             }
 644 
 645             for (int i = 0; i < glyphs.length; ++i) {
 646                 if (glyphs[i] != other.glyphs[i]) {
 647                     return false;
 648                 }
 649             }
 650 
 651             if (!font.equals(other.font)) {
 652                 return false;
 653             }
 654 
 655             if (!frc.equals(other.frc)) {
 656                 return false;
 657             }
 658 
 659             if ((other.positions == null) != (positions == null)) {
 660                 if (positions == null) {
 661                     initPositions();
 662                 } else {
 663                     other.initPositions();
 664                 }
 665             }
 666 
 667             if (positions != null) {
 668                 for (int i = 0; i < positions.length; ++i) {
 669                     if (positions[i] != other.positions[i]) {
 670                         return false;
 671                     }
 672                 }
 673             }
 674 
 675             if (gti == null) {
 676                 return other.gti == null;
 677             } else {
 678                 return gti.equals(other.gti);
 679             }
 680         }
 681         catch (ClassCastException e) {
 682             // assume they are different simply by virtue of the class difference
 683 
 684             return false;
 685         }
 686     }
 687 
 688     /**
 689      * As a concrete subclass of Object that implements equality, this must
 690      * implement hashCode.
 691      */
 692     public int hashCode() {
 693         return font.hashCode() ^ glyphs.length;
 694     }
 695 
 696     /**
 697      * Since we implement equality comparisons for GlyphVector, we implement
 698      * the inherited Object.equals(Object) as well.  GlyphVector should do
 699      * this, and define two glyphvectors as not equal if the classes differ.
 700      */
 701     public boolean equals(Object rhs) {
 702         try {
 703             return equals((GlyphVector)rhs);
 704         }
 705         catch (ClassCastException e) {
 706             return false;
 707         }
 708     }
 709 
 710     /**
 711      * Sometimes I wish java had covariant return types...
 712      */
 713     public StandardGlyphVector copy() {
 714         return (StandardGlyphVector)clone();
 715     }
 716 
 717     /**
 718      * As a concrete subclass of GlyphVector, this must implement clone.
 719      */
 720     public Object clone() {
 721         // positions, gti are mutable so we have to clone them
 722         // font2d can be shared
 723         // fsref is a cache and can be shared
 724         try {
 725             StandardGlyphVector result = (StandardGlyphVector)super.clone();
 726 
 727             result.clearCaches();
 728 
 729             if (positions != null) {
 730                 result.positions = positions.clone();
 731             }
 732 
 733             if (gti != null) {
 734                 result.gti = new GlyphTransformInfo(result, gti);
 735             }
 736 
 737             return result;
 738         }
 739         catch (CloneNotSupportedException e) {
 740         }
 741 
 742         return this;
 743     }
 744 
 745     //////////////////////
 746     // StandardGlyphVector new public methods
 747     /////////////////////
 748 
 749     /*
 750      * Set a multiple glyph positions at one time.  GlyphVector only
 751      * provides API to set a single glyph at a time.
 752      */
 753     public void setGlyphPositions(float[] srcPositions, int srcStart,
 754                                   int start, int count) {
 755         if (count < 0) {
 756             throw new IllegalArgumentException("count = " + count);
 757         }
 758 
 759         initPositions();
 760         for (int i = start * 2, e = i + count * 2, p = srcStart; i < e; ++i, ++p) {
 761             positions[i] = srcPositions[p];
 762         }
 763 
 764         clearCaches();
 765         addFlags(FLAG_HAS_POSITION_ADJUSTMENTS);
 766     }
 767 
 768     /**
 769      * Set all the glyph positions, including the 'after last glyph' position.
 770      * The srcPositions array must be of length (numGlyphs + 1) * 2.
 771      */
 772     public void setGlyphPositions(float[] srcPositions) {
 773         int requiredLength = glyphs.length * 2 + 2;
 774         if (srcPositions.length != requiredLength) {
 775             throw new IllegalArgumentException("srcPositions.length != " + requiredLength);
 776         }
 777 
 778         positions = srcPositions.clone();
 779 
 780         clearCaches();
 781         addFlags(FLAG_HAS_POSITION_ADJUSTMENTS);
 782     }
 783 
 784     /**
 785      * This is a convenience overload that gets all the glyph positions, which
 786      * is what you usually want to do if you're getting more than one.
 787      * !!! should I bother taking result parameter?
 788      */
 789     public float[] getGlyphPositions(float[] result) {
 790         return internalGetGlyphPositions(0, glyphs.length + 1, 0, result);
 791     }
 792 
 793     /**
 794      * Get transform information for the requested range of glyphs.
 795      * If no glyphs have a transform, return null.
 796      * If a glyph has no transform (or is the identity transform) its entry in the result array will be null.
 797      * If the passed-in result is null an array will be allocated for the caller.
 798      * Each transform instance in the result array will unique, and independent of the GlyphVector's transform.
 799      */
 800     public AffineTransform[] getGlyphTransforms(int start, int count, AffineTransform[] result) {
 801         if (start < 0 || count < 0 || start + count > glyphs.length) {
 802             throw new IllegalArgumentException("start: " + start + " count: " + count);
 803         }
 804 
 805         if (gti == null) {
 806             return null;
 807         }
 808 
 809         if (result == null) {
 810             result = new AffineTransform[count];
 811         }
 812 
 813         for (int i = 0; i < count; ++i, ++start) {
 814             result[i] = gti.getGlyphTransform(start);
 815         }
 816 
 817         return result;
 818     }
 819 
 820     /**
 821      * Convenience overload for getGlyphTransforms(int, int, AffineTransform[], int);
 822      */
 823     public AffineTransform[] getGlyphTransforms() {
 824         return getGlyphTransforms(0, glyphs.length, null);
 825     }
 826 
 827     /**
 828      * Set a number of glyph transforms.
 829      * Original transforms are unchanged.  The array may contain nulls, and also may
 830      * contain multiple references to the same transform instance.
 831      */
 832     public void setGlyphTransforms(AffineTransform[] srcTransforms, int srcStart, int start, int count) {
 833         for (int i = start, e = start + count; i < e; ++i) {
 834             setGlyphTransform(i, srcTransforms[srcStart + i]);
 835         }
 836     }
 837 
 838     /**
 839      * Convenience overload of setGlyphTransforms(AffineTransform[], int, int, int).
 840      */
 841     public void setGlyphTransforms(AffineTransform[] srcTransforms) {
 842         setGlyphTransforms(srcTransforms, 0, 0, glyphs.length);
 843     }
 844 
 845     /**
 846      * For each glyph return posx, posy, advx, advy, visx, visy, visw, vish.
 847      */
 848     public float[] getGlyphInfo() {
 849         setFRCTX();
 850         initPositions();
 851         float[] result = new float[glyphs.length * 8];
 852         for (int i = 0, n = 0; i < glyphs.length; ++i, n += 8) {
 853             float x = positions[i*2];
 854             float y = positions[i*2+1];
 855             result[n] = x;
 856             result[n+1] = y;
 857 
 858             int glyphID = glyphs[i];
 859             GlyphStrike s = getGlyphStrike(i);
 860             Point2D.Float adv = s.strike.getGlyphMetrics(glyphID);
 861             result[n+2] = adv.x;
 862             result[n+3] = adv.y;
 863 
 864             Rectangle2D vb = getGlyphVisualBounds(i).getBounds2D();
 865             result[n+4] = (float)(vb.getMinX());
 866             result[n+5] = (float)(vb.getMinY());
 867             result[n+6] = (float)(vb.getWidth());
 868             result[n+7] = (float)(vb.getHeight());
 869         }
 870         return result;
 871     }
 872 
 873     /**
 874      * !!! not used currently, but might be by getPixelbounds?
 875      */
 876     public void pixellate(FontRenderContext renderFRC, Point2D loc, Point pxResult) {
 877         if (renderFRC == null) {
 878             renderFRC = frc;
 879         }
 880 
 881         // it is a total pain that you have to copy the transform.
 882 
 883         AffineTransform at = renderFRC.getTransform();
 884         at.transform(loc, loc);
 885         pxResult.x = (int)loc.getX(); // but must not behave oddly around zero
 886         pxResult.y = (int)loc.getY();
 887         loc.setLocation(pxResult.x, pxResult.y);
 888         try {
 889             at.inverseTransform(loc, loc);
 890         }
 891         catch (NoninvertibleTransformException e) {
 892             throw new IllegalArgumentException("must be able to invert frc transform");
 893         }
 894     }
 895 
 896     //////////////////////
 897     // StandardGlyphVector package private methods
 898     /////////////////////
 899 
 900     // used by glyphlist to determine if it needs to allocate/size positions array
 901     // gti always uses positions because the gtx might have translation.  We also
 902     // need positions if the rendering dtx is different from the frctx.
 903 
 904     boolean needsPositions(double[] devTX) {
 905         return gti != null ||
 906             (getLayoutFlags() & FLAG_HAS_POSITION_ADJUSTMENTS) != 0 ||
 907             !matchTX(devTX, frctx);
 908     }
 909 
 910     // used by glyphList to get strong refs to font strikes for duration of rendering call
 911     // if devTX matches current devTX, we're ready to go
 912     // if we don't have multiple transforms, we're already ok
 913 
 914     // !!! I'm not sure fontInfo works so well for glyphvector, since we have to be able to handle
 915     // the multiple-strikes case
 916 
 917     /*
 918      * GlyphList calls this to set up its images data.  First it calls needsPositions,
 919      * passing the devTX, to see if it should provide us a positions array to fill.
 920      * It only doesn't need them if we're a simple glyph vector whose frctx matches the
 921      * devtx.
 922      * Then it calls setupGlyphImages.  If we need positions, we make sure we have our
 923      * default positions based on the frctx first. Then we set the devTX, and use
 924      * strikes based on it to generate the images.  Finally, we fill in the positions
 925      * array.
 926      * If we have transforms, we delegate to gti.  It depends on our having first
 927      * initialized the positions and devTX.
 928      */
 929     Object setupGlyphImages(long[] images, float[] positions, double[] devTX) {
 930         initPositions(); // FIRST ensure we have positions based on our frctx
 931         setRenderTransform(devTX); // THEN make sure we are using the desired devTX
 932 
 933         if (gti != null) {
 934             return gti.setupGlyphImages(images, positions, dtx);
 935         }
 936 
 937         GlyphStrike gs = getDefaultStrike();
 938         gs.strike.getGlyphImagePtrs(glyphs, images, glyphs.length);
 939 
 940         if (positions != null) {
 941             if (dtx.isIdentity()) {
 942                 System.arraycopy(this.positions, 0, positions, 0, glyphs.length * 2);
 943             } else {
 944                 dtx.transform(this.positions, 0, positions, 0, glyphs.length);
 945             }
 946         }
 947 
 948         return gs;
 949     }
 950 
 951     //////////////////////
 952     // StandardGlyphVector private methods
 953     /////////////////////
 954 
 955     // We keep translation in our frctx since getPixelBounds uses it.  But
 956     // GlyphList pulls out the translation and applies it separately, so
 957     // we strip it out when we set the dtx.  Basically nothing uses the
 958     // translation except getPixelBounds.
 959 
 960     // called by needsPositions, setRenderTransform
 961     private static boolean matchTX(double[] lhs, AffineTransform rhs) {
 962         return
 963             lhs[0] == rhs.getScaleX() &&
 964             lhs[1] == rhs.getShearY() &&
 965             lhs[2] == rhs.getShearX() &&
 966             lhs[3] == rhs.getScaleY();
 967     }
 968 
 969     // returns new tx if old one has translation, otherwise returns old one
 970     private static AffineTransform getNonTranslateTX(AffineTransform tx) {
 971         if (tx.getTranslateX() != 0 || tx.getTranslateY() != 0) {
 972             tx = new AffineTransform(tx.getScaleX(), tx.getShearY(),
 973                                      tx.getShearX(), tx.getScaleY(),
 974                                      0, 0);
 975         }
 976         return tx;
 977     }
 978 
 979     private static boolean equalNonTranslateTX(AffineTransform lhs, AffineTransform rhs) {
 980         return lhs.getScaleX() == rhs.getScaleX() &&
 981             lhs.getShearY() == rhs.getShearY() &&
 982             lhs.getShearX() == rhs.getShearX() &&
 983             lhs.getScaleY() == rhs.getScaleY();
 984     }
 985 
 986     // called by setupGlyphImages (after needsPositions, so redundant match check?)
 987     private void setRenderTransform(double[] devTX) {
 988         assert(devTX.length == 4);
 989         if (!matchTX(devTX, dtx)) {
 990             resetDTX(new AffineTransform(devTX)); // no translation since devTX len == 4.
 991         }
 992     }
 993 
 994     // called by getGlyphsPixelBounds
 995     private final void setDTX(AffineTransform tx) {
 996         if (!equalNonTranslateTX(dtx, tx)) {
 997             resetDTX(getNonTranslateTX(tx));
 998         }
 999     }
1000 
1001     // called by most functions
1002     private final void setFRCTX() {
1003         if (!equalNonTranslateTX(frctx, dtx)) {
1004             resetDTX(getNonTranslateTX(frctx));
1005         }
1006     }
1007 
1008     /**
1009      * Change the dtx for the strike refs we use.  Keeps a reference to the at.  At
1010      * must not contain translation.
1011      * Called by setRenderTransform, setDTX, initFontData.
1012      */
1013     private final void resetDTX(AffineTransform at) {
1014         fsref = null;
1015         dtx = at;
1016         invdtx = null;
1017         if (!dtx.isIdentity()) {
1018             try {
1019                 invdtx = dtx.createInverse();
1020             }
1021             catch (NoninvertibleTransformException e) {
1022                 // we needn't care for rendering
1023             }
1024         }
1025         if (gti != null) {
1026             gti.strikesRef = null;
1027         }
1028     }
1029 
1030     /**
1031      * Utility used by getStandardGV.
1032      * Constructs a StandardGlyphVector from a generic glyph vector.
1033      * Do not call this from new contexts without considering the comment
1034      * about "userGlyphs".
1035      */
1036     private StandardGlyphVector(GlyphVector gv, FontRenderContext frc) {
1037         this.font = gv.getFont();
1038         this.frc = frc;
1039         initFontData();
1040 
1041         int nGlyphs = gv.getNumGlyphs();
1042         this.userGlyphs = gv.getGlyphCodes(0, nGlyphs, null);
1043         if (gv instanceof StandardGlyphVector) {
1044             /* userGlyphs will be OK because this is a private constructor
1045              * and the returned instance is used only for rendering.
1046              * It's not constructable by user code, nor returned to the
1047              * application. So we know "userGlyphs" are valid as having
1048              * been either already validated or are the result of layout.
1049              */
1050             this.glyphs = userGlyphs;
1051         } else {
1052             this.glyphs = getValidatedGlyphs(this.userGlyphs);
1053         }
1054         this.flags = gv.getLayoutFlags() & FLAG_MASK;
1055 
1056         if ((flags & FLAG_HAS_POSITION_ADJUSTMENTS) != 0) {
1057             this.positions = gv.getGlyphPositions(0, nGlyphs + 1, null);
1058         }
1059 
1060         if ((flags & FLAG_COMPLEX_GLYPHS) != 0) {
1061             this.charIndices = gv.getGlyphCharIndices(0, nGlyphs, null);
1062         }
1063 
1064         if ((flags & FLAG_HAS_TRANSFORMS) != 0) {
1065             AffineTransform[] txs = new AffineTransform[nGlyphs]; // worst case
1066             for (int i = 0; i < nGlyphs; ++i) {
1067                 txs[i] = gv.getGlyphTransform(i); // gv doesn't have getGlyphsTransforms
1068             }
1069 
1070             setGlyphTransforms(txs);
1071         }
1072     }
1073 
1074     /* Before asking the Font we see if the glyph code is
1075      * FFFE or FFFF which are special values that we should be internally
1076      * ready to handle as meaning invisible glyphs. The Font would report
1077      * those as the missing glyph.
1078      */
1079     int[] getValidatedGlyphs(int[] oglyphs) {
1080         int len = oglyphs.length;
1081         int[] vglyphs = new int[len];
1082         for (int i=0; i<len; i++) {
1083             if (oglyphs[i] == 0xFFFE || oglyphs[i] == 0xFFFF) {
1084                 vglyphs[i] = oglyphs[i];
1085             } else {
1086                 vglyphs[i] = font2D.getValidatedGlyphCode(oglyphs[i]);
1087             }
1088         }
1089         return vglyphs;
1090     }
1091 
1092     // utility used by constructors
1093     private void init(Font font, char[] text, int start, int count,
1094                       FontRenderContext frc, int flags) {
1095 
1096         if (start < 0 || count < 0 || start + count > text.length) {
1097             throw new ArrayIndexOutOfBoundsException("start or count out of bounds");
1098         }
1099 
1100         this.font = font;
1101         this.frc = frc;
1102         this.flags = flags;
1103 
1104         if (getTracking(font) != 0) {
1105             addFlags(FLAG_HAS_POSITION_ADJUSTMENTS);
1106         }
1107 
1108         // !!! change mapper interface?
1109         if (start != 0) {
1110             char[] temp = new char[count];
1111             System.arraycopy(text, start, temp, 0, count);
1112             text = temp;
1113         }
1114 
1115         initFontData(); // sets up font2D
1116 
1117         // !!! no layout for now, should add checks
1118         // !!! need to support creating a StandardGlyphVector from a TextMeasurer's info...
1119         glyphs = new int[count]; // hmmm
1120         /* Glyphs obtained here are already validated by the font */
1121         userGlyphs = glyphs;
1122         font2D.getMapper().charsToGlyphs(count, text, glyphs);
1123     }
1124 
1125     private void initFontData() {
1126         font2D = FontUtilities.getFont2D(font);
1127         if (font2D instanceof FontSubstitution) {
1128            font2D = ((FontSubstitution)font2D).getCompositeFont2D();
1129         }
1130         float s = font.getSize2D();
1131         if (font.isTransformed()) {
1132             ftx = font.getTransform();
1133             if (ftx.getTranslateX() != 0 || ftx.getTranslateY() != 0) {
1134                 addFlags(FLAG_HAS_POSITION_ADJUSTMENTS);
1135             }
1136             ftx.setTransform(ftx.getScaleX(), ftx.getShearY(), ftx.getShearX(), ftx.getScaleY(), 0, 0);
1137             ftx.scale(s, s);
1138         } else {
1139             ftx = AffineTransform.getScaleInstance(s, s);
1140         }
1141 
1142         frctx = frc.getTransform();
1143         resetDTX(getNonTranslateTX(frctx));
1144     }
1145 
1146     /**
1147      * Copy glyph position data into a result array starting at the indicated
1148      * offset in the array.  If the passed-in result array is null, a new
1149      * array will be allocated and returned.
1150      *
1151      * This is an internal method and does no extra argument checking.
1152      *
1153      * @param start the index of the first glyph to get
1154      * @param count the number of glyphs to get
1155      * @param offset the offset into result at which to put the data
1156      * @param result an array to hold the x,y positions
1157      * @return the modified position array
1158      */
1159     private float[] internalGetGlyphPositions(int start, int count, int offset, float[] result) {
1160         if (result == null) {
1161             result = new float[offset + count * 2];
1162         }
1163 
1164         initPositions();
1165 
1166         // System.arraycopy is slow for stuff like this
1167         for (int i = offset, e = offset + count * 2, p = start * 2; i < e; ++i, ++p) {
1168             result[i] = positions[p];
1169         }
1170 
1171         return result;
1172     }
1173 
1174     private Rectangle2D getGlyphOutlineBounds(int ix) {
1175         setFRCTX();
1176         initPositions();
1177         return getGlyphStrike(ix).getGlyphOutlineBounds(glyphs[ix], positions[ix*2], positions[ix*2+1]);
1178     }
1179 
1180     /**
1181      * Used by getOutline, getGlyphsOutline
1182      */
1183     private Shape getGlyphsOutline(int start, int count, float x, float y) {
1184         setFRCTX();
1185         initPositions();
1186 
1187         GeneralPath result = new GeneralPath(GeneralPath.WIND_NON_ZERO);
1188         for (int i = start, e = start + count, n = start * 2; i < e; ++i, n += 2) {
1189             float px = x + positions[n];
1190             float py = y + positions[n+1];
1191 
1192             getGlyphStrike(i).appendGlyphOutline(glyphs[i], result, px, py);
1193         }
1194 
1195         return result;
1196     }
1197 
1198     private Rectangle getGlyphsPixelBounds(FontRenderContext frc, float x, float y, int start, int count) {
1199         initPositions(); // FIRST ensure we have positions based on our frctx
1200 
1201         AffineTransform tx = null;
1202         if (frc == null || frc.equals(this.frc)) {
1203             tx = frctx;
1204         } else {
1205             tx = frc.getTransform();
1206         }
1207         setDTX(tx); // need to get the right strikes, but we use tx itself to translate the points
1208 
1209         if (gti != null) {
1210             return gti.getGlyphsPixelBounds(tx, x, y, start, count);
1211         }
1212 
1213         FontStrike fs = getDefaultStrike().strike;
1214         Rectangle result = null;
1215         Rectangle r = new Rectangle();
1216         Point2D.Float pt = new Point.Float();
1217         int n = start * 2;
1218         while (--count >= 0) {
1219             pt.x = x + positions[n++];
1220             pt.y = y + positions[n++];
1221             tx.transform(pt, pt);
1222             fs.getGlyphImageBounds(glyphs[start++], pt, r);
1223             if (!r.isEmpty()) {
1224                 if (result == null) {
1225                     result = new Rectangle(r);
1226                 } else {
1227                     result.add(r);
1228                 }
1229             }
1230         }
1231         return result != null ? result : r;
1232     }
1233 
1234     private void clearCaches(int ix) {
1235         if (lbcacheRef != null) {
1236             Shape[] lbcache = lbcacheRef.get();
1237             if (lbcache != null) {
1238                 lbcache[ix] = null;
1239             }
1240         }
1241 
1242         if (vbcacheRef != null) {
1243             Shape[] vbcache = vbcacheRef.get();
1244             if (vbcache != null) {
1245                 vbcache[ix] = null;
1246             }
1247         }
1248     }
1249 
1250     private void clearCaches() {
1251         lbcacheRef = null;
1252         vbcacheRef = null;
1253     }
1254 
1255     // internal use only for possible future extension
1256 
1257     /**
1258      * A flag used with getLayoutFlags that indicates whether this <code>GlyphVector</code> uses
1259      * a vertical baseline.
1260      */
1261     public static final int FLAG_USES_VERTICAL_BASELINE = 128;
1262 
1263     /**
1264      * A flag used with getLayoutFlags that indicates whether this <code>GlyphVector</code> uses
1265      * vertical glyph metrics.  A <code>GlyphVector</code> can use vertical metrics on a
1266      * horizontal line, or vice versa.
1267      */
1268     public static final int FLAG_USES_VERTICAL_METRICS = 256;
1269 
1270     /**
1271      * A flag used with getLayoutFlags that indicates whether this <code>GlyphVector</code> uses
1272      * the 'alternate orientation.'  Glyphs have a default orientation given a
1273      * particular baseline and metrics orientation, this is the orientation appropriate
1274      * for left-to-right text.  For example, the letter 'A' can have four orientations,
1275      * with the point at 12, 3, 6, or 9 'o clock.  The following table shows where the
1276      * point displays for different values of vertical baseline (vb), vertical
1277      * metrics (vm) and alternate orientation (fo):<br>
1278      * <blockquote>
1279      * vb vm ao
1280      * -- -- --  --
1281      *  f  f  f  12   ^  horizontal metrics on horizontal lines
1282      *  f  f  t   6   v
1283      *  f  t  f   9   <  vertical metrics on horizontal lines
1284      *  f  t  t   3   >
1285      *  t  f  f   3   >  horizontal metrics on vertical lines
1286      *  t  f  t   9   <
1287      *  t  t  f  12   ^  vertical metrics on vertical lines
1288      *  t  t  t   6   v
1289      * </blockquote>
1290      */
1291     public static final int FLAG_USES_ALTERNATE_ORIENTATION = 512;
1292 
1293 
1294     /**
1295      * Ensure that the positions array exists and holds position data.
1296      * If the array is null, this allocates it and sets default positions.
1297      */
1298     private void initPositions() {
1299         if (positions == null) {
1300             setFRCTX();
1301 
1302             positions = new float[glyphs.length * 2 + 2];
1303 
1304             Point2D.Float trackPt = null;
1305             float track = getTracking(font);
1306             if (track != 0) {
1307                 track *= font.getSize2D();
1308                 trackPt = new Point2D.Float(track, 0); // advance delta
1309             }
1310 
1311             Point2D.Float pt = new Point2D.Float(0, 0);
1312             if (font.isTransformed()) {
1313                 AffineTransform at = font.getTransform();
1314                 at.transform(pt, pt);
1315                 positions[0] = pt.x;
1316                 positions[1] = pt.y;
1317 
1318                 if (trackPt != null) {
1319                     at.deltaTransform(trackPt, trackPt);
1320                 }
1321             }
1322             for (int i = 0, n = 2; i < glyphs.length; ++i, n += 2) {
1323                 getGlyphStrike(i).addDefaultGlyphAdvance(glyphs[i], pt);
1324                 if (trackPt != null) {
1325                     pt.x += trackPt.x;
1326                     pt.y += trackPt.y;
1327                 }
1328                 positions[n] = pt.x;
1329                 positions[n+1] = pt.y;
1330             }
1331         }
1332     }
1333 
1334     /**
1335      * OR newFlags with existing flags.  First computes existing flags if needed.
1336      */
1337     private void addFlags(int newflags) {
1338         flags = getLayoutFlags() | newflags;
1339     }
1340 
1341     /**
1342      * AND the complement of clearedFlags with existing flags.  First computes existing flags if needed.
1343      */
1344     private void clearFlags(int clearedFlags) {
1345         flags = getLayoutFlags() & ~clearedFlags;
1346     }
1347 
1348     // general utility methods
1349 
1350     // encapsulate the test to check whether we have per-glyph transforms
1351     private GlyphStrike getGlyphStrike(int ix) {
1352         if (gti == null) {
1353             return getDefaultStrike();
1354         } else {
1355             return gti.getStrike(ix);
1356         }
1357     }
1358 
1359     // encapsulate access to cached default glyph strike
1360     private GlyphStrike getDefaultStrike() {
1361         GlyphStrike gs = null;
1362         if (fsref != null) {
1363             gs = fsref.get();
1364         }
1365         if (gs == null) {
1366             gs = GlyphStrike.create(this, dtx, null);
1367             fsref = new SoftReference<>(gs);
1368         }
1369         return gs;
1370     }
1371 
1372 
1373     /////////////////////
1374     // Internal utility classes
1375     /////////////////////
1376 
1377     // !!! I have this as a separate class instead of just inside SGV,
1378     // but I previously didn't bother.  Now I'm trying this again.
1379     // Probably still not worth it, but I'd like to keep sgv's small in the common case.
1380 
1381     static final class GlyphTransformInfo {
1382         StandardGlyphVector sgv;  // reference back to glyph vector - yuck
1383         int[] indices;            // index into unique strikes
1384         double[] transforms;      // six doubles per unique transform, because AT is a pain to manipulate
1385         SoftReference<GlyphStrike[]> strikesRef; // ref to unique strikes, one per transform
1386         boolean haveAllStrikes;   // true if the strike array has been filled by getStrikes().
1387 
1388         // used when first setting a transform
1389         GlyphTransformInfo(StandardGlyphVector sgv) {
1390             this.sgv = sgv;
1391         }
1392 
1393         // used when cloning a glyph vector, need to set back link
1394         GlyphTransformInfo(StandardGlyphVector sgv, GlyphTransformInfo rhs) {
1395             this.sgv = sgv;
1396 
1397             this.indices = rhs.indices == null ? null : rhs.indices.clone();
1398             this.transforms = rhs.transforms == null ? null : rhs.transforms.clone();
1399             this.strikesRef = null; // can't share cache, so rather than clone, we just null out
1400         }
1401 
1402         // used in sgv equality
1403         public boolean equals(GlyphTransformInfo rhs) {
1404             if (rhs == null) {
1405                 return false;
1406             }
1407             if (rhs == this) {
1408                 return true;
1409             }
1410             if (this.indices.length != rhs.indices.length) {
1411                 return false;
1412             }
1413             if (this.transforms.length != rhs.transforms.length) {
1414                 return false;
1415             }
1416 
1417             // slow since we end up processing the same transforms multiple
1418             // times, but since transforms can be in any order, we either do
1419             // this or create a mapping.  Equality tests aren't common so
1420             // leave it like this.
1421             for (int i = 0; i < this.indices.length; ++i) {
1422                 int tix = this.indices[i];
1423                 int rix = rhs.indices[i];
1424                 if ((tix == 0) != (rix == 0)) {
1425                     return false;
1426                 }
1427                 if (tix != 0) {
1428                     tix *= 6;
1429                     rix *= 6;
1430                     for (int j = 6; j > 0; --j) {
1431                         if (this.indices[--tix] != rhs.indices[--rix]) {
1432                             return false;
1433                         }
1434                     }
1435                 }
1436             }
1437             return true;
1438         }
1439 
1440         // implements sgv.setGlyphTransform
1441         void setGlyphTransform(int glyphIndex, AffineTransform newTX) {
1442 
1443             // we store all the glyph transforms as a double array, and for each glyph there
1444             // is an entry in the txIndices array indicating which transform to use.  0 means
1445             // there's no transform, 1 means use the first transform (the 6 doubles at offset
1446             // 0), 2 means use the second transform (the 6 doubles at offset 6), etc.
1447             //
1448             // Since this can be called multiple times, and since the number of transforms
1449             // affects the time it takes to construct the glyphs, we try to keep the arrays as
1450             // compact as possible, by removing transforms that are no longer used, and reusing
1451             // transforms where we already have them.
1452 
1453             double[] temp = new double[6];
1454             boolean isIdentity = true;
1455             if (newTX == null || newTX.isIdentity()) {
1456                 // Fill in temp
1457                 temp[0] = temp[3] = 1.0;
1458             }
1459             else {
1460                 isIdentity = false;
1461                 newTX.getMatrix(temp);
1462             }
1463 
1464             if (indices == null) {
1465                 if (isIdentity) { // no change
1466                     return;
1467                 }
1468 
1469                 indices = new int[sgv.glyphs.length];
1470                 indices[glyphIndex] = 1;
1471                 transforms = temp;
1472             } else {
1473                 boolean addSlot = false; // assume we're not growing
1474                 int newIndex = -1;
1475                 if (isIdentity) {
1476                     newIndex = 0; // might shrink
1477                 } else {
1478                     addSlot = true; // assume no match
1479                     int i;
1480                 loop:
1481                     for (i = 0; i < transforms.length; i += 6) {
1482                         for (int j = 0; j < 6; ++j) {
1483                             if (transforms[i + j] != temp[j]) {
1484                                 continue loop;
1485                             }
1486                         }
1487                         addSlot = false;
1488                         break;
1489                     }
1490                     newIndex = i / 6 + 1; // if no match, end of list
1491                 }
1492 
1493                 // if we're using the same transform, nothing to do
1494                 int oldIndex = indices[glyphIndex];
1495                 if (newIndex != oldIndex) {
1496                     // see if we are removing last use of the old slot
1497                     boolean removeSlot = false;
1498                     if (oldIndex != 0) {
1499                         removeSlot = true;
1500                         for (int i = 0; i < indices.length; ++i) {
1501                             if (indices[i] == oldIndex && i != glyphIndex) {
1502                                 removeSlot = false;
1503                                 break;
1504                             }
1505                         }
1506                     }
1507 
1508                     if (removeSlot && addSlot) { // reuse old slot with new transform
1509                         newIndex = oldIndex;
1510                         System.arraycopy(temp, 0, transforms, (newIndex - 1) * 6, 6);
1511                     } else if (removeSlot) {
1512                         if (transforms.length == 6) { // removing last one, so clear arrays
1513                             indices = null;
1514                             transforms = null;
1515 
1516                             sgv.clearCaches(glyphIndex);
1517                             sgv.clearFlags(FLAG_HAS_TRANSFORMS);
1518                             strikesRef = null;
1519 
1520                             return;
1521                         }
1522 
1523                         double[] ttemp = new double[transforms.length - 6];
1524                         System.arraycopy(transforms, 0, ttemp, 0, (oldIndex - 1) * 6);
1525                         System.arraycopy(transforms, oldIndex * 6, ttemp, (oldIndex - 1) * 6,
1526                                          transforms.length - oldIndex * 6);
1527                         transforms = ttemp;
1528 
1529                         // clean up indices
1530                         for (int i = 0; i < indices.length; ++i) {
1531                             if (indices[i] > oldIndex) { // ignore == oldIndex, it's going away
1532                                 indices[i] -= 1;
1533                             }
1534                         }
1535                         if (newIndex > oldIndex) { // don't forget to decrement this too if we need to
1536                             --newIndex;
1537                         }
1538                     } else if (addSlot) {
1539                         double[] ttemp = new double[transforms.length + 6];
1540                         System.arraycopy(transforms, 0, ttemp, 0, transforms.length);
1541                         System.arraycopy(temp, 0, ttemp, transforms.length, 6);
1542                         transforms = ttemp;
1543                     }
1544 
1545                     indices[glyphIndex] = newIndex;
1546                 }
1547             }
1548 
1549             sgv.clearCaches(glyphIndex);
1550             sgv.addFlags(FLAG_HAS_TRANSFORMS);
1551             strikesRef = null;
1552         }
1553 
1554         // implements sgv.getGlyphTransform
1555         AffineTransform getGlyphTransform(int ix) {
1556             int index = indices[ix];
1557             if (index == 0) {
1558                 return null;
1559             }
1560 
1561             int x = (index - 1) * 6;
1562             return new AffineTransform(transforms[x + 0],
1563                                        transforms[x + 1],
1564                                        transforms[x + 2],
1565                                        transforms[x + 3],
1566                                        transforms[x + 4],
1567                                        transforms[x + 5]);
1568         }
1569 
1570         int transformCount() {
1571             if (transforms == null) {
1572                 return 0;
1573             }
1574             return transforms.length / 6;
1575         }
1576 
1577         /**
1578          * The strike cache works like this.
1579          *
1580          * -Each glyph is thought of as having a transform, usually identity.
1581          * -Each request for a strike is based on a device transform, either the
1582          * one in the frc or the rendering transform.
1583          * -For general info, strikes are held with soft references.
1584          * -When rendering, strikes must be held with hard references for the
1585          * duration of the rendering call.  GlyphList will have to hold this
1586          * info along with the image and position info, but toss the strike info
1587          * when done.
1588          * -Build the strike cache as needed.  If the dev transform we want to use
1589          * has changed from the last time it is built, the cache is flushed by
1590          * the caller before these methods are called.
1591          *
1592          * Use a tx that doesn't include translation components of dst tx.
1593          */
1594         Object setupGlyphImages(long[] images, float[] positions, AffineTransform tx) {
1595             int len = sgv.glyphs.length;
1596 
1597             GlyphStrike[] sl = getAllStrikes();
1598             for (int i = 0; i < len; ++i) {
1599                 GlyphStrike gs = sl[indices[i]];
1600                 int glyphID = sgv.glyphs[i];
1601                 images[i] = gs.strike.getGlyphImagePtr(glyphID);
1602 
1603                 gs.getGlyphPosition(glyphID, i*2, sgv.positions, positions);
1604             }
1605             tx.transform(positions, 0, positions, 0, len);
1606 
1607             return sl;
1608         }
1609 
1610         Rectangle getGlyphsPixelBounds(AffineTransform tx, float x, float y, int start, int count) {
1611             Rectangle result = null;
1612             Rectangle r = new Rectangle();
1613             Point2D.Float pt = new Point.Float();
1614             int n = start * 2;
1615             while (--count >= 0) {
1616                 GlyphStrike gs = getStrike(start);
1617                 pt.x = x + sgv.positions[n++] + gs.dx;
1618                 pt.y = y + sgv.positions[n++] + gs.dy;
1619                 tx.transform(pt, pt);
1620                 gs.strike.getGlyphImageBounds(sgv.glyphs[start++], pt, r);
1621                 if (!r.isEmpty()) {
1622                     if (result == null) {
1623                         result = new Rectangle(r);
1624                     } else {
1625                         result.add(r);
1626                     }
1627                 }
1628             }
1629             return result != null ? result : r;
1630         }
1631 
1632         GlyphStrike getStrike(int glyphIndex) {
1633             if (indices != null) {
1634                 GlyphStrike[] strikes = getStrikeArray();
1635                 return getStrikeAtIndex(strikes, indices[glyphIndex]);
1636             }
1637             return sgv.getDefaultStrike();
1638         }
1639 
1640         private GlyphStrike[] getAllStrikes() {
1641             if (indices == null) {
1642                 return null;
1643             }
1644 
1645             GlyphStrike[] strikes = getStrikeArray();
1646             if (!haveAllStrikes) {
1647                 for (int i = 0; i < strikes.length; ++i) {
1648                     getStrikeAtIndex(strikes, i);
1649                 }
1650                 haveAllStrikes = true;
1651             }
1652 
1653             return strikes;
1654         }
1655 
1656         private GlyphStrike[] getStrikeArray() {
1657             GlyphStrike[] strikes = null;
1658             if (strikesRef != null) {
1659                 strikes = strikesRef.get();
1660             }
1661             if (strikes == null) {
1662                 haveAllStrikes = false;
1663                 strikes = new GlyphStrike[transformCount() + 1];
1664                 strikesRef = new SoftReference<>(strikes);
1665             }
1666 
1667             return strikes;
1668         }
1669 
1670         private GlyphStrike getStrikeAtIndex(GlyphStrike[] strikes, int strikeIndex) {
1671             GlyphStrike strike = strikes[strikeIndex];
1672             if (strike == null) {
1673                 if (strikeIndex == 0) {
1674                     strike = sgv.getDefaultStrike();
1675                 } else {
1676                     int ix = (strikeIndex - 1) * 6;
1677                     AffineTransform gtx = new AffineTransform(transforms[ix],
1678                                                               transforms[ix+1],
1679                                                               transforms[ix+2],
1680                                                               transforms[ix+3],
1681                                                               transforms[ix+4],
1682                                                               transforms[ix+5]);
1683 
1684                     strike = GlyphStrike.create(sgv, sgv.dtx, gtx);
1685                 }
1686                 strikes[strikeIndex] = strike;
1687             }
1688             return strike;
1689         }
1690     }
1691 
1692     // This adjusts the metrics by the translation components of the glyph
1693     // transform.  It is done here since the translation is not known by the
1694     // strike.
1695     // It adjusts the position of the image and the advance.
1696 
1697     public static final class GlyphStrike {
1698         StandardGlyphVector sgv;
1699         FontStrike strike; // hard reference
1700         float dx;
1701         float dy;
1702 
1703         static GlyphStrike create(StandardGlyphVector sgv, AffineTransform dtx, AffineTransform gtx) {
1704             float dx = 0;
1705             float dy = 0;
1706 
1707             AffineTransform tx = sgv.ftx;
1708             if (!dtx.isIdentity() || gtx != null) {
1709                 tx = new AffineTransform(sgv.ftx);
1710                 if (gtx != null) {
1711                     tx.preConcatenate(gtx);
1712                     dx = (float)tx.getTranslateX(); // uses ftx then gtx to get translation
1713                     dy = (float)tx.getTranslateY();
1714                 }
1715                 if (!dtx.isIdentity()) {
1716                     tx.preConcatenate(dtx);
1717                 }
1718             }
1719 
1720             int ptSize = 1; // only matters for 'gasp' case.
1721             Object aaHint = sgv.frc.getAntiAliasingHint();
1722             if (aaHint == VALUE_TEXT_ANTIALIAS_GASP) {
1723                 /* Must pass in the calculated point size for rendering.
1724                  * If the glyph tx is anything other than identity or a
1725                  *  simple translate, calculate the transformed point size.
1726                  */
1727                 if (!tx.isIdentity() &&
1728                     (tx.getType() & ~AffineTransform.TYPE_TRANSLATION) != 0) {
1729                     double shearx = tx.getShearX();
1730                     if (shearx != 0) {
1731                         double scaley = tx.getScaleY();
1732                         ptSize =
1733                             (int)Math.sqrt(shearx * shearx + scaley * scaley);
1734                     } else {
1735                         ptSize = (int)(Math.abs(tx.getScaleY()));
1736                     }
1737                 }
1738             }
1739             int aa = FontStrikeDesc.getAAHintIntVal(aaHint,sgv.font2D, ptSize);
1740             int fm = FontStrikeDesc.getFMHintIntVal
1741                 (sgv.frc.getFractionalMetricsHint());
1742             FontStrikeDesc desc = new FontStrikeDesc(dtx,
1743                                                      tx,
1744                                                      sgv.font.getStyle(),
1745                                                      aa, fm);
1746             // Get the strike via the handle. Shouldn't matter
1747             // if we've invalidated the font but its an extra precaution.
1748         // do we want the CompFont from CFont here ?
1749         Font2D f2d = sgv.font2D;
1750         if (f2d instanceof FontSubstitution) {
1751            f2d = ((FontSubstitution)f2d).getCompositeFont2D();
1752         }
1753             FontStrike strike = f2d.handle.font2D.getStrike(desc);  // !!! getStrike(desc, false)
1754 
1755             return new GlyphStrike(sgv, strike, dx, dy);
1756         }
1757 
1758         private GlyphStrike(StandardGlyphVector sgv, FontStrike strike, float dx, float dy) {
1759             this.sgv = sgv;
1760             this.strike = strike;
1761             this.dx = dx;
1762             this.dy = dy;
1763         }
1764 
1765         void getADL(ADL result) {
1766             StrikeMetrics sm = strike.getFontMetrics();
1767             Point2D.Float delta = null;
1768             if (sgv.font.isTransformed()) {
1769                 delta = new Point2D.Float();
1770                 delta.x = (float)sgv.font.getTransform().getTranslateX();
1771                 delta.y = (float)sgv.font.getTransform().getTranslateY();
1772             }
1773 
1774             result.ascentX = -sm.ascentX;
1775             result.ascentY = -sm.ascentY;
1776             result.descentX = sm.descentX;
1777             result.descentY = sm.descentY;
1778             result.leadingX = sm.leadingX;
1779             result.leadingY = sm.leadingY;
1780         }
1781 
1782         void getGlyphPosition(int glyphID, int ix, float[] positions, float[] result) {
1783             result[ix] = positions[ix] + dx;
1784             ++ix;
1785             result[ix] = positions[ix] + dy;
1786         }
1787 
1788         void addDefaultGlyphAdvance(int glyphID, Point2D.Float result) {
1789             // !!! change this API?  Creates unnecessary garbage.  Also the name doesn't quite fit.
1790             // strike.addGlyphAdvance(Point2D.Float adv);  // hey, whaddya know, matches my api :-)
1791             Point2D.Float adv = strike.getGlyphMetrics(glyphID);
1792             result.x += adv.x + dx;
1793             result.y += adv.y + dy;
1794         }
1795 
1796         Rectangle2D getGlyphOutlineBounds(int glyphID, float x, float y) {
1797             Rectangle2D result = null;
1798             if (sgv.invdtx == null) {
1799                 result = new Rectangle2D.Float();
1800                 result.setRect(strike.getGlyphOutlineBounds(glyphID)); // don't mutate cached rect
1801             } else {
1802                 GeneralPath gp = strike.getGlyphOutline(glyphID, 0, 0);
1803                 gp.transform(sgv.invdtx);
1804                 result = gp.getBounds2D();
1805             }
1806             /* Since x is the logical advance of the glyph to this point.
1807              * Because of the way that Rectangle.union is specified, this
1808              * means that subsequent unioning of a rect including that
1809              * will be affected, even if the glyph is empty. So skip such
1810              * cases. This alone isn't a complete solution since x==0
1811              * may also not be what is wanted. The code that does the
1812              * unioning also needs to be aware to ignore empty glyphs.
1813              */
1814             if (!result.isEmpty()) {
1815                 result.setRect(result.getMinX() + x + dx,
1816                                result.getMinY() + y + dy,
1817                                result.getWidth(), result.getHeight());
1818             }
1819             return result;
1820         }
1821 
1822         void appendGlyphOutline(int glyphID, GeneralPath result, float x, float y) {
1823             // !!! fontStrike needs a method for this.  For that matter, GeneralPath does.
1824             GeneralPath gp = null;
1825             if (sgv.invdtx == null) {
1826                 gp = strike.getGlyphOutline(glyphID, x + dx, y + dy);
1827             } else {
1828                 gp = strike.getGlyphOutline(glyphID, 0, 0);
1829                 gp.transform(sgv.invdtx);
1830                 gp.transform(AffineTransform.getTranslateInstance(x + dx, y + dy));
1831             }
1832             PathIterator iterator = gp.getPathIterator(null);
1833             result.append(iterator, false);
1834         }
1835     }
1836 
1837     public String toString() {
1838         return appendString(null).toString();
1839     }
1840 
1841     StringBuffer appendString(StringBuffer buf) {
1842         if (buf == null) {
1843             buf = new StringBuffer();
1844         }
1845         try {
1846             buf.append("SGV{font: ");
1847             buf.append(font.toString());
1848             buf.append(", frc: ");
1849             buf.append(frc.toString());
1850             buf.append(", glyphs: (");
1851             buf.append(glyphs.length);
1852             buf.append(")[");
1853             for (int i = 0; i < glyphs.length; ++i) {
1854                 if (i > 0) {
1855                     buf.append(", ");
1856                 }
1857                 buf.append(Integer.toHexString(glyphs[i]));
1858             }
1859             buf.append("]");
1860             if (positions != null) {
1861                 buf.append(", positions: (");
1862                 buf.append(positions.length);
1863                 buf.append(")[");
1864                 for (int i = 0; i < positions.length; i += 2) {
1865                     if (i > 0) {
1866                         buf.append(", ");
1867                     }
1868                     buf.append(positions[i]);
1869                     buf.append("@");
1870                     buf.append(positions[i+1]);
1871                 }
1872                 buf.append("]");
1873             }
1874             if (charIndices != null) {
1875                 buf.append(", indices: (");
1876                 buf.append(charIndices.length);
1877                 buf.append(")[");
1878                 for (int i = 0; i < charIndices.length; ++i) {
1879                     if (i > 0) {
1880                         buf.append(", ");
1881                     }
1882                     buf.append(charIndices[i]);
1883                 }
1884                 buf.append("]");
1885             }
1886             buf.append(", flags:");
1887             if (getLayoutFlags() == 0) {
1888                 buf.append(" default");
1889             } else {
1890                 if ((flags & FLAG_HAS_TRANSFORMS) != 0) {
1891                     buf.append(" tx");
1892                 }
1893                 if ((flags & FLAG_HAS_POSITION_ADJUSTMENTS) != 0) {
1894                     buf.append(" pos");
1895                 }
1896                 if ((flags & FLAG_RUN_RTL) != 0) {
1897                     buf.append(" rtl");
1898                 }
1899                 if ((flags & FLAG_COMPLEX_GLYPHS) != 0) {
1900                     buf.append(" complex");
1901                 }
1902             }
1903         }
1904         catch(Exception e) {
1905             buf.append(' ').append(e.getMessage());
1906         }
1907         buf.append('}');
1908 
1909         return buf;
1910     }
1911 
1912     static class ADL {
1913         public float ascentX;
1914         public float ascentY;
1915         public float descentX;
1916         public float descentY;
1917         public float leadingX;
1918         public float leadingY;
1919 
1920         public String toString() {
1921             return toStringBuffer(null).toString();
1922         }
1923 
1924         protected StringBuffer toStringBuffer(StringBuffer result) {
1925             if (result == null) {
1926                 result = new StringBuffer();
1927             }
1928             result.append("ax: ");
1929             result.append(ascentX);
1930             result.append(" ay: ");
1931             result.append(ascentY);
1932             result.append(" dx: ");
1933             result.append(descentX);
1934             result.append(" dy: ");
1935             result.append(descentY);
1936             result.append(" lx: ");
1937             result.append(leadingX);
1938             result.append(" ly: ");
1939             result.append(leadingY);
1940 
1941             return result;
1942         }
1943     }
1944 }
--- EOF ---