ref: 765e2086b63cf705bfddb85af870dec02ee71860
dir: /qoisend.c/
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <tos.h>
enum {
Opind = 0x00,
Opdif = 0x40,
Oplum = 0x80,
Oprun = 0xc0,
Oprgb = 0xfe,
};
#define Nsec 1000000000ULL
#define Nmsec 1000000ULL
typedef struct Img Img;
typedef union Pix Pix;
struct Img {
int w;
int h;
u8int bgrx[];
};
union Pix {
struct {
u8int r, g, b, a;
};
u32int v;
};
static Channel *frame, *done;
int mainstacksize = 8192;
static uvlong
nanosec(void)
{
static uvlong fasthz, xstart;
uvlong x, div;
if(fasthz == ~0ULL)
return nsec() - xstart;
if(fasthz == 0){
if(_tos->cyclefreq){
cycles(&xstart);
fasthz = _tos->cyclefreq;
} else {
xstart = nsec();
fasthz = ~0ULL;
fprint(2, "cyclefreq not available, falling back to nsec()\n");
fprint(2, "you might want to disable aux/timesync\n");
return 0;
}
}
cycles(&x);
x -= xstart;
/* this is ugly */
for(div = Nsec; x < 0x1999999999999999ULL && div > 1 ; div /= 10ULL, x *= 10ULL);
return x / (fasthz / div);
}
static void
nsleep(uvlong ns)
{
uvlong start, end;
start = nanosec();
end = start + ns;
ns = start;
do{
if(end - ns > 85*Nmsec)
sleep(80);
else if (end - ns > 25*Nmsec)
sleep(20);
else if (end - ns > 1*Nmsec)
sleep(1);
else
break;
ns = nanosec();
}while(ns < end);
}
static Img *
imgread(int in)
{
char tmp[61], *f[5];
int r, n, e, w, h;
Img *i;
tmp[60] = 0;
if(pread(in, tmp, 60, 0) != 60 || tokenize(tmp, f, 5) != 5)
sysfatal("invalid image");
if(strcmp(f[0]+1, "8r8g8b8") != 0)
sysfatal("only [ax]8r8g8b8 is supported");
w = atoi(f[3]) - atoi(f[1]);
h = atoi(f[4]) - atoi(f[2]);
e = w*h*4;
i = malloc(sizeof(*i) + e);
i->w = w;
i->h = h;
for(n = 0; n < e; n += r){
if((r = pread(in, i->bgrx+n, e-n, n+5*12)) <= 0){
free(i);
return nil;
}
}
return i;
}
static void
encthread(void *)
{
int i, j, bsz, run, indpos;
Pix p[64], pix, prevpix;
Img *img, *prev;
u8int *b, *x;
threadsetname("qoi/encthread");
prev = nil;
b = nil;
bsz = 0;
for(;;){
if(recv(frame, &img) < 0)
break;
if(prev != nil && memcmp(img->bgrx, prev->bgrx, img->w*img->h*4) == 0){
free(img);
continue;
}else if(bsz < (i = 14+img->w*img->h*4)){
b = realloc(b, bsz = i); /* a lot of extra for the worst case */
if(b == nil)
sysfatal("memory");
b[0] = 'q';
b[1] = 'o';
b[2] = 'i';
b[3] = 'f';
b[12] = 3;
b[13] = 0;
}
b[4] = img->w>>24;
b[5] = img->w>>16;
b[6] = img->w>>8;
b[7] = img->w;
b[8] = img->h>>24;
b[9] = img->h>>16;
b[10] = img->h>>8;
b[11] = img->h;
memset(p, 0, sizeof(p));
memset(&prevpix, 0, sizeof(0));
prevpix.a = 255;
x = img->bgrx;
run = 0;
j = 14;
for(i = 0; i < img->w*img->h; i++){
pix.b = *x++;
pix.g = *x++;
pix.r = *x++;
x++;
if(pix.v == prevpix.v){
run++;
if(run == 62 || i+1 == img->w*img->h){
b[j++] = Oprun | (run-1);
run = 0;
}
}else{
if(run > 0){
b[j++] = Oprun | (run-1);
run = 0;
}
indpos = (pix.r*3 + pix.g*5 + pix.b*7 + 255*11) % 64;
if(pix.v == p[indpos].v){
b[j++] = Opind | indpos;
}else{
signed char Δr, Δg, Δb, Δgr, Δgb;
p[indpos].v = pix.v;
Δr = pix.r - prevpix.r;
Δg = pix.g - prevpix.g;
Δb = pix.b - prevpix.b;
Δgr = Δr - Δg;
Δgb = Δb - Δg;
if(Δr > -3 && Δr < 2 && Δg > -3 && Δg < 2 && Δb > -3 && Δb < 2){
b[j++] = Opdif | (Δr + 2)<<4 | (Δg + 2)<<2 | (Δb + 2);
}else if(Δgr > -9 && Δgr < 8 && Δg > -33 && Δg < 32 && Δgb > -9 && Δgb < 8){
b[j++] = Oplum | (Δg + 32);
b[j++] = (Δgr + 8)<<4 | (Δgb + 8);
}else{
b[j++] = Oprgb;
b[j++] = pix.r;
b[j++] = pix.g;
b[j++] = pix.b;
}
}
}
prevpix.v = pix.v;
}
/* padding */
for(i = 0; i < 7; i++)
b[j++] = 0;
b[j++] = 1;
if(write(1, b, j) != j)
break;
free(prev);
prev = img;
}
sendul(done, 0);
threadexits(nil);
}
static void
usage(void)
{
fprint(2, "usage: %s [-f FPS] FILE\n", argv0);
threadexitsall("usage");
}
void
threadmain(int argc, char **argv)
{
uvlong fstart, fend, f₀, nframes;
int fps, in, one, debug;
Img *img;
one = 0;
debug = 0;
fps = 30;
ARGBEGIN{
case '1':
one++;
break;
case 'd':
debug++;
break;
case 'f':
fps = atoi(EARGF(usage()));
break;
default:
usage();
}ARGEND
if(argc != 1)
usage();
if((in = open(*argv, OREAD)) < 0)
sysfatal("input: %r");
frame = chancreate(sizeof(void*), 1);
done = chancreate(sizeof(ulong), 1);
proccreate(encthread, nil, mainstacksize);
f₀ = nanosec();
for(nframes = 0;;){
fstart = nanosec();
while((img = imgread(in)) == nil);
if(sendp(frame, img) != 1)
break;
if(one){
chanclose(frame);
recvul(done);
}
fend = nanosec();
nframes++;
if(debug && nframes > 0 && (nframes % fps) == 0){
fprint(2, "avg fps: %llud\n", nframes/((fend - f₀)/Nsec));
f₀ = nanosec();
nframes = -1;
}
if(Nsec/fps > (fend - fstart))
nsleep(Nsec/fps - (fend - fstart));
}
threadexitsall(nil);
}