ref: b9a42023aab3946ae6e1625cc430af8842c4fdf3
parent: ddfbe7c4de63f8a47d69974e1b6262cc78f7bb16
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Thu Oct 31 23:38:18 EDT 2024
sixel: scaling; fix hls → rgb conversion
--- a/flisp.h
+++ b/flisp.h
@@ -224,6 +224,7 @@
symbol_t *tosymbol(value_t v);
fixnum_t tofixnum(value_t v);
char *tostring(value_t v);
+double todouble(value_t a);
/* conses */
extern value_t the_empty_vector;
--- a/sixel.c
+++ b/sixel.c
@@ -15,10 +15,15 @@
sixel_dither_t *dither;
ios_t *io;
int numcolors;
+ int scalex;
+ int scaley;
+ uint8_t *buf;
+ int bufsz;
};
static value_t fsosym;
-fltype_t *fsotype;
+static fltype_t *fsotype;
+static sixel_allocator_t *salloc;
static int
issixeloutput(value_t v)
@@ -51,32 +56,36 @@
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);
+ SIXELSTATUS r = sixel_output_new(&f->out, fso_write, f, salloc);
if(SIXEL_FAILED(r))
lerrorf(IOError, "could not create sixel output");
- r = sixel_dither_new(&f->dither, numcolors, nil);
+ r = sixel_dither_new(&f->dither, numcolors, salloc);
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);
+ sixel_dither_set_transparent(f->dither, 0xff);
f->numcolors = numcolors;
+ f->scalex = f->scaley = 1;
return v;
}
-static float
-h2rgb(float p, float q, float t)
+BUILTIN("sixel-set-scale", fsixel_set_scale)
{
- 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;
+ 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 = toulong(args[1]);
+ int scaley = scalex;
+ if(nargs > 2)
+ scaley = toulong(args[2]);
+ if(scalex < 1 || scalex > 32 || scaley < 1 || scaley > 32)
+ lerrorf(ArgError, "invalid scale factor: %dx%d", scalex, scaley);
+ f->scalex = scalex;
+ f->scaley = scaley;
+ return FL_T;
}
// :: sixel-output -> palette -> [paltype ->] ...
@@ -126,13 +135,25 @@
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 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;
- 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);
+ 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;
}
}
}
@@ -149,9 +170,9 @@
type_error("sixel-output", args[0]);
fso_t *f = value2c(fso_t*, args[0]);
f->io = toiostream(args[1]);
- char *pix;
+ uint8_t *pix;
size_t sz;
- to_sized_ptr(args[2], &pix, &sz);
+ to_sized_ptr(args[2], (char**)&pix, &sz);
size_t w = toulong(args[3]);
size_t h = toulong(args[4]);
if(w <= 0 || w > sz)
@@ -158,9 +179,37 @@
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);
+ 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(ArgError, "scaling out of range");
+ if(f->bufsz < osz){
+ f->buf = LLT_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(IOError, "could not scale image");
+ w = ow;
+ h = oh;
+ pix = f->buf;
+ }else if(f->buf != nil){
+ LLT_FREE(f->buf);
+ f->buf = nil;
+ f->bufsz = 0;
+ }
+ r = sixel_encode(pix, w, h, 0, f->dither, f->out);
if(SIXEL_FAILED(r))
- return FL_F;
+ lerrorf(IOError, "could not encode image");
return FL_T;
}
@@ -177,7 +226,7 @@
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);
+ SIXELSTATUS r = sixel_output_new(&f->out, fso_write, f, salloc);
if(SIXEL_FAILED(r))
lerrorf(IOError, "could not recreate sixel output");
sixel_output_set_palette_type(f->out, SIXEL_PALETTETYPE_RGB);
@@ -189,6 +238,7 @@
fso_t *f = value2c(fso_t*, self);
sixel_dither_destroy(f->dither);
sixel_output_destroy(f->out);
+ LLT_FREE(f->buf);
}
static cvtable_t fso_vtable = {
@@ -203,4 +253,5 @@
{
fsosym = symbol("sixel-output");
fsotype = define_opaque_type(fsosym, sizeof(fso_t), &fso_vtable, nil);
+ sixel_allocator_new(&salloc, malloc, calloc, realloc, free);
}