@@ -221,9 +221,10 @@ oc_glyph_run* oc_harfbuzz_font_shape(oc_arena* arena,
221
221
222
222
glyph -> metrics .advance = (oc_vec2 ){ glyphPos [i ].x_advance , - glyphPos [i ].y_advance };
223
223
224
+ //TODO: we should compute run metrics with the general range-based function at the end
224
225
run -> metrics .ink = oc_rect_combine (run -> metrics .ink , glyph -> metrics .ink );
225
- run -> metrics .logical .w = oc_max (run -> metrics .logical .w , run -> metrics .advance .x );
226
226
run -> metrics .advance = oc_vec2_add (run -> metrics .advance , glyph -> metrics .advance );
227
+ run -> metrics .logical .w = oc_max (run -> metrics .logical .w , run -> metrics .advance .x );
227
228
}
228
229
229
230
//------------------------------------------------------------------------------
@@ -244,64 +245,90 @@ oc_glyph_run* oc_harfbuzz_font_shape(oc_arena* arena,
244
245
{
245
246
//NOTE: collate clusters and cluster widths
246
247
u64 * clusters = oc_arena_push_array (scratch .arena , u64 , run -> glyphCount );
247
- u64 * clusterFirstGlyphs = oc_arena_push_array (scratch .arena , u64 , run -> glyphCount );
248
248
oc_text_metrics * clusterMetrics = oc_arena_push_array (scratch .arena , oc_text_metrics , run -> glyphCount );
249
249
250
250
u64 clusterCount = 0 ;
251
251
u64 currentClusterValue = 0 ;
252
252
253
253
currentClusterValue = glyphInfo [0 ].cluster ;
254
254
clusters [clusterCount ] = currentClusterValue ;
255
- clusterFirstGlyphs [clusterCount ] = 0 ;
256
255
clusterMetrics [clusterCount ] = run -> glyphs [0 ].metrics ;
257
256
clusterCount ++ ;
258
257
259
258
for (u64 glyphIndex = 1 ; glyphIndex < run -> glyphCount ; glyphIndex ++ )
260
259
{
261
260
if (glyphInfo [glyphIndex ].cluster != currentClusterValue )
262
261
{
262
+ //open a new cluster
263
263
currentClusterValue = glyphInfo [glyphIndex ].cluster ;
264
264
clusters [clusterCount ] = currentClusterValue ;
265
- clusterFirstGlyphs [clusterCount ] = glyphIndex ;
266
265
clusterMetrics [clusterCount ] = run -> glyphs [glyphIndex ].metrics ;
267
266
clusterCount ++ ;
268
267
}
269
- clusterMetrics [clusterCount ] = oc_text_metrics_combine (clusterMetrics [clusterCount ],
270
- run -> glyphs [glyphIndex ].metrics );
268
+ else
269
+ {
270
+ //accumulate metrics in the current cluster
271
+ clusterMetrics [clusterCount - 1 ] = oc_text_metrics_combine (clusterMetrics [clusterCount - 1 ],
272
+ run -> glyphs [glyphIndex ].metrics );
273
+ }
271
274
}
272
275
273
276
//NOTE: bucket graphemes into clusters
274
- u64 * clusterFirstGrapheme = oc_arena_push_array (scratch .arena , u64 , clusterCount );
275
- u64 * clusterGraphemeCount = oc_arena_push_array (scratch .arena , u64 , clusterCount );
277
+ u64 * clusterFirstGraphemes = oc_arena_push_array (scratch .arena , u64 , clusterCount );
278
+ u64 * clusterGraphemeCounts = oc_arena_push_array (scratch .arena , u64 , clusterCount );
276
279
u64 nextGrapheme = 0 ;
277
280
278
- for (u64 clusterIndex = 0 ; clusterIndex < clusterCount ; clusterIndex ++ )
281
+ hb_direction_t bufferDirection = hb_buffer_get_direction (buffer );
282
+ oc_text_direction direction = OC_TEXT_DIRECTION_LTR ;
283
+ if (bufferDirection == HB_DIRECTION_RTL || bufferDirection == HB_DIRECTION_BTT )
284
+ {
285
+ direction = OC_TEXT_DIRECTION_RTL ;
286
+ }
287
+
288
+ for (i64 clusterIndex = (direction == OC_TEXT_DIRECTION_LTR ) ? (0 ) : (clusterCount - 1 );
289
+ clusterIndex < clusterCount && clusterIndex >= 0 ;
290
+ (direction == OC_TEXT_DIRECTION_LTR ) ? (clusterIndex ++ ) : (clusterIndex -- ))
279
291
{
280
- clusterFirstGrapheme [clusterIndex ] = nextGrapheme ;
281
- clusterGraphemeCount [clusterIndex ] = 0 ;
292
+ //NOTE: graphemes potentially straddle several subsequent clusters. We advance to the next grapheme only if all codepoints
293
+ // of the current grapheme are before the current cluster
294
+ if (run -> graphemes [nextGrapheme ].offset + run -> graphemes [nextGrapheme ].count <= clusters [clusterIndex ])
295
+ {
296
+ nextGrapheme ++ ;
297
+ }
298
+
299
+ clusterFirstGraphemes [clusterIndex ] = nextGrapheme ;
300
+ clusterGraphemeCounts [clusterIndex ] = 0 ;
301
+
282
302
for (u64 graphemeIndex = nextGrapheme ; graphemeIndex < run -> graphemeCount ; graphemeIndex ++ )
283
303
{
284
- if ((clusterIndex < clusterCount - 1 )
285
- && run -> graphemes [graphemeIndex ].offset >= clusters [clusterIndex + 1 ])
304
+ bool hasNextCluster = (direction == OC_TEXT_DIRECTION_LTR ) ? (clusterIndex < clusterCount - 1 ) : (clusterIndex > 0 );
305
+ u64 nextClusterIndex = (direction == OC_TEXT_DIRECTION_LTR ) ? (clusterIndex + 1 ) : (clusterIndex - 1 );
306
+
307
+ if (hasNextCluster
308
+ && run -> graphemes [graphemeIndex ].offset >= clusters [nextClusterIndex ])
286
309
{
287
310
//NOTE: End of current cluster.
288
311
// Cluster contains clusterGraphemeCount graphemes starting at clusterFirstGrapheme
289
- nextGrapheme += clusterGraphemeCount [clusterIndex ];
312
+ // We advance to the last grapheme of the current cluster, which could straddle into the next cluster
313
+ OC_DEBUG_ASSERT (clusterGraphemeCounts [clusterIndex ]);
314
+ nextGrapheme += clusterGraphemeCounts [clusterIndex ] - 1 ;
290
315
break ;
291
316
}
292
317
else
293
318
{
294
- // add graphemes to current cluster
295
- clusterGraphemeCount [clusterIndex ]++ ;
319
+ // add grapheme to current cluster
320
+ clusterGraphemeCounts [clusterIndex ]++ ;
296
321
}
297
322
}
298
323
}
324
+ bool * graphemeInit = oc_arena_push_array (scratch .arena , bool , run -> graphemeCount );
325
+ memset (graphemeInit , 0 , sizeof (bool ) * run -> graphemeCount );
299
326
300
327
//NOTE: compute grapheme positions
301
328
for (u64 clusterIndex = 0 ; clusterIndex < clusterCount ; clusterIndex ++ )
302
329
{
303
- u64 firstGrapheme = clusterFirstGrapheme [clusterIndex ];
304
- u64 graphemeCount = clusterGraphemeCount [clusterIndex ];
330
+ u64 clusterFirstGrapheme = clusterFirstGraphemes [clusterIndex ];
331
+ u64 clusterGraphemeCount = clusterGraphemeCounts [clusterIndex ];
305
332
306
333
//TODO: we should try to use ligature caret position if they exist, using
307
334
// hb_ot_layout_get_ligature_carets(). However, all latin fonts with ligatures
@@ -310,34 +337,45 @@ oc_glyph_run* oc_harfbuzz_font_shape(oc_arena* arena,
310
337
// check with someone who does.
311
338
312
339
//NOTE: Fall back to evenly subdividing the cluster size.
313
- //////////////////////////////////////////////////////////////////////////////////////////////
314
- //TODO: this works only for LTR
315
- //////////////////////////////////////////////////////////////////////////////////////////////
316
-
317
340
oc_text_metrics metrics = clusterMetrics [clusterIndex ];
318
- oc_vec2 advance = oc_vec2_mul (1. / graphemeCount , metrics .advance );
341
+ oc_vec2 advance = oc_vec2_mul (1. / clusterGraphemeCount , metrics .advance );
319
342
320
- for (u64 graphemeIndex = 0 ; graphemeIndex < graphemeCount ; graphemeIndex ++ )
343
+ for (u64 clusterGraphemeIndex = 0 ; clusterGraphemeIndex < clusterGraphemeCount ; clusterGraphemeIndex ++ )
321
344
{
322
- oc_vec2 offset = oc_vec2_mul (graphemeIndex , advance );
323
-
324
- oc_text_metrics * graphemeMetrics = & run -> graphemes [firstGrapheme + graphemeIndex ].metrics ;
325
-
326
- graphemeMetrics -> ink = (oc_rect ){
327
- metrics .ink .x + graphemeIndex * advance .x ,
328
- metrics .ink .y ,
329
- metrics .ink .w / graphemeCount ,
330
- metrics .ink .h ,
345
+ u64 graphemeIndex = clusterFirstGrapheme + clusterGraphemeIndex ;
346
+
347
+ oc_text_metrics * graphemeMetrics = & run -> graphemes [graphemeIndex ].metrics ;
348
+
349
+ //TODO: this works only for horizontal (LTR or RTL)layout, revise when we actually support vertical layout!
350
+ oc_text_metrics localMetrics = {
351
+ .ink = {
352
+ metrics .ink .x + clusterGraphemeIndex * advance .x ,
353
+ metrics .ink .y ,
354
+ metrics .ink .w / clusterGraphemeCount ,
355
+ metrics .ink .h ,
356
+ },
357
+ .logical = {
358
+ metrics .logical .x + clusterGraphemeIndex * advance .x ,
359
+ metrics .logical .y ,
360
+ metrics .logical .w / clusterGraphemeCount ,
361
+ metrics .logical .h ,
362
+ },
363
+ .advance = advance ,
331
364
};
332
365
333
- graphemeMetrics -> logical = (oc_rect ){
334
- metrics .logical .x + graphemeIndex * advance .x ,
335
- metrics .logical .y ,
336
- metrics .logical .w / graphemeCount ,
337
- metrics .logical .h ,
338
- };
366
+ bool isInit = graphemeInit [graphemeIndex ];
339
367
340
- graphemeMetrics -> advance = advance ;
368
+ if (!isInit )
369
+ {
370
+ * graphemeMetrics = localMetrics ;
371
+ graphemeInit [graphemeIndex ] = true;
372
+ }
373
+ else
374
+ {
375
+ //NOTE: if a grapheme has already been encountered (i.e. it straddles several clusters),
376
+ // we expand its metrics rather than reset them
377
+ * graphemeMetrics = oc_text_metrics_combine (* graphemeMetrics , localMetrics );
378
+ }
341
379
}
342
380
}
343
381
oc_scratch_end (scratch );
@@ -450,6 +488,33 @@ void oc_text_draw_run(oc_glyph_run* run, f32 fontSize)
450
488
451
489
f32 flipY = (context -> textFlip ) ? -1 : 1 ;
452
490
491
+ oc_vec2 origin = pos ;
492
+
493
+ oc_set_width (2 );
494
+ oc_set_color_rgba (1 , 0 , 0 , 1 );
495
+ for (u64 i = 0 ; i < run -> graphemeCount ; i ++ )
496
+ {
497
+ oc_grapheme_info * grapheme = & run -> graphemes [i ];
498
+ oc_rectangle_stroke (origin .x + grapheme -> metrics .logical .x * scale ,
499
+ origin .y + grapheme -> metrics .logical .y * scale ,
500
+ grapheme -> metrics .logical .w * scale ,
501
+ grapheme -> metrics .logical .h * scale );
502
+ }
503
+ /*
504
+ oc_set_color_rgba(0, 0, 1, 1);
505
+ oc_rectangle_stroke(origin.x + run->metrics.logical.x * scale,
506
+ origin.y + run->metrics.logical.y * scale,
507
+ run->metrics.logical.w * scale,
508
+ run->metrics.logical.h * scale);
509
+
510
+ oc_set_color_rgba(0, 1, 0, 1);
511
+ oc_rectangle_stroke(origin.x + run->metrics.ink.x * scale,
512
+ origin.y + run->metrics.ink.y * scale,
513
+ run->metrics.ink.w * scale,
514
+ run->metrics.ink.h * scale);
515
+ */
516
+ oc_set_color_rgba (0 , 0 , 0 , 1 );
517
+
453
518
for (u64 i = 0 ; i < run -> glyphCount ; i ++ )
454
519
{
455
520
oc_glyph_info * glyph = & run -> glyphs [i ];
@@ -464,6 +529,13 @@ void oc_text_draw_run(oc_glyph_run* run, f32 fontSize)
464
529
465
530
hb_font_draw_glyph (harfbuzzFont -> hbFont , glyph -> index , oc_hbDrawFuncs , & data );
466
531
532
+ /*
533
+ oc_set_width(1);
534
+ oc_rectangle_stroke(origin.x + glyph->metrics.logical.x * scale,
535
+ origin.y + glyph->metrics.logical.y * scale,
536
+ glyph->metrics.logical.w * scale,
537
+ glyph->metrics.logical.h * scale);
538
+ */
467
539
pos .x += glyph -> metrics .advance .x * scale ;
468
540
pos .y += glyph -> metrics .advance .y * scale * flipY ;
469
541
}
0 commit comments