ref: f4a3042aada0c8012de84907baae3ab193032a43
dir: /main.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 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*Nsec) sleep(80); else if (end - ns > 25*Nsec) sleep(20); else if (end - ns > 1*Nsec) sleep(1); else break; ns = nanosec(); }while(ns < end); } static Img * imgread(int f, int w, int h) { int r, n, e; Img *i; 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(f, i->bgrx+n, e-n, n+5*12)) <= 0){ free(i); return nil; } } return i; } static void encthread(void *) { Pix p[64], pix, prevpix; int i, j, run, indpos; Img *img, *prev; u8int *b, *x; threadsetname("qoi/encthread"); memset(p, 0, sizeof(p)); memset(&prevpix, 0, sizeof(0)); prevpix.a = 255; prev = nil; b = nil; 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(prev == nil){ b = malloc(14 + img->w*img->h*4); /* a lot of extra for the worst case */ b[0] = 'q'; b[1] = 'o'; b[2] = 'i'; b[3] = 'f'; 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; b[12] = 3; b[13] = 0; } 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, w, h, one, debug; char tmp[61], *f[5]; 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"); tmp[60] = 0; if(readn(in, tmp, 60) != 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]); frame = chancreate(sizeof(void*), 1); done = chancreate(sizeof(ulong), 1); proccreate(encthread, nil, mainstacksize); f₀ = nanosec(); for(nframes = 0;; nframes++){ fstart = nanosec(); if((img = imgread(in, w, h)) == nil) break; if(sendp(frame, img) != 1) break; if(one){ chanclose(frame); recvul(done); } fend = nanosec(); 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); }