1 /*
   2  * Copyright (c) 2003, 2020, 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.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              SunFontManager.getInstance().deRegisterBadFont(nativeFont);
 116              pScalerContext = createNullScalerContext();
 117              numGlyphs = 0;
 118              FontUtilities.logSevere("Could not create native strike " +
 119                                      new String(nameBytes));
 120              return;
 121          }
 122          numGlyphs = nativeFont.getMapper().getNumGlyphs();
 123          this.disposer = new NativeStrikeDisposer(nativeFont, desc,
 124                                                   pScalerContext);
 125      }
 126 
 127      /* The asymmetry of the following methods is to help preserve
 128       * performance with minimal textual changes to the calling code
 129       * when moving initialisation of these arrays out of the constructor.
 130       * This may be restructured later when there's more room for changes
 131       */
 132      private boolean usingIntGlyphImages() {
 133          if (intGlyphImages != null) {
 134             return true;
 135         } else if (longAddresses) {
 136             return false;
 137         } else {
 138             /* We could obtain minGlyphIndex and index relative to that
 139              * if we need to save space.
 140              */
 141             int glyphLenArray = getMaxGlyph(pScalerContext);
 142 
 143             /* This shouldn't be necessary - its a precaution */
 144             if (glyphLenArray < numGlyphs) {
 145                 glyphLenArray = numGlyphs;
 146             }
 147             intGlyphImages = new int[glyphLenArray];
 148             this.disposer.intGlyphImages = intGlyphImages;
 149             return true;
 150         }
 151      }
 152 
 153      private long[] getLongGlyphImages() {
 154         if (longGlyphImages == null && longAddresses) {
 155 
 156             /* We could obtain minGlyphIndex and index relative to that
 157              * if we need to save space.
 158              */
 159             int glyphLenArray = getMaxGlyph(pScalerContext);
 160 
 161             /* This shouldn't be necessary - its a precaution */
 162             if (glyphLenArray < numGlyphs) {
 163                 glyphLenArray = numGlyphs;
 164             }
 165             longGlyphImages = new long[glyphLenArray];
 166             this.disposer.longGlyphImages = longGlyphImages;
 167         }
 168         return longGlyphImages;
 169      }
 170 
 171      NativeStrike(NativeFont nativeFont, FontStrikeDesc desc,
 172                   boolean nocache) {
 173          super(nativeFont, desc);
 174          this.nativeFont = nativeFont;
 175 
 176          int ptSize = (int)desc.glyphTx.getScaleY();
 177          double scale = desc.devTx.getScaleX(); // uniform scale
 178          byte [] nameBytes = nativeFont.getPlatformNameBytes(ptSize);
 179          pScalerContext = createScalerContext(nameBytes, ptSize, scale);
 180 
 181          int numGlyphs = nativeFont.getMapper().getNumGlyphs();
 182      }
 183 
 184      /* We want the native font to be responsible for reporting the
 185       * font metrics, even if it often delegates to another font.
 186       * The code here isn't yet implementing exactly that. If the glyph
 187       * transform was something native couldn't handle, there's no native
 188       * context from which to obtain metrics. Need to revise this to obtain
 189       * the metrics and transform them. But currently in such a case it
 190       * gets the metrics from a different font - its glyph delegate font.
 191       */
 192      StrikeMetrics getFontMetrics() {
 193          if (strikeMetrics == null) {
 194              if (pScalerContext != 0) {
 195                  strikeMetrics = nativeFont.getFontMetrics(pScalerContext);
 196              }
 197              if (strikeMetrics != null && fontTx != null) {
 198                  strikeMetrics.convertToUserSpace(fontTx);
 199              }
 200          }
 201          return strikeMetrics;
 202      }
 203 
 204      private native long createScalerContext(byte[] nameBytes,
 205                                              int ptSize, double scale);
 206 
 207      private native int getMaxGlyph(long pScalerContext);
 208 
 209      private native long createNullScalerContext();
 210 
 211      void getGlyphImagePtrs(int[] glyphCodes, long[] images,int  len) {
 212          for (int i=0; i<len; i++) {
 213              images[i] = getGlyphImagePtr(glyphCodes[i]);
 214          }
 215      }
 216 
 217      long getGlyphImagePtr(int glyphCode) {
 218          long glyphPtr;
 219 
 220          if (usingIntGlyphImages()) {
 221              if ((glyphPtr = intGlyphImages[glyphCode] & INTMASK) != 0L) {
 222                  return glyphPtr;
 223              } else {
 224                  glyphPtr = nativeFont.getGlyphImage(pScalerContext,glyphCode);
 225                  /* Synchronize in case some other thread has updated this
 226                   * cache entry already - unlikely but possible.
 227                   */
 228                  synchronized (this) {
 229                      if (intGlyphImages[glyphCode] == 0) {
 230                          intGlyphImages[glyphCode] = (int)glyphPtr;
 231                          return glyphPtr;
 232                      } else {
 233                          StrikeCache.freeIntPointer((int)glyphPtr);
 234                          return intGlyphImages[glyphCode] & INTMASK;
 235                      }
 236                  }
 237              }
 238          }
 239          /* must be using long (8 byte) addresses */
 240          else if ((glyphPtr = getLongGlyphImages()[glyphCode]) != 0L) {
 241              return glyphPtr;
 242          } else {
 243              glyphPtr = nativeFont.getGlyphImage(pScalerContext, glyphCode);
 244 
 245              synchronized (this) {
 246                  if (longGlyphImages[glyphCode] == 0L) {
 247                      longGlyphImages[glyphCode] = glyphPtr;
 248                      return glyphPtr;
 249                  } else {
 250                      StrikeCache.freeLongPointer(glyphPtr);
 251                      return longGlyphImages[glyphCode];
 252                  }
 253              }
 254          }
 255      }
 256 
 257      /* This is used when a FileFont uses the native names to create a
 258       * delegate NativeFont/Strike to get images from native. This is used
 259       * because Solaris TrueType fonts have external PCF bitmaps rather than
 260       * embedded bitmaps. This is really only important for CJK fonts as
 261       * for most scripts the external X11 bitmaps aren't much better - if
 262       * at all - than the results from hinting the outlines.
 263       */
 264      long getGlyphImagePtrNoCache(int glyphCode) {
 265          return nativeFont.getGlyphImageNoDefault(pScalerContext, glyphCode);
 266      }
 267 
 268      void getGlyphImageBounds(int glyphcode, Point2D.Float pt,
 269                               Rectangle result) {
 270      }
 271 
 272      Point2D.Float getGlyphMetrics(int glyphCode) {
 273          Point2D.Float pt = new Point2D.Float(getGlyphAdvance(glyphCode), 0f);
 274          return pt;
 275      }
 276 
 277      float getGlyphAdvance(int glyphCode) {
 278          return nativeFont.getGlyphAdvance(pScalerContext, glyphCode);
 279      }
 280 
 281      Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {
 282          return nativeFont.getGlyphOutlineBounds(pScalerContext, glyphCode);
 283      }
 284 
 285      GeneralPath getGlyphOutline(int glyphCode, float x, float y) {
 286          return new GeneralPath();
 287      }
 288 
 289      GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {
 290          return new GeneralPath();
 291      }
 292 
 293 }