1 /*
   2  * Copyright 2003-2004 Sun Microsystems, Inc.  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.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 package sun.font;
  27 
  28 import java.awt.geom.AffineTransform;
  29 import java.awt.geom.GeneralPath;
  30 import java.awt.geom.Point2D;
  31 import java.awt.Rectangle;
  32 import java.awt.geom.Rectangle2D;
  33 import java.awt.geom.NoninvertibleTransformException;
  34 
  35  class NativeStrike extends PhysicalStrike {
  36 
  37      NativeFont nativeFont;
  38      int numGlyphs;
  39      AffineTransform invertDevTx;
  40      AffineTransform fontTx;
  41 
  42      /* The following method prepares data used in obtaining FontMetrics.
  43       * This is the one case in which we allow anything other than a
  44       * simple scale to be used with a native font. We do this because in
  45       * order to ensure that clients get the overall metrics they expect
  46       * for a font whatever coordinate system (combination of font and
  47       * device transform) they use.
  48       * X11 fonts can only have a scale applied (remind : non-uniform?)
  49       * We strip out everything else and if necessary obtain an inverse
  50       * tx which we use to return metrics for the font in the transformed
  51       * coordinate system of the font. ie we pass X11 a simple scale, and
  52       * then apply the non-scale part of the font TX to that result.
  53       */
  54      private int getNativePointSize() {
  55          /* Make a copy of the glyphTX in which we will store the
  56           * font transform, inverting the devTx if necessary
  57           */
  58          double[] mat = new double[4];
  59          desc.glyphTx.getMatrix(mat);
  60          fontTx = new AffineTransform(mat);
  61 
  62          /* Now work backwards to get the font transform */
  63          if (!desc.devTx.isIdentity() &&
  64              desc.devTx.getType() != AffineTransform.TYPE_TRANSLATION) {
  65              try {
  66                  invertDevTx = desc.devTx.createInverse();
  67                  fontTx.concatenate(invertDevTx);
  68              } catch (NoninvertibleTransformException e) {
  69                  e.printStackTrace();
  70              }
  71          }
  72 
  73          /* At this point the fontTx may be a simple +ve scale, or it
  74           * may be something more complex.
  75           */
  76          Point2D.Float pt = new Point2D.Float(1f,1f);
  77          fontTx.deltaTransform(pt, pt);
  78          double ptSize = Math.abs(pt.y);
  79          int ttype = fontTx.getType();
  80          if ((ttype & ~AffineTransform.TYPE_UNIFORM_SCALE) != 0 ||
  81              fontTx.getScaleY() <= 0) {
  82              /* We need to create an inverse transform that doesn't
  83               * include the point size (strictly the uniform scale)
  84               */
  85              fontTx.scale(1/ptSize, 1/ptSize);
  86          } else {
  87              fontTx = null; // no need
  88          }
  89          return (int)ptSize;
  90      }
  91 
  92      NativeStrike(NativeFont nativeFont, FontStrikeDesc desc) {
  93          super(nativeFont, desc);
  94          this.nativeFont = nativeFont;
  95 
  96 
  97          /* If this is a delegate for bitmaps, we expect to have
  98           * been invoked only for a simple scale. If that's not
  99           * true, just bail
 100           */
 101          if (nativeFont.isBitmapDelegate) {
 102              int ttype = desc.glyphTx.getType();
 103              if ((ttype & ~AffineTransform.TYPE_UNIFORM_SCALE) != 0 ||
 104                  desc.glyphTx.getScaleX() <= 0) {
 105              numGlyphs = 0;
 106              return;
 107              }
 108          }
 109 
 110          int ptSize = getNativePointSize();
 111          byte [] nameBytes = nativeFont.getPlatformNameBytes(ptSize);
 112          double scale = Math.abs(desc.devTx.getScaleX());
 113          pScalerContext = createScalerContext(nameBytes, ptSize, scale);
 114          if (pScalerContext == 0L) {
 115              FontManager.deRegisterBadFont(nativeFont);
 116              pScalerContext = createNullScalerContext();
 117              numGlyphs = 0;
 118              if (FontManager.logging) {
 119                  FontManager.logger.severe("Could not create native strike " +
 120                                            new String(nameBytes));
 121              }
 122              return;
 123          }
 124          numGlyphs = nativeFont.getMapper().getNumGlyphs();
 125          this.disposer = new NativeStrikeDisposer(nativeFont, desc,
 126                                                   pScalerContext);
 127      }
 128 
 129      /* The asymmetry of the following methods is to help preserve
 130       * performance with minimal textual changes to the calling code
 131       * when moving initialisation of these arrays out of the constructor.
 132       * This may be restructured later when there's more room for changes
 133       */
 134      private boolean usingIntGlyphImages() {
 135          if (intGlyphImages != null) {
 136             return true;
 137         } else if (FontManager.longAddresses) {
 138             return false;
 139         } else {
 140             /* We could obtain minGlyphIndex and index relative to that
 141              * if we need to save space.
 142              */
 143             int glyphLenArray = getMaxGlyph(pScalerContext);
 144 
 145             /* This shouldn't be necessary - its a precaution */
 146             if (glyphLenArray < numGlyphs) {
 147                 glyphLenArray = numGlyphs;
 148             }
 149             intGlyphImages = new int[glyphLenArray];
 150             this.disposer.intGlyphImages = intGlyphImages;
 151             return true;
 152         }
 153      }
 154 
 155      private long[] getLongGlyphImages() {
 156         if (longGlyphImages == null && FontManager.longAddresses) {
 157 
 158             /* We could obtain minGlyphIndex and index relative to that
 159              * if we need to save space.
 160              */
 161             int glyphLenArray = getMaxGlyph(pScalerContext);
 162 
 163             /* This shouldn't be necessary - its a precaution */
 164             if (glyphLenArray < numGlyphs) {
 165                 glyphLenArray = numGlyphs;
 166             }
 167             longGlyphImages = new long[glyphLenArray];
 168             this.disposer.longGlyphImages = longGlyphImages;
 169         }
 170         return longGlyphImages;
 171      }
 172 
 173      NativeStrike(NativeFont nativeFont, FontStrikeDesc desc,
 174                   boolean nocache) {
 175          super(nativeFont, desc);
 176          this.nativeFont = nativeFont;
 177 
 178          int ptSize = (int)desc.glyphTx.getScaleY();
 179          double scale = desc.devTx.getScaleX(); // uniform scale
 180          byte [] nameBytes = nativeFont.getPlatformNameBytes(ptSize);
 181          pScalerContext = createScalerContext(nameBytes, ptSize, scale);
 182 
 183          int numGlyphs = nativeFont.getMapper().getNumGlyphs();
 184      }
 185 
 186      /* We want the native font to be responsible for reporting the
 187       * font metrics, even if it often delegates to another font.
 188       * The code here isn't yet implementing exactly that. If the glyph
 189       * transform was something native couldn't handle, there's no native
 190       * context from which to obtain metrics. Need to revise this to obtain
 191       * the metrics and transform them. But currently in such a case it
 192       * gets the metrics from a different font - its glyph delegate font.
 193       */
 194      StrikeMetrics getFontMetrics() {
 195          if (strikeMetrics == null) {
 196              if (pScalerContext != 0) {
 197                  strikeMetrics = nativeFont.getFontMetrics(pScalerContext);
 198              }
 199              if (strikeMetrics != null && fontTx != null) {
 200                  strikeMetrics.convertToUserSpace(fontTx);
 201              }
 202          }
 203          return strikeMetrics;
 204      }
 205 
 206      private native long createScalerContext(byte[] nameBytes,
 207                                              int ptSize, double scale);
 208 
 209      private native int getMaxGlyph(long pScalerContext);
 210 
 211      private native long createNullScalerContext();
 212 
 213      void getGlyphImagePtrs(int[] glyphCodes, long[] images,int  len) {
 214          for (int i=0; i<len; i++) {
 215              images[i] = getGlyphImagePtr(glyphCodes[i]);
 216          }
 217      }
 218 
 219      long getGlyphImagePtr(int glyphCode) {
 220          long glyphPtr;
 221 
 222          if (usingIntGlyphImages()) {
 223              if ((glyphPtr = intGlyphImages[glyphCode] & INTMASK) != 0L) {
 224                  return glyphPtr;
 225              } else {
 226                  glyphPtr = nativeFont.getGlyphImage(pScalerContext,glyphCode);
 227                  /* Synchronize in case some other thread has updated this
 228                   * cache entry already - unlikely but possible.
 229                   */
 230                  synchronized (this) {
 231                      if (intGlyphImages[glyphCode] == 0) {
 232                          intGlyphImages[glyphCode] = (int)glyphPtr;
 233                          return glyphPtr;
 234                      } else {
 235                          StrikeCache.freeIntPointer((int)glyphPtr);
 236                          return intGlyphImages[glyphCode] & INTMASK;
 237                      }
 238                  }
 239              }
 240          }
 241          /* must be using long (8 byte) addresses */
 242          else if ((glyphPtr = getLongGlyphImages()[glyphCode]) != 0L) {
 243              return glyphPtr;
 244          } else {
 245              glyphPtr = nativeFont.getGlyphImage(pScalerContext, glyphCode);
 246 
 247              synchronized (this) {
 248                  if (longGlyphImages[glyphCode] == 0L) {
 249                      longGlyphImages[glyphCode] = glyphPtr;
 250                      return glyphPtr;
 251                  } else {
 252                      StrikeCache.freeLongPointer(glyphPtr);
 253                      return longGlyphImages[glyphCode];
 254                  }
 255              }
 256          }
 257      }
 258 
 259      /* This is used when a FileFont uses the native names to create a
 260       * delegate NativeFont/Strike to get images from native. This is used
 261       * because Solaris TrueType fonts have external PCF bitmaps rather than
 262       * embedded bitmaps. This is really only important for CJK fonts as
 263       * for most scripts the external X11 bitmaps aren't much better - if
 264       * at all - than the results from hinting the outlines.
 265       */
 266      long getGlyphImagePtrNoCache(int glyphCode) {
 267          return nativeFont.getGlyphImageNoDefault(pScalerContext, glyphCode);
 268      }
 269 
 270      void getGlyphImageBounds(int glyphcode, Point2D.Float pt,
 271                               Rectangle result) {
 272      }
 273 
 274      Point2D.Float getGlyphMetrics(int glyphCode) {
 275          Point2D.Float pt = new Point2D.Float(getGlyphAdvance(glyphCode), 0f);
 276          return pt;
 277      }
 278 
 279      float getGlyphAdvance(int glyphCode) {
 280          return nativeFont.getGlyphAdvance(pScalerContext, glyphCode);
 281      }
 282 
 283      Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {
 284          return nativeFont.getGlyphOutlineBounds(pScalerContext, glyphCode);
 285      }
 286 
 287      GeneralPath getGlyphOutline(int glyphCode, float x, float y) {
 288          return new GeneralPath();
 289      }
 290 
 291      GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {
 292          return new GeneralPath();
 293      }
 294 
 295 }
 296 
 297 /* Returned instead of a NativeStrike.
 298  * It can intercept any request it wants, but mostly
 299  * passes them on to its delegate strike. It is important that
 300  * it override all the inherited FontStrike methods to delegate them
 301  * appropriately.
 302  */
 303 
 304 class DelegateStrike extends NativeStrike {
 305 
 306     private FontStrike delegateStrike;
 307 
 308     DelegateStrike(NativeFont nativeFont, FontStrikeDesc desc,
 309                    FontStrike delegate) {
 310         super(nativeFont, desc);
 311         this.delegateStrike = delegate;
 312     }
 313 
 314     /* We want the native font to be responsible for reporting the
 315      * font metrics, even if it often delegates to another font.
 316      * The code here isn't yet implementing exactly that. If the glyph
 317      * transform was something native couldn't handle, there's no native
 318      * context from which to obtain metrics. Need to revise this to obtain
 319      * the metrics and transform them. But currently in such a case it
 320      * gets the metrics from a different font - its glyph delegate font.
 321      */
 322    StrikeMetrics getFontMetrics() {
 323        if (strikeMetrics == null) {
 324            if (pScalerContext != 0) {
 325                strikeMetrics = super.getFontMetrics();
 326            }
 327             if (strikeMetrics == null) {
 328                 strikeMetrics = delegateStrike.getFontMetrics();
 329             }
 330         }
 331         return strikeMetrics;
 332     }
 333 
 334     void getGlyphImagePtrs(int[] glyphCodes, long[] images,int  len) {
 335         delegateStrike.getGlyphImagePtrs(glyphCodes, images, len);
 336     }
 337 
 338     long getGlyphImagePtr(int glyphCode) {
 339         return delegateStrike.getGlyphImagePtr(glyphCode);
 340     }
 341 
 342     void getGlyphImageBounds(int glyphCode,
 343                              Point2D.Float pt, Rectangle result) {
 344         delegateStrike.getGlyphImageBounds(glyphCode, pt, result);
 345     }
 346 
 347     Point2D.Float getGlyphMetrics(int glyphCode) {
 348         return delegateStrike.getGlyphMetrics(glyphCode);
 349     }
 350 
 351     float getGlyphAdvance(int glyphCode) {
 352         return delegateStrike.getGlyphAdvance(glyphCode);
 353     }
 354 
 355      Point2D.Float getCharMetrics(char ch) {
 356         return delegateStrike.getCharMetrics(ch);
 357     }
 358 
 359     float getCodePointAdvance(int cp) {
 360         if (cp < 0 || cp >= 0x10000) {
 361             cp = 0xffff;
 362         }
 363         return delegateStrike.getGlyphAdvance(cp);
 364     }
 365 
 366     Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {
 367         return delegateStrike.getGlyphOutlineBounds(glyphCode);
 368     }
 369 
 370     GeneralPath getGlyphOutline(int glyphCode, float x, float y) {
 371         return delegateStrike.getGlyphOutline(glyphCode, x, y);
 372     }
 373 
 374     GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {
 375         return delegateStrike.getGlyphVectorOutline(glyphs, x, y);
 376     }
 377 
 378 }