aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPo Lu2024-01-01 11:27:59 +0800
committerPo Lu2024-01-01 11:27:59 +0800
commitf80f1b23bfd277a9db0ee6961a3d9f09c4cba219 (patch)
treef7f167f20fc1076118064e82f5b0d870f22e4f96 /src
parent82bb8de74617b3af083412614c126c82153c2a29 (diff)
downloademacs-f80f1b23bfd277a9db0ee6961a3d9f09c4cba219.tar.gz
emacs-f80f1b23bfd277a9db0ee6961a3d9f09c4cba219.zip
Bring GX point interpolation further into standards compliance
* src/sfnt.c (sfnt_infer_deltas_2): New function; factor much of sfnt_infer_deltas_1 into this function, then modify its treatment of untouched points positioned at their reference points to align with standard GX treatment. (sfnt_infer_deltas_1): Remove all code not concerning anchor point discovery. (main): Adjust tests.
Diffstat (limited to 'src')
-rw-r--r--src/sfnt.c451
1 files changed, 182 insertions, 269 deletions
diff --git a/src/sfnt.c b/src/sfnt.c
index 7625254d0bd..deafaec31e7 100644
--- a/src/sfnt.c
+++ b/src/sfnt.c
@@ -15314,322 +15314,235 @@ sfnt_compute_tuple_scale (struct sfnt_blend *blend, bool intermediate_p,
15314 return scale; 15314 return scale;
15315} 15315}
15316 15316
15317/* Infer point positions for points that have been partially moved 15317/* Move each point in the simple glyph GLYPH between PAIR_START and
15318 within the contour in GLYPH denoted by START and END. */ 15318 PAIR_END to agree with the positions of those two anchor points as
15319 compared with their initial positions recorded within the arrays X
15320 and Y.
15321
15322 The range formed between PAIR_START and PAIR_END may encompass the
15323 upper extreme of the contour between START and END. */
15319 15324
15320static void 15325static void
15321sfnt_infer_deltas_1 (struct sfnt_glyph *glyph, size_t start, 15326sfnt_infer_deltas_2 (struct sfnt_glyph *glyph, size_t pair_start,
15322 size_t end, bool *touched, sfnt_fword *x, 15327 size_t pair_end, size_t start, size_t end,
15323 sfnt_fword *y) 15328 sfnt_fword *x, sfnt_fword *y)
15324{ 15329{
15325 size_t i, pair_start, pair_end, pair_first, j; 15330 size_t j;
15326 sfnt_fword min_pos, max_pos, position; 15331 sfnt_fword min_pos, max_pos, position, d1, d2;
15327 sfnt_fixed ratio, delta; 15332 sfnt_fixed ratio, delta;
15328 15333
15329 pair_start = pair_first = -1; 15334 j = pair_start + 1;
15330
15331 /* Look for pairs of touched points. */
15332 15335
15333 for (i = start; i <= end; ++i) 15336 while (j != pair_end)
15334 { 15337 {
15335 if (!touched[i]) 15338 /* Reset j to the contour's start position if it is about to
15336 continue; 15339 overrun this contour. */
15337 15340
15338 if (pair_start == -1) 15341 if (j > end)
15339 { 15342 {
15340 pair_first = i; 15343 /* The start of the contour might also be the end of this
15341 goto next; 15344 reference point. */
15345 if (start == pair_end)
15346 return;
15347
15348 j = start;
15342 } 15349 }
15343 15350
15344 pair_end = i; 15351 /* Consider the X axis. Set min_pos and max_pos to the
15352 smallest and greatest values along that axis. */
15353 min_pos = MIN (x[pair_start], x[pair_end]);
15354 max_pos = MAX (x[pair_start], x[pair_end]);
15345 15355
15346 /* pair_start to pair_end are now a pair of points, where points 15356 /* Now see if the current point lies between min and
15347 in between should be interpolated. */ 15357 max...
15348 15358
15349 for (j = pair_start + 1; j < pair_end; ++j) 15359 GX interpolation differs from IUP in one important detail:
15360 points are shifted to follow the movement of their reference
15361 points if their positions are identical to those of any of
15362 their reference points, whereas IUP considers such points to
15363 fall within their reference points. */
15364 if (x[j] > min_pos && x[j] < max_pos)
15365 {
15366 /* Interpolate between min_pos and max_pos. */
15367 ratio = sfnt_div_fixed ((sfnt_sub (x[j], min_pos)
15368 * 65536),
15369 (sfnt_sub (max_pos, min_pos)
15370 * 65536));
15371
15372 /* Load the current positions of pair_start and pair_end
15373 along this axis. */
15374 min_pos = MIN (glyph->simple->x_coordinates[pair_start],
15375 glyph->simple->x_coordinates[pair_end]);
15376 max_pos = MAX (glyph->simple->x_coordinates[pair_start],
15377 glyph->simple->x_coordinates[pair_end]);
15378
15379 /* Lerp in between. */
15380 delta = sfnt_sub (max_pos, min_pos);
15381 delta = sfnt_mul_fixed (ratio, delta);
15382 glyph->simple->x_coordinates[j] = min_pos + delta;
15383 }
15384 else
15350 { 15385 {
15351 /* Consider the X axis. Set min_pos and max_pos to the 15386 /* ... otherwise, move point j by the delta of the
15352 smallest and greatest values along that axis. */ 15387 nearest touched point. */
15353 min_pos = MIN (x[pair_start], x[pair_end]); 15388
15354 max_pos = MAX (x[pair_start], x[pair_end]); 15389 /* If min_pos and max_pos are the same, apply
15355 15390 pair_start's delta if it is identical to that of
15356 /* Now see if the current point lies between min and 15391 pair_end, or apply nothing at all otherwise. */
15357 max... */ 15392
15358 if (x[j] >= min_pos && x[j] <= max_pos) 15393 if (min_pos == max_pos)
15359 { 15394 {
15360 /* If min_pos and max_pos are the same, apply 15395 d1 = (glyph->simple->x_coordinates[pair_start]
15361 pair_start's delta if it is identical to that of 15396 - x[pair_start]);
15362 pair_end, or apply nothing at all otherwise. */ 15397 d2 = (glyph->simple->x_coordinates[pair_end]
15398 - x[pair_start]);
15363 15399
15364 if (min_pos == max_pos) 15400 if (d1 == d2)
15365 { 15401 glyph->simple->x_coordinates[j] += d1;
15366 if ((glyph->simple->x_coordinates[pair_start]
15367 - x[pair_start])
15368 == (glyph->simple->x_coordinates[pair_end]
15369 - x[pair_end]))
15370 glyph->simple->x_coordinates[j]
15371 += (glyph->simple->x_coordinates[pair_start]
15372 - x[pair_start]);
15373 15402
15374 continue; 15403 goto consider_y;
15375 } 15404 }
15376 15405
15377 /* Interpolate between min_pos and max_pos. */ 15406 if (x[j] >= max_pos)
15378 ratio = sfnt_div_fixed ((sfnt_sub (x[j], min_pos) 15407 {
15379 * 65536), 15408 position = MAX (glyph->simple->x_coordinates[pair_start],
15380 (sfnt_sub (max_pos, min_pos) 15409 glyph->simple->x_coordinates[pair_end]);
15381 * 65536)); 15410 delta = position - max_pos;
15382
15383 /* Load the current positions of pair_start and pair_end
15384 along this axis. */
15385 min_pos = MIN (glyph->simple->x_coordinates[pair_start],
15386 glyph->simple->x_coordinates[pair_end]);
15387 max_pos = MAX (glyph->simple->x_coordinates[pair_start],
15388 glyph->simple->x_coordinates[pair_end]);
15389
15390 /* Lerp in between. */
15391 delta = sfnt_sub (max_pos, min_pos);
15392 delta = sfnt_mul_fixed (ratio, delta);
15393 glyph->simple->x_coordinates[j] = min_pos + delta;
15394 } 15411 }
15395 else 15412 else
15396 { 15413 {
15397 /* ... otherwise, move point j by the delta of the 15414 position = MIN (glyph->simple->x_coordinates[pair_start],
15398 nearest touched point. */ 15415 glyph->simple->x_coordinates[pair_end]);
15416 delta = position - min_pos;
15417 }
15399 15418
15400 if (x[j] >= max_pos) 15419 glyph->simple->x_coordinates[j] = x[j] + delta;
15401 { 15420 }
15402 position = MAX (glyph->simple->x_coordinates[pair_start],
15403 glyph->simple->x_coordinates[pair_end]);
15404 delta = position - max_pos;
15405 }
15406 else
15407 {
15408 position = MIN (glyph->simple->x_coordinates[pair_start],
15409 glyph->simple->x_coordinates[pair_end]);
15410 delta = position - min_pos;
15411 }
15412 15421
15413 glyph->simple->x_coordinates[j] = x[j] + delta; 15422 consider_y:
15414 } 15423
15424 /* Now, consider the Y axis. */
15425 min_pos = MIN (y[pair_start], y[pair_end]);
15426 max_pos = MAX (y[pair_start], y[pair_end]);
15415 15427
15416 /* Now, consider the Y axis. */ 15428 /* Now see if the current point lies between min and
15417 min_pos = MIN (y[pair_start], y[pair_end]); 15429 max...
15418 max_pos = MAX (y[pair_start], y[pair_end]);
15419 15430
15420 /* Now see if the current point lies between min and 15431 GX interpolation differs from IUP in one important detail:
15421 max... */ 15432 points are shifted to follow the movement of their reference
15422 if (y[j] >= min_pos && y[j] <= max_pos) 15433 points if their positions are identical to those of any of
15434 their reference points, whereas IUP considers such points to
15435 fall within their reference points. */
15436 if (y[j] > min_pos && y[j] < max_pos)
15437 {
15438 /* Interpolate between min_pos and max_pos. */
15439 ratio = sfnt_div_fixed ((sfnt_sub (y[j], min_pos)
15440 * 65536),
15441 (sfnt_sub (max_pos, min_pos)
15442 * 65536));
15443
15444 /* Load the current positions of pair_start and pair_end
15445 along this axis. */
15446 min_pos = MIN (glyph->simple->y_coordinates[pair_start],
15447 glyph->simple->y_coordinates[pair_end]);
15448 max_pos = MAX (glyph->simple->y_coordinates[pair_start],
15449 glyph->simple->y_coordinates[pair_end]);
15450
15451 /* Lerp in between. */
15452 delta = sfnt_sub (max_pos, min_pos);
15453 delta = sfnt_mul_fixed (ratio, delta);
15454 glyph->simple->y_coordinates[j] = min_pos + delta;
15455 }
15456 else
15457 {
15458 /* ... otherwise, move point j by the delta of the
15459 nearest touched point. */
15460
15461 /* If min_pos and max_pos are the same, apply
15462 pair_start's delta if it is identical to that of
15463 pair_end, or apply nothing at all otherwise. */
15464
15465 if (min_pos == max_pos)
15423 { 15466 {
15424 /* If min_pos and max_pos are the same, apply 15467 d1 = (glyph->simple->y_coordinates[pair_start]
15425 pair_start's delta if it is identical to that of 15468 - y[pair_start]);
15426 pair_end, or apply nothing at all otherwise. */ 15469 d2 = (glyph->simple->y_coordinates[pair_end]
15470 - y[pair_start]);
15427 15471
15428 if (min_pos == max_pos) 15472 if (d1 == d2)
15429 { 15473 glyph->simple->y_coordinates[j] += d1;
15430 if ((glyph->simple->y_coordinates[pair_start]
15431 - y[pair_start])
15432 == (glyph->simple->y_coordinates[pair_end]
15433 - y[pair_end]))
15434 glyph->simple->y_coordinates[j]
15435 += (glyph->simple->y_coordinates[pair_start]
15436 - y[pair_start]);
15437 15474
15438 continue; 15475 goto next;
15439 } 15476 }
15440 15477
15441 /* Interpolate between min_pos and max_pos. */ 15478 if (y[j] >= max_pos)
15442 ratio = sfnt_div_fixed ((sfnt_sub (y[j], min_pos) 15479 {
15443 * 65536), 15480 position = MAX (glyph->simple->y_coordinates[pair_start],
15444 (sfnt_sub (max_pos, min_pos) 15481 glyph->simple->y_coordinates[pair_end]);
15445 * 65536)); 15482 delta = position - max_pos;
15446
15447 /* Load the current positions of pair_start and pair_end
15448 along this axis. */
15449 min_pos = MIN (glyph->simple->y_coordinates[pair_start],
15450 glyph->simple->y_coordinates[pair_end]);
15451 max_pos = MAX (glyph->simple->y_coordinates[pair_start],
15452 glyph->simple->y_coordinates[pair_end]);
15453
15454 /* Lerp in between. */
15455 delta = sfnt_sub (max_pos, min_pos);
15456 delta = sfnt_mul_fixed (ratio, delta);
15457 glyph->simple->y_coordinates[j] = min_pos + delta;
15458 } 15483 }
15459 else 15484 else
15460 { 15485 {
15461 /* ... otherwise, move point j by the delta of the 15486 position = MIN (glyph->simple->y_coordinates[pair_start],
15462 nearest touched point. */ 15487 glyph->simple->y_coordinates[pair_end]);
15463 15488 delta = position - min_pos;
15464 if (y[j] >= max_pos)
15465 {
15466 position = MAX (glyph->simple->y_coordinates[pair_start],
15467 glyph->simple->y_coordinates[pair_end]);
15468 delta = position - max_pos;
15469 }
15470 else
15471 {
15472 position = MIN (glyph->simple->y_coordinates[pair_start],
15473 glyph->simple->y_coordinates[pair_end]);
15474 delta = position - min_pos;
15475 }
15476
15477 glyph->simple->y_coordinates[j] = y[j] + delta;
15478 } 15489 }
15490
15491 glyph->simple->y_coordinates[j] = y[j] + delta;
15479 } 15492 }
15480 15493
15481 next: 15494 next:
15482 pair_start = i; 15495 j++;
15483 } 15496 }
15497}
15484 15498
15485 /* If pair_start is set, then lerp points between it and 15499/* Infer point positions for points that have been partially moved
15486 pair_first. */ 15500 within the contour in GLYPH denoted by START and END. */
15487
15488 if (pair_start != (size_t) -1)
15489 {
15490 j = pair_start + 1;
15491
15492 if (j > end)
15493 j = start;
15494
15495 pair_end = pair_first;
15496
15497 while (j != pair_first)
15498 {
15499 /* Consider the X axis. Set min_pos and max_pos to the
15500 smallest and greatest values along that axis. */
15501 min_pos = MIN (x[pair_start], x[pair_end]);
15502 max_pos = MAX (x[pair_start], x[pair_end]);
15503
15504 /* Now see if the current point lies between min and
15505 max... */
15506 if (x[j] >= min_pos && x[j] <= max_pos)
15507 {
15508 /* If min_pos and max_pos are the same, apply
15509 pair_start's delta if it is identical to that of
15510 pair_end, or apply nothing at all otherwise. */
15511 15501
15512 if (min_pos == max_pos) 15502static void
15513 { 15503sfnt_infer_deltas_1 (struct sfnt_glyph *glyph, size_t start,
15514 if ((glyph->simple->x_coordinates[pair_start] 15504 size_t end, bool *touched, sfnt_fword *x,
15515 - x[pair_start]) 15505 sfnt_fword *y)
15516 == (glyph->simple->x_coordinates[pair_end] 15506{
15517 - x[pair_end])) 15507 size_t i, pair_start, pair_end, pair_first;
15518 glyph->simple->x_coordinates[j]
15519 += (glyph->simple->x_coordinates[pair_start]
15520 - x[pair_start]);
15521
15522 goto next_1;
15523 }
15524 15508
15525 /* Interpolate between min_pos and max_pos. */ 15509 pair_start = pair_first = -1;
15526 ratio = sfnt_div_fixed ((sfnt_sub (x[j], min_pos)
15527 * 65536),
15528 (sfnt_sub (max_pos, min_pos)
15529 * 65536));
15530
15531 /* Load the current positions of pair_start and pair_end
15532 along this axis. */
15533 min_pos = MIN (glyph->simple->x_coordinates[pair_start],
15534 glyph->simple->x_coordinates[pair_end]);
15535 max_pos = MAX (glyph->simple->x_coordinates[pair_start],
15536 glyph->simple->x_coordinates[pair_end]);
15537
15538 /* Lerp in between. */
15539 delta = sfnt_sub (max_pos, min_pos);
15540 delta = sfnt_mul_fixed (ratio, delta);
15541 glyph->simple->x_coordinates[j] = min_pos + delta;
15542 }
15543 else
15544 {
15545 /* ... otherwise, move point j by the delta of the
15546 nearest touched point. */
15547 15510
15548 if (x[j] >= max_pos) 15511 /* Look for pairs of touched points. */
15549 {
15550 position = MAX (glyph->simple->x_coordinates[pair_start],
15551 glyph->simple->x_coordinates[pair_end]);
15552 delta = position - max_pos;
15553 }
15554 else
15555 {
15556 position = MIN (glyph->simple->x_coordinates[pair_start],
15557 glyph->simple->x_coordinates[pair_end]);
15558 delta = position - min_pos;
15559 }
15560 15512
15561 glyph->simple->x_coordinates[j] = x[j] + delta; 15513 for (i = start; i <= end; ++i)
15562 } 15514 {
15515 if (!touched[i])
15516 continue;
15563 15517
15564 /* Now, consider the Y axis. */ 15518 if (pair_start == -1)
15565 min_pos = MIN (y[pair_start], y[pair_end]); 15519 {
15566 max_pos = MAX (y[pair_start], y[pair_end]); 15520 pair_first = i;
15521 goto next;
15522 }
15567 15523
15568 /* Now see if the current point lies between min and 15524 pair_end = i;
15569 max... */
15570 if (y[j] >= min_pos && y[j] <= max_pos)
15571 {
15572 /* If min_pos and max_pos are the same, apply
15573 pair_start's delta if it is identical to that of
15574 pair_end, or apply nothing at all otherwise. */
15575 15525
15576 if (min_pos == max_pos) 15526 /* pair_start to pair_end are now a pair of points whose
15577 { 15527 intermediates should be interpolated. */
15578 if ((glyph->simple->y_coordinates[pair_start] 15528 sfnt_infer_deltas_2 (glyph, pair_start, pair_end,
15579 - y[pair_start]) 15529 start, end, x, y);
15580 == (glyph->simple->y_coordinates[pair_end]
15581 - y[pair_end]))
15582 glyph->simple->y_coordinates[j]
15583 += (glyph->simple->y_coordinates[pair_start]
15584 - y[pair_start]);
15585
15586 goto next_1;
15587 }
15588 15530
15589 /* Interpolate between min_pos and max_pos. */ 15531 next:
15590 ratio = sfnt_div_fixed ((sfnt_sub (y[j], min_pos) 15532 pair_start = i;
15591 * 65536), 15533 }
15592 (sfnt_sub (max_pos, min_pos)
15593 * 65536));
15594
15595 /* Load the current positions of pair_start and pair_end
15596 along this axis. */
15597 min_pos = MIN (glyph->simple->y_coordinates[pair_start],
15598 glyph->simple->y_coordinates[pair_end]);
15599 max_pos = MAX (glyph->simple->y_coordinates[pair_start],
15600 glyph->simple->y_coordinates[pair_end]);
15601
15602 /* Lerp in between. */
15603 delta = sfnt_sub (max_pos, min_pos);
15604 delta = sfnt_mul_fixed (ratio, delta);
15605 glyph->simple->y_coordinates[j] = min_pos + delta;
15606 }
15607 else
15608 {
15609 /* ... otherwise, move point j by the delta of the
15610 nearest touched point. */
15611 15534
15612 if (y[j] >= max_pos) 15535 /* If pair_start is set, then lerp points between it and
15613 { 15536 pair_first. */
15614 position = MAX (glyph->simple->y_coordinates[pair_start],
15615 glyph->simple->y_coordinates[pair_end]);
15616 delta = position - max_pos;
15617 }
15618 else
15619 {
15620 position = MIN (glyph->simple->y_coordinates[pair_start],
15621 glyph->simple->y_coordinates[pair_end]);
15622 delta = position - min_pos;
15623 }
15624 15537
15625 glyph->simple->y_coordinates[j] = y[j] + delta; 15538 if (pair_start != (size_t) -1)
15626 } 15539 {
15540 pair_end = pair_first;
15627 15541
15628 next_1: 15542 /* pair_start to pair_end are now a pair of points whose
15629 j++; 15543 intermediates should be interpolated. */
15630 if (j > end) 15544 sfnt_infer_deltas_2 (glyph, pair_start, pair_end,
15631 j = start; 15545 start, end, x, y);
15632 }
15633 } 15546 }
15634} 15547}
15635 15548
@@ -20696,8 +20609,8 @@ main (int argc, char **argv)
20696 return 1; 20609 return 1;
20697 } 20610 }
20698 20611
20699#define FANCY_PPEM 30 20612#define FANCY_PPEM 44
20700#define EASY_PPEM 30 20613#define EASY_PPEM 44
20701 20614
20702 interpreter = NULL; 20615 interpreter = NULL;
20703 head = sfnt_read_head_table (fd, font); 20616 head = sfnt_read_head_table (fd, font);