mirror of
https://github.com/dankamongmen/notcurses.git
synced 2024-11-11 13:11:20 +00:00
ncvisual_from_sixel() atop ncsixel_as_rgba()
This commit is contained in:
parent
bb91c170dd
commit
3d0fbc4d8f
6
USAGE.md
6
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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
150
src/lib/sixel.c
150
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
|
||||
}
|
||||
|
167
src/lib/sixel.h
Normal file
167
src/lib/sixel.h
Normal file
@ -0,0 +1,167 @@
|
||||
#ifndef NOTCURSES_SIXEL
|
||||
#define NOTCURSES_SIXEL
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
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
|
@ -2,6 +2,7 @@
|
||||
#include "lib/visual-details.h"
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include "lib/sixel.h"
|
||||
|
||||
/*
|
||||
void print_bmap(const std::vector<uint32_t> rgba, int pixy, int pixx){
|
||||
|
Loading…
Reference in New Issue
Block a user