1
4
5
23
24# include <luametatex.h>
25# include <potracelib.h>
26# include <curve.h>
27
28# define POTRACE_METATABLE "potracer"
29
30#define BM_WORDSIZE ((int)sizeof(potrace_word))
31#define BM_WORDBITS (8*BM_WORDSIZE)
32#define BM_HIBIT (((potrace_word)1)<<(BM_WORDBITS-1))
33#define bm_scanline(bm, y) ((bm)->map + (y)*(bm)->dy)
34#define bm_index(bm, x, y) (&bm_scanline(bm, y)[(x)/BM_WORDBITS])
35#define bm_mask(x) (BM_HIBIT >> ((x) & (BM_WORDBITS-1)))
36#define bm_range(x, a) ((int)(x) >= 0 && (int)(x) < (a))
37#define bm_safe(bm, x, y) (bm_range(x, (bm)->w) && bm_range(y, (bm)->h))
38#define BM_USET(bm, x, y) (*bm_index(bm, x, y) |= bm_mask(x))
39#define BM_UCLR(bm, x, y) (*bm_index(bm, x, y) &= ~bm_mask(x))
40#define BM_UPUT(bm, x, y, b) ((b) ? BM_USET(bm, x, y) : BM_UCLR(bm, x, y))
41#define BM_PUT(bm, x, y, b) (bm_safe(bm, x, y) ? BM_UPUT(bm, x, y, b) : 0)
42
43
44
45static potrace_bitmap_t *new_bitmap(int w, int h)
46{
47 int dy = (w + BM_WORDBITS - 1) / BM_WORDBITS;
48 potrace_bitmap_t *bitmap = (potrace_bitmap_t *) lmt_memory_malloc(sizeof(potrace_bitmap_t));
49 if (! bitmap) {
50 return NULL;
51 }
52 bitmap->w = w;
53 bitmap->h = h;
54 bitmap->dy = dy;
55 bitmap->map = (potrace_word *) lmt_memory_calloc(h, dy * BM_WORDSIZE);
56 if (! bitmap->map) {
57 lmt_memory_free(bitmap);
58 return NULL;
59 } else {
60 return bitmap;
61 }
62}
63
64static void free_bitmap(potrace_bitmap_t *bitmap)
65{
66 if (bitmap) {
67 lmt_memory_free(bitmap->map);
68 }
69 lmt_memory_free(bitmap);
70}
71
72static const char* const policies[] = { "black", "white", "left", "right", "minority", "majority", "random", NULL };
73
74typedef struct potracer {
75 potrace_state_t *state;
76 potrace_param_t *parameters;
77 potrace_bitmap_t *bitmap;
78 const char *bytes;
79 int width;
80 int height;
81 int swap;
82 int nx;
83 int ny;
84 unsigned char value;
85 unsigned char match;
86
87} potracer;
88
89static potracer *potracelib_aux_maybe_ispotracer(lua_State *L)
90{
91 return (potracer *) luaL_checkudata(L, 1, POTRACE_METATABLE);
92}
93
94static unsigned char lmt_tochar(lua_State *L, int index)
95{
96 const char *s = lua_tostring(L, index);
97 return s ? (unsigned char) s[0] : '0';
98}
99
100static void potracelib_aux_get_parameters(lua_State *L, int index, potracer *p)
101{
102 if (lua_type(L, index) == LUA_TTABLE) {
103 if (lua_getfield(L, index, "size") == LUA_TNUMBER ) { p->parameters->turdsize = lmt_tointeger(L, -1); } lua_pop(L, 1);
104 if (lua_getfield(L, index, "threshold") == LUA_TNUMBER ) { p->parameters->alphamax = lua_tonumber (L, -1); } lua_pop(L, 1);
105 if (lua_getfield(L, index, "tolerance") == LUA_TNUMBER ) { p->parameters->opttolerance = lua_tonumber (L, -1); } lua_pop(L, 1);
106 if (lua_getfield(L, index, "optimize") == LUA_TBOOLEAN) { p->parameters->opticurve = lua_toboolean(L, -1); } lua_pop(L, 1);
107 if (lua_getfield(L, index, "value") == LUA_TSTRING ) { p->value = lmt_tochar (L, -1); } lua_pop(L, 1);
108 if (lua_getfield(L, index, "negate") == LUA_TBOOLEAN) { p->match = ! lua_toboolean(L, -1); } lua_pop(L, 1);
109
110 if (lua_getfield(L, index, "policy") == LUA_TSTRING ) {
111 p->parameters->turnpolicy = luaL_checkoption(L, -1, "minority", policies);
112 }
113 lua_pop(L, 1);
114 }
115}
116
117static void potracelib_get_bitmap(potracer *p, unsigned char match)
118{
119
120
121 const char *bytes = p->bytes;
122
123 if (bytes) {
124 unsigned char c = p->value;
125 if (p->swap) {
126 if (p->nx != 1 || p->ny != 1) {
127
128 for (int x = 0; x < p->width; x += p->nx) {
129 int bp = (p->height/p->ny) * (x/p->nx);
130 for (int y = 0; y < p->height; y += p->ny) {
131 unsigned char b = (unsigned char) bytes[bp++] == c ? 1 : 0;
132 if (b) {
133 int dy = p->height - y - 1;
134 for (int xn = 0; xn < p->nx; xn++) {
135 for (int yn = 0; yn < p->ny; yn++) {
136 BM_PUT(p->bitmap, x + xn, dy - yn, b);
137 }
138 }
139 }
140 }
141 }
142 } else {
143 for (int x = 0; x < p->width; x++) {
144 int bp = p->height * x;
145 int dy = p->height - 1;
146 for (int y = 0; y < p->height; y++) {
147 unsigned char b = (unsigned char) bytes[bp++] == c ? 1 : 0;
148 if (b) {
149 BM_PUT(p->bitmap, x, dy - y, b);
150 }
151 }
152 }
153 }
154 } else {
155 if (p->nx != 1 || p->ny != 1) {
156
157 for (int y = 0; y < p->height; y += p->ny) {
158 int bp = (p->width/p->nx) * (y/p->ny);
159 for (int x = 0; x < p->width; x += p->nx) {
160 unsigned char b = (unsigned char) bytes[bp++] == c ? 1 : 0;
161 if (b) {
162 int dy = p->height - y - 1;
163 for (int xn = 0; xn < p->nx; xn++) {
164 for (int yn = 0; yn < p->ny; yn++) {
165 BM_PUT(p->bitmap, x + xn, dy - yn, b);
166 }
167 }
168 }
169 }
170 }
171 } else {
172 for (int y = 0; y < p->height; y++) {
173 int bp = p->width * y;
174 int dy = p->height - y - 1;
175 for (int x = 0; x < p->width; x++) {
176 unsigned char b = ((unsigned char) bytes[bp++] == c ? 1 : 0) == match;
177 if (b) {
178 BM_PUT(p->bitmap, x, dy, b);
179 }
180 }
181 }
182 }
183 }
184 }
185}
186
187# define max_explode 4
188
189static int potracelib_new(lua_State *L)
190{
191 if (lua_type(L, 1) == LUA_TTABLE) {
192
193 potracer p = {
194 .state = NULL,
195 .parameters = NULL,
196 .bitmap = NULL,
197 .bytes = NULL,
198 .height = 0,
199 .width = 0,
200 .swap = 0,
201 .nx = 1,
202 .ny = 1,
203 .value = '1',
204 .match = 1,
205 };
206
207 size_t length = 0;
208
209 if (lua_getfield(L, 1, "bytes") == LUA_TSTRING) { p.bytes = lua_tolstring(L, -1, &length); } lua_pop(L, 1);
210 if (lua_getfield(L, 1, "width") == LUA_TNUMBER) { p.width = lmt_tointeger(L, -1); } lua_pop(L, 1);
211 if (lua_getfield(L, 1, "height") == LUA_TNUMBER) { p.height = lmt_tointeger(L, -1); } lua_pop(L, 1);
212 if (lua_getfield(L, 1, "nx") == LUA_TNUMBER) { p.nx = lmt_tointeger(L, -1); } lua_pop(L, 1);
213 if (lua_getfield(L, 1, "ny") == LUA_TNUMBER) { p.ny = lmt_tointeger(L, -1); } lua_pop(L, 1);
214 if (lua_getfield(L, 1, "swap") == LUA_TBOOLEAN) { p.swap = lua_toboolean(L, -1); } lua_pop(L, 1);
215 if (lua_getfield(L, 1, "value") == LUA_TSTRING) { p.value = lmt_tochar (L, -1); } lua_pop(L, 1);
216 if (lua_getfield(L, 1, "negate") == LUA_TBOOLEAN) { p.match = ! lua_toboolean(L, -1); } lua_pop(L, 1);
217
218 if (! p.bytes) {
219 return 0;
220 }
221
222 if ((size_t) (p.width * p.height) > length) {
223 return 0;
224 }
225
226 p.nx = p.nx < 1 ? 1 : (p.nx > max_explode ? max_explode : p.nx);
227 p.ny = p.ny < 1 ? 1 : (p.ny > max_explode ? max_explode : p.ny);
228
229 p.width *= p.nx;
230 p.height *= p.ny;
231
232 if (p.swap) {
233 int tmp = p.width;
234 p.width = p.height;
235 p.height = tmp;
236 tmp = p.nx;
237 p.nx = p.ny;
238 p.ny = tmp;
239 }
240
241 p.parameters = potrace_param_default();
242 if (! p.parameters) {
243 free_bitmap(p.bitmap);
244 return 0;
245 }
246
247 potracelib_aux_get_parameters(L, 1, &p);
248
249 lua_pop(L, 1);
250
251 {
252 potracer *pp = (potracer *) lua_newuserdatauv(L, sizeof(potracer), 0);
253 if (pp) {
254 *pp = p;
255 luaL_getmetatable(L, POTRACE_METATABLE);
256 lua_setmetatable(L, -2);
257 return 1;
258 }
259 }
260 }
261 return 0;
262}
263
264static void potracelib_aux_free(potracer *p)
265{
266 if (p) {
267 if (p->state) {
268 potrace_state_free(p->state);
269 p->state = NULL;
270 }
271 if (p->parameters) {
272 potrace_param_free(p->parameters);
273 p->parameters = NULL;
274 }
275 if (p->bitmap) {
276 free_bitmap(p->bitmap);
277 p->bitmap = NULL;
278 }
279 }
280}
281
282static int potracelib_free(lua_State *L)
283{
284 potracelib_aux_free(potracelib_aux_maybe_ispotracer(L));
285 return 0;
286}
287
288static int aux_potracelib_entries(potracer *p)
289{
290 potrace_path_t *entry = p->state->plist;
291 int entries = 0;
292 while (entry) {
293 entries++;
294 entry = entry->next;
295 }
296 return entries;
297}
298
299
300
301static potrace_path_t *aux_potrace_goto_first(potracer *p, int nofentries, int *first, int *last, int *used) {
302 potrace_path_t *entry = p->state->plist;
303 if (*first && ! *last) {
304 *last = nofentries;
305 }
306 if (*last < 0) {
307 *last = nofentries - *last;
308 if (*last < 0) {
309 *last = 1;
310 }
311 }
312 if (*first <= 0) {
313 *first = 1;
314 }
315 if ((! *last) || (*last > nofentries)) {
316 *last = nofentries;
317 }
318 if (*first > *last) {
319 *first = *last;
320 }
321 for (int i = 1; i < *first; i++) {
322 entry = entry->next;
323 }
324 *used = *last - *first + 1;
325 return entry;
326}
327
328static int potracelib_totable_normal(lua_State *L, potracer *p, int first, int last)
329{
330 int entries = 0;
331 int nofentries = aux_potracelib_entries(p);
332 int used = nofentries;
333 potrace_path_t *entry = aux_potrace_goto_first(p, nofentries, &first, &last, &used);
334 lua_createtable(L, used, 0);
335 while (entry) {
336 int segments = 0;
337 int n = entry->curve.n;
338 int m = n + 1;
339 int *tag = entry->curve.tag;
340
341 int sign = (entry->sign == '+') ? 1 : 0;
342 potrace_dpoint_t (*c)[3] =entry->curve.c;
343 lua_createtable(L, m, sign ? 2 : 1);
344 if (sign) {
345 lua_push_boolean_at_key(L, sign, 1);
346 }
347 lua_push_integer_at_key(L, index, first + entries);
348 lua_createtable(L, 2, 0);
349 lua_push_number_at_index(L, 1, c[n-1][2].x);
350 lua_push_number_at_index(L, 2, c[n-1][2].y);
351 lua_rawseti(L, -2, ++segments);
352 for (int i = 0; i < n; i++) {
353 switch (tag[i]) {
354 case POTRACE_CORNER:
355 lua_createtable(L, 2, 0);
356 lua_push_number_at_index(L, 1, c[i][1].x);
357 lua_push_number_at_index(L, 2, c[i][1].y);
358 lua_rawseti(L, -2, ++segments);
359 lua_createtable(L, 2, 0);
360 lua_push_number_at_index(L, 1, c[i][2].x);
361 lua_push_number_at_index(L, 2, c[i][2].y);
362 lua_rawseti(L, -2, ++segments);
363 break;
364 case POTRACE_CURVETO:
365 lua_createtable(L, 6, 0);
366 lua_push_number_at_index(L, 1, c[i][2].x);
367 lua_push_number_at_index(L, 2, c[i][2].y);
368 lua_push_number_at_index(L, 3, c[i][0].x);
369 lua_push_number_at_index(L, 4, c[i][0].y);
370 lua_push_number_at_index(L, 5, c[i][1].x);
371 lua_push_number_at_index(L, 6, c[i][1].y);
372 lua_rawseti(L, -2, ++segments);
373 break;
374 }
375 }
376 lua_rawseti(L, -2, ++entries);
377 if (first + entries > last) {
378 break;
379 } else {
380 entry = entry->next;
381 }
382 }
383 return 1;
384}
385
386
390
391static int potracelib_totable_debug(lua_State *L, potracer *p, int first, int last)
392{
393 int entries = 0;
394 int nofentries = aux_potracelib_entries(p);
395 int used = nofentries;
396 potrace_path_t *entry = aux_potrace_goto_first(p, nofentries, &first, &last, &used);
397 lua_createtable(L, used, 0);
398 while (entry) {
399 point_t *pt = entry->priv->pt;
400 int segments = 0;
401
402 int sign = (entry->sign == '+') ? 1 : 0;
403 lua_newtable(L);
404 if (sign) {
405 lua_push_boolean_at_key(L, sign, 1);
406 }
407 lua_push_integer_at_key(L, index, first + entries);
408
412 if (sign) {
413 point_t cur = pt[entry->priv->len - 1];
414 point_t prev = cur;
415 lua_push_integer_at_index(L, ++segments, cur.x);
416 lua_push_integer_at_index(L, ++segments, cur.y);
417 for (int i = 0; i < entry->priv->len; i++) {
418 if (pt[i].x != cur.x && pt[i].y != cur.y) {
419 cur = prev;
420 lua_push_integer_at_index(L, ++segments, cur.x);
421 lua_push_integer_at_index(L, ++segments, cur.y);
422 }
423 prev = pt[i];
424 }
425 lua_push_integer_at_index(L, ++segments, pt[entry->priv->len-1].x);
426 lua_push_integer_at_index(L, ++segments, pt[entry->priv->len-1].y);
427 } else {
428 point_t cur = pt[0];
429 point_t prev = cur;
430 lua_push_integer_at_index(L, ++segments, cur.x);
431 lua_push_integer_at_index(L, ++segments, cur.y);
432 for (int i = entry->priv->len - 1; i >= 0; i--) {
433 if (pt[i].x != cur.x && pt[i].y != cur.y) {
434 cur = prev;
435 lua_push_integer_at_index(L, ++segments, cur.x);
436 lua_push_integer_at_index(L, ++segments, cur.y);
437 }
438 prev = pt[i];
439 }
440 lua_push_integer_at_index(L, ++segments, pt[0].x);
441 lua_push_integer_at_index(L, ++segments, pt[0].y);
442 }
443 lua_rawseti(L, -2, ++entries);
444 if (first + entries > last) {
445 break;
446 } else {
447 entry = entry->next;
448 }
449 }
450 return 1;
451}
452
453static int potracelib_totable(lua_State *L)
454{
455 int debug = lua_toboolean(L, 2);
456 int first = lmt_optinteger(L, 3, 0);
457 int last = lmt_optinteger(L, 4, first);
458 lua_settop(L, 1);
459 {
460 potracer *p = potracelib_aux_maybe_ispotracer(L);
461 if (p) {
462 return debug ? potracelib_totable_debug(L, p, first, last) : potracelib_totable_normal(L, p, first, last);
463 } else {
464 return 0;
465 }
466 }
467}
468
469static int potracelib_process(lua_State *L)
470{
471 potracer *p = potracelib_aux_maybe_ispotracer(L);
472 if (p) {
473 potracelib_aux_get_parameters(L, 2, p);
474 if (p->bitmap) {
475 free_bitmap(p->bitmap);
476 }
477 p->bitmap = new_bitmap(p->width, p->height);
478 if (p->bitmap) {
479 potracelib_get_bitmap(p, p->match);
480 p->state = potrace_trace(p->parameters, p->bitmap);
481 if (p->state && p->state->status == POTRACE_STATUS_OK) {
482 lua_pushboolean(L, 1);
483 return 1;
484 }
485 }
486 }
487 lua_pushboolean(L, 0);
488 return 1;
489}
490
491static int potracelib_tostring(lua_State * L)
492 {
493 potracer *p = potracelib_aux_maybe_ispotracer(L);
494 if (p) {
495 (void) lua_pushfstring(L, "<potracer %p>", p);
496 } else {
497 lua_pushnil(L);
498 }
499 return 1;
500}
501
502
505
506static const struct luaL_Reg potracelib_instance_metatable[] = {
507 { "__tostring", potracelib_tostring },
508 { "__gc", potracelib_free },
509 { NULL, NULL },
510};
511
512static const luaL_Reg potracelib_function_list[] =
513{
514 { "new", potracelib_new },
515 { "free", potracelib_free },
516 { "process", potracelib_process },
517 { "totable", potracelib_totable },
518
519 { NULL, NULL },
520};
521
522int luaopen_potrace(lua_State *L)
523{
524 luaL_newmetatable(L, POTRACE_METATABLE);
525 luaL_setfuncs(L, potracelib_instance_metatable, 0);
526 lua_newtable(L);
527 luaL_setfuncs(L, potracelib_function_list, 0);
528 return 1;
529} |