1 /* 2 * Copyright (c) 2011, 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 sun.font; 27 28 import java.awt.Rectangle; 29 import java.awt.geom.*; 30 import java.util.*; 31 32 import sun.awt.SunHints; 33 34 public final class CStrike extends PhysicalStrike { 35 36 // Creates the native strike 37 private static native long createNativeStrikePtr(long nativeFontPtr, 38 double[] glyphTx, 39 double[] invDevTxMatrix, 40 int aaHint, 41 int fmHint); 42 43 // Disposes the native strike 44 private static native void disposeNativeStrikePtr(long nativeStrikePtr); 45 46 // Creates a StrikeMetrics from the underlying native system fonts 47 private static native StrikeMetrics getFontMetrics(long nativeStrikePtr); 48 49 // Returns native struct pointers used by the Sun 2D Renderer 50 private static native void getGlyphImagePtrsNative(long nativeStrikePtr, 51 long[] glyphInfos, 52 int[] uniCodes, int len); 53 54 // Returns the advance give a glyph code. It should be used only 55 // when the glyph code belongs to the CFont passed in. 56 private static native float getNativeGlyphAdvance(long nativeStrikePtr, 57 int glyphCode); 58 59 // Returns the outline shape of a glyph 60 private static native GeneralPath getNativeGlyphOutline(long nativeStrikePtr, 61 int glyphCode, 62 double x, 63 double y); 64 65 // returns the bounding rect for a glyph 66 private static native void getNativeGlyphImageBounds(long nativeStrikePtr, 67 int glyphCode, 68 Rectangle2D.Float result, 69 double x, double y); 70 71 private final CFont nativeFont; 72 private AffineTransform invDevTx; 73 private final GlyphInfoCache glyphInfoCache; 74 private final GlyphAdvanceCache glyphAdvanceCache; 75 private long nativeStrikePtr; 76 77 CStrike(final CFont font, final FontStrikeDesc inDesc) { 78 nativeFont = font; 79 desc = inDesc; 80 glyphInfoCache = new GlyphInfoCache(font, desc); 81 glyphAdvanceCache = new GlyphAdvanceCache(); 82 disposer = glyphInfoCache; 83 84 // Normally the device transform should be the identity transform 85 // for screen operations. The device transform only becomes 86 // interesting when we are outputting between different dpi surfaces, 87 // like when we are printing to postscript or use retina. 88 if (inDesc.devTx != null && !inDesc.devTx.isIdentity()) { 89 try { 90 invDevTx = inDesc.devTx.createInverse(); 91 } catch (NoninvertibleTransformException ignored) { 92 // ignored, since device transforms should not be that 93 // complicated, and if they are - there is nothing we can do, 94 // so we won't worry about it. 95 } 96 } 97 } 98 99 public long getNativeStrikePtr() { 100 if (nativeStrikePtr != 0) { 101 return nativeStrikePtr; 102 } 103 104 final double[] glyphTx = new double[6]; 105 desc.glyphTx.getMatrix(glyphTx); 106 107 final double[] invDevTxMatrix = new double[6]; 108 if (invDevTx == null) { 109 invDevTxMatrix[0] = 1; 110 invDevTxMatrix[3] = 1; 111 } else { 112 invDevTx.getMatrix(invDevTxMatrix); 113 } 114 115 final int aaHint = desc.aaHint; 116 final int fmHint = desc.fmHint; 117 118 synchronized (this) { 119 if (nativeStrikePtr != 0) { 120 return nativeStrikePtr; 121 } 122 nativeStrikePtr = 123 createNativeStrikePtr(nativeFont.getNativeFontPtr(), 124 glyphTx, invDevTxMatrix, aaHint, fmHint); 125 } 126 127 return nativeStrikePtr; 128 } 129 130 protected synchronized void finalize() throws Throwable { 131 if (nativeStrikePtr != 0) { 132 disposeNativeStrikePtr(nativeStrikePtr); 133 } 134 nativeStrikePtr = 0; 135 } 136 137 138 @Override 139 public int getNumGlyphs() { 140 return nativeFont.getNumGlyphs(); 141 } 142 143 @Override 144 StrikeMetrics getFontMetrics() { 145 if (strikeMetrics == null) { 146 StrikeMetrics metrics = getFontMetrics(getNativeStrikePtr()); 147 if (invDevTx != null) { 148 metrics.convertToUserSpace(invDevTx); 149 } 150 metrics.convertToUserSpace(desc.glyphTx); 151 strikeMetrics = metrics; 152 } 153 return strikeMetrics; 154 } 155 156 @Override 157 float getGlyphAdvance(final int glyphCode) { 158 return getCachedNativeGlyphAdvance(glyphCode); 159 } 160 161 @Override 162 float getCodePointAdvance(final int cp) { 163 return getGlyphAdvance(nativeFont.getMapper().charToGlyph(cp)); 164 } 165 166 @Override 167 Point2D.Float getCharMetrics(final char ch) { 168 return getGlyphMetrics(nativeFont.getMapper().charToGlyph(ch)); 169 } 170 171 @Override 172 Point2D.Float getGlyphMetrics(final int glyphCode) { 173 return new Point2D.Float(getGlyphAdvance(glyphCode), 0.0f); 174 } 175 176 Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) { 177 GeneralPath gp = getGlyphOutline(glyphCode, 0f, 0f); 178 Rectangle2D r2d = gp.getBounds2D(); 179 Rectangle2D.Float r2df; 180 if (r2d instanceof Rectangle2D.Float) { 181 r2df = (Rectangle2D.Float)r2d; 182 } else { 183 float x = (float)r2d.getX(); 184 float y = (float)r2d.getY(); 185 float w = (float)r2d.getWidth(); 186 float h = (float)r2d.getHeight(); 187 r2df = new Rectangle2D.Float(x, y, w, h); 188 } 189 return r2df; 190 } 191 192 // pt, result in device space 193 void getGlyphImageBounds(int glyphCode, Point2D.Float pt, Rectangle result) { 194 Rectangle2D.Float floatRect = new Rectangle2D.Float(); 195 196 if (invDevTx != null) { 197 invDevTx.transform(pt, pt); 198 } 199 200 getGlyphImageBounds(glyphCode, pt.x, pt.y, floatRect); 201 202 if (floatRect.width == 0 && floatRect.height == 0) { 203 result.setRect(0, 0, -1, -1); 204 return; 205 } 206 207 result.setRect(floatRect.x + pt.x, floatRect.y + pt.y, floatRect.width, floatRect.height); 208 } 209 210 private void getGlyphImageBounds(int glyphCode, float x, float y, Rectangle2D.Float floatRect) { 211 getNativeGlyphImageBounds(getNativeStrikePtr(), glyphCode, floatRect, x, y); 212 } 213 214 GeneralPath getGlyphOutline(int glyphCode, float x, float y) { 215 return getNativeGlyphOutline(getNativeStrikePtr(), glyphCode, x, y); 216 } 217 218 // should implement, however not called though any path that is publicly exposed 219 GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) { 220 throw new Error("not implemented yet"); 221 } 222 223 // called from the Sun2D renderer 224 long getGlyphImagePtr(int glyphCode) { 225 synchronized (glyphInfoCache) { 226 long ptr = glyphInfoCache.get(glyphCode); 227 if (ptr != 0L) return ptr; 228 229 long[] ptrs = new long[1]; 230 int[] codes = new int[1]; 231 codes[0] = glyphCode; 232 233 getGlyphImagePtrs(codes, ptrs, 1); 234 235 ptr = ptrs[0]; 236 glyphInfoCache.put(glyphCode, ptr); 237 238 return ptr; 239 } 240 } 241 242 // called from the Sun2D renderer 243 void getGlyphImagePtrs(int[] glyphCodes, long[] images, int len) { 244 synchronized (glyphInfoCache) { 245 // fill the image pointer array with existing pointers 246 // from the cache 247 int missed = 0; 248 for (int i = 0; i < len; i++) { 249 int code = glyphCodes[i]; 250 251 final long ptr = glyphInfoCache.get(code); 252 if (ptr != 0L) { 253 images[i] = ptr; 254 } else { 255 // zero this element out, because the caller does not 256 // promise to keep it clean 257 images[i] = 0L; 258 missed++; 259 } 260 } 261 262 if (missed == 0) { 263 return; // horray! we got away without touching native! 264 } 265 266 // all distinct glyph codes requested (partially filled) 267 final int[] filteredCodes = new int[missed]; 268 // indices into filteredCodes array (totally filled) 269 final int[] filteredIndicies = new int[missed]; 270 271 // scan, mark, and store the requested glyph codes again to 272 // send into native 273 int j = 0; 274 int dupes = 0; 275 for (int i = 0; i < len; i++) { 276 if (images[i] != 0L) continue; // already filled 277 278 final int code = glyphCodes[i]; 279 280 // we have already promised to strike this glyph - this is 281 // a dupe 282 if (glyphInfoCache.get(code) == -1L) { 283 filteredIndicies[j] = -1; 284 dupes++; 285 j++; 286 continue; 287 } 288 289 // this is a distinct glyph we have not struck before, or 290 // promised to strike mark this one as "promise to strike" 291 // in the global cache with a -1L 292 final int k = j - dupes; 293 filteredCodes[k] = code; 294 glyphInfoCache.put(code, -1L); 295 filteredIndicies[j] = k; 296 j++; 297 } 298 299 final int filteredRunLen = j - dupes; 300 final long[] filteredImages = new long[filteredRunLen]; 301 302 // bulk call to fill in the distinct glyph pointers from native 303 getFilteredGlyphImagePtrs(filteredImages, filteredCodes, filteredRunLen); 304 305 // scan the requested glyph list, and fill in pointers from our 306 // distinct glyph list which has been filled from native 307 j = 0; 308 for (int i = 0; i < len; i++) { 309 if (images[i] != 0L && images[i] != -1L) { 310 continue; // already placed 311 } 312 313 // index into filteredImages array 314 final int k = filteredIndicies[j]; 315 final int code = glyphCodes[i]; 316 if (k == -1L) { 317 // we should have already filled the cache with this pointer 318 images[i] = glyphInfoCache.get(code); 319 } else { 320 // fill the particular glyph code request, and store 321 // in the cache 322 final long ptr = filteredImages[k]; 323 images[i] = ptr; 324 glyphInfoCache.put(code, ptr); 325 } 326 327 j++; 328 } 329 } 330 } 331 332 private void getFilteredGlyphImagePtrs(long[] glyphInfos, 333 int[] uniCodes, int len) 334 { 335 getGlyphImagePtrsNative(getNativeStrikePtr(), glyphInfos, uniCodes, len); 336 } 337 338 private float getCachedNativeGlyphAdvance(int glyphCode) { 339 synchronized(glyphAdvanceCache) { 340 float advance = glyphAdvanceCache.get(glyphCode); 341 if (advance != 0) { 342 return advance; 343 } 344 345 advance = getNativeGlyphAdvance(getNativeStrikePtr(), glyphCode); 346 glyphAdvanceCache.put(glyphCode, advance); 347 return advance; 348 } 349 } 350 351 // This class stores glyph pointers, and is indexed based on glyph codes, 352 // and negative unicode values. See the comments in 353 // CCharToGlyphMapper for more details on our glyph code strategy. 354 private static class GlyphInfoCache extends CStrikeDisposer { 355 private static final int FIRST_LAYER_SIZE = 256; 356 private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128 357 358 // rdar://problem/5204197 359 private boolean disposed = false; 360 361 private final long[] firstLayerCache; 362 private SparseBitShiftingTwoLayerArray secondLayerCache; 363 private HashMap<Integer, Long> generalCache; 364 365 GlyphInfoCache(final Font2D nativeFont, final FontStrikeDesc desc) { 366 super(nativeFont, desc); 367 firstLayerCache = new long[FIRST_LAYER_SIZE]; 368 } 369 370 public synchronized long get(final int index) { 371 if (index < 0) { 372 if (-index < SECOND_LAYER_SIZE) { 373 // catch common unicodes 374 if (secondLayerCache == null) { 375 return 0L; 376 } 377 return secondLayerCache.get(-index); 378 } 379 } else { 380 if (index < FIRST_LAYER_SIZE) { 381 // catch common glyphcodes 382 return firstLayerCache[index]; 383 } 384 } 385 386 if (generalCache == null) { 387 return 0L; 388 } 389 final Long value = generalCache.get(new Integer(index)); 390 if (value == null) { 391 return 0L; 392 } 393 return value.longValue(); 394 } 395 396 public synchronized void put(final int index, final long value) { 397 if (index < 0) { 398 if (-index < SECOND_LAYER_SIZE) { 399 // catch common unicodes 400 if (secondLayerCache == null) { 401 secondLayerCache = new SparseBitShiftingTwoLayerArray(SECOND_LAYER_SIZE, 7); // 128x128 402 } 403 secondLayerCache.put(-index, value); 404 return; 405 } 406 } else { 407 if (index < FIRST_LAYER_SIZE) { 408 // catch common glyphcodes 409 firstLayerCache[index] = value; 410 return; 411 } 412 } 413 414 if (generalCache == null) { 415 generalCache = new HashMap<Integer, Long>(); 416 } 417 418 generalCache.put(new Integer(index), Long.valueOf(value)); 419 } 420 421 public synchronized void dispose() { 422 // rdar://problem/5204197 423 // Note that sun.font.Font2D.getStrike() actively disposes 424 // cleared strikeRef. We need to check the disposed flag to 425 // prevent double frees of native resources. 426 if (disposed) { 427 return; 428 } 429 430 super.dispose(); 431 432 // clean out the first array 433 disposeLongArray(firstLayerCache); 434 435 // clean out the two layer arrays 436 if (secondLayerCache != null) { 437 final long[][] secondLayerLongArrayArray = secondLayerCache.cache; 438 for (int i = 0; i < secondLayerLongArrayArray.length; i++) { 439 final long[] longArray = secondLayerLongArrayArray[i]; 440 if (longArray != null) disposeLongArray(longArray); 441 } 442 } 443 444 // clean up everyone else 445 if (generalCache != null) { 446 final Iterator<Long> i = generalCache.values().iterator(); 447 while (i.hasNext()) { 448 final long longValue = i.next().longValue(); 449 if (longValue != -1 && longValue != 0) { 450 removeGlyphInfoFromCache(longValue); 451 StrikeCache.freeLongPointer(longValue); 452 } 453 } 454 } 455 456 // rdar://problem/5204197 457 // Finally, set the flag. 458 disposed = true; 459 } 460 461 private static void disposeLongArray(final long[] longArray) { 462 for (int i = 0; i < longArray.length; i++) { 463 final long ptr = longArray[i]; 464 if (ptr != 0 && ptr != -1) { 465 removeGlyphInfoFromCache(ptr); 466 StrikeCache.freeLongPointer(ptr); // free's the native struct pointer 467 } 468 } 469 } 470 471 private static class SparseBitShiftingTwoLayerArray { 472 final long[][] cache; 473 final int shift; 474 final int secondLayerLength; 475 476 SparseBitShiftingTwoLayerArray(final int size, final int shift) { 477 this.shift = shift; 478 this.cache = new long[1 << shift][]; 479 this.secondLayerLength = size >> shift; 480 } 481 482 public long get(final int index) { 483 final int firstIndex = index >> shift; 484 final long[] firstLayerRow = cache[firstIndex]; 485 if (firstLayerRow == null) return 0L; 486 return firstLayerRow[index - (firstIndex * (1 << shift))]; 487 } 488 489 public void put(final int index, final long value) { 490 final int firstIndex = index >> shift; 491 long[] firstLayerRow = cache[firstIndex]; 492 if (firstLayerRow == null) { 493 cache[firstIndex] = firstLayerRow = new long[secondLayerLength]; 494 } 495 firstLayerRow[index - (firstIndex * (1 << shift))] = value; 496 } 497 } 498 } 499 500 private static class GlyphAdvanceCache { 501 private static final int FIRST_LAYER_SIZE = 256; 502 private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128 503 504 private final float[] firstLayerCache = new float[FIRST_LAYER_SIZE]; 505 private SparseBitShiftingTwoLayerArray secondLayerCache; 506 private HashMap<Integer, Float> generalCache; 507 508 // Empty non private constructor was added because access to this 509 // class shouldn't be emulated by a synthetic accessor method. 510 GlyphAdvanceCache() { 511 super(); 512 } 513 514 public synchronized float get(final int index) { 515 if (index < 0) { 516 if (-index < SECOND_LAYER_SIZE) { 517 // catch common unicodes 518 if (secondLayerCache == null) return 0; 519 return secondLayerCache.get(-index); 520 } 521 } else { 522 if (index < FIRST_LAYER_SIZE) { 523 // catch common glyphcodes 524 return firstLayerCache[index]; 525 } 526 } 527 528 if (generalCache == null) return 0; 529 final Float value = generalCache.get(new Integer(index)); 530 if (value == null) return 0; 531 return value.floatValue(); 532 } 533 534 public synchronized void put(final int index, final float value) { 535 if (index < 0) { 536 if (-index < SECOND_LAYER_SIZE) { 537 // catch common unicodes 538 if (secondLayerCache == null) { 539 secondLayerCache = new SparseBitShiftingTwoLayerArray(SECOND_LAYER_SIZE, 7); // 128x128 540 } 541 secondLayerCache.put(-index, value); 542 return; 543 } 544 } else { 545 if (index < FIRST_LAYER_SIZE) { 546 // catch common glyphcodes 547 firstLayerCache[index] = value; 548 return; 549 } 550 } 551 552 if (generalCache == null) { 553 generalCache = new HashMap<Integer, Float>(); 554 } 555 556 generalCache.put(new Integer(index), new Float(value)); 557 } 558 559 private static class SparseBitShiftingTwoLayerArray { 560 final float[][] cache; 561 final int shift; 562 final int secondLayerLength; 563 564 SparseBitShiftingTwoLayerArray(final int size, final int shift) { 565 this.shift = shift; 566 this.cache = new float[1 << shift][]; 567 this.secondLayerLength = size >> shift; 568 } 569 570 public float get(final int index) { 571 final int firstIndex = index >> shift; 572 final float[] firstLayerRow = cache[firstIndex]; 573 if (firstLayerRow == null) return 0L; 574 return firstLayerRow[index - (firstIndex * (1 << shift))]; 575 } 576 577 public void put(final int index, final float value) { 578 final int firstIndex = index >> shift; 579 float[] firstLayerRow = cache[firstIndex]; 580 if (firstLayerRow == null) { 581 cache[firstIndex] = firstLayerRow = 582 new float[secondLayerLength]; 583 } 584 firstLayerRow[index - (firstIndex * (1 << shift))] = value; 585 } 586 } 587 } 588 }