/* === S Y N F I G ========================================================= */ /*! \file gtkmm/render.cpp ** \brief Template File ** ** $Id$ ** ** \legal ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley ** Copyright (c) 2007, 2008 Chris Moore ** ** This package is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public License as ** published by the Free Software Foundation; either version 2 of ** the License, or (at your option) any later version. ** ** This package is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ** General Public License for more details. ** \endlegal */ /* ========================================================================= */ /* === H E A D E R S ======================================================= */ #ifdef USING_PCH # include "pch.h" #else #ifdef HAVE_CONFIG_H # include #endif #include "render.h" #include "app.h" #include #include #include #include #include #include "asyncrenderer.h" #include "dialogs/dialog_ffmpegparam.h" #include "dialogs/dialog_spritesheetparam.h" #include "general.h" #include #include #endif /* === U S I N G =========================================================== */ using namespace std; using namespace etl; using namespace synfig; using namespace studio; /* === M A C R O S ========================================================= */ /* === G L O B A L S ======================================================= */ /* === P R O C E D U R E S ================================================= */ /* === M E T H O D S ======================================================= */ RenderSettings::RenderSettings(Gtk::Window& parent, etl::handle canvas_interface): Gtk::Dialog(_("Render Settings"),parent), canvas_interface_(canvas_interface), adjustment_quality(Gtk::Adjustment::create(3,0,9)), entry_quality(adjustment_quality,1,0), adjustment_antialias(Gtk::Adjustment::create(1,1,31)), entry_antialias(adjustment_antialias,1,0), toggle_single_frame(_("Render _current frame only"), true), toggle_extract_alpha(_("Extract alpha"), true), tparam("libx264-lossless",2000) { tparam.sequence_separator=App::sequence_separator; widget_rend_desc.show(); widget_rend_desc.signal_changed().connect(sigc::mem_fun(*this,&studio::RenderSettings::on_rend_desc_changed)); widget_rend_desc.set_rend_desc(canvas_interface_->get_canvas()->rend_desc()); canvas_interface->signal_rend_desc_changed().connect(sigc::mem_fun(*this,&RenderSettings::on_rend_desc_changed)); comboboxtext_target.append(_("Auto")); target_names.push_back(String()); synfig::Target::Book::iterator iter; synfig::Target::Book book(synfig::Target::book()); for(iter=book.begin();iter!=book.end();iter++) { comboboxtext_target.append(iter->first); target_names.push_back(iter->first); } comboboxtext_target.set_active(0); comboboxtext_target.signal_changed().connect(sigc::mem_fun(this, &RenderSettings::on_comboboxtext_target_changed)); Gtk::Alignment *dialogPadding = manage(new Gtk::Alignment(0, 0, 1, 1)); dialogPadding->set_padding(12, 12, 12, 12); get_vbox()->pack_start(*dialogPadding, false, false, 0); Gtk::VBox *dialogBox = manage(new Gtk::VBox(false, 12)); dialogPadding->add(*dialogBox); Gtk::Button *choose_button(manage(new class Gtk::Button(Gtk::StockID(_("Choose..."))))); choose_button->show(); choose_button->signal_clicked().connect(sigc::mem_fun(*this, &studio::RenderSettings::on_choose_pressed)); tparam_button=manage(new class Gtk::Button(Gtk::StockID(_("Parameters...")))); tparam_button->show(); tparam_button->set_sensitive(false); tparam_button->signal_clicked().connect(sigc::mem_fun(*this, &studio::RenderSettings::on_targetparam_pressed)); Gtk::Frame *target_frame=manage(new Gtk::Frame(_("Target"))); target_frame->set_shadow_type(Gtk::SHADOW_NONE); ((Gtk::Label *) target_frame->get_label_widget())->set_markup(_("Target")); dialogBox->pack_start(*target_frame); Gtk::Alignment *targetPadding = manage(new Gtk::Alignment(0, 0, 1, 1)); targetPadding->set_padding(6, 0, 24, 0); target_frame->add(*targetPadding); Gtk::Table *target_table = manage(new Gtk::Table(2, 3, false)); target_table->set_row_spacings(6); target_table->set_col_spacings(12); targetPadding->add(*target_table); Gtk::Label *filenameLabel = manage(new Gtk::Label(_("_Filename"), true)); filenameLabel->set_alignment(0, 0.5); filenameLabel->set_mnemonic_widget(entry_filename); target_table->attach(*filenameLabel, 0, 1, 0, 1, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0); target_table->attach(entry_filename, 1, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0); target_table->attach(*choose_button, 2, 3, 0, 1, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0); Gtk::Label *targetLabel = manage(new Gtk::Label(_("_Target"), true)); targetLabel->set_alignment(0, 0.5); targetLabel->set_mnemonic_widget(comboboxtext_target); target_table->attach(*targetLabel, 0, 1, 1, 2, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0); target_table->attach(comboboxtext_target, 1, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0); target_table->attach(*tparam_button, 2, 3, 1, 2, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0); toggle_single_frame.signal_toggled().connect(sigc::mem_fun(*this, &studio::RenderSettings::on_single_frame_toggle)); Gtk::Frame *settings_frame=manage(new Gtk::Frame(_("Settings"))); settings_frame->set_shadow_type(Gtk::SHADOW_NONE); ((Gtk::Label *) settings_frame->get_label_widget())->set_markup(_("Settings")); dialogBox->pack_start(*settings_frame); Gtk::Alignment *settingsPadding = manage(new Gtk::Alignment(0, 0, 1, 1)); settingsPadding->set_padding(6, 0, 24, 0); settings_frame->add(*settingsPadding); Gtk::Table *settings_table=manage(new Gtk::Table(3,2,false)); settings_table->set_row_spacings(6); settings_table->set_col_spacings(12); settingsPadding->add(*settings_table); Gtk::Label *qualityLabel = manage(new Gtk::Label(_("_Quality"), true)); qualityLabel->set_alignment(0, 0.5); qualityLabel->set_mnemonic_widget(entry_quality); settings_table->attach(*qualityLabel, 0, 1, 0, 1, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0); settings_table->attach(entry_quality, 1, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0); Gtk::Label *antiAliasLabel = manage(new Gtk::Label(_("_Anti-Aliasing"), true)); antiAliasLabel->set_alignment(0, 0.5); antiAliasLabel->set_mnemonic_widget(entry_antialias); settings_table->attach(*antiAliasLabel, 0, 1, 1, 2, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0); settings_table->attach(entry_antialias, 1, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0); toggle_single_frame.set_alignment(0, 0.5); settings_table->attach(toggle_single_frame, 2, 3, 0, 1, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0); toggle_single_frame.set_active(false); toggle_extract_alpha.set_alignment(0, 0.5); settings_table->attach(toggle_extract_alpha, 2, 3, 1, 2, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0); toggle_extract_alpha.set_active(false); dialogBox->pack_start(widget_rend_desc); Gtk::Button *render_button(manage(new class Gtk::Button(Gtk::StockID(_("Render"))))); render_button->show(); add_action_widget(*render_button,1); render_button->signal_clicked().connect(sigc::mem_fun(*this, &studio::RenderSettings::on_render_pressed)); Gtk::Button *cancel_button(manage(new class Gtk::Button(Gtk::StockID("gtk-cancel")))); cancel_button->show(); add_action_widget(*cancel_button,0); cancel_button->signal_clicked().connect(sigc::mem_fun(*this, &studio::RenderSettings::on_cancel_pressed)); //set_default_response(1); set_title(_("Render Settings")+String(" - ")+canvas_interface_->get_canvas()->get_name()); widget_rend_desc.enable_time_section(); set_entry_filename(); get_vbox()->show_all(); } RenderSettings::~RenderSettings() { } void RenderSettings::set_entry_filename() { String filename(filename_sans_extension(canvas_interface_->get_canvas()->get_file_name())); // if this isn't the root canvas, append () to the filename etl::handle canvas = canvas_interface_->get_canvas(); if (!canvas->is_root()) { if(canvas->get_name().empty()) filename+=" ("+canvas->get_id()+')'; else filename+=" ("+canvas->get_name()+')'; } filename += ".png"; try { entry_filename.set_text((filename)); } catch(...) { synfig::warning("Averted crash!"); entry_filename.set_text("output.png"); } } void RenderSettings::on_comboboxtext_target_changed() { int i = comboboxtext_target.get_active_row_number(); if (i < 0 || i >= (int)target_names.size()) return; if (target_name == target_names[i]) return; set_target(target_names[i]); } void RenderSettings::on_rend_desc_changed() { widget_rend_desc.set_rend_desc(canvas_interface_->get_canvas()->rend_desc()); } void RenderSettings::set_target(synfig::String name) { target_name=name; //TODO: Replace this condition tparam_button->set_sensitive(target_name.compare("ffmpeg") && target_name.compare("png-spritesheet")?false:true); } void RenderSettings::on_choose_pressed() { String filename=entry_filename.get_text(); if(App::dialog_save_file_render("Save Render As", filename, RENDER_DIR_PREFERENCE)) entry_filename.set_text(filename); } void RenderSettings::on_targetparam_pressed() { Dialog_TargetParam * dialogtp; //TODO: Replace this conditions too if (target_name.compare("ffmpeg") == 0) dialogtp = new Dialog_FFmpegParam (*this); else if (target_name.compare("png-spritesheet") == 0) dialogtp = new Dialog_SpriteSheetParam (*this); else return; RendDesc rend_desc(widget_rend_desc.get_rend_desc()); dialogtp->set_desc(rend_desc); dialogtp->set_tparam(tparam); if(dialogtp->run() == Gtk::RESPONSE_OK) tparam = dialogtp->get_tparam(); delete dialogtp; } void RenderSettings::on_render_pressed() { String filename=entry_filename.get_text(); calculated_target_name=target_name; if(filename.empty()) { canvas_interface_->get_ui_interface()->error(_("You must supply a filename!")); return; } // If the target type is not yet defined, // try to figure it out from the outfile. if(calculated_target_name.empty()) { try { String ext(filename_extension(filename)); if (ext.size()) ext=ext.substr(1); // skip initial '.' synfig::info("render target filename: '%s'; extension: '%s'", filename.c_str(), ext.c_str()); if(Target::ext_book().count(ext)) { calculated_target_name=Target::ext_book()[ext]; synfig::info("'%s' is a known extension - using target '%s'", ext.c_str(), calculated_target_name.c_str()); } else { calculated_target_name=ext; synfig::info("unknown extension"); } } catch(std::runtime_error x) { canvas_interface_->get_ui_interface()->error(_("Unable to determine proper target from filename.")); return; } } if(filename.empty() && calculated_target_name!="null") { canvas_interface_->get_ui_interface()->error(_("A filename is required for this target")); return; } hide(); render_passes.clear(); if (toggle_extract_alpha.get_active()) { String filename_alpha(filename_sans_extension(filename)+"-alpha"+filename_extension(filename)); render_passes.push_back(make_pair(TARGET_ALPHA_MODE_EXTRACT, filename_alpha)); render_passes.push_back(make_pair(TARGET_ALPHA_MODE_REDUCE, filename)); } else { render_passes.push_back(make_pair(TARGET_ALPHA_MODE_KEEP, filename)); } submit_next_render_pass(); return; } void RenderSettings::submit_next_render_pass() { if (render_passes.size()>0) { pair pass_info = render_passes.back(); render_passes.pop_back(); TargetAlphaMode pass_alpha_mode = pass_info.first; #ifdef WIN32 String pass_filename = Glib::locale_from_utf8(pass_info.second); #else String pass_filename = pass_info.second; #endif Target::Handle target=Target::create(calculated_target_name,pass_filename, tparam); if(!target) { canvas_interface_->get_ui_interface()->error(_("Unable to create target for ")+pass_filename); return; } // Test whether the output file is writable (path exists or has write permit) if (access(dirname(pass_filename).c_str(),W_OK) == -1) { canvas_interface_->get_ui_interface()->error(_("Unable to create file for ")+pass_filename+": "+strerror( errno )); return; } target->set_canvas(canvas_interface_->get_canvas()); RendDesc rend_desc(widget_rend_desc.get_rend_desc()); rend_desc.set_antialias((int)adjustment_antialias->get_value()); rend_desc.set_render_excluded_contexts(false); // If we are to only render the current frame if(toggle_single_frame.get_active()) rend_desc.set_time(canvas_interface_->get_time()); target->set_rend_desc(&rend_desc); target->set_quality((int)adjustment_quality->get_value()); if( !target->init(canvas_interface_->get_ui_interface().get()) ){ canvas_interface_->get_ui_interface()->error(_("Target initialization failure")); return; } if(pass_alpha_mode!=TARGET_ALPHA_MODE_KEEP) target->set_alpha_mode(pass_alpha_mode); canvas_interface_->get_ui_interface()->task(_("Rendering ")+pass_filename); /* if(async_renderer) { async_renderer->stop(); async_renderer.detach(); } */ async_renderer=new AsyncRenderer(target); async_renderer->signal_finished().connect( sigc::mem_fun(*this,&RenderSettings::on_finished)); async_renderer->start(); /* if(!target->render(canvas_interface_->get_ui_interface().get())) { canvas_interface_->get_ui_interface()->error(_("Render Failure")); canvas_interface_->get_ui_interface()->amount_complete(0,10000); return; } // Success! canvas_interface_->get_ui_interface()->task(pass_filename+_(" rendered successfully")); canvas_interface_->get_ui_interface()->amount_complete(0,10000); */ } return; } void RenderSettings::on_finished() { String text(_("File rendered successfully")); Real execution_time = async_renderer ? async_renderer->get_execution_time() : 0.0; if (execution_time > 0) text += strprintf(" (%f %s)", execution_time, _("sec")); canvas_interface_->get_ui_interface()->task(text); canvas_interface_->get_ui_interface()->amount_complete(0,10000); submit_next_render_pass(); } void RenderSettings::on_cancel_pressed() { hide(); } void RenderSettings::on_single_frame_toggle() { if(toggle_single_frame.get_active()) widget_rend_desc.disable_time_section(); else widget_rend_desc.enable_time_section(); }