You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

240 lines
7.4KB

  1. /*
  2. * Copyright (c) 2011 Michael Niedermayer
  3. *
  4. * This file is part of FFmpeg.
  5. *
  6. * FFmpeg is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2.1 of the License, or (at your option) any later version.
  10. *
  11. * FFmpeg is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with FFmpeg; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  19. *
  20. * The vsrc_color filter from Stefano Sabatini was used as template to create
  21. * this
  22. */
  23. /**
  24. * @file
  25. * Mandelbrot fraktal renderer
  26. */
  27. #include "avfilter.h"
  28. #include "libavutil/imgutils.h"
  29. #include "libavutil/parseutils.h"
  30. enum Outer{
  31. ITERATION_COUNT,
  32. NORMALIZED_ITERATION_COUNT,
  33. };
  34. typedef struct Point {
  35. double p[2];
  36. uint32_t val;
  37. } Point;
  38. typedef struct {
  39. int w, h;
  40. AVRational time_base;
  41. uint64_t pts;
  42. int maxiter;
  43. double start_x;
  44. double start_y;
  45. double start_scale;
  46. double end_scale;
  47. double end_pts;
  48. double bailout;
  49. enum Outer outer;
  50. int cache_allocated;
  51. int cache_used;
  52. Point *point_cache;
  53. Point *next_cache;
  54. } MBContext;
  55. static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
  56. {
  57. MBContext *mb = ctx->priv;
  58. char frame_size [128] = "320x240";
  59. char frame_rate [128] = "25";
  60. AVRational frame_rate_q;
  61. int ret;
  62. mb->maxiter=256;
  63. mb->start_x=-0.743643887037158704752191506114774;
  64. mb->start_y=-0.131825904205311970493132056385139;
  65. mb->start_scale=3.0;
  66. mb->end_scale=0.3;
  67. mb->end_pts=200;
  68. mb->bailout=100;
  69. mb->outer= NORMALIZED_ITERATION_COUNT;
  70. if (args)
  71. sscanf(args, "%127[^:]:%127[^:]:%d,%lf:%lf:%lf", frame_size, frame_rate, &mb->maxiter, &mb->start_x, &mb->start_y, &mb->start_scale);
  72. if (av_parse_video_size(&mb->w, &mb->h, frame_size) < 0) {
  73. av_log(ctx, AV_LOG_ERROR, "Invalid frame size: %s\n", frame_size);
  74. return AVERROR(EINVAL);
  75. }
  76. mb->start_scale /=mb->h;
  77. mb->end_scale /=mb->h;
  78. if (av_parse_video_rate(&frame_rate_q, frame_rate) < 0 ||
  79. frame_rate_q.den <= 0 || frame_rate_q.num <= 0) {
  80. av_log(ctx, AV_LOG_ERROR, "Invalid frame rate: %s\n", frame_rate);
  81. return AVERROR(EINVAL);
  82. }
  83. mb->time_base.num = frame_rate_q.den;
  84. mb->time_base.den = frame_rate_q.num;
  85. mb->cache_allocated = mb->w * mb->h*2;
  86. mb->cache_used = 0;
  87. mb->point_cache= av_malloc(sizeof(*mb->point_cache)*mb->cache_allocated);
  88. mb-> next_cache= av_malloc(sizeof(*mb-> next_cache)*mb->cache_allocated);
  89. return 0;
  90. }
  91. static av_cold void uninit(AVFilterContext *ctx)
  92. {
  93. MBContext *mb = ctx->priv;
  94. int i;
  95. av_freep(&mb->point_cache);
  96. av_freep(&mb-> next_cache);
  97. }
  98. static int query_formats(AVFilterContext *ctx)
  99. {
  100. static const enum PixelFormat pix_fmts[] = {
  101. PIX_FMT_BGR32,
  102. PIX_FMT_NONE
  103. };
  104. avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
  105. return 0;
  106. }
  107. static int config_props(AVFilterLink *inlink)
  108. {
  109. AVFilterContext *ctx = inlink->src;
  110. MBContext *mb = ctx->priv;
  111. if (av_image_check_size(mb->w, mb->h, 0, ctx) < 0)
  112. return AVERROR(EINVAL);
  113. inlink->w = mb->w;
  114. inlink->h = mb->h;
  115. inlink->time_base = mb->time_base;
  116. return 0;
  117. }
  118. static void fill_from_cache(AVFilterContext *ctx, uint32_t *color, int *in_cidx, int *out_cidx, double py, double scale){
  119. MBContext *mb = ctx->priv;
  120. for(; *in_cidx < mb->cache_used; (*in_cidx)++){
  121. Point *p= &mb->point_cache[*in_cidx];
  122. int x;
  123. if(*in_cidx >= mb->cache_used || p->p[1] > py)
  124. break;
  125. x= round((p->p[0] - mb->start_x) / scale + mb->w/2);
  126. if(x<0 || x >= mb->w)
  127. continue;
  128. if(color) color[x] = p->val;
  129. if(out_cidx && *out_cidx < mb->cache_allocated)
  130. mb->next_cache[(*out_cidx)++]= *p;
  131. }
  132. }
  133. static void draw_mandelbrot(AVFilterContext *ctx, uint32_t *color, int linesize, int64_t pts)
  134. {
  135. MBContext *mb = ctx->priv;
  136. int x,y,i, in_cidx=0, next_cidx=0, tmp_cidx;
  137. double scale= mb->start_scale*pow(mb->end_scale/mb->start_scale, pts/mb->end_pts);
  138. fill_from_cache(ctx, NULL, &in_cidx, NULL, mb->start_y+scale*(-mb->h/2-0.5), scale);
  139. for(y=0; y<mb->h; y++){
  140. const double ci=mb->start_y+scale*(y-mb->h/2);
  141. memset(color+linesize*y, 0, sizeof(*color)*mb->w);
  142. fill_from_cache(ctx, color+linesize*y, &in_cidx, &next_cidx, ci, scale);
  143. tmp_cidx= in_cidx;
  144. fill_from_cache(ctx, color+linesize*y, &tmp_cidx, NULL, ci + scale/2, scale);
  145. for(x=0; x<mb->w; x++){
  146. const double cr=mb->start_x+scale*(x-mb->w/2);
  147. double zr=cr;
  148. double zi=ci;
  149. uint32_t c=0;
  150. if(color[x + y*linesize] & 0xFF000000)
  151. continue;
  152. for(i=0; i<mb->maxiter; i++){
  153. double t;
  154. if(zr*zr + zi*zi > mb->bailout){
  155. switch(mb->outer){
  156. case ITERATION_COUNT: zr= i; break;
  157. case NORMALIZED_ITERATION_COUNT: zr= i + (log(log(mb->bailout)) - log(log(sqrt(zr*zr + zi*zi))))/log(2); break;
  158. }
  159. c= lrintf((sin(zr)+1)*127) + lrintf((sin(zr/1.234)+1)*127)*256*256 + lrintf((sin(zr/100)+1)*127)*256;
  160. break;
  161. }
  162. t= zr*zr - zi*zi;
  163. zi= 2*zr*zi + ci;
  164. zr= t + cr;
  165. }
  166. c |= 0xFF000000;
  167. color[x + y*linesize]= c;
  168. if(next_cidx < mb->cache_allocated){
  169. mb->next_cache[next_cidx ].p[0]= cr;
  170. mb->next_cache[next_cidx ].p[1]= ci;
  171. mb->next_cache[next_cidx++].val = c;
  172. }
  173. }
  174. fill_from_cache(ctx, NULL, &in_cidx, &next_cidx, ci + scale/2, scale);
  175. }
  176. FFSWAP(void*, mb->next_cache, mb->point_cache);
  177. mb->cache_used = next_cidx;
  178. }
  179. static int request_frame(AVFilterLink *link)
  180. {
  181. MBContext *mb = link->src->priv;
  182. AVFilterBufferRef *picref = avfilter_get_video_buffer(link, AV_PERM_WRITE, mb->w, mb->h);
  183. picref->video->sample_aspect_ratio = (AVRational) {1, 1};
  184. picref->pts = mb->pts++;
  185. picref->pos = -1;
  186. avfilter_start_frame(link, avfilter_ref_buffer(picref, ~0));
  187. draw_mandelbrot(link->src, picref->data[0], picref->linesize[0]/4, picref->pts);
  188. avfilter_draw_slice(link, 0, mb->h, 1);
  189. avfilter_end_frame(link);
  190. avfilter_unref_buffer(picref);
  191. return 0;
  192. }
  193. AVFilter avfilter_vsrc_mandelbrot = {
  194. .name = "mandelbrot",
  195. .description = NULL_IF_CONFIG_SMALL("Mandelbrot renderer"),
  196. .priv_size = sizeof(MBContext),
  197. .init = init,
  198. .uninit = uninit,
  199. .query_formats = query_formats,
  200. .inputs = (const AVFilterPad[]) {{ .name = NULL}},
  201. .outputs = (const AVFilterPad[]) {{ .name = "default",
  202. .type = AVMEDIA_TYPE_VIDEO,
  203. .request_frame = request_frame,
  204. .config_props = config_props },
  205. { .name = NULL}},
  206. };