GCC Code Coverage Report


Directory: ../src/
File: /home/joels/Current/lispbm/src/extensions/schrift.c
Date: 2025-08-08 18:10:24
Exec Total Coverage
Lines: 590 884 66.7%
Functions: 47 53 88.7%
Branches: 249 497 50.1%

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