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