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_getnewfields(lua_State *L)
190{
191 lua_createtable(L, 0, 8);
192 lua_set_string_by_key(L, "bytes", "string");
193 lua_set_string_by_key(L, "width", "integer");
194 lua_set_string_by_key(L, "height", "integer");
195 lua_set_string_by_key(L, "nx", "integer");
196 lua_set_string_by_key(L, "ny", "integer");
197 lua_set_string_by_key(L, "swap", "boolean");
198 lua_set_string_by_key(L, "value", "character");
199 lua_set_string_by_key(L, "negate", "boolean");
200 return 1;
201}
202
203static int potracelib_getprocessfields(lua_State *L)
204{
205 lua_createtable(L, 0, 8);
206 lua_set_string_by_key(L, "size", "integer");
207 lua_set_string_by_key(L, "threshold", "number");
208 lua_set_string_by_key(L, "tolerance", "number");
209 lua_set_string_by_key(L, "optimize", "boolean");
210 lua_set_string_by_key(L, "value", "characetr");
211 lua_set_string_by_key(L, "negate", "boolean");
212 lua_set_string_by_key(L, "policy", "string");
213 return 1;
214}
215
216static int potracelib_getpolicyvalues(lua_State *L)
217{
218 lua_createtable(L, 7, 0);
219 lua_set_string_by_index(L, 1, "black");
220 lua_set_string_by_index(L, 2, "white");
221 lua_set_string_by_index(L, 3, "left");
222 lua_set_string_by_index(L, 4, "right");
223 lua_set_string_by_index(L, 5, "minority");
224 lua_set_string_by_index(L, 6, "majority");
225 lua_set_string_by_index(L, 7, "random");
226 return 1;
227}
228
229static int potracelib_new(lua_State *L)
230{
231 if (lua_type(L, 1) == LUA_TTABLE) {
232
233 potracer p = {
234 .state = NULL,
235 .parameters = NULL,
236 .bitmap = NULL,
237 .bytes = NULL,
238 .height = 0,
239 .width = 0,
240 .swap = 0,
241 .nx = 1,
242 .ny = 1,
243 .value = '1',
244 .match = 1,
245 };
246
247 size_t length = 0;
248
249 if (lua_getfield(L, 1, "bytes") == LUA_TSTRING) { p.bytes = lua_tolstring(L, -1, &length); } lua_pop(L, 1);
250 if (lua_getfield(L, 1, "width") == LUA_TNUMBER) { p.width = lmt_tointeger(L, -1); } lua_pop(L, 1);
251 if (lua_getfield(L, 1, "height") == LUA_TNUMBER) { p.height = lmt_tointeger(L, -1); } lua_pop(L, 1);
252 if (lua_getfield(L, 1, "nx") == LUA_TNUMBER) { p.nx = lmt_tointeger(L, -1); } lua_pop(L, 1);
253 if (lua_getfield(L, 1, "ny") == LUA_TNUMBER) { p.ny = lmt_tointeger(L, -1); } lua_pop(L, 1);
254 if (lua_getfield(L, 1, "swap") == LUA_TBOOLEAN) { p.swap = lua_toboolean(L, -1); } lua_pop(L, 1);
255 if (lua_getfield(L, 1, "value") == LUA_TSTRING) { p.value = lmt_tochar (L, -1); } lua_pop(L, 1);
256 if (lua_getfield(L, 1, "negate") == LUA_TBOOLEAN) { p.match = ! lua_toboolean(L, -1); } lua_pop(L, 1);
257
258 if (! p.bytes) {
259 return 0;
260 }
261
262 if ((size_t) (p.width * p.height) > length) {
263 return 0;
264 }
265
266 p.nx = p.nx < 1 ? 1 : (p.nx > max_explode ? max_explode : p.nx);
267 p.ny = p.ny < 1 ? 1 : (p.ny > max_explode ? max_explode : p.ny);
268
269 p.width *= p.nx;
270 p.height *= p.ny;
271
272 if (p.swap) {
273 int tmp = p.width;
274 p.width = p.height;
275 p.height = tmp;
276 tmp = p.nx;
277 p.nx = p.ny;
278 p.ny = tmp;
279 }
280
281 p.parameters = potrace_param_default();
282 if (! p.parameters) {
283 free_bitmap(p.bitmap);
284 return 0;
285 }
286
287 potracelib_aux_get_parameters(L, 1, &p);
288
289 lua_pop(L, 1);
290
291 {
292 potracer *pp = (potracer *) lua_newuserdatauv(L, sizeof(potracer), 0);
293 if (pp) {
294 *pp = p;
295 luaL_getmetatable(L, POTRACE_METATABLE);
296 lua_setmetatable(L, -2);
297 return 1;
298 }
299 }
300 }
301 return 0;
302}
303
304static void potracelib_aux_free(potracer *p)
305{
306 if (p) {
307 if (p->state) {
308 potrace_state_free(p->state);
309 p->state = NULL;
310 }
311 if (p->parameters) {
312 potrace_param_free(p->parameters);
313 p->parameters = NULL;
314 }
315 if (p->bitmap) {
316 free_bitmap(p->bitmap);
317 p->bitmap = NULL;
318 }
319 }
320}
321
322static int potracelib_free(lua_State *L)
323{
324 potracelib_aux_free(potracelib_aux_maybe_ispotracer(L));
325 return 0;
326}
327
328static int aux_potracelib_entries(potracer *p)
329{
330 potrace_path_t *entry = p->state->plist;
331 int entries = 0;
332 while (entry) {
333 entries++;
334 entry = entry->next;
335 }
336 return entries;
337}
338
339
340
341static potrace_path_t *aux_potrace_goto_first(potracer *p, int nofentries, int *first, int *last, int *used) {
342 potrace_path_t *entry = p->state->plist;
343 if (*first && ! *last) {
344 *last = nofentries;
345 }
346 if (*last < 0) {
347 *last = nofentries - *last;
348 if (*last < 0) {
349 *last = 1;
350 }
351 }
352 if (*first <= 0) {
353 *first = 1;
354 }
355 if ((! *last) || (*last > nofentries)) {
356 *last = nofentries;
357 }
358 if (*first > *last) {
359 *first = *last;
360 }
361 for (int i = 1; i < *first; i++) {
362 entry = entry->next;
363 }
364 *used = *last - *first + 1;
365 return entry;
366}
367
368static int potracelib_totable_normal(lua_State *L, potracer *p, int first, int last)
369{
370 int entries = 0;
371 int nofentries = aux_potracelib_entries(p);
372 int used = nofentries;
373 potrace_path_t *entry = aux_potrace_goto_first(p, nofentries, &first, &last, &used);
374 lua_createtable(L, used, 0);
375 while (entry) {
376 int segments = 0;
377 int n = entry->curve.n;
378 int m = n + 1;
379 int *tag = entry->curve.tag;
380
381 int sign = (entry->sign == '+') ? 1 : 0;
382 potrace_dpoint_t (*c)[3] =entry->curve.c;
383 lua_createtable(L, m, sign ? 2 : 1);
384 if (sign) {
385 lua_push_boolean_at_key(L, sign, 1);
386 }
387 lua_push_integer_at_key(L, index, first + entries);
388 lua_createtable(L, 2, 0);
389 lua_push_number_at_index(L, 1, c[n-1][2].x);
390 lua_push_number_at_index(L, 2, c[n-1][2].y);
391 lua_rawseti(L, -2, ++segments);
392 for (int i = 0; i < n; i++) {
393 switch (tag[i]) {
394 case POTRACE_CORNER:
395 lua_createtable(L, 2, 0);
396 lua_push_number_at_index(L, 1, c[i][1].x);
397 lua_push_number_at_index(L, 2, c[i][1].y);
398 lua_rawseti(L, -2, ++segments);
399 lua_createtable(L, 2, 0);
400 lua_push_number_at_index(L, 1, c[i][2].x);
401 lua_push_number_at_index(L, 2, c[i][2].y);
402 lua_rawseti(L, -2, ++segments);
403 break;
404 case POTRACE_CURVETO:
405 lua_createtable(L, 6, 0);
406 lua_push_number_at_index(L, 1, c[i][2].x);
407 lua_push_number_at_index(L, 2, c[i][2].y);
408 lua_push_number_at_index(L, 3, c[i][0].x);
409 lua_push_number_at_index(L, 4, c[i][0].y);
410 lua_push_number_at_index(L, 5, c[i][1].x);
411 lua_push_number_at_index(L, 6, c[i][1].y);
412 lua_rawseti(L, -2, ++segments);
413 break;
414 }
415 }
416 lua_rawseti(L, -2, ++entries);
417 if (first + entries > last) {
418 break;
419 } else {
420 entry = entry->next;
421 }
422 }
423 return 1;
424}
425
426
430
431static int potracelib_totable_debug(lua_State *L, potracer *p, int first, int last)
432{
433 int entries = 0;
434 int nofentries = aux_potracelib_entries(p);
435 int used = nofentries;
436 potrace_path_t *entry = aux_potrace_goto_first(p, nofentries, &first, &last, &used);
437 lua_createtable(L, used, 0);
438 while (entry) {
439 point_t *pt = entry->priv->pt;
440 int segments = 0;
441
442 int sign = (entry->sign == '+') ? 1 : 0;
443 lua_newtable(L);
444 if (sign) {
445 lua_push_boolean_at_key(L, sign, 1);
446 }
447 lua_push_integer_at_key(L, index, first + entries);
448
452 if (sign) {
453 point_t cur = pt[entry->priv->len - 1];
454 point_t prev = cur;
455 lua_push_integer_at_index(L, ++segments, cur.x);
456 lua_push_integer_at_index(L, ++segments, cur.y);
457 for (int i = 0; i < entry->priv->len; i++) {
458 if (pt[i].x != cur.x && pt[i].y != cur.y) {
459 cur = prev;
460 lua_push_integer_at_index(L, ++segments, cur.x);
461 lua_push_integer_at_index(L, ++segments, cur.y);
462 }
463 prev = pt[i];
464 }
465 lua_push_integer_at_index(L, ++segments, pt[entry->priv->len-1].x);
466 lua_push_integer_at_index(L, ++segments, pt[entry->priv->len-1].y);
467 } else {
468 point_t cur = pt[0];
469 point_t prev = cur;
470 lua_push_integer_at_index(L, ++segments, cur.x);
471 lua_push_integer_at_index(L, ++segments, cur.y);
472 for (int i = entry->priv->len - 1; i >= 0; i--) {
473 if (pt[i].x != cur.x && pt[i].y != cur.y) {
474 cur = prev;
475 lua_push_integer_at_index(L, ++segments, cur.x);
476 lua_push_integer_at_index(L, ++segments, cur.y);
477 }
478 prev = pt[i];
479 }
480 lua_push_integer_at_index(L, ++segments, pt[0].x);
481 lua_push_integer_at_index(L, ++segments, pt[0].y);
482 }
483 lua_rawseti(L, -2, ++entries);
484 if (first + entries > last) {
485 break;
486 } else {
487 entry = entry->next;
488 }
489 }
490 return 1;
491}
492
493static int potracelib_totable(lua_State *L)
494{
495 int debug = lua_toboolean(L, 2);
496 int first = lmt_optinteger(L, 3, 0);
497 int last = lmt_optinteger(L, 4, first);
498 lua_settop(L, 1);
499 {
500 potracer *p = potracelib_aux_maybe_ispotracer(L);
501 if (p) {
502 return debug ? potracelib_totable_debug(L, p, first, last) : potracelib_totable_normal(L, p, first, last);
503 } else {
504 return 0;
505 }
506 }
507}
508
509static int potracelib_process(lua_State *L)
510{
511 potracer *p = potracelib_aux_maybe_ispotracer(L);
512 if (p) {
513 potracelib_aux_get_parameters(L, 2, p);
514 if (p->bitmap) {
515 free_bitmap(p->bitmap);
516 }
517 p->bitmap = new_bitmap(p->width, p->height);
518 if (p->bitmap) {
519 potracelib_get_bitmap(p, p->match);
520 p->state = potrace_trace(p->parameters, p->bitmap);
521 if (p->state && p->state->status == POTRACE_STATUS_OK) {
522 lua_pushboolean(L, 1);
523 return 1;
524 }
525 }
526 }
527 lua_pushboolean(L, 0);
528 return 1;
529}
530
531static int potracelib_tostring(lua_State * L)
532 {
533 potracer *p = potracelib_aux_maybe_ispotracer(L);
534 if (p) {
535 (void) lua_pushfstring(L, "<potracer %p>", p);
536 } else {
537 lua_pushnil(L);
538 }
539 return 1;
540}
541
542
545
546static const struct luaL_Reg potracelib_instance_metatable[] = {
547 { "__tostring", potracelib_tostring },
548 { "__gc", potracelib_free },
549 { NULL, NULL },
550};
551
552static const luaL_Reg potracelib_function_list[] = {
553 { "new", potracelib_new },
554 { "free", potracelib_free },
555 { "process", potracelib_process },
556 { "totable", potracelib_totable },
557 { "getnewfields", potracelib_getnewfields },
558 { "getprocessfields", potracelib_getprocessfields },
559 { "getpolicyvalues", potracelib_getpolicyvalues },
560 { NULL, NULL },
561};
562
563int luaopen_potrace(lua_State *L)
564{
565 luaL_newmetatable(L, POTRACE_METATABLE);
566 luaL_setfuncs(L, potracelib_instance_metatable, 0);
567 lua_newtable(L);
568 luaL_setfuncs(L, potracelib_function_list, 0);
569 return 1;
570} |