ref: c164ffe64f6cff88a8115acf62dd0d9d64581982
dir: /sixel.c/
#include <sixel.h> #include "llt.h" #include "flisp.h" #include "cvalues.h" #include "types.h" #include "iostream.h" #include "print.h" #include "operators.h" #include "fsixel.h" typedef struct fso_t fso_t; struct fso_t { sixel_output_t *out; sixel_dither_t *dither; ios_t *io; int numcolors; }; static value_t fsosym; fltype_t *fsotype; static int issixeloutput(value_t v) { return iscvalue(v) && cv_class(ptr(v)) == fsotype; } BUILTIN("sixel-ouput?", fsixel_outputp) { argcount(nargs, 1); return issixeloutput(args[0]) ? FL_T : FL_F; } static int fso_write(char *data, int size, void *priv) { fso_t *f = priv; return ios_write(f->io, data, size); } // :: num-colors -> ... BUILTIN("sixel-output", fsixel_output) { int numcolors = 256; if(nargs > 0){ argcount(nargs, 1); numcolors = toulong(args[0]); } if(numcolors < 1 || numcolors > 256) lerrorf(ArgError, "invalid number of colors: %d", numcolors); value_t v = cvalue(fsotype, sizeof(fso_t)); fso_t *f = value2c(fso_t*, v); SIXELSTATUS r = sixel_output_new(&f->out, fso_write, f, nil); if(SIXEL_FAILED(r)) lerrorf(IOError, "could not create sixel output"); r = sixel_dither_new(&f->dither, numcolors, nil); if(SIXEL_FAILED(r)) lerrorf(IOError, "could not create sixel dither"); sixel_output_set_palette_type(f->out, SIXEL_PALETTETYPE_RGB); sixel_dither_set_pixelformat(f->dither, SIXEL_PIXELFORMAT_PAL8); f->numcolors = numcolors; return v; } static float h2rgb(float p, float q, float t) { if(t < 0) t++; if(t > 1) t--; if(t >= 2.0/3.0) return p; if(t < 1.0/6.0) return p+(q-p)*6.0*t; if(t < 1.0/2.0) return q; return p+(q-p)*(2.0/3.0-t)*6.0; } // :: sixel-output -> palette -> [paltype ->] ... BUILTIN("sixel-set-palette", fsixel_set_palette) { if(nargs < 2 || nargs > 3) argcount(nargs, 2); if(!issixeloutput(args[0])) type_error("sixel-output", args[0]); fso_t *f = value2c(fso_t*, args[0]); bool isrgb = true; size_t len; if(nargs >= 3){ if(!fl_isstring(args[2])) type_error("string", args[2]); len = cv_len((cvalue_t*)ptr(args[2])); char *s = cvalue_data(args[2]); if(len == 3 && strncmp(s, "rgb", 3) == 0) isrgb = true; else if(len == 3 && strncmp(s, "hls", 3) == 0) isrgb = false; else lerrorf(ArgError, "invalid palette type (must be either \"rgb\" or \"hls\")"); } if(!isarray(args[1])) type_error("array", args[1]); len = cvalue_arraylen(args[1]); if(f->numcolors*3 != (int)len) lerrorf(ArgError, "invalid palette: expected %d colors, got %d", f->numcolors, (int)len); fltype_t *type = cv_class(ptr(args[1])); size_t elsize = type->elsz; fltype_t *eltype = type->eltype; numerictype_t nt = eltype->numtype; uint8_t out[256*3] = {0}; if(isrgb){ if(eltype->type == uint8sym || eltype->type == bytesym) memcpy(out, cptr(args[1]), f->numcolors*3); else lerrorf(ArgError, "invalid palette type: expected bytes"); }else{ uint8_t *pal = cptr(args[1]); for(int i = 0; i < f->numcolors; i++){ float s = (float)conv_to_int32(pal+(i*3+2)*elsize, nt) / 100.0; if(s == 0) out[i*3+0] = out[i*3+1] = out[i*3+2] = 255*(int)conv_to_int32(pal+(i*3+1)*elsize, nt)/100; else{ float h = (float)conv_to_int32(pal+(i*3+0)*elsize, nt) / 100.0; float l = (float)conv_to_int32(pal+(i*3+1)*elsize, nt) / 100.0; float q = l < 0.5 ? l*(s+1) : l+s-l*s; float p = 2*l - q; out[i*3+0] = 255*h2rgb(p, q, h+1.0/3.0); out[i*3+1] = 255*h2rgb(p, q, h+0.0/3.0); out[i*3+2] = 255*h2rgb(p, q, h-1.0/3.0); } } } sixel_dither_set_palette(f->dither, out); return FL_T; } // :: sixel-output -> iostream -> pixels -> width -> height -> ... BUILTIN("sixel-encode", fsixel_encode) { argcount(nargs, 5); if(!issixeloutput(args[0])) type_error("sixel-output", args[0]); fso_t *f = value2c(fso_t*, args[0]); f->io = toiostream(args[1]); char *pix; size_t sz; to_sized_ptr(args[2], &pix, &sz); size_t w = toulong(args[3]); size_t h = toulong(args[4]); if(w <= 0 || w > sz) bounds_error(args[2], args[3]); if(h <= 0 || w*h > sz) bounds_error(args[2], args[4]); SIXELSTATUS r = sixel_encode((uint8_t*)pix, w, h, 0, f->dither, f->out); if(SIXEL_FAILED(r)) return FL_F; return FL_T; } static void print_sixeloutput(value_t v, ios_t *s) { USED(v); fl_print_str("#<sixel output>", s); } static void relocate_sixeloutput(value_t oldv, value_t newv) { fso_t *oldf = cv_data(ptr(oldv)); fso_t *f = cv_data(ptr(newv)); sixel_output_destroy(oldf->out); SIXELSTATUS r = sixel_output_new(&f->out, fso_write, f, nil); if(SIXEL_FAILED(r)) lerrorf(IOError, "could not recreate sixel output"); sixel_output_set_palette_type(f->out, SIXEL_PALETTETYPE_RGB); } static void free_sixeloutput(value_t self) { fso_t *f = value2c(fso_t*, self); sixel_dither_destroy(f->dither); sixel_output_destroy(f->out); } static cvtable_t fso_vtable = { print_sixeloutput, relocate_sixeloutput, free_sixeloutput, nil }; void fsixel_init(void) { fsosym = symbol("sixel-output"); fsotype = define_opaque_type(fsosym, sizeof(fso_t), &fso_vtable, nil); }