1 /*
   2  * Copyright (c) 2013, 2015, 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 com.sun.javafx.font.freetype;
  27 
  28 import com.sun.javafx.font.Disposer;
  29 import com.sun.javafx.font.FontResource;
  30 import com.sun.javafx.font.FontStrikeDesc;
  31 import com.sun.javafx.font.PrismFontFactory;
  32 import com.sun.javafx.font.PrismFontFile;
  33 import com.sun.javafx.font.PrismFontStrike;
  34 import com.sun.javafx.geom.Path2D;
  35 import com.sun.javafx.geom.transform.BaseTransform;
  36 
  37 class FTFontFile extends PrismFontFile {
  38     /*
  39      * Font files can be accessed by several threads. In general this are the
  40      * JFX thread (measuring) and the Prism thread (rendering). But, if a Text
  41      * node is no connected a Scene it can be used in any user thread, thus a
  42      * font resource can be accessed from any thread.
  43      *
  44      * Freetype resources are not thread safe. In this implementation each font
  45      * resources has its own FT_Face and FT_Library, and while it permits these
  46      * resources be used by different threads it does not allow concurrent access.
  47      * This is enforced by converging all operation (from FTFontStrike and
  48      * FTGlyph) to this object and synchronizing the access to the native
  49      * resources using the same lock.
  50      */
  51     private long library;
  52     private long face;
  53     private FTDisposer disposer;
  54 
  55     FTFontFile(String name, String filename, int fIndex, boolean register,
  56                boolean embedded, boolean copy, boolean tracked) throws Exception {
  57         super(name, filename, fIndex, register, embedded, copy, tracked);
  58         init();
  59     }
  60 
  61     private synchronized void init() throws Exception {
  62         long[] ptr = new long[1];
  63         int error = OSFreetype.FT_Init_FreeType(ptr);
  64         if (error != 0) {
  65             throw new Exception("FT_Init_FreeType Failed error " + error);
  66         }
  67         library = ptr[0];
  68         if (FTFactory.LCD_SUPPORT) {
  69             OSFreetype.FT_Library_SetLcdFilter(library, OSFreetype.FT_LCD_FILTER_DEFAULT);
  70         }
  71 
  72         String file = getFileName();
  73         int fontIndex = getFontIndex();
  74         /* Freetype expects 'a standard C string' */
  75         byte[] buffer = (file+"\0").getBytes();
  76         error = OSFreetype.FT_New_Face(library, buffer, fontIndex, ptr);
  77         if (error != 0) {
  78             throw new Exception("FT_New_Face Failed error " + error +
  79                                 " Font File " + file +
  80                                 " Font Index " + fontIndex);
  81         }
  82         face = ptr[0];
  83 
  84         if (!isRegistered()) {
  85             disposer = new FTDisposer(library, face);
  86             Disposer.addRecord(this, disposer);
  87         }
  88     }
  89 
  90     @Override
  91     protected PrismFontStrike<?> createStrike(float size, BaseTransform transform,
  92                                               int aaMode, FontStrikeDesc desc) {
  93         return new FTFontStrike(this, size, transform, aaMode, desc);
  94     }
  95 
  96     @Override
  97     protected synchronized int[] createGlyphBoundingBox(int gc) {
  98         int flags = OSFreetype.FT_LOAD_NO_SCALE;
  99         OSFreetype.FT_Load_Glyph(face, gc, flags);
 100         int[] bbox = new int[4];
 101         FT_GlyphSlotRec glyphRec = OSFreetype.getGlyphSlot(face);
 102         if (glyphRec != null && glyphRec.metrics != null) {
 103             FT_Glyph_Metrics gm = glyphRec.metrics;
 104             bbox[0] = (int)gm.horiBearingX;
 105             bbox[1] = (int)(gm.horiBearingY - gm.height);
 106             bbox[2] = (int)(gm.horiBearingX + gm.width);
 107             bbox[3] = (int)gm.horiBearingY;
 108         }
 109         return bbox;
 110     }
 111 
 112     synchronized Path2D createGlyphOutline(int gc, float size) {
 113         int size26dot6 = (int)(size * 64);
 114         OSFreetype.FT_Set_Char_Size(face, 0, size26dot6, 72, 72);
 115         int flags = OSFreetype.FT_LOAD_NO_HINTING | OSFreetype.FT_LOAD_NO_BITMAP | OSFreetype.FT_LOAD_IGNORE_TRANSFORM;
 116         OSFreetype.FT_Load_Glyph(face, gc, flags);
 117         return OSFreetype.FT_Outline_Decompose(face);
 118     }
 119 
 120     synchronized void initGlyph(FTGlyph glyph, FTFontStrike strike) {
 121         float size = strike.getSize();
 122         if (size == 0) {
 123             glyph.buffer = new byte[0];
 124             glyph.bitmap = new FT_Bitmap();
 125             return;
 126         }
 127         int size26dot6 = (int)(size * 64);
 128         OSFreetype.FT_Set_Char_Size(face, 0, size26dot6, 72, 72);
 129 
 130         boolean lcd = strike.getAAMode() == FontResource.AA_LCD &&
 131                       FTFactory.LCD_SUPPORT;
 132 
 133         int flags = OSFreetype.FT_LOAD_RENDER | OSFreetype.FT_LOAD_NO_HINTING | OSFreetype.FT_LOAD_NO_BITMAP;
 134         FT_Matrix matrix = strike.matrix;
 135         if (matrix != null) {
 136             OSFreetype.FT_Set_Transform(face, matrix, 0, 0);
 137         } else {
 138             flags |= OSFreetype.FT_LOAD_IGNORE_TRANSFORM;
 139         }
 140         if (lcd) {
 141             flags |= OSFreetype.FT_LOAD_TARGET_LCD;
 142         } else {
 143             flags |= OSFreetype.FT_LOAD_TARGET_NORMAL;
 144         }
 145 
 146         int glyphCode = glyph.getGlyphCode();
 147         int error = OSFreetype.FT_Load_Glyph(face, glyphCode, flags);
 148         if (error != 0) {
 149             if (PrismFontFactory.debugFonts) {
 150                 System.err.println("FT_Load_Glyph failed " + error +
 151                                    " glyph code " + glyphCode +
 152                                    " load falgs " + flags);
 153             }
 154             return;
 155         }
 156 
 157         FT_GlyphSlotRec glyphRec = OSFreetype.getGlyphSlot(face);
 158         if (glyphRec == null) return;
 159         FT_Bitmap bitmap = glyphRec.bitmap;
 160         if (bitmap == null) return;
 161         int pixelMode = bitmap.pixel_mode;
 162         int width = bitmap.width;
 163         int height = bitmap.rows;
 164         int pitch = bitmap.pitch;
 165         if (pixelMode != OSFreetype.FT_PIXEL_MODE_GRAY && pixelMode != OSFreetype.FT_PIXEL_MODE_LCD) {
 166             /* This procedure only requests FT_RENDER_MODE_NORMAL and FT_RENDER_MODE_LCD,
 167              * and for its output is expects FT_PIXEL_MODE_GRAY and FT_PIXEL_MODE_LCD, respectively.
 168              * But it is possible the requested rendering mode is not supported and a different
 169              * output is returned by Freetype. For example, a FT_PIXEL_MODE_MONO can be returned
 170              * if the font contains a bitmap for the given glyph code.
 171              */
 172             if (PrismFontFactory.debugFonts) {
 173                 System.err.println("Unexpected pixel mode: " + pixelMode +
 174                                    " glyph code " + glyphCode +
 175                                    " load falgs " + flags);
 176             }
 177             return;
 178         }
 179         byte[] buffer;
 180         if (width != 0 && height != 0) {
 181             buffer = OSFreetype.getBitmapData(face);
 182             if (buffer != null && pitch != width) {
 183                 /* Common for LCD glyphs */
 184                 byte[] newBuffer = new byte[width * height];
 185                 int src = 0, dst = 0;
 186                 for (int y = 0; y < height; y++) {
 187                     for (int x = 0; x < width; x++) {
 188                         newBuffer[dst + x] = buffer[src + x];
 189                     }
 190                     dst += width;
 191                     src += pitch;
 192                 }
 193                 buffer = newBuffer;
 194             }
 195         } else {
 196             /* white space */
 197             buffer = new byte[0];
 198         }
 199 
 200         glyph.buffer = buffer;
 201         glyph.bitmap = bitmap;
 202         glyph.bitmap_left = glyphRec.bitmap_left;
 203         glyph.bitmap_top = glyphRec.bitmap_top;
 204         glyph.advanceX = glyphRec.advance_x / 64f;    /* Fixed 26.6*/
 205         glyph.advanceY = glyphRec.advance_y / 64f;
 206         glyph.userAdvance = glyphRec.linearHoriAdvance / 65536.0f; /* Fixed 16.16 */
 207         glyph.lcd = lcd;
 208     }
 209 }