[FFmpeg-devel] [PATCH] life: add slow_death, life_color and death_color options.
Stefano Sabatini
stefasab at gmail.com
Thu Dec 8 00:29:52 CET 2011
On date Wednesday 2011-12-07 07:51:55 +0100, Clément Bœsch encoded:
> On Tue, Dec 06, 2011 at 03:18:37PM +0100, Stefano Sabatini wrote:
> > On date Monday 2011-12-05 00:48:47 +0100, Clément Bœsch encoded:
> > > ---
> > > Additionally to the example, here is a more organic one:
> > > ffplay -f lavfi life=f=CREDITS:s=400x400:slow_death=1:r=120:death_color=#113366:life_color=#ff0000,boxblur=2:2
> > > ---
> > > doc/filters.texi | 17 ++++++++++
> > > libavfilter/vsrc_life.c | 82 +++++++++++++++++++++++++++++++---------------
> > > 2 files changed, 72 insertions(+), 27 deletions(-)
> > >
> > > diff --git a/doc/filters.texi b/doc/filters.texi
> > > index eb1a4df..5e38b08 100644
> > > --- a/doc/filters.texi
> > > +++ b/doc/filters.texi
> > > @@ -2863,6 +2863,17 @@ If a filename is not specified, the size value defaults to "320x240"
> > > @item stitch
> > > If set to 1, stitch the left and right grid edges together, and the
> > > top and bottom edges also. Defaults to 1.
> > > +
> > > + at item slow_death
> > > +If set to 1, the progressive death effect will be effective: the dead cells
> > > +will slightly go from @option{death_color} to black.
> > > +
> > > + at item life_color
> > > +Color of a living cell.
> > > +
> > > + at item death_color
> > > +Color of a dead cell. If @option{slow_death} is set, this is the first color
> > > +used to represent a dead cell.
> > > @end table
> >
> > nit: sort items
> >
>
> I'm not sure about that; life_color and death_color won't be together. I
> could rename them to something like color0 and color1, but then they will
> be the first option user will see (while they are less important than the
> options we can see in the current list).
I'm still slightly in favor of sorted items so that I know where to
put options if I add them, but that's bikeshed so as you prefer.
>
> > [...]
> >
> > > static int query_formats(AVFilterContext *ctx)
> > > {
> > > - static const enum PixelFormat pix_fmts[] = { PIX_FMT_MONOBLACK, PIX_FMT_NONE };
> > > + static const enum PixelFormat pix_fmts[] = { PIX_FMT_RGB24, PIX_FMT_NONE };
> > > avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
> > > return 0;
> >
> > I'm not overly happy with this, indeed I liked the idea of keeping
> > this a mono B/W source, please check if making the output format
> > conditional (depending on slow_death&co.) would complicate the code
> > so much.
>
> Seems not much trouble. New patch attached.
>
> --
> Clément B.
> From c0e0355682330844f78d560f287721a1cb9ffa57 Mon Sep 17 00:00:00 2001
> From: =?UTF-8?q?Cl=C3=A9ment=20B=C5=93sch?= <ubitux at gmail.com>
> Date: Mon, 5 Dec 2011 00:38:39 +0100
> Subject: [PATCH] life: add slow_death, life_color and death_color options.
>
> ---
> doc/filters.texi | 16 ++++++++
> libavfilter/vsrc_life.c | 98 +++++++++++++++++++++++++++++++++++++---------
> 2 files changed, 95 insertions(+), 19 deletions(-)
>
> diff --git a/doc/filters.texi b/doc/filters.texi
> index eb1a4df..0e5bda7 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -2863,6 +2863,17 @@ If a filename is not specified, the size value defaults to "320x240"
> @item stitch
> If set to 1, stitch the left and right grid edges together, and the
> top and bottom edges also. Defaults to 1.
> +
> + at item slow_death
> +If set to 1, the progressive death effect will be effective: the dead cells
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
not very clear, I suggest "enable slow death effect:"
> +will slightly go from @option{death_color} to black.
> +
> + at item life_color
> +Color of a living (or new born) cell.
> +
> + at item death_color
> +Color of a dead cell. If @option{slow_death} is set, this is the first color
> +used to represent a dead cell.
> @end table
>
> @subsection Examples
> @@ -2887,6 +2898,11 @@ Specify a custom rule for evolving a randomly generated grid:
> life=rule=S14/B34
> @end example
>
> + at item
> +Full example with slow death effect using ffplay:
> + at example
> +ffplay -f lavfi life=s=300x200:slow_death=1:r=60:ratio=0.1:death_color=#C83232:life_color=#00ff00,scale=1200:800:flags=16
> + at end example
> @end itemize
>
> @section nullsrc, rgbtestsrc, testsrc
> diff --git a/libavfilter/vsrc_life.c b/libavfilter/vsrc_life.c
> index ce0eecc..6c8206a 100644
> --- a/libavfilter/vsrc_life.c
> +++ b/libavfilter/vsrc_life.c
> @@ -50,7 +50,13 @@ typedef struct {
> double random_fill_ratio;
> uint32_t random_seed;
> int stitch;
> + int slow_death;
> + char *life_color_str;
> + char *death_color_str;
> + uint8_t life_color[4];
> + uint8_t death_color[4];
> AVLFG lfg;
> + void (*draw)(AVFilterContext*, AVFilterBufferRef*);
> } LifeContext;
>
> #define OFFSET(x) offsetof(LifeContext, x)
> @@ -68,6 +74,9 @@ static const AVOption life_options[] = {
> { "random_seed", "set the seed for filling the initial grid randomly", OFFSET(random_seed), AV_OPT_TYPE_INT, {.dbl=-1}, -1, UINT32_MAX },
> { "seed", "set the seed for filling the initial grid randomly", OFFSET(random_seed), AV_OPT_TYPE_INT, {.dbl=-1}, -1, UINT32_MAX },
> { "stitch", "stitch boundaries", OFFSET(stitch), AV_OPT_TYPE_INT, {.dbl=1}, 0, 1 },
> + { "slow_death", "set progressive death effect", OFFSET(slow_death), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1 },
> + { "life_color", "set life color", OFFSET( life_color_str), AV_OPT_TYPE_STRING, {.str="white"}, CHAR_MIN, CHAR_MAX },
> + { "death_color", "set death color", OFFSET(death_color_str), AV_OPT_TYPE_STRING, {.str="black"}, CHAR_MIN, CHAR_MAX },
> { NULL },
> };
>
> @@ -135,7 +144,7 @@ static void show_life_grid(AVFilterContext *ctx)
> return;
> for (i = 0; i < life->h; i++) {
> for (j = 0; j < life->w; j++)
> - line[j] = life->buf[life->buf_idx][i*life->w + j] ? '@' : ' ';
> + line[j] = life->buf[life->buf_idx][i*life->w + j] ? ' ' : '@';
> line[j] = 0;
> av_log(ctx, AV_LOG_DEBUG, "%3d: %s\n", i, line);
> }
> @@ -186,13 +195,14 @@ static int init_pattern_from_file(AVFilterContext *ctx)
>
> /* fill buf[0] */
> p = life->file_buf;
> + memset(life->buf[0], 0xff, life->w * life->h);
why the inverted logic? My more natural interpretation is that alive
cells should have non-zero values, decreasing to zero as they die.
> for (i0 = 0, i = (life->h - h)/2; i0 < h; i0++, i++) {
> for (j = (life->w - max_w)/2;; j++) {
> av_log(ctx, AV_LOG_DEBUG, "%d:%d %c\n", i, j, *p == '\n' ? 'N' : *p);
> if (*p == '\n') {
> p++; break;
> } else
> - life->buf[0][i*life->w + j] = !!isgraph(*(p++));
> + life->buf[0][i*life->w + j] = !isgraph(*(p++)) * 0xff;
> }
> }
> life->buf_idx = 0;
> @@ -231,6 +241,16 @@ static int init(AVFilterContext *ctx, const char *args, void *opaque)
> if ((ret = parse_rule(&life->born_rule, &life->stay_rule, life->rule_str, ctx)) < 0)
> return ret;
>
> + if ((ret = av_parse_color(life->life_color, life->life_color_str, -1, ctx))) {
> + av_log(ctx, AV_LOG_ERROR, "Invalid life color '%s'\n", life->life_color_str);
> + return ret;
> + }
> +
> + if ((ret = av_parse_color(life->death_color, life->death_color_str, -1, ctx))) {
> + av_log(ctx, AV_LOG_ERROR, "Invalid death color '%s'\n", life->death_color_str);
> + return ret;
> + }
> +
> life->time_base.num = frame_rate.den;
> life->time_base.den = frame_rate.num;
>
> @@ -251,14 +271,15 @@ static int init(AVFilterContext *ctx, const char *args, void *opaque)
>
> for (i = 0; i < life->w * life->h; i++) {
> double r = (double)av_lfg_get(&life->lfg) / UINT32_MAX;
> - if (r <= life->random_fill_ratio)
> - life->buf[0][i] = 1;
> + if (r > life->random_fill_ratio)
> + life->buf[0][i] = 0xff; // dead
> }
> life->buf_idx = 0;
> } else {
> if ((ret = init_pattern_from_file(ctx)) < 0)
> return ret;
> }
> + memcpy(life->buf[1], life->buf[0], life->w * life->h);
>
> av_log(ctx, AV_LOG_INFO,
> "s:%dx%d r:%d/%d rule:%s stay_rule:%d born_rule:%d stitch:%d\n",
> @@ -322,24 +343,25 @@ static void evolve(AVFilterContext *ctx)
> }
>
> /* compute the number of live neighbor cells */
> - n = (pos[NW][0] == -1 || pos[NW][1] == -1 ? 0 : oldbuf[pos[NW][0]*life->w + pos[NW][1]]) +
> - (pos[N ][0] == -1 || pos[N ][1] == -1 ? 0 : oldbuf[pos[N ][0]*life->w + pos[N ][1]]) +
> - (pos[NE][0] == -1 || pos[NE][1] == -1 ? 0 : oldbuf[pos[NE][0]*life->w + pos[NE][1]]) +
> - (pos[W ][0] == -1 || pos[W ][1] == -1 ? 0 : oldbuf[pos[W ][0]*life->w + pos[W ][1]]) +
> - (pos[E ][0] == -1 || pos[E ][1] == -1 ? 0 : oldbuf[pos[E ][0]*life->w + pos[E ][1]]) +
> - (pos[SW][0] == -1 || pos[SW][1] == -1 ? 0 : oldbuf[pos[SW][0]*life->w + pos[SW][1]]) +
> - (pos[S ][0] == -1 || pos[S ][1] == -1 ? 0 : oldbuf[pos[S ][0]*life->w + pos[S ][1]]) +
> - (pos[SE][0] == -1 || pos[SE][1] == -1 ? 0 : oldbuf[pos[SE][0]*life->w + pos[SE][1]]);
> - v = !!(1<<n & (oldbuf[i*life->w + j] ? life->stay_rule : life->born_rule));
> - av_dlog(ctx, "i:%d j:%d live_neighbors:%d cell:%d -> cell:%d\n", i, j, n, oldbuf[i*life->w + j], v);
> - newbuf[i*life->w+j] = v;
> + n = (pos[NW][0] == -1 || pos[NW][1] == -1 ? 0 : !oldbuf[pos[NW][0]*life->w + pos[NW][1]]) +
> + (pos[N ][0] == -1 || pos[N ][1] == -1 ? 0 : !oldbuf[pos[N ][0]*life->w + pos[N ][1]]) +
> + (pos[NE][0] == -1 || pos[NE][1] == -1 ? 0 : !oldbuf[pos[NE][0]*life->w + pos[NE][1]]) +
> + (pos[W ][0] == -1 || pos[W ][1] == -1 ? 0 : !oldbuf[pos[W ][0]*life->w + pos[W ][1]]) +
> + (pos[E ][0] == -1 || pos[E ][1] == -1 ? 0 : !oldbuf[pos[E ][0]*life->w + pos[E ][1]]) +
> + (pos[SW][0] == -1 || pos[SW][1] == -1 ? 0 : !oldbuf[pos[SW][0]*life->w + pos[SW][1]]) +
> + (pos[S ][0] == -1 || pos[S ][1] == -1 ? 0 : !oldbuf[pos[S ][0]*life->w + pos[S ][1]]) +
> + (pos[SE][0] == -1 || pos[SE][1] == -1 ? 0 : !oldbuf[pos[SE][0]*life->w + pos[SE][1]]);
> + v = 1<<n & (!oldbuf[i*life->w + j] ? life->stay_rule : life->born_rule);
> + *newbuf = v ? 0 : *newbuf + (*newbuf < 0xff);
> + av_dlog(ctx, "i:%d j:%d live_neighbors:%d cell:%d -> cell:%d\n", i, j, n, oldbuf[i*life->w + j], *newbuf);
> + newbuf++;
> }
> }
>
> life->buf_idx = !life->buf_idx;
> }
>
> -static void fill_picture(AVFilterContext *ctx, AVFilterBufferRef *picref)
> +static void fill_picture_monoblack(AVFilterContext *ctx, AVFilterBufferRef *picref)
> {
> LifeContext *life = ctx->priv;
> uint8_t *buf = life->buf[life->buf_idx];
> @@ -350,7 +372,7 @@ static void fill_picture(AVFilterContext *ctx, AVFilterBufferRef *picref)
> uint8_t byte = 0;
> uint8_t *p = picref->data[0] + i * picref->linesize[0];
> for (k = 0, j = 0; j < life->w; j++) {
> - byte |= buf[i*life->w+j]<<(7-k++);
> + byte |= (!buf[i*life->w+j])<<(7-k++);
> if (k==8 || j == life->w-1) {
> k = 0;
> *p++ = byte;
> @@ -360,6 +382,36 @@ static void fill_picture(AVFilterContext *ctx, AVFilterBufferRef *picref)
> }
> }
>
> +static void fill_picture_rgb(AVFilterContext *ctx, AVFilterBufferRef *picref)
> +{
> + LifeContext *life = ctx->priv;
> + uint8_t *buf = life->buf[life->buf_idx];
> + int i, j;
> +
> + /* fill the output picture with the old grid buffer */
> + for (i = 0; i < life->h; i++) {
> + uint8_t *p = picref->data[0] + i * picref->linesize[0];
> + for (j = 0; j < life->w; j++) {
> + uint8_t death_age = buf[i*life->w + j];
> + if (death_age == 0) {
> + *p++ = life->life_color[0];
> + *p++ = life->life_color[1];
> + *p++ = life->life_color[2];
> + } else {
> + if (life->slow_death) {
> + *p++ = FFMAX((int)life->death_color[0] - death_age, 0);
> + *p++ = FFMAX((int)life->death_color[1] - death_age, 0);
> + *p++ = FFMAX((int)life->death_color[2] - death_age, 0);
wht about interpolating between life and death color? In this case
you may also want to define how fast a cell dies.
> + } else {
> + *p++ = life->death_color[0];
> + *p++ = life->death_color[1];
> + *p++ = life->death_color[2];
> + }
> + }
> + }
> + }
> +}
> +
> static int request_frame(AVFilterLink *outlink)
> {
> LifeContext *life = outlink->src->priv;
> @@ -368,7 +420,7 @@ static int request_frame(AVFilterLink *outlink)
> picref->pts = life->pts++;
> picref->pos = -1;
>
> - fill_picture(outlink->src, picref);
> + life->draw(outlink->src, picref);
> evolve(outlink->src);
> #ifdef DEBUG
> show_life_grid(outlink->src);
> @@ -384,7 +436,15 @@ static int request_frame(AVFilterLink *outlink)
>
> static int query_formats(AVFilterContext *ctx)
> {
> - static const enum PixelFormat pix_fmts[] = { PIX_FMT_MONOBLACK, PIX_FMT_NONE };
> + LifeContext *life = ctx->priv;
> + enum PixelFormat pix_fmts[] = { PIX_FMT_MONOBLACK, PIX_FMT_NONE };
> + if ( life->slow_death
> + || memcmp(life-> life_color, "\xff\xff\xff", 3)
> + || memcmp(life->death_color, "\x00\x00\x00", 3)) {
> + pix_fmts[0] = PIX_FMT_RGB24;
> + life->draw = fill_picture_rgb;
> + } else
> + life->draw = fill_picture_monoblack;
Nit: set pix_fmts[] = { PIX_FMT_NONE, PIX_FMT_NONE } and set
pix_fmts[0] = MONOB explicitely.
> avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
> return 0;
--
FFmpeg = Fantastic and Free Merciful Philosophical Enchanting Gospel
More information about the ffmpeg-devel
mailing list