ref: 22ae0e366f8678ac2df9104ba37c2e2ab9c0190d
dir: /sixel.c/
#include <sixel.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; int scalex; int scaley; uint8_t *buf; int bufsz; }; static sixel_allocator_t *salloc; static int issixeloutput(value_t v) { return iscvalue(v) && cv_class(ptr(v)) == FL(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 = tosize(args[0]); } if(numcolors < 1 || numcolors > 256) lerrorf(FL(ArgError), "invalid number of colors: %d", numcolors); value_t v = cvalue(FL(fsotype), sizeof(fso_t)); fso_t *f = value2c(fso_t*, v); if(salloc == nil) sixel_allocator_new(&salloc, malloc, calloc, realloc, free); SIXELSTATUS r = sixel_output_new(&f->out, fso_write, f, salloc); if(SIXEL_FAILED(r)) lerrorf(FL(IOError), "could not create sixel output"); r = sixel_dither_new(&f->dither, numcolors, salloc); if(SIXEL_FAILED(r)) lerrorf(FL(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); sixel_dither_set_transparent(f->dither, 0xff); f->numcolors = numcolors; f->scalex = f->scaley = 1; f->buf = nil; f->bufsz = 0; return v; } BUILTIN("sixel-set-scale", fsixel_set_scale) { 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]); int scalex = tosize(args[1]); int scaley = scalex; if(nargs > 2) scaley = tosize(args[2]); if(scalex < 1 || scalex > 32 || scaley < 1 || scaley > 32) lerrorf(FL(ArgError), "invalid scale factor: %dx%d", scalex, scaley); f->scalex = scalex; f->scaley = scaley; return FL(t); } // :: 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(FL(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(FL(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 == FL(uint8sym) || eltype->type == FL(bytesym)) memcpy(out, cptr(args[1]), f->numcolors*3); else lerrorf(FL(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) / 360.0; float l = (float)conv_to_int32(pal+(i*3+1)*elsize, nt) / 100.0; int k = h*6; float f = h*6 - k; float p = l*(1-s); float q = l*(1-f*s); float t = l*(1-(1-f)*s); float r, g, b; switch(k % 6){ case 0: r=l; g=t; b=p; break; case 1: r=q; g=l; b=p; break; case 2: r=p; g=l; b=t; break; case 3: r=p; g=q; b=l; break; case 4: r=t; g=p; b=l; break; default: r=l; g=p; b=q; break; } out[i*3+0] = 255*r; out[i*3+1] = 255*g; out[i*3+2] = 255*b; } } } 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]); uint8_t *pix; size_t sz; to_sized_ptr(args[2], &pix, &sz); size_t w = tosize(args[3]); size_t h = tosize(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; if(f->scalex > 1 || f->scaley > 1){ int ow = w * f->scalex, oh = h * f->scaley, osz = ow*oh; if(ow < 1 || oh < 1 || osz < ow || osz < oh) lerrorf(FL(ArgError), "scaling out of range"); if(f->bufsz < osz){ f->buf = MEM_REALLOC(f->buf, osz); f->bufsz = osz; } r = sixel_helper_scale_image( f->buf, pix, w, h, SIXEL_PIXELFORMAT_PAL8, ow, oh, SIXEL_RES_NEAREST, salloc ); if(SIXEL_FAILED(r)) lerrorf(FL(IOError), "could not scale image"); w = ow; h = oh; pix = f->buf; }else if(f->buf != nil){ MEM_FREE(f->buf); f->buf = nil; f->bufsz = 0; } r = sixel_encode(pix, w, h, 0, f->dither, f->out); if(SIXEL_FAILED(r)) lerrorf(FL(IOError), "could not encode image"); 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 = cvalue_data(oldv); fso_t *f = cvalue_data(newv); sixel_output_destroy(oldf->out); SIXELSTATUS r = sixel_output_new(&f->out, fso_write, f, salloc); if(SIXEL_FAILED(r)) lerrorf(FL(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); MEM_FREE(f->buf); } static cvtable_t fso_vtable = { print_sixeloutput, relocate_sixeloutput, free_sixeloutput, nil }; void fsixel_init(void) { FL(fsosym) = symbol("sixel-output", false); FL(fsotype) = define_opaque_type(FL(fsosym), sizeof(fso_t), &fso_vtable, nil); }