[FFmpeg-devel] [PATCH] life: add slow_death, life_color and death_color options.

Clément Bœsch ubitux at gmail.com
Wed Dec 7 07:51:55 CET 2011


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

> [...]
> 
> >  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.
-------------- next part --------------
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
+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);
     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);
+                } 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;
     avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
     return 0;
 }
-- 
1.7.8

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 490 bytes
Desc: not available
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20111207/1b5217fe/attachment.asc>


More information about the ffmpeg-devel mailing list