1 /* 2 * Copyright (c) 2023, 2024, 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.runWhere(scopedVars, vars, () -> { 463 try (Arena arena = Arena.ofConfined()) { 464 465 float startX = (float)startPt.getX(); 466 float startY = (float)startPt.getY(); 467 468 MemorySegment matrix = arena.allocateFrom(JAVA_FLOAT, mat); 469 MemorySegment chars = arena.allocateFrom(JAVA_CHAR, text); 470 471 /*int ret =*/ jdk_hb_shape_handle.invokeExact( 472 ptSize, matrix, hbface, chars, text.length, 473 script, offset, limit, 474 baseIndex, startX, startY, flags, slot, 475 hb_jdk_font_funcs_struct, 476 store_layout_results_stub); 477 } catch (Throwable t) { 478 } 479 }); 480 } 481 482 private static int getFontTableData(Font2D font2D, 483 int tag, 484 MemorySegment data_ptr_out) { 485 486 /* 487 * On return, the data_out_ptr will point to memory allocated by native malloc, 488 * so it will be freed by the caller using native free - when it is 489 * done with it. 490 */ 491 @SuppressWarnings("restricted") 492 MemorySegment data_ptr = data_ptr_out.reinterpret(ADDRESS.byteSize()); 493 if (tag == 0) { 494 data_ptr.setAtIndex(ADDRESS, 0, NULL); 495 return 0; 496 } 497 byte[] data = font2D.getTableBytes(tag); 498 if (data == null) { 499 data_ptr.setAtIndex(ADDRESS, 0, NULL); 500 return 0; 501 } 502 int len = data.length; 503 MemorySegment zero_len = NULL; 504 try { 505 zero_len = (MemorySegment)malloc_handle.invokeExact((long)len); 506 } catch (Throwable t) { 507 } 508 if (zero_len.equals(NULL)) { 509 data_ptr.setAtIndex(ADDRESS, 0, NULL); 510 return 0; 511 } 512 @SuppressWarnings("restricted") 513 MemorySegment mem = zero_len.reinterpret(len); 514 MemorySegment.copy(data, 0, mem, JAVA_BYTE, 0, len); 515 data_ptr.setAtIndex(ADDRESS, 0, mem); 516 return len; 517 } 518 519 /* WeakHashMap is used so that we do not retain temporary fonts 520 * 521 * The value is a class that implements the 2D Disposer, so 522 * that the native resources for temp. fonts can be freed. 523 * 524 * Installed fonts should never be cleared from the map as 525 * they are permanently referenced. 526 */ 527 private static final WeakHashMap<Font2D, FaceRef> 528 faceMap = new WeakHashMap<>(); 529 530 static MemorySegment getFace(Font2D font2D) { 531 FaceRef ref; 532 synchronized (faceMap) { 533 ref = faceMap.computeIfAbsent(font2D, FaceRef::new); 534 } 535 return ref.getFace(); 536 } 537 538 private static class FaceRef implements DisposerRecord { 539 private Font2D font2D; 540 private MemorySegment face; 541 // get_table_data_fn uses an Arena managed by GC, 542 // so we need to keep a reference to it here until 543 // this FaceRef is collected. 544 private MemorySegment get_table_data_fn; 545 546 private FaceRef(Font2D font) { 547 this.font2D = font; 548 } 549 550 private synchronized MemorySegment getFace() { 551 if (face == null) { 552 createFace(); 553 if (face != null) { 554 Disposer.addObjectRecord(font2D, this); 555 } 556 font2D = null; 557 } 558 return face; 559 } 560 561 private void createFace() { 562 try { 563 get_table_data_fn = getBoundUpcallStub(Arena.ofAuto(), 564 Font2D.class, 565 font2D, // bind arg 566 "getFontTableData", // method name 567 JAVA_INT, // return type 568 JAVA_INT, ADDRESS); // arg types 569 if (get_table_data_fn == null) { 570 return; 571 } 572 face = (MemorySegment)create_face_handle.invokeExact(get_table_data_fn); 573 } catch (Throwable t) { 574 } 575 } 576 577 @Override 578 public void dispose() { 579 try { 580 dispose_face_handle.invokeExact(face); 581 } catch (Throwable t) { 582 } 583 } 584 } 585 586 587 /* Upcall to receive results of layout */ 588 private static void store_layout_results( 589 int slot, 590 int baseIndex, 591 int offset, 592 float startX, 593 float startY, 594 float devScale, 595 int charCount, 596 int glyphCount, 597 MemorySegment /* hb_glyph_info_t* */ glyphInfo, 598 MemorySegment /* hb_glyph_position_t* */ glyphPos 599 ) { 600 601 GVData gvdata = scopedVars.get().gvData(); 602 Point2D.Float startPt = scopedVars.get().point(); 603 float x=0, y=0; 604 float advX, advY; 605 float scale = 1.0f / HBFloatToFixedScale / devScale; 606 607 int initialCount = gvdata._count; 608 609 int maxGlyphs = (charCount > glyphCount) ? charCount : glyphCount; 610 int maxStore = maxGlyphs + initialCount; 611 boolean needToGrow = (maxStore > gvdata._glyphs.length) || 612 ((maxStore * 2 + 2) > gvdata._positions.length); 613 if (needToGrow) { 614 gvdata.grow(maxStore-initialCount); 615 } 616 617 int glyphPosLen = glyphCount * 2 + 2; 618 long posSize = glyphPosLen * PositionLayout.byteSize(); 619 @SuppressWarnings("restricted") 620 MemorySegment glyphPosArr = glyphPos.reinterpret(posSize); 621 622 long glyphInfoSize = glyphCount * GlyphInfoLayout.byteSize(); 623 @SuppressWarnings("restricted") 624 MemorySegment glyphInfoArr = glyphInfo.reinterpret(glyphInfoSize); 625 626 for (int i = 0; i < glyphCount; i++) { 627 int storei = i + initialCount; 628 int cluster = (int)clusterHandle.get(glyphInfoArr, (long)i) - offset; 629 gvdata._indices[storei] = baseIndex + cluster; 630 int codePoint = (int)codePointHandle.get(glyphInfoArr, (long)i); 631 gvdata._glyphs[storei] = (slot | codePoint); 632 int x_offset = (int)x_offsetHandle.get(glyphPosArr, (long)i); 633 int y_offset = (int)y_offsetHandle.get(glyphPosArr, (long)i); 634 gvdata._positions[(storei*2)] = startX + x + (x_offset * scale); 635 gvdata._positions[(storei*2)+1] = startY + y - (y_offset * scale); 636 int x_advance = (int)x_advanceHandle.get(glyphPosArr, (long)i); 637 int y_advance = (int)y_advanceHandle.get(glyphPosArr, (long)i); 638 x += x_advance * scale; 639 y += y_advance * scale; 640 } 641 int storeadv = initialCount + glyphCount; 642 gvdata._count = storeadv; 643 // The final slot in the positions array is important 644 // because when the GlyphVector is created from this 645 // data it determines the overall advance of the glyphvector 646 // and this is used in positioning the next glyphvector 647 // during rendering where text is broken into runs. 648 // We also need to report it back into "pt", so layout can 649 // pass it back down for any next run. 650 advX = startX + x; 651 advY = startY + y; 652 gvdata._positions[(storeadv*2)] = advX; 653 gvdata._positions[(storeadv*2)+1] = advY; 654 startPt.x = advX; 655 startPt.y = advY; 656 } 657 }