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