lmtdecodelib.c /size: 22 Kb    last modification: 2024-01-16 10:22
1/*
2    See license.txt in the root of this project.
3*/
4
5# include "luametatex.h"
6
7/*tex
8
9    Some png helpers, I could have introduced a userdata for blobs at some point but it's not that
10    useful as string sare also sequences of bytes and lua handles those well. These are interfaces
11    can change any time we like without notice till we like what we have.
12
13*/
14
15/* t xsize ysize bpp (includes mask) */
16
17static int pnglib_applyfilter(lua_State *L)
18{
19    size_t size;
20    const char *s = luaL_checklstring(L, 1, &size);
21    int xsize     = lmt_tointeger(L, 2);
22    int ysize     = lmt_tointeger(L, 3);
23    int slice     = lmt_tointeger(L, 4);
24    int len       = xsize * slice + 1; /* filter byte */
25    int n         = 0;
26    int m         = len - 1;
27    unsigned char *t;
28    if (ysize * len != (int) size) {
29        tex_formatted_warning("png filter", "sizes don't match: %i expected, %i provided", ysize *len, size);
30        return 0;
31    }
32    t = lmt_memory_malloc(size);
33    if (! t) {
34        tex_normal_warning("png filter", "not enough memory");
35        return 0;
36    }
37    memcpy(t, s, size);
38    for (int i = 0; i < ysize; i++) {
39        switch (t[n]) {
40            case 0 :
41                break;
42            case 1 :
43                for (int j = n + slice + 1; j <= n + m; j++) {
44                    t[j] = (unsigned char) (t[j] + t[j-slice]);
45                }
46                break;
47            case 2 :
48                if (i > 0) {
49                    for (int j = n + 1; j <= n + m; j++) {
50                        t[j] = (unsigned char) (t[j] + t[j-len]);
51                    }
52                }
53                break;
54            case 3 :
55                if (i > 0) {
56                    for (int j = n + 1; j <= n + slice; j++) {
57                        t[j] = (unsigned char) (t[j] + t[j-len]/2);
58                    }
59                    for (int j = n + slice + 1; j <= n + m; j++) {
60                        t[j] = (unsigned char) (t[j] + (t[j-slice] + t[j-len])/2);
61                    }
62                } else {
63                    for (int j = n + slice + 1; j <= n + m; j++) {
64                        t[j] = (unsigned char) (t[j] + t[j-slice]/2);
65                    }
66                }
67                break;
68            case 4 :
69                if (i > 0) {
70                    for (int j = n + 1; j <= n + slice; j++) {
71                        int p = j - len;
72                        t[j] = (unsigned char) (t[j] + t[p]);
73                    }
74                    for (int j = n + slice + 1; j <= n + m; j++) {
75                        int p = j - len;
76                        unsigned char a = t[j-slice];
77                        unsigned char b = t[p];
78                        unsigned char c = t[p-slice];
79                        int pa = b - c;
80                        int pb = a - c;
81                        int pc = pa + pb;
82                        if (pa < 0) { pa = - pa; }
83                        if (pb < 0) { pb = - pb; }
84                        if (pc < 0) { pc = - pc; }
85                        t[j] = (unsigned char) (t[j] + ((pa <= pb && pa <= pc) ? a : ((pb <= pc) ? b : c)));
86                    }
87                } else {
88                    /* What to do here? */
89                    /*
90                    for (int j = n + slice + 1; j <= n + m; j++) {
91                        int p = j - len;
92                        unsigned char a = t[j-slice];
93                        unsigned char b = t[p];
94                        unsigned char c = t[p-slice];
95                        int pa = b - c;
96                        int pb = a - c;
97                        int pc = pa + pb;
98                        if (pa < 0) { pa = - pa; }
99                        if (pb < 0) { pb = - pb; }
100                        if (pc < 0) { pc = - pc; }
101                        t[j] = (unsigned char) (t[j] + ((pa <= pb && pa <= pc) ? a : ((pb <= pc) ? b : c)));
102                    }
103                    */
104                }
105                break;
106            default:
107                break;
108        }
109        n = n + len;
110    }
111    /* wipe out filter byte */
112    {
113        int j = 0; /* source */
114        int m = 0; /* target */
115        for (int i = 0; i < ysize; i++) {
116            // (void) memcpy(&t[m], &t[j+1], len-1); /* target source size */
117            (void) memmove(&t[m], &t[j+1], (size_t)len - 1); /* target source size */
118            j += len;
119            m += len - 1;
120        }
121        lua_pushlstring(L, (char *) t, size-ysize);
122        /*
123        int j = 0;
124        luaL_Buffer b;
125        luaL_buffinit(L, &b);
126        for (int i = 0; i < ysize; i++) {
127            luaL_addlstring(&b, (const char *)&t[j+1], len-1);
128            j += len;
129        }
130        luaL_pushresult(&b);
131        */
132    }
133    lmt_memory_free(t);
134    return 1;
135}
136
137/* t xsize ysize bpp (includes mask) bytes */
138
139static int pnglib_splitmask(lua_State *L)
140{
141    size_t size;
142    const char *t  = luaL_checklstring(L, 1, &size);
143    int xsize      = lmt_tointeger(L, 2);
144    int ysize      = lmt_tointeger(L, 3);
145    int bpp        = lmt_tointeger(L, 4); /* 1 or 3 */
146    int bytes      = lmt_tointeger(L, 5); /* 1 or 2 */
147    int slice      = (bpp + 1) * bytes;
148    int len        = xsize * slice;
149    int blen       = bpp * bytes;
150    int mlen       = bytes;
151    int nt         = 0;
152    int nb         = 0;
153    int nm         = 0;
154    int bsize      = ysize * xsize * blen;
155    int msize      = ysize * xsize * mlen;
156    char *b, *m;
157    /* we assume that the filter byte is gone */
158    if (ysize * len != (int) size) {
159        tex_formatted_warning("png split", "sizes don't match: %i expected, %i provided", ysize * len, size);
160        return 0;
161    }
162    b = lmt_memory_malloc(bsize);
163    m = lmt_memory_malloc(msize);
164    if (! (b && m)) {
165        tex_normal_warning("png split mask", "not enough memory");
166        return 0;
167    }
168    /* a bit optimized */
169    switch (blen) {
170        case 1:
171            /* 8 bit gray or indexed graphics */
172            for (int i = 0; i < ysize * xsize; i++) {
173                b[nb++] = t[nt++];
174                m[nm++] = t[nt++];
175            }
176            break;
177        case 3:
178            /* 8 bit rgb graphics */
179            for (int i = 0; i < ysize * xsize; i++) {
180                /*
181                b[nb++] = t[nt++];
182                b[nb++] = t[nt++];
183                b[nb++] = t[nt++];
184                */
185                memcpy(&b[nb], &t[nt], 3);
186                nt += 3;
187                nb += 3;
188                m[nm++] = t[nt++];
189            }
190            break;
191        default:
192            /* everything else */
193            for (int i = 0; i < ysize * xsize; i++) {
194                memcpy (&b[nb], &t[nt], blen);
195                nt += blen;
196                nb += blen;
197                memcpy (&m[nm], &t[nt], mlen);
198                nt += mlen;
199                nm += mlen;
200            }
201            break;
202    }
203    lua_pushlstring(L, b, bsize);
204    lmt_memory_free(b);
205    lua_pushlstring(L, m, msize);
206    lmt_memory_free(m);
207    return 2;
208}
209
210/* output input xsize ysize slice pass filter */
211
212static int pnglib_interlace(lua_State *L)
213{
214    int xstarts[] = { 0, 4, 0, 2, 0, 1, 0 };
215    int ystarts[] = { 0, 0, 4, 0, 2, 0, 1 };
216    int xsteps[]  = { 8, 8, 4, 4, 2, 2, 1 };
217    int ysteps[]  = { 8, 8, 8, 4, 4, 2, 2 };
218    size_t isize = 0;
219    size_t psize = 0;
220    const char *inp;
221    const char *pre;
222    char *out;
223    int xsize, ysize, xstep, ystep, xstart, ystart, slice, pass, nx, ny;
224    int target, start, step, size;
225    /* dimensions */
226    xsize  = lmt_tointeger(L, 1);
227    ysize  = lmt_tointeger(L, 2);
228    slice  = lmt_tointeger(L, 3);
229    pass   = lmt_tointeger(L, 4);
230    if (pass < 1 || pass > 7) {
231        tex_formatted_warning("png interlace", "bass pass: %i (1..7)", pass);
232        return 0;
233    }
234    pass   = pass - 1;
235    /* */
236    nx     = (xsize + xsteps[pass] - xstarts[pass] - 1) / xsteps[pass];
237    ny     = (ysize + ysteps[pass] - ystarts[pass] - 1) / ysteps[pass];
238    /* */
239    xstart = xstarts[pass];
240    xstep  = xsteps[pass];
241    ystart = ystarts[pass];
242    ystep  = ysteps[pass];
243    /* */
244    xstep  = xstep * slice;
245    xstart = xstart * slice;
246    xsize  = xsize * slice;
247    target = ystart * xsize + xstart;
248    ystep  = ystep * xsize;
249    /* */
250    step   = nx * xstep;
251    size   = ysize * xsize;
252    start  = 0;
253    /* */
254    inp    = luaL_checklstring(L, 5, &isize);
255    pre    = NULL;
256    out    = NULL;
257    if (pass > 0) {
258        pre = luaL_checklstring(L, 6, &psize);
259        if ((int) psize < size) {
260            tex_formatted_warning("png interlace", "output sizes don't match: %i expected, %i provided", psize, size);
261            return 0;
262        }
263    }
264    /* todo: some more checking */
265    out = lmt_memory_malloc(size);
266    if (out) {
267        if (pass == 0) {
268            memset(out, 0, size);
269        }
270        else {
271            memcpy(out, pre, psize);
272        }
273    } else {
274        tex_normal_warning("png interlace", "not enough memory");
275        return 0;
276    }
277    switch (slice) {
278        case 1:
279            for (int j = 0; j < ny; j++) {
280                int t = target + j * ystep;
281                for (int i = t; i < t + step; i += xstep) {
282                    out[i] = inp[start];
283                    start = start + slice;
284                }
285            }
286            break;
287        case 2:
288            for (int j = 0; j < ny; j++) {
289                int t = target + j * ystep;
290                for (int i = t; i < t + step; i += xstep) {
291                    out[i]   = inp[start];
292                    out[i+1] = inp[start+1];
293                    start = start + slice;
294                }
295            }
296            break;
297        case 3:
298            for (int j = 0; j < ny; j++) {
299                int t = target + j * ystep;
300                for (int i = t; i < t + step;i += xstep) {
301                    out[i]   = inp[start];
302                    out[i+1] = inp[start+1];
303                    out[i+2] = inp[start+2];
304                    start = start + slice;
305                }
306            }
307            break;
308        default:
309            for (int j = 0; j < ny; j++) {
310                int t = target + j * ystep;
311                for (int i = t; i < t + step; i += xstep) {
312                    memcpy(&out[i], &inp[start], slice);
313                    start = start + slice;
314                }
315            }
316            break;
317    }
318    lua_pushlstring(L, out, size);
319    lmt_memory_free(out);
320    return 1;
321}
322
323/* content xsize ysize parts run factor */
324
325# define extract1(a,b) ((a >> b) & 0x01)
326# define extract2(a,b) ((a >> b) & 0x03)
327# define extract4(a,b) ((a >> b) & 0x0F)
328
329static int pnglib_expand(lua_State *L)
330{
331    size_t tsize;
332    const char *t = luaL_checklstring(L, 1, &tsize);
333    char *o       = NULL;
334    int n         = 0;
335    int k         = 0;
336    int xsize     = lmt_tointeger(L, 2);
337    int ysize     = lmt_tointeger(L, 3);
338    int parts     = lmt_tointeger(L, 4);
339    int xline     = lmt_tointeger(L, 5);
340    int factor    = lua_toboolean(L, 6);
341    int size      = ysize * xsize;
342    int extra     = ysize * xsize + 16; /* probably a few bytes is enough */
343    if (xline*ysize > (int) tsize) {
344        tex_formatted_warning("png expand","expand sizes don't match: %i expected, %i provided",size,parts*tsize);
345        return 0;
346    }
347    o = lmt_memory_malloc(extra);
348    if (! o) {
349        tex_normal_warning ("png expand", "not enough memory");
350        return 0;
351    }
352    /* we could use on branch and factor variables ,, saves code, costs cycles */
353    if (factor) {
354        switch (parts) {
355            case 4:
356                for (int i = 0; i < ysize; i++) {
357                    k = i * xsize;
358                    for (int j = n; j < n + xline; j++) {
359                        unsigned char v = t[j];
360                        o[k++] = (unsigned char) extract4 (v, 4) * 0x11;
361                        o[k++] = (unsigned char) extract4 (v, 0) * 0x11;
362                    }
363                    n = n + xline;
364                }
365                break;
366            case 2:
367                for (int i = 0; i < ysize; i++) {
368                    k = i * xsize;
369                    for (int j = n; j < n + xline; j++) {
370                        unsigned char v = t[j];
371                        for (int b = 6; b >= 0; b -= 2) {
372                            o[k++] = (unsigned char) extract2 (v, b) * 0x55;
373                        }
374                    }
375                    n = n + xline;
376                }
377                break;
378            default:
379                for (int i = 0; i < ysize; i++) {
380                    k = i * xsize;
381                    for (int j = n; j < n + xline; j++) {
382                        unsigned char v = t[j];
383                        for (int b = 7; b >= 0; b--) {
384                            o[k++] = (unsigned char) extract1 (v, b) * 0xFF;
385                        }
386                    }
387                    n = n + xline;
388                }
389                break;
390        }
391    } else {
392        switch (parts) {
393            case 4:
394                for (int i = 0; i < ysize; i++) {
395                    k = i * xsize;
396                    for (int j = n; j < n + xline; j++) {
397                        unsigned char v = t[j];
398                        o[k++] = (unsigned char) extract4 (v, 4);
399                        o[k++] = (unsigned char) extract4 (v, 0);
400                    }
401                    n = n + xline;
402                }
403                break;
404            case 2:
405                for (int i = 0; i < ysize; i++) {
406                    k = i * xsize;
407                    for (int j = n; j < n + xline; j++) {
408                        unsigned char v = t[j];
409                        for (int b = 6; b >= 0; b -= 2) {
410                            o[k++] = (unsigned char) extract2 (v, b);
411                        }
412                    }
413                    n = n + xline;
414                }
415                break;
416            default:
417                for (int i = 0; i < ysize; i++) {
418                    k = i * xsize;
419                    for (int j = n; j < n + xline; j++) {
420                        unsigned char v = t[j];
421                        for (int b = 7; b >= 0; b--) {
422                            o[k++] = (unsigned char) extract1 (v, b);
423                        }
424                    }
425                    n = n + xline;
426                }
427                break;
428        }
429    }
430    lua_pushlstring(L, o, size);
431    lmt_memory_free(o);
432    return 1;
433}
434
435/*tex
436    This is just a quick and dirty experiment. We need to satisfy pdf standards
437    and simple graphics can be converted this way. Maybe add some more control
438    over calculating |k|.
439*/
440
441static int pnglib_tocmyk(lua_State *L)
442{
443    size_t tsize;
444    const char *t = luaL_checklstring(L, 1, &tsize);
445    int depth = lmt_optinteger(L, 2, 0);
446    if ((tsize > 0) && (depth == 8 || depth == 16)) {
447        size_t osize = 0;
448        char *o = NULL;
449        if (depth == 8) {
450            o = lmt_memory_malloc(4 * (tfloor(tsize/3) + 1)); /*tex Plus some slack. */
451        } else {
452            o = lmt_memory_malloc(8 * (tfloor(tsize/6) + 1)); /*tex Plus some slack. */
453        }
454        if (! o) {
455            tex_normal_warning ("png tocmyk", "not enough memory");
456            return 0;
457        } else if (depth == 8) {
458            /*
459            for (size_t i = 0; i < tsize; i += 3) {
460                o[osize++] = (const char) (0xFF - t[i]);
461                o[osize++] = (const char) (0xFF - t[i + 1]);
462                o[osize++] = (const char) (0xFF - t[i + 2]);
463                o[osize++] = '\0';
464            }
465            */
466            for (size_t i = 0; i < tsize; ) {
467                o[osize++] = (const char) (0xFF - t[i++]);
468                o[osize++] = (const char) (0xFF - t[i++]);
469                o[osize++] = (const char) (0xFF - t[i++]);
470                o[osize++] = '\0';
471            }
472        } else {
473            /*tex This needs checking! */
474            /*
475            for (size_t i = 0; i < tsize; i += 6) {
476                o[osize++] = (const char) (0xFF - t[i]);
477                o[osize++] = (const char) (0xFF - t[i + 1]);
478                o[osize++] = (const char) (0xFF - t[i + 2]);
479                o[osize++] = (const char) (0xFF - t[i + 3]);
480                o[osize++] = (const char) (0xFF - t[i + 4]);
481                o[osize++] = (const char) (0xFF - t[i + 5]);
482                o[osize++] = '\0';
483                o[osize++] = '\0';
484            }
485            */
486            for (size_t i = 0; i < tsize; ) {
487                o[osize++] = (const char) (0xFF - t[i++]);
488                o[osize++] = (const char) (0xFF - t[i++]);
489                o[osize++] = (const char) (0xFF - t[i++]);
490                o[osize++] = (const char) (0xFF - t[i++]);
491                o[osize++] = (const char) (0xFF - t[i++]);
492                o[osize++] = (const char) (0xFF - t[i++]);
493                o[osize++] = '\0';
494                o[osize++] = '\0';
495            }
496        }
497        lua_pushlstring(L, o, osize-1);
498        lmt_memory_free(o);
499    } else {
500        lua_pushnil(L);
501    }
502    return 1;
503}
504
505/*tex Make a mask for a pallete. */
506
507static int pnglib_tomask(lua_State *L) /* for palette */
508{
509    size_t tsize, ssize;
510    const char *t  = luaL_checklstring(L, 1, &tsize);
511    const char *s  = luaL_checklstring(L, 2, &ssize);
512    size_t xsize   = lmt_tosizet(L, 3);
513    size_t ysize   = lmt_tosizet(L, 4);
514    int colordepth = lmt_tointeger(L, 5);
515    size_t osize   = xsize * ysize;
516    if (osize  == tsize) {
517        char *o    = lmt_memory_malloc(osize);
518        char *v    = lmt_memory_calloc(256,1);
519        size_t len = xsize * colordepth / 8; // ceil
520        size_t k   = 0;
521        memset(v, 0xFF, 256);
522        memcpy(v, s, ssize > 256 ? 256 : ssize);
523        for (size_t i = 0; i < ysize; i++) {
524            size_t f = i * len;
525            size_t l = f + len;
526            switch (colordepth) {
527                case 8:
528                    for (size_t j = f; j < l; j++) {
529                        int c = t[j];
530                        o[k++] =  (unsigned char) v[c];
531                    }
532                    break;
533                case 4:
534                    for (size_t j = f; j < l; j++) {
535                        int c = t[j];
536                        o[k++] = (unsigned char) v[(c >> 4) & 0x0F];
537                        o[k++] = (unsigned char) v[(c >> 0) & 0x0F];
538                    }
539                    break;
540                case 2:
541                    for (size_t j = f; j < l; j++) {
542                        int c = t[j];
543                        o[k++] = (unsigned char) v[(c >> 6) & 0x03];
544                        o[k++] = (unsigned char) v[(c >> 4) & 0x03];
545                        o[k++] = (unsigned char) v[(c >> 2) & 0x03];
546                        o[k++] = (unsigned char) v[(c >> 0) & 0x03];
547                    }
548                    break;
549                default:
550                    for (size_t j = f; j < l; j++) {
551                        int c = t[j];
552                        o[k++] = (unsigned char) v[(c >> 7) & 0x01];
553                        o[k++] = (unsigned char) v[(c >> 6) & 0x01];
554                        o[k++] = (unsigned char) v[(c >> 5) & 0x01];
555                        o[k++] = (unsigned char) v[(c >> 4) & 0x01];
556                        o[k++] = (unsigned char) v[(c >> 3) & 0x01];
557                        o[k++] = (unsigned char) v[(c >> 2) & 0x01];
558                        o[k++] = (unsigned char) v[(c >> 1) & 0x01];
559                        o[k++] = (unsigned char) v[(c >> 0) & 0x01];
560                    }
561                    break;
562            }
563        }
564        lua_pushlstring(L, o, osize);
565        lmt_memory_free(o);
566    } else {
567        lua_pushnil(L);
568    }
569    return 1;
570}
571static int pnglib_makemask(lua_State *L) /* for palette */
572{
573    size_t size;
574    const char *content  = luaL_checklstring(L, 1, &size);
575    char mapping[256] = { 0x00 };
576    char *mask = lmt_memory_malloc(size);
577    switch (lua_type(L, 2)) {
578        case LUA_TNUMBER:
579            {
580                int n = lua_tointeger(L, 2);
581                n = n < 0 ? 0 : n > 255 ? 255 : n;
582                for (int i = 0; i <= n; i++) {
583                    mapping[i] = 0xFF;
584                }
585            }
586            break;
587        case LUA_TTABLE:
588            {
589                int n = (int) lua_rawlen(L, 2);
590                for (int i = 1; i <= n; i++) {
591                    if (lua_rawgeti(L, 2, i) == LUA_TTABLE) {
592                        int m = (int) lua_rawlen(L, -1);
593                        if (m == 3) { 
594                            int b, e, v; 
595                            lua_rawgeti(L, -1, 1);
596                            lua_rawgeti(L, -2, 2);
597                            lua_rawgeti(L, -3, 3);
598                            b = lua_tointeger(L, -3);
599                            e = lua_tointeger(L, -2);
600                            v = lua_tointeger(L, -1);
601                            b = b < 0 ? 0 : b > 255 ? 255 : b;
602                            e = e < 0 ? 0 : e > 255 ? 255 : e;
603                            v = v < 0 ? 0 : v > 255 ? 255 : v;
604                            for (int i = b; i <= e; i++) {
605                                mapping[i] = (char) v;
606                            }
607                            lua_pop(L, 3);
608                        }
609                    }
610                    lua_pop(L, 1);
611                }
612            }
613        case LUA_TSTRING:
614            {
615                size_t l = 0;
616                const char *m  = luaL_checklstring(L, 1, &l);
617                memcpy(mask, m, l > size ? size : l);               
618            }
619            break;
620        default:
621            break;
622    }
623    for (int i = 0; i < size; i++) {
624        mask[i] = (unsigned char) mapping[(unsigned char) content[i]];
625    }
626    lua_pushlstring(L, mask, size);
627    lua_pushlstring(L, mapping, 256);
628    lmt_memory_free(mask);
629    return 2;
630}
631
632static const struct luaL_Reg pngdecodelib_function_list[] = {
633    { "applyfilter", pnglib_applyfilter },
634    { "splitmask",   pnglib_splitmask   },
635    { "interlace",   pnglib_interlace   },
636    { "expand",      pnglib_expand      },
637    { "tocmyk",      pnglib_tocmyk      },
638    { "tomask",      pnglib_tomask      },
639    { "makemask",    pnglib_makemask    },
640    { NULL,          NULL               },
641};
642
643int luaopen_pngdecode(lua_State *L)
644{
645    lua_newtable(L);
646    luaL_setfuncs(L, pngdecodelib_function_list, 0);
647    return 1;
648}
649
650/*tex This is a placeholder! */
651
652static const struct luaL_Reg pdfdecodelib_function_list[] = {
653    { NULL, NULL }
654};
655
656int luaopen_pdfdecode(lua_State *L)
657{
658    lua_newtable(L);
659    luaL_setfuncs(L, pdfdecodelib_function_list, 0);
660    return 1;
661}
662