diff --git a/src/ass_file.cpp b/src/ass_file.cpp index 90e895a4b..80dad9d9d 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 f4295d629..aaaf807f0 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 bec1b784b..6c4c72afa 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 66e582958..f0d44f063 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 4e3cd34e9..d8ce0eefa 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 0f8824a65..d8f6d9b22 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 c139c1733..396c9b8e9 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 deb0f7189..35f486c6c 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 3834f12cb..2da21437e 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 98ae6f1a0..1b3d7e704 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);