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