| 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 |  |  |  |