1 /* 2 * Copyright (c) 2023, 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 27 package sun.font; 28 29 import java.awt.geom.Point2D; 30 import sun.font.GlyphLayout.GVData; 31 import sun.java2d.Disposer; 32 import sun.java2d.DisposerRecord; 33 34 import java.lang.foreign.Arena; 35 import java.lang.foreign.FunctionDescriptor; 36 import java.lang.foreign.Linker; 37 import java.lang.foreign.MemoryLayout; 38 import java.lang.foreign.MemorySegment; 39 import static java.lang.foreign.MemorySegment.NULL; 40 import java.lang.foreign.SequenceLayout; 41 import java.lang.foreign.StructLayout; 42 import java.lang.foreign.SymbolLookup; 43 import java.lang.foreign.UnionLayout; 44 import static java.lang.foreign.ValueLayout.*; 45 46 import java.lang.invoke.MethodHandle; 47 import java.lang.invoke.MethodHandles; 48 import java.lang.invoke.MethodType; 49 import java.lang.invoke.VarHandle; 50 51 import java.util.Optional; 52 import java.util.WeakHashMap; 53 54 public class HBShaper { 55 56 /* 57 * union _hb_var_int_t { 58 * uint32_t u32; 59 * int32_t i32; 60 * uint16_t u16[2]; 61 * int16_t i16[2]; 62 * uint8_t u8[4]; 63 * int8_t i8[4]; 64 * }; 65 */ 66 private static final UnionLayout VarIntLayout = MemoryLayout.unionLayout( 67 JAVA_INT.withName("u32"), 68 JAVA_INT.withName("i32"), 69 MemoryLayout.sequenceLayout(2, JAVA_SHORT).withName("u16"), 70 MemoryLayout.sequenceLayout(2, JAVA_SHORT).withName("i16"), 71 MemoryLayout.sequenceLayout(4, JAVA_BYTE).withName("u8"), 72 MemoryLayout.sequenceLayout(4, JAVA_BYTE).withName("i8") 73 ).withName("_hb_var_int_t"); 74 75 /* 76 * struct hb_glyph_position_t { 77 * hb_position_t x_advance; 78 * hb_position_t y_advance; 79 * hb_position_t x_offset; 80 * hb_position_t y_offset; 81 * hb_var_int_t var; 82 * }; 83 */ 84 private static final StructLayout PositionLayout = MemoryLayout.structLayout( 85 JAVA_INT.withName("x_advance"), 86 JAVA_INT.withName("y_advance"), 87 JAVA_INT.withName("x_offset"), 88 JAVA_INT.withName("y_offset"), 89 VarIntLayout.withName("var") 90 ).withName("hb_glyph_position_t"); 91 92 /** 93 * struct hb_glyph_info_t { 94 * hb_codepoint_t codepoint; 95 * hb_mask_t mask; 96 * uint32_t cluster; 97 * hb_var_int_t var1; 98 * hb_var_int_t var2; 99 * }; 100 */ 101 private static final StructLayout GlyphInfoLayout = MemoryLayout.structLayout( 102 JAVA_INT.withName("codepoint"), 103 JAVA_INT.withName("mask"), 104 JAVA_INT.withName("cluster"), 105 VarIntLayout.withName("var1"), 106 VarIntLayout.withName("var2") 107 ).withName("hb_glyph_info_t"); 108 109 private static VarHandle getVarHandle(StructLayout struct, String name) { 110 VarHandle h = struct.arrayElementVarHandle(PathElement.groupElement(name)); 111 /* insert 0 offset so don't need to pass arg every time */ 112 return MethodHandles.insertCoordinates(h, 1, 0L).withInvokeExactBehavior(); 113 } 114 115 private static final VarHandle x_offsetHandle; 116 private static final VarHandle y_offsetHandle; 117 private static final VarHandle x_advanceHandle; 118 private static final VarHandle y_advanceHandle; 119 private static final VarHandle codePointHandle; 120 private static final VarHandle clusterHandle; 121 122 private static final MethodHandles.Lookup MH_LOOKUP; 123 private static final Linker LINKER; 124 private static final SymbolLookup SYM_LOOKUP; 125 private static final MethodHandle malloc_handle; 126 private static final MethodHandle create_face_handle; 127 private static final MethodHandle dispose_face_handle; 128 private static final MethodHandle jdk_hb_shape_handle; 129 130 /* hb_jdk_font_funcs_struct is a pointer to a harfbuzz font_funcs 131 * object which references the 5 following upcall stubs. 132 * The singleton shared font_funcs ptr is passed down in each 133 * call to shape() and installed on the hb_font. 134 */ 135 private static final MemorySegment hb_jdk_font_funcs_struct; 136 private static final MemorySegment get_var_glyph_stub; 137 private static final MemorySegment get_nominal_glyph_stub; 138 private static final MemorySegment get_h_advance_stub; 139 private static final MemorySegment get_v_advance_stub; 140 private static final MemorySegment get_contour_pt_stub; 141 142 private static final MemorySegment store_layout_results_stub; 143 144 private static FunctionDescriptor 145 getFunctionDescriptor(MemoryLayout retType, 146 MemoryLayout... argTypes) { 147 148 return (retType == null) ? 149 FunctionDescriptor.ofVoid(argTypes) : 150 FunctionDescriptor.of(retType, argTypes); 151 } 152 153 private static MethodHandle getMethodHandle 154 (String mName, 155 FunctionDescriptor fd) { 156 157 try { 158 MethodType mType = fd.toMethodType(); 159 return MH_LOOKUP.findStatic(HBShaper.class, mName, mType); 160 } catch (IllegalAccessException | NoSuchMethodException e) { 161 return null; 162 } 163 } 164 165 static { 166 MH_LOOKUP = MethodHandles.lookup(); 167 LINKER = Linker.nativeLinker(); 168 SYM_LOOKUP = SymbolLookup.loaderLookup().or(LINKER.defaultLookup()); 169 FunctionDescriptor mallocDescriptor = 170 FunctionDescriptor.of(ADDRESS, JAVA_LONG); 171 MemorySegment malloc_symbol = SYM_LOOKUP.findOrThrow("malloc"); 172 @SuppressWarnings("restricted") 173 MethodHandle tmp1 = LINKER.downcallHandle(malloc_symbol, mallocDescriptor); 174 malloc_handle = tmp1; 175 176 FunctionDescriptor createFaceDescriptor = 177 FunctionDescriptor.of(ADDRESS, ADDRESS); 178 MemorySegment create_face_symbol = SYM_LOOKUP.findOrThrow("HBCreateFace"); 179 @SuppressWarnings("restricted") 180 MethodHandle tmp2 = LINKER.downcallHandle(create_face_symbol, createFaceDescriptor); 181 create_face_handle = tmp2; 182 183 FunctionDescriptor disposeFaceDescriptor = FunctionDescriptor.ofVoid(ADDRESS); 184 MemorySegment dispose_face_symbol = SYM_LOOKUP.findOrThrow("HBDisposeFace"); 185 @SuppressWarnings("restricted") 186 MethodHandle tmp3 = LINKER.downcallHandle(dispose_face_symbol, disposeFaceDescriptor); 187 dispose_face_handle = tmp3; 188 189 FunctionDescriptor shapeDesc = FunctionDescriptor.ofVoid( 190 //JAVA_INT, // return type 191 JAVA_FLOAT, // ptSize 192 ADDRESS, // matrix 193 ADDRESS, // face 194 ADDRESS, // chars 195 JAVA_INT, // len 196 JAVA_INT, // script 197 JAVA_INT, // offset 198 JAVA_INT, // limit 199 JAVA_INT, // baseIndex 200 JAVA_FLOAT, // startX 201 JAVA_FLOAT, // startY 202 JAVA_INT, // flags, 203 JAVA_INT, // slot, 204 ADDRESS, // ptr to harfbuzz font_funcs object. 205 ADDRESS); // store_results_fn 206 207 MemorySegment shape_sym = SYM_LOOKUP.findOrThrow("jdk_hb_shape"); 208 @SuppressWarnings("restricted") 209 MethodHandle tmp4 = LINKER.downcallHandle(shape_sym, shapeDesc); 210 jdk_hb_shape_handle = tmp4; 211 212 Arena garena = Arena.global(); // creating stubs that exist until VM exit. 213 FunctionDescriptor get_var_glyph_fd = getFunctionDescriptor(JAVA_INT, // return type 214 ADDRESS, ADDRESS, JAVA_INT, JAVA_INT, ADDRESS, ADDRESS); // arg types 215 MethodHandle get_var_glyph_mh = 216 getMethodHandle("get_variation_glyph", get_var_glyph_fd); 217 @SuppressWarnings("restricted") 218 MemorySegment tmp5 = LINKER.upcallStub(get_var_glyph_mh, get_var_glyph_fd, garena); 219 get_var_glyph_stub = tmp5; 220 221 FunctionDescriptor get_nominal_glyph_fd = getFunctionDescriptor(JAVA_INT, // return type 222 ADDRESS, ADDRESS, JAVA_INT, ADDRESS, ADDRESS); // arg types 223 MethodHandle get_nominal_glyph_mh = 224 getMethodHandle("get_nominal_glyph", get_nominal_glyph_fd); 225 @SuppressWarnings("restricted") 226 MemorySegment tmp6 = LINKER.upcallStub(get_nominal_glyph_mh, get_nominal_glyph_fd, garena); 227 get_nominal_glyph_stub = tmp6; 228 229 FunctionDescriptor get_h_adv_fd = getFunctionDescriptor(JAVA_INT, // return type 230 ADDRESS, ADDRESS, JAVA_INT, ADDRESS); // arg types 231 MethodHandle get_h_adv_mh = 232 getMethodHandle("get_glyph_h_advance", get_h_adv_fd); 233 @SuppressWarnings("restricted") 234 MemorySegment tmp7 = LINKER.upcallStub(get_h_adv_mh, get_h_adv_fd, garena); 235 get_h_advance_stub = tmp7; 236 237 FunctionDescriptor get_v_adv_fd = getFunctionDescriptor(JAVA_INT, // return type 238 ADDRESS, ADDRESS, JAVA_INT, ADDRESS); // arg types 239 MethodHandle get_v_adv_mh = 240 getMethodHandle("get_glyph_v_advance", get_v_adv_fd); 241 @SuppressWarnings("restricted") 242 MemorySegment tmp8 = LINKER.upcallStub(get_v_adv_mh, get_v_adv_fd, garena); 243 get_v_advance_stub = tmp8; 244 245 FunctionDescriptor get_contour_pt_fd = getFunctionDescriptor(JAVA_INT, // return type 246 ADDRESS, ADDRESS, JAVA_INT, JAVA_INT, ADDRESS, ADDRESS, ADDRESS); // arg types 247 MethodHandle get_contour_pt_mh = 248 getMethodHandle("get_glyph_contour_point", get_contour_pt_fd); 249 @SuppressWarnings("restricted") 250 MemorySegment tmp9 = LINKER.upcallStub(get_contour_pt_mh, get_contour_pt_fd, garena); 251 get_contour_pt_stub = tmp9; 252 253 /* Having now created the font upcall stubs, we can call down to create 254 * the native harfbuzz object holding these. 255 */ 256 FunctionDescriptor createFontFuncsDescriptor = FunctionDescriptor.of( 257 ADDRESS, // hb_font_funcs* return type 258 ADDRESS, // glyph_fn upcall stub 259 ADDRESS, // variation_fn upcall stub 260 ADDRESS, // h_advance_fn upcall stub 261 ADDRESS, // v_advance_fn upcall stub 262 ADDRESS); // contour_pt_fn upcall stub 263 MemorySegment create_font_funcs_symbol = SYM_LOOKUP.findOrThrow("HBCreateFontFuncs"); 264 @SuppressWarnings("restricted") 265 MethodHandle create_font_funcs_handle = 266 LINKER.downcallHandle(create_font_funcs_symbol, createFontFuncsDescriptor); 267 268 MemorySegment s = null; 269 try { 270 s = (MemorySegment)create_font_funcs_handle.invokeExact( 271 get_nominal_glyph_stub, 272 get_var_glyph_stub, 273 get_h_advance_stub, 274 get_v_advance_stub, 275 get_contour_pt_stub); 276 } catch (Throwable t) { 277 t.printStackTrace(); 278 } 279 hb_jdk_font_funcs_struct = s; 280 281 FunctionDescriptor store_layout_fd = 282 FunctionDescriptor.ofVoid( 283 JAVA_INT, // slot 284 JAVA_INT, // baseIndex 285 JAVA_INT, // offset 286 JAVA_FLOAT, // startX 287 JAVA_FLOAT, // startX 288 JAVA_FLOAT, // devScale 289 JAVA_INT, // charCount 290 JAVA_INT, // glyphCount 291 ADDRESS, // glyphInfo 292 ADDRESS); // glyphPos 293 MethodHandle store_layout_mh = 294 getMethodHandle("store_layout_results", store_layout_fd); 295 @SuppressWarnings("restricted") 296 MemorySegment tmp10 = LINKER.upcallStub(store_layout_mh, store_layout_fd, garena); 297 store_layout_results_stub = tmp10; 298 299 x_offsetHandle = getVarHandle(PositionLayout, "x_offset"); 300 y_offsetHandle = getVarHandle(PositionLayout, "y_offset"); 301 x_advanceHandle = getVarHandle(PositionLayout, "x_advance"); 302 y_advanceHandle = getVarHandle(PositionLayout, "y_advance"); 303 codePointHandle = getVarHandle(GlyphInfoLayout, "codepoint"); 304 clusterHandle = getVarHandle(GlyphInfoLayout, "cluster"); 305 } 306 307 308 /* 309 * This is expensive but it is done just once per font. 310 * The unbound stub could be cached but the savings would 311 * be very low in the only case it is used. 312 */ 313 @SuppressWarnings("restricted") 314 private static MemorySegment getBoundUpcallStub 315 (Arena arena, Class<?> clazz, Object bindArg, String mName, 316 MemoryLayout retType, MemoryLayout... argTypes) { 317 318 try { 319 FunctionDescriptor nativeDescriptor = 320 (retType == null) ? 321 FunctionDescriptor.ofVoid(argTypes) : 322 FunctionDescriptor.of(retType, argTypes); 323 MethodType mType = nativeDescriptor.toMethodType(); 324 mType = mType.insertParameterTypes(0, clazz); 325 MethodHandle mh = MH_LOOKUP.findStatic(HBShaper.class, mName, mType); 326 MethodHandle bound_handle = mh.bindTo(bindArg); 327 return LINKER.upcallStub(bound_handle, nativeDescriptor, arena); 328 } catch (IllegalAccessException | NoSuchMethodException e) { 329 return null; 330 } 331 } 332 333 private static int get_nominal_glyph( 334 MemorySegment font_ptr, /* Not used */ 335 MemorySegment font_data, /* Not used */ 336 int unicode, 337 MemorySegment glyph, /* pointer to location to store glyphID */ 338 MemorySegment user_data /* Not used */ 339 ) { 340 341 Font2D font2D = scopedVars.get().font(); 342 int glyphID = font2D.charToGlyph(unicode); 343 @SuppressWarnings("restricted") 344 MemorySegment glyphIDPtr = glyph.reinterpret(4); 345 glyphIDPtr.setAtIndex(JAVA_INT, 0, glyphID); 346 return (glyphID != 0) ? 1 : 0; 347 } 348 349 private static int get_variation_glyph( 350 MemorySegment font_ptr, /* Not used */ 351 MemorySegment font_data, /* Not used */ 352 int unicode, 353 int variation_selector, 354 MemorySegment glyph, /* pointer to location to store glyphID */ 355 MemorySegment user_data /* Not used */ 356 ) { 357 Font2D font2D = scopedVars.get().font(); 358 int glyphID = font2D.charToVariationGlyph(unicode, variation_selector); 359 @SuppressWarnings("restricted") 360 MemorySegment glyphIDPtr = glyph.reinterpret(4); 361 glyphIDPtr.setAtIndex(JAVA_INT, 0, glyphID); 362 return (glyphID != 0) ? 1 : 0; 363 } 364 365 private static final float HBFloatToFixedScale = ((float)(1 << 16)); 366 private static final int HBFloatToFixed(float f) { 367 return ((int)((f) * HBFloatToFixedScale)); 368 } 369 370 private static int get_glyph_h_advance( 371 MemorySegment font_ptr, /* Not used */ 372 MemorySegment font_data, /* Not used */ 373 int glyph, 374 MemorySegment user_data /* Not used */ 375 ) { 376 FontStrike strike = scopedVars.get().fontStrike(); 377 Point2D.Float pt = strike.getGlyphMetrics(glyph); 378 return (pt != null) ? HBFloatToFixed(pt.x) : 0; 379 } 380 381 private static int get_glyph_v_advance( 382 MemorySegment font_ptr, /* Not used */ 383 MemorySegment font_data, /* Not used */ 384 int glyph, 385 MemorySegment user_data /* Not used */ 386 ) { 387 388 FontStrike strike = scopedVars.get().fontStrike(); 389 Point2D.Float pt = strike.getGlyphMetrics(glyph); 390 return (pt != null) ? HBFloatToFixed(pt.y) : 0; 391 } 392 393 /* 394 * This class exists to make the code that uses it less verbose 395 */ 396 private static class IntPtr { 397 MemorySegment seg; 398 IntPtr(MemorySegment seg) { 399 } 400 401 void set(int i) { 402 seg.setAtIndex(JAVA_INT, 0, i); 403 } 404 } 405 406 private static int get_glyph_contour_point( 407 MemorySegment font_ptr, /* Not used */ 408 MemorySegment font_data, /* Not used */ 409 int glyph, 410 int point_index, 411 MemorySegment x_ptr, /* ptr to return x */ 412 MemorySegment y_ptr, /* ptr to return y */ 413 MemorySegment user_data /* Not used */ 414 ) { 415 IntPtr x = new IntPtr(x_ptr); 416 IntPtr y = new IntPtr(y_ptr); 417 418 if ((glyph & 0xfffe) == 0xfffe) { 419 x.set(0); 420 y.set(0); 421 return 1; 422 } 423 424 FontStrike strike = scopedVars.get().fontStrike(); 425 Point2D.Float pt = ((PhysicalStrike)strike).getGlyphPoint(glyph, point_index); 426 x.set(HBFloatToFixed(pt.x)); 427 y.set(HBFloatToFixed(pt.y)); 428 429 return 1; 430 } 431 432 record ScopedVars ( 433 Font2D font, 434 FontStrike fontStrike, 435 GVData gvData, 436 Point2D.Float point) {} 437 438 static final ScopedValue<ScopedVars> scopedVars = ScopedValue.newInstance(); 439 440 static void shape( 441 Font2D font2D, 442 FontStrike fontStrike, 443 float ptSize, 444 float[] mat, 445 MemorySegment hbface, 446 char[] text, 447 GVData gvData, 448 int script, 449 int offset, 450 int limit, 451 int baseIndex, 452 Point2D.Float startPt, 453 int flags, 454 int slot) { 455 456 /* 457 * ScopedValue is needed so that call backs into Java during 458 * shaping can locate the correct instances of these to query or update. 459 * The alternative of creating bound method handles is far too slow. 460 */ 461 ScopedVars vars = new ScopedVars(font2D, fontStrike, gvData, startPt); 462 ScopedValue.where(scopedVars, vars) 463 .run(() -> { 464 465 try (Arena arena = Arena.ofConfined()) { 466 467 float startX = (float)startPt.getX(); 468 float startY = (float)startPt.getY(); 469 470 MemorySegment matrix = arena.allocateFrom(JAVA_FLOAT, mat); 471 MemorySegment chars = arena.allocateFrom(JAVA_CHAR, text); 472 473 /*int ret =*/ jdk_hb_shape_handle.invokeExact( 474 ptSize, matrix, hbface, chars, text.length, 475 script, offset, limit, 476 baseIndex, startX, startY, flags, slot, 477 hb_jdk_font_funcs_struct, 478 store_layout_results_stub); 479 } catch (Throwable t) { 480 } 481 }); 482 } 483 484 private static int getFontTableData(Font2D font2D, 485 int tag, 486 MemorySegment data_ptr_out) { 487 488 /* 489 * On return, the data_out_ptr will point to memory allocated by native malloc, 490 * so it will be freed by the caller using native free - when it is 491 * done with it. 492 */ 493 @SuppressWarnings("restricted") 494 MemorySegment data_ptr = data_ptr_out.reinterpret(ADDRESS.byteSize()); 495 if (tag == 0) { 496 data_ptr.setAtIndex(ADDRESS, 0, NULL); 497 return 0; 498 } 499 byte[] data = font2D.getTableBytes(tag); 500 if (data == null) { 501 data_ptr.setAtIndex(ADDRESS, 0, NULL); 502 return 0; 503 } 504 int len = data.length; 505 MemorySegment zero_len = NULL; 506 try { 507 zero_len = (MemorySegment)malloc_handle.invokeExact((long)len); 508 } catch (Throwable t) { 509 } 510 if (zero_len.equals(NULL)) { 511 data_ptr.setAtIndex(ADDRESS, 0, NULL); 512 return 0; 513 } 514 @SuppressWarnings("restricted") 515 MemorySegment mem = zero_len.reinterpret(len); 516 MemorySegment.copy(data, 0, mem, JAVA_BYTE, 0, len); 517 data_ptr.setAtIndex(ADDRESS, 0, mem); 518 return len; 519 } 520 521 /* WeakHashMap is used so that we do not retain temporary fonts 522 * 523 * The value is a class that implements the 2D Disposer, so 524 * that the native resources for temp. fonts can be freed. 525 * 526 * Installed fonts should never be cleared from the map as 527 * they are permanently referenced. 528 */ 529 private static final WeakHashMap<Font2D, FaceRef> 530 faceMap = new WeakHashMap<>(); 531 532 static MemorySegment getFace(Font2D font2D) { 533 FaceRef ref; 534 synchronized (faceMap) { 535 ref = faceMap.computeIfAbsent(font2D, FaceRef::new); 536 } 537 return ref.getFace(); 538 } 539 540 private static class FaceRef implements DisposerRecord { 541 private Font2D font2D; 542 private MemorySegment face; 543 // get_table_data_fn uses an Arena managed by GC, 544 // so we need to keep a reference to it here until 545 // this FaceRef is collected. 546 private MemorySegment get_table_data_fn; 547 548 private FaceRef(Font2D font) { 549 this.font2D = font; 550 } 551 552 private synchronized MemorySegment getFace() { 553 if (face == null) { 554 createFace(); 555 if (face != null) { 556 Disposer.addObjectRecord(font2D, this); 557 } 558 font2D = null; 559 } 560 return face; 561 } 562 563 private void createFace() { 564 try { 565 get_table_data_fn = getBoundUpcallStub(Arena.ofAuto(), 566 Font2D.class, 567 font2D, // bind arg 568 "getFontTableData", // method name 569 JAVA_INT, // return type 570 JAVA_INT, ADDRESS); // arg types 571 if (get_table_data_fn == null) { 572 return; 573 } 574 face = (MemorySegment)create_face_handle.invokeExact(get_table_data_fn); 575 } catch (Throwable t) { 576 } 577 } 578 579 @Override 580 public void dispose() { 581 try { 582 dispose_face_handle.invokeExact(face); 583 } catch (Throwable t) { 584 } 585 } 586 } 587 588 589 /* Upcall to receive results of layout */ 590 private static void store_layout_results( 591 int slot, 592 int baseIndex, 593 int offset, 594 float startX, 595 float startY, 596 float devScale, 597 int charCount, 598 int glyphCount, 599 MemorySegment /* hb_glyph_info_t* */ glyphInfo, 600 MemorySegment /* hb_glyph_position_t* */ glyphPos 601 ) { 602 603 GVData gvdata = scopedVars.get().gvData(); 604 Point2D.Float startPt = scopedVars.get().point(); 605 float x=0, y=0; 606 float advX, advY; 607 float scale = 1.0f / HBFloatToFixedScale / devScale; 608 609 int initialCount = gvdata._count; 610 611 int maxGlyphs = (charCount > glyphCount) ? charCount : glyphCount; 612 int maxStore = maxGlyphs + initialCount; 613 boolean needToGrow = (maxStore > gvdata._glyphs.length) || 614 ((maxStore * 2 + 2) > gvdata._positions.length); 615 if (needToGrow) { 616 gvdata.grow(maxStore-initialCount); 617 } 618 619 int glyphPosLen = glyphCount * 2 + 2; 620 long posSize = glyphPosLen * PositionLayout.byteSize(); 621 @SuppressWarnings("restricted") 622 MemorySegment glyphPosArr = glyphPos.reinterpret(posSize); 623 624 long glyphInfoSize = glyphCount * GlyphInfoLayout.byteSize(); 625 @SuppressWarnings("restricted") 626 MemorySegment glyphInfoArr = glyphInfo.reinterpret(glyphInfoSize); 627 628 for (int i = 0; i < glyphCount; i++) { 629 int storei = i + initialCount; 630 int cluster = (int)clusterHandle.get(glyphInfoArr, (long)i) - offset; 631 gvdata._indices[storei] = baseIndex + cluster; 632 int codePoint = (int)codePointHandle.get(glyphInfoArr, (long)i); 633 gvdata._glyphs[storei] = (slot | codePoint); 634 int x_offset = (int)x_offsetHandle.get(glyphPosArr, (long)i); 635 int y_offset = (int)y_offsetHandle.get(glyphPosArr, (long)i); 636 gvdata._positions[(storei*2)] = startX + x + (x_offset * scale); 637 gvdata._positions[(storei*2)+1] = startY + y - (y_offset * scale); 638 int x_advance = (int)x_advanceHandle.get(glyphPosArr, (long)i); 639 int y_advance = (int)y_advanceHandle.get(glyphPosArr, (long)i); 640 x += x_advance * scale; 641 y += y_advance * scale; 642 } 643 int storeadv = initialCount + glyphCount; 644 gvdata._count = storeadv; 645 // The final slot in the positions array is important 646 // because when the GlyphVector is created from this 647 // data it determines the overall advance of the glyphvector 648 // and this is used in positioning the next glyphvector 649 // during rendering where text is broken into runs. 650 // We also need to report it back into "pt", so layout can 651 // pass it back down for any next run. 652 advX = startX + x; 653 advY = startY + y; 654 gvdata._positions[(storeadv*2)] = advX; 655 gvdata._positions[(storeadv*2)+1] = advY; 656 startPt.x = advX; 657 startPt.y = advY; 658 } 659 }