mirror of
https://github.com/dankamongmen/notcurses
synced 2025-03-10 01:29:05 -04:00
everybody's movin', everybody's groovin', baby #1977
This commit is contained in:
parent
625564b4d9
commit
19797b3859
@ -6,6 +6,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include <time.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define NANOSECS_IN_SEC 1000000000ul
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
199
src/lib/kitty.c
199
src/lib/kitty.c
@ -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,9 +740,9 @@ 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",
|
||||
lenx, leny, sprixelid,
|
||||
animated ? "o=z,q=2" : chunks ? "m=1;" : "q=2;");
|
||||
*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
|
||||
// the control block, since we're not yet sure what m= to write. we've
|
||||
// otherwise written q=2; if we're the only chunk, and m=1; otherwise.
|
||||
@ -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;",
|
||||
xcell * s->cellpxx, ycell * s->cellpxy, xlen, ylen,
|
||||
s->id, chunks ? "m=1" : "q=2");
|
||||
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") < 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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
196
src/lib/sixel.c
196
src/lib/sixel.c
@ -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),
|
||||
(stab->deets[idx].sums[1] * 100 / count / 255),
|
||||
(stab->deets[idx].sums[2] * 100 / count / 255));
|
||||
if(f < 0){
|
||||
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(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){
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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));
|
||||
|
Loading…
x
Reference in New Issue
Block a user