everybody's movin', everybody's groovin', baby #1977

This commit is contained in:
nick black 2021-07-23 05:27:37 -04:00 committed by nick black
parent 625564b4d9
commit 19797b3859
12 changed files with 343 additions and 203 deletions

View File

@ -6,6 +6,7 @@ extern "C" {
#endif
#include <time.h>
#include <stdint.h>
#define NANOSECS_IN_SEC 1000000000ul

View File

@ -496,13 +496,6 @@ ncdirect_dump_plane(ncdirect* n, const ncplane* np, int xoff){
int dimy, dimx;
ncplane_dim_yx(np, &dimy, &dimx);
if(np->sprite){
if(np->sprite->mstreamfp){
FILE* fp = np->sprite->mstreamfp;
np->sprite->mstreamfp = NULL;
if(fclose(fp) == EOF){
return -1;
}
}
if(xoff){
// doing an x-move without specifying the y coordinate requires asking
// the terminal where the cursor currently is.

View File

@ -7,6 +7,7 @@ extern "C" {
#include "version.h"
#include "builddef.h"
#include "compat/compat.h"
#include <term.h>
#include <ncurses.h>
@ -27,7 +28,6 @@ extern "C" {
#include <langinfo.h>
#endif
#include "notcurses/notcurses.h"
#include "compat/compat.h"
#include "termdesc.h"
#include "egcpool.h"
#include "sprite.h"
@ -702,7 +702,7 @@ void sprixel_hide(sprixel* s);
sprixel* sprixel_alloc(const tinfo* ti, ncplane* n, int dimy, int dimx);
sprixel* sprixel_recycle(ncplane* n);
// takes ownership of s on success.
int sprixel_load(sprixel* spx, char* s, int bytes, int pixy, int pixx, int parse_start);
int sprixel_load(sprixel* spx, fbuf* f, int pixy, int pixx, int parse_start);
int sprite_init(const tinfo* t, int fd);
int sprite_clear_all(const tinfo* t, FILE* fp);
// these three all use absolute coordinates
@ -1438,9 +1438,9 @@ egc_rtl(const char* egc, int* bytes){
// a sprixel occupies the entirety of its associated plane, usually an entirely
// new, purpose-specific plane. |leny| and |lenx| are output geometry in pixels.
static inline int
plane_blit_sixel(sprixel* spx, char* s, int bytes, int leny, int lenx,
plane_blit_sixel(sprixel* spx, fbuf* f, int leny, int lenx,
int parse_start, tament* tam){
if(sprixel_load(spx, s, bytes, leny, lenx, parse_start)){
if(sprixel_load(spx, f, leny, lenx, parse_start)){
return -1;
}
ncplane* n = spx->n;

View File

@ -5,6 +5,7 @@
#include "internal.h"
#include "termdesc.h"
#include "sprite.h"
#include "fbuf.h"
#include "png.h"
// yank a cell out of the PNG by setting all of its alphas to 0. the alphas
@ -33,40 +34,25 @@ int iterm_draw(const tinfo* ti, const ncpile *p, sprixel* s, FILE* out, int y, i
return -1;
}
}
if(fwrite(s->glyph, s->glyphlen, 1, out) != 1){
if(fwrite(s->glyph.buf, s->glyph.used, 1, out) != 1){
return -1;
}
return s->glyphlen;
return s->glyph.used;
}
static int
write_iterm_graphic(sprixel* s, const void* data, int leny, int stride, int lenx){
s->glyph = NULL;
FILE* fp = open_memstream(&s->glyph, &s->glyphlen);
if(fp == NULL){
write_iterm_graphic(const void* data, int leny, int stride, int lenx, fbuf *f){
if(fbuf_puts(f, "\e]1337;File=inline=1:") < 0){
return -1;
}
if(ncfputs("\e]1337;File=inline=1:", fp) == EOF){
goto err;
}
// FIXME won't we need to pass TAM into write_png_b64()?
if(write_png_b64(data, leny, stride, lenx, fp)){
goto err;
if(write_png_b64(data, leny, stride, lenx, f)){
return -1;
}
if(ncfputs("\e\\", fp) == EOF){
goto err;
}
if(fclose(fp) == EOF){
logerror("Error flushing %zuB (%s)\n", s->glyphlen, strerror(errno));
free(s->glyph);
if(fbuf_puts(f, "\x1b\\") < 0){
return -1;
}
return 0;
err:
fclose(fp);
// s->glyph is freed by caller
return -1;
}
// create an iterm2 control sequence complete with base64-encoded PNG.
@ -89,16 +75,28 @@ int iterm_blit(ncplane* n, int linesize, const void* data,
if(!reuse){
tam = malloc(sizeof(*tam) * rows * cols);
if(tam == NULL){
goto error;
return -1;
}
memset(tam, 0, sizeof(*tam) * rows * cols);
}
if(write_iterm_graphic(s, data, leny, linesize, lenx)){
goto error;
if(fbuf_init(&s->glyph)){
free(tam);
return -1;
}
if(write_iterm_graphic(data, leny, linesize, lenx, &s->glyph)){
if(!reuse){
free(tam);
}
fbuf_free(&s->glyph);
return -1;
}
scrub_tam_boundaries(tam, leny, lenx, s->cellpxy, s->cellpxx);
if(plane_blit_sixel(s, s->glyph, s->glyphlen, leny, lenx, parse_start, tam) < 0){
goto error;
if(plane_blit_sixel(s, &s->glyph, leny, lenx, parse_start, tam) < 0){
if(!reuse){
free(tam);
}
fbuf_free(&s->glyph);
return -1;
}
return 1;

View File

@ -215,16 +215,13 @@ kitty_restore(char* triplet, int skip, int max, int pleft,
// base. we're blowing away the glyph.
static int
init_sprixel_animation(sprixel* s){
if(s->mstreamfp){
if(s->glyph.buf){
return 0;
}
free(s->glyph);
s->glyph = NULL;
s->glyphlen = 0;
if((s->mstreamfp = open_memstream(&s->glyph, &s->glyphlen)) == NULL){
loginfo("Opened animation buffer for sprixel %u\n", s->id);
if(fbuf_init(&s->glyph)){
return -1;
}
loginfo("Opened animation buffer for sprixel %u\n", s->id);
return 0;
}
@ -243,7 +240,7 @@ int kitty_rebuild(sprixel* s, int ycell, int xcell, uint8_t* auxvec){
if((ycell + 1) * ypixels > s->pixy){
targy = s->pixy - ycell * ypixels;
}
char* c = s->glyph + s->parse_start;
char* c = s->glyph.buf + s->parse_start;
int nextpixel = (s->pixx * ycell * ypixels) + (xpixels * xcell);
int thisrow = targx;
int chunkedhandled = 0;
@ -356,26 +353,61 @@ int kitty_wipe_animation(sprixel* s, int ycell, int xcell){
return -1;
}
logdebug("Wiping sprixel %u at %d/%d\n", s->id, ycell, xcell);
FILE* fp = s->mstreamfp;
fprintf(fp, "\e_Ga=f,x=%d,y=%d,s=%d,v=%d,i=%d,X=1,r=1,q=2;",
xcell * s->cellpxx,
ycell * s->cellpxy,
s->cellpxx,
s->cellpxy,
s->id);
fbuf* f = &s->glyph;
if(fbuf_puts(f, "\x1b_Ga=f,x=") < 0){
return -1;
}
if(fbuf_putint(f, xcell * s->cellpxx) < 0){
return -1;
}
if(fbuf_puts(f, ",y=") < 0){
return -1;
}
if(fbuf_putint(f, ycell * s->cellpxy)){
return -1;
}
if(fbuf_puts(f, ",s=") < 0){
return -1;
}
if(fbuf_putint(f, s->cellpxx)){
return -1;
}
if(fbuf_puts(f, ",v=") < 0){
return -1;
}
if(fbuf_putint(f, s->cellpxy)){
return -1;
}
if(fbuf_puts(f, ",i=") < 0){
return -1;
}
if(fbuf_putint(f, s->id)){
return -1;
}
if(fbuf_puts(f, ",X=1,r=1,q=2;") < 0){
return -1;
}
// FIXME ought be smaller around the fringes!
int totalp = s->cellpxy * s->cellpxx;
// FIXME preserve so long as cellpixel geom stays constant?
for(int p = 0 ; p + 3 <= totalp ; p += 3){
ncfputs("AAAAAAAAAAAAAAAA", fp);
if(fbuf_puts(f, "AAAAAAAAAAAAAAAA") < 0){
return -1;
}
}
if(totalp % 3 == 1){
ncfputs("AAAAAA==", fp);
if(fbuf_puts(f, "AAAAAA==") < 0){
return -1;
}
}else if(totalp % 3 == 2){
ncfputs("AAAAAAAAAAA=", fp);
if(fbuf_puts(f, "AAAAAAAAAAA=") < 0){
return -1;
}
}
// FIXME need chunking for cells of 768+ pixels
ncfputs("\e\\", fp);
if(fbuf_putn(f, "\x1b\\", 2) != 2){
return -1;
}
s->invalidated = SPRIXEL_INVALIDATED;
return 1;
}
@ -438,7 +470,7 @@ int kitty_wipe(sprixel* s, int ycell, int xcell){
if((ycell + 1) * ypixels > s->pixy){
targy = s->pixy - ycell * ypixels;
}
char* c = s->glyph + s->parse_start;
char* c = s->glyph.buf + s->parse_start;
//fprintf(stderr, "TARGET AREA: %d x %d @ %dx%d of %d/%d (%d/%d) len %zu\n", targy, targx, ycell, xcell, s->dimy, s->dimx, s->pixy, s->pixx, strlen(c));
// every pixel was 4 source bytes, 32 bits, 6.33 base64 bytes. every 3 input pixels is
// 12 bytes (96 bits), an even 16 base64 bytes. there is chunking to worry about. there
@ -495,7 +527,7 @@ int kitty_wipe(sprixel* s, int ycell, int xcell){
c += RGBA_MAXLEN * 4 * 4 / 3; // 4bpp * 4/3 for base64, 4096b per chunk
c += 8; // new chunk header
++chunkedhandled;
//fprintf(stderr, "LOOKING NOW AT %u [%s]\n", c - s->glyph, c);
fprintf(stderr, "LOOKING NOW AT %ld [%s]\n", c - s->glyph.buf, c);
while(*c != ';'){
++c;
}
@ -529,54 +561,70 @@ zctx_origbuf(z_stream* zctx, int pixy, int pixx){
}
static int
encode_and_chunkify(FILE* fp, z_stream* zctx, int pixy, int pixx){
encode_and_chunkify(fbuf* f, z_stream* zctx, int pixy, int pixx){
unsigned long totw = zctx->total_out;
unsigned char* buf = zctx_origbuf(zctx, pixy, pixx);
// need to terminate the header, requiring semicolon
fprintf(fp, "%s;", totw > 4096 * 3 / 4 ? ",m=1" : "");
if(fbuf_printf(f, "%s;", totw > 4096 * 3 / 4 ? ",m=1" : "") < 0){
return -1;
}
bool first = true;
unsigned long i = 0;
char b64d[4];
while(totw - i > 4096 * 3 / 4){
if(!first){
ncfputs("\e_Gm=1;", fp); // successor chunk
if(fbuf_putn(f, "\x1b_Gm=1;", 7) < 0){
return -1; // successor chunk
}
}
unsigned long max = i + 4096 * 3 / 4;
while(i < max){
base64x3(buf + i, b64d);
fwrite(b64d, 4, 1, fp);
if(fbuf_putn(f, b64d, 4) < 0){
return -1;
}
i += 3;
}
first = false;
ncfputs("\e\\", fp);
if(fbuf_putn(f, "\x1b\\", 2) < 0){
return -1;
}
}
if(!first){
ncfputs("\e_Gm=0;", fp);
if(fbuf_putn(f, "\x1b_Gm=0;", 7) < 0){
return -1; // successor chunk
}
}
while(i < totw){
if(totw - i < 3){
base64final(buf + i, b64d, totw - i);
fwrite(b64d, 4, 1, fp);
if(fbuf_putn(f, b64d, 4) < 0){
return -1;
}
i += totw - i;
}else{
base64x3(buf + i, b64d);
fwrite(b64d, 4, 1, fp);
if(fbuf_putn(f, b64d, 4) < 0){
return -1;
}
i += 3;
}
}
ncfputs("\e\\", fp);
if(fbuf_putn(f, "\x1b\\", 2) < 0){
return -1;
}
return 0;
}
static int
finalize_deflator(z_stream* zctx, FILE* fp, int dimy, int dimx){
finalize_deflator(z_stream* zctx, fbuf* f, int dimy, int dimx){
assert(0 == zctx->avail_in);
int zret = deflate(zctx, Z_FINISH);
if(zret != Z_STREAM_END){
logerror("Error deflating (%d)\n", zret);
return -1;
}
if(encode_and_chunkify(fp, zctx, dimy, dimx)){
if(encode_and_chunkify(f, zctx, dimy, dimx)){
return -1;
}
return 0;
@ -655,7 +703,7 @@ destroy_deflator(unsigned animated, z_stream* zctx, int pixy, int pixx){
// 16 base64-encoded bytes. 4096 / 16 == 256 3-pixel groups, or 768 pixels.
// closes |fp| on all paths.
static int
write_kitty_data(FILE* fp, int linesize, int leny, int lenx, int cols,
write_kitty_data(fbuf* f, int linesize, int leny, int lenx, int cols,
const uint32_t* data, const blitterargs* bargs,
tament* tam, int* parse_start, kitty_graphics_e level){
//fprintf(stderr, "drawing kitty %p\n", tam);
@ -692,7 +740,7 @@ write_kitty_data(FILE* fp, int linesize, int leny, int lenx, int cols,
// older versions of kitty will delete uploaded images when scrolling,
// alas. see https://github.com/dankamongmen/notcurses/issues/1910 =[.
// parse_start isn't used in animation mode, so no worries there.
*parse_start = fprintf(fp, "\e_Gf=32,s=%d,v=%d,i=%d,p=1,a=t,%s",
*parse_start = fbuf_printf(f, "\e_Gf=32,s=%d,v=%d,i=%d,p=1,a=t,%s",
lenx, leny, sprixelid,
animated ? "o=z,q=2" : chunks ? "m=1;" : "q=2;");
// so if we're animated, we've printed q=2, but no semicolon to close
@ -702,7 +750,9 @@ write_kitty_data(FILE* fp, int linesize, int leny, int lenx, int cols,
// handled following deflate.
}else{
if(!animated){
fprintf(fp, "\e_G%sm=%d;", chunks ? "" : "q=2,", chunks ? 1 : 0);
if(fbuf_printf(f, "\e_G%sm=%d;", chunks ? "" : "q=2,", chunks ? 1 : 0) < 0){
return -1;
}
}
}
if((targetout += RGBA_MAXLEN) > total){
@ -798,15 +848,19 @@ write_kitty_data(FILE* fp, int linesize, int leny, int lenx, int cols,
// we already took transcolor to alpha 0; there's no need to
// check it again, so pass 0.
base64_rgba3(source, encodeable, out, wipe, 0);
ncfputs(out, fp);
if(fbuf_puts(f, out) < 0){
return -1;
}
}
}
if(!animated){
ncfputs("\e\\", fp);
if(fbuf_putn(f, "\x1b\\", 2) < 0){
return -1;
}
}
}
if(animated){
if(finalize_deflator(&zctx, fp, leny, lenx)){
if(finalize_deflator(&zctx, f, leny, lenx)){
goto err;
}
}
@ -846,8 +900,8 @@ int kitty_rebuild_animation(sprixel* s, int ycell, int xcell, uint8_t* auxvec){
if(init_sprixel_animation(s)){
return -1;
}
FILE* fp = s->mstreamfp;
logdebug("rebuilding %u at %d/%d\n", s->id, ycell, xcell);
fbuf* f = &s->glyph;
logdebug("rebuilding sprixel %u %d at %d/%d\n", s->id, s->invalidated, ycell, xcell);
const int ystart = ycell * s->cellpxy;
const int xstart = xcell * s->cellpxx;
const int xlen = xstart + s->cellpxx > s->pixx ? s->pixx - xstart : s->cellpxx;
@ -865,11 +919,29 @@ int kitty_rebuild_animation(sprixel* s, int ycell, int xcell, uint8_t* auxvec){
logdebug("Placing %d/%d at %d/%d\n", ylen, xlen, ycell * s->cellpxy, xcell * s->cellpxx);
while(chunks--){
if(totalout == 0){
fprintf(fp, "\e_Ga=f,x=%d,y=%d,s=%d,v=%d,i=%d,X=1,r=1,%s;",
if(fbuf_printf(f, "\e_Ga=f,x=%d,y=%d,s=%d,v=%d,i=%d,X=1,r=1,%s;",
xcell * s->cellpxx, ycell * s->cellpxy, xlen, ylen,
s->id, chunks ? "m=1" : "q=2");
s->id, chunks ? "m=1" : "q=2") < 0){
return -1;
}
}else{
fprintf(fp, "\e_G%sm=%d;", chunks ? "" : "q=2,", chunks ? 1 : 0);
if(fbuf_putn(f, "\x1b_G", 3) < 0){
return -1;
}
if(!chunks){
if(fbuf_putn(f, "q=2,", 4) < 0){
return -1;
}
}
if(fbuf_putn(f, "m=", 2) < 0){
return -1;
}
if(fbuf_putint(f, chunks ? 1 : 0) < 0){
return -1;
}
if(fbuf_putc(f, ';') != 1){
return -1;
}
}
if((targetout += RGBA_MAXLEN) > total){
targetout = total;
@ -909,9 +981,13 @@ int kitty_rebuild_animation(sprixel* s, int ycell, int xcell, uint8_t* auxvec){
totalout += encodeable;
char out[17];
base64_rgba3(source, encodeable, out, wipe, 0);
ncfputs(out, fp);
if(fbuf_puts(f, out) < 0){
return -1;
}
}
if(fbuf_putn(f, "\x1b\\", 2) < 0){
return -1;
}
ncfputs("\e\\", fp);
}
//fprintf(stderr, "EMERGED WITH TAM STATE %d\n", s->n->tam[tyx].state);
s->invalidated = SPRIXEL_INVALIDATED;
@ -960,20 +1036,16 @@ kitty_blit_core(ncplane* n, int linesize, const void* data, int leny, int lenx,
s->mstreamfp = NULL;
}
// take ownership of |buf| and |tam| on success.
if(plane_blit_sixel(s, s->glyph, s->glyphlen, leny, lenx, parse_start, tam) < 0){
if(plane_blit_sixel(s, &s->glyph, leny, lenx, parse_start, tam) < 0){
goto error;
}
return 1;
error:
if(s->mstreamfp){
fclose(s->mstreamfp);
}
if(!reuse){
free(tam);
}
free(s->glyph);
s->glyph = NULL;
fbuf_free(&s->glyph);
return -1;
}
@ -1040,26 +1112,15 @@ int kitty_draw(const tinfo* ti, const ncpile* p, sprixel* s, FILE* out,
(void)p;
(void)y;
(void)x;
bool animated = false;
if(s->mstreamfp){ // active animation
int fret = fclose(s->mstreamfp);
s->mstreamfp = NULL;
if(fret == EOF){
return -1;
}
animated = true;
}
int ret = s->glyphlen;
logdebug("Writing out %zub for %u\n", s->glyphlen, s->id);
int ret = s->glyph.used; // active animation if non-0
if(ret){
if(fwrite(s->glyph, s->glyphlen, 1, out) != 1){
logdebug("Writing out %db for %u\n", ret, s->id);
if(fwrite(s->glyph.buf, s->glyph.used, 1, out) != 1){
ret = -1;
}
}
if(animated){
free(s->glyph);
s->glyph = NULL;
s->glyphlen = 0;
if(s->glyph.used){
fbuf_free(&s->glyph);
s->invalidated = SPRIXEL_LOADED;
}else{
s->invalidated = SPRIXEL_LOADED;

View File

@ -8,7 +8,7 @@ int fbcon_wipe(sprixel* s, int ycell, int xcell){
if(auxvec == NULL){
return -1;
}
char* glyph = s->glyph;
char* glyph = s->glyph.buf;
for(int y = 0 ; y < s->cellpxy ; ++y){
if(ycell * s->cellpxy + y >= s->pixy){
break;
@ -75,9 +75,9 @@ int fbcon_blit(struct ncplane* n, int linesize, const void* data,
sprixel* s = bargs->u.pixel.spx;
int cdimx = s->cellpxx;
int cdimy = s->cellpxy;
s->glyphlen = leny * lenx * 4;
s->glyph = malloc(s->glyphlen);
if(s->glyph == NULL){
size_t flen = leny * lenx * 4;
s->glyph.buf = malloc(flen);
if(s->glyph.buf == NULL){
return -1;
}
tament* tam = NULL;
@ -100,7 +100,7 @@ int fbcon_blit(struct ncplane* n, int linesize, const void* data,
size_t soffset = l * linesize;
const uint8_t* src = (const unsigned char*)data + soffset;
size_t toffset = l * lenx * 4;
char* dst = s->glyph + toffset;
char* dst = (char *)s->glyph.buf + toffset;
for(int c = 0 ; c < lenx ; ++c){
int xcell = c / cdimx;
int tyx = xcell + ycell * bargs->u.pixel.spx->dimx;
@ -142,7 +142,7 @@ int fbcon_blit(struct ncplane* n, int linesize, const void* data,
}
}
scrub_tam_boundaries(tam, leny, lenx, cdimy, cdimx);
if(plane_blit_sixel(s, s->glyph, s->glyphlen, leny, lenx, 0, tam) < 0){
if(plane_blit_sixel(s, &s->glyph, leny, lenx, 0, tam) < 0){
goto error;
}
return 1;
@ -151,7 +151,8 @@ error:
if(!reuse){
free(tam);
}
free(s->glyph);
free(s->glyph.buf);
s->glyph.size = 0;
return -1;
}
@ -167,7 +168,7 @@ int fbcon_draw(const tinfo* ti, const struct ncpile *p, sprixel* s, FILE* out, i
// FIXME pixel size isn't necessarily 4B, line isn't necessarily psize*pixx
size_t offset = ((l + y * ti->cellpixy) * ti->pixx + x * ti->cellpixx) * 4;
uint8_t* tl = ti->linux_fbuffer + offset;
const char* src = s->glyph + (l * s->pixx * 4);
const char* src = (char*)s->glyph.buf + (l * s->pixx * 4);
for(unsigned c = 0 ; c < (unsigned)s->pixx && c < ti->pixx ; ++c){
uint32_t pixel;
memcpy(&pixel, src, 4);

View File

@ -4,6 +4,7 @@
#include "visual-details.h"
#include "internal.h"
#include "base64.h"
#include "fbuf.h"
#include "png.h"
// http://www.libpng.org/pub/png/spec/1.2/PNG-Contents.html
@ -191,7 +192,7 @@ struct b64ctx {
};
static int
fwrite64(const void* src, size_t osize, FILE* fp, struct b64ctx* bctx){
fbuf_putn64(fbuf* f, const void* src, size_t osize, struct b64ctx* bctx){
size_t w = 0;
char b64[4];
if(bctx->srcidx){
@ -205,7 +206,7 @@ fwrite64(const void* src, size_t osize, FILE* fp, struct b64ctx* bctx){
memcpy(bctx->src + bctx->srcidx, src, copy);
base64x3(bctx->src, b64);
bctx->srcidx = 0;
if(fwrite(b64, 4, 1, fp) != 1){
if(fbuf_putn(f, b64, 4) != 4){
return -1;
}
w = copy;
@ -213,7 +214,7 @@ fwrite64(const void* src, size_t osize, FILE* fp, struct b64ctx* bctx){
// the bctx is now guaranteed to be empty
while(w + 3 <= osize){
base64x3((const unsigned char*)src + w, b64);
if(fwrite(b64, 4, 1, fp) != 1){
if(fbuf_putn(f, b64, 4) != 4){
return -1;
}
w += 3;
@ -227,7 +228,7 @@ fwrite64(const void* src, size_t osize, FILE* fp, struct b64ctx* bctx){
}
static size_t
fwrite_idats(FILE* fp, const unsigned char* data, size_t dlen,
fwrite_idats(fbuf* f, const unsigned char* data, size_t dlen,
struct b64ctx* bctx){
static const char ctype[] = "IDAT";
uint32_t written = 0;
@ -238,9 +239,9 @@ fwrite_idats(FILE* fp, const unsigned char* data, size_t dlen,
thischunk = CHUNK_MAX_DATA;
}
uint32_t nclen = htonl(thischunk);
if(fwrite64(&nclen, 4, fp, bctx) != 1 ||
fwrite64(ctype, 4, fp, bctx) != 1 ||
fwrite64(data + dwritten, thischunk, fp, bctx) != 1){
if(fbuf_putn64(f, &nclen, 4, bctx) != 1 ||
fbuf_putn64(f, ctype, 4, bctx) != 1 ||
fbuf_putn64(f, data + dwritten, thischunk, bctx) != 1){
return 0;
}
// FIXME horrible; PoC; do not retain!
@ -251,7 +252,7 @@ memcpy(crcbuf + 8, data + dwritten, thischunk);
// END horribleness
uint32_t crc = chunk_crc(crcbuf);
free(crcbuf); // FIXME well a bit more
if(fwrite64(&crc, 4, fp, bctx) != 1){
if(fbuf_putn64(f, &crc, 4, bctx) != 1){
return 0;
}
dlen -= thischunk;
@ -261,7 +262,7 @@ free(crcbuf); // FIXME well a bit more
return written;
}
int write_png_b64(const void* data, int rows, int rowstride, int cols, FILE* fp){
int write_png_b64(const void* data, int rows, int rowstride, int cols, fbuf* f){
void* deflated;
size_t dlen;
compute_png_size(data, rows, rowstride, cols, &deflated, &dlen);
@ -269,28 +270,28 @@ int write_png_b64(const void* data, int rows, int rowstride, int cols, FILE* fp)
return -1;
}
struct b64ctx bctx = { };
if(fwrite64(PNGHEADER, sizeof(PNGHEADER) - 1, fp, &bctx) != 1){
if(fbuf_putn64(f, PNGHEADER, sizeof(PNGHEADER) - 1, &bctx) < 0){
free(deflated);
return -1;
}
unsigned char ihdr[25];
write_ihdr(rows, cols, ihdr);
if(fwrite64(ihdr, sizeof(ihdr), fp, &bctx) != 1){
if(fbuf_putn64(f, ihdr, sizeof(ihdr), &bctx) != 1){
free(deflated);
return -1;
}
if(fwrite_idats(fp, deflated, dlen, &bctx) == 0){
if(fwrite_idats(f, deflated, dlen, &bctx) == 0){
free(deflated);
return -1;
}
free(deflated);
if(fwrite64(IEND, sizeof(IEND) - 1, fp, &bctx) != 1){
if(fbuf_putn64(f, IEND, sizeof(IEND) - 1, &bctx) != 1){
return -1;
}
if(bctx.srcidx){
char b64[4];
base64final(bctx.src, b64, bctx.srcidx);
if(fwrite(b64, 4, 1, fp) != 1){
if(fbuf_putn(f, b64, 4) < 0){
return -1;
}
}

View File

@ -7,11 +7,11 @@ extern "C" {
#include <stdio.h>
struct fbuf;
struct ncvisual;
// create the PNG, encode it using base64, and write it to |fp|
int write_png_b64(const void* data, int rows, int rowstride, int cols,
FILE* fp);
int write_png_b64(const void* data, int rows, int rowstride, int cols, fbuf* f);
#ifdef __cplusplus
}

View File

@ -1,4 +1,5 @@
#include "internal.h"
#include "fbuf.h"
#define RGBSIZE 3
#define CENTSIZE (RGBSIZE + 1) // size of a color table entry
@ -237,7 +238,7 @@ int sixel_wipe(sprixel* s, int ycell, int xcell){
if(w){
s->wipes_outstanding = true;
}
change_p2(s->glyph, SIXEL_P2_TRANS);
change_p2(s->glyph.buf, SIXEL_P2_TRANS);
s->n->tam[s->dimx * ycell + xcell].auxvector = auxvec;
return 0;
}
@ -535,40 +536,130 @@ refine_color_table(const uint32_t* data, int linesize, int begy, int begx,
// Emit some number of equivalent, subsequent sixels, using sixel RLE. We've
// seen the sixel |crle| for |seenrle| columns in a row. |seenrle| must > 0.
static int
write_rle(int* printed, int color, FILE* fp, int seenrle, unsigned char crle,
write_rle(int* printed, int color, fbuf* f, int seenrle, unsigned char crle,
int* needclosure){
if(!*printed){
fprintf(fp, "%s#%d", *needclosure ? "$" : "", color);
if(*needclosure){
if(fbuf_putc(f, '$') != 1){
return -1;
}
}
if(fbuf_putc(f, '#') != 1){
return -1;
}
if(fbuf_putint(f, color) < 0){
return -1;
}
*printed = 1;
*needclosure = 0;
}
crle += 63;
if(seenrle == 1){
if(fputc(crle, fp) == EOF){
if(seenrle == 2){
if(fbuf_putc(f, crle) != 1){
return -1;
}
}else if(seenrle == 2){
if(fprintf(fp, "%c%c", crle, crle) <= 0){
}else if(seenrle != 1){
if(fbuf_putc(f, '!') != 1){
return -1;
}
}else{
if(fprintf(fp, "!%d%c", seenrle, crle) <= 0){
if(fbuf_putint(f, seenrle) < 0){
return -1;
}
}
if(fbuf_putc(f, crle) != 1){
return -1;
}
return 0;
}
static inline int
write_sixel_intro(fbuf* f, sixel_p2_e p2, int leny, int lenx){
int r = fbuf_puts(f, "\x1bP0;");
if(r < 0){
return -1;
}
int rr = fbuf_putint(f, p2);
if(rr < 0){
return -1;
}
r += rr;
rr = fbuf_puts(f, ";0q\"1;1;");
if(rr < 0){
return -1;
}
r += rr;
rr = fbuf_putint(f, lenx);
if(rr < 0){
return -1;
}
r += rr;
if(fbuf_putc(f, ';') != 1){
return -1;
}
++r;
rr = fbuf_putint(f, leny);
if(rr < 0){
return -1;
}
r += rr;
return r;
}
// write a single color register. rc/gc/bc are on [0..100].
static inline int
write_sixel_creg(fbuf* f, int idx, int rc, int gc, int bc){
int r = 0;
if(fbuf_putc(f, '#') != 1){
return -1;
}
++r;
int rr = fbuf_putint(f, idx);
if(rr < 0){
return -1;
}
r += rr;
rr = fbuf_puts(f, ";2;");
if(rr < 0){
return -1;
}
r += rr;
rr = fbuf_putint(f, rc);
if(rr < 0){
return -1;
}
r += rr;
if(fbuf_putc(f, ';') != 1){
return -1;
}
++r;
rr = fbuf_putint(f, gc);
if(rr < 0){
return -1;
}
r += rr;
if(fbuf_putc(f, ';') != 1){
return -1;
}
++r;
rr = fbuf_putint(f, bc);
if(rr < 0){
return -1;
}
r += rr;
return r;
}
// write the escape which opens a Sixel, plus the palette table. returns the
// number of bytes written, so that this header can be directly copied in
// future reencodings. |leny| and |lenx| are output pixel geometry.
// returns the number of bytes written, so it can be stored at *parse_start.
static int
write_sixel_header(FILE* fp, int leny, int lenx, const sixeltable* stab, sixel_p2_e p2){
write_sixel_header(fbuf* f, int leny, int lenx, const sixeltable* stab, sixel_p2_e p2){
if(leny % 6){
return -1;
}
// Set Raster Attributes - pan/pad=1 (pixel aspect ratio), Ph=lenx, Pv=leny
int r = fprintf(fp, "\eP0;%d;0q\"1;1;%d;%d", p2, lenx, leny);
int r = write_sixel_intro(f, p2, leny, lenx);
if(r < 0){
return -1;
}
@ -580,20 +671,19 @@ write_sixel_header(FILE* fp, int leny, int lenx, const sixeltable* stab, sixel_p
//fprintf(fp, "#%d;2;%u;%u;%u", i, rgb[0], rgb[1], rgb[2]);
// we emit the average of the actual sums rather than the RGB clustering
// point, as it can be (and usually is) much more accurate.
int f = fprintf(fp, "#%d;2;%" PRId64 ";%" PRId64 ";%" PRId64, i,
(stab->deets[idx].sums[0] * 100 / count / 255),
int rr = write_sixel_creg(f, i, (stab->deets[idx].sums[0] * 100 / count / 255),
(stab->deets[idx].sums[1] * 100 / count / 255),
(stab->deets[idx].sums[2] * 100 / count / 255));
if(f < 0){
if(rr < 0){
return -1;
}
r += f;
r += rr;
}
return r;
}
static int
write_sixel_payload(FILE* fp, int lenx, const sixelmap* map){
write_sixel_payload(fbuf* f, int lenx, const sixelmap* map){
int p = 0;
while(p < map->sixelcount){
int needclosure = 0;
@ -609,7 +699,9 @@ write_sixel_payload(FILE* fp, int lenx, const sixelmap* map){
if(map->data[idx * map->sixelcount + m] == crle){
++seenrle;
}else{
write_rle(&printed, i, fp, seenrle, crle, &needclosure);
if(write_rle(&printed, i, f, seenrle, crle, &needclosure)){
return -1;
}
seenrle = 1;
crle = map->data[idx * map->sixelcount + m];
}
@ -619,16 +711,22 @@ write_sixel_payload(FILE* fp, int lenx, const sixelmap* map){
}
}
if(crle){
write_rle(&printed, i, fp, seenrle, crle, &needclosure);
if(write_rle(&printed, i, f, seenrle, crle, &needclosure)){
return -1;
}
}
needclosure = needclosure | printed;
}
if(p + lenx < map->sixelcount){
fputc('-', fp);
if(fbuf_putc(f, '-') != 1){
return -1;
}
}
p += lenx;
}
fprintf(fp, "\e\\");
if(fbuf_puts(f, "\e\\") < 0){
return -1;
}
return 0;
}
@ -637,16 +735,13 @@ write_sixel_payload(FILE* fp, int lenx, const sixelmap* map){
// constant, and is simply copied. fclose()s |fp| on success. |outx| and |outy|
// are output geometry.
static int
write_sixel(FILE* fp, int outy, int outx, const sixeltable* stab,
write_sixel(fbuf* f, int outy, int outx, const sixeltable* stab,
int* parse_start, sixel_p2_e p2){
*parse_start = write_sixel_header(fp, outy, outx, stab, p2);
*parse_start = write_sixel_header(f, outy, outx, stab, p2);
if(*parse_start < 0){
return -1;
}
if(write_sixel_payload(fp, outx, stab->map) < 0){
return -1;
}
if(fclose(fp) == EOF){
if(write_sixel_payload(f, outx, stab->map) < 0){
return -1;
}
return 0;
@ -662,30 +757,21 @@ write_sixel(FILE* fp, int outy, int outx, const sixeltable* stab,
// is redrawn, and annihilated sprixcells still require a glyph to be emitted.
static inline int
sixel_reblit(sprixel* s){
char* buf = NULL;
size_t size = 0;
FILE* fp = open_memstream(&buf, &size);
if(fp == NULL){
fbuf f;
if(fbuf_init(&f)){
return -1;
}
if(fwrite(s->glyph, s->parse_start, 1, fp) != 1){
fclose(fp);
free(buf);
if(fbuf_putn(&f, s->glyph.buf, s->parse_start) != s->parse_start){
fbuf_free(&f);
return -1;
}
if(write_sixel_payload(fp, s->pixx, s->smap) < 0){
fclose(fp);
free(buf);
if(write_sixel_payload(&f, s->pixx, s->smap) < 0){
fbuf_free(&f);
return -1;
}
if(fclose(fp) == EOF){
free(buf);
return -1;
}
free(s->glyph);
fbuf_free(&s->glyph);
// FIXME update P2 if necessary
s->glyph = buf;
s->glyphlen = size;
memcpy(&s->glyph, &f, sizeof(f));
return 0;
}
@ -697,10 +783,8 @@ sixel_reblit(sprixel* s){
// transparent filler input for any missing rows.
static inline int
sixel_blit_inner(int leny, int lenx, sixeltable* stab, sprixel* s, tament* tam){
char* buf = NULL;
size_t size = 0;
FILE* fp = open_memstream(&buf, &size);
if(fp == NULL){
fbuf f;
if(fbuf_init(&f)){
return -1;
}
int parse_start = 0;
@ -710,17 +794,17 @@ sixel_blit_inner(int leny, int lenx, sixeltable* stab, sprixel* s, tament* tam){
stab->p2 = SIXEL_P2_TRANS;
}
// calls fclose() on success
if(write_sixel(fp, outy, lenx, stab, &parse_start, stab->p2)){
fclose(fp);
free(buf);
if(write_sixel(&f, outy, lenx, stab, &parse_start, stab->p2)){
fbuf_free(&f);
return -1;
}
scrub_tam_boundaries(tam, outy, lenx, s->cellpxy, s->cellpxx);
// take ownership of buf on success
if(plane_blit_sixel(s, buf, size, outy, lenx, parse_start, tam) < 0){
free(buf);
if(plane_blit_sixel(s, &f, outy, lenx, parse_start, tam) < 0){
fbuf_free(&f);
return -1;
}
// we're keeping the buf remnants
sixelmap_trim(stab->map);
s->smap = stab->map;
return 1;
@ -858,11 +942,11 @@ int sixel_draw(const tinfo* ti, const ncpile* p, sprixel* s, FILE* out,
}
}
}
if(fwrite(s->glyph, s->glyphlen, 1, out) != 1){
if(fwrite(s->glyph.buf, s->glyph.used, 1, out) != 1){
return -1;
}
s->invalidated = SPRIXEL_QUIESCENT;
return s->glyphlen;
return s->glyph.used;
}
int sixel_init(const tinfo* ti, int fd){

View File

@ -6,7 +6,7 @@ static atomic_uint_fast32_t sprixelid_nonce;
void sprixel_debug(const sprixel* s, FILE* out){
fprintf(out, "Sprixel %d (%p) %zuB %dx%d (%dx%d) @%d/%d state: %d\n",
s->id, s, s->glyphlen, s->dimy, s->dimx, s->pixy, s->pixx,
s->id, s, s->glyph.used, s->dimy, s->dimx, s->pixy, s->pixx,
s->n ? s->n->absy : 0, s->n ? s->n->absx : 0,
s->invalidated);
if(s->n){
@ -49,7 +49,7 @@ void sprixel_free(sprixel* s){
s->n->sprite = NULL;
}
sixelmap_free(s->smap);
free(s->glyph);
fbuf_free(&s->glyph);
free(s);
}
}
@ -151,7 +151,7 @@ sprixel* sprixel_alloc(const tinfo* ti, ncplane* n, int dimy, int dimx){
// |pixy| and |pixx| are the output pixel geometry (i.e. |pixy| must be a
// multiple of 6 for sixel). output coverage ought already have been loaded.
// takes ownership of 's' on success. frees any existing glyph.
int sprixel_load(sprixel* spx, char* s, int bytes, int pixy, int pixx,
int sprixel_load(sprixel* spx, fbuf* f, int pixy, int pixx,
int parse_start){
assert(spx->n);
if(spx->cellpxy > 0){ // don't explode on ncdirect case
@ -162,11 +162,10 @@ int sprixel_load(sprixel* spx, char* s, int bytes, int pixy, int pixx,
return -1;
}
}
if(spx->glyph != s){
free(spx->glyph);
spx->glyph = s;
if(&spx->glyph != f){
fbuf_free(&spx->glyph);
memcpy(&spx->glyph, f, sizeof(*f));
}
spx->glyphlen = bytes;
spx->invalidated = SPRIXEL_INVALIDATED;
spx->pixx = pixx;
spx->pixy = pixy;

View File

@ -7,6 +7,7 @@ extern "C" {
#include <stdint.h>
#include <stdbool.h>
#include "fbuf.h"
struct tinfo;
struct ncpile;
@ -131,8 +132,7 @@ typedef struct tament {
// protocol, we just have to rewrite them. there's a doubly-linked list of
// sprixels per ncpile, to which the pile keeps a head link.
typedef struct sprixel {
char* glyph; // glyph; can be quite large
size_t glyphlen; // length of the glyph in bytes
fbuf glyph;
uint32_t id; // embedded into gcluster field of nccell, 24 bits
// both the plane and visual can die before the sprixel does. they are
// responsible in such a case for NULLing out this link themselves.
@ -151,8 +151,6 @@ typedef struct sprixel {
int parse_start; // where to start parsing for cell wipes
// only used for sixel-based sprixels
struct sixelmap* smap; // copy of palette indices + transparency bits
FILE* mstreamfp; // mstream for writing animation-type updates,
// (only available for kitty animation sprixels)
bool wipes_outstanding; // do we need rebuild the sixel next render?
} sprixel;

View File

@ -5,7 +5,7 @@
// convert the sprixel at s having pixel dimensions dimyXdimx to an rgb(a)
// matrix for easier analysis. breaks on malformed sixels.
std::vector<uint32_t> sixel_to_rgb(const char* s, int dimy, int dimx) {
std::vector<uint32_t> sixel_to_rgb(const char* s, size_t len, int dimy, int dimx) {
std::vector<uint32_t> bmap(dimy * dimx, 0x00000000ull);
std::vector<uint32_t> colors;
// first we skip the header
@ -25,7 +25,8 @@ std::vector<uint32_t> sixel_to_rgb(const char* s, int dimy, int dimx) {
unsigned x = 0;
unsigned y = 0;
unsigned rle = 1;
while(*s){
const char* begin = s;
while((size_t)(s - begin) < len){
if(*s == '\e'){
break;
}
@ -166,7 +167,8 @@ TEST_CASE("Sixels") {
auto newn = ncvisual_render(nc_, ncv, &vopts);
CHECK(newn);
CHECK(0 == notcurses_render(nc_));
auto rgb = sixel_to_rgb(newn->sprite->glyph, newn->sprite->pixy, newn->sprite->pixx);
auto rgb = sixel_to_rgb(newn->sprite->glyph.buf, newn->sprite->glyph.used,
newn->sprite->pixy, newn->sprite->pixx);
for(int y = 0 ; y < newn->sprite->pixy ; ++y){
for(int x = 0 ; x < newn->sprite->pixx ; ++x){
//fprintf(stderr, "%03d/%03d NCV: %08x RGB: %08x\n", y, x, ncv->data[y * newn->sprite->pixx + x], rgb[y * newn->sprite->pixx + x]);
@ -186,7 +188,8 @@ TEST_CASE("Sixels") {
vopts.flags = NCVISUAL_OPTION_NODEGRADE;
auto newn = ncvisual_render(nc_, ncv, &vopts);
CHECK(newn);
auto rgbold = sixel_to_rgb(newn->sprite->glyph, newn->sprite->pixy, newn->sprite->pixx);
auto rgbold = sixel_to_rgb(newn->sprite->glyph.buf, newn->sprite->glyph.used,
newn->sprite->pixy, newn->sprite->pixx);
//print_bmap(rgbold, newn->sprite->pixy, newn->sprite->pixx);
CHECK(0 == notcurses_render(nc_));
struct ncplane_options nopts = {
@ -208,7 +211,8 @@ TEST_CASE("Sixels") {
CHECK(0 == notcurses_render(nc_));
// FIXME at this point currently, we get a degraded back of the orca
// test via conversion back to image? unsure
auto rgbnew = sixel_to_rgb(newn->sprite->glyph, newn->sprite->pixy, newn->sprite->pixx);
auto rgbnew = sixel_to_rgb(newn->sprite->glyph.buf, newn->sprite->glyph.used,
newn->sprite->pixy, newn->sprite->pixx);
//print_bmap(rgbnew, newn->sprite->pixy, newn->sprite->pixx);
CHECK(0 == ncplane_destroy(newn));
CHECK(0 == ncplane_destroy(blockerplane));