diff --git a/include/notcurses.h b/include/notcurses.h index 5093bf567..43b81ae3f 100644 --- a/include/notcurses.h +++ b/include/notcurses.h @@ -600,16 +600,15 @@ API struct ncvisual* ncplane_visual_open(struct ncplane* nc, const char* file, // can be neither decoded nor rendered any further. API void ncvisual_destroy(struct ncvisual* ncv); -// extract the next frame from an ncvisual. returns NULL on end of file, writing -// AVERROR_EOF to 'averr'. returns NULL on a decoding or allocation error, -// placing the AVError in 'averr'. this frame is invalidated by a subsequent -// call to ncvisual_decode(), and should not be freed by the caller. +// extract the next frame from an ncvisual. returns NULL on end of file, +// writing AVERROR_EOF to 'averr'. returns NULL on a decoding or allocation +// error, placing the AVError in 'averr'. this frame is invalidated by a +// subsequent call to ncvisual_decode(), and should not be freed by the caller. API struct AVFrame* ncvisual_decode(struct ncvisual* nc, int* averr); -// render the next frame to the associated ncplane at the current cursor -// position, going through ystop/xstop. the frame will be scaled to the -// appropriate size. -API int ncvisual_render(struct ncvisual* ncv, int ystop, int xstop); +// render the decoded frame to the associated ncplane. the frame will be scaled +// to the size of the ncplane at ncplane_visual_open() time. +API int ncvisual_render(const struct ncvisual* ncv); // A panelreel is an notcurses region devoted to displaying zero or more // line-oriented, contained panels between which the user may navigate. If at diff --git a/src/lib/internal.h b/src/lib/internal.h new file mode 100644 index 000000000..09cceea51 --- /dev/null +++ b/src/lib/internal.h @@ -0,0 +1,41 @@ +#ifndef NOTCURSES_INTERNAL +#define NOTCURSES_INTERNAL + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct AVFormatContext; +struct AVCodecContext; +struct AVFrame; +struct AVCodec; +struct AVCodecParameters; +struct AVPacket; +struct SwsContext; +struct ncplane; + +typedef struct ncvisual { + struct AVFormatContext* fmtctx; + struct AVCodecContext* codecctx; + struct AVFrame* frame; + struct AVFrame* oframe; + struct AVCodec* codec; + struct AVCodecParameters* cparams; + struct AVPacket* packet; + struct SwsContext* swsctx; + int packet_outstanding; + int dstwidth, dstheight; + struct ncplane* ncp; +} ncvisual; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/lib/libav.c b/src/lib/libav.c index 871e53100..7a5036374 100644 --- a/src/lib/libav.c +++ b/src/lib/libav.c @@ -3,18 +3,7 @@ #include #include #include "notcurses.h" - -typedef struct ncvisual { - AVFormatContext* fmtctx; - AVCodecContext* codecctx; - AVFrame* frame; - AVCodec* codec; - AVCodecParameters* cparams; - AVPacket* packet; - struct SwsContext* swsctx; - int packet_outstanding; - int dstwidth, dstheight; -} ncvisual; +#include "internal.h" static ncvisual* ncvisual_create(void){ @@ -31,6 +20,7 @@ void ncvisual_destroy(ncvisual* ncv){ avcodec_close(ncv->codecctx); avcodec_free_context(&ncv->codecctx); av_frame_free(&ncv->frame); + av_frame_free(&ncv->oframe); avcodec_parameters_free(&ncv->cparams); sws_freeContext(ncv->swsctx); av_packet_free(&ncv->packet); @@ -109,31 +99,28 @@ print_frame_summary(nc->codecctx, nc->frame); fprintf(stderr, "Error retrieving swsctx (%s)\n", av_err2str(*averr)); return NULL; } - AVFrame* oframe = av_frame_alloc(); - if(oframe == NULL){ - fprintf(stderr, "Couldn't allocate output frame\n"); - return NULL; - } - oframe->format = AV_PIX_FMT_RGB24; - oframe->width = nc->dstwidth; - oframe->height = nc->dstheight; - if((*averr = av_image_alloc(oframe->data, oframe->linesize, oframe->width, oframe->height, - oframe->format, IMGALLOCALIGN)) < 0){ + nc->oframe->format = AV_PIX_FMT_RGB24; + nc->oframe->width = nc->dstwidth; + nc->oframe->height = nc->dstheight; + if((*averr = av_image_alloc(nc->oframe->data, nc->oframe->linesize, + nc->oframe->width, nc->oframe->height, + nc->oframe->format, IMGALLOCALIGN)) < 0){ fprintf(stderr, "Error allocating visual data (%s)\n", av_err2str(*averr)); - av_frame_free(&oframe); + av_frame_free(&nc->oframe); return NULL; } -print_frame_summary(nc->codecctx, oframe); - *averr = sws_scale(nc->swsctx, (const uint8_t* const*)nc->frame->data, nc->frame->linesize, 0, - nc->frame->height, oframe->data, oframe->linesize); +fprintf(stderr, "ALLOCATED %d BYTES\n", *averr); + *averr = sws_scale(nc->swsctx, (const uint8_t* const*)nc->frame->data, + nc->frame->linesize, 0, + nc->frame->height, nc->oframe->data, nc->oframe->linesize); if(*averr < 0){ fprintf(stderr, "Error applying scaling (%s)\n", av_err2str(*averr)); - av_frame_free(&oframe); + av_frame_free(&nc->oframe); return NULL; } -print_frame_summary(nc->codecctx, oframe); +print_frame_summary(nc->codecctx, nc->oframe); #undef IMGALLOCALIGN - return oframe; + return nc->oframe; } ncvisual* ncplane_visual_open(struct ncplane* nc, const char* filename, int* averr){ @@ -144,6 +131,7 @@ ncvisual* ncplane_visual_open(struct ncplane* nc, const char* filename, int* ave return NULL; } memset(ncv, 0, sizeof(*ncv)); + ncv->ncp = nc; ncplane_dim_yx(nc, &ncv->dstheight, &ncv->dstwidth); *averr = avformat_open_input(&ncv->fmtctx, filename, NULL, NULL); if(*averr < 0){ @@ -203,6 +191,11 @@ ncvisual* ncplane_visual_open(struct ncplane* nc, const char* filename, int* ave *averr = AVERROR(ENOMEM); goto err; } + if((ncv->oframe = av_frame_alloc()) == NULL){ + fprintf(stderr, "Couldn't allocate output frame for %s\n", filename); + *averr = AVERROR(ENOMEM); + goto err; + } return ncv; err: diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index bca65eace..3f90375ca 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -12,10 +12,13 @@ #include #include #include +#include +#include #include #include #include #include "notcurses.h" +#include "internal.h" #include "timespec.h" #include "version.h" #include "egcpool.h" @@ -1531,3 +1534,32 @@ int notcurses_getc_blocking(const notcurses* nc, cell* c, ncspecial_key* special } return -1; } + +int ncvisual_render(const ncvisual* ncv){ + const AVFrame* f = ncv->frame; + if(f == NULL){ + return -1; + } + int x, y; + int dimy, dimx; + ncplane_dim_yx(ncv->ncp, &dimy, &dimx); + ncplane_cursor_move_yx(ncv->ncp, 0, 0); + for(y = 0 ; y < f->height && y < dimy ; ++y){ + for(x = 0 ; x < f->width && x < dimx ; ++x){ + const unsigned char* rgbbase = ((const unsigned char*)f->data) + (f->width * y) + x; + fprintf(stderr, "[%04d/%04d] %02x %02x %02x\n", y, x, + rgbbase[0], rgbbase[1], rgbbase[2]); + cell c = CELL_TRIVIAL_INITIALIZER; + if(cell_load(ncv->ncp, &c, "▓") <= 0){ + return -1; + } + cell_set_fg(&c, rgbbase[0], rgbbase[1], rgbbase[2]); + if(ncplane_putc(ncv->ncp, &c) <= 0){ + cell_release(ncv->ncp, &c); + return -1; + } + cell_release(ncv->ncp, &c); + } + } + return 0; +} diff --git a/src/view/view.cpp b/src/view/view.cpp index c627b6722..48ccc7e44 100644 --- a/src/view/view.cpp +++ b/src/view/view.cpp @@ -1,6 +1,8 @@ #include #include +#include #include +#include #include #include "notcurses.h" @@ -14,12 +16,20 @@ void usage(std::ostream& o, const char* name, int exitcode){ int ncview(struct notcurses* nc, struct ncvisual* ncv, int* averr){ struct ncplane* n = notcurses_stdplane(nc); - int frame = 0; + int frame = 1; AVFrame* avf; while( (avf = ncvisual_decode(ncv, averr)) ){ ncplane_cursor_move_yx(n, 0, 0); ncplane_printf(n, "Got frame %05d\u2026", frame); + if(ncvisual_render(ncv)){ +fprintf(stderr, "couldn't render, fuck!\n"); + return -1; + } + if(notcurses_render(nc)){ + return -1; + } ++frame; + sleep(100); } if(*averr == AVERROR_EOF){ return 0; @@ -28,38 +38,42 @@ int ncview(struct notcurses* nc, struct ncvisual* ncv, int* averr){ } int main(int argc, char** argv){ + setlocale(LC_ALL, ""); if(argc == 1){ usage(std::cerr, argv[0], EXIT_FAILURE); } notcurses_options opts{}; opts.outfp = stdout; - bool success = true; auto nc = notcurses_init(&opts); if(nc == nullptr){ - return -1; + return EXIT_FAILURE; + } + int dimy, dimx; + notcurses_term_dim_yx(nc, &dimy, &dimx); + auto ncp = notcurses_newplane(nc, dimy - 1, dimx, 1, 0, nullptr); + if(ncp == nullptr){ + notcurses_stop(nc); + return EXIT_FAILURE; } - auto ncp = notcurses_stdplane(nc); for(int i = 1 ; i < argc ; ++i){ std::array errbuf; int averr; auto ncv = ncplane_visual_open(ncp, argv[i], &averr); if(ncv == nullptr){ av_make_error_string(errbuf.data(), errbuf.size(), averr); + notcurses_stop(nc); std::cerr << "Error opening " << argv[i] << ": " << errbuf.data() << std::endl; - success = false; - continue; + return EXIT_FAILURE; } if(ncview(nc, ncv, &averr)){ av_make_error_string(errbuf.data(), errbuf.size(), averr); + notcurses_stop(nc); std::cerr << "Error decoding " << argv[i] << ": " << errbuf.data() << std::endl; - success = false; + return EXIT_FAILURE; } ncvisual_destroy(ncv); } if(notcurses_stop(nc)){ - success = false; - } - if(!success){ return EXIT_FAILURE; } return EXIT_SUCCESS; diff --git a/tests/egcpool.cpp b/tests/egcpool.cpp index 4e1dbd439..d5a2eec9b 100644 --- a/tests/egcpool.cpp +++ b/tests/egcpool.cpp @@ -28,6 +28,10 @@ TEST_F(EGCPoolTest, UTF8EGC) { auto ulen = utf8_egc_len(wstr, &c); ASSERT_LT(0, ulen); EXPECT_LT(0, c); + wstr = "▓"; + ulen = utf8_egc_len(wstr, &c); + ASSERT_LT(0, ulen); + EXPECT_LT(0, c); } // we're gonna run both a composed latin a with grave, and then a latin a with diff --git a/tests/libav.cpp b/tests/libav.cpp index a3c7458f9..867932262 100644 --- a/tests/libav.cpp +++ b/tests/libav.cpp @@ -31,11 +31,16 @@ class LibavTest : public :: testing::Test { TEST_F(LibavTest, LoadImage) { int averr; + int dimy, dimx; + ncplane_dim_yx(ncp_, &dimy, &dimx); auto ncv = ncplane_visual_open(ncp_, "../tests/dsscaw-purp.png", &averr); EXPECT_EQ(0, averr); ASSERT_NE(nullptr, ncv); - ASSERT_NE(nullptr, ncvisual_decode(ncv, &averr)); + auto frame = ncvisual_decode(ncv, &averr); + ASSERT_NE(nullptr, frame); EXPECT_EQ(AVERROR_EOF, averr); + EXPECT_EQ(dimy, frame->height); + EXPECT_EQ(dimx, frame->width); ncvisual_destroy(ncv); }