From 694a8d648cd2c8eb885c924a22af6dffd9e77799 Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Tue, 3 Dec 2024 00:18:01 +0100 Subject: [PATCH] Support LayoutRes Initial support for LayoutRes. Allow editing the values in the properties and respect them in the visual tools. However, resolution resampling and the resolution mismatch dialogs still need to be adjusted. --- src/ass_file.cpp | 20 ++++++++++++ src/ass_file.h | 9 ++++++ src/dialog_properties.cpp | 56 +++++++++++++++++++++++++++++---- src/gl_wrap.cpp | 4 +-- src/gl_wrap.h | 2 +- src/visual_tool.cpp | 16 ++++++---- src/visual_tool.h | 2 ++ src/visual_tool_perspective.cpp | 24 ++++++++------ src/visual_tool_perspective.h | 2 ++ src/visual_tool_rotatexy.cpp | 2 +- 10 files changed, 111 insertions(+), 26 deletions(-) diff --git a/src/ass_file.cpp b/src/ass_file.cpp index 90e895a4bc..80dad9d9d8 100644 --- a/src/ass_file.cpp +++ b/src/ass_file.cpp @@ -21,7 +21,10 @@ #include "ass_info.h" #include "ass_style.h" #include "ass_style_storage.h" +#include "async_video_provider.h" #include "options.h" +#include "project.h" +#include "include/aegisub/context.h" #include #include @@ -153,6 +156,23 @@ void AssFile::GetResolution(int &sw, int &sh) const { sh = sw == 1280 ? 1024 : sw * 3 / 4; } +void AssFile::GetLayoutResolution(int &lw, int &lh) const { + lw = GetScriptInfoAsInt("LayoutResX"); + lh = GetScriptInfoAsInt("LayoutResY"); +} + +void AssFile::GetEffectiveLayoutResolution(agi::Context *c, int &lw, int &lh) const { + GetLayoutResolution(lw, lh); + if (lw == 0 || lh == 0) { + if (c->project->VideoProvider()) { + lw = c->project->VideoProvider()->GetWidth(); + lh = c->project->VideoProvider()->GetHeight(); + } else { + GetResolution(lw, lh); + } + } +} + std::vector AssFile::GetStyles() const { std::vector styles; for (auto& style : Styles) diff --git a/src/ass_file.h b/src/ass_file.h index f4295d6295..aaaf807f00 100644 --- a/src/ass_file.h +++ b/src/ass_file.h @@ -44,6 +44,7 @@ class AssDialogue; class AssInfo; class AssStyle; class wxString; +namespace agi { struct Context; } template using EntryList = typename boost::intrusive::make_list, boost::intrusive::base_hook>::type; @@ -125,6 +126,14 @@ class AssFile { /// @param[out] w Width /// @param[in] h Height void GetResolution(int &w,int &h) const; + /// @brief Get the specified layout resolution, if present, or 0 if it is not present + /// @param[out] w Width + /// @param[in] h Height + void GetLayoutResolution(int &w,int &h) const; + /// @brief Get the effective layout resolution (i.e. falling back to the video resolution, if present) + /// @param[out] w Width + /// @param[in] h Height + void GetEffectiveLayoutResolution(agi::Context *c, int &w,int &h) const; /// Get the value in a [Script Info] key as int, or 0 if it is not present int GetScriptInfoAsInt(std::string const& key) const; /// Get the value in a [Script Info] key as string. diff --git a/src/dialog_properties.cpp b/src/dialog_properties.cpp index bec1b784b9..6c4c72afae 100644 --- a/src/dialog_properties.cpp +++ b/src/dialog_properties.cpp @@ -36,6 +36,7 @@ #include "project.h" #include "resolution_resampler.h" #include "validators.h" +#include "video_controller.h" #include #include @@ -59,6 +60,8 @@ class DialogProperties { wxComboBox *WrapStyle; ///< Wrapping style for long lines wxTextCtrl *ResX; ///< Script x resolution wxTextCtrl *ResY; ///< Script y resolution + wxTextCtrl *LayoutResX; ///< Layout x resolution + wxTextCtrl *LayoutResY; ///< Layout y resolution wxCheckBox *ScaleBorder; ///< If script resolution != video resolution how should borders be handled wxComboBox *YCbCrMatrix; @@ -66,6 +69,8 @@ class DialogProperties { void OnOK(wxCommandEvent &event); /// Set script resolution to video resolution button void OnSetFromVideo(wxCommandEvent &event); + /// Set layout resolution to video resolution button + void OnSetLayoutResFromVideo(wxCommandEvent &event); /// Set a script info field /// @param key Name of field /// @param value New value @@ -119,18 +124,36 @@ DialogProperties::DialogProperties(agi::Context *c) ResX = new wxTextCtrl(&d,-1,"",wxDefaultPosition,wxDefaultSize,0,IntValidator(c->ass->GetScriptInfoAsInt("PlayResX"))); ResY = new wxTextCtrl(&d,-1,"",wxDefaultPosition,wxDefaultSize,0,IntValidator(c->ass->GetScriptInfoAsInt("PlayResY"))); + LayoutResX = new wxTextCtrl(&d,-1,"",wxDefaultPosition,wxDefaultSize,0,IntValidator(c->ass->GetScriptInfoAsInt("LayoutResX"))); + LayoutResY = new wxTextCtrl(&d,-1,"",wxDefaultPosition,wxDefaultSize,0,IntValidator(c->ass->GetScriptInfoAsInt("LayoutResY"))); + wxButton *FromVideo = new wxButton(&d,-1,_("From &video")); if (!c->project->VideoProvider()) FromVideo->Enable(false); else FromVideo->Bind(wxEVT_BUTTON, &DialogProperties::OnSetFromVideo, this); - auto res_sizer = new wxBoxSizer(wxHORIZONTAL); - res_sizer->Add(ResX, 1, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5); - res_sizer->Add(new wxStaticText(&d, -1, "x"), 0, wxALIGN_CENTER | wxRIGHT, 5); - res_sizer->Add(ResY, 1, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5); + auto res_sizer = new wxFlexGridSizer(5, 5, 5); + res_sizer->AddGrowableCol(1, 1); + res_sizer->AddGrowableCol(3, 1); + res_sizer->Add(new wxStaticText(&d, -1, _("Script: ")), wxSizerFlags().Center().Left()); + res_sizer->Add(ResX, 1, wxRIGHT | wxALIGN_CENTER_VERTICAL | wxEXPAND, 2); + res_sizer->Add(new wxStaticText(&d, -1, "x"), 0, wxALIGN_CENTER | wxRIGHT, 2); + res_sizer->Add(ResY, 1, wxRIGHT | wxALIGN_CENTER_VERTICAL | wxEXPAND, 2); res_sizer->Add(FromVideo, 1, 0, 0); + wxButton *LayoutResFromVideo = new wxButton(&d,-1,_("From video")); + if (!c->project->VideoProvider()) + LayoutResFromVideo->Enable(false); + else + LayoutResFromVideo->Bind(wxEVT_BUTTON, &DialogProperties::OnSetLayoutResFromVideo, this); + + res_sizer->Add(new wxStaticText(&d, -1, _("Layout: ")), wxSizerFlags().Center().Left()); + res_sizer->Add(LayoutResX, 1, wxRIGHT | wxALIGN_CENTER_VERTICAL | wxEXPAND, 2); + res_sizer->Add(new wxStaticText(&d, -1, "x"), 0, wxALIGN_CENTER | wxRIGHT, 2); + res_sizer->Add(LayoutResY, 1, wxRIGHT | wxALIGN_CENTER_VERTICAL | wxEXPAND, 2); + res_sizer->Add(LayoutResFromVideo, 1, 0, 0); + YCbCrMatrix = new wxComboBox(&d, -1, to_wx(c->ass->GetScriptInfo("YCbCr Matrix")), wxDefaultPosition, wxDefaultSize, to_wx(MatrixNames()), wxCB_READONLY); @@ -189,6 +212,8 @@ void DialogProperties::OnOK(wxCommandEvent &) { count += SetInfoIfDifferent("PlayResX", from_wx(ResX->GetValue())); count += SetInfoIfDifferent("PlayResY", from_wx(ResY->GetValue())); + count += SetInfoIfDifferent("LayoutResX", from_wx(LayoutResX->GetValue())); + count += SetInfoIfDifferent("LayoutResY", from_wx(LayoutResY->GetValue())); count += SetInfoIfDifferent("WrapStyle", std::to_string(WrapStyle->GetSelection())); count += SetInfoIfDifferent("ScaledBorderAndShadow", ScaleBorder->GetValue() ? "yes" : "no"); count += SetInfoIfDifferent("YCbCr Matrix", from_wx(YCbCrMatrix->GetValue())); @@ -206,9 +231,28 @@ int DialogProperties::SetInfoIfDifferent(std::string const& key, std::string con return 0; } +std::pair GetVideoDisplayResolution(agi::Context *c) { + double dar = c->videoController->GetAspectRatioValue(); + int width = c->project->VideoProvider()->GetWidth(); + int height = c->project->VideoProvider()->GetHeight(); + double sar = double(width) / double(height); + + return std::make_pair( + width * std::max(1., dar / sar), + height * std::max(1., sar / dar) + ); +} + void DialogProperties::OnSetFromVideo(wxCommandEvent &) { - ResX->SetValue(std::to_wstring(c->project->VideoProvider()->GetWidth())); - ResY->SetValue(std::to_wstring(c->project->VideoProvider()->GetHeight())); + auto [width, height] = GetVideoDisplayResolution(c); + ResX->SetValue(std::to_wstring(width)); + ResY->SetValue(std::to_wstring(height)); +} + +void DialogProperties::OnSetLayoutResFromVideo(wxCommandEvent &) { + auto [width, height] = GetVideoDisplayResolution(c); + LayoutResX->SetValue(std::to_wstring(width)); + LayoutResY->SetValue(std::to_wstring(height)); } } diff --git a/src/gl_wrap.cpp b/src/gl_wrap.cpp index 66e5829583..f0d44f0639 100644 --- a/src/gl_wrap.cpp +++ b/src/gl_wrap.cpp @@ -401,11 +401,11 @@ void OpenGLWrapper::SetScale(Vector2D scale) { glScalef(scale.X() / 100.f, scale.Y() / 100.f, 1.f); } -void OpenGLWrapper::SetRotation(float x, float y, float z) { +void OpenGLWrapper::SetRotation(float x, float y, float z, float zScale) { PrepareTransform(); float matrix[16] = { 2500, 0, 0, 0, 0, 2500, 0, 0, 0, 0, 1, 1, 0, 0, 2500, 2500 }; glMultMatrixf(matrix); - glScalef(1.f, 1.f, 8.f); + glScalef(1.f, 1.f, 8.f / zScale); glRotatef(y, 0.f, -1.f, 0.f); glRotatef(x, -1.f, 0.f, 0.f); glRotatef(z, 0.f, 0.f, -1.f); diff --git a/src/gl_wrap.h b/src/gl_wrap.h index 4e3cd34e94..d8ce0eefad 100644 --- a/src/gl_wrap.h +++ b/src/gl_wrap.h @@ -48,7 +48,7 @@ class OpenGLWrapper { void SetScale(Vector2D scale); void SetOrigin(Vector2D origin); - void SetRotation(float x, float y, float z); + void SetRotation(float x, float y, float z, float zScale = 1); void SetShear(float x, float y); void ResetTransform(); diff --git a/src/visual_tool.cpp b/src/visual_tool.cpp index 0f8824a651..d8f6d9b22b 100644 --- a/src/visual_tool.cpp +++ b/src/visual_tool.cpp @@ -57,23 +57,27 @@ VisualToolBase::VisualToolBase(VideoDisplay *parent, agi::Context *context) , shaded_area_alpha_opt(OPT_GET("Colour/Visual Tools/Shaded Area Alpha")) , file_changed_connection(c->ass->AddCommitListener(&VisualToolBase::OnCommit, this)) { - int script_w, script_h; - c->ass->GetResolution(script_w, script_h); - script_res = Vector2D(script_w, script_h); + SetResolutions(); active_line = GetActiveDialogueLine(); connections.push_back(c->selectionController->AddActiveLineListener(&VisualToolBase::OnActiveLineChanged, this)); connections.push_back(c->videoController->AddSeekListener(&VisualToolBase::OnSeek, this)); parent->Bind(wxEVT_MOUSE_CAPTURE_LOST, &VisualToolBase::OnMouseCaptureLost, this); } +void VisualToolBase::SetResolutions() { + int script_w, script_h, layout_w, layout_h; + c->ass->GetResolution(script_w, script_h); + c->ass->GetEffectiveLayoutResolution(c, layout_w, layout_h); + script_res = Vector2D(script_w, script_h); + layout_res = Vector2D(layout_w, layout_h); +} + void VisualToolBase::OnCommit(int type) { holding = false; dragging = false; if (type == AssFile::COMMIT_NEW || type & AssFile::COMMIT_SCRIPTINFO) { - int script_w, script_h; - c->ass->GetResolution(script_w, script_h); - script_res = Vector2D(script_w, script_h); + SetResolutions(); OnCoordinateSystemsChanged(); } diff --git a/src/visual_tool.h b/src/visual_tool.h index c139c1733f..396c9b8e99 100644 --- a/src/visual_tool.h +++ b/src/visual_tool.h @@ -47,6 +47,7 @@ namespace agi { /// functionality as possible is implemented here to avoid having four copies /// of each method for no good reason (and four times as many error messages) class VisualToolBase { + void SetResolutions(); void OnCommit(int type); void OnSeek(int new_frame); @@ -102,6 +103,7 @@ class VisualToolBase { Vector2D mouse_pos; ///< Last seen mouse position Vector2D drag_start; ///< Mouse position at the beginning of the last drag Vector2D script_res; ///< Script resolution + Vector2D layout_res; ///< Layout resolution Vector2D video_pos; ///< Top-left corner of the video in the display area Vector2D video_res; ///< Video resolution Vector2D client_size; ///< The size of the display area diff --git a/src/visual_tool_perspective.cpp b/src/visual_tool_perspective.cpp index deb0f71891..35f486c6c7 100644 --- a/src/visual_tool_perspective.cpp +++ b/src/visual_tool_perspective.cpp @@ -43,7 +43,7 @@ static const float pi = 3.1415926536f; static const float deg2rad = pi / 180.f; static const float rad2deg = 180.f / pi; -static const float screen_z = 312.5; +static const float default_screen_z = 312.5; static const char *ambient_plane_key = "_aegi_perspective_ambient_plane"; static const int BUTTON_ID_BASE = 1400; @@ -127,6 +127,10 @@ std::vector MakeRect(Vector2D a, Vector2D b) { }); } +inline float VisualToolPerspective::screenZ() const { + return default_screen_z * script_res.Y() / layout_res.Y(); +} + void VisualToolPerspective::AddTool(std::string command_name, VisualToolPerspectiveSetting setting) { cmd::Command *command = cmd::get(command_name); int icon_size = OPT_GET("App/Toolbar Icon Size")->GetInt(); @@ -331,7 +335,7 @@ void VisualToolPerspective::Draw() { // Transform grid gl.SetOrigin(FromScriptCoords(org)); gl.SetScale(100 * video_res / script_res); - gl.SetRotation(angle_x, angle_y, angle_z); + gl.SetRotation(angle_x, angle_y, angle_z, script_res.Y() / layout_res.Y()); gl.SetScale(fsc); gl.SetShear(fax, fay); Vector2D glScale = (bbox.second.Y() - bbox.first.Y()) * Vector2D(1, 1) / spacing / 4; @@ -591,7 +595,7 @@ bool VisualToolPerspective::InnerToText() { // with the following coefficients. float a = (1 - z1) * (1 - z3); Vector2D b = z1 * v1 + z3 * v3 - z1 * z3 * (v1 + v3); - float c = z1 * z3 * v1.Dot(v3) + (z1 - 1) * (z3 - 1) * screen_z * screen_z; + float c = z1 * z3 * v1.Dot(v3) + (z1 - 1) * (z3 - 1) * screenZ() * screenZ(); // Our default value for t, which would put \org at the center of the quad. // We'll try to find a value for \org that's as close as possible to it. @@ -635,10 +639,10 @@ bool VisualToolPerspective::InnerToText() { q2 = q2 - org; q3 = q3 - org; - Vector3D r0 = Vector3D(q0, screen_z); - Vector3D r1 = z1 * Vector3D(q1, screen_z); - Vector3D r2 = (z1 + z3 - 1) * Vector3D(q2, screen_z); - Vector3D r3 = z3 * Vector3D(q3, screen_z); + Vector3D r0 = Vector3D(q0, screenZ()); + Vector3D r1 = z1 * Vector3D(q1, screenZ()); + Vector3D r2 = (z1 + z3 - 1) * Vector3D(q2, screenZ()); + Vector3D r3 = z3 * Vector3D(q3, screenZ()); std::vector r({r0, r1, r2, r3}); // Find the z coordinate of the point projecting to the origin @@ -648,9 +652,9 @@ bool VisualToolPerspective::InnerToText() { Solve2x2(side0.X(), side1.X(), side0.Y(), side1.Y(), -r0.X(), -r0.Y(), orgla0, orgla1); float orgz = (r0 + orgla0 * side0 + orgla1 * side1).Z(); - // Normalize so the origin has z=screen_z, and move the screen plane to z=0 + // Normalize so the origin has z=screenZ, and move the screen plane to z=0 for (int i = 0; i < 4; i++) - r[i] = r[i] * screen_z / orgz - Vector3D(0, 0, screen_z); + r[i] = r[i] * screenZ() / orgz - Vector3D(0, 0, screenZ()); // Find the rotations Vector3D n = (r[1] - r[0]).Cross(r[3] - r[0]); @@ -825,7 +829,7 @@ void VisualToolPerspective::TextToPersp() { q = q.RotateX(-angle_x * deg2rad); q = q.RotateY(angle_y * deg2rad); // Project - q = (screen_z / (q.Z() + screen_z)) * q; + q = (screenZ() / (q.Z() + screenZ())) * q; // Move to origin Vector2D r = q.XY() + org; inner_corners[i]->pos = FromScriptCoords(r); diff --git a/src/visual_tool_perspective.h b/src/visual_tool_perspective.h index 3834f12cb7..2da21437e3 100644 --- a/src/visual_tool_perspective.h +++ b/src/visual_tool_perspective.h @@ -93,6 +93,8 @@ class VisualToolPerspective final : public VisualTool inner_corners; std::vector outer_corners; + inline float screenZ() const; + std::vector FeaturePositions(std::vector features) const; void UpdateInner(); void UpdateOuter(); diff --git a/src/visual_tool_rotatexy.cpp b/src/visual_tool_rotatexy.cpp index 98ae6f1a0f..1b3d7e7042 100644 --- a/src/visual_tool_rotatexy.cpp +++ b/src/visual_tool_rotatexy.cpp @@ -50,7 +50,7 @@ void VisualToolRotateXY::Draw() { // Transform grid gl.SetOrigin(org->pos); gl.SetScale(100 * video_res / script_res); - gl.SetRotation(angle_x, angle_y, angle_z); + gl.SetRotation(angle_x, angle_y, angle_z, script_res.Y() / layout_res.Y()); gl.SetScale(fsc); gl.SetShear(fax, fay);