shithub: femtolisp

Download patch

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);
 }