diff --git a/NEWS.md b/NEWS.md index d8ceef937..055f73b2f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -20,6 +20,7 @@ rearrangements of Notcurses. the media of an `ncvisual`. As a result, it now returns a `ncplane*` rather than `char*`. I don't think anyone was using this, so hopefully that's ok. + * Add `ncvisual_from_palidx()`, which does what you would think. * 2.3.11 (2021-07-20) * Notcurses now requires libz to build. In exchange, it can now generate diff --git a/USAGE.md b/USAGE.md index d94a023a2..3e608825f 100644 --- a/USAGE.md +++ b/USAGE.md @@ -3134,6 +3134,13 @@ struct ncvisual* ncvisual_from_rgb_loose(const void* rgba, int rows, // ncvisual_from_rgba(), but for BGRA. struct ncvisual* ncvisual_from_bgra(struct notcurses* nc, const void* bgra, int rows, int rowstride, int cols); + +// ncvisual_from_rgba(), but 'data' is 'pstride'-byte palette-indexed pixels, +// arranged in 'rows' lines of 'rowstride' bytes each, composed of 'cols' +// pixels. 'palette' is an array of at least 'palsize' ncchannels. +struct ncvisual* ncvisual_from_palidx(const void* data, int rows, + int rowstride, int cols, int palsize, + int pstride, const uint32_t* palette); ``` `ncvisual`s can also be loaded from the contents of a plane: diff --git a/doc/man/man3/notcurses_visual.3.md b/doc/man/man3/notcurses_visual.3.md index 96aa73d0a..2965b2281 100644 --- a/doc/man/man3/notcurses_visual.3.md +++ b/doc/man/man3/notcurses_visual.3.md @@ -62,6 +62,8 @@ typedef int (*streamcb)(struct notcurses*, struct ncvisual*, void*); **struct ncvisual* ncvisual_from_bgra(const void* ***bgra***, int ***rows***, int ***rowstride***, int ***cols***);** +**struct ncvisual* ncvisual_from_palidx(const void* ***data***, int ***rows***, int ***rowstride***, int ***cols***, int ***palsize***, int ***pstride***, const uint32_t* ***palette***);** + **struct ncvisual* ncvisual_from_plane(struct ncplane* ***n***, ncblitter_e ***blit***, int ***begy***, int ***begx***, int ***leny***, int ***lenx***);** **int ncvisual_blitter_geom(const struct notcurses* ***nc***, const struct ncvisual* ***n***, const struct ncvisual_options* ***vopts***, int* ***y***, int* ***x***, int* ***scaley***, int* ***scalex***, ncblitter_e* ***blitter***);** @@ -138,6 +140,10 @@ resulting plane will be ceil(**rows**/2) rows, and **cols** columns. **ncvisual_from_rgb_loose** uses 4-byte RGBx source data. Both will fill in the alpha component of every target pixel with the specified **alpha**. +**ncvisual_from_palidx** requires a ***palette*** of at least ***palsize*** +**ncchannel**s. Pixels are ***pstride*** bytes each, arranged as ***cols*** pixels +per row, with each row occupying ***rowstride*** bytes, across ***rows*** rows. + **ncvisual_from_plane** requires specification of a rectangle via ***begy***, ***begx***, ***leny***, and ***lenx***, and also a blitter. The only valid glyphs within this region are those used by the specified blitter. diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index fb05b0400..3e469870a 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -2568,7 +2568,8 @@ ncplane_double_box_sized(struct ncplane* n, uint32_t styles, uint64_t channels, // Open a visual at 'file', extract a codec and parameters, decode the first // image to memory. -API ALLOC struct ncvisual* ncvisual_from_file(const char* file); +API ALLOC struct ncvisual* ncvisual_from_file(const char* file) + __attribute__ ((nonnull (1))); // Prepare an ncvisual, and its underlying plane, based off RGBA content in // memory at 'rgba'. 'rgba' is laid out as 'rows' lines, each of which is @@ -2577,25 +2578,38 @@ API ALLOC struct ncvisual* ncvisual_from_file(const char* file); // of padding). The total size of 'rgba' is thus (rows * rowstride) bytes, of // which (rows * cols * 4) bytes are actual non-padding data. API ALLOC struct ncvisual* ncvisual_from_rgba(const void* rgba, int rows, - int rowstride, int cols); + int rowstride, int cols) + __attribute__ ((nonnull (1))); // ncvisual_from_rgba(), but the pixels are 3-byte RGB. A is filled in // throughout using 'alpha'. API ALLOC struct ncvisual* ncvisual_from_rgb_packed(const void* rgba, int rows, int rowstride, int cols, - int alpha); + int alpha) + __attribute__ ((nonnull (1))); // ncvisual_from_rgba(), but the pixels are 4-byte RGBx. A is filled in // throughout using 'alpha'. rowstride must be a multiple of 4. API ALLOC struct ncvisual* ncvisual_from_rgb_loose(const void* rgba, int rows, int rowstride, int cols, - int alpha); + int alpha) + __attribute__ ((nonnull (1))); // ncvisual_from_rgba(), but 'bgra' is arranged as BGRA. note that this is a // byte-oriented layout, despite being bunched in 32-bit pixels; the lowest // memory address ought be B, and A is reached by adding 3 to that address. API ALLOC struct ncvisual* ncvisual_from_bgra(const void* bgra, int rows, - int rowstride, int cols); + int rowstride, int cols) + __attribute__ ((nonnull (1))); + +// ncvisual_from_rgba(), but 'data' is 'pstride'-byte palette-indexed pixels, +// arranged in 'rows' lines of 'rowstride' bytes each, composed of 'cols' +// pixels. 'palette' is an array of at least 'palsize' ncchannels. +API ALLOC struct ncvisual* ncvisual_from_palidx(const void* data, int rows, + int rowstride, int cols, + int palsize, int pstride, + const uint32_t* palette) + __attribute__ ((nonnull (1, 7))); // Promote an ncplane 'n' to an ncvisual. The plane may contain only spaces, // half blocks, and full blocks. The latter will be checked, and any other diff --git a/src/lib/visual.c b/src/lib/visual.c index a9c6453fb..2e6c840c7 100644 --- a/src/lib/visual.c +++ b/src/lib/visual.c @@ -693,6 +693,47 @@ ncvisual* ncvisual_from_bgra(const void* bgra, int rows, int rowstride, int cols return ncv; } +ncvisual* ncvisual_from_palidx(const void* pdata, int rows, int rowstride, + int cols, int palsize, int pstride, + const uint32_t* palette){ + if(rowstride % pstride){ + logerror("bad pstride (%d) for rowstride (%d)\n", pstride, rowstride); + return NULL; + } + ncvisual* ncv = ncvisual_create(); + if(ncv){ + ncv->rowstride = pad_for_image(rowstride, cols); + ncv->pixx = cols; + ncv->pixy = rows; + uint32_t* data = malloc(ncv->rowstride * ncv->pixy); + if(data == NULL){ + ncvisual_destroy(ncv); + return NULL; + } + for(int y = 0 ; y < rows ; ++y){ + for(int x = 0 ; x < cols ; ++x){ + int palidx = ((const unsigned char*)pdata)[y * rowstride + x * pstride]; + if(palidx >= palsize){ + free(data); + ncvisual_destroy(ncv); + logerror("invalid palette idx %d >= %d\n", palidx, palsize); + return NULL; + } + uint32_t src = palette[palidx]; + uint32_t* dst = &data[ncv->rowstride * y / 4 + x]; + ncpixel_set_a(dst, 255); + ncpixel_set_r(dst, 255); + ncpixel_set_g(dst, 255); + ncpixel_set_b(dst, 255); // FIXME pull out of palette +//fprintf(stderr, "BGRA PIXEL: %02x%02x%02x%02x RGBA result: %02x%02x%02x%02x\n", ((const char*)&src)[0], ((const char*)&src)[1], ((const char*)&src)[2], ((const char*)&src)[3], ((const char*)dst)[0], ((const char*)dst)[1], ((const char*)dst)[2], ((const char*)dst)[3]); + } + } + ncvisual_set_data(ncv, data, true); + ncvisual_details_seed(ncv); + } + return ncv; +} + int ncvisual_resize(ncvisual* n, int rows, int cols){ if(!visual_implementation.visual_resize){ return ncvisual_resize_noninterpolative(n, rows, cols);