ref: 603e53b509dd59e5dc1e1a1f1f9a31197e4bc655
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);
}