From 3d0fbc4d8f4309a9453a0df899f74d66c72a5728 Mon Sep 17 00:00:00 2001 From: nick black Date: Thu, 23 Dec 2021 08:15:18 -0500 Subject: [PATCH] ncvisual_from_sixel() atop ncsixel_as_rgba() --- USAGE.md | 6 +- doc/man/man3/notcurses_visual.3.md | 2 +- include/notcurses/notcurses.h | 8 +- src/lib/sixel.c | 150 -------------------------- src/lib/sixel.h | 167 +++++++++++++++++++++++++++++ src/tests/sixel.cpp | 1 + 6 files changed, 176 insertions(+), 158 deletions(-) create mode 100644 src/lib/sixel.h diff --git a/USAGE.md b/USAGE.md index 4edd72cb0..93c29777f 100644 --- a/USAGE.md +++ b/USAGE.md @@ -3382,6 +3382,9 @@ struct ncvisual* ncvisual_from_bgra(struct notcurses* nc, const void* bgra, struct ncvisual* ncvisual_from_palidx(const void* data, int rows, int rowstride, int cols, int palsize, int pstride, const uint32_t* palette); + +// Construct an ncvisual from a nul-terminated Sixel control sequence. +struct ncvisual* ncvisual_from_sixel(const char* s, unsigned leny, unsigned lenx); ``` `ncvisual`s can also be loaded from the contents of a plane: @@ -3635,9 +3638,6 @@ struct ncvisual* ncvisual_from_file(const char* file); // extract the next frame from an ncvisual. returns NCERR_EOF on end of file, // and NCERR_SUCCESS on success, otherwise some other NCERR. int ncvisual_decode(struct ncvisual* nc); - -// Convert a sixel escape into an RGBA vector. -uint32_t* ncsixel_as_rgba(const char *s, unsigned leny, unsigned lenx); ``` ### Pixels diff --git a/doc/man/man3/notcurses_visual.3.md b/doc/man/man3/notcurses_visual.3.md index 970e2d01c..5f88dcce5 100644 --- a/doc/man/man3/notcurses_visual.3.md +++ b/doc/man/man3/notcurses_visual.3.md @@ -121,7 +121,7 @@ typedef struct ncvgeom { **int ncplane_qrcode(struct ncplane* ***n***, unsigned* ***ymax***, unsigned* ***xmax***, const void* ***data***, size_t ***len***)** -**uint32_t* ncsixel_as_rgba(const char* ***s***, unsigned ***leny***, unsigned ***lenx***);** +**struct ncvisual* ncvisual_from_sixel(const char* ***s***, unsigned ***leny***, unsigned ***lenx***);** # DESCRIPTION diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index 577034997..f66c13c8c 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -3153,6 +3153,10 @@ API ALLOC struct ncvisual* ncvisual_from_plane(const struct ncplane* n, unsigned leny, unsigned lenx) __attribute__ ((nonnull (1))); +// Construct an ncvisual from a nul-terminated Sixel control sequence. +API ALLOC struct ncvisual* ncvisual_from_sixel(const char* s, unsigned leny, unsigned lenx) + __attribute__ ((nonnull (1))); + #define NCVISUAL_OPTION_NODEGRADE 0x0001ull // fail rather than degrade #define NCVISUAL_OPTION_BLEND 0x0002ull // use NCALPHA_BLEND with visual #define NCVISUAL_OPTION_HORALIGNED 0x0004ull // x is an alignment, not absolute @@ -4492,10 +4496,6 @@ API ALLOC char* notcurses_hostname(void); // Returns a heap-allocated copy of human-readable OS name and version. API ALLOC char* notcurses_osversion(void); -// Convert a sixel escape into an RGBA vector. -API ALLOC uint32_t* ncsixel_as_rgba(const char *s, unsigned leny, unsigned lenx) - __attribute__ ((nonnull (1))); - // Dump selected Notcurses state to the supplied 'debugfp'. Output is freeform, // newline-delimited, and subject to change. It includes geometry of all // planes, from all piles. No line has more than 80 columns' worth of output. diff --git a/src/lib/sixel.c b/src/lib/sixel.c index a28a64a3c..d517c5c01 100644 --- a/src/lib/sixel.c +++ b/src/lib/sixel.c @@ -1125,153 +1125,3 @@ uint8_t* sixel_trans_auxvec(const ncpile* p){ } return a; } - -uint32_t* ncsixel_as_rgba(const char *sx, unsigned leny, unsigned lenx){ -#define MAXCOLORS 65535 - uint32_t* rgba = malloc(sizeof(*rgba) * leny * lenx); - if(rgba == NULL){ - return NULL; - } - uint32_t* colors = malloc(sizeof(*colors) * MAXCOLORS); - if(colors == NULL){ - free(rgba); - return NULL; - } - // first we skip the header - while(*sx != '#'){ - ++sx; - } - // now we build the color table (form: #Pc;Pu;Px;Py;Pz). data starts with - // a color spec lacking a semicolon. - enum { - STATE_WANT_HASH, - STATE_WANT_COLOR, - STATE_WANT_COLORSEMI, - STATE_WANT_COLORSPACE, - STATE_WANT_DATA, - } state = STATE_WANT_HASH; - unsigned color = 0; - unsigned x = 0; - unsigned y = 0; - unsigned rle = 1; - while(*sx){ - if(*sx == '\e'){ - break; - } - if(state == STATE_WANT_HASH){ - if('#' != *sx){ - goto err; - } - state = STATE_WANT_COLOR; - }else if(state == STATE_WANT_COLOR){ - if(!isdigit(*sx)){ - goto err; - } - color = 0; - do{ - color *= 10; - color += *sx - '0'; - ++sx; - }while(isdigit(*sx)); -//std::cerr << "Got color " << color << std::endl; - --sx; - state = STATE_WANT_COLORSEMI; - }else if(state == STATE_WANT_COLORSEMI){ - // if we get a semicolon, we're a colorspec, otherwise data - if(*sx == ';'){ - state = STATE_WANT_COLORSPACE; - }else{ - state = STATE_WANT_DATA; - rle = 1; - } - }else if(state == STATE_WANT_COLORSPACE){ - if('2' != *(sx++)){ - goto err; - } - if(';' != *(sx++)){ - goto err; - } - int r = 0; - do{ - r *= 10; - r += *sx - '0'; - ++sx; - }while(isdigit(*sx)); - if(';' != *(sx++)){ - goto err; - } - int g = 0; - do{ - g *= 10; - g += *sx - '0'; - ++sx; - }while(isdigit(*sx)); - if(';' != *(sx++)){ - goto err; - } - int b = 0; - do{ - b *= 10; - b += *sx - '0'; - ++sx; - }while(isdigit(*sx)); - uint32_t rgb = htole(0xff000000 + (r << 16u) * 255 / 100 + (g << 8u) * 255 / 100 + b * 255 / 100); -//std::cerr << "Got color " << color << ": " << r << "/" << g << "/" << b << std::endl; - if(color >= MAXCOLORS){ - goto err; - } - colors[color] = rgb; - state = STATE_WANT_HASH; - --sx; - } - // read until we hit next colorspec - if(state == STATE_WANT_DATA){ -//std::cerr << "Character " << *sx << std::endl; - if(*sx == '#'){ - state = STATE_WANT_HASH; - --sx; - }else if(*sx == '!'){ // RLE - ++sx; - rle = 0; - do{ - rle *= 10; - rle += *sx - '0'; - ++sx; - }while(isdigit(*sx)); - if(2 >= rle){ - goto err; - } - --sx; - }else if(*sx == '$'){ - x = 0; - state = STATE_WANT_HASH; - }else if(*sx == '-'){ - x = 0; - y += 6; - state = STATE_WANT_HASH; - }else{ -//std::cerr << "RLE: " << rle << " pos: " << y << "*" << x << std::endl; - for(unsigned xpos = x ; xpos < x + rle ; ++xpos){ - for(unsigned ypos = y ; ypos < y + 6 ; ++ypos){ - if((*sx - 63) & (1u << (ypos - y))){ - // ought be an empty pixel -//std::cerr << *s << " BMAP[" << ypos << "][" << xpos << "] = " << std::hex << colors[color] << std::dec << std::endl; - rgba[ypos * lenx + xpos] = colors[color]; - } - } - } - x += rle; - rle = 1; - } - } - ++sx; - } - free(colors); - return rgba; - -err: - free(rgba); - free(colors); - return NULL; -#undef MAXCOLORS -} diff --git a/src/lib/sixel.h b/src/lib/sixel.h new file mode 100644 index 000000000..9c507cc69 --- /dev/null +++ b/src/lib/sixel.h @@ -0,0 +1,167 @@ +#ifndef NOTCURSES_SIXEL +#define NOTCURSES_SIXEL + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +uint32_t* ncsixel_as_rgba(const char *sx, unsigned leny, unsigned lenx){ +#define MAXCOLORS 65535 + // cast is necessary for c++ callers + uint32_t* rgba = (typeof rgba)malloc(sizeof(*rgba) * leny * lenx); + if(rgba == NULL){ + return NULL; + } + // cast is necessary for c++ callers + uint32_t* colors = (typeof colors)malloc(sizeof(*colors) * MAXCOLORS); + if(colors == NULL){ + free(rgba); + return NULL; + } + // first we skip the header + while(*sx != '#'){ + ++sx; + } + // now we build the color table (form: #Pc;Pu;Px;Py;Pz). data starts with + // a color spec lacking a semicolon. + enum { + STATE_WANT_HASH, + STATE_WANT_COLOR, + STATE_WANT_COLORSEMI, + STATE_WANT_COLORSPACE, + STATE_WANT_DATA, + } state = STATE_WANT_HASH; + unsigned color = 0; + unsigned x = 0; + unsigned y = 0; + unsigned rle = 1; + while(*sx){ + if(*sx == '\e'){ + break; + } + if(state == STATE_WANT_HASH){ + if('#' != *sx){ + goto err; + } + state = STATE_WANT_COLOR; + }else if(state == STATE_WANT_COLOR){ + if(!isdigit(*sx)){ + goto err; + } + color = 0; + do{ + color *= 10; + color += *sx - '0'; + ++sx; + }while(isdigit(*sx)); +//std::cerr << "Got color " << color << std::endl; + --sx; + state = STATE_WANT_COLORSEMI; + }else if(state == STATE_WANT_COLORSEMI){ + // if we get a semicolon, we're a colorspec, otherwise data + if(*sx == ';'){ + state = STATE_WANT_COLORSPACE; + }else{ + state = STATE_WANT_DATA; + rle = 1; + } + }else if(state == STATE_WANT_COLORSPACE){ + if('2' != *(sx++)){ + goto err; + } + if(';' != *(sx++)){ + goto err; + } + int r = 0; + do{ + r *= 10; + r += *sx - '0'; + ++sx; + }while(isdigit(*sx)); + if(';' != *(sx++)){ + goto err; + } + int g = 0; + do{ + g *= 10; + g += *sx - '0'; + ++sx; + }while(isdigit(*sx)); + if(';' != *(sx++)){ + goto err; + } + int b = 0; + do{ + b *= 10; + b += *sx - '0'; + ++sx; + }while(isdigit(*sx)); + uint32_t rgb = htole(0xff000000 + (r << 16u) * 255 / 100 + (g << 8u) * 255 / 100 + b * 255 / 100); +//std::cerr << "Got color " << color << ": " << r << "/" << g << "/" << b << std::endl; + if(color >= MAXCOLORS){ + goto err; + } + colors[color] = rgb; + state = STATE_WANT_HASH; + --sx; + } + // read until we hit next colorspec + if(state == STATE_WANT_DATA){ +//std::cerr << "Character " << *sx << std::endl; + if(*sx == '#'){ + state = STATE_WANT_HASH; + --sx; + }else if(*sx == '!'){ // RLE + ++sx; + rle = 0; + do{ + rle *= 10; + rle += *sx - '0'; + ++sx; + }while(isdigit(*sx)); + if(2 >= rle){ + goto err; + } + --sx; + }else if(*sx == '$'){ + x = 0; + state = STATE_WANT_HASH; + }else if(*sx == '-'){ + x = 0; + y += 6; + state = STATE_WANT_HASH; + }else{ +//std::cerr << "RLE: " << rle << " pos: " << y << "*" << x << std::endl; + for(unsigned xpos = x ; xpos < x + rle ; ++xpos){ + for(unsigned ypos = y ; ypos < y + 6 ; ++ypos){ + if((*sx - 63) & (1u << (ypos - y))){ + // ought be an empty pixel +//std::cerr << *s << " BMAP[" << ypos << "][" << xpos << "] = " << std::hex << colors[color] << std::dec << std::endl; + rgba[ypos * lenx + xpos] = colors[color]; + } + } + } + x += rle; + rle = 1; + } + } + ++sx; + } + free(colors); + return rgba; + +err: + free(rgba); + free(colors); + return NULL; +#undef MAXCOLORS +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/tests/sixel.cpp b/src/tests/sixel.cpp index b2ae2ca16..e89b28d35 100644 --- a/src/tests/sixel.cpp +++ b/src/tests/sixel.cpp @@ -2,6 +2,7 @@ #include "lib/visual-details.h" #include #include +#include "lib/sixel.h" /* void print_bmap(const std::vector rgba, int pixy, int pixx){