yading@11
|
1 /*
|
yading@11
|
2 * Copyright (c) Stefano Sabatini 2010
|
yading@11
|
3 *
|
yading@11
|
4 * This file is part of FFmpeg.
|
yading@11
|
5 *
|
yading@11
|
6 * FFmpeg is free software; you can redistribute it and/or
|
yading@11
|
7 * modify it under the terms of the GNU Lesser General Public
|
yading@11
|
8 * License as published by the Free Software Foundation; either
|
yading@11
|
9 * version 2.1 of the License, or (at your option) any later version.
|
yading@11
|
10 *
|
yading@11
|
11 * FFmpeg is distributed in the hope that it will be useful,
|
yading@11
|
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
yading@11
|
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
yading@11
|
14 * Lesser General Public License for more details.
|
yading@11
|
15 *
|
yading@11
|
16 * You should have received a copy of the GNU Lesser General Public
|
yading@11
|
17 * License along with FFmpeg; if not, write to the Free Software
|
yading@11
|
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
yading@11
|
19 */
|
yading@11
|
20
|
yading@11
|
21 /**
|
yading@11
|
22 * @file
|
yading@11
|
23 * life video source, based on John Conways' Life Game
|
yading@11
|
24 */
|
yading@11
|
25
|
yading@11
|
26 /* #define DEBUG */
|
yading@11
|
27
|
yading@11
|
28 #include "libavutil/file.h"
|
yading@11
|
29 #include "libavutil/intreadwrite.h"
|
yading@11
|
30 #include "libavutil/lfg.h"
|
yading@11
|
31 #include "libavutil/opt.h"
|
yading@11
|
32 #include "libavutil/parseutils.h"
|
yading@11
|
33 #include "libavutil/random_seed.h"
|
yading@11
|
34 #include "libavutil/avstring.h"
|
yading@11
|
35 #include "avfilter.h"
|
yading@11
|
36 #include "internal.h"
|
yading@11
|
37 #include "formats.h"
|
yading@11
|
38 #include "video.h"
|
yading@11
|
39
|
yading@11
|
40 typedef struct {
|
yading@11
|
41 const AVClass *class;
|
yading@11
|
42 int w, h;
|
yading@11
|
43 char *filename;
|
yading@11
|
44 char *rule_str;
|
yading@11
|
45 uint8_t *file_buf;
|
yading@11
|
46 size_t file_bufsize;
|
yading@11
|
47
|
yading@11
|
48 /**
|
yading@11
|
49 * The two grid state buffers.
|
yading@11
|
50 *
|
yading@11
|
51 * A 0xFF (ALIVE_CELL) value means the cell is alive (or new born), while
|
yading@11
|
52 * the decreasing values from 0xFE to 0 means the cell is dead; the range
|
yading@11
|
53 * of values is used for the slow death effect, or mold (0xFE means dead,
|
yading@11
|
54 * 0xFD means very dead, 0xFC means very very dead... and 0x00 means
|
yading@11
|
55 * definitely dead/mold).
|
yading@11
|
56 */
|
yading@11
|
57 uint8_t *buf[2];
|
yading@11
|
58
|
yading@11
|
59 uint8_t buf_idx;
|
yading@11
|
60 uint16_t stay_rule; ///< encode the behavior for filled cells
|
yading@11
|
61 uint16_t born_rule; ///< encode the behavior for empty cells
|
yading@11
|
62 uint64_t pts;
|
yading@11
|
63 AVRational frame_rate;
|
yading@11
|
64 double random_fill_ratio;
|
yading@11
|
65 uint32_t random_seed;
|
yading@11
|
66 int stitch;
|
yading@11
|
67 int mold;
|
yading@11
|
68 char *life_color_str;
|
yading@11
|
69 char *death_color_str;
|
yading@11
|
70 char *mold_color_str;
|
yading@11
|
71 uint8_t life_color[4];
|
yading@11
|
72 uint8_t death_color[4];
|
yading@11
|
73 uint8_t mold_color[4];
|
yading@11
|
74 AVLFG lfg;
|
yading@11
|
75 void (*draw)(AVFilterContext*, AVFrame*);
|
yading@11
|
76 } LifeContext;
|
yading@11
|
77
|
yading@11
|
78 #define ALIVE_CELL 0xFF
|
yading@11
|
79 #define OFFSET(x) offsetof(LifeContext, x)
|
yading@11
|
80 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
|
yading@11
|
81
|
yading@11
|
82 static const AVOption life_options[] = {
|
yading@11
|
83 { "filename", "set source file", OFFSET(filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
|
yading@11
|
84 { "f", "set source file", OFFSET(filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
|
yading@11
|
85 { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, FLAGS },
|
yading@11
|
86 { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, FLAGS },
|
yading@11
|
87 { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, FLAGS },
|
yading@11
|
88 { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, FLAGS },
|
yading@11
|
89 { "rule", "set rule", OFFSET(rule_str), AV_OPT_TYPE_STRING, {.str = "B3/S23"}, CHAR_MIN, CHAR_MAX, FLAGS },
|
yading@11
|
90 { "random_fill_ratio", "set fill ratio for filling initial grid randomly", OFFSET(random_fill_ratio), AV_OPT_TYPE_DOUBLE, {.dbl=1/M_PHI}, 0, 1, FLAGS },
|
yading@11
|
91 { "ratio", "set fill ratio for filling initial grid randomly", OFFSET(random_fill_ratio), AV_OPT_TYPE_DOUBLE, {.dbl=1/M_PHI}, 0, 1, FLAGS },
|
yading@11
|
92 { "random_seed", "set the seed for filling the initial grid randomly", OFFSET(random_seed), AV_OPT_TYPE_INT, {.i64=-1}, -1, UINT32_MAX, FLAGS },
|
yading@11
|
93 { "seed", "set the seed for filling the initial grid randomly", OFFSET(random_seed), AV_OPT_TYPE_INT, {.i64=-1}, -1, UINT32_MAX, FLAGS },
|
yading@11
|
94 { "stitch", "stitch boundaries", OFFSET(stitch), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS },
|
yading@11
|
95 { "mold", "set mold speed for dead cells", OFFSET(mold), AV_OPT_TYPE_INT, {.i64=0}, 0, 0xFF, FLAGS },
|
yading@11
|
96 { "life_color", "set life color", OFFSET( life_color_str), AV_OPT_TYPE_STRING, {.str="white"}, CHAR_MIN, CHAR_MAX, FLAGS },
|
yading@11
|
97 { "death_color", "set death color", OFFSET(death_color_str), AV_OPT_TYPE_STRING, {.str="black"}, CHAR_MIN, CHAR_MAX, FLAGS },
|
yading@11
|
98 { "mold_color", "set mold color", OFFSET( mold_color_str), AV_OPT_TYPE_STRING, {.str="black"}, CHAR_MIN, CHAR_MAX, FLAGS },
|
yading@11
|
99 { NULL },
|
yading@11
|
100 };
|
yading@11
|
101
|
yading@11
|
102 AVFILTER_DEFINE_CLASS(life);
|
yading@11
|
103
|
yading@11
|
104 static int parse_rule(uint16_t *born_rule, uint16_t *stay_rule,
|
yading@11
|
105 const char *rule_str, void *log_ctx)
|
yading@11
|
106 {
|
yading@11
|
107 char *tail;
|
yading@11
|
108 const char *p = rule_str;
|
yading@11
|
109 *born_rule = 0;
|
yading@11
|
110 *stay_rule = 0;
|
yading@11
|
111
|
yading@11
|
112 if (strchr("bBsS", *p)) {
|
yading@11
|
113 /* parse rule as a Born / Stay Alive code, see
|
yading@11
|
114 * http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life */
|
yading@11
|
115 do {
|
yading@11
|
116 uint16_t *rule = (*p == 'b' || *p == 'B') ? born_rule : stay_rule;
|
yading@11
|
117 p++;
|
yading@11
|
118 while (*p >= '0' && *p <= '8') {
|
yading@11
|
119 *rule += 1<<(*p - '0');
|
yading@11
|
120 p++;
|
yading@11
|
121 }
|
yading@11
|
122 if (*p != '/')
|
yading@11
|
123 break;
|
yading@11
|
124 p++;
|
yading@11
|
125 } while (strchr("bBsS", *p));
|
yading@11
|
126
|
yading@11
|
127 if (*p)
|
yading@11
|
128 goto error;
|
yading@11
|
129 } else {
|
yading@11
|
130 /* parse rule as a number, expressed in the form STAY|(BORN<<9),
|
yading@11
|
131 * where STAY and BORN encode the corresponding 9-bits rule */
|
yading@11
|
132 long int rule = strtol(rule_str, &tail, 10);
|
yading@11
|
133 if (*tail)
|
yading@11
|
134 goto error;
|
yading@11
|
135 *born_rule = ((1<<9)-1) & rule;
|
yading@11
|
136 *stay_rule = rule >> 9;
|
yading@11
|
137 }
|
yading@11
|
138
|
yading@11
|
139 return 0;
|
yading@11
|
140
|
yading@11
|
141 error:
|
yading@11
|
142 av_log(log_ctx, AV_LOG_ERROR, "Invalid rule code '%s' provided\n", rule_str);
|
yading@11
|
143 return AVERROR(EINVAL);
|
yading@11
|
144 }
|
yading@11
|
145
|
yading@11
|
146 #ifdef DEBUG
|
yading@11
|
147 static void show_life_grid(AVFilterContext *ctx)
|
yading@11
|
148 {
|
yading@11
|
149 LifeContext *life = ctx->priv;
|
yading@11
|
150 int i, j;
|
yading@11
|
151
|
yading@11
|
152 char *line = av_malloc(life->w + 1);
|
yading@11
|
153 if (!line)
|
yading@11
|
154 return;
|
yading@11
|
155 for (i = 0; i < life->h; i++) {
|
yading@11
|
156 for (j = 0; j < life->w; j++)
|
yading@11
|
157 line[j] = life->buf[life->buf_idx][i*life->w + j] == ALIVE_CELL ? '@' : ' ';
|
yading@11
|
158 line[j] = 0;
|
yading@11
|
159 av_log(ctx, AV_LOG_DEBUG, "%3d: %s\n", i, line);
|
yading@11
|
160 }
|
yading@11
|
161 av_free(line);
|
yading@11
|
162 }
|
yading@11
|
163 #endif
|
yading@11
|
164
|
yading@11
|
165 static int init_pattern_from_file(AVFilterContext *ctx)
|
yading@11
|
166 {
|
yading@11
|
167 LifeContext *life = ctx->priv;
|
yading@11
|
168 char *p;
|
yading@11
|
169 int ret, i, i0, j, h = 0, w, max_w = 0;
|
yading@11
|
170
|
yading@11
|
171 if ((ret = av_file_map(life->filename, &life->file_buf, &life->file_bufsize,
|
yading@11
|
172 0, ctx)) < 0)
|
yading@11
|
173 return ret;
|
yading@11
|
174 av_freep(&life->filename);
|
yading@11
|
175
|
yading@11
|
176 /* prescan file to get the number of lines and the maximum width */
|
yading@11
|
177 w = 0;
|
yading@11
|
178 for (i = 0; i < life->file_bufsize; i++) {
|
yading@11
|
179 if (life->file_buf[i] == '\n') {
|
yading@11
|
180 h++; max_w = FFMAX(w, max_w); w = 0;
|
yading@11
|
181 } else {
|
yading@11
|
182 w++;
|
yading@11
|
183 }
|
yading@11
|
184 }
|
yading@11
|
185 av_log(ctx, AV_LOG_DEBUG, "h:%d max_w:%d\n", h, max_w);
|
yading@11
|
186
|
yading@11
|
187 if (life->w) {
|
yading@11
|
188 if (max_w > life->w || h > life->h) {
|
yading@11
|
189 av_log(ctx, AV_LOG_ERROR,
|
yading@11
|
190 "The specified size is %dx%d which cannot contain the provided file size of %dx%d\n",
|
yading@11
|
191 life->w, life->h, max_w, h);
|
yading@11
|
192 return AVERROR(EINVAL);
|
yading@11
|
193 }
|
yading@11
|
194 } else {
|
yading@11
|
195 /* size was not specified, set it to size of the grid */
|
yading@11
|
196 life->w = max_w;
|
yading@11
|
197 life->h = h;
|
yading@11
|
198 }
|
yading@11
|
199
|
yading@11
|
200 if (!(life->buf[0] = av_mallocz(sizeof(char) * life->h * life->w)) ||
|
yading@11
|
201 !(life->buf[1] = av_mallocz(sizeof(char) * life->h * life->w))) {
|
yading@11
|
202 av_free(life->buf[0]);
|
yading@11
|
203 av_free(life->buf[1]);
|
yading@11
|
204 return AVERROR(ENOMEM);
|
yading@11
|
205 }
|
yading@11
|
206
|
yading@11
|
207 /* fill buf[0] */
|
yading@11
|
208 p = life->file_buf;
|
yading@11
|
209 for (i0 = 0, i = (life->h - h)/2; i0 < h; i0++, i++) {
|
yading@11
|
210 for (j = (life->w - max_w)/2;; j++) {
|
yading@11
|
211 av_log(ctx, AV_LOG_DEBUG, "%d:%d %c\n", i, j, *p == '\n' ? 'N' : *p);
|
yading@11
|
212 if (*p == '\n') {
|
yading@11
|
213 p++; break;
|
yading@11
|
214 } else
|
yading@11
|
215 life->buf[0][i*life->w + j] = av_isgraph(*(p++)) ? ALIVE_CELL : 0;
|
yading@11
|
216 }
|
yading@11
|
217 }
|
yading@11
|
218 life->buf_idx = 0;
|
yading@11
|
219
|
yading@11
|
220 return 0;
|
yading@11
|
221 }
|
yading@11
|
222
|
yading@11
|
223 static int init(AVFilterContext *ctx)
|
yading@11
|
224 {
|
yading@11
|
225 LifeContext *life = ctx->priv;
|
yading@11
|
226 int ret;
|
yading@11
|
227
|
yading@11
|
228 if (!life->w && !life->filename)
|
yading@11
|
229 av_opt_set(life, "size", "320x240", 0);
|
yading@11
|
230
|
yading@11
|
231 if ((ret = parse_rule(&life->born_rule, &life->stay_rule, life->rule_str, ctx)) < 0)
|
yading@11
|
232 return ret;
|
yading@11
|
233
|
yading@11
|
234 #define PARSE_COLOR(name) do { \
|
yading@11
|
235 if ((ret = av_parse_color(life->name ## _color, life->name ## _color_str, -1, ctx))) { \
|
yading@11
|
236 av_log(ctx, AV_LOG_ERROR, "Invalid " #name " color '%s'\n", \
|
yading@11
|
237 life->name ## _color_str); \
|
yading@11
|
238 return ret; \
|
yading@11
|
239 } \
|
yading@11
|
240 av_freep(&life->name ## _color_str); \
|
yading@11
|
241 } while (0)
|
yading@11
|
242
|
yading@11
|
243 PARSE_COLOR(life);
|
yading@11
|
244 PARSE_COLOR(death);
|
yading@11
|
245 PARSE_COLOR(mold);
|
yading@11
|
246
|
yading@11
|
247 if (!life->mold && memcmp(life->mold_color, "\x00\x00\x00", 3))
|
yading@11
|
248 av_log(ctx, AV_LOG_WARNING,
|
yading@11
|
249 "Mold color is set while mold isn't, ignoring the color.\n");
|
yading@11
|
250
|
yading@11
|
251 if (!life->filename) {
|
yading@11
|
252 /* fill the grid randomly */
|
yading@11
|
253 int i;
|
yading@11
|
254
|
yading@11
|
255 if (!(life->buf[0] = av_mallocz(sizeof(char) * life->h * life->w)) ||
|
yading@11
|
256 !(life->buf[1] = av_mallocz(sizeof(char) * life->h * life->w))) {
|
yading@11
|
257 av_free(life->buf[0]);
|
yading@11
|
258 av_free(life->buf[1]);
|
yading@11
|
259 return AVERROR(ENOMEM);
|
yading@11
|
260 }
|
yading@11
|
261 if (life->random_seed == -1)
|
yading@11
|
262 life->random_seed = av_get_random_seed();
|
yading@11
|
263
|
yading@11
|
264 av_lfg_init(&life->lfg, life->random_seed);
|
yading@11
|
265
|
yading@11
|
266 for (i = 0; i < life->w * life->h; i++) {
|
yading@11
|
267 double r = (double)av_lfg_get(&life->lfg) / UINT32_MAX;
|
yading@11
|
268 if (r <= life->random_fill_ratio)
|
yading@11
|
269 life->buf[0][i] = ALIVE_CELL;
|
yading@11
|
270 }
|
yading@11
|
271 life->buf_idx = 0;
|
yading@11
|
272 } else {
|
yading@11
|
273 if ((ret = init_pattern_from_file(ctx)) < 0)
|
yading@11
|
274 return ret;
|
yading@11
|
275 }
|
yading@11
|
276
|
yading@11
|
277 av_log(ctx, AV_LOG_VERBOSE,
|
yading@11
|
278 "s:%dx%d r:%d/%d rule:%s stay_rule:%d born_rule:%d stitch:%d seed:%u\n",
|
yading@11
|
279 life->w, life->h, life->frame_rate.num, life->frame_rate.den,
|
yading@11
|
280 life->rule_str, life->stay_rule, life->born_rule, life->stitch,
|
yading@11
|
281 life->random_seed);
|
yading@11
|
282 return 0;
|
yading@11
|
283 }
|
yading@11
|
284
|
yading@11
|
285 static av_cold void uninit(AVFilterContext *ctx)
|
yading@11
|
286 {
|
yading@11
|
287 LifeContext *life = ctx->priv;
|
yading@11
|
288
|
yading@11
|
289 av_file_unmap(life->file_buf, life->file_bufsize);
|
yading@11
|
290 av_freep(&life->rule_str);
|
yading@11
|
291 av_freep(&life->buf[0]);
|
yading@11
|
292 av_freep(&life->buf[1]);
|
yading@11
|
293 }
|
yading@11
|
294
|
yading@11
|
295 static int config_props(AVFilterLink *outlink)
|
yading@11
|
296 {
|
yading@11
|
297 LifeContext *life = outlink->src->priv;
|
yading@11
|
298
|
yading@11
|
299 outlink->w = life->w;
|
yading@11
|
300 outlink->h = life->h;
|
yading@11
|
301 outlink->time_base = av_inv_q(life->frame_rate);
|
yading@11
|
302
|
yading@11
|
303 return 0;
|
yading@11
|
304 }
|
yading@11
|
305
|
yading@11
|
306 static void evolve(AVFilterContext *ctx)
|
yading@11
|
307 {
|
yading@11
|
308 LifeContext *life = ctx->priv;
|
yading@11
|
309 int i, j;
|
yading@11
|
310 uint8_t *oldbuf = life->buf[ life->buf_idx];
|
yading@11
|
311 uint8_t *newbuf = life->buf[!life->buf_idx];
|
yading@11
|
312
|
yading@11
|
313 enum { NW, N, NE, W, E, SW, S, SE };
|
yading@11
|
314
|
yading@11
|
315 /* evolve the grid */
|
yading@11
|
316 for (i = 0; i < life->h; i++) {
|
yading@11
|
317 for (j = 0; j < life->w; j++) {
|
yading@11
|
318 int pos[8][2], n, alive, cell;
|
yading@11
|
319 if (life->stitch) {
|
yading@11
|
320 pos[NW][0] = (i-1) < 0 ? life->h-1 : i-1; pos[NW][1] = (j-1) < 0 ? life->w-1 : j-1;
|
yading@11
|
321 pos[N ][0] = (i-1) < 0 ? life->h-1 : i-1; pos[N ][1] = j ;
|
yading@11
|
322 pos[NE][0] = (i-1) < 0 ? life->h-1 : i-1; pos[NE][1] = (j+1) == life->w ? 0 : j+1;
|
yading@11
|
323 pos[W ][0] = i ; pos[W ][1] = (j-1) < 0 ? life->w-1 : j-1;
|
yading@11
|
324 pos[E ][0] = i ; pos[E ][1] = (j+1) == life->w ? 0 : j+1;
|
yading@11
|
325 pos[SW][0] = (i+1) == life->h ? 0 : i+1; pos[SW][1] = (j-1) < 0 ? life->w-1 : j-1;
|
yading@11
|
326 pos[S ][0] = (i+1) == life->h ? 0 : i+1; pos[S ][1] = j ;
|
yading@11
|
327 pos[SE][0] = (i+1) == life->h ? 0 : i+1; pos[SE][1] = (j+1) == life->w ? 0 : j+1;
|
yading@11
|
328 } else {
|
yading@11
|
329 pos[NW][0] = (i-1) < 0 ? -1 : i-1; pos[NW][1] = (j-1) < 0 ? -1 : j-1;
|
yading@11
|
330 pos[N ][0] = (i-1) < 0 ? -1 : i-1; pos[N ][1] = j ;
|
yading@11
|
331 pos[NE][0] = (i-1) < 0 ? -1 : i-1; pos[NE][1] = (j+1) == life->w ? -1 : j+1;
|
yading@11
|
332 pos[W ][0] = i ; pos[W ][1] = (j-1) < 0 ? -1 : j-1;
|
yading@11
|
333 pos[E ][0] = i ; pos[E ][1] = (j+1) == life->w ? -1 : j+1;
|
yading@11
|
334 pos[SW][0] = (i+1) == life->h ? -1 : i+1; pos[SW][1] = (j-1) < 0 ? -1 : j-1;
|
yading@11
|
335 pos[S ][0] = (i+1) == life->h ? -1 : i+1; pos[S ][1] = j ;
|
yading@11
|
336 pos[SE][0] = (i+1) == life->h ? -1 : i+1; pos[SE][1] = (j+1) == life->w ? -1 : j+1;
|
yading@11
|
337 }
|
yading@11
|
338
|
yading@11
|
339 /* compute the number of live neighbor cells */
|
yading@11
|
340 n = (pos[NW][0] == -1 || pos[NW][1] == -1 ? 0 : oldbuf[pos[NW][0]*life->w + pos[NW][1]] == ALIVE_CELL) +
|
yading@11
|
341 (pos[N ][0] == -1 || pos[N ][1] == -1 ? 0 : oldbuf[pos[N ][0]*life->w + pos[N ][1]] == ALIVE_CELL) +
|
yading@11
|
342 (pos[NE][0] == -1 || pos[NE][1] == -1 ? 0 : oldbuf[pos[NE][0]*life->w + pos[NE][1]] == ALIVE_CELL) +
|
yading@11
|
343 (pos[W ][0] == -1 || pos[W ][1] == -1 ? 0 : oldbuf[pos[W ][0]*life->w + pos[W ][1]] == ALIVE_CELL) +
|
yading@11
|
344 (pos[E ][0] == -1 || pos[E ][1] == -1 ? 0 : oldbuf[pos[E ][0]*life->w + pos[E ][1]] == ALIVE_CELL) +
|
yading@11
|
345 (pos[SW][0] == -1 || pos[SW][1] == -1 ? 0 : oldbuf[pos[SW][0]*life->w + pos[SW][1]] == ALIVE_CELL) +
|
yading@11
|
346 (pos[S ][0] == -1 || pos[S ][1] == -1 ? 0 : oldbuf[pos[S ][0]*life->w + pos[S ][1]] == ALIVE_CELL) +
|
yading@11
|
347 (pos[SE][0] == -1 || pos[SE][1] == -1 ? 0 : oldbuf[pos[SE][0]*life->w + pos[SE][1]] == ALIVE_CELL);
|
yading@11
|
348 cell = oldbuf[i*life->w + j];
|
yading@11
|
349 alive = 1<<n & (cell == ALIVE_CELL ? life->stay_rule : life->born_rule);
|
yading@11
|
350 if (alive) *newbuf = ALIVE_CELL; // new cell is alive
|
yading@11
|
351 else if (cell) *newbuf = cell - 1; // new cell is dead and in the process of mold
|
yading@11
|
352 else *newbuf = 0; // new cell is definitely dead
|
yading@11
|
353 av_dlog(ctx, "i:%d j:%d live_neighbors:%d cell:%d -> cell:%d\n", i, j, n, cell, *newbuf);
|
yading@11
|
354 newbuf++;
|
yading@11
|
355 }
|
yading@11
|
356 }
|
yading@11
|
357
|
yading@11
|
358 life->buf_idx = !life->buf_idx;
|
yading@11
|
359 }
|
yading@11
|
360
|
yading@11
|
361 static void fill_picture_monoblack(AVFilterContext *ctx, AVFrame *picref)
|
yading@11
|
362 {
|
yading@11
|
363 LifeContext *life = ctx->priv;
|
yading@11
|
364 uint8_t *buf = life->buf[life->buf_idx];
|
yading@11
|
365 int i, j, k;
|
yading@11
|
366
|
yading@11
|
367 /* fill the output picture with the old grid buffer */
|
yading@11
|
368 for (i = 0; i < life->h; i++) {
|
yading@11
|
369 uint8_t byte = 0;
|
yading@11
|
370 uint8_t *p = picref->data[0] + i * picref->linesize[0];
|
yading@11
|
371 for (k = 0, j = 0; j < life->w; j++) {
|
yading@11
|
372 byte |= (buf[i*life->w+j] == ALIVE_CELL)<<(7-k++);
|
yading@11
|
373 if (k==8 || j == life->w-1) {
|
yading@11
|
374 k = 0;
|
yading@11
|
375 *p++ = byte;
|
yading@11
|
376 byte = 0;
|
yading@11
|
377 }
|
yading@11
|
378 }
|
yading@11
|
379 }
|
yading@11
|
380 }
|
yading@11
|
381
|
yading@11
|
382 // divide by 255 and round to nearest
|
yading@11
|
383 // apply a fast variant: (X+127)/255 = ((X+127)*257+257)>>16 = ((X+128)*257)>>16
|
yading@11
|
384 #define FAST_DIV255(x) ((((x) + 128) * 257) >> 16)
|
yading@11
|
385
|
yading@11
|
386 static void fill_picture_rgb(AVFilterContext *ctx, AVFrame *picref)
|
yading@11
|
387 {
|
yading@11
|
388 LifeContext *life = ctx->priv;
|
yading@11
|
389 uint8_t *buf = life->buf[life->buf_idx];
|
yading@11
|
390 int i, j;
|
yading@11
|
391
|
yading@11
|
392 /* fill the output picture with the old grid buffer */
|
yading@11
|
393 for (i = 0; i < life->h; i++) {
|
yading@11
|
394 uint8_t *p = picref->data[0] + i * picref->linesize[0];
|
yading@11
|
395 for (j = 0; j < life->w; j++) {
|
yading@11
|
396 uint8_t v = buf[i*life->w + j];
|
yading@11
|
397 if (life->mold && v != ALIVE_CELL) {
|
yading@11
|
398 const uint8_t *c1 = life-> mold_color;
|
yading@11
|
399 const uint8_t *c2 = life->death_color;
|
yading@11
|
400 int death_age = FFMIN((0xff - v) * life->mold, 0xff);
|
yading@11
|
401 *p++ = FAST_DIV255((c2[0] << 8) + ((int)c1[0] - (int)c2[0]) * death_age);
|
yading@11
|
402 *p++ = FAST_DIV255((c2[1] << 8) + ((int)c1[1] - (int)c2[1]) * death_age);
|
yading@11
|
403 *p++ = FAST_DIV255((c2[2] << 8) + ((int)c1[2] - (int)c2[2]) * death_age);
|
yading@11
|
404 } else {
|
yading@11
|
405 const uint8_t *c = v == ALIVE_CELL ? life->life_color : life->death_color;
|
yading@11
|
406 AV_WB24(p, c[0]<<16 | c[1]<<8 | c[2]);
|
yading@11
|
407 p += 3;
|
yading@11
|
408 }
|
yading@11
|
409 }
|
yading@11
|
410 }
|
yading@11
|
411 }
|
yading@11
|
412
|
yading@11
|
413 static int request_frame(AVFilterLink *outlink)
|
yading@11
|
414 {
|
yading@11
|
415 LifeContext *life = outlink->src->priv;
|
yading@11
|
416 AVFrame *picref = ff_get_video_buffer(outlink, life->w, life->h);
|
yading@11
|
417 if (!picref)
|
yading@11
|
418 return AVERROR(ENOMEM);
|
yading@11
|
419 picref->sample_aspect_ratio = (AVRational) {1, 1};
|
yading@11
|
420 picref->pts = life->pts++;
|
yading@11
|
421
|
yading@11
|
422 life->draw(outlink->src, picref);
|
yading@11
|
423 evolve(outlink->src);
|
yading@11
|
424 #ifdef DEBUG
|
yading@11
|
425 show_life_grid(outlink->src);
|
yading@11
|
426 #endif
|
yading@11
|
427 return ff_filter_frame(outlink, picref);
|
yading@11
|
428 }
|
yading@11
|
429
|
yading@11
|
430 static int query_formats(AVFilterContext *ctx)
|
yading@11
|
431 {
|
yading@11
|
432 LifeContext *life = ctx->priv;
|
yading@11
|
433 enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_NONE, AV_PIX_FMT_NONE };
|
yading@11
|
434 if (life->mold || memcmp(life-> life_color, "\xff\xff\xff", 3)
|
yading@11
|
435 || memcmp(life->death_color, "\x00\x00\x00", 3)) {
|
yading@11
|
436 pix_fmts[0] = AV_PIX_FMT_RGB24;
|
yading@11
|
437 life->draw = fill_picture_rgb;
|
yading@11
|
438 } else {
|
yading@11
|
439 pix_fmts[0] = AV_PIX_FMT_MONOBLACK;
|
yading@11
|
440 life->draw = fill_picture_monoblack;
|
yading@11
|
441 }
|
yading@11
|
442 ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
|
yading@11
|
443 return 0;
|
yading@11
|
444 }
|
yading@11
|
445
|
yading@11
|
446 static const AVFilterPad life_outputs[] = {
|
yading@11
|
447 {
|
yading@11
|
448 .name = "default",
|
yading@11
|
449 .type = AVMEDIA_TYPE_VIDEO,
|
yading@11
|
450 .request_frame = request_frame,
|
yading@11
|
451 .config_props = config_props,
|
yading@11
|
452 },
|
yading@11
|
453 { NULL}
|
yading@11
|
454 };
|
yading@11
|
455
|
yading@11
|
456 AVFilter avfilter_vsrc_life = {
|
yading@11
|
457 .name = "life",
|
yading@11
|
458 .description = NULL_IF_CONFIG_SMALL("Create life."),
|
yading@11
|
459 .priv_size = sizeof(LifeContext),
|
yading@11
|
460 .init = init,
|
yading@11
|
461 .uninit = uninit,
|
yading@11
|
462 .query_formats = query_formats,
|
yading@11
|
463 .inputs = NULL,
|
yading@11
|
464 .outputs = life_outputs,
|
yading@11
|
465 .priv_class = &life_class,
|
yading@11
|
466 };
|