1 /* 2 * Copyright (c) 2013, 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; 27 28 import java.util.HashMap; 29 import java.util.Map; 30 31 import com.sun.javafx.geom.Path2D; 32 import com.sun.javafx.geom.Point2D; 33 import com.sun.javafx.geom.Shape; 34 import com.sun.javafx.geom.transform.Affine2D; 35 import com.sun.javafx.geom.transform.BaseTransform; 36 import com.sun.javafx.scene.text.GlyphList; 37 38 public abstract class PrismFontStrike<T extends PrismFontFile> implements FontStrike { 39 private DisposerRecord disposer; 40 private T fontResource; 41 private Map<Integer,Glyph> glyphMap = new HashMap<Integer,Glyph>(); 42 private PrismMetrics metrics; 43 protected boolean drawShapes = false; 44 private float size; 45 private BaseTransform transform; 46 private int aaMode; 47 private FontStrikeDesc desc; 48 49 protected PrismFontStrike(T fontResource, 50 float size, BaseTransform tx, int aaMode, 51 FontStrikeDesc desc) { 52 53 this.fontResource = fontResource; 54 this.size = size; 55 this.desc = desc; 56 if (tx.isTranslateOrIdentity()) { 57 transform = BaseTransform.IDENTITY_TRANSFORM; 58 } else { 59 transform = new Affine2D(tx.getMxx(), tx.getMyx(), 60 tx.getMxy(), tx.getMyy(), 61 0f, 0f); 62 } 63 this.aaMode = calculateAAMode(aaMode); 64 } 65 66 protected int calculateAAMode(int aaMode) { 67 if (aaMode == FontResource.AA_GREYSCALE) return aaMode; 68 PrismFontFactory factory = PrismFontFactory.getFontFactory(); 69 return factory.isLCDTextSupported() ? aaMode : FontResource.AA_GREYSCALE; 70 } 71 72 DisposerRecord getDisposer() { 73 if (disposer == null) { 74 // Caller will arrange for the disposer to be enqueued. 75 // Strikes are partialy managed by a GlyphCache such that when it 76 // wants to free space there it calls back to remove the 77 // strike from a font's map. 78 // So we could instead arrange for synchronously freeing the resources 79 // at that time, in which case a disposer reference queue isn't needed. 80 // But the disposer is more certain (safer). 81 disposer = createDisposer(desc); 82 } 83 return disposer; 84 } 85 86 protected abstract DisposerRecord createDisposer(FontStrikeDesc desc); 87 88 public synchronized void clearDesc() { 89 fontResource.getStrikeMap().remove(desc); 90 // Native resources are freed via a disposer once we are sure 91 // all references are cleared. It also ensures we don't leak. 92 // So this is approach is not used right now, in favour of a disposer. 93 //T2KFontFile.freePointer(pScalerContext); 94 //pScalerContext = 0L; 95 } 96 97 /** 98 * Returns the notional size of this strike with 99 * the graphics transform factored out. This is presently 100 * needed for the J2D pipeline but arguably the strike should 101 * not even need to keep this around except for needing to 102 * return metrics and outlines in user space. The consequence is 103 * we can't share a strike between 12 pt at scale of 2.0 and 24 pt 104 * at scale of 1.0 105 */ 106 public float getSize() { 107 return size; 108 } 109 110 public Metrics getMetrics() { 111 // I don't need native code to do this .. it can be done 112 // by just reading the hhea table once for the font. This should 113 // save a JNI call per strike. 114 // T2K uses the hhea table. Maybe we should use OS/2 metrics 115 // but www.microsoft.com/typography/otspec/recom.htm#tad has 116 // a section on GDI baseline to baseline distance which shows it 117 // to be a wash if the usWin ascent and descent match, and in any 118 // case, clearly the hhea values are part of the calculation for 119 // leading. 120 if (metrics == null) { 121 metrics = fontResource.getFontMetrics(size); 122 } 123 return metrics; 124 } 125 126 public T getFontResource() { 127 return fontResource; 128 } 129 130 public boolean drawAsShapes() { 131 return drawShapes; 132 } 133 134 public int getAAMode() { 135 return aaMode; 136 } 137 138 public BaseTransform getTransform() { 139 return transform; 140 } 141 142 @Override 143 public int getQuantizedPosition(Point2D point) { 144 if (aaMode == FontResource.AA_GREYSCALE) { 145 /* No subpixel position */ 146 point.x = (float)Math.round(point.x); 147 } else { 148 /* Prism can produce 3 subpixel positions in the shader */ 149 point.x = (float)Math.round(3.0 * point.x)/ 3.0f; 150 } 151 point.y = (float)Math.round(point.y); 152 return 0; 153 } 154 155 /** 156 * Access to individual character advances are frequently needed for layout 157 * understand that advance may vary for single glyph if ligatures or kerning 158 * are enabled 159 * @param ch char 160 * @return advance of single char 161 */ 162 public float getCharAdvance(char ch) { 163 int glyphCode = fontResource.getGlyphMapper().charToGlyph((int)ch); 164 return fontResource.getAdvance(glyphCode, size); 165 } 166 167 /* REMIND A map is not the solution ultimately required here */ 168 public Glyph getGlyph(char ch) { 169 int glyphCode = fontResource.getGlyphMapper().charToGlyph((int)ch); 170 return getGlyph(glyphCode); 171 } 172 173 protected abstract Glyph createGlyph(int glyphCode); 174 175 public Glyph getGlyph(int glyphCode) { 176 Glyph glyph = glyphMap.get(glyphCode); 177 if (glyph == null) { 178 glyph = createGlyph(glyphCode); 179 glyphMap.put(glyphCode, glyph); 180 } 181 return glyph; 182 } 183 184 protected abstract Path2D createGlyphOutline(int glyphCode); 185 186 public Shape getOutline(GlyphList gl, BaseTransform transform) { 187 Path2D result = new Path2D(); 188 getOutline(gl, transform, result); 189 return result; 190 } 191 192 void getOutline(GlyphList gl, BaseTransform transform, Path2D p) { 193 p.reset(); 194 if (gl == null) { 195 return; 196 } 197 if (transform == null) { 198 transform = BaseTransform.IDENTITY_TRANSFORM; 199 } 200 Affine2D t = new Affine2D(); 201 for (int i = 0; i < gl.getGlyphCount(); i++) { 202 int glyphCode = gl.getGlyphCode(i); 203 if (glyphCode != CharToGlyphMapper.INVISIBLE_GLYPH_ID) { 204 Shape gp = createGlyphOutline(glyphCode); 205 if (gp != null) { 206 t.setTransform(transform); 207 t.translate(gl.getPosX(i), gl.getPosY(i)); 208 p.append(gp.getPathIterator(t), false); 209 } 210 } 211 } 212 } 213 214 @Override 215 public boolean equals(Object obj) { 216 if (obj == null) { 217 return false; 218 } 219 if (!(obj instanceof PrismFontStrike)) { 220 return false; 221 } 222 final PrismFontStrike other = (PrismFontStrike) obj; 223 224 // REMIND: When fonts can be rendered other than as greyscale 225 // and generally differ in ways other than the size 226 // we need to update this method. 227 return this.size == other.size && 228 this.transform.getMxx() == other.transform.getMxx() && 229 this.transform.getMxy() == other.transform.getMxy() && 230 this.transform.getMyx() == other.transform.getMyx() && 231 this.transform.getMyy() == other.transform.getMyy() && 232 this.fontResource.equals(other.fontResource); 233 } 234 235 private int hash; 236 @Override 237 public int hashCode() { 238 if (hash != 0) { 239 return hash; 240 } 241 hash = Float.floatToIntBits(size) + 242 Float.floatToIntBits((float)transform.getMxx()) + 243 Float.floatToIntBits((float)transform.getMyx()) + 244 Float.floatToIntBits((float)transform.getMxy()) + 245 Float.floatToIntBits((float)transform.getMyy()); 246 hash = 71 * hash + fontResource.hashCode(); 247 return hash; 248 } 249 250 public String toString() { 251 return "FontStrike: " + super.toString() + 252 " font resource = " + fontResource + 253 " size = " + size + 254 " matrix = " + transform; 255 } 256 }