diff options
| author | Po Lu | 2023-12-25 11:21:15 +0800 |
|---|---|---|
| committer | Po Lu | 2023-12-25 11:21:15 +0800 |
| commit | 995dd36da1df70c55ef2e72d4ff5b2641cc83292 (patch) | |
| tree | f00cd748784e04cb919c9f74e32c540ec0542dca | |
| parent | 62f2c4386259f998442e8098d8a368835a36fb65 (diff) | |
| download | emacs-995dd36da1df70c55ef2e72d4ff5b2641cc83292.tar.gz emacs-995dd36da1df70c55ef2e72d4ff5b2641cc83292.zip | |
Optimize font edge filling loop
* src/sfnt.c (sfnt_fedge_sort): Delete function.
(sfnt_poly_edges_exact): Don't sort edges, iterate through each
instead.
(main): Adjust tests.
| -rw-r--r-- | src/sfnt.c | 87 |
1 files changed, 33 insertions, 54 deletions
diff --git a/src/sfnt.c b/src/sfnt.c index 553b828a2db..d945342c264 100644 --- a/src/sfnt.c +++ b/src/sfnt.c | |||
| @@ -4946,36 +4946,6 @@ sfnt_insert_raster_step (struct sfnt_step_raster *raster, | |||
| 4946 | step->coverage += coverage; | 4946 | step->coverage += coverage; |
| 4947 | } | 4947 | } |
| 4948 | 4948 | ||
| 4949 | /* Sort an array of SIZE edges to increase by bottom Y position, in | ||
| 4950 | preparation for building spans. | ||
| 4951 | |||
| 4952 | Insertion sort is used because there are usually not very many | ||
| 4953 | edges, and anything larger would bloat up the code. */ | ||
| 4954 | |||
| 4955 | static void | ||
| 4956 | sfnt_fedge_sort (struct sfnt_fedge *edges, size_t size) | ||
| 4957 | { | ||
| 4958 | ssize_t i, j; | ||
| 4959 | struct sfnt_fedge edge; | ||
| 4960 | |||
| 4961 | for (i = 1; i < size; ++i) | ||
| 4962 | { | ||
| 4963 | edge = edges[i]; | ||
| 4964 | j = i - 1; | ||
| 4965 | |||
| 4966 | /* Comparing truncated values yields a faint speedup, for not as | ||
| 4967 | many edges must be moved as would be otherwise. */ | ||
| 4968 | while (j >= 0 && ((int) edges[j].bottom | ||
| 4969 | > (int) edge.bottom)) | ||
| 4970 | { | ||
| 4971 | edges[j + 1] = edges[j]; | ||
| 4972 | j--; | ||
| 4973 | } | ||
| 4974 | |||
| 4975 | edges[j + 1] = edge; | ||
| 4976 | } | ||
| 4977 | } | ||
| 4978 | |||
| 4979 | /* Draw EDGES, an unsorted array of polygon edges of size NEDGES. | 4949 | /* Draw EDGES, an unsorted array of polygon edges of size NEDGES. |
| 4980 | 4950 | ||
| 4981 | Transform EDGES into an array of steps representing a raster with | 4951 | Transform EDGES into an array of steps representing a raster with |
| @@ -4993,23 +4963,19 @@ sfnt_poly_edges_exact (struct sfnt_fedge *edges, size_t nedges, | |||
| 4993 | sfnt_step_raster_proc proc, void *dcontext) | 4963 | sfnt_step_raster_proc proc, void *dcontext) |
| 4994 | { | 4964 | { |
| 4995 | int y; | 4965 | int y; |
| 4996 | size_t size, e; | 4966 | size_t size, e, edges_processed; |
| 4997 | struct sfnt_fedge *active, **prev, *a; | 4967 | struct sfnt_fedge *active, **prev, *a, sentinel; |
| 4998 | struct sfnt_step_raster raster; | 4968 | struct sfnt_step_raster raster; |
| 4999 | struct sfnt_step_chunk *next, *last; | 4969 | struct sfnt_step_chunk *next, *last; |
| 5000 | 4970 | ||
| 5001 | if (!height) | 4971 | if (!height) |
| 5002 | return; | 4972 | return; |
| 5003 | 4973 | ||
| 5004 | /* Sort edges to ascend by Y-order. Once again, remember: cartesian | ||
| 5005 | coordinates. */ | ||
| 5006 | sfnt_fedge_sort (edges, nedges); | ||
| 5007 | |||
| 5008 | /* Step down line by line. Find active edges. */ | 4974 | /* Step down line by line. Find active edges. */ |
| 5009 | 4975 | ||
| 5010 | y = sfnt_floor_fixed (MAX (0, edges[0].bottom)); | 4976 | y = sfnt_floor_fixed (MAX (0, edges[0].bottom)); |
| 5011 | e = 0; | 4977 | e = edges_processed = 0; |
| 5012 | active = NULL; | 4978 | active = &sentinel; |
| 5013 | 4979 | ||
| 5014 | /* Allocate the array of edges. */ | 4980 | /* Allocate the array of edges. */ |
| 5015 | 4981 | ||
| @@ -5023,20 +4989,28 @@ sfnt_poly_edges_exact (struct sfnt_fedge *edges, size_t nedges, | |||
| 5023 | 4989 | ||
| 5024 | for (; y != height; y += 1) | 4990 | for (; y != height; y += 1) |
| 5025 | { | 4991 | { |
| 5026 | /* Add in new edges keeping them sorted. */ | 4992 | /* Run over the whole array on each iteration of this loop; |
| 5027 | for (; e < nedges && edges[e].bottom < y + 1; ++e) | 4993 | experiments demonstrate this is faster for the majority of |
| 4994 | glyphs. */ | ||
| 4995 | for (e = 0; e < nedges; ++e) | ||
| 5028 | { | 4996 | { |
| 5029 | if (edges[e].top > y) | 4997 | /* Although edges is unsorted, edges which have already been |
| 4998 | processed will see their next fields set, and can thus be | ||
| 4999 | disregarded. */ | ||
| 5000 | if (!edges[e].next | ||
| 5001 | && (edges[e].bottom < y + 1) | ||
| 5002 | && (edges[e].top > y)) | ||
| 5030 | { | 5003 | { |
| 5031 | /* Find where to place this edge. */ | 5004 | /* As steps generated from each edge are sorted at the |
| 5032 | for (prev = &active; (a = *prev); prev = &(a->next)) | 5005 | time of their insertion, sorting the list of active |
| 5033 | { | 5006 | edges itself is redundant. */ |
| 5034 | if (a->x > edges[e].x) | 5007 | edges[e].next = active; |
| 5035 | break; | 5008 | active = &edges[e]; |
| 5036 | } | 5009 | |
| 5037 | 5010 | /* Increment the counter recording the number of edges | |
| 5038 | edges[e].next = *prev; | 5011 | processed, which is used to terminate this loop early |
| 5039 | *prev = &edges[e]; | 5012 | once all have been processed. */ |
| 5013 | edges_processed++; | ||
| 5040 | } | 5014 | } |
| 5041 | } | 5015 | } |
| 5042 | 5016 | ||
| @@ -5044,7 +5018,7 @@ sfnt_poly_edges_exact (struct sfnt_fedge *edges, size_t nedges, | |||
| 5044 | removing it if it does not overlap with the next | 5018 | removing it if it does not overlap with the next |
| 5045 | scanline. */ | 5019 | scanline. */ |
| 5046 | 5020 | ||
| 5047 | for (prev = &active; (a = *prev);) | 5021 | for (prev = &active; (a = *prev) != &sentinel;) |
| 5048 | { | 5022 | { |
| 5049 | float x_top, x_bot, x_min, x_max; | 5023 | float x_top, x_bot, x_min, x_max; |
| 5050 | float y_top, y_bot; | 5024 | float y_top, y_bot; |
| @@ -5371,11 +5345,15 @@ be as well. */ | |||
| 5371 | if (a->top < y + 1) | 5345 | if (a->top < y + 1) |
| 5372 | *prev = a->next; | 5346 | *prev = a->next; |
| 5373 | else | 5347 | else |
| 5348 | /* This edge doesn't intersect with the next scanline; | ||
| 5349 | remove it from the list. After the edge at hand is so | ||
| 5350 | deleted from the list, its next field remains set, | ||
| 5351 | excluding it from future consideration. */ | ||
| 5374 | prev = &a->next; | 5352 | prev = &a->next; |
| 5375 | } | 5353 | } |
| 5376 | 5354 | ||
| 5377 | /* Break if all is done. */ | 5355 | /* Break if all is done. */ |
| 5378 | if (!active && e == nedges) | 5356 | if (active == &sentinel && edges_processed == nedges) |
| 5379 | break; | 5357 | break; |
| 5380 | } | 5358 | } |
| 5381 | 5359 | ||
| @@ -21139,7 +21117,7 @@ main (int argc, char **argv) | |||
| 21139 | 21117 | ||
| 21140 | clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start); | 21118 | clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start); |
| 21141 | 21119 | ||
| 21142 | for (i = 0; i < 800; ++i) | 21120 | for (i = 0; i < 12800; ++i) |
| 21143 | { | 21121 | { |
| 21144 | xfree (raster); | 21122 | xfree (raster); |
| 21145 | raster = (*test_raster_glyph_outline) (outline); | 21123 | raster = (*test_raster_glyph_outline) (outline); |
| @@ -21265,7 +21243,8 @@ main (int argc, char **argv) | |||
| 21265 | printf ("time spent building edges: %lld sec %ld nsec\n", | 21243 | printf ("time spent building edges: %lld sec %ld nsec\n", |
| 21266 | (long long) sub1.tv_sec, sub1.tv_nsec); | 21244 | (long long) sub1.tv_sec, sub1.tv_nsec); |
| 21267 | printf ("time spent rasterizing: %lld sec %ld nsec\n", | 21245 | printf ("time spent rasterizing: %lld sec %ld nsec\n", |
| 21268 | (long long) sub2.tv_sec / 800, sub2.tv_nsec / 800); | 21246 | (long long) sub2.tv_sec / 12800, |
| 21247 | sub2.tv_nsec / 12800); | ||
| 21269 | 21248 | ||
| 21270 | xfree (outline); | 21249 | xfree (outline); |
| 21271 | } | 21250 | } |