diff --git a/build/Aegisub/Aegisub.vcxproj b/build/Aegisub/Aegisub.vcxproj index 4241a63c2..ce9b19969 100644 --- a/build/Aegisub/Aegisub.vcxproj +++ b/build/Aegisub/Aegisub.vcxproj @@ -1,4 +1,4 @@ - + @@ -173,6 +173,7 @@ + @@ -370,6 +371,7 @@ + diff --git a/build/Aegisub/Aegisub.vcxproj.filters b/build/Aegisub/Aegisub.vcxproj.filters index 64750b2fd..c29884ca8 100644 --- a/build/Aegisub/Aegisub.vcxproj.filters +++ b/build/Aegisub/Aegisub.vcxproj.filters @@ -621,6 +621,9 @@ Subtitle formats + + Features\Resolution resampler + @@ -1175,9 +1178,12 @@ Subtitle formats + + Features\Resolution resampler + - + \ No newline at end of file diff --git a/src/Makefile b/src/Makefile index 3cfde47f0..32b187536 100644 --- a/src/Makefile +++ b/src/Makefile @@ -184,6 +184,7 @@ SRC += \ dialog_translation.cpp \ dialog_version_check.cpp \ dialog_video_details.cpp \ + dialog_video_properties.cpp \ export_fixstyle.cpp \ export_framerate.cpp \ fft.cpp \ diff --git a/src/dialog_video_properties.cpp b/src/dialog_video_properties.cpp new file mode 100644 index 000000000..b0a687881 --- /dev/null +++ b/src/dialog_video_properties.cpp @@ -0,0 +1,156 @@ +// Copyright (c) 2014, Thomas Goyne +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// Aegisub Project http://www.aegisub.org/ + +#include "dialog_video_properties.h" + +#include "ass_file.h" +#include "include/aegisub/video_provider.h" +#include "options.h" +#include "resolution_resampler.h" + +#include +#include +#include +#include + +namespace { +enum { + MISMATCH_IGNORE, + MISMATCH_PROMPT, + MISMATCH_RESAMPLE, + MISMATCH_SET +}; +enum { + FIX_IGNORE, + FIX_SET, + FIX_RESAMPLE +}; + +class Prompt : public wxDialog { +public: + Prompt(wxWindow *parent, bool ar_changed, int sx, int sy, int vx, int vy) + : wxDialog(parent, -1, _("Resolution mismatch")) + { + auto label_text = wxString::Format(_("The resolution of the loaded video and the resolution specified for the subtitles don't match.\n\nVideo resolution:\t%d x %d\nScript resolution:\t%d x %d\n\nChange subtitles resolution to match video?"), vx, vy, sx, sy); + + auto sizer = new wxBoxSizer(wxVERTICAL); + sizer->Add(new wxStaticText(this, -1, label_text), wxSizerFlags().Border()); + + wxRadioBox *rb; + if (ar_changed) { + wxString choices[] = { + _("Set to video resolution"), + _("Resample script (stretch to new aspect ratio)"), + _("Resample script (add borders)"), + _("Resample script (remove borders)") + }; + rb = new wxRadioBox(this, -1, "", wxDefaultPosition, wxDefaultSize, 4, choices, 1); + } + else { + wxString choices[] = { + _("Set to video resolution"), + _("Resample script"), + }; + rb = new wxRadioBox(this, -1, "", wxDefaultPosition, wxDefaultSize, 2, choices, 1); + } + sizer->Add(rb, wxSizerFlags().Border(wxALL & ~wxTOP).Expand()); + sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP), wxSizerFlags().Border().Expand()); + + unsigned int sel = OPT_GET("Video/Last Script Resolution Mismatch Choice")->GetInt(); + rb->SetSelection(std::min(sel - 1, rb->GetCount())); + + SetSizerAndFit(sizer); + CenterOnParent(); + + Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { EndModal(rb->GetSelection() + 1); }, wxID_OK); + Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { EndModal(0); }, wxID_CANCEL); + } +}; +} + +bool UpdateVideoProperties(AssFile *file, const VideoProvider *new_provider, wxWindow *parent) { + bool commit_subs = false; + + // When opening dummy video only want to set the script properties if + // they were previously unset + bool set_properties = new_provider->ShouldSetVideoProperties(); + + auto matrix = new_provider->GetColorSpace(); + if (set_properties && matrix != file->GetScriptInfo("YCbCr Matrix")) { + file->SetScriptInfo("YCbCr Matrix", matrix); + commit_subs = true; + } + + // Check that the script resolution matches the video resolution + int sx = file->GetScriptInfoAsInt("PlayResX"); + int sy = file->GetScriptInfoAsInt("PlayResY"); + int vx = new_provider->GetWidth(); + int vy = new_provider->GetHeight(); + + // If the script resolution hasn't been set at all just force it to the + // video resolution + if (sx == 0 && sy == 0) { + file->SetScriptInfo("PlayResX", std::to_string(vx)); + file->SetScriptInfo("PlayResY", std::to_string(vy)); + return true; + } + + if (!set_properties) + return false; + + // Treat exact multiples of the video resolution as equaling the resolution + // for the people who use that for subpixel precision (which is mostly + // pointless these days due to decimals being supported almost everywhere) + if (sx % vx == 0 && sy % vy == 0) + return commit_subs; + + auto sar = double(sx) / sy; + auto var = double(vx) / vy; + bool ar_changed = abs(sar - var) / var > .01; + + switch (OPT_GET("Video/Script Resolution Mismatch")->GetInt()) { + case MISMATCH_IGNORE: default: + return commit_subs; + + case MISMATCH_SET: + file->SetScriptInfo("PlayResX", std::to_string(vx)); + file->SetScriptInfo("PlayResY", std::to_string(vy)); + return true; + + case MISMATCH_RESAMPLE: + // Fallthrough to prompt if the AR changed + if (!ar_changed) { + ResampleResolution(file, { + {0, 0, 0, 0}, + sx, sy, vx, vy, + ResampleARMode::Stretch + }); + return true; + } + + case MISMATCH_PROMPT: + int res = Prompt(parent, ar_changed, sx, sy, vx, vy).ShowModal(); + if (res == FIX_IGNORE) return commit_subs; + OPT_SET("Video/Last Script Resolution Mismatch Choice")->SetInt(res); + + ResampleResolution(file, { + {0, 0, 0, 0}, + sx, sy, vx, vy, + static_cast(res - FIX_RESAMPLE) + }); + return true; + } +} diff --git a/src/dialog_video_properties.h b/src/dialog_video_properties.h new file mode 100644 index 000000000..5d48445a4 --- /dev/null +++ b/src/dialog_video_properties.h @@ -0,0 +1,23 @@ +// Copyright (c) 2014, Thomas Goyne +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// Aegisub Project http://www.aegisub.org/ + +class AssFile; +class VideoProvider; +class wxWindow; + +/// Update the video properties for a newly opened video, possibly prompting the user about what to do +/// @return Does the file need to be committed? +bool UpdateVideoProperties(AssFile *file, const VideoProvider *new_provider, wxWindow *parent); \ No newline at end of file diff --git a/src/libresrc/default_config.json b/src/libresrc/default_config.json index d0ef192d0..9a5e8916e 100644 --- a/src/libresrc/default_config.json +++ b/src/libresrc/default_config.json @@ -572,7 +572,6 @@ }, "Video" : { - "Check Script Res" : 1, "Default Zoom" : 7, "Detached" : { "Enabled" : false, @@ -592,9 +591,11 @@ "Pattern" : false }, "Force BT.601" : true, + "Last Script Resolution Mismatch Choice" : 2, "Open Audio" : false, "Overscan Mask" : false, "Provider" : "ffmpegsource", + "Script Resolution Mismatch" : 1, "Slider" : { "Fast Jump Step" : 10, "Show Keyframes" : true diff --git a/src/libresrc/osx/default_config.json b/src/libresrc/osx/default_config.json index 458cf0d2c..91615704a 100644 --- a/src/libresrc/osx/default_config.json +++ b/src/libresrc/osx/default_config.json @@ -572,7 +572,6 @@ }, "Video" : { - "Check Script Res" : 1, "Default Zoom" : 7, "Detached" : { "Enabled" : false, @@ -592,9 +591,11 @@ "Pattern" : false }, "Force BT.601" : true, + "Last Script Resolution Mismatch Choice" : 2, "Open Audio" : false, "Overscan Mask" : false, "Provider" : "ffmpegsource", + "Script Resolution Mismatch" : 1, "Slider" : { "Fast Jump Step" : 10, "Show Keyframes" : true diff --git a/src/preferences.cpp b/src/preferences.cpp index 8e93cac05..0f2fab6cb 100644 --- a/src/preferences.cpp +++ b/src/preferences.cpp @@ -220,9 +220,9 @@ Video::Video(wxTreebook *book, Preferences *parent): OptionPage(book, parent, _( DisableIfChecked(autocb, OptionAdd(resolution, _("Default height"), "Subtitle/Default Resolution/Height")); - const wxString cres_arr[3] = { _("Never"), _("Ask"), _("Always") }; - wxArrayString choice_res(3, cres_arr); - OptionChoice(resolution, _("Match video resolution on open"), choice_res, "Video/Check Script Res"); + const wxString cres_arr[] = {_("Never"), _("Ask"), _("Always set"), _("Always resample")}; + wxArrayString choice_res(4, cres_arr); + OptionChoice(resolution, _("Match video resolution on open"), choice_res, "Video/Script Resolution Mismatch"); SetSizerAndFit(sizer); } diff --git a/src/video_context.cpp b/src/video_context.cpp index 2ba0f2b10..2d099e691 100644 --- a/src/video_context.cpp +++ b/src/video_context.cpp @@ -40,6 +40,7 @@ #include "audio_controller.h" #include "compat.h" #include "dialog_progress.h" +#include "dialog_video_properties.h" #include "include/aegisub/context.h" #include "include/aegisub/video_provider.h" #include "mkv_wrap.h" @@ -121,51 +122,7 @@ void VideoContext::SetVideo(const agi::fs::path &filename) { video_provider = provider->GetVideoProvider(); video_filename = filename; - // When opening dummy video only want to set the script properties if - // they were previously unset - bool set_properties = video_provider->ShouldSetVideoProperties(); - - auto matrix = video_provider->GetColorSpace(); - if (set_properties && matrix != old_matrix) { - context->ass->SetScriptInfo("YCbCr Matrix", matrix); - commit_subs = true; - } - - // Check that the script resolution matches the video resolution - int sx = context->ass->GetScriptInfoAsInt("PlayResX"); - int sy = context->ass->GetScriptInfoAsInt("PlayResY"); - int vx = GetWidth(); - int vy = GetHeight(); - - // If the script resolution hasn't been set at all just force it to the - // video resolution - if (sx == 0 && sy == 0) { - context->ass->SetScriptInfo("PlayResX", std::to_string(vx)); - context->ass->SetScriptInfo("PlayResY", std::to_string(vy)); - commit_subs = true; - } - // If it has been set to something other than a multiple of the video - // resolution, ask the user if they want it to be fixed - else if (set_properties && (sx % vx != 0 || sy % vy != 0)) { - switch (OPT_GET("Video/Check Script Res")->GetInt()) { - case 1: // Ask to change on mismatch - if (wxYES != wxMessageBox( - wxString::Format(_("The resolution of the loaded video and the resolution specified for the subtitles don't match.\n\nVideo resolution:\t%d x %d\nScript resolution:\t%d x %d\n\nChange subtitles resolution to match video?"), vx, vy, sx, sy), - _("Resolution mismatch"), - wxYES_NO | wxCENTER, - context->parent)) - - break; - // Fallthrough to case 2 - case 2: // Always change script res - context->ass->SetScriptInfo("PlayResX", std::to_string(vx)); - context->ass->SetScriptInfo("PlayResY", std::to_string(vy)); - commit_subs = true; - break; - default: // Never change - break; - } - } + bool needs_commit = UpdateVideoProperties(context->ass.get(), video_provider, context->parent); keyframes = video_provider->GetKeyFrames();