00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00029 #include "avfilter.h"
00030 #include "formats.h"
00031 #include "video.h"
00032 #include "internal.h"
00033 #include "libavutil/imgutils.h"
00034 #include "libavutil/opt.h"
00035 #include "libavutil/parseutils.h"
00036 #include <float.h>
00037 #include <math.h>
00038 
00039 #define SQR(a) ((a)*(a))
00040 
00041 enum Outer{
00042     ITERATION_COUNT,
00043     NORMALIZED_ITERATION_COUNT,
00044 };
00045 
00046 enum Inner{
00047     BLACK,
00048     PERIOD,
00049     CONVTIME,
00050     MINCOL,
00051 };
00052 
00053 typedef struct Point {
00054     double p[2];
00055     uint32_t val;
00056 } Point;
00057 
00058 typedef struct {
00059     const AVClass *class;
00060     int w, h;
00061     AVRational time_base;
00062     uint64_t pts;
00063     char *rate;
00064     int maxiter;
00065     double start_x;
00066     double start_y;
00067     double start_scale;
00068     double end_scale;
00069     double end_pts;
00070     double bailout;
00071     enum Outer outer;
00072     enum Inner inner;
00073     int cache_allocated;
00074     int cache_used;
00075     Point *point_cache;
00076     Point *next_cache;
00077     double (*zyklus)[2];
00078     uint32_t dither;
00079 } MBContext;
00080 
00081 #define OFFSET(x) offsetof(MBContext, x)
00082 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
00083 
00084 static const AVOption mandelbrot_options[] = {
00085     {"size",        "set frame size",                OFFSET(w),       AV_OPT_TYPE_IMAGE_SIZE, {.str="640x480"},  CHAR_MIN, CHAR_MAX, FLAGS },
00086     {"s",           "set frame size",                OFFSET(w),       AV_OPT_TYPE_IMAGE_SIZE, {.str="640x480"},  CHAR_MIN, CHAR_MAX, FLAGS },
00087     {"rate",        "set frame rate",                OFFSET(rate),    AV_OPT_TYPE_STRING,     {.str="25"},  CHAR_MIN, CHAR_MAX, FLAGS },
00088     {"r",           "set frame rate",                OFFSET(rate),    AV_OPT_TYPE_STRING,     {.str="25"},  CHAR_MIN, CHAR_MAX, FLAGS },
00089     {"maxiter",     "set max iterations number",     OFFSET(maxiter), AV_OPT_TYPE_INT,        {.i64=7189},  1,        INT_MAX, FLAGS },
00090     {"start_x",     "set the initial x position",    OFFSET(start_x), AV_OPT_TYPE_DOUBLE,     {.dbl=-0.743643887037158704752191506114774}, -100, 100, FLAGS },
00091     {"start_y",     "set the initial y position",    OFFSET(start_y), AV_OPT_TYPE_DOUBLE,     {.dbl=-0.131825904205311970493132056385139}, -100, 100, FLAGS },
00092     {"start_scale", "set the initial scale value",   OFFSET(start_scale), AV_OPT_TYPE_DOUBLE, {.dbl=3.0},  0, FLT_MAX, FLAGS },
00093     {"end_scale",   "set the terminal scale value",  OFFSET(end_scale), AV_OPT_TYPE_DOUBLE,   {.dbl=0.3},  0, FLT_MAX, FLAGS },
00094     {"end_pts",     "set the terminal pts value",    OFFSET(end_pts), AV_OPT_TYPE_DOUBLE,     {.dbl=400},  0, INT64_MAX, FLAGS },
00095     {"bailout",     "set the bailout value",         OFFSET(bailout), AV_OPT_TYPE_DOUBLE,     {.dbl=10},   0, FLT_MAX, FLAGS },
00096 
00097     {"outer",       "set outer coloring mode",       OFFSET(outer), AV_OPT_TYPE_INT, {.i64=NORMALIZED_ITERATION_COUNT}, 0, INT_MAX, FLAGS, "outer" },
00098     {"iteration_count", "set iteration count mode",  0, AV_OPT_TYPE_CONST, {.i64=ITERATION_COUNT}, INT_MIN, INT_MAX, FLAGS, "outer" },
00099     {"normalized_iteration_count", "set normalized iteration count mode",   0, AV_OPT_TYPE_CONST, {.i64=NORMALIZED_ITERATION_COUNT}, INT_MIN, INT_MAX, FLAGS, "outer" },
00100 
00101     {"inner",       "set inner coloring mode",       OFFSET(inner), AV_OPT_TYPE_INT, {.i64=MINCOL}, 0, INT_MAX, FLAGS, "inner" },
00102     {"black",       "set black mode",                0, AV_OPT_TYPE_CONST, {.i64=BLACK}, INT_MIN, INT_MAX, FLAGS, "inner"},
00103     {"period",      "set period mode",               0, AV_OPT_TYPE_CONST, {.i64=PERIOD}, INT_MIN, INT_MAX, FLAGS, "inner"},
00104     {"convergence", "show time until convergence",   0, AV_OPT_TYPE_CONST, {.i64=CONVTIME}, INT_MIN, INT_MAX, FLAGS, "inner"},
00105     {"mincol",      "color based on point closest to the origin of the iterations",   0, AV_OPT_TYPE_CONST, {.i64=MINCOL}, INT_MIN, INT_MAX, FLAGS, "inner"},
00106 
00107     {NULL},
00108 };
00109 
00110 AVFILTER_DEFINE_CLASS(mandelbrot);
00111 
00112 static av_cold int init(AVFilterContext *ctx, const char *args)
00113 {
00114     MBContext *mb = ctx->priv;
00115     AVRational rate_q;
00116     int err;
00117 
00118     mb->class = &mandelbrot_class;
00119     av_opt_set_defaults(mb);
00120 
00121     if ((err = (av_set_options_string(mb, args, "=", ":"))) < 0)
00122         return err;
00123     mb->bailout *= mb->bailout;
00124 
00125     mb->start_scale /=mb->h;
00126     mb->end_scale /=mb->h;
00127 
00128     if (av_parse_video_rate(&rate_q, mb->rate) < 0) {
00129         av_log(ctx, AV_LOG_ERROR, "Invalid frame rate: %s\n", mb->rate);
00130         return AVERROR(EINVAL);
00131     }
00132     mb->time_base.num = rate_q.den;
00133     mb->time_base.den = rate_q.num;
00134 
00135     mb->cache_allocated = mb->w * mb->h * 3;
00136     mb->cache_used = 0;
00137     mb->point_cache= av_malloc(sizeof(*mb->point_cache)*mb->cache_allocated);
00138     mb-> next_cache= av_malloc(sizeof(*mb-> next_cache)*mb->cache_allocated);
00139     mb-> zyklus    = av_malloc(sizeof(*mb->zyklus) * (mb->maxiter+16));
00140 
00141     return 0;
00142 }
00143 
00144 static av_cold void uninit(AVFilterContext *ctx)
00145 {
00146     MBContext *mb = ctx->priv;
00147 
00148     av_freep(&mb->rate);
00149     av_freep(&mb->point_cache);
00150     av_freep(&mb-> next_cache);
00151     av_freep(&mb->zyklus);
00152 }
00153 
00154 static int query_formats(AVFilterContext *ctx)
00155 {
00156     static const enum PixelFormat pix_fmts[] = {
00157         PIX_FMT_BGR32,
00158         PIX_FMT_NONE
00159     };
00160 
00161     ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
00162     return 0;
00163 }
00164 
00165 static int config_props(AVFilterLink *inlink)
00166 {
00167     AVFilterContext *ctx = inlink->src;
00168     MBContext *mb = ctx->priv;
00169 
00170     if (av_image_check_size(mb->w, mb->h, 0, ctx) < 0)
00171         return AVERROR(EINVAL);
00172 
00173     inlink->w = mb->w;
00174     inlink->h = mb->h;
00175     inlink->time_base = mb->time_base;
00176 
00177     return 0;
00178 }
00179 
00180 static void fill_from_cache(AVFilterContext *ctx, uint32_t *color, int *in_cidx, int *out_cidx, double py, double scale){
00181     MBContext *mb = ctx->priv;
00182     for(; *in_cidx < mb->cache_used; (*in_cidx)++){
00183         Point *p= &mb->point_cache[*in_cidx];
00184         int x;
00185         if(p->p[1] > py)
00186             break;
00187         x= round((p->p[0] - mb->start_x) / scale + mb->w/2);
00188         if(x<0 || x >= mb->w)
00189             continue;
00190         if(color) color[x] = p->val;
00191         if(out_cidx && *out_cidx < mb->cache_allocated)
00192             mb->next_cache[(*out_cidx)++]= *p;
00193     }
00194 }
00195 
00196 static int interpol(MBContext *mb, uint32_t *color, int x, int y, int linesize)
00197 {
00198     uint32_t a,b,c,d, i;
00199     uint32_t ipol=0xFF000000;
00200     int dist;
00201 
00202     if(!x || !y || x+1==mb->w || y+1==mb->h)
00203         return 0;
00204 
00205     dist= FFMAX(FFABS(x-(mb->w>>1))*mb->h, FFABS(y-(mb->h>>1))*mb->w);
00206 
00207     if(dist<(mb->w*mb->h>>3))
00208         return 0;
00209 
00210     a=color[(x+1) + (y+0)*linesize];
00211     b=color[(x-1) + (y+1)*linesize];
00212     c=color[(x+0) + (y+1)*linesize];
00213     d=color[(x+1) + (y+1)*linesize];
00214 
00215     if(a&&c){
00216         b= color[(x-1) + (y+0)*linesize];
00217         d= color[(x+0) + (y-1)*linesize];
00218     }else if(b&&d){
00219         a= color[(x+1) + (y-1)*linesize];
00220         c= color[(x-1) + (y-1)*linesize];
00221     }else if(c){
00222         d= color[(x+0) + (y-1)*linesize];
00223         a= color[(x-1) + (y+0)*linesize];
00224         b= color[(x+1) + (y-1)*linesize];
00225     }else if(d){
00226         c= color[(x-1) + (y-1)*linesize];
00227         a= color[(x-1) + (y+0)*linesize];
00228         b= color[(x+1) + (y-1)*linesize];
00229     }else
00230         return 0;
00231 
00232     for(i=0; i<3; i++){
00233         int s= 8*i;
00234         uint8_t ac= a>>s;
00235         uint8_t bc= b>>s;
00236         uint8_t cc= c>>s;
00237         uint8_t dc= d>>s;
00238         int ipolab= (ac + bc);
00239         int ipolcd= (cc + dc);
00240         if(FFABS(ipolab - ipolcd) > 5)
00241             return 0;
00242         if(FFABS(ac-bc)+FFABS(cc-dc) > 20)
00243             return 0;
00244         ipol |= ((ipolab + ipolcd + 2)/4)<<s;
00245     }
00246     color[x + y*linesize]= ipol;
00247     return 1;
00248 }
00249 
00250 static void draw_mandelbrot(AVFilterContext *ctx, uint32_t *color, int linesize, int64_t pts)
00251 {
00252     MBContext *mb = ctx->priv;
00253     int x,y,i, in_cidx=0, next_cidx=0, tmp_cidx;
00254     double scale= mb->start_scale*pow(mb->end_scale/mb->start_scale, pts/mb->end_pts);
00255     int use_zyklus=0;
00256     fill_from_cache(ctx, NULL, &in_cidx, NULL, mb->start_y+scale*(-mb->h/2-0.5), scale);
00257     tmp_cidx= in_cidx;
00258     memset(color, 0, sizeof(*color)*mb->w);
00259     for(y=0; y<mb->h; y++){
00260         int y1= y+1;
00261         const double ci=mb->start_y+scale*(y-mb->h/2);
00262         fill_from_cache(ctx, NULL, &in_cidx, &next_cidx, ci, scale);
00263         if(y1<mb->h){
00264             memset(color+linesize*y1, 0, sizeof(*color)*mb->w);
00265             fill_from_cache(ctx, color+linesize*y1, &tmp_cidx, NULL, ci + 3*scale/2, scale);
00266         }
00267 
00268         for(x=0; x<mb->w; x++){
00269             float av_uninit(epsilon);
00270             const double cr=mb->start_x+scale*(x-mb->w/2);
00271             double zr=cr;
00272             double zi=ci;
00273             uint32_t c=0;
00274             double dv= mb->dither / (double)(1LL<<32);
00275             mb->dither= mb->dither*1664525+1013904223;
00276 
00277             if(color[x + y*linesize] & 0xFF000000)
00278                 continue;
00279             if(interpol(mb, color, x, y, linesize)){
00280                 if(next_cidx < mb->cache_allocated){
00281                     mb->next_cache[next_cidx  ].p[0]= cr;
00282                     mb->next_cache[next_cidx  ].p[1]= ci;
00283                     mb->next_cache[next_cidx++].val = color[x + y*linesize];
00284                 }
00285                 continue;
00286             }
00287 
00288             use_zyklus= (x==0 || mb->inner!=BLACK ||color[x-1 + y*linesize] == 0xFF000000);
00289             if(use_zyklus)
00290                 epsilon= scale*1*sqrt(SQR(x-mb->w/2) + SQR(y-mb->h/2))/mb->w;
00291 
00292 #define Z_Z2_C(outr,outi,inr,ini)\
00293             outr= inr*inr - ini*ini + cr;\
00294             outi= 2*inr*ini + ci;
00295 
00296 #define Z_Z2_C_ZYKLUS(outr,outi,inr,ini, Z)\
00297             Z_Z2_C(outr,outi,inr,ini)\
00298             if(use_zyklus){\
00299                 if(Z && fabs(mb->zyklus[i>>1][0]-outr)+fabs(mb->zyklus[i>>1][1]-outi) <= epsilon)\
00300                     break;\
00301             }\
00302             mb->zyklus[i][0]= outr;\
00303             mb->zyklus[i][1]= outi;\
00304 
00305 
00306 
00307             for(i=0; i<mb->maxiter-8; i++){
00308                 double t;
00309                 Z_Z2_C_ZYKLUS(t, zi, zr, zi, 0)
00310                 i++;
00311                 Z_Z2_C_ZYKLUS(zr, zi, t, zi, 1)
00312                 i++;
00313                 Z_Z2_C_ZYKLUS(t, zi, zr, zi, 0)
00314                 i++;
00315                 Z_Z2_C_ZYKLUS(zr, zi, t, zi, 1)
00316                 i++;
00317                 Z_Z2_C_ZYKLUS(t, zi, zr, zi, 0)
00318                 i++;
00319                 Z_Z2_C_ZYKLUS(zr, zi, t, zi, 1)
00320                 i++;
00321                 Z_Z2_C_ZYKLUS(t, zi, zr, zi, 0)
00322                 i++;
00323                 Z_Z2_C_ZYKLUS(zr, zi, t, zi, 1)
00324                 if(zr*zr + zi*zi > mb->bailout){
00325                     i-= FFMIN(7, i);
00326                     for(; i<mb->maxiter; i++){
00327                         zr= mb->zyklus[i][0];
00328                         zi= mb->zyklus[i][1];
00329                         if(zr*zr + zi*zi > mb->bailout){
00330                             switch(mb->outer){
00331                             case            ITERATION_COUNT: zr = i; break;
00332                             case NORMALIZED_ITERATION_COUNT: zr= i + log2(log(mb->bailout) / log(zr*zr + zi*zi)); break;
00333                             }
00334                             c= lrintf((sin(zr)+1)*127) + lrintf((sin(zr/1.234)+1)*127)*256*256 + lrintf((sin(zr/100)+1)*127)*256;
00335                             break;
00336                         }
00337                     }
00338                     break;
00339                 }
00340             }
00341             if(!c){
00342                 if(mb->inner==PERIOD){
00343                 int j;
00344                 for(j=i-1; j; j--)
00345                     if(SQR(mb->zyklus[j][0]-zr) + SQR(mb->zyklus[j][1]-zi) < epsilon*epsilon*10)
00346                         break;
00347                 if(j){
00348                     c= i-j;
00349                     c= ((c<<5)&0xE0) + ((c<<16)&0xE000) + ((c<<27)&0xE00000);
00350                 }
00351                 }else if(mb->inner==CONVTIME){
00352                     c= floor(i*255.0/mb->maxiter+dv)*0x010101;
00353                 } else if(mb->inner==MINCOL){
00354                     int j;
00355                     double closest=9999;
00356                     int closest_index=0;
00357                     for(j=i-1; j>=0; j--)
00358                         if(SQR(mb->zyklus[j][0]) + SQR(mb->zyklus[j][1]) < closest){
00359                             closest= SQR(mb->zyklus[j][0]) + SQR(mb->zyklus[j][1]);
00360                             closest_index= j;
00361                         }
00362                     closest = sqrt(closest);
00363                     c= lrintf((mb->zyklus[closest_index][0]/closest+1)*127+dv) + lrintf((mb->zyklus[closest_index][1]/closest+1)*127+dv)*256;
00364                 }
00365             }
00366             c |= 0xFF000000;
00367             color[x + y*linesize]= c;
00368             if(next_cidx < mb->cache_allocated){
00369                 mb->next_cache[next_cidx  ].p[0]= cr;
00370                 mb->next_cache[next_cidx  ].p[1]= ci;
00371                 mb->next_cache[next_cidx++].val = c;
00372             }
00373         }
00374         fill_from_cache(ctx, NULL, &in_cidx, &next_cidx, ci + scale/2, scale);
00375     }
00376     FFSWAP(void*, mb->next_cache, mb->point_cache);
00377     mb->cache_used = next_cidx;
00378     if(mb->cache_used == mb->cache_allocated)
00379         av_log(0, AV_LOG_INFO, "Mandelbrot cache is too small!\n");
00380 }
00381 
00382 static int request_frame(AVFilterLink *link)
00383 {
00384     MBContext *mb = link->src->priv;
00385     AVFilterBufferRef *picref = ff_get_video_buffer(link, AV_PERM_WRITE, mb->w, mb->h);
00386     picref->video->sample_aspect_ratio = (AVRational) {1, 1};
00387     picref->pts = mb->pts++;
00388     picref->pos = -1;
00389 
00390     ff_start_frame(link, avfilter_ref_buffer(picref, ~0));
00391     draw_mandelbrot(link->src, (uint32_t*)picref->data[0], picref->linesize[0]/4, picref->pts);
00392     ff_draw_slice(link, 0, mb->h, 1);
00393     ff_end_frame(link);
00394     avfilter_unref_buffer(picref);
00395 
00396     return 0;
00397 }
00398 
00399 AVFilter avfilter_vsrc_mandelbrot = {
00400     .name        = "mandelbrot",
00401     .description = NULL_IF_CONFIG_SMALL("Render a Mandelbrot fractal."),
00402 
00403     .priv_size = sizeof(MBContext),
00404     .init      = init,
00405     .uninit    = uninit,
00406 
00407     .query_formats = query_formats,
00408 
00409     .inputs    = (const AVFilterPad[]) {{ .name = NULL}},
00410 
00411     .outputs   = (const AVFilterPad[]) {{ .name      = "default",
00412                                     .type            = AVMEDIA_TYPE_VIDEO,
00413                                     .request_frame   = request_frame,
00414                                     .config_props    = config_props },
00415                                   { .name = NULL}},
00416 };