GCC Code Coverage Report


Directory: ../src/
File: /home/joels/Current/lispbm/src/extensions/schrift.c
Date: 2025-10-27 19:12:55
Exec Total Coverage
Lines: 587 855 68.7%
Functions: 47 52 90.4%
Branches: 246 473 52.0%

Line Branch Exec Source
1 /* This file is part of libschrift.
2 *
3 * © 2019-2022 Thomas Oltmann and contributors
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
16
17 /* Adapted to LBM by Joel Svensson in 2025 */
18
19 #include <assert.h>
20 #include <errno.h>
21 #include <math.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdbool.h>
26
27 #include "schrift.h"
28
29 // LBM interoperation
30 #include <extensions/display_extensions.h>
31 #include <lbm_memory.h>
32
33 #define SCHRIFT_VERSION "0.10.2"
34
35 #define FILE_MAGIC_ONE 0x00010000
36 #define FILE_MAGIC_TWO 0x74727565
37
38 #define HORIZONTAL_KERNING 0x01
39 #define MINIMUM_KERNING 0x02
40 #define CROSS_STREAM_KERNING 0x04
41 #define OVERRIDE_KERNING 0x08
42
43 #define POINT_IS_ON_CURVE 0x01
44 #define X_CHANGE_IS_SMALL 0x02
45 #define Y_CHANGE_IS_SMALL 0x04
46 #define REPEAT_FLAG 0x08
47 #define X_CHANGE_IS_ZERO 0x10
48 #define X_CHANGE_IS_POSITIVE 0x10
49 #define Y_CHANGE_IS_ZERO 0x20
50 #define Y_CHANGE_IS_POSITIVE 0x20
51
52 #define OFFSETS_ARE_LARGE 0x001
53 #define ACTUAL_XY_OFFSETS 0x002
54 #define GOT_A_SINGLE_SCALE 0x008
55 #define THERE_ARE_MORE_COMPONENTS 0x020
56 #define GOT_AN_X_AND_Y_SCALE 0x040
57 #define GOT_A_SCALE_MATRIX 0x080
58
59 //define GPOS lookup-type:
60 #define SINGLE_ADJUSTMENT 1
61 #define PAIR_ADJUSTMENT 2
62 #define CURSIVE_ATTACHMENT 3
63 #define MARK_TO_BASE_ATTACHMENT 4
64 #define MARK_TO_LIGATURE_ATTACHMENT 5
65 #define MARK_TO_MARK_ATTACHMENT 6
66 #define CONTEXT_POSITIONING 7
67 #define CHAINED_CONTEXT_POSITIONING 8
68 #define EXTENSION_POSITIONING 9
69
70 //VALUE RECORD FLAGS
71 #define X_PLACEMENT 0x0001 // Includes horizontal adjustment for placement.
72 #define Y_PLACEMENT 0x0002 // Includes vertical adjustment for placement.
73 #define X_ADVANCE 0x0004 // Includes horizontal adjustment for advance.
74 #define Y_ADVANCE 0x0008 // Includes vertical adjustment for advance.
75 #define X_PLACEMENT_DEVICE 0x0010 // Includes Device table (non-variable font) / VariationIndex table (variable font) for horizontal placement.
76 #define Y_PLACEMENT_DEVICE 0x0020 // Includes Device table (non-variable font) / VariationIndex table (variable font) for vertical placement.
77 #define X_ADVANCE_DEVICE 0x0040 // Includes Device table (non-variable font) / VariationIndex table (variable font) for horizontal advance.
78 #define Y_ADVANCE_DEVICE 0x0080 // Includes Device table (non-variable font) / VariationIndex table (variable font) for vertical advance.
79
80 /* macros */
81 #define MIN(a, b) ((a) < (b) ? (a) : (b))
82 #define SIGN(x) (((x) > 0) - ((x) < 0))
83
84 /* structs */
85 typedef struct Point Point;
86 typedef struct Line Line;
87 typedef struct Curve Curve;
88 typedef struct Cell Cell;
89 typedef struct Outline Outline;
90 typedef struct Raster Raster;
91
92 struct Point { float x, y; };
93 struct Line { uint_least16_t beg, end; };
94 struct Curve { uint_least16_t beg, end, ctrl; };
95 struct Cell { float area, cover; };
96
97 struct Outline
98 {
99 Point *points;
100 Curve *curves;
101 Line *lines;
102 uint_least16_t numPoints;
103 uint_least16_t capPoints;
104 uint_least16_t numCurves;
105 uint_least16_t capCurves;
106 uint_least16_t numLines;
107 uint_least16_t capLines;
108 };
109
110 struct Raster
111 {
112 Cell *cells;
113 int width;
114 int height;
115 };
116
117 /* function declarations */
118 /* generic utility functions */
119 static inline int fast_floor(float x);
120 static inline int fast_ceil (float x);
121 /* simple mathematical operations */
122 static Point midpoint(Point a, Point b);
123 static void transform_points(unsigned int numPts, Point *points, float trf[6]);
124 static void clip_points(unsigned int numPts, Point *points, int width, int height);
125 /* 'outline' data structure management */
126 static int init_outline(Outline *outl);
127 static void free_outline(Outline *outl);
128 static int grow_points (Outline *outl);
129 static int grow_curves (Outline *outl);
130 static int grow_lines (Outline *outl);
131 /* TTF parsing utilities */
132 static inline int is_safe_offset(SFT_Font *font, uint_fast32_t offset, uint_fast32_t margin);
133 static void *csearch(const void *key, const void *base,
134 size_t nmemb, size_t size, int (*compar)(const void *, const void *));
135 static int cmpu16(const void *a, const void *b);
136 static int cmpu32(const void *a, const void *b);
137 static inline uint_least8_t getu8 (SFT_Font *font, uint_fast32_t offset);
138 static inline int_least8_t geti8 (SFT_Font *font, uint_fast32_t offset);
139 static inline uint_least16_t getu16(SFT_Font *font, uint_fast32_t offset);
140 static inline int_least16_t geti16(SFT_Font *font, uint_fast32_t offset);
141 static inline uint_least32_t getu32(SFT_Font *font, uint_fast32_t offset);
142 static int gettable(SFT_Font *font, char tag[4], uint_fast32_t *offset);
143 /* codepoint to glyph id translation */
144 static int cmap_fmt4(SFT_Font *font, uint_fast32_t table, SFT_UChar charCode, uint_fast32_t *glyph);
145 static int cmap_fmt6(SFT_Font *font, uint_fast32_t table, SFT_UChar charCode, uint_fast32_t *glyph);
146 static int glyph_id(SFT_Font *font, SFT_UChar charCode, uint_fast32_t *glyph);
147 /* glyph metrics lookup */
148 static int hor_metrics(SFT_Font *font, uint_fast32_t glyph, int *advanceWidth, int *leftSideBearing);
149 static int glyph_bbox(const SFT *sft, uint_fast32_t outline, int box[4]);
150 /* decoding outlines */
151 static int outline_offset(SFT_Font *font, uint_fast32_t glyph, uint_fast32_t *offset);
152 static int simple_flags(SFT_Font *font, uint_fast32_t *offset, uint_fast16_t numPts, uint8_t *flags);
153 static int simple_points(SFT_Font *font, uint_fast32_t offset, uint_fast16_t numPts, uint8_t *flags, Point *points);
154 static int decode_contour(uint8_t *flags, uint_fast16_t basePoint, uint_fast16_t count, Outline *outl);
155 static int simple_outline(SFT_Font *font, uint_fast32_t offset, unsigned int numContours, Outline *outl);
156 static int compound_outline(SFT_Font *font, uint_fast32_t offset, int recDepth, Outline *outl);
157 static int decode_outline(SFT_Font *font, uint_fast32_t offset, int recDepth, Outline *outl);
158 /* tesselation */
159 static int is_flat(Outline *outl, Curve curve);
160 static int tesselate_curve(Curve curve, Outline *outl);
161 static int tesselate_curves(Outline *outl);
162 /* silhouette rasterization */
163 static void draw_line(Raster buf, Point origin, Point goal);
164 static void draw_lines(Outline *outl, Raster buf);
165 /* post-processing */
166 static void post_process(Raster buf, image_buffer_t *image);
167 /* glyph rendering */
168 static int render_outline(Outline *outl, float transform[6], image_buffer_t * image);
169
170 /* function implementations */
171 int
172 12 sft_lmetrics(const SFT *sft, SFT_LMetrics *metrics)
173 {
174 float factor;
175 uint_fast32_t hhea;
176 12 memset(metrics, 0, sizeof *metrics);
177
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (gettable(sft->font, "hhea", &hhea) < 0)
178 return -1;
179
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (!is_safe_offset(sft->font, hhea, 36))
180 return -1;
181 12 factor = sft->yScale / sft->font->unitsPerEm;
182 12 metrics->ascender = geti16(sft->font, hhea + 4) * factor;
183 12 metrics->descender = geti16(sft->font, hhea + 6) * factor;
184 12 metrics->lineGap = geti16(sft->font, hhea + 8) * factor;
185 12 return 0;
186 }
187
188 int
189 5669 sft_lookup(const SFT *sft, SFT_UChar codepoint, SFT_Glyph *glyph)
190 {
191 5669 return glyph_id(sft->font, codepoint, glyph);
192 }
193
194 int
195 295 sft_gmetrics(const SFT *sft, SFT_Glyph glyph, SFT_GMetrics *metrics)
196 {
197 int adv, lsb;
198 295 float xScale = sft->xScale / sft->font->unitsPerEm;
199 uint_fast32_t outline;
200 int bbox[4];
201
202 295 memset(metrics, 0, sizeof *metrics);
203
204
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 295 times.
295 if (hor_metrics(sft->font, glyph, &adv, &lsb) < 0)
205 return -1;
206 295 metrics->advanceWidth = (float)adv * xScale;
207 295 metrics->leftSideBearing = (float)lsb * xScale + sft->xOffset;
208
209
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 295 times.
295 if (outline_offset(sft->font, glyph, &outline) < 0)
210 return -1;
211
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 286 times.
295 if (!outline)
212 9 return 0;
213
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 286 times.
286 if (glyph_bbox(sft, outline, bbox) < 0)
214 return -1;
215 286 metrics->minWidth = bbox[2] - bbox[0] + 1;
216 286 metrics->minHeight = bbox[3] - bbox[1] + 1;
217
1/2
✓ Branch 0 taken 286 times.
✗ Branch 1 not taken.
286 metrics->yOffset = sft->flags & SFT_DOWNWARD_Y ? -bbox[3] : bbox[1];
218
219 286 return 0;
220 }
221
222
223 // Check if a glyph is in a coverage table.
224 // A covered glyph is a glyph to which the property at hand applies...
225 5044 static int is_glyph_covered(const SFT *sft, uint32_t coverage_table_offset, SFT_Glyph g) {
226
227 5044 uint32_t offset = coverage_table_offset;
228
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5044 times.
5044 if (!is_safe_offset(sft->font, offset, 6)) // jump into header at pos
229 return -1;
230 //uint16_t format = getu16(sft->font, offset);
231 5044 uint16_t num = getu16(sft->font, offset+2);
232
233
2/2
✓ Branch 0 taken 469196 times.
✓ Branch 1 taken 4444 times.
473640 for (uint16_t i = 0; i < num; i ++) {
234 469196 uint16_t glyphid = getu16(sft->font, offset+4+(i * 2));
235
2/2
✓ Branch 0 taken 600 times.
✓ Branch 1 taken 468596 times.
469196 if (glyphid == g) return (int)i; //lookup index
236 }
237 4444 return -1;
238 }
239
240 // given glyph 2 of a pair and a pair-set related to glyp 1 you get pair-adjustment value
241 600 static int get_pair_x_adjustment(const SFT *sft, uint32_t pair_set_offset, SFT_Glyph g, float *x_adj) {
242 600 uint32_t offset = pair_set_offset;
243
244 600 uint16_t numPairs = getu16(sft->font, offset);
245
246
2/2
✓ Branch 0 taken 2352 times.
✓ Branch 1 taken 600 times.
2952 for (uint16_t i = 0; i < numPairs; i ++) {
247 2352 uint16_t glyph = getu16(sft->font, offset + 2 + (i * 4));
248 2352 int16_t x_adjust = geti16(sft->font, offset + 2 + (i * 4) + 2);
249
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2352 times.
2352 if (glyph == g) {
250 *x_adj = ((float) x_adjust) / sft->font->unitsPerEm * sft->xScale;
251 return 1;
252 }
253 }
254 600 return 0;
255 }
256
257 // -1 if there is no pair adjustment table
258 // or if it is in a format we are not concerned with.
259 // TODO: font should be a const SFT_Font * here I think.
260 // but gettable discards the constness
261 14 static int locate_pair_adjustment_table(SFT_Font *font)
262 {
263 uint_fast32_t offset;
264
265
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (gettable(font, "GPOS", &offset) < 0)
266 return -1;
267
268
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (!is_safe_offset(font, offset, 10))
269 return -1;
270 14 uint16_t ll = getu16(font, offset + 8);
271
272 14 offset+=ll;
273 //TODO: Check if offset is safe? (corrupt font if not at this point);
274 14 uint16_t lookupListCount = getu16(font, offset);
275
276
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 while (lookupListCount) {
277 14 uint_fast32_t tmp_offs = offset + 2;
278
279 14 uint16_t table = getu16(font, tmp_offs);
280 14 uint32_t loffset = offset + table;
281 14 uint16_t lookupType = getu16(font, loffset );
282 //uint16_t lookupFlag = getu16(font, loffset + 2);
283 14 uint16_t subTableCount = getu16(font, loffset + 4);
284
285
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (lookupType == PAIR_ADJUSTMENT) {
286
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (!is_safe_offset(font, loffset, 6)) {
287 return -1;
288 }
289
290
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (!is_safe_offset(font, loffset, (subTableCount * sizeof(uint16_t)))) {
291 return -1;
292 }
293 // pair adjustment subtable found
294 14 uint16_t subtableOffset = getu16(font, loffset + 6);
295 14 return (int)(loffset + subtableOffset);
296 }
297 lookupListCount--;
298 }
299 return -1;
300 }
301
302 // TODO: font should probably have type const SFT_Font * but getu16 discards.
303 14 static int locate_pair_adjust_coverage_table(SFT_Font *font) {
304
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (font->pairAdjustOffset < 0) return -1;
305
306 // If this function is called, pairAdjustOffset will be > 0
307 14 uint32_t offset = (uint32_t)font->pairAdjustOffset;
308 14 uint16_t coverageOffset = getu16(font, offset + 2); // coverage index in pair adjust header
309 14 return (int)(offset + coverageOffset);
310 }
311
312 5044 bool sft_gpos_kerning(const SFT *sft, SFT_Glyph leftGlyph, SFT_Glyph rightGlyph, SFT_Kerning *kerning) {
313
314
1/2
✓ Branch 0 taken 5044 times.
✗ Branch 1 not taken.
5044 if ((sft->font->pairAdjustOffset <= 0) ||
315
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5044 times.
5044 (sft->font->pairAdjustCoverageOffset <= 0)) {
316 return false;
317 }
318
319 5044 memset(kerning, 0, sizeof *kerning);
320
321 5044 uint32_t coverage_tab = (uint32_t)sft->font->pairAdjustCoverageOffset;
322 5044 uint32_t pair_adj_tab = (uint32_t)sft->font->pairAdjustOffset;
323
324 5044 int glyph_cover = is_glyph_covered(sft, coverage_tab, leftGlyph);
325
2/2
✓ Branch 0 taken 600 times.
✓ Branch 1 taken 4444 times.
5044 if (glyph_cover >= 0) {
326 600 uint16_t pairSetOffset = getu16(sft->font, pair_adj_tab + 10 + ((uint32_t)glyph_cover * 2));
327 600 uint32_t coffset = pair_adj_tab + pairSetOffset;
328
329 600 float x_adjust = 0;
330 600 get_pair_x_adjustment(sft, coffset, rightGlyph, &x_adjust);
331 600 kerning->xShift = x_adjust;
332 600 return true;
333 }
334 4444 return false;
335 }
336
337 int
338 5044 sft_kerning(const SFT *sft, SFT_Glyph leftGlyph, SFT_Glyph rightGlyph,
339 SFT_Kerning *kerning)
340 {
341 void *match;
342 uint_fast32_t offset;
343 unsigned int numTables, numPairs, length, format, flags;
344 int value;
345 uint8_t key[4];
346
347 5044 memset(kerning, 0, sizeof *kerning);
348
349
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5044 times.
5044 if (gettable(sft->font, "kern", &offset) < 0)
350 return 0;
351
352 /* Read kern table header. */
353
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5044 times.
5044 if (!is_safe_offset(sft->font, offset, 4))
354 return -1;
355
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5044 times.
5044 if (getu16(sft->font, offset) != 0)
356 return 0;
357 5044 numTables = getu16(sft->font, offset + 2);
358 5044 offset += 4;
359
360
2/2
✓ Branch 0 taken 5044 times.
✓ Branch 1 taken 5044 times.
10088 while (numTables > 0) {
361 /* Read subtable header. */
362
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5044 times.
5044 if (!is_safe_offset(sft->font, offset, 6))
363 return -1;
364 5044 length = getu16(sft->font, offset + 2);
365 5044 format = getu8 (sft->font, offset + 4);
366 5044 flags = getu8 (sft->font, offset + 5);
367 5044 offset += 6;
368
369
3/6
✓ Branch 0 taken 5044 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5044 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5044 times.
✗ Branch 5 not taken.
5044 if (format == 0 && (flags & HORIZONTAL_KERNING) && !(flags & MINIMUM_KERNING)) {
370 /* Read format 0 header. */
371
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5044 times.
5044 if (!is_safe_offset(sft->font, offset, 8))
372 return -1;
373 5044 numPairs = getu16(sft->font, offset);
374 5044 offset += 8;
375 /* Look up character code pair via binary search. */
376 5044 key[0] = (leftGlyph >> 8) & 0xFF;
377 5044 key[1] = leftGlyph & 0xFF;
378 5044 key[2] = (rightGlyph >> 8) & 0xFF;
379 5044 key[3] = rightGlyph & 0xFF;
380
2/2
✓ Branch 0 taken 558 times.
✓ Branch 1 taken 4486 times.
5044 if ((match = bsearch(key, sft->font->memory + offset,
381 numPairs, 6, cmpu32)) != NULL) {
382
383 558 value = geti16(sft->font, (uint_fast32_t) ((uint8_t *) match - sft->font->memory + 4));
384
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 558 times.
558 if (flags & CROSS_STREAM_KERNING) {
385 kerning->yShift += (float)value;
386 } else {
387 558 kerning->xShift += (float)value;
388 }
389 }
390
391 }
392
393 5044 offset += length;
394 5044 --numTables;
395 }
396
397 5044 kerning->xShift = kerning->xShift / sft->font->unitsPerEm * sft->xScale;
398 5044 kerning->yShift = kerning->yShift / sft->font->unitsPerEm * sft->yScale;
399
400 5044 return 0;
401 }
402
403 int
404 97 sft_render(const SFT *sft, SFT_Glyph glyph, image_buffer_t * image)
405 {
406 uint_fast32_t outline;
407 float transform[6];
408 int bbox[4];
409 Outline outl;
410 97 int r = 0;
411
412
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
97 if (outline_offset(sft->font, glyph, &outline) < 0)
413 return -1;
414
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 94 times.
97 if (!outline)
415 3 return 0;
416
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if (glyph_bbox(sft, outline, bbox) < 0)
417 return -1;
418 /* Set up the transformation matrix such that
419 * the transformed bounding boxes min corner lines
420 * up with the (0, 0) point. */
421 94 transform[0] = sft->xScale / sft->font->unitsPerEm;
422 94 transform[1] = 0.0f;
423 94 transform[2] = 0.0f;
424 94 transform[4] = sft->xOffset - (float)bbox[0];
425
1/2
✓ Branch 0 taken 94 times.
✗ Branch 1 not taken.
94 if (sft->flags & SFT_DOWNWARD_Y) {
426 94 transform[3] = -sft->yScale / sft->font->unitsPerEm;
427 94 transform[5] = (float)bbox[3] - sft->yOffset;
428 } else {
429 transform[3] = +sft->yScale / sft->font->unitsPerEm;
430 transform[5] = sft->yOffset - (float)bbox[1];
431 }
432
433 94 memset(&outl, 0, sizeof outl);
434
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if ((r = init_outline(&outl)) < 0)
435 goto failure;
436
437
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if ((r = decode_outline(sft->font, outline, 0, &outl)) < 0)
438 goto failure;
439
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if ((r = render_outline(&outl, transform, image)) < 0)
440 goto failure;
441
442 94 free_outline(&outl);
443 94 return 0;
444
445 failure:
446 free_outline(&outl);
447 return r;
448 }
449
450 /* TODO maybe we should use long here instead of int. */
451 static inline int
452 6411 fast_floor(float x)
453 {
454 6411 int i = (int) x;
455 6411 return i - (i > x);
456 }
457
458 static inline int
459 6151 fast_ceil(float x)
460 {
461 6151 int i = (int) x;
462 6151 return i + (i < x);
463 }
464
465 int
466 15 init_font(SFT_Font *font)
467 {
468 uint_fast32_t scalerType, head, hhea;
469
470 15 font->unitsPerEm = 0;
471 15 font->locaFormat = 0;
472 15 font->numLongHmtx = 0;
473
474
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 14 times.
15 if (!is_safe_offset(font, 0, 12))
475 1 return -1;
476 /* Check for a compatible scalerType (magic number). */
477 14 scalerType = getu32(font, 0);
478
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
14 if (scalerType != FILE_MAGIC_ONE && scalerType != FILE_MAGIC_TWO)
479 return -1;
480
481
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (gettable(font, "head", &head) < 0)
482 return -1;
483
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (!is_safe_offset(font, head, 54))
484 return -1;
485 14 font->unitsPerEm = getu16(font, head + 18);
486 14 font->locaFormat = geti16(font, head + 50);
487
488
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (gettable(font, "hhea", &hhea) < 0)
489 return -1;
490
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (!is_safe_offset(font, hhea, 36))
491 return -1;
492 14 font->numLongHmtx = getu16(font, hhea + 34);
493
494 14 int pairAdjust = locate_pair_adjustment_table(font);
495 14 font->pairAdjustOffset = pairAdjust;
496
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (pairAdjust >= 0) {
497 14 int coverage = locate_pair_adjust_coverage_table(font);
498 14 font->pairAdjustCoverageOffset = coverage;
499 }
500 14 return 0;
501 }
502
503 15 void free_font(SFT_Font *font) {
504 (void) font;
505 // Dont need to do anything here.
506 15 }
507
508 static Point
509 1627 midpoint(Point a, Point b)
510 {
511 3254 return (Point) {
512 1627 0.5f * (a.x + b.x),
513 1627 0.5f * (a.y + b.y)
514 };
515 }
516
517 /* Applies an affine linear transformation matrix to a set of points. */
518 static void
519 94 transform_points(unsigned int numPts, Point *points, float trf[6])
520 {
521 Point pt;
522 unsigned int i;
523
2/2
✓ Branch 0 taken 4065 times.
✓ Branch 1 taken 94 times.
4159 for (i = 0; i < numPts; ++i) {
524 4065 pt = points[i];
525 4065 points[i] = (Point) {
526 4065 pt.x * trf[0] + pt.y * trf[2] + trf[4],
527 4065 pt.x * trf[1] + pt.y * trf[3] + trf[5]
528 };
529 }
530 94 }
531
532 static void
533 94 clip_points(unsigned int numPts, Point *points, int width, int height)
534 {
535 Point pt;
536 unsigned int i;
537
538
2/2
✓ Branch 0 taken 4065 times.
✓ Branch 1 taken 94 times.
4159 for (i = 0; i < numPts; ++i) {
539 4065 pt = points[i];
540
541
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4065 times.
4065 if (pt.x < 0.0f) {
542 points[i].x = 0.0f;
543 }
544
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4065 times.
4065 if (pt.x >= width) {
545 points[i].x = nextafterf((float)width, 0.0f);
546 }
547
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 4045 times.
4065 if (pt.y < 0.0f) {
548 20 points[i].y = 0.0f;
549 }
550
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4065 times.
4065 if (pt.y >= height) {
551 points[i].y = nextafterf((float)height, 0.0f);
552 }
553 }
554 94 }
555
556 static int
557 94 init_outline(Outline *outl)
558 {
559 /* TODO Smaller initial allocations */
560 94 outl->numPoints = 0;
561 94 outl->capPoints = 64;
562
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if (!(outl->points = lbm_malloc(outl->capPoints * sizeof *outl->points)))
563 return SFT_MEM_ERROR;
564 94 outl->numCurves = 0;
565 94 outl->capCurves = 64;
566
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if (!(outl->curves = lbm_malloc(outl->capCurves * sizeof *outl->curves)))
567 return SFT_MEM_ERROR;
568 94 outl->numLines = 0;
569 94 outl->capLines = 64;
570
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if (!(outl->lines = lbm_malloc(outl->capLines * sizeof *outl->lines)))
571 return SFT_MEM_ERROR;
572 94 return 0;
573 }
574
575 static void
576 94 free_outline(Outline *outl)
577 {
578 94 lbm_free(outl->points);
579 94 lbm_free(outl->curves);
580 94 lbm_free(outl->lines);
581 94 }
582
583 static int
584 32 grow_points(Outline *outl)
585 {
586 uint_fast16_t cap;
587
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 assert(outl->capPoints);
588 /* Since we use uint_fast16_t for capacities, we have to be extra careful not to trigger integer overflow. */
589
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 if (outl->capPoints > UINT16_MAX / 2)
590 return -1;
591 32 cap = (uint_fast16_t) (2U * outl->capPoints);
592 Point *ps;
593
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 if (!(ps = (Point*)lbm_malloc(cap * sizeof(Point))))
594 return SFT_MEM_ERROR;
595 32 memset(ps,0, sizeof(Point) * cap);
596 32 memcpy(ps,outl->points, sizeof(Point) * outl->capPoints);
597 32 lbm_free(outl->points);
598 32 outl->points = ps;
599 32 outl->capPoints = (uint_least16_t) cap;
600 32 return 0;
601 }
602
603 static int
604 grow_curves(Outline *outl)
605 {
606 uint_fast16_t cap;
607 assert(outl->capCurves);
608 if (outl->capCurves > UINT16_MAX / 2)
609 return -1;
610 cap = (uint_fast16_t) (2U * outl->capCurves);
611 Curve *cs;
612 if (!(cs = (Curve*)lbm_malloc(cap * sizeof(Curve))))
613 return SFT_MEM_ERROR;
614 memset(cs, 0, sizeof(Curve) * cap);
615 memcpy(cs,outl->curves, sizeof(Curve) * outl->capCurves);
616 lbm_free(outl->curves);
617 outl->curves = cs;
618 outl->capCurves = (uint_least16_t) cap;
619 return 0;
620 }
621
622 static int
623 3 grow_lines(Outline *outl)
624 {
625 uint_fast16_t cap;
626
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 assert(outl->capLines);
627
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (outl->capLines > UINT16_MAX / 2)
628 return -1;
629 3 cap = (uint_fast16_t) (2U * outl->capLines);
630 Line *ls;
631
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (!(ls = lbm_malloc(cap * sizeof(Line))))
632 return SFT_MEM_ERROR;
633 3 memset(ls, 0, sizeof(Line) * cap);
634 3 memcpy(ls, outl->lines, sizeof(Line) * outl->capLines);
635 3 lbm_free(outl->lines);
636 3 outl->lines = ls;
637 3 outl->capLines = (uint_least16_t) cap;
638 3 return 0;
639 }
640
641 static inline int
642 68989 is_safe_offset(SFT_Font *font, uint_fast32_t offset, uint_fast32_t margin)
643 {
644
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 68989 times.
68989 if (offset > font->size) return 0;
645
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 68988 times.
68989 if (font->size - offset < margin) return 0;
646 68988 return 1;
647 }
648
649 /* Like bsearch(), but returns the next highest element if key could not be found. */
650 static void *
651 5669 csearch(const void *key, const void *base,
652 size_t nmemb, size_t size,
653 int (*compar)(const void *, const void *))
654 {
655 5669 const uint8_t *bytes = base, *sample;
656 5669 size_t low = 0, high = nmemb - 1, mid;
657
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5669 times.
5669 if (!nmemb) return NULL;
658
2/2
✓ Branch 0 taken 39683 times.
✓ Branch 1 taken 5669 times.
45352 while (low != high) {
659 39683 mid = low + (high - low) / 2;
660 39683 sample = bytes + mid * size;
661
2/2
✓ Branch 0 taken 5669 times.
✓ Branch 1 taken 34014 times.
39683 if (compar(key, sample) > 0) {
662 5669 low = mid + 1;
663 } else {
664 34014 high = mid;
665 }
666 }
667 5669 return (uint8_t *) bytes + low * size;
668 }
669
670 /* Used as a comparison function for [bc]search(). */
671 static int
672 39683 cmpu16(const void *a, const void *b)
673 {
674 39683 return memcmp(a, b, 2);
675 }
676
677 /* Used as a comparison function for [bc]search(). */
678 static int
679 114048 cmpu32(const void *a, const void *b)
680 {
681 114048 return memcmp(a, b, 4);
682 }
683
684 static inline uint_least8_t
685 17056 getu8(SFT_Font *font, uint_fast32_t offset)
686 {
687
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17056 times.
17056 assert(offset + 1 <= font->size);
688 17056 return *(font->memory + offset);
689 }
690
691 static inline int_least8_t
692 geti8(SFT_Font *font, uint_fast32_t offset)
693 {
694 return (int_least8_t) getu8(font, offset);
695 }
696
697 static inline uint_least16_t
698 595095 getu16(SFT_Font *font, uint_fast32_t offset)
699 {
700
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 595095 times.
595095 assert(offset + 2 <= font->size);
701 595095 const uint8_t *base = font->memory + offset;
702 595095 uint_least16_t b1 = base[0], b0 = base[1];
703 595095 return (uint_least16_t) (b1 << 8 | b0);
704 }
705
706 static inline int16_t
707 5171 geti16(SFT_Font *font, uint_fast32_t offset)
708 {
709 5171 return (int_least16_t) getu16(font, offset);
710 }
711
712 static inline uint32_t
713 18313 getu32(SFT_Font *font, uint_fast32_t offset)
714 {
715
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18313 times.
18313 assert(offset + 4 <= font->size);
716 18313 const uint8_t *base = font->memory + offset;
717 18313 uint_least32_t b3 = base[0], b2 = base[1], b1 = base[2], b0 = base[3];
718 18313 return (uint_least32_t) (b3 << 24 | b2 << 16 | b1 << 8 | b0);
719 }
720
721 static int
722 11846 gettable(SFT_Font *font, char tag[4], uint_fast32_t *offset)
723 {
724 void *match;
725 unsigned int numTables;
726 /* No need to bounds-check access to the first 12 bytes - this gets already checked by init_font(). */
727 11846 numTables = getu16(font, 4);
728
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11846 times.
11846 if (!is_safe_offset(font, 12, (uint_fast32_t) numTables * 16))
729 return -1;
730
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11846 times.
11846 if (!(match = bsearch(tag, font->memory + 12, numTables, 16, cmpu32)))
731 return -1;
732 11846 *offset = getu32(font, (uint_fast32_t) ((uint8_t *) match - font->memory + 8));
733 11846 return 0;
734 }
735
736 static int
737 5669 cmap_fmt4(SFT_Font *font, uint_fast32_t table, SFT_UChar charCode, SFT_Glyph *glyph)
738 {
739 const uint8_t *segPtr;
740 uint_fast32_t segIdxX2;
741 uint_fast32_t endCodes, startCodes, idDeltas, idRangeOffsets, idOffset;
742 uint_fast16_t segCountX2, idRangeOffset, startCode, shortCode, idDelta, id;
743 5669 uint8_t key[2] = { (uint8_t) (charCode >> 8), (uint8_t) charCode };
744 /* cmap format 4 only supports the Unicode BMP. */
745
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5669 times.
5669 if (charCode > 0xFFFF) {
746 *glyph = 0;
747 return 0;
748 }
749 5669 shortCode = (uint_fast16_t) charCode;
750
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5669 times.
5669 if (!is_safe_offset(font, table, 8))
751 return -1;
752 5669 segCountX2 = getu16(font, table);
753
2/4
✓ Branch 0 taken 5669 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5669 times.
5669 if ((segCountX2 & 1) || !segCountX2)
754 return -1;
755 /* Find starting positions of the relevant arrays. */
756 5669 endCodes = table + 8;
757 5669 startCodes = endCodes + segCountX2 + 2;
758 5669 idDeltas = startCodes + segCountX2;
759 5669 idRangeOffsets = idDeltas + segCountX2;
760
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5669 times.
5669 if (!is_safe_offset(font, idRangeOffsets, segCountX2))
761 return -1;
762 /* Find the segment that contains shortCode by binary searching over
763 * the highest codes in the segments. */
764 5669 segPtr = csearch(key, font->memory + endCodes, segCountX2 / 2, 2, cmpu16);
765 5669 segIdxX2 = (uint_fast32_t) (segPtr - (font->memory + endCodes));
766 /* Look up segment info from the arrays & short circuit if the spec requires. */
767
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5669 times.
5669 if ((startCode = getu16(font, startCodes + segIdxX2)) > shortCode)
768 return 0;
769 5669 idDelta = getu16(font, idDeltas + segIdxX2);
770
1/2
✓ Branch 0 taken 5669 times.
✗ Branch 1 not taken.
5669 if (!(idRangeOffset = getu16(font, idRangeOffsets + segIdxX2))) {
771 /* Intentional integer under- and overflow. */
772 5669 *glyph = (shortCode + idDelta) & 0xFFFF;
773 5669 return 0;
774 }
775 /* Calculate offset into glyph array and determine ultimate value. */
776 idOffset = idRangeOffsets + segIdxX2 + idRangeOffset + 2U * (unsigned int) (shortCode - startCode);
777 if (!is_safe_offset(font, idOffset, 2))
778 return -1;
779 id = getu16(font, idOffset);
780 /* Intentional integer under- and overflow. */
781 *glyph = id ? (id + idDelta) & 0xFFFF : 0;
782 return 0;
783 }
784
785 static int
786 cmap_fmt6(SFT_Font *font, uint_fast32_t table, SFT_UChar charCode, SFT_Glyph *glyph)
787 {
788 unsigned int firstCode, entryCount;
789 /* cmap format 6 only supports the Unicode BMP. */
790 if (charCode > 0xFFFF) {
791 *glyph = 0;
792 return 0;
793 }
794 if (!is_safe_offset(font, table, 4))
795 return -1;
796 firstCode = getu16(font, table);
797 entryCount = getu16(font, table + 2);
798 if (!is_safe_offset(font, table, 4 + 2 * entryCount))
799 return -1;
800 if (charCode < firstCode)
801 return -1;
802 charCode -= firstCode;
803 if (!(charCode < entryCount))
804 return -1;
805 *glyph = getu16(font, table + 4 + 2 * charCode);
806 return 0;
807 }
808
809 static int
810 cmap_fmt12_13(SFT_Font *font, uint_fast32_t table, SFT_UChar charCode, SFT_Glyph *glyph, int which)
811 {
812 uint32_t len, numEntries;
813 uint_fast32_t i;
814
815 *glyph = 0;
816
817 /* check that the entire header is present */
818 if (!is_safe_offset(font, table, 16))
819 return -1;
820
821 len = getu32(font, table + 4);
822
823 /* A minimal header is 16 bytes */
824 if (len < 16)
825 return -1;
826
827 if (!is_safe_offset(font, table, len))
828 return -1;
829
830 numEntries = getu32(font, table + 12);
831
832 for (i = 0; i < numEntries; ++i) {
833 uint32_t firstCode, lastCode, glyphOffset;
834 firstCode = getu32(font, table + (i * 12) + 16);
835 lastCode = getu32(font, table + (i * 12) + 16 + 4);
836 if (charCode < firstCode || charCode > lastCode)
837 continue;
838 glyphOffset = getu32(font, table + (i * 12) + 16 + 8);
839 if (which == 12)
840 *glyph = (charCode-firstCode) + glyphOffset;
841 else
842 *glyph = glyphOffset;
843 return 0;
844 }
845
846 return 0;
847 }
848
849 /* Maps Unicode code points to glyph indices. */
850 static int
851 5669 glyph_id(SFT_Font *font, SFT_UChar charCode, SFT_Glyph *glyph)
852 {
853 uint_fast32_t cmap, entry, table;
854 unsigned int idx, numEntries;
855 int type, format;
856
857 5669 *glyph = 0;
858
859
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5669 times.
5669 if (gettable(font, "cmap", &cmap) < 0)
860 return -1;
861
862
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5669 times.
5669 if (!is_safe_offset(font, cmap, 4))
863 return -1;
864 5669 numEntries = getu16(font, cmap + 2);
865
866
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5669 times.
5669 if (!is_safe_offset(font, cmap, 4 + numEntries * 8))
867 return -1;
868
869 /* First look for a 'full repertoire'/non-BMP map. */
870
2/2
✓ Branch 0 taken 17007 times.
✓ Branch 1 taken 5669 times.
22676 for (idx = 0; idx < numEntries; ++idx) {
871 17007 entry = cmap + 4 + idx * 8;
872 17007 type = getu16(font, entry) * 0100 + getu16(font, entry + 2);
873 /* Complete unicode map */
874
2/4
✓ Branch 0 taken 17007 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 17007 times.
17007 if (type == 0004 || type == 0312) {
875 table = cmap + getu32(font, entry + 4);
876 if (!is_safe_offset(font, table, 8))
877 return -1;
878 /* Dispatch based on cmap format. */
879 format = getu16(font, table);
880 switch (format) {
881 case 12:
882 return cmap_fmt12_13(font, table, charCode, glyph, 12);
883 default:
884 return -1;
885 }
886 }
887 }
888
889 /* If no 'full repertoire' cmap was found, try looking for a BMP map. */
890
1/2
✓ Branch 0 taken 5669 times.
✗ Branch 1 not taken.
5669 for (idx = 0; idx < numEntries; ++idx) {
891 5669 entry = cmap + 4 + idx * 8;
892 5669 type = getu16(font, entry) * 0100 + getu16(font, entry + 2);
893 /* Unicode BMP */
894
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 5669 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
5669 if (type == 0003 || type == 0301) {
895 5669 table = cmap + getu32(font, entry + 4);
896
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5669 times.
5669 if (!is_safe_offset(font, table, 6))
897 return -1;
898 /* Dispatch based on cmap format. */
899
1/3
✓ Branch 0 taken 5669 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
5669 switch (getu16(font, table)) {
900 5669 case 4:
901 5669 return cmap_fmt4(font, table + 6, charCode, glyph);
902 case 6:
903 return cmap_fmt6(font, table + 6, charCode, glyph);
904 default:
905 return -1;
906 }
907 }
908 }
909
910 return -1;
911 }
912
913 static int
914 295 hor_metrics(SFT_Font *font, SFT_Glyph glyph, int *advanceWidth, int *leftSideBearing)
915 {
916 uint_fast32_t hmtx, offset, boundary;
917
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 295 times.
295 if (gettable(font, "hmtx", &hmtx) < 0)
918 return -1;
919
1/2
✓ Branch 0 taken 295 times.
✗ Branch 1 not taken.
295 if (glyph < font->numLongHmtx) {
920 /* glyph is inside long metrics segment. */
921 295 offset = hmtx + 4 * glyph;
922
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 295 times.
295 if (!is_safe_offset(font, offset, 4))
923 return -1;
924 295 *advanceWidth = getu16(font, offset);
925 295 *leftSideBearing = geti16(font, offset + 2);
926 295 return 0;
927 } else {
928 /* glyph is inside short metrics segment. */
929 boundary = hmtx + 4U * (uint_fast32_t) font->numLongHmtx;
930 if (boundary < 4)
931 return -1;
932
933 offset = boundary - 4;
934 if (!is_safe_offset(font, offset, 4))
935 return -1;
936 *advanceWidth = getu16(font, offset);
937
938 offset = boundary + 2 * (glyph - font->numLongHmtx);
939 if (!is_safe_offset(font, offset, 2))
940 return -1;
941 *leftSideBearing = geti16(font, offset);
942 return 0;
943 }
944 }
945
946 static int
947 380 glyph_bbox(const SFT *sft, uint_fast32_t outline, int box[4])
948 {
949 float xScale, yScale;
950 /* Read the bounding box from the font file verbatim. */
951
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 380 times.
380 if (!is_safe_offset(sft->font, outline, 10))
952 return -1;
953 380 box[0] = geti16(sft->font, outline + 2);
954 380 box[1] = geti16(sft->font, outline + 4);
955 380 box[2] = geti16(sft->font, outline + 6);
956 380 box[3] = geti16(sft->font, outline + 8);
957
2/4
✓ Branch 0 taken 380 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 380 times.
380 if (box[2] <= box[0] || box[3] <= box[1])
958 return -1;
959 /* Transform the bounding box into SFT coordinate space. */
960 380 xScale = sft->xScale / sft->font->unitsPerEm;
961 380 yScale = sft->yScale / sft->font->unitsPerEm;
962 380 box[0] = (int) floor((float)box[0] * xScale + sft->xOffset);
963 380 box[1] = (int) floor((float)box[1] * yScale + sft->yOffset);
964 380 box[2] = (int) ceil ((float)box[2] * xScale + sft->xOffset);
965 380 box[3] = (int) ceil ((float)box[3] * yScale + sft->yOffset);
966 380 return 0;
967 }
968
969 /* Returns the offset into the font that the glyph's outline is stored at. */
970 static int
971 392 outline_offset(SFT_Font *font, SFT_Glyph glyph, uint_fast32_t *offset)
972 {
973 uint_fast32_t loca, glyf;
974 uint_fast32_t base, this, next;
975
976
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 392 times.
392 if (gettable(font, "loca", &loca) < 0)
977 return -1;
978
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 392 times.
392 if (gettable(font, "glyf", &glyf) < 0)
979 return -1;
980
981
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 392 times.
392 if (font->locaFormat == 0) {
982 base = loca + 2 * glyph;
983
984 if (!is_safe_offset(font, base, 4))
985 return -1;
986
987 this = 2U * (uint_fast32_t) getu16(font, base);
988 next = 2U * (uint_fast32_t) getu16(font, base + 2);
989 } else {
990 392 base = loca + 4 * glyph;
991
992
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 392 times.
392 if (!is_safe_offset(font, base, 8))
993 return -1;
994
995 392 this = getu32(font, base);
996 392 next = getu32(font, base + 4);
997 }
998
999
2/2
✓ Branch 0 taken 380 times.
✓ Branch 1 taken 12 times.
392 *offset = this == next ? 0 : glyf + this;
1000 392 return 0;
1001 }
1002
1003 /* For a 'simple' outline, determines each point of the outline with a set of flags. */
1004 static int
1005 94 simple_flags(SFT_Font *font, uint_fast32_t *offset, uint_fast16_t numPts, uint8_t *flags)
1006 {
1007 94 uint_fast32_t off = *offset;
1008 uint_fast16_t i;
1009 94 uint8_t value = 0, repeat = 0;
1010
2/2
✓ Branch 0 taken 2918 times.
✓ Branch 1 taken 94 times.
3012 for (i = 0; i < numPts; ++i) {
1011
2/2
✓ Branch 0 taken 732 times.
✓ Branch 1 taken 2186 times.
2918 if (repeat) {
1012 732 --repeat;
1013 } else {
1014
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2186 times.
2186 if (!is_safe_offset(font, off, 1))
1015 return -1;
1016 2186 value = getu8(font, off++);
1017
2/2
✓ Branch 0 taken 408 times.
✓ Branch 1 taken 1778 times.
2186 if (value & REPEAT_FLAG) {
1018
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 408 times.
408 if (!is_safe_offset(font, off, 1))
1019 return -1;
1020 408 repeat = getu8(font, off++);
1021 }
1022 }
1023 2918 flags[i] = value;
1024 }
1025 94 *offset = off;
1026 94 return 0;
1027 }
1028
1029 /* For a 'simple' outline, decodes both X and Y coordinates for each point of the outline. */
1030 static int
1031 94 simple_points(SFT_Font *font, uint_fast32_t offset, uint_fast16_t numPts, uint8_t *flags, Point *points)
1032 {
1033 long accum, value, bit;
1034 uint_fast16_t i;
1035
1036 94 accum = 0L;
1037
2/2
✓ Branch 0 taken 2918 times.
✓ Branch 1 taken 94 times.
3012 for (i = 0; i < numPts; ++i) {
1038
2/2
✓ Branch 0 taken 2309 times.
✓ Branch 1 taken 609 times.
2918 if (flags[i] & X_CHANGE_IS_SMALL) {
1039
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2309 times.
2309 if (!is_safe_offset(font, offset, 1))
1040 return -1;
1041 2309 value = (long) getu8(font, offset++);
1042 2309 bit = !!(flags[i] & X_CHANGE_IS_POSITIVE);
1043 2309 accum -= (value ^ -bit) + bit;
1044
2/2
✓ Branch 0 taken 106 times.
✓ Branch 1 taken 503 times.
609 } else if (!(flags[i] & X_CHANGE_IS_ZERO)) {
1045
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 106 times.
106 if (!is_safe_offset(font, offset, 2))
1046 return -1;
1047 106 accum += geti16(font, offset);
1048 106 offset += 2;
1049 }
1050 2918 points[i].x = (float) accum;
1051 }
1052
1053 94 accum = 0L;
1054
2/2
✓ Branch 0 taken 2918 times.
✓ Branch 1 taken 94 times.
3012 for (i = 0; i < numPts; ++i) {
1055
2/2
✓ Branch 0 taken 2065 times.
✓ Branch 1 taken 853 times.
2918 if (flags[i] & Y_CHANGE_IS_SMALL) {
1056
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2065 times.
2065 if (!is_safe_offset(font, offset, 1))
1057 return -1;
1058 2065 value = (long) getu8(font, offset++);
1059 2065 bit = !!(flags[i] & Y_CHANGE_IS_POSITIVE);
1060 2065 accum -= (value ^ -bit) + bit;
1061
2/2
✓ Branch 0 taken 196 times.
✓ Branch 1 taken 657 times.
853 } else if (!(flags[i] & Y_CHANGE_IS_ZERO)) {
1062
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 196 times.
196 if (!is_safe_offset(font, offset, 2))
1063 return -1;
1064 196 accum += geti16(font, offset);
1065 196 offset += 2;
1066 }
1067 2918 points[i].y = (float) accum;
1068 }
1069
1070 94 return 0;
1071 }
1072
1073 static int
1074 135 decode_contour(uint8_t *flags, uint_fast16_t basePoint, uint_fast16_t count, Outline *outl)
1075 {
1076 uint_fast16_t i;
1077 uint_least16_t looseEnd, beg, ctrl, center, cur;
1078 unsigned int gotCtrl;
1079 135 int r = 0;
1080
1081 /* Skip contours with less than two points, since the following algorithm can't handle them and
1082 * they should appear invisible either way (because they don't have any area). */
1083
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 135 times.
135 if (count < 2) return 0;
1084
1085
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 135 times.
135 assert(basePoint <= UINT16_MAX - count);
1086
1087
1/2
✓ Branch 0 taken 135 times.
✗ Branch 1 not taken.
135 if (flags[0] & POINT_IS_ON_CURVE) {
1088 135 looseEnd = (uint_least16_t) basePoint++;
1089 135 ++flags;
1090 135 --count;
1091 } else if (flags[count - 1] & POINT_IS_ON_CURVE) {
1092 looseEnd = (uint_least16_t) (basePoint + --count);
1093 } else {
1094 if (outl->numPoints >= outl->capPoints && (r = grow_points(outl)) < 0)
1095 return r;
1096
1097 looseEnd = outl->numPoints;
1098 outl->points[outl->numPoints++] = midpoint(
1099 outl->points[basePoint],
1100 outl->points[basePoint + count - 1]);
1101 }
1102 135 beg = looseEnd;
1103 135 gotCtrl = 0;
1104
2/2
✓ Branch 0 taken 2783 times.
✓ Branch 1 taken 135 times.
2918 for (i = 0; i < count; ++i) {
1105 /* cur can't overflow because we ensure that basePoint + count < 0xFFFF before calling decode_contour(). */
1106 2783 cur = (uint_least16_t) (basePoint + i);
1107 /* NOTE clang-analyzer will often flag this and another piece of code because it thinks that flags and
1108 * outl->points + basePoint don't always get properly initialized -- even when you explicitly loop over both
1109 * and set every element to zero (but not when you use memset). This is a known clang-analyzer bug:
1110 * http://clang-developers.42468.n3.nabble.com/StaticAnalyzer-False-positive-with-loop-handling-td4053875.html */
1111
2/2
✓ Branch 0 taken 1022 times.
✓ Branch 1 taken 1761 times.
2783 if (flags[i] & POINT_IS_ON_CURVE) {
1112
2/2
✓ Branch 0 taken 547 times.
✓ Branch 1 taken 475 times.
1022 if (gotCtrl) {
1113
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 547 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
547 if (outl->numCurves >= outl->capCurves && (r = grow_curves(outl)) < 0)
1114 return r;
1115 547 outl->curves[outl->numCurves++] = (Curve) { beg, cur, ctrl };
1116 } else {
1117
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 475 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
475 if (outl->numLines >= outl->capLines && (r = grow_lines(outl)) < 0)
1118 return r;
1119 475 outl->lines[outl->numLines++] = (Line) { beg, cur };
1120 }
1121 1022 beg = cur;
1122 1022 gotCtrl = 0;
1123 } else {
1124
2/2
✓ Branch 0 taken 1147 times.
✓ Branch 1 taken 614 times.
1761 if (gotCtrl) {
1125 1147 center = outl->numPoints;
1126
3/4
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 1133 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
1147 if (outl->numPoints >= outl->capPoints && (r = grow_points(outl)) < 0)
1127 return r;
1128 1147 outl->points[center] = midpoint(outl->points[ctrl], outl->points[cur]);
1129 1147 ++outl->numPoints;
1130
1131
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1147 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1147 if (outl->numCurves >= outl->capCurves && (r = grow_curves(outl)) < 0)
1132 return r;
1133 1147 outl->curves[outl->numCurves++] = (Curve) { beg, center, ctrl };
1134
1135 1147 beg = center;
1136 }
1137 1761 ctrl = cur;
1138 1761 gotCtrl = 1;
1139 }
1140 }
1141
2/2
✓ Branch 0 taken 67 times.
✓ Branch 1 taken 68 times.
135 if (gotCtrl) {
1142
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 67 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
67 if (outl->numCurves >= outl->capCurves && (r = grow_curves(outl)) < 0)
1143 return r;
1144 67 outl->curves[outl->numCurves++] = (Curve) { beg, looseEnd, ctrl };
1145 } else {
1146
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 68 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
68 if (outl->numLines >= outl->capLines && (r = grow_lines(outl)) < 0)
1147 return r;
1148 68 outl->lines[outl->numLines++] = (Line) { beg, looseEnd };
1149 }
1150
1151 135 return 0;
1152 }
1153
1154 static int
1155 94 simple_outline(SFT_Font *font, uint_fast32_t offset, unsigned int numContours, Outline *outl)
1156 {
1157 94 uint_fast16_t *endPts = NULL;
1158 94 uint8_t *flags = NULL;
1159 uint_fast16_t numPts;
1160 unsigned int i;
1161
1162 94 int fail_r = -1;
1163
1164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 assert(numContours > 0);
1165
1166 94 uint_fast16_t basePoint = outl->numPoints;
1167
1168
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if (!is_safe_offset(font, offset, numContours * 2 + 2))
1169 goto failure;
1170 94 numPts = getu16(font, offset + (numContours - 1) * 2);
1171
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if (numPts >= UINT16_MAX)
1172 goto failure;
1173 94 numPts++;
1174
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if (outl->numPoints > UINT16_MAX - numPts)
1175 goto failure;
1176
1177
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 94 times.
98 while (outl->capPoints < basePoint + numPts) {
1178
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (grow_points(outl) < 0)
1179 goto failure;
1180 }
1181
1182 94 endPts = lbm_malloc(numContours * sizeof(uint_fast16_t));
1183
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if (endPts == NULL) {
1184 fail_r = SFT_MEM_ERROR;
1185 goto failure;
1186 }
1187
1188 94 memset(endPts,0,numContours * sizeof(uint_fast16_t));
1189 94 flags = lbm_malloc(numPts);
1190
1191
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if (flags == NULL) {
1192 fail_r = SFT_MEM_ERROR;
1193 goto failure;
1194 }
1195 94 memset(flags, 0, numPts);
1196
1197
2/2
✓ Branch 0 taken 135 times.
✓ Branch 1 taken 94 times.
229 for (i = 0; i < numContours; ++i) {
1198 135 endPts[i] = getu16(font, offset);
1199 135 offset += 2;
1200 }
1201 /* Ensure that endPts are never falling.
1202 * Falling endPts have no sensible interpretation and most likely only occur in malicious input.
1203 * Therefore, we bail, should we ever encounter such input. */
1204
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 94 times.
135 for (i = 0; i < numContours - 1; ++i) {
1205
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41 times.
41 if (endPts[i + 1] < endPts[i] + 1)
1206 goto failure;
1207 }
1208 94 offset += 2U + getu16(font, offset);
1209
1210
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if (simple_flags(font, &offset, numPts, flags) < 0)
1211 goto failure;
1212
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if (simple_points(font, offset, numPts, flags, outl->points + basePoint) < 0)
1213 goto failure;
1214 94 outl->numPoints = (uint_least16_t) (outl->numPoints + numPts);
1215
1216 94 uint_fast16_t beg = 0;
1217
2/2
✓ Branch 0 taken 135 times.
✓ Branch 1 taken 94 times.
229 for (i = 0; i < numContours; ++i) {
1218 135 uint_fast16_t count = endPts[i] - beg + 1;
1219
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 135 times.
135 if (decode_contour(flags + beg, basePoint + beg, count, outl) < 0)
1220 goto failure;
1221 135 beg = endPts[i] + 1;
1222 }
1223
1224 94 lbm_free(endPts);
1225 94 lbm_free(flags);
1226 94 return 0;
1227 failure:
1228 lbm_free(endPts);
1229 lbm_free(flags);
1230 return fail_r;
1231 }
1232
1233 static int
1234 compound_outline(SFT_Font *font, uint_fast32_t offset, int recDepth, Outline *outl)
1235 {
1236 float local[6];
1237 uint_fast32_t outline;
1238 unsigned int flags, glyph, basePoint;
1239 /* Guard against infinite recursion (compound glyphs that have themselves as component). */
1240 if (recDepth >= 4)
1241 return -1;
1242 do {
1243 memset(local, 0, sizeof local);
1244 if (!is_safe_offset(font, offset, 4))
1245 return -1;
1246 flags = getu16(font, offset);
1247 glyph = getu16(font, offset + 2);
1248 offset += 4;
1249 /* We don't implement point matching, and neither does stb_truetype for that matter. */
1250 if (!(flags & ACTUAL_XY_OFFSETS))
1251 return -1;
1252 /* Read additional X and Y offsets (in FUnits) of this component. */
1253 if (flags & OFFSETS_ARE_LARGE) {
1254 if (!is_safe_offset(font, offset, 4))
1255 return -1;
1256 local[4] = geti16(font, offset);
1257 local[5] = geti16(font, offset + 2);
1258 offset += 4;
1259 } else {
1260 if (!is_safe_offset(font, offset, 2))
1261 return -1;
1262 local[4] = geti8(font, offset);
1263 local[5] = geti8(font, offset + 1);
1264 offset += 2;
1265 }
1266 if (flags & GOT_A_SINGLE_SCALE) {
1267 if (!is_safe_offset(font, offset, 2))
1268 return -1;
1269 local[0] = geti16(font, offset) / 16384.0f;
1270 local[3] = local[0];
1271 offset += 2;
1272 } else if (flags & GOT_AN_X_AND_Y_SCALE) {
1273 if (!is_safe_offset(font, offset, 4))
1274 return -1;
1275 local[0] = geti16(font, offset + 0) / 16384.0f;
1276 local[3] = geti16(font, offset + 2) / 16384.0f;
1277 offset += 4;
1278 } else if (flags & GOT_A_SCALE_MATRIX) {
1279 if (!is_safe_offset(font, offset, 8))
1280 return -1;
1281 local[0] = geti16(font, offset + 0) / 16384.0f;
1282 local[1] = geti16(font, offset + 2) / 16384.0f;
1283 local[2] = geti16(font, offset + 4) / 16384.0f;
1284 local[3] = geti16(font, offset + 6) / 16384.0f;
1285 offset += 8;
1286 } else {
1287 local[0] = 1.0f;
1288 local[3] = 1.0f;
1289 }
1290 /* At this point, Apple's spec more or less tells you to scale the matrix by its own L1 norm.
1291 * But stb_truetype scales by the L2 norm. And FreeType2 doesn't scale at all.
1292 * Furthermore, Microsoft's spec doesn't even mention anything like this.
1293 * It's almost as if nobody ever uses this feature anyway. */
1294 if (outline_offset(font, glyph, &outline) < 0)
1295 return -1;
1296 if (outline) {
1297 basePoint = outl->numPoints;
1298 if (decode_outline(font, outline, recDepth + 1, outl) < 0)
1299 return -1;
1300 transform_points(outl->numPoints - basePoint, outl->points + basePoint, local);
1301 }
1302 } while (flags & THERE_ARE_MORE_COMPONENTS);
1303
1304 return 0;
1305 }
1306
1307 static int
1308 94 decode_outline(SFT_Font *font, uint_fast32_t offset, int recDepth, Outline *outl)
1309 {
1310 int numContours;
1311
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if (!is_safe_offset(font, offset, 10))
1312 return -1;
1313 94 numContours = geti16(font, offset);
1314
1/2
✓ Branch 0 taken 94 times.
✗ Branch 1 not taken.
94 if (numContours > 0) {
1315 /* Glyph has a 'simple' outline consisting of a number of contours. */
1316 94 return simple_outline(font, offset + 10, (unsigned int) numContours, outl);
1317 } else if (numContours < 0) {
1318 /* Glyph has a compound outline combined from mutiple other outlines. */
1319 return compound_outline(font, offset + 10, recDepth, outl);
1320 } else {
1321 return 0;
1322 }
1323 }
1324
1325 /* A heuristic to tell whether a given curve can be approximated closely enough by a line. */
1326 static int
1327 2081 is_flat(Outline *outl, Curve curve)
1328 {
1329 2081 const float maxArea2 = 2.0f;
1330 2081 Point a = outl->points[curve.beg];
1331 2081 Point b = outl->points[curve.ctrl];
1332 2081 Point c = outl->points[curve.end];
1333 2081 Point g = { b.x-a.x, b.y-a.y };
1334 2081 Point h = { c.x-a.x, c.y-a.y };
1335 2081 float area2 = fabsf(g.x*h.y-h.x*g.y);
1336 2081 return area2 <= maxArea2;
1337 }
1338
1339 static int
1340 1761 tesselate_curve(Curve curve, Outline *outl)
1341 {
1342 /* From my tests I can conclude that this stack barely reaches a top height
1343 * of 4 elements even for the largest font sizes I'm willing to support. And
1344 * as space requirements should only grow logarithmically, I think 10 is
1345 * more than enough. */
1346 #define STACK_SIZE 10
1347 Curve stack[STACK_SIZE];
1348 1761 unsigned int top = 0;
1349 for (;;) {
1350
3/4
✓ Branch 0 taken 160 times.
✓ Branch 1 taken 1921 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 160 times.
2081 if (is_flat(outl, curve) || top >= STACK_SIZE) {
1351
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1918 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
1921 if (outl->numLines >= outl->capLines && grow_lines(outl) < 0)
1352 return -1;
1353 1921 outl->lines[outl->numLines++] = (Line) { curve.beg, curve.end };
1354
2/2
✓ Branch 0 taken 1761 times.
✓ Branch 1 taken 160 times.
1921 if (top == 0) break;
1355 160 curve = stack[--top];
1356 } else {
1357 160 uint_least16_t ctrl0 = outl->numPoints;
1358
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 157 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
160 if (outl->numPoints >= outl->capPoints && grow_points(outl) < 0)
1359 return -1;
1360 160 outl->points[ctrl0] = midpoint(outl->points[curve.beg], outl->points[curve.ctrl]);
1361 160 ++outl->numPoints;
1362
1363 160 uint_least16_t ctrl1 = outl->numPoints;
1364
3/4
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 151 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
160 if (outl->numPoints >= outl->capPoints && grow_points(outl) < 0)
1365 return -1;
1366 160 outl->points[ctrl1] = midpoint(outl->points[curve.ctrl], outl->points[curve.end]);
1367 160 ++outl->numPoints;
1368
1369 160 uint_least16_t pivot = outl->numPoints;
1370
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
160 if (outl->numPoints >= outl->capPoints && grow_points(outl) < 0)
1371 return -1;
1372 160 outl->points[pivot] = midpoint(outl->points[ctrl0], outl->points[ctrl1]);
1373 160 ++outl->numPoints;
1374
1375 160 stack[top++] = (Curve) { curve.beg, pivot, ctrl0 };
1376 160 curve = (Curve) { pivot, curve.end, ctrl1 };
1377 }
1378 }
1379 1761 return 0;
1380 #undef STACK_SIZE
1381 }
1382
1383 static int
1384 94 tesselate_curves(Outline *outl)
1385 {
1386 unsigned int i;
1387
2/2
✓ Branch 0 taken 1761 times.
✓ Branch 1 taken 94 times.
1855 for (i = 0; i < outl->numCurves; ++i) {
1388
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1761 times.
1761 if (tesselate_curve(outl->curves[i], outl) < 0)
1389 return -1;
1390 }
1391 94 return 0;
1392 }
1393
1394 /* Draws a line into the buffer. Uses a custom 2D raycasting algorithm to do so. */
1395 static void
1396 2464 draw_line(Raster buf, Point origin, Point goal)
1397 {
1398 Point delta;
1399 Point nextCrossing;
1400 Point crossingIncr;
1401 float halfDeltaX;
1402 2464 float prevDistance = 0.0f, nextDistance;
1403 float xAverage, yDifference;
1404 struct { int x, y; } pixel;
1405 struct { int x, y; } dir;
1406 2464 int step, numSteps = 0;
1407 Cell *restrict cptr, cell;
1408
1409 2464 delta.x = goal.x - origin.x;
1410 2464 delta.y = goal.y - origin.y;
1411 2464 dir.x = SIGN(delta.x);
1412 2464 dir.y = SIGN(delta.y);
1413
1414
2/2
✓ Branch 0 taken 300 times.
✓ Branch 1 taken 2164 times.
2464 if (!dir.y) {
1415 300 return;
1416 }
1417
1418
2/2
✓ Branch 0 taken 1953 times.
✓ Branch 1 taken 211 times.
2164 crossingIncr.x = dir.x ? fabsf(1.0f / delta.x) : 1.0f;
1419 2164 crossingIncr.y = fabsf(1.0f / delta.y);
1420
1421
2/2
✓ Branch 0 taken 211 times.
✓ Branch 1 taken 1953 times.
2164 if (!dir.x) {
1422 211 pixel.x = fast_floor(origin.x);
1423 211 nextCrossing.x = 100.0f;
1424 } else {
1425
2/2
✓ Branch 0 taken 979 times.
✓ Branch 1 taken 974 times.
1953 if (dir.x > 0) {
1426 979 pixel.x = fast_floor(origin.x);
1427 979 nextCrossing.x = (origin.x - (float)pixel.x) * crossingIncr.x;
1428 979 nextCrossing.x = crossingIncr.x - nextCrossing.x;
1429 979 numSteps += fast_ceil(goal.x) - fast_floor(origin.x) - 1;
1430 } else {
1431 974 pixel.x = fast_ceil(origin.x) - 1;
1432 974 nextCrossing.x = (origin.x - (float)pixel.x) * crossingIncr.x;
1433 974 numSteps += fast_ceil(origin.x) - fast_floor(goal.x) - 1;
1434 }
1435 }
1436
1437
2/2
✓ Branch 0 taken 1104 times.
✓ Branch 1 taken 1060 times.
2164 if (dir.y > 0) {
1438 1104 pixel.y = fast_floor(origin.y);
1439 1104 nextCrossing.y = (origin.y - (float)pixel.y) * crossingIncr.y;
1440 1104 nextCrossing.y = crossingIncr.y - nextCrossing.y;
1441 1104 numSteps += fast_ceil(goal.y) - fast_floor(origin.y) - 1;
1442 } else {
1443 1060 pixel.y = fast_ceil(origin.y) - 1;
1444 1060 nextCrossing.y = (origin.y - (float)pixel.y) * crossingIncr.y;
1445 1060 numSteps += fast_ceil(origin.y) - fast_floor(goal.y) - 1;
1446 }
1447
1448
2/2
✓ Branch 0 taken 1031 times.
✓ Branch 1 taken 1133 times.
2164 nextDistance = MIN(nextCrossing.x, nextCrossing.y);
1449 2164 halfDeltaX = 0.5f * delta.x;
1450
1451
2/2
✓ Branch 0 taken 7238 times.
✓ Branch 1 taken 2164 times.
9402 for (step = 0; step < numSteps; ++step) {
1452 7238 xAverage = origin.x + (prevDistance + nextDistance) * halfDeltaX;
1453 7238 yDifference = (nextDistance - prevDistance) * delta.y;
1454 7238 cptr = &buf.cells[pixel.y * buf.width + pixel.x];
1455 7238 cell = *cptr;
1456 7238 cell.cover += yDifference;
1457 7238 xAverage -= (float) pixel.x;
1458 7238 cell.area += (1.0f - xAverage) * yDifference;
1459 7238 *cptr = cell;
1460 7238 prevDistance = nextDistance;
1461 7238 int alongX = nextCrossing.x < nextCrossing.y;
1462
2/2
✓ Branch 0 taken 2615 times.
✓ Branch 1 taken 4623 times.
7238 pixel.x += alongX ? dir.x : 0;
1463
2/2
✓ Branch 0 taken 4623 times.
✓ Branch 1 taken 2615 times.
7238 pixel.y += alongX ? 0 : dir.y;
1464
2/2
✓ Branch 0 taken 2615 times.
✓ Branch 1 taken 4623 times.
7238 nextCrossing.x += alongX ? crossingIncr.x : 0.0f;
1465
2/2
✓ Branch 0 taken 4623 times.
✓ Branch 1 taken 2615 times.
7238 nextCrossing.y += alongX ? 0.0f : crossingIncr.y;
1466
2/2
✓ Branch 0 taken 2611 times.
✓ Branch 1 taken 4627 times.
7238 nextDistance = MIN(nextCrossing.x, nextCrossing.y);
1467 }
1468
1469 2164 xAverage = origin.x + (prevDistance + 1.0f) * halfDeltaX;
1470 2164 yDifference = (1.0f - prevDistance) * delta.y;
1471 2164 cptr = &buf.cells[pixel.y * buf.width + pixel.x];
1472 2164 cell = *cptr;
1473 2164 cell.cover += yDifference;
1474 2164 xAverage -= (float) pixel.x;
1475 2164 cell.area += (1.0f - xAverage) * yDifference;
1476 2164 *cptr = cell;
1477 }
1478
1479 static void
1480 94 draw_lines(Outline *outl, Raster buf)
1481 {
1482 unsigned int i;
1483
2/2
✓ Branch 0 taken 2464 times.
✓ Branch 1 taken 94 times.
2558 for (i = 0; i < outl->numLines; ++i) {
1484 2464 Line line = outl->lines[i];
1485 2464 Point origin = outl->points[line.beg];
1486 2464 Point goal = outl->points[line.end];
1487 2464 draw_line(buf, origin, goal);
1488 }
1489 94 }
1490
1491 static const uint8_t indexed4_mask[4] = {0x03, 0x0C, 0x30, 0xC0};
1492 static const uint8_t indexed4_shift[4] = {0, 2, 4, 6};
1493 static const uint8_t indexed16_mask[2] = {0x0F, 0xF0};
1494 static const uint8_t indexed16_shift[2] = {0, 4};
1495
1496 /* Integrate the values in the buffer to arrive at the final grayscale image. */
1497 94 static void post_process(Raster buf, image_buffer_t *image)
1498 {
1499 Cell cell;
1500 94 float accum = 0.0f, value;
1501 unsigned int i, num;
1502 94 num = (unsigned int) buf.width * (unsigned int) buf.height;
1503 94 uint8_t *image_data = image->data;
1504
1505
3/4
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
94 switch(image->fmt) {
1506 26 case indexed2: {
1507
2/2
✓ Branch 0 taken 3169 times.
✓ Branch 1 taken 26 times.
3195 for (i = 0; i < num; ++i) {
1508 3169 cell = buf.cells[i];
1509 3169 value = fabsf(accum + cell.area);
1510
2/2
✓ Branch 0 taken 3045 times.
✓ Branch 1 taken 124 times.
3169 value = MIN(value, 1.0f);
1511 3169 uint32_t byte = i >> 3;
1512 3169 uint32_t bit = 7 - (i & 0x7);
1513
2/2
✓ Branch 0 taken 984 times.
✓ Branch 1 taken 2185 times.
3169 if (value > 0.5f) {
1514 984 image_data[byte] |= (uint8_t)(1 << bit);
1515 } else {
1516 2185 image_data[byte] &= (uint8_t)~(1 << bit);
1517 }
1518 3169 accum += cell.cover;
1519 }
1520 26 } break;
1521 64 case indexed4: {
1522
2/2
✓ Branch 0 taken 22598 times.
✓ Branch 1 taken 64 times.
22662 for (i = 0; i < num; ++i) {
1523 22598 cell = buf.cells[i];
1524 22598 value = fabsf(accum + cell.area);
1525
2/2
✓ Branch 0 taken 20559 times.
✓ Branch 1 taken 2039 times.
22598 value = MIN(value, 1.0f);
1526 22598 uint32_t byte = i >> 2;
1527 22598 uint32_t ix = 3 - (i & 0x3);
1528 22598 uint8_t c = (uint8_t)(value * 4);
1529
2/2
✓ Branch 0 taken 2039 times.
✓ Branch 1 taken 20559 times.
22598 if (c == 4) c = 3;
1530 22598 image_data[byte] = (uint8_t)((uint8_t)(image_data[byte] & ~indexed4_mask[ix]) | (uint8_t)(c << indexed4_shift[ix]));
1531 22598 accum += cell.cover;
1532 }
1533 64 } break;
1534 case indexed16: {
1535 for (i = 0; i < num; ++i) {
1536 cell = buf.cells[i];
1537 value = fabsf(accum + cell.area);
1538 value = MIN(value, 1.0f);
1539 uint32_t byte = i >> 1;
1540 uint32_t ix = 1 - (i & 0x1);
1541 uint8_t c = (uint8_t)(value * 16);
1542 if (c == 16) c = 15;
1543 image_data[byte] = (uint8_t)((uint8_t)(image_data[byte] & ~indexed16_mask[ix]) | (uint8_t)(c << indexed16_shift[ix]));
1544 accum += cell.cover;
1545 }
1546 } break;
1547 4 default:
1548 4 break;
1549 }
1550 94 }
1551
1552 94 static int render_outline(Outline *outl, float transform[6], image_buffer_t * image) {
1553 94 Cell *cells = NULL;
1554 Raster buf;
1555 unsigned int numPixels;
1556
1557 94 numPixels = (unsigned int) image->width * (unsigned int) image->height;
1558
1559 94 cells = (Cell *)lbm_malloc(numPixels * sizeof(Cell));
1560
1561
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if (!cells) {
1562 return SFT_MEM_ERROR;
1563 }
1564 94 memset(cells, 0, numPixels * sizeof *cells);
1565 94 buf.cells = cells;
1566 94 buf.width = image->width;
1567 94 buf.height = image->height;
1568
1569 94 transform_points(outl->numPoints, outl->points, transform);
1570
1571 94 clip_points(outl->numPoints, outl->points, image->width, image->height);
1572
1573
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if (tesselate_curves(outl) < 0) {
1574 lbm_free(cells);
1575 return -1;
1576 }
1577
1578 94 draw_lines(outl, buf);
1579
1580 94 post_process(buf, image);
1581
1582 94 lbm_free(cells);
1583 94 return 0;
1584 }
1585
1586