diff --git a/src/data/format/formats.hpp b/src/data/format/formats.hpp index 4d98dfcb..54c7f5ec 100644 --- a/src/data/format/formats.hpp +++ b/src/data/format/formats.hpp @@ -96,7 +96,7 @@ Bitmap export_bitmap(const SetP& set, const vector& cards, bool scale_to_ Image export_image(const SetP& set, const CardP& card, const double zoom = 1.0, const Radians angle_radians = 0.0); Image export_image(const SetP& set, const vector& cards, bool scale_to_lowest_dpi = false, int padding = 0, const double zoom = 1.0, const Radians angle_radians = 0.0); -/// Export the image of one or more cards to a given filename +/// Export the image of one or more cards to a given filename, using the app's zoom and rotation settings void export_image(const SetP& set, const CardP& card, const String& filename); void export_image(const SetP& set, const vector& cards, const String& path, const String& filename_template, FilenameConflicts conflicts); diff --git a/src/data/format/image.cpp b/src/data/format/image.cpp index d482720f..70510fca 100644 --- a/src/data/format/image.cpp +++ b/src/data/format/image.cpp @@ -52,7 +52,7 @@ Rotation UnzoomedDataViewer::getRotation() const { return Rotation(angle, stylesheet->getCardRect(), zoom, 1.0, ROTATION_ATTACH_TOP_LEFT); } - double export_zoom = settings.stylesheetSettingsFor(set->stylesheetFor(card)).export_zoom(); + double export_zoom = settings.exportZoomSettingsFor(set->stylesheetFor(card)); bool use_viewer_rotation = !settings.stylesheetSettingsFor(set->stylesheetFor(card)).card_normal_export(); if (use_viewer_rotation) { @@ -195,10 +195,13 @@ Image export_image(const SetP& set, const vector& cards, bool scale_to_lo return img; } -void export_image(const SetP& set, const CardP& card, const String& filename) { - Image img = export_image(set, card); - img.SaveFile(filename); // can't use Bitmap::saveFile, it wants to know the file type - // but image.saveFile determines it automagicly +void export_image(const SetP& set, const CardP& card, const String& filename) { + const StyleSheet& stylesheet = set->stylesheetFor(card); + StyleSheetSettings& stylesheet_settings = settings.stylesheetSettingsFor(stylesheet); + double zoom = settings.exportZoomSettingsFor(stylesheet); + Radians angle = stylesheet_settings.card_normal_export() ? 0.0 : stylesheet_settings.card_angle() / 360.0 * 2.0 * M_PI; + Image img = export_image(set, card, zoom, angle); + img.SaveFile(filename); } void export_image(const SetP& set, const vector& cards, diff --git a/src/data/set.hpp b/src/data/set.hpp index 0775953f..a5b19729 100644 --- a/src/data/set.hpp +++ b/src/data/set.hpp @@ -164,7 +164,7 @@ public: ~SetView(); /// Get the set that is currently being viewed - //inline SetP getSet() const { return set; } + inline SetP getSet() const { return set; } /// Change the set that is being viewed void setSet(const SetP& set); diff --git a/src/data/settings.cpp b/src/data/settings.cpp index 898952f6..54cb9c63 100644 --- a/src/data/settings.cpp +++ b/src/data/settings.cpp @@ -53,6 +53,8 @@ IMPLEMENT_REFLECTION_ENUM(FilenameConflicts) { VALUE_N("number overwrite", CONFLICT_NUMBER_OVERWRITE); } +const vector Settings::export_zoom_choices = { 50,66,75,80,100,120,125,150,175,200 }; + const int COLUMN_NOT_INITIALIZED = -100000; ColumnSettings::ColumnSettings() @@ -127,7 +129,7 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(GameSettings) { StyleSheetSettings::StyleSheetSettings() : card_zoom (1.0, true) - , export_zoom (1.0, true) + , export_zoom_selection (0, true) , card_angle (0, true) , card_anti_alias (true, true) , card_borders (true, true) @@ -139,7 +141,7 @@ StyleSheetSettings::StyleSheetSettings() void StyleSheetSettings::useDefault(const StyleSheetSettings& ss) { if (card_zoom .isDefault()) card_zoom .assignDefault(ss.card_zoom); - if (export_zoom .isDefault()) export_zoom .assignDefault(ss.export_zoom); + if (export_zoom_selection .isDefault()) export_zoom_selection .assignDefault(ss.export_zoom_selection); if (card_angle .isDefault()) card_angle .assignDefault(ss.card_angle); if (card_anti_alias .isDefault()) card_anti_alias .assignDefault(ss.card_anti_alias); if (card_borders .isDefault()) card_borders .assignDefault(ss.card_borders); @@ -151,7 +153,7 @@ void StyleSheetSettings::useDefault(const StyleSheetSettings& ss) { IMPLEMENT_REFLECTION_NO_SCRIPT(StyleSheetSettings) { REFLECT(card_zoom); - REFLECT(export_zoom); + REFLECT(export_zoom_selection); REFLECT(card_angle); REFLECT(card_anti_alias); REFLECT(card_borders); @@ -182,19 +184,19 @@ IMPLEMENT_REFLECTION_ENUM(DarkModeType) { Settings settings; Settings::Settings() - : locale (_("en")) - , set_window_maximized (false) - , set_window_width (790) - , set_window_height (300) - , card_notes_height (40) - , open_sets_in_new_window(true) - , symbol_grid_size (30) - , symbol_grid (true) - , symbol_grid_snap (false) - , print_spacing (0.33) - , print_cutter_lines (CUTTER_ALL) - , dark_mode_type (DARKMODE_SYSTEM) - , internal_scale (1.0) + : locale (_("en")) + , set_window_maximized (false) + , set_window_width (790) + , set_window_height (300) + , card_notes_height (40) + , open_sets_in_new_window (true) + , symbol_grid_size (30) + , symbol_grid (true) + , symbol_grid_snap (false) + , print_spacing (0.33) + , print_cutter_lines (CUTTER_ALL) + , dark_mode_type (DARKMODE_SYSTEM) + , internal_scale_selection(0) , internal_image_extension(true) #if USE_OLD_STYLE_UPDATE_CHECKER , updates_url (_("https://magicseteditor.boards.net/page/downloads")) @@ -249,7 +251,29 @@ StyleSheetSettings& Settings::stylesheetSettingsFor(const StyleSheet& stylesheet if (!ss) ss = make_intrusive(); ss->useDefault(default_stylesheet_settings); // update default settings return *ss; -} +} + +double Settings::exportZoomSettingsFor(const StyleSheet& stylesheet) { + StyleSheetSettings& ss = stylesheetSettingsFor(stylesheet); + int export_zoom = ss.export_zoom_selection(); + if (export_zoom == 0) return adaptiveZoomSettingsFor(stylesheet, 300.0, 50.0); + if (export_zoom == 1) return adaptiveZoomSettingsFor(stylesheet, 300.0, 1.0); + if (export_zoom == 2) return adaptiveZoomSettingsFor(stylesheet, 150.0, 1.0); + return export_zoom_choices[export_zoom - 3] / 100; +} + +double Settings::internalScaleSettingsFor(const StyleSheet& stylesheet) { + if (internal_scale_selection == 0) return exportZoomSettingsFor(stylesheet); + if (internal_scale_selection == 1) return adaptiveZoomSettingsFor(stylesheet, 300.0, 50.0); + if (internal_scale_selection == 2) return adaptiveZoomSettingsFor(stylesheet, 300.0, 1.0); + if (internal_scale_selection == 3) return adaptiveZoomSettingsFor(stylesheet, 150.0, 1.0); + return export_zoom_choices[internal_scale_selection - 4] / 100; +} + +double Settings::adaptiveZoomSettingsFor(const StyleSheet& stylesheet, double dpi_target, double dpi_leeway) { + if (abs(stylesheet.card_dpi - dpi_target) <= dpi_leeway) return 1.0; + return dpi_target / max(10.0, stylesheet.card_dpi); +} IndexMap& Settings::exportOptionsFor(const ExportTemplate& export_template) { return export_options.get(export_template.name(), export_template.option_fields); @@ -300,7 +324,7 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(Settings) { REFLECT(print_cutter_lines); REFLECT(dark_mode_type); REFLECT(apprentice_location); - REFLECT(internal_scale); + REFLECT(internal_scale_selection); REFLECT(internal_image_extension); #if USE_OLD_STYLE_UPDATE_CHECKER REFLECT(updates_url); @@ -341,9 +365,7 @@ void Settings::read() { // make sure things aren't in a problematic state if (locale.Trim().empty()) locale = _("en"); if (symbol_grid_size < 30) symbol_grid_size = 30; - if (internal_scale < 1.0) internal_scale = 1.0; - if (default_stylesheet_settings.card_zoom < 0.5) default_stylesheet_settings.card_zoom = 1.0; - if (default_stylesheet_settings.export_zoom < 0.5) default_stylesheet_settings.export_zoom = 1.0; + if (default_stylesheet_settings.card_zoom < 0.5) default_stylesheet_settings.card_zoom = 1.0; } } diff --git a/src/data/settings.hpp b/src/data/settings.hpp index 0791731b..fd2076a0 100644 --- a/src/data/settings.hpp +++ b/src/data/settings.hpp @@ -97,15 +97,15 @@ public: StyleSheetSettings(); // Rendering/display settings - Defaultable card_zoom; - Defaultable export_zoom; + Defaultable card_zoom; + Defaultable export_zoom_selection; Defaultable card_angle; - Defaultable card_anti_alias; - Defaultable card_borders; - Defaultable card_draw_editing; - Defaultable card_normal_export; - Defaultable card_notes_export; - Defaultable card_spellcheck_enabled; + Defaultable card_anti_alias; + Defaultable card_borders; + Defaultable card_draw_editing; + Defaultable card_normal_export; + Defaultable card_notes_export; + Defaultable card_spellcheck_enabled; /// Where the settings are the default, use the value from ss void useDefault(const StyleSheetSettings& ss); @@ -175,12 +175,17 @@ public: // --------------------------------------------------- : Game/stylesheet specific /// Get the settings object for a specific game - GameSettings& gameSettingsFor (const Game& game); + GameSettings& gameSettingsFor (const Game& game); /// Get the settings for a column for a specific field in a game - ColumnSettings& columnSettingsFor (const Game& game, const Field& field); + ColumnSettings& columnSettingsFor (const Game& game, const Field& field); /// Get the settings object for a specific stylesheet - StyleSheetSettings& stylesheetSettingsFor(const StyleSheet& stylesheet); - + StyleSheetSettings& stylesheetSettingsFor (const StyleSheet& stylesheet); + double exportZoomSettingsFor (const StyleSheet& stylesheet); + double internalScaleSettingsFor(const StyleSheet& stylesheet); + double adaptiveZoomSettingsFor (const StyleSheet& stylesheet, double target_dpi, double leeway_dpi); + + static const vector export_zoom_choices; + private: map game_settings; map stylesheet_settings; @@ -211,13 +216,16 @@ public: Color darkModeColor(); // --------------------------------------------------- : Special game stuff + String apprentice_location; - // --------------------------------------------------- : Internal settings - double internal_scale; + // --------------------------------------------------- : Internal settings + + int internal_scale_selection; bool internal_image_extension; - // --------------------------------------------------- : Update checking + // --------------------------------------------------- : Update checking + #if USE_OLD_STYLE_UPDATE_CHECKER String updates_url; #endif @@ -227,7 +235,8 @@ public: bool check_updates_all; ///< Check updates of all packages, not just the program String website_url; - // --------------------------------------------------- : Installation settings + // --------------------------------------------------- : Installation settings + InstallType install_type; // --------------------------------------------------- : The io diff --git a/src/gui/preferences_window.cpp b/src/gui/preferences_window.cpp index 3e23c110..2a3194fb 100644 --- a/src/gui/preferences_window.cpp +++ b/src/gui/preferences_window.cpp @@ -57,14 +57,11 @@ private: wxComboBox* zoom; int zoom_int; - wxComboBox* export_zoom; - int export_zoom_int; + wxChoice* export_zoom; void onSelectColumns(wxCommandEvent&); void onZoomChange(wxCommandEvent&); void updateZoom(); - void onExportZoomChange(wxCommandEvent&); - void updateExportZoom(); }; class InternalPreferencesPage : public PreferencesPage { @@ -73,15 +70,9 @@ public: void store() override; private: - DECLARE_EVENT_TABLE(); - wxCheckBox* internal_image_extension; - wxComboBox* internal_scale; - int internal_scale_int; - - void onInternalScaleChange(wxCommandEvent&); - void updateInternalScale(); + wxChoice* internal_scale; }; // Preferences page for directories of programs @@ -225,10 +216,10 @@ DisplayPreferencesPage::DisplayPreferencesPage(Window* parent) borders = new wxCheckBox(this, wxID_ANY, _BUTTON_("show lines")); draw_editing = new wxCheckBox(this, wxID_ANY, _BUTTON_("show editing hints")); spellcheck_enabled = new wxCheckBox(this, wxID_ANY, _BUTTON_("spellcheck enabled")); - non_normal_export = new wxCheckBox(this, wxID_ANY, _BUTTON_("zoom export")); + non_normal_export = new wxCheckBox(this, wxID_ANY, _BUTTON_("rotation export")); notes_export = new wxCheckBox(this, wxID_ANY, _BUTTON_("notes export")); zoom = new wxComboBox(this, ID_ZOOM); - export_zoom = new wxComboBox(this, ID_EXPORT_ZOOM); + export_zoom = new wxChoice (this, ID_EXPORT_ZOOM); //wxButton* columns = new wxButton(this, ID_SELECT_COLUMNS, _BUTTON_("select")); // set values @@ -240,17 +231,19 @@ DisplayPreferencesPage::DisplayPreferencesPage(Window* parent) notes_export-> SetValue( settings.default_stylesheet_settings.card_notes_export()); zoom_int = static_cast(settings.default_stylesheet_settings.card_zoom() * 100); zoom->SetValue(String::Format(_("%d%%"),zoom_int)); - int zoom_choices[] = { 50,66,75,80,100,120,125,150,175,200 }; - for (unsigned int i = 0 ; i < sizeof(zoom_choices)/sizeof(zoom_choices[0]) ; ++i) { - zoom->Append(String::Format(_("%d%%"), zoom_choices[i])); + for (int i : Settings::export_zoom_choices) { + zoom->Append(String::Format(_("%d%%"), i)); } - export_zoom_int = static_cast(settings.default_stylesheet_settings.export_zoom() * 100); - export_zoom->SetValue(String::Format(_("%d%%"), export_zoom_int)); - int export_choices[] = { 50,66,75,80,100,120,125,150,175,200 }; - for (unsigned int i = 0; i < sizeof(export_choices) / sizeof(export_choices[0]); ++i) { - export_zoom->Append(String::Format(_("%d%%"), export_choices[i])); - } + export_zoom->Append(_LABEL_("export around 300")); + export_zoom->Append(_LABEL_("export force 300")); + export_zoom->Append(_LABEL_("export force 150")); + for (int i : Settings::export_zoom_choices) { + export_zoom->Append(String::Format(_("%d%%"), i)); + } + int default_export_zoom = settings.default_stylesheet_settings.export_zoom_selection(); + if (default_export_zoom < 0 || default_export_zoom > export_zoom->GetCount() - 1) default_export_zoom = 0; + export_zoom->SetSelection(default_export_zoom); // init sizer wxSizer* s = new wxBoxSizer(wxVERTICAL); @@ -268,7 +261,7 @@ DisplayPreferencesPage::DisplayPreferencesPage(Window* parent) s4->Add(new wxStaticText(this, wxID_ANY, _LABEL_("export")), 0, wxALL & ~wxLEFT, 4); s4->AddSpacer(2); s4->Add(export_zoom); - s4->Add(new wxStaticText(this, wxID_ANY, _LABEL_("percent of normal")), 1, wxALL & ~wxRIGHT, 4); + //s4->Add(new wxStaticText(this, wxID_ANY, _LABEL_("percent of normal")), 1, wxALL & ~wxRIGHT, 4); s2->Add(s3, 0, wxEXPAND | wxALL, 4); s2->Add(s4, 0, wxEXPAND | wxALL, 4); @@ -290,8 +283,8 @@ void DisplayPreferencesPage::store() { settings.default_stylesheet_settings.card_notes_export = notes_export->GetValue(); updateZoom(); - settings.default_stylesheet_settings.card_zoom = zoom_int / 100.0; - settings.default_stylesheet_settings.export_zoom = export_zoom_int / 100.0; + settings.default_stylesheet_settings.card_zoom = zoom_int / 100.0; + settings.default_stylesheet_settings.export_zoom_selection = export_zoom->GetSelection(); } void DisplayPreferencesPage::onSelectColumns(wxCommandEvent&) { @@ -303,50 +296,38 @@ void DisplayPreferencesPage::onZoomChange(wxCommandEvent&) { } void DisplayPreferencesPage::updateZoom() { - String s = zoom->GetValue(); - int i = zoom_int; - if (wxSscanf(s.c_str(),_("%u"),&i)) { - zoom_int = min(max(i,1),1000); - } - zoom->SetValue(String::Format(_("%d%%"),(int)zoom_int)); -} - -void DisplayPreferencesPage::onExportZoomChange(wxCommandEvent&) { - updateExportZoom(); -} - -void DisplayPreferencesPage::updateExportZoom() { - String s = export_zoom->GetValue(); - int i = export_zoom_int; - if (wxSscanf(s.c_str(), _("%u"), &i)) { - export_zoom_int = min(max(i, 1), 1000); - } - export_zoom->SetValue(String::Format(_("%d%%"), (int)export_zoom_int)); + String s = zoom->GetValue(); + int i = zoom_int; + if (wxSscanf(s.c_str(),_("%u"),&i)) { + zoom_int = min(max(i,1),1000); + } + zoom->SetValue(String::Format(_("%d%%"),(int)zoom_int)); } BEGIN_EVENT_TABLE(DisplayPreferencesPage, wxPanel) EVT_BUTTON (ID_SELECT_COLUMNS, DisplayPreferencesPage::onSelectColumns) EVT_COMBOBOX (ID_ZOOM, DisplayPreferencesPage::onZoomChange) EVT_TEXT_ENTER (ID_ZOOM, DisplayPreferencesPage::onZoomChange) - EVT_COMBOBOX(ID_EXPORT_ZOOM, DisplayPreferencesPage::onExportZoomChange) - EVT_TEXT_ENTER(ID_EXPORT_ZOOM, DisplayPreferencesPage::onExportZoomChange) END_EVENT_TABLE () // ----------------------------------------------------------------------------- : Preferences page : internal InternalPreferencesPage::InternalPreferencesPage(Window* parent) : PreferencesPage(parent) { internal_image_extension = new wxCheckBox(this, wxID_ANY, _BUTTON_("internal image extension")); - internal_scale = new wxComboBox(this, ID_INTERNAL_SCALE); + internal_scale = new wxChoice(this, ID_INTERNAL_SCALE); internal_image_extension->SetValue(settings.internal_image_extension); - internal_scale_int = static_cast(settings.internal_scale * 100); - internal_scale->SetValue(String::Format(_("%d%%"), internal_scale_int)); - - int choices[] = { 100,120,125,150,175,200 }; - for (unsigned int i = 0; i < sizeof(choices) / sizeof(choices[0]); ++i) { - internal_scale->Append(String::Format(_("%d%%"), choices[i])); - } + internal_scale->Append(_LABEL_("use export scale")); + internal_scale->Append(_LABEL_("export around 300")); + internal_scale->Append(_LABEL_("export force 300")); + internal_scale->Append(_LABEL_("export force 150")); + for (int i : Settings::export_zoom_choices) { + internal_scale->Append(String::Format(_("%d%%"), i)); + } + int default_internal_scale = settings.internal_scale_selection; + if (default_internal_scale < 0 || default_internal_scale > internal_scale->GetCount() - 1) default_internal_scale = 0; + internal_scale->SetSelection(default_internal_scale); wxSizer* s = new wxBoxSizer(wxVERTICAL); wxSizer* s2 = new wxStaticBoxSizer(wxVERTICAL, this, _LABEL_("storage")); @@ -354,7 +335,7 @@ InternalPreferencesPage::InternalPreferencesPage(Window* parent) : PreferencesPa s3->Add(new wxStaticText(this, wxID_ANY, _LABEL_("scale")), 0, wxALL & ~wxLEFT, 4); s3->AddSpacer(2); s3->Add(internal_scale); - s3->Add(new wxStaticText(this, wxID_ANY, _LABEL_("percent of normal")), 1, wxALL & ~wxRIGHT, 4); + //s3->Add(new wxStaticText(this, wxID_ANY, _LABEL_("percent of normal")), 1, wxALL & ~wxRIGHT, 4); s2->Add(s3); s2->Add(new wxStaticText(this, wxID_ANY, _LABEL_("internal scale desc")), 0, wxALL & ~wxLEFT, 4); s2->Add(internal_image_extension, 0, wxEXPAND | wxALL, 4); @@ -364,29 +345,10 @@ InternalPreferencesPage::InternalPreferencesPage(Window* parent) : PreferencesPa } void InternalPreferencesPage::store() { - settings.internal_image_extension = internal_image_extension->GetValue(); - - updateInternalScale(); - settings.internal_scale = internal_scale_int / 100.0; + settings.internal_image_extension = internal_image_extension->GetValue(); + settings.internal_scale_selection = internal_scale->GetSelection(); } -void InternalPreferencesPage::onInternalScaleChange(wxCommandEvent&) { - updateInternalScale(); -} - -void InternalPreferencesPage::updateInternalScale() { - String s = internal_scale->GetValue(); - int i = internal_scale_int; - if (wxSscanf(s.c_str(), _("%u"), &i)) { - internal_scale_int = min(max(i, 1), 1000); - } - internal_scale->SetValue(String::Format(_("%d%%"), (int)internal_scale_int)); -} - -BEGIN_EVENT_TABLE(InternalPreferencesPage, wxPanel) - EVT_COMBOBOX(ID_INTERNAL_SCALE, InternalPreferencesPage::onInternalScaleChange) -END_EVENT_TABLE() - // ----------------------------------------------------------------------------- : Preferences page : directories DirsPreferencesPage::DirsPreferencesPage(Window* parent) diff --git a/src/gui/value/image.cpp b/src/gui/value/image.cpp index 52acde83..bac4c419 100644 --- a/src/gui/value/image.cpp +++ b/src/gui/value/image.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -59,12 +60,21 @@ void ImageValueEditor::sliceImage(const Image& image, const String& filename, co if (s.ShowModal() == wxID_OK) { // store the image into the set LocalFileName new_image_file = getLocalPackage().newFileName(field().name, settings.internal_image_extension ? _(".png") : _("")); // a new unique name in the package - + // Specify a desired size based on the stylesheet and a scale multiplier defined within the user's settings. // Storing at a greater than 100% resolution allows for better exports >100%, but may change how images look when filters (sharpen) are applied. // It also disrupts some of the patterns in use for doing popout planeswalkers since you have to do the math at both scales. // Additionally, this bloats the set file size as even under-resolution images are upscaled to the new minimum size. - Image img = s.getImage(settings.internal_scale); + double internal_scale = 1.0; + try { + // surrounding this in try catch to be safe for now. maybe this is overkill + StyleSheetP stylesheet = editor().getCard()->stylesheet; + if (!stylesheet) stylesheet = editor().getSet()->stylesheet; + internal_scale = settings.internalScaleSettingsFor(*stylesheet); + } catch (...) { + queue_message(MESSAGE_ERROR, _("Could not find stylesheet to determine export zoom.\nfilename: " + filename + _("\ncardname: " + cardname))); + } + Image img = s.getImage(internal_scale); img.SaveFile(getLocalPackage().nameOut(new_image_file), wxBITMAP_TYPE_PNG); // always use PNG images, see #69. Disk space is cheap anyway. addAction(value_action(valueP(), new_image_file)); }