static void animation_render_main (IterativeMap *map, Animation *animation, const gchar *filename, double quality) { const double frame_rate = 24; AnimationIter iter; ParameterHolderPair frame; guint frame_count = 0; gboolean continuation; double current_quality; AviWriter *avi = avi_writer_new(fopen(filename, "wb"), HISTOGRAM_IMAGER(map)->width, HISTOGRAM_IMAGER(map)->height, frame_rate); animation_iter_get_first(animation, &iter); frame.a = PARAMETER_HOLDER(de_jong_new()); frame.b = PARAMETER_HOLDER(de_jong_new()); while (animation_iter_read_frame(animation, &iter, &frame, frame_rate)) { continuation = FALSE; do { /* Calculate 0.5 seconds between quality updates. This is lower than the * batch image rendering default of 2.0 seconds, to better handle * lower-quality animations where the individual frames go quicker. */ iterative_map_calculate_motion_timed(map, 0.5, continuation, PARAMETER_INTERPOLATOR(parameter_holder_interpolate_linear), &frame); current_quality = histogram_imager_compute_quality(HISTOGRAM_IMAGER(map)); printf("\rFrame %d, %e iterations, %.04f quality", frame_count, map->iterations, current_quality); fflush(stdout); continuation = TRUE; } while (current_quality < quality); histogram_imager_update_image(HISTOGRAM_IMAGER(map)); avi_writer_append_frame(avi, HISTOGRAM_IMAGER(map)->image); /* Move to the next line for each frame. * Updates within a frame overwrite that line. */ printf("\n"); frame_count++; } avi_writer_close(avi); }
static BifurcationDiagram* get_bifurcation_diagram (CellRendererBifurcation *self) { /* Using the current iterator and animation, find the corresponding * bifurcation diagram object, creating and/or updating it if necessary. */ GObject *obj; BifurcationDiagram *bd; GtkTreeIter keyframe, next_keyframe; DeJong *a, *b; /* Look up the first keyframe from a row ID */ if (!animation_keyframe_find_by_id(self->animation, self->row_id, &keyframe)) return NULL; /* Iterate to the next keyframe */ next_keyframe = keyframe; if (!gtk_tree_model_iter_next(GTK_TREE_MODEL(self->animation->model), &next_keyframe)) { /* We're at the last keyframe, no bifurcation diagram for us */ return NULL; } /* Try to extract an existing bifurcation diagram */ gtk_tree_model_get(GTK_TREE_MODEL(self->animation->model), &keyframe, ANIMATION_MODEL_BIFURCATION, &obj, -1); if (obj) { /* We have an existing object, yay */ bd = BIFURCATION_DIAGRAM(obj); } else { /* Nope, create a new object and store it in the model */ bd = bifurcation_diagram_new(); gtk_list_store_set(self->animation->model, &keyframe, ANIMATION_MODEL_BIFURCATION, bd, -1); } /* Load parameters from both keyframes */ a = de_jong_new(); b = de_jong_new(); animation_keyframe_load(self->animation, &keyframe, PARAMETER_HOLDER(a)); animation_keyframe_load(self->animation, &next_keyframe, PARAMETER_HOLDER(b)); /* Set up this bifurcation diagram for linear interpolation between the two */ bifurcation_diagram_set_linear_endpoints(bd, a, b); g_object_unref(a); g_object_unref(b); return bd; }
static void cmd_set_param (RemoteServerConn* self, const char* command, const char* parameters) { parameter_holder_set_from_line(PARAMETER_HOLDER(self->map), parameters); remote_server_send_response(self, FYRE_RESPONSE_OK, "ok"); }
void histogram_imager_load_image_file (HistogramImager *self, const gchar *filename, GError **error) { /* Try to open the given PNG file and load parameters from it */ const gchar *params; GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (filename, error); if (!pixbuf) return; params = gdk_pixbuf_get_option (pixbuf, "tEXt::fyre_params"); /* For backward compatibility with de Jong Explorer and early versions of Fyre */ if (!params) params = gdk_pixbuf_get_option (pixbuf, "tEXt::de_jong_params"); if (params) { parameter_holder_load_string (PARAMETER_HOLDER (self), params); } else { if (error != NULL) { GError *nerror = g_error_new (fyre_histogram_imager_error_quark(), FYRE_HISTOGRAM_IMAGER_ERROR_NO_METADATA, "The image does not contain Fyre metadata"); *error = nerror; } } gdk_pixbuf_unref (pixbuf); }
static void animation_render_ui_init(AnimationRenderUi *self) { if (g_file_test (FYRE_DATADIR "/animation-render.glade", G_FILE_TEST_EXISTS)) self->xml = glade_xml_new(FYRE_DATADIR "/animation-render.glade", NULL, NULL); if (!self->xml) self->xml = glade_xml_new(BR_DATADIR("/fyre/animation-render.glade"), NULL, NULL); fyre_set_icon_later(glade_xml_get_widget(self->xml, "window")); glade_xml_signal_connect_data(self->xml, "on_ok_clicked", G_CALLBACK(on_ok_clicked), self); glade_xml_signal_connect_data(self->xml, "on_cancel_clicked", G_CALLBACK(on_cancel_clicked), self); glade_xml_signal_connect_data(self->xml, "on_select_output_file_clicked", G_CALLBACK(on_select_output_file_clicked), self); glade_xml_signal_connect_data(self->xml, "on_delete_event", G_CALLBACK(on_delete_event), self); self->map = ITERATIVE_MAP(de_jong_new()); self->frame.a = PARAMETER_HOLDER(de_jong_new()); self->frame.b = PARAMETER_HOLDER(de_jong_new()); }
Explorer* explorer_new(IterativeMap *map, Animation *animation) { Explorer *self = EXPLORER(g_object_new(explorer_get_type(), NULL)); GtkWidget *editor, *window, *scroll; GtkRequisition win_req; self->animation = ANIMATION(g_object_ref(animation)); self->map = ITERATIVE_MAP(g_object_ref(map)); /* Create the parameter editor */ editor = parameter_editor_new(PARAMETER_HOLDER(map)); gtk_box_pack_start(GTK_BOX(glade_xml_get_widget(self->xml, "parameter_editor_box")), editor, FALSE, FALSE, 0); gtk_widget_show_all(editor); /* Create the view */ self->view = histogram_view_new(HISTOGRAM_IMAGER(map)); gtk_container_add(GTK_CONTAINER(glade_xml_get_widget(self->xml, "drawing_area_viewport")), self->view); gtk_widget_show_all(self->view); /* Set the initial render time */ on_render_time_changed(glade_xml_get_widget(self->xml, "render_time"), self); explorer_init_history(self); explorer_init_animation(self); explorer_init_tools(self); explorer_init_cluster(self); explorer_init_about(self); /* Start the iterative map rendering in the background, and get a callback every time a block * of calculations finish so we can update the GUI. */ iterative_map_start_calculation(self->map); g_signal_connect(G_OBJECT(self->map), "calculation-finished", G_CALLBACK(on_calculation_finished), self); /* Set the window's default size to include our default image size. * The cleanest way I know of to do this is to set the scrolled window's scrollbar policies * to 'never' and get the window's size requests, set them back to automatic, then set the * default size to that size request. */ window = glade_xml_get_widget(self->xml, "explorer_window"); scroll = glade_xml_get_widget(self->xml, "main_scrolledwindow"); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_NEVER, GTK_POLICY_NEVER); gtk_widget_size_request(window, &win_req); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_window_set_default_size(GTK_WINDOW(window), win_req.width, win_req.height); gtk_widget_show(window); return self; }
ScreenSaver* screensaver_new(IterativeMap *map, Animation *animation) { ScreenSaver *self = SCREENSAVER(g_object_new(screensaver_get_type(), NULL)); int i; AnimationIter iter; gchar* common_parameters; self->animation = ANIMATION(g_object_ref(animation)); self->map = ITERATIVE_MAP(g_object_ref(map)); self->view = g_object_ref(histogram_view_new(HISTOGRAM_IMAGER(self->map))); /* Allocate and interpolate all frames */ self->framerate = 10; self->num_frames = animation_get_length(self->animation) * self->framerate; self->frame_renders = g_new0(IterativeMap*, self->num_frames); self->frame_parameters = g_new0(ParameterHolderPair, self->num_frames); self->current_frame = 0; common_parameters = parameter_holder_save_string(PARAMETER_HOLDER(map)); animation_iter_seek(animation, &iter, 0); for (i=0; i<self->num_frames; i++) { self->frame_renders[i] = ITERATIVE_MAP(de_jong_new()); parameter_holder_load_string(PARAMETER_HOLDER(self->frame_renders[i]), common_parameters); self->frame_parameters[i].a = PARAMETER_HOLDER(de_jong_new()); animation_iter_load(animation, &iter, self->frame_parameters[i].a); animation_iter_seek_relative(animation, &iter, 1/self->framerate); } for (i=0; i<self->num_frames-1; i++) self->frame_parameters[i].b = self->frame_parameters[i+1].a; self->frame_parameters[self->num_frames-1].b = self->frame_parameters[self->num_frames-1].a; g_free(common_parameters); self->direction = 1; screensaver_start(self); return self; }
void histogram_imager_save_image_file (HistogramImager *self, const gchar *filename, GError **error) { /* Save our current image to a .PNG file */ gchar *params; histogram_imager_update_image (self); /* Save our current parameters in a tEXt chunk, using a format that * is both human-readable and easy to load parameters from automatically. */ params = parameter_holder_save_string (PARAMETER_HOLDER(self)); gdk_pixbuf_save (self->image, filename, "png", error, "tEXt::fyre_params", params, NULL); g_free (params); }
static HistoryNode* history_node_new (HistogramImager* map) { HistoryNode* self = g_new0(HistoryNode, 1); gint width, height; g_get_current_time(&self->timestamp); self->params = parameter_holder_save_string(PARAMETER_HOLDER(map)); /* Use the normal icon size plus a little extra */ gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height); width *= 1.75; height *= 1.75; self->thumbnail = histogram_imager_make_thumbnail(map, width, height); return self; }
static void on_load_defaults(GtkWidget *widget, Explorer* self) { parameter_holder_reset_to_defaults(PARAMETER_HOLDER(self->map)); }
int main(int argc, char ** argv) { IterativeMap* map; Animation* animation; gboolean animate = FALSE; gboolean have_gtk; gboolean verbose = FALSE; gboolean hidden = FALSE; enum {INTERACTIVE, RENDER, SCREENSAVER, REMOTE} mode = INTERACTIVE; const gchar *outputFile = NULL; const gchar *pidfile = NULL; int c, option_index=0; double quality = 1.0; #ifdef HAVE_GNET int port_number = FYRE_DEFAULT_PORT; #endif GError *error = NULL; math_init(); g_type_init(); have_gtk = gtk_init_check(&argc, &argv); #ifdef HAVE_GNET gnet_init(); # ifdef WIN32 gnet_ipv6_set_policy(GIPV6_POLICY_IPV4_ONLY); # endif #endif map = ITERATIVE_MAP(de_jong_new()); animation = animation_new(); while (1) { static struct option long_options[] = { {"help", 0, NULL, 'h'}, {"read", 1, NULL, 'i'}, {"animate", 1, NULL, 'n'}, {"output", 1, NULL, 'o'}, {"param", 1, NULL, 'p'}, {"size", 1, NULL, 's'}, {"oversample", 1, NULL, 'S'}, {"quality", 1, NULL, 'q'}, {"remote", 0, NULL, 'r'}, {"verbose", 0, NULL, 'v'}, {"port", 1, NULL, 'P'}, {"cluster", 1, NULL, 'c'}, {"auto-cluster", 0, NULL, 'C'}, {"screensaver", 0, NULL, 1000}, /* Undocumented, still experimental */ {"hidden", 0, NULL, 1001}, {"chdir", 1, NULL, 1002}, /* Undocumented, used by win32 file associations */ {"pidfile", 1, NULL, 1003}, {"version", 0, NULL, 1004}, {NULL}, }; c = getopt_long(argc, argv, "hi:n:o:p:s:S:q:rvP:c:C", long_options, &option_index); if (c == -1) break; switch (c) { case 'i': { histogram_imager_load_image_file(HISTOGRAM_IMAGER(map), optarg, &error); break; } case 'n': { GtkTreeIter iter; animation_load_file(animation, optarg); animate = TRUE; gtk_tree_model_get_iter_first(GTK_TREE_MODEL(animation->model), &iter); animation_keyframe_load(animation, &iter, PARAMETER_HOLDER(map)); break; } case 'o': mode = RENDER; outputFile = optarg; break; case 'p': parameter_holder_load_string(PARAMETER_HOLDER(map), optarg); break; case 's': parameter_holder_set(PARAMETER_HOLDER(map), "size" , optarg); break; case 'S': parameter_holder_set(PARAMETER_HOLDER(map), "oversample", optarg); break; case 'q': quality = atof(optarg); break; case 'v': verbose = TRUE; break; #ifdef HAVE_GNET case 'c': { ClusterModel *cluster = cluster_model_get(map, TRUE); cluster_model_add_nodes(cluster, optarg); } break; case 'C': { ClusterModel *cluster = cluster_model_get(map, TRUE); cluster_model_enable_discovery(cluster); } break; case 'r': mode = REMOTE; break; case 'P': port_number = atol(optarg); break; #else case 'c': case 'C': case 'P': fprintf(stderr, "This Fyre binary was compiled without gnet support.\n" "Cluster support is not available.\n"); break; case 'r': fprintf(stderr, "This Fyre binary was compiled without gnet support.\n" "Cluster support is not available.\n"); exit(1); break; #endif case 1000: /* --screensaver */ mode = SCREENSAVER; break; case 1001: /* --hidden */ hidden = TRUE; break; case 1002: /* --chdir */ chdir(optarg); break; case 1003: /* --pidfile */ pidfile = optarg; break; case 1004: /* --version */ printf("%s\n", VERSION); return 0; case 'h': default: usage(argv); return 1; } } if (optind + 1 < argc) { usage(argv); return 1; } if (optind != argc) { char *ext = strrchr (argv[optind], '.'); if (ext) { if (g_strcasecmp(ext, ".png") == 0) { histogram_imager_load_image_file(HISTOGRAM_IMAGER(map), argv[optind], &error); } else if (g_strcasecmp(ext, ".fa") == 0) { GtkTreeIter iter; animation_load_file(animation, argv[optind]); animate = TRUE; gtk_tree_model_get_iter_first(GTK_TREE_MODEL(animation->model), &iter); animation_keyframe_load(animation, &iter, PARAMETER_HOLDER(map)); } else { usage(argv); return 1; } } else { usage(argv); return 1; } } switch (mode) { case INTERACTIVE: { Explorer *explorer; if (!have_gtk) { fprintf(stderr, "GTK intiailization failed, can't start in interactive mode\n"); return 1; } explorer = explorer_new (map, animation); if (error) { GtkWidget *dialog, *label; gchar *text; dialog = glade_xml_get_widget (explorer->xml, "error dialog"); label = glade_xml_get_widget (explorer->xml, "error label"); text = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">Error!</span>\n\n%s", error->message); gtk_label_set_markup (GTK_LABEL (label), text); g_free (text); g_error_free (error); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_hide (dialog); } gtk_main(); break; } case RENDER: { acquire_console(); if (error) { g_print ("Error: %s\n", error->message); g_error_free (error); } if (animate) animation_render_main (map, animation, outputFile, quality); else batch_image_render (map, outputFile, quality); break; } case REMOTE: { #ifdef HAVE_GNET if (verbose) { acquire_console(); } else { daemonize_to_pidfile(pidfile); } if (!hidden) discovery_server_new(FYRE_DEFAULT_SERVICE, port_number); remote_server_main_loop(port_number, have_gtk, verbose); #else fprintf(stderr, "This Fyre binary was compiled without gnet support.\n" "Remote control mode is not available.\n"); #endif break; } case SCREENSAVER: { ScreenSaver* screensaver; GtkWidget* window; if (!have_gtk) { fprintf(stderr, "GTK intiailization failed, can't start in screensaver mode\n"); return 1; } screensaver = screensaver_new(map, animation); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); fyre_set_icon_later(window); gtk_window_set_resizable(GTK_WINDOW(window), FALSE); gtk_window_set_title(GTK_WINDOW(window), "Fyre Screensaver"); gtk_container_add(GTK_CONTAINER(window), screensaver->view); gtk_widget_show_all(window); gtk_main(); break; } } return 0; }
static void history_node_apply (HistoryNode* self, HistogramImager* map) { parameter_holder_load_string(PARAMETER_HOLDER(map), self->params); }