forked from mia/Aegisub
Make AR-changing resampling more user-friendly
Add modes where the borders to add or remove are automatically calculated rather than forcing the user to do it manually, and hopefully make it a bit clearer what exactly will happen.
This commit is contained in:
parent
7e2780f57a
commit
0f030c45f3
5 changed files with 65 additions and 15 deletions
|
@ -24,9 +24,10 @@
|
||||||
#include "help_button.h"
|
#include "help_button.h"
|
||||||
#include "libresrc/libresrc.h"
|
#include "libresrc/libresrc.h"
|
||||||
#include "resolution_resampler.h"
|
#include "resolution_resampler.h"
|
||||||
|
#include "validators.h"
|
||||||
#include "video_context.h"
|
#include "video_context.h"
|
||||||
|
|
||||||
#include <boost/rational.hpp>
|
#include <boost/range/size.hpp>
|
||||||
#include <wx/checkbox.h>
|
#include <wx/checkbox.h>
|
||||||
#include <wx/sizer.h>
|
#include <wx/sizer.h>
|
||||||
#include <wx/spinctrl.h>
|
#include <wx/spinctrl.h>
|
||||||
|
@ -88,9 +89,9 @@ DialogResample::DialogResample(agi::Context *c, ResampleSettings &settings)
|
||||||
from_script = new wxButton(this, -1, _("From s&cript"));
|
from_script = new wxButton(this, -1, _("From s&cript"));
|
||||||
from_script->Enable(false);
|
from_script->Enable(false);
|
||||||
|
|
||||||
change_ar = new wxCheckBox(this, -1, _("&Change aspect ratio"));
|
wxString ar_modes[] = {_("Stretch"), _("Add borders"), _("Remove borders"), _("Manual")};
|
||||||
change_ar->SetValidator(wxGenericValidator(&settings.change_ar));
|
ar_mode = new wxRadioBox(this, -1, _("Aspect Ratio Handling"), wxDefaultPosition,
|
||||||
change_ar->Enable(false);
|
wxDefaultSize, boost::size(ar_modes), ar_modes, 1, 4, MakeEnumBinder(&settings.ar_mode));
|
||||||
|
|
||||||
// Position the controls
|
// Position the controls
|
||||||
auto margin_sizer = new wxGridSizer(3, 3, 5, 5);
|
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"));
|
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(dest_res_sizer, wxSizerFlags(1).Expand().Border(wxBOTTOM));
|
||||||
dest_res_box->Add(change_ar);
|
|
||||||
|
|
||||||
auto main_sizer = new wxBoxSizer(wxVERTICAL);
|
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(source_res_sizer, wxSizerFlags(0).Expand().Border());
|
||||||
main_sizer->Add(dest_res_box, 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));
|
main_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP), wxSizerFlags().Expand().Border(wxALL & ~wxTOP));
|
||||||
SetSizerAndFit(main_sizer);
|
SetSizerAndFit(main_sizer);
|
||||||
CenterOnParent();
|
CenterOnParent();
|
||||||
|
|
||||||
|
TransferDataToWindow();
|
||||||
|
UpdateButtons();
|
||||||
|
|
||||||
// Bind events
|
// Bind events
|
||||||
using std::bind;
|
using std::bind;
|
||||||
Bind(wxEVT_BUTTON, bind(&HelpButton::OpenPage, "Resample resolution"), wxID_HELP);
|
Bind(wxEVT_BUTTON, bind(&HelpButton::OpenPage, "Resample resolution"), wxID_HELP);
|
||||||
Bind(wxEVT_SPINCTRL, [=](wxCommandEvent&) { UpdateButtons(); });
|
Bind(wxEVT_SPINCTRL, [=](wxCommandEvent&) { UpdateButtons(); });
|
||||||
|
Bind(wxEVT_RADIOBOX, [=](wxCommandEvent&) { UpdateButtons(); });
|
||||||
from_video->Bind(wxEVT_BUTTON, &DialogResample::SetDestFromVideo, this);
|
from_video->Bind(wxEVT_BUTTON, &DialogResample::SetDestFromVideo, this);
|
||||||
from_script->Bind(wxEVT_BUTTON, &DialogResample::SetSourceFromScript, this);
|
from_script->Bind(wxEVT_BUTTON, &DialogResample::SetSourceFromScript, this);
|
||||||
symmetrical->Bind(wxEVT_CHECKBOX, &DialogResample::OnSymmetrical, 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));
|
(dest_x->GetValue() != video_w || dest_y->GetValue() != video_h));
|
||||||
from_script->Enable(source_x->GetValue() != script_w || source_y->GetValue() != script_h);
|
from_script->Enable(source_x->GetValue() != script_w || source_y->GetValue() != script_h);
|
||||||
|
|
||||||
boost::rational<int> source_ar(source_x->GetValue(), source_y->GetValue());
|
auto source_ar = double(source_x->GetValue()) / source_y->GetValue();
|
||||||
boost::rational<int> dest_ar(dest_x->GetValue(), dest_y->GetValue());
|
auto dest_ar = double(dest_x->GetValue()) / dest_y->GetValue();
|
||||||
change_ar->Enable(source_ar != dest_ar);
|
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 &) {
|
void DialogResample::OnSymmetrical(wxCommandEvent &) {
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
namespace agi { struct Context; }
|
namespace agi { struct Context; }
|
||||||
class AssFile;
|
class AssFile;
|
||||||
class wxCheckBox;
|
class wxCheckBox;
|
||||||
|
class wxRadioBox;
|
||||||
class wxSpinCtrl;
|
class wxSpinCtrl;
|
||||||
struct ResampleSettings;
|
struct ResampleSettings;
|
||||||
|
|
||||||
|
@ -42,7 +43,7 @@ class DialogResample final : public wxDialog {
|
||||||
wxSpinCtrl *dest_x;
|
wxSpinCtrl *dest_x;
|
||||||
wxSpinCtrl *dest_y;
|
wxSpinCtrl *dest_y;
|
||||||
wxCheckBox *symmetrical;
|
wxCheckBox *symmetrical;
|
||||||
wxCheckBox *change_ar;
|
wxRadioBox *ar_mode;
|
||||||
wxSpinCtrl *margin_ctrl[4];
|
wxSpinCtrl *margin_ctrl[4];
|
||||||
|
|
||||||
wxButton *from_script;
|
wxButton *from_script;
|
||||||
|
|
|
@ -154,6 +154,36 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResampleResolution(AssFile *ass, ResampleSettings settings) {
|
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
|
// Add margins to original resolution
|
||||||
settings.source_x += settings.margin[LEFT] + settings.margin[RIGHT];
|
settings.source_x += settings.margin[LEFT] + settings.margin[RIGHT];
|
||||||
settings.source_y += settings.margin[TOP] + settings.margin[BOTTOM];
|
settings.source_y += settings.margin[TOP] + settings.margin[BOTTOM];
|
||||||
|
@ -162,12 +192,9 @@ void ResampleResolution(AssFile *ass, ResampleSettings settings) {
|
||||||
settings.margin,
|
settings.margin,
|
||||||
double(settings.dest_x) / double(settings.source_x),
|
double(settings.dest_x) / double(settings.source_x),
|
||||||
double(settings.dest_y) / double(settings.source_y),
|
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)
|
for (auto& line : ass->Styles)
|
||||||
resample_style(&state, line);
|
resample_style(&state, line);
|
||||||
for (auto& line : ass->Events)
|
for (auto& line : ass->Events)
|
||||||
|
|
|
@ -16,6 +16,13 @@
|
||||||
|
|
||||||
class AssFile;
|
class AssFile;
|
||||||
|
|
||||||
|
enum class ResampleARMode {
|
||||||
|
Stretch,
|
||||||
|
AddBorder,
|
||||||
|
RemoveBorder,
|
||||||
|
Manual
|
||||||
|
};
|
||||||
|
|
||||||
/// Configuration parameters for a resample
|
/// Configuration parameters for a resample
|
||||||
struct ResampleSettings {
|
struct ResampleSettings {
|
||||||
int margin[4]; ///< Amount to add to each margin
|
int margin[4]; ///< Amount to add to each margin
|
||||||
|
@ -23,7 +30,7 @@ struct ResampleSettings {
|
||||||
int source_y; ///< Original Y resolution
|
int source_y; ///< Original Y resolution
|
||||||
int dest_x; ///< New X resolution
|
int dest_x; ///< New X resolution
|
||||||
int dest_y; ///< New Y 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
|
/// Resample the subtitles in the project
|
||||||
|
|
|
@ -76,6 +76,7 @@ class EnumBinder final : public wxValidator {
|
||||||
T *value;
|
T *value;
|
||||||
|
|
||||||
wxObject *Clone() const override { return new EnumBinder<T>(value); }
|
wxObject *Clone() const override { return new EnumBinder<T>(value); }
|
||||||
|
bool Validate(wxWindow *) override { return true; }
|
||||||
|
|
||||||
bool TransferFromWindow() override {
|
bool TransferFromWindow() override {
|
||||||
if (wxRadioBox *rb = dynamic_cast<wxRadioBox*>(GetWindow()))
|
if (wxRadioBox *rb = dynamic_cast<wxRadioBox*>(GetWindow()))
|
||||||
|
|
Loading…
Reference in a new issue