implement sixel_as_rgba() #1724

pull/2501/head
nick black 2 years ago committed by nick black
parent 61000ce2a4
commit bb91c170dd

@ -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()

@ -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

@ -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

@ -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

@ -1,25 +0,0 @@
#ifndef NOTCURSES_LIBSIXEL
#define NOTCURSES_LIBSIXEL
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
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

@ -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.

@ -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

@ -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

@ -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

@ -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

@ -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
}

@ -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.

@ -13,6 +13,8 @@ extern "C" {
#include <pthread.h>
#include <stdbool.h>
#include <notcurses/notcurses.h>
#include "sprite.h"
#include "blit.h"
#include "fbuf.h"
#include "in.h"

@ -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){

@ -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;

@ -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);

@ -1,200 +0,0 @@
#include "sixel/sixel.h"
#include <notcurses/notcurses.h>
// 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);
}
}

@ -1,6 +0,0 @@
#include <stdlib.h>
#include <notcurses/libsixel.h>
int main(void){
return EXIT_SUCCESS;
}

@ -3,131 +3,6 @@
#include <vector>
#include <iostream>
// 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, 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
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<uint32_t> 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));

Loading…
Cancel
Save