diff --git a/src/dialog_resample.cpp b/src/dialog_resample.cpp index f94854fc4..16f8e3c88 100644 --- a/src/dialog_resample.cpp +++ b/src/dialog_resample.cpp @@ -24,9 +24,10 @@ #include "help_button.h" #include "libresrc/libresrc.h" #include "resolution_resampler.h" +#include "validators.h" #include "video_context.h" -#include +#include #include #include #include @@ -88,9 +89,9 @@ DialogResample::DialogResample(agi::Context *c, ResampleSettings &settings) from_script = new wxButton(this, -1, _("From s&cript")); from_script->Enable(false); - change_ar = new wxCheckBox(this, -1, _("&Change aspect ratio")); - change_ar->SetValidator(wxGenericValidator(&settings.change_ar)); - change_ar->Enable(false); + wxString ar_modes[] = {_("Stretch"), _("Add borders"), _("Remove borders"), _("Manual")}; + ar_mode = new wxRadioBox(this, -1, _("Aspect Ratio Handling"), wxDefaultPosition, + wxDefaultSize, boost::size(ar_modes), ar_modes, 1, 4, MakeEnumBinder(&settings.ar_mode)); // Position the controls auto margin_sizer = new wxGridSizer(3, 3, 5, 5); @@ -121,20 +122,24 @@ DialogResample::DialogResample(agi::Context *c, ResampleSettings &settings) auto dest_res_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Destination Resolution")); dest_res_box->Add(dest_res_sizer, wxSizerFlags(1).Expand().Border(wxBOTTOM)); - dest_res_box->Add(change_ar); auto main_sizer = new wxBoxSizer(wxVERTICAL); - main_sizer->Add(margin_box, wxSizerFlags(1).Expand().Border()); main_sizer->Add(source_res_sizer, wxSizerFlags(0).Expand().Border()); main_sizer->Add(dest_res_box, wxSizerFlags(0).Expand().Border()); + main_sizer->Add(ar_mode, wxSizerFlags(0).Expand().Border()); + main_sizer->Add(margin_box, wxSizerFlags(1).Expand().Border()); main_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP), wxSizerFlags().Expand().Border(wxALL & ~wxTOP)); SetSizerAndFit(main_sizer); CenterOnParent(); + TransferDataToWindow(); + UpdateButtons(); + // Bind events using std::bind; Bind(wxEVT_BUTTON, bind(&HelpButton::OpenPage, "Resample resolution"), wxID_HELP); Bind(wxEVT_SPINCTRL, [=](wxCommandEvent&) { UpdateButtons(); }); + Bind(wxEVT_RADIOBOX, [=](wxCommandEvent&) { UpdateButtons(); }); from_video->Bind(wxEVT_BUTTON, &DialogResample::SetDestFromVideo, this); from_script->Bind(wxEVT_BUTTON, &DialogResample::SetSourceFromScript, this); symmetrical->Bind(wxEVT_CHECKBOX, &DialogResample::OnSymmetrical, this); @@ -157,9 +162,18 @@ void DialogResample::UpdateButtons() { (dest_x->GetValue() != video_w || dest_y->GetValue() != video_h)); from_script->Enable(source_x->GetValue() != script_w || source_y->GetValue() != script_h); - boost::rational source_ar(source_x->GetValue(), source_y->GetValue()); - boost::rational dest_ar(dest_x->GetValue(), dest_y->GetValue()); - change_ar->Enable(source_ar != dest_ar); + auto source_ar = double(source_x->GetValue()) / source_y->GetValue(); + auto dest_ar = double(dest_x->GetValue()) / dest_y->GetValue(); + bool ar_changed = abs(source_ar - dest_ar) / dest_ar > .01; + + ar_mode->Enable(ar_changed); + + bool margins = ar_changed && ar_mode->GetSelection() == (int)ResampleARMode::Manual; + symmetrical->Enable(margins); + margin_ctrl[LEFT]->Enable(margins); + margin_ctrl[TOP]->Enable(margins); + margin_ctrl[RIGHT]->Enable(margins && !symmetrical->GetValue()); + margin_ctrl[BOTTOM]->Enable(margins && !symmetrical->GetValue()); } void DialogResample::OnSymmetrical(wxCommandEvent &) { diff --git a/src/dialog_resample.h b/src/dialog_resample.h index 5fdc57e41..1f8f084c9 100644 --- a/src/dialog_resample.h +++ b/src/dialog_resample.h @@ -22,6 +22,7 @@ namespace agi { struct Context; } class AssFile; class wxCheckBox; +class wxRadioBox; class wxSpinCtrl; struct ResampleSettings; @@ -42,7 +43,7 @@ class DialogResample final : public wxDialog { wxSpinCtrl *dest_x; wxSpinCtrl *dest_y; wxCheckBox *symmetrical; - wxCheckBox *change_ar; + wxRadioBox *ar_mode; wxSpinCtrl *margin_ctrl[4]; wxButton *from_script; diff --git a/src/resolution_resampler.cpp b/src/resolution_resampler.cpp index 7220992b5..d1ddf8d8b 100644 --- a/src/resolution_resampler.cpp +++ b/src/resolution_resampler.cpp @@ -154,6 +154,36 @@ namespace { } void ResampleResolution(AssFile *ass, ResampleSettings settings) { + auto horizontal_stretch = 1.0; + auto old_ar = double(settings.source_x) / settings.source_y; + auto new_ar = double(settings.dest_x) / settings.dest_y; + bool border_horizontally = new_ar > old_ar; + // Don't convert aspect ratio if it's very close to correct + // (for reference, 848x480 <-> 1280x720 is .006) + if (abs(old_ar - new_ar) / new_ar > .01) { + switch (settings.ar_mode) { + case ResampleARMode::RemoveBorder: + border_horizontally = !border_horizontally; + case ResampleARMode::AddBorder: + if (border_horizontally) // Wider/Shorter + settings.margin[LEFT] = settings.margin[RIGHT] = (settings.source_y * new_ar - settings.source_x) / 2; + else // Taller/Narrower + settings.margin[TOP] = settings.margin[BOTTOM] = (settings.source_x / new_ar - settings.source_y) / 2; + break; + case ResampleARMode::Stretch: + horizontal_stretch = new_ar / old_ar; + break; + case ResampleARMode::Manual: + old_ar = + double(settings.source_x + settings.margin[LEFT] + settings.margin[RIGHT]) / + double(settings.source_y + settings.margin[TOP] + settings.margin[BOTTOM]); + + if (abs(old_ar - new_ar) / new_ar > .01) + horizontal_stretch = new_ar / old_ar; + break; + } + } + // Add margins to original resolution settings.source_x += settings.margin[LEFT] + settings.margin[RIGHT]; settings.source_y += settings.margin[TOP] + settings.margin[BOTTOM]; @@ -162,12 +192,9 @@ void ResampleResolution(AssFile *ass, ResampleSettings settings) { settings.margin, double(settings.dest_x) / double(settings.source_x), double(settings.dest_y) / double(settings.source_y), - 1.0 + horizontal_stretch }; - if (settings.change_ar) - state.ar = state.rx / state.ry; - for (auto& line : ass->Styles) resample_style(&state, line); for (auto& line : ass->Events) diff --git a/src/resolution_resampler.h b/src/resolution_resampler.h index 8b55b37c7..43b7211c7 100644 --- a/src/resolution_resampler.h +++ b/src/resolution_resampler.h @@ -16,6 +16,13 @@ class AssFile; +enum class ResampleARMode { + Stretch, + AddBorder, + RemoveBorder, + Manual +}; + /// Configuration parameters for a resample struct ResampleSettings { int margin[4]; ///< Amount to add to each margin @@ -23,7 +30,7 @@ struct ResampleSettings { int source_y; ///< Original Y resolution int dest_x; ///< New X resolution int dest_y; ///< New Y resolution - bool change_ar; ///< Should the aspect ratio of the subs be changed? + ResampleARMode ar_mode; ///< What to do if the old AR and new AR don't match }; /// Resample the subtitles in the project diff --git a/src/validators.h b/src/validators.h index d130b9d9d..9031909b5 100644 --- a/src/validators.h +++ b/src/validators.h @@ -76,6 +76,7 @@ class EnumBinder final : public wxValidator { T *value; wxObject *Clone() const override { return new EnumBinder(value); } + bool Validate(wxWindow *) override { return true; } bool TransferFromWindow() override { if (wxRadioBox *rb = dynamic_cast(GetWindow()))