From bb91c170ddb045b167abf60975ad912a908ed0ca Mon Sep 17 00:00:00 2001 From: nick black Date: Mon, 20 Dec 2021 20:26:38 -0500 Subject: [PATCH] implement sixel_as_rgba() #1724 --- CMakeLists.txt | 75 ----------- NEWS.md | 1 + USAGE.md | 4 +- doc/man/man3/notcurses_visual.3.md | 2 + include/notcurses/libsixel.h | 25 ---- include/notcurses/notcurses.h | 4 + include/sixel/sixel.h | 42 ------ src/lib/blit.h | 23 ++++ src/lib/blitset.h | 4 +- src/lib/internal.h | 13 +- src/lib/sixel.c | 150 ++++++++++++++++++++++ src/lib/sprite.h | 9 +- src/lib/termdesc.h | 2 + src/lib/visual.c | 2 +- src/media/ffmpeg.c | 2 +- src/media/oiio.h | 2 +- src/sixel/sixel.c | 200 ----------------------------- src/sixelanalyzer/main.c | 6 - src/tests/sixel.cpp | 137 +------------------- 19 files changed, 204 insertions(+), 499 deletions(-) delete mode 100644 include/notcurses/libsixel.h delete mode 100644 include/sixel/sixel.h create mode 100644 src/lib/blit.h delete mode 100644 src/sixel/sixel.c delete mode 100644 src/sixelanalyzer/main.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 4189a60de..043c42f23 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -408,80 +408,6 @@ target_link_directories(notcurses PRIVATE ${OIIO_LIBRARY_DIRS}) target_link_directories(notcurses-static PRIVATE ${OIIO_STATIC_LIBRARY_DIRS}) endif() -############################################################################ -# libncsixel (sixel library built atop notcurses) -file(GLOB NCSIXSRCS CONFIGURE_DEPENDS src/sixel/*.c) -add_library(ncsixel SHARED ${NCSIXSRCS} ${COMPATSRC}) -if(${USE_STATIC}) -add_library(ncsixel-static STATIC ${NCSIXSRCS}) -else() -add_library(ncsixel-static STATIC EXCLUDE_FROM_ALL ${NCSIXSRCS}) -endif() -set_target_properties(ncsixel PROPERTIES - VERSION ${PROJECT_VERSION} - SOVERSION ${PROJECT_VERSION_MAJOR} -) -set_target_properties(ncsixel-static PROPERTIES - VERSION ${PROJECT_VERSION} - OUTPUT_NAME ncsixel -) -target_include_directories(ncsixel - BEFORE - PRIVATE - include - src - "${CMAKE_REQUIRED_INCLUDES}" - "${PROJECT_BINARY_DIR}/include" - "${TERMINFO_INCLUDE_DIRS}" -) -target_include_directories(ncsixel-static - BEFORE - PRIVATE - include - src - "${CMAKE_REQUIRED_INCLUDES}" - "${PROJECT_BINARY_DIR}/include" - "${TERMINFO_INCLUDE_DIRS}" -) -target_compile_definitions(ncsixel - PRIVATE - _GNU_SOURCE _DEFAULT_SOURCE -) -target_compile_definitions(ncsixel-static - PRIVATE - _GNU_SOURCE _DEFAULT_SOURCE -) -target_link_libraries(ncsixel - PUBLIC - notcurses -) -target_link_libraries(ncsixel-static - PUBLIC - notcurses-static -) -file(GLOB SIXELANALYZERSRC CONFIGURE DEPENDS src/sixelanalyzer/*.c) -add_executable(sixelanalyzer ${SIXELANALYZERSRC}) -target_compile_definitions(sixelanalyzer - PRIVATE - _GNU_SOURCE -) -target_include_directories(sixelanalyzer - BEFORE - PRIVATE - include - src - "${TERMINFO_INCLUDE_DIRS}" - "${CMAKE_REQUIRED_INCLUDES}" - "${PROJECT_BINARY_DIR}/include" -) -target_link_libraries(sixelanalyzer - PRIVATE - ncsixel - ${LIBM} - ${LIBRT} - Threads::Threads -) - ############################################################################ if(${USE_CXX}) # libnotcurses++ (C++ wrappers) @@ -1082,7 +1008,6 @@ if(BUILD_EXECUTABLES) install(TARGETS notcurses-demo DESTINATION bin) install(TARGETS notcurses-info DESTINATION bin) install(TARGETS ncneofetch DESTINATION bin) -install(TARGETS sixelanalyzer DESTINATION bin) if(NOT WIN32) install(TARGETS tfman DESTINATION bin) endif() diff --git a/NEWS.md b/NEWS.md index b6c63645e..90cb2f975 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,6 +8,7 @@ rearrangements of Notcurses. * Added `NCOPTION_CLI_MODE`, an alias for the bitwise OR of `NCOPTION_SCROLLING`, `NCOPTION_NO_CLEAR_BITMAPS`, `NCOPTION_NO_ALTERNATE_SCREEN`, and `NCOPTION_PRESERVE_CURSOR`. + * Added `ncsixel_as_rgba()`. * 3.0.1 (2021-12-14) * Added the `NCPLANE_OPTION_VSCROLL` flag. Creating an `ncplane` with this diff --git a/USAGE.md b/USAGE.md index 4461a553a..4edd72cb0 100644 --- a/USAGE.md +++ b/USAGE.md @@ -3632,10 +3632,12 @@ have only one frame), until it returns `NCERR_EOF`: // Open a visual at 'file', extracting a codec and parameters. 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 25130e93b..970e2d01c 100644 --- a/doc/man/man3/notcurses_visual.3.md +++ b/doc/man/man3/notcurses_visual.3.md @@ -121,6 +121,8 @@ 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***);** + # DESCRIPTION An **ncvisual** is a virtual pixel framebuffer. They can be created from diff --git a/include/notcurses/libsixel.h b/include/notcurses/libsixel.h deleted file mode 100644 index b95929d16..000000000 --- a/include/notcurses/libsixel.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef NOTCURSES_LIBSIXEL -#define NOTCURSES_LIBSIXEL - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -struct sixel; -struct sixelctx; - -struct sixelctx* libncsixel_init(void); - -struct sixel* libncsixel_encode(struct sixelctx* sctx, const char* file, unsigned colorregs); - -uint32_t* libncsixel_explode(const struct sixel* s); - -void libncsixel_stop(struct sixelctx* sctx); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index 5f9103388..577034997 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -4492,6 +4492,10 @@ 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/include/sixel/sixel.h b/include/sixel/sixel.h deleted file mode 100644 index fded7d11b..000000000 --- a/include/sixel/sixel.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef NOTCURSES_SIXEL_SIXEL -#define NOTCURSES_SIXEL_SIXEL - -// a library built atop Notcurses suitable for working with sixels outside -// the Notcurses framework--a replacement for libsixel. - -#ifdef __cplusplus -extern "C" { -#define RESTRICT -#else -#define RESTRICT restrict -#endif - -#ifndef __MINGW64__ -#define API __attribute__((visibility("default"))) -#else -#define API __declspec(dllexport) -#endif -#define ALLOC __attribute__((malloc)) __attribute__((warn_unused_result)) - -struct sixel; -struct sixelctx; - -API ALLOC struct sixelctx* libncsixel_init(void); - -// load the file and encode the first frame as a sixel. if |colorregs| is 0, -// use the number supported by the terminal, or 256 if the terminal does not -// support sixel. -API ALLOC __attribute__ ((nonnull (1, 2))) -struct sixel* libncsixel_encode(struct sixelctx* sctx, const char* file, - unsigned colorregs); - -API void libncsixel_stop(struct sixelctx* sctx); - -#undef API -#undef ALLOC - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif diff --git a/src/lib/blit.h b/src/lib/blit.h new file mode 100644 index 000000000..530506ef3 --- /dev/null +++ b/src/lib/blit.h @@ -0,0 +1,23 @@ +#ifndef NOTCURSES_BLIT +#define NOTCURSES_BLIT + +#ifdef __cplusplus +extern "C" { +#endif + +struct ncplane; +struct blitterargs; + +// scaledy and scaledx are output geometry from scaling; data is output data +// from scaling. we might actually need more pixels due to framing concerns, +// in which case just assume transparent input pixels where needed. +typedef int (*ncblitter)(struct ncplane* n, int linesize, const void* data, + int scaledy, int scaledx, const struct blitterargs* bargs); + +void set_pixel_blitter(ncblitter blitfxn); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/lib/blitset.h b/src/lib/blitset.h index b82bb1c3e..69ed55e3c 100644 --- a/src/lib/blitset.h +++ b/src/lib/blitset.h @@ -5,6 +5,8 @@ extern "C" { #endif +#include "blit.h" + // number of pixels that map to a single cell, height-wise static inline int encoding_y_scale(const tinfo* tcache, const struct blitset* bset) { @@ -50,8 +52,6 @@ ncplot_defblitter(const notcurses* nc){ return NCBLIT_1x1; } -void set_pixel_blitter(ncblitter blitfxn); - #ifdef __cplusplus } #endif diff --git a/src/lib/internal.h b/src/lib/internal.h index b48cfb2fa..8539691e3 100644 --- a/src/lib/internal.h +++ b/src/lib/internal.h @@ -390,12 +390,6 @@ typedef struct blitterargs { } u; } blitterargs; -// scaledy and scaledx are output geometry from scaling; data is output data -// from scaling. we might actually need more pixels due to framing concerns, -// in which case just assume transparent input pixels where needed. -typedef int (*ncblitter)(struct ncplane* n, int linesize, const void* data, - int scaledy, int scaledx, const blitterargs* bargs); - // a system for rendering RGBA pixels as text glyphs or sixel/kitty bitmaps struct blitset { ncblitter_e geom; @@ -693,7 +687,6 @@ void sprixel_hide(sprixel* s); // dimy and dimx are cell geometry, not pixel. sprixel* sprixel_alloc(ncplane* n, int dimy, int dimx); sprixel* sprixel_recycle(ncplane* n); -int sprite_init(const tinfo* t, int fd); int sprite_clear_all(const tinfo* t, fbuf* f); // these three all use absolute coordinates void sprixel_invalidate(sprixel* s, int y, int x); @@ -1077,7 +1070,7 @@ ALLOC char* ncplane_vprintf_prep(const char* format, va_list ap); // Resize the provided ncvisual to the specified 'rows' x 'cols', but do not // change the internals of the ncvisual. Uses oframe. -int ncvisual_blit_internal(struct ncvisual* ncv, int rows, int cols, +int ncvisual_blit_internal(const struct ncvisual* ncv, int rows, int cols, ncplane* n, const struct blitset* bset, const blitterargs* bargs); @@ -1794,8 +1787,8 @@ create_polyfill_op(int y, int x, struct topolyfill** stck){ typedef struct ncvisual_implementation { int (*visual_init)(int loglevel); void (*visual_printbanner)(fbuf* f); - int (*visual_blit)(struct ncvisual* ncv, unsigned rows, unsigned cols, ncplane* n, - const struct blitset* bset, const blitterargs* barg); + int (*visual_blit)(const struct ncvisual* ncv, unsigned rows, unsigned cols, + ncplane* n, const struct blitset* bset, const blitterargs* barg); struct ncvisual* (*visual_create)(void); struct ncvisual* (*visual_from_file)(const char* fname); // ncv constructors other than ncvisual_from_file() need to set up the diff --git a/src/lib/sixel.c b/src/lib/sixel.c index d517c5c01..a28a64a3c 100644 --- a/src/lib/sixel.c +++ b/src/lib/sixel.c @@ -1125,3 +1125,153 @@ 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/sprite.h b/src/lib/sprite.h index 48ee8a2cb..b72934388 100644 --- a/src/lib/sprite.h +++ b/src/lib/sprite.h @@ -167,6 +167,7 @@ create_tam(int rows, int cols){ return tam; } +int sprite_init(const struct tinfo* t, int fd); int sixel_wipe(sprixel* s, int ycell, int xcell); // nulls out a cell from a kitty bitmap via changing the alpha value // throughout to 0. the same trick doesn't work on sixel, but there we @@ -183,9 +184,9 @@ int kitty_rebuild(sprixel* s, int ycell, int xcell, uint8_t* auxvec); int fbcon_rebuild(sprixel* s, int ycell, int xcell, uint8_t* auxvec); int kitty_rebuild_animation(sprixel* s, int ycell, int xcell, uint8_t* auxvec); int kitty_rebuild_selfref(sprixel* s, int ycell, int xcell, uint8_t* auxvec); -int sixel_draw(const tinfo* ti, const struct ncpile *p, sprixel* s, +int sixel_draw(const struct tinfo* ti, const struct ncpile *p, sprixel* s, fbuf* f, int yoff, int xoff); -int kitty_draw(const tinfo* ti, const struct ncpile *p, sprixel* s, +int kitty_draw(const struct tinfo* ti, const struct ncpile *p, sprixel* s, fbuf* f, int yoff, int xoff); int kitty_move(sprixel* s, fbuf* f, unsigned noscroll, int yoff, int xoff); int sixel_scrub(const struct ncpile* p, sprixel* s); @@ -208,8 +209,8 @@ int kitty_blit_selfref(struct ncplane* nc, int linesize, const void* data, int leny, int lenx, const struct blitterargs* bargs); int fbcon_blit(struct ncplane* nc, int linesize, const void* data, int leny, int lenx, const struct blitterargs* bargs); -int fbcon_draw(const tinfo* ti, sprixel* s, int yoff, int xoff); -void fbcon_scroll(const struct ncpile* p, tinfo* ti, int rows); +int fbcon_draw(const struct tinfo* ti, sprixel* s, int yoff, int xoff); +void fbcon_scroll(const struct ncpile* p, struct tinfo* ti, int rows); void sixel_refresh(const struct ncpile* p, sprixel* s); // takes ownership of s on success. diff --git a/src/lib/termdesc.h b/src/lib/termdesc.h index 64a19f5c2..a2a0fb93c 100644 --- a/src/lib/termdesc.h +++ b/src/lib/termdesc.h @@ -13,6 +13,8 @@ extern "C" { #include #include #include +#include "sprite.h" +#include "blit.h" #include "fbuf.h" #include "in.h" diff --git a/src/lib/visual.c b/src/lib/visual.c index 93e77ff93..23a6d89aa 100644 --- a/src/lib/visual.c +++ b/src/lib/visual.c @@ -80,7 +80,7 @@ ncplane* ncvisual_subtitle_plane(ncplane* parent, const ncvisual* ncv){ return visual_implementation->visual_subtitle(parent, ncv); } -int ncvisual_blit_internal(ncvisual* ncv, int rows, int cols, ncplane* n, +int ncvisual_blit_internal(const ncvisual* ncv, int rows, int cols, ncplane* n, const struct blitset* bset, const blitterargs* barg){ if(!(barg->flags & NCVISUAL_OPTION_NOINTERPOLATE)){ if(visual_implementation->visual_blit){ diff --git a/src/media/ffmpeg.c b/src/media/ffmpeg.c index db5fee5cf..cc9ffb03f 100644 --- a/src/media/ffmpeg.c +++ b/src/media/ffmpeg.c @@ -641,7 +641,7 @@ ffmpeg_resize(ncvisual* n, unsigned rows, unsigned cols){ // rows/cols: scaled output geometry (pixels) static int -ffmpeg_blit(ncvisual* ncv, unsigned rows, unsigned cols, ncplane* n, +ffmpeg_blit(const ncvisual* ncv, unsigned rows, unsigned cols, ncplane* n, const struct blitset* bset, const blitterargs* bargs){ void* data; int stride = 0; diff --git a/src/media/oiio.h b/src/media/oiio.h index 9b87d10f5..1f4ef2f42 100644 --- a/src/media/oiio.h +++ b/src/media/oiio.h @@ -11,7 +11,7 @@ int oiio_decode(ncvisual* nc); struct ncvisual_details* oiio_details_init(void); void oiio_printbanner(fbuf* f); void oiio_details_seed(struct ncvisual* ncv); -int oiio_blit(ncvisual* ncv, unsigned rows, unsigned cols, +int oiio_blit(const ncvisual* ncv, unsigned rows, unsigned cols, struct ncplane* n, const struct blitset* bset, const blitterargs* bargs); ncvisual* oiio_from_file(const char* filename); diff --git a/src/sixel/sixel.c b/src/sixel/sixel.c deleted file mode 100644 index 243421559..000000000 --- a/src/sixel/sixel.c +++ /dev/null @@ -1,200 +0,0 @@ -#include "sixel/sixel.h" -#include - -// represents a sixel generated from some image -typedef struct sixel { - char* escape; // nul-terminated escape suitable for writing to the terminal - // there is both the true pixel geometry, and the sprixel-padded pixel - // geometry--the latter always has a height which is a multiple of six. - unsigned pixy, pixx; // original pixel geometry - unsigned sprixpixy, sprixpixx; // sprixel-padded pixel geometry - // we might only occupy a portion of the final column and row of cells. - unsigned celly, cellx; // cell geometry - unsigned colorregs_avail; // color registers available - unsigned colorregs_used; // color registers used -} sixel; - -typedef struct sixelctx { - struct notcurses* nc; -} sixelctx; - -sixelctx* libncsixel_init(void){ - sixelctx* sctx = malloc(sizeof(*sctx)); - if(sctx == NULL){ - return NULL; - } - struct notcurses_options nopts = { - .flags = NCOPTION_NO_ALTERNATE_SCREEN | - NCOPTION_PRESERVE_CURSOR | - NCOPTION_SUPPRESS_BANNERS | - NCOPTION_DRAIN_INPUT, - }; - if((sctx->nc = notcurses_init(&nopts, NULL)) == NULL){ - free(sctx); - return NULL; - } - return sctx; -} - -sixel* libncsixel_encode(sixelctx* sctx, const char* file, unsigned colorregs){ - (void)sctx; - (void)file; - (void)colorregs; - return NULL; -} - -uint32_t* libncsixel_explode(const sixel* s){ - uint32_t* rgba = malloc(sizeof(*rgba) * s->pixy * s->pixx); - if(rgba == NULL){ - return NULL; - } - uint32_t* colors = malloc(sizeof(*colors) * s->colorregs_used); - if(colors == NULL){ - free(rgba); - return NULL; - } - const char* sx = s->escape; - // 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'; - ++s; - }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 >= s->colorregs_used){ - 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 * s->pixx + xpos] = colors[color]; - } - } - } - x += rle; - rle = 1; - } - } - ++sx; - } - free(colors); - return rgba; - -err: - free(rgba); - free(colors); - return NULL; -} - -void libncsixel_stop(sixelctx* sctx){ - if(sctx){ - notcurses_stop(sctx->nc); - free(sctx); - } -} diff --git a/src/sixelanalyzer/main.c b/src/sixelanalyzer/main.c deleted file mode 100644 index 6b45bf900..000000000 --- a/src/sixelanalyzer/main.c +++ /dev/null @@ -1,6 +0,0 @@ -#include -#include - -int main(void){ - return EXIT_SUCCESS; -} diff --git a/src/tests/sixel.cpp b/src/tests/sixel.cpp index e267779cd..b2ae2ca16 100644 --- a/src/tests/sixel.cpp +++ b/src/tests/sixel.cpp @@ -3,131 +3,6 @@ #include #include -// convert the sprixel at s having pixel dimensions dimyXdimx to an rgb(a) -// matrix for easier analysis. breaks on malformed sixels. -std::vector sixel_to_rgb(const char* s, size_t len, int dimy, int dimx) { - std::vector bmap(dimy * dimx, 0x00000000ull); - std::vector colors; - // first we skip the header - while(*s != '#'){ - ++s; - } - // 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; - const char* begin = s; - while((size_t)(s - begin) < len){ - if(*s == '\e'){ - break; - } - if(state == STATE_WANT_HASH){ - REQUIRE('#' == *s); - state = STATE_WANT_COLOR; - }else if(state == STATE_WANT_COLOR){ - CHECK(isdigit(*s)); - color = 0; - do{ - color *= 10; - color += *s - '0'; - ++s; - }while(isdigit(*s)); -//std::cerr << "Got color " << color << std::endl; - --s; - state = STATE_WANT_COLORSEMI; - }else if(state == STATE_WANT_COLORSEMI){ - // if we get a semicolon, we're a colorspec, otherwise data - if(*s == ';'){ - state = STATE_WANT_COLORSPACE; - }else{ - state = STATE_WANT_DATA; - rle = 1; - } - }else if(state == STATE_WANT_COLORSPACE){ - CHECK('2' == *(s++)); - CHECK(';' == *(s++)); - int r = 0; - do{ - r *= 10; - r += *s - '0'; - ++s; - }while(isdigit(*s)); - CHECK(';' == *(s++)); - int g = 0; - do{ - g *= 10; - g += *s - '0'; - ++s; - }while(isdigit(*s)); - CHECK(';' == *(s++)); - int b = 0; - do{ - b *= 10; - b += *s - '0'; - ++s; - }while(isdigit(*s)); - 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 >= colors.capacity()){ - colors.resize(color + 1); - } - colors[color] = rgb; - state = STATE_WANT_HASH; - --s; - } - // read until we hit next colorspec - if(state == STATE_WANT_DATA){ -//std::cerr << "Character " << *s << std::endl; - if(*s == '#'){ - state = STATE_WANT_HASH; - --s; - }else if(*s == '!'){ // RLE - ++s; - rle = 0; - do{ - rle *= 10; - rle += *s - '0'; - ++s; - }while(isdigit(*s)); - CHECK(2 < rle); - --s; - }else if(*s == '$'){ - x = 0; - state = STATE_WANT_HASH; - }else if(*s == '-'){ - 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((*s - 63) & (1u << (ypos - y))){ - // ought be an empty pixel - CHECK(0x00000000ull == bmap[ypos * dimx + xpos]); -//std::cerr << *s << " BMAP[" << ypos << "][" << xpos << "] = " << std::hex << colors[color] << std::dec << std::endl; - bmap[ypos * dimx + xpos] = colors[color]; - } - } - } - x += rle; - rle = 1; - } - } - ++s; - } - return bmap; -} - /* void print_bmap(const std::vector rgba, int pixy, int pixx){ for(int y = 0 ; y < pixy ; ++y){ @@ -169,8 +44,8 @@ TEST_CASE("Sixels") { auto newn = ncvisual_blit(nc_, ncv, &vopts); REQUIRE(nullptr != newn); CHECK(0 == notcurses_render(nc_)); - auto rgb = sixel_to_rgb(newn->sprite->glyph.buf, newn->sprite->glyph.used, - newn->sprite->pixy, newn->sprite->pixx); + auto rgb = ncsixel_as_rgba(newn->sprite->glyph.buf, newn->sprite->pixy, newn->sprite->pixx); + REQUIRE(rgb); 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]); @@ -191,8 +66,8 @@ TEST_CASE("Sixels") { vopts.flags = NCVISUAL_OPTION_NODEGRADE | NCVISUAL_OPTION_CHILDPLANE; auto newn = ncvisual_blit(nc_, ncv, &vopts); REQUIRE(nullptr != newn); - auto rgbold = sixel_to_rgb(newn->sprite->glyph.buf, newn->sprite->glyph.used, - newn->sprite->pixy, newn->sprite->pixx); + auto rgbold = ncsixel_as_rgba(newn->sprite->glyph.buf, newn->sprite->pixy, newn->sprite->pixx); + REQUIRE(rgbold); //print_bmap(rgbold, newn->sprite->pixy, newn->sprite->pixx); CHECK(0 == notcurses_render(nc_)); struct ncplane_options nopts = { @@ -214,8 +89,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.buf, newn->sprite->glyph.used, - newn->sprite->pixy, newn->sprite->pixx); + auto rgbnew = ncsixel_as_rgba(newn->sprite->glyph.buf, newn->sprite->pixy, newn->sprite->pixx); + REQUIRE(rgbnew); //print_bmap(rgbnew, newn->sprite->pixy, newn->sprite->pixx); CHECK(0 == ncplane_destroy(newn)); CHECK(0 == ncplane_destroy(blockerplane));