Add color matrix conversion to the resolution resampler
This commit is contained in:
parent
7a06e08ad0
commit
ad33fdb109
14 changed files with 184 additions and 35 deletions
|
@ -17,6 +17,8 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <libaegisub/color.h>
|
||||||
|
|
||||||
namespace agi {
|
namespace agi {
|
||||||
enum class ycbcr_matrix {
|
enum class ycbcr_matrix {
|
||||||
bt601,
|
bt601,
|
||||||
|
@ -84,6 +86,11 @@ public:
|
||||||
return to_uint8_t(prod(from_ycbcr,
|
return to_uint8_t(prod(from_ycbcr,
|
||||||
add(add(prod(to_ycbcr, input), shift_to), shift_from)));
|
add(add(prod(to_ycbcr, input), shift_to), shift_from)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Color rgb_to_rgb(Color c) const {
|
||||||
|
auto arr = rgb_to_rgb(std::array<uint8_t, 3>{{c.r, c.g, c.b}});
|
||||||
|
return Color{arr[0], arr[1], arr[2]};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -294,10 +294,10 @@ static void load_protos() {
|
||||||
proto[++i].Set("\\fr", VariableDataType::FLOAT); // \fr<degrees>
|
proto[++i].Set("\\fr", VariableDataType::FLOAT); // \fr<degrees>
|
||||||
proto[++i].Set("\\fax", VariableDataType::FLOAT); // \fax<factor>
|
proto[++i].Set("\\fax", VariableDataType::FLOAT); // \fax<factor>
|
||||||
proto[++i].Set("\\fay", VariableDataType::FLOAT); // \fay<factor>
|
proto[++i].Set("\\fay", VariableDataType::FLOAT); // \fay<factor>
|
||||||
proto[++i].Set("\\1c", VariableDataType::TEXT); // \1c&H<bbggrr>&
|
proto[++i].Set("\\1c", VariableDataType::TEXT, AssParameterClass::COLOR); // \1c&H<bbggrr>&
|
||||||
proto[++i].Set("\\2c", VariableDataType::TEXT); // \2c&H<bbggrr>&
|
proto[++i].Set("\\2c", VariableDataType::TEXT, AssParameterClass::COLOR); // \2c&H<bbggrr>&
|
||||||
proto[++i].Set("\\3c", VariableDataType::TEXT); // \3c&H<bbggrr>&
|
proto[++i].Set("\\3c", VariableDataType::TEXT, AssParameterClass::COLOR); // \3c&H<bbggrr>&
|
||||||
proto[++i].Set("\\4c", VariableDataType::TEXT); // \4c&H<bbggrr>&
|
proto[++i].Set("\\4c", VariableDataType::TEXT, AssParameterClass::COLOR); // \4c&H<bbggrr>&
|
||||||
proto[++i].Set("\\1a", VariableDataType::TEXT, AssParameterClass::ALPHA); // \1a&H<aa>&
|
proto[++i].Set("\\1a", VariableDataType::TEXT, AssParameterClass::ALPHA); // \1a&H<aa>&
|
||||||
proto[++i].Set("\\2a", VariableDataType::TEXT, AssParameterClass::ALPHA); // \2a&H<aa>&
|
proto[++i].Set("\\2a", VariableDataType::TEXT, AssParameterClass::ALPHA); // \2a&H<aa>&
|
||||||
proto[++i].Set("\\3a", VariableDataType::TEXT, AssParameterClass::ALPHA); // \3a&H<aa>&
|
proto[++i].Set("\\3a", VariableDataType::TEXT, AssParameterClass::ALPHA); // \3a&H<aa>&
|
||||||
|
@ -312,7 +312,7 @@ static void load_protos() {
|
||||||
proto[++i].Set("\\fs-", VariableDataType::FLOAT); // \fs-<size>
|
proto[++i].Set("\\fs-", VariableDataType::FLOAT); // \fs-<size>
|
||||||
proto[++i].Set("\\fs", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \fs<size>
|
proto[++i].Set("\\fs", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \fs<size>
|
||||||
proto[++i].Set("\\an", VariableDataType::INT); // \an<alignment>
|
proto[++i].Set("\\an", VariableDataType::INT); // \an<alignment>
|
||||||
proto[++i].Set("\\c", VariableDataType::TEXT); // \c&H<bbggrr>&
|
proto[++i].Set("\\c", VariableDataType::TEXT, AssParameterClass::COLOR); // \c&H<bbggrr>&
|
||||||
proto[++i].Set("\\b", VariableDataType::INT); // \b<0/1/weight>
|
proto[++i].Set("\\b", VariableDataType::INT); // \b<0/1/weight>
|
||||||
proto[++i].Set("\\i", VariableDataType::BOOL); // \i<0/1>
|
proto[++i].Set("\\i", VariableDataType::BOOL); // \i<0/1>
|
||||||
proto[++i].Set("\\u", VariableDataType::BOOL); // \u<0/1>
|
proto[++i].Set("\\u", VariableDataType::BOOL); // \u<0/1>
|
||||||
|
|
|
@ -49,7 +49,8 @@ enum class AssParameterClass {
|
||||||
RELATIVE_TIME_END,
|
RELATIVE_TIME_END,
|
||||||
KARAOKE,
|
KARAOKE,
|
||||||
DRAWING,
|
DRAWING,
|
||||||
ALPHA
|
ALPHA,
|
||||||
|
COLOR
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class VariableDataType {
|
enum class VariableDataType {
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include "help_button.h"
|
#include "help_button.h"
|
||||||
#include "include/aegisub/context.h"
|
#include "include/aegisub/context.h"
|
||||||
#include "libresrc/libresrc.h"
|
#include "libresrc/libresrc.h"
|
||||||
|
#include "resolution_resampler.h"
|
||||||
#include "validators.h"
|
#include "validators.h"
|
||||||
#include "video_context.h"
|
#include "video_context.h"
|
||||||
|
|
||||||
|
@ -90,15 +91,8 @@ DialogProperties::DialogProperties(agi::Context *c)
|
||||||
res_sizer->Add(ResY, 1, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
|
res_sizer->Add(ResY, 1, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
|
||||||
res_sizer->Add(FromVideo, 1, 0, 0);
|
res_sizer->Add(FromVideo, 1, 0, 0);
|
||||||
|
|
||||||
wxString matricies[] = {
|
|
||||||
"None",
|
|
||||||
"TV.601", "PC.601",
|
|
||||||
"TV.709", "PC.709",
|
|
||||||
"TV.FCC", "PC.FCC",
|
|
||||||
"TV.240M", "PC.240M"
|
|
||||||
};
|
|
||||||
YCbCrMatrix = new wxComboBox(this, -1, c->ass->GetScriptInfo("YCbCr Matrix"),
|
YCbCrMatrix = new wxComboBox(this, -1, c->ass->GetScriptInfo("YCbCr Matrix"),
|
||||||
wxDefaultPosition, wxDefaultSize, boost::size(matricies), matricies, wxCB_READONLY);
|
wxDefaultPosition, wxDefaultSize, to_wx(MatrixNames()), wxCB_READONLY);
|
||||||
|
|
||||||
auto matrix_sizer = new wxBoxSizer(wxHORIZONTAL);
|
auto matrix_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
matrix_sizer->Add(new wxStaticText(this, -1, "YCbCr Matrix:"), wxSizerFlags().Center());
|
matrix_sizer->Add(new wxStaticText(this, -1, "YCbCr Matrix:"), wxSizerFlags().Center());
|
||||||
|
|
|
@ -20,8 +20,10 @@
|
||||||
#include "dialog_resample.h"
|
#include "dialog_resample.h"
|
||||||
|
|
||||||
#include "ass_file.h"
|
#include "ass_file.h"
|
||||||
#include "include/aegisub/context.h"
|
#include "compat.h"
|
||||||
#include "help_button.h"
|
#include "help_button.h"
|
||||||
|
#include "include/aegisub/context.h"
|
||||||
|
#include "include/aegisub/video_provider.h"
|
||||||
#include "libresrc/libresrc.h"
|
#include "libresrc/libresrc.h"
|
||||||
#include "resolution_resampler.h"
|
#include "resolution_resampler.h"
|
||||||
#include "validators.h"
|
#include "validators.h"
|
||||||
|
@ -29,6 +31,7 @@
|
||||||
|
|
||||||
#include <boost/range/size.hpp>
|
#include <boost/range/size.hpp>
|
||||||
#include <wx/checkbox.h>
|
#include <wx/checkbox.h>
|
||||||
|
#include <wx/combobox.h>
|
||||||
#include <wx/sizer.h>
|
#include <wx/sizer.h>
|
||||||
#include <wx/spinctrl.h>
|
#include <wx/spinctrl.h>
|
||||||
#include <wx/statbox.h>
|
#include <wx/statbox.h>
|
||||||
|
@ -52,14 +55,18 @@ DialogResample::DialogResample(agi::Context *c, ResampleSettings &settings)
|
||||||
c->ass->GetResolution(script_w, script_h);
|
c->ass->GetResolution(script_w, script_h);
|
||||||
settings.source_x = script_w;
|
settings.source_x = script_w;
|
||||||
settings.source_y = script_h;
|
settings.source_y = script_h;
|
||||||
|
settings.source_matrix = script_mat = MatrixFromString(c->ass->GetScriptInfo("YCbCr Matrix"));
|
||||||
|
|
||||||
if (c->videoController->IsLoaded()) {
|
if (c->videoController->IsLoaded()) {
|
||||||
settings.dest_x = video_w = c->videoController->GetWidth();
|
settings.dest_x = video_w = c->videoController->GetWidth();
|
||||||
settings.dest_y = video_h = c->videoController->GetHeight();
|
settings.dest_y = video_h = c->videoController->GetHeight();
|
||||||
|
settings.dest_matrix = video_mat = MatrixFromString(c->videoController->GetProvider()->GetRealColorSpace());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
settings.dest_x = script_w;
|
settings.dest_x = script_w;
|
||||||
settings.dest_y = script_h;
|
settings.dest_y = script_h;
|
||||||
|
settings.dest_matrix = script_mat;
|
||||||
|
video_mat = YCbCrMatrix::rgb;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create all controls and set validators
|
// Create all controls and set validators
|
||||||
|
@ -76,13 +83,19 @@ DialogResample::DialogResample(agi::Context *c, ResampleSettings &settings)
|
||||||
|
|
||||||
source_x = new wxSpinCtrl(this, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, 1, INT_MAX);
|
source_x = new wxSpinCtrl(this, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, 1, INT_MAX);
|
||||||
source_y = new wxSpinCtrl(this, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, 1, INT_MAX);
|
source_y = new wxSpinCtrl(this, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, 1, INT_MAX);
|
||||||
|
source_matrix = new wxComboBox(this, -1, "", wxDefaultPosition,
|
||||||
|
wxDefaultSize, to_wx(MatrixNames()), wxCB_READONLY);
|
||||||
dest_x = new wxSpinCtrl(this, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, 1, INT_MAX);
|
dest_x = new wxSpinCtrl(this, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, 1, INT_MAX);
|
||||||
dest_y = new wxSpinCtrl(this, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, 1, INT_MAX);
|
dest_y = new wxSpinCtrl(this, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, 1, INT_MAX);
|
||||||
|
dest_matrix = new wxComboBox(this, -1, "", wxDefaultPosition, wxDefaultSize,
|
||||||
|
to_wx(MatrixNames()), wxCB_READONLY);
|
||||||
|
|
||||||
source_x->SetValidator(wxGenericValidator(&settings.source_x));
|
source_x->SetValidator(wxGenericValidator(&settings.source_x));
|
||||||
source_y->SetValidator(wxGenericValidator(&settings.source_y));
|
source_y->SetValidator(wxGenericValidator(&settings.source_y));
|
||||||
|
source_matrix->SetValidator(MakeEnumBinder(&settings.source_matrix));
|
||||||
dest_x->SetValidator(wxGenericValidator(&settings.dest_x));
|
dest_x->SetValidator(wxGenericValidator(&settings.dest_x));
|
||||||
dest_y->SetValidator(wxGenericValidator(&settings.dest_y));
|
dest_y->SetValidator(wxGenericValidator(&settings.dest_y));
|
||||||
|
dest_matrix->SetValidator(MakeEnumBinder(&settings.dest_matrix));
|
||||||
|
|
||||||
from_video = new wxButton(this, -1, _("From &video"));
|
from_video = new wxButton(this, -1, _("From &video"));
|
||||||
from_video->Enable(false);
|
from_video->Enable(false);
|
||||||
|
@ -108,25 +121,38 @@ DialogResample::DialogResample(agi::Context *c, ResampleSettings &settings)
|
||||||
auto margin_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Margin offset"));
|
auto margin_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Margin offset"));
|
||||||
margin_box->Add(margin_sizer, wxSizerFlags(1).Expand().Border(wxBOTTOM));
|
margin_box->Add(margin_sizer, wxSizerFlags(1).Expand().Border(wxBOTTOM));
|
||||||
|
|
||||||
auto source_res_sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Source Resolution"));
|
auto source_res_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
source_res_sizer->Add(source_x, wxSizerFlags(1).Border(wxRIGHT).Align(wxALIGN_CENTER_VERTICAL));
|
source_res_sizer->Add(source_x, wxSizerFlags(1).Border(wxRIGHT).Align(wxALIGN_CENTER_VERTICAL));
|
||||||
source_res_sizer->Add(new wxStaticText(this, -1, _("x")), wxSizerFlags().Center().Border(wxRIGHT));
|
source_res_sizer->Add(new wxStaticText(this, -1, _("x")), wxSizerFlags().Center().Border(wxRIGHT));
|
||||||
source_res_sizer->Add(source_y, wxSizerFlags(1).Border(wxRIGHT).Align(wxALIGN_CENTER_VERTICAL));
|
source_res_sizer->Add(source_y, wxSizerFlags(1).Border(wxRIGHT).Align(wxALIGN_CENTER_VERTICAL));
|
||||||
source_res_sizer->Add(from_script, wxSizerFlags(1));
|
source_res_sizer->Add(from_script, wxSizerFlags(1));
|
||||||
|
|
||||||
|
auto source_matrix_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
source_matrix_sizer->Add(new wxStaticText(this, -1, _("YCbCr Matrix:")), wxSizerFlags().Border(wxRIGHT).Center());
|
||||||
|
source_matrix_sizer->Add(source_matrix, wxSizerFlags(1).Center().Right());
|
||||||
|
|
||||||
|
auto source_res_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Source Resolution"));
|
||||||
|
source_res_box->Add(source_res_sizer, wxSizerFlags(1).Expand().Border(wxBOTTOM));
|
||||||
|
source_res_box->Add(source_matrix_sizer, wxSizerFlags(1).Expand());
|
||||||
|
|
||||||
auto dest_res_sizer = new wxBoxSizer(wxHORIZONTAL);
|
auto dest_res_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
dest_res_sizer->Add(dest_x, wxSizerFlags(1).Border(wxRIGHT).Align(wxALIGN_CENTER_VERTICAL));
|
dest_res_sizer->Add(dest_x, wxSizerFlags(1).Border(wxRIGHT).Align(wxALIGN_CENTER_VERTICAL));
|
||||||
dest_res_sizer->Add(new wxStaticText(this, -1, _("x")), wxSizerFlags().Center().Border(wxRIGHT));
|
dest_res_sizer->Add(new wxStaticText(this, -1, _("x")), wxSizerFlags().Center().Border(wxRIGHT));
|
||||||
dest_res_sizer->Add(dest_y, wxSizerFlags(1).Border(wxRIGHT).Align(wxALIGN_CENTER_VERTICAL));
|
dest_res_sizer->Add(dest_y, wxSizerFlags(1).Border(wxRIGHT).Align(wxALIGN_CENTER_VERTICAL));
|
||||||
dest_res_sizer->Add(from_video, wxSizerFlags(1));
|
dest_res_sizer->Add(from_video, wxSizerFlags(1));
|
||||||
|
|
||||||
|
auto dest_matrix_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
dest_matrix_sizer->Add(new wxStaticText(this, -1, _("YCbCr Matrix:")), wxSizerFlags().Border(wxRIGHT).Center());
|
||||||
|
dest_matrix_sizer->Add(dest_matrix, wxSizerFlags(1).Center().Right());
|
||||||
|
|
||||||
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(dest_matrix_sizer, wxSizerFlags(1).Expand());
|
||||||
|
|
||||||
auto main_sizer = new wxBoxSizer(wxVERTICAL);
|
auto main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
main_sizer->Add(source_res_sizer, wxSizerFlags(0).Expand().Border());
|
main_sizer->Add(source_res_box, wxSizerFlags().Expand().Border());
|
||||||
main_sizer->Add(dest_res_box, wxSizerFlags(0).Expand().Border());
|
main_sizer->Add(dest_res_box, wxSizerFlags().Expand().Border());
|
||||||
main_sizer->Add(ar_mode, wxSizerFlags(0).Expand().Border());
|
main_sizer->Add(ar_mode, wxSizerFlags().Expand().Border());
|
||||||
main_sizer->Add(margin_box, wxSizerFlags(1).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);
|
||||||
|
@ -150,11 +176,13 @@ DialogResample::DialogResample(agi::Context *c, ResampleSettings &settings)
|
||||||
void DialogResample::SetDestFromVideo(wxCommandEvent &) {
|
void DialogResample::SetDestFromVideo(wxCommandEvent &) {
|
||||||
dest_x->SetValue(video_w);
|
dest_x->SetValue(video_w);
|
||||||
dest_y->SetValue(video_h);
|
dest_y->SetValue(video_h);
|
||||||
|
dest_matrix->SetSelection((int)video_mat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogResample::SetSourceFromScript(wxCommandEvent&) {
|
void DialogResample::SetSourceFromScript(wxCommandEvent&) {
|
||||||
source_x->SetValue(script_w);
|
source_x->SetValue(script_w);
|
||||||
source_y->SetValue(script_h);
|
source_y->SetValue(script_h);
|
||||||
|
source_matrix->SetSelection((int)script_mat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogResample::UpdateButtons() {
|
void DialogResample::UpdateButtons() {
|
||||||
|
|
|
@ -22,8 +22,10 @@
|
||||||
namespace agi { struct Context; }
|
namespace agi { struct Context; }
|
||||||
class AssFile;
|
class AssFile;
|
||||||
class wxCheckBox;
|
class wxCheckBox;
|
||||||
|
class wxComboBox;
|
||||||
class wxRadioBox;
|
class wxRadioBox;
|
||||||
class wxSpinCtrl;
|
class wxSpinCtrl;
|
||||||
|
enum class YCbCrMatrix : int;
|
||||||
struct ResampleSettings;
|
struct ResampleSettings;
|
||||||
|
|
||||||
/// @class DialogResample
|
/// @class DialogResample
|
||||||
|
@ -35,13 +37,17 @@ class DialogResample final : public wxDialog {
|
||||||
|
|
||||||
int script_w;
|
int script_w;
|
||||||
int script_h;
|
int script_h;
|
||||||
|
YCbCrMatrix script_mat;
|
||||||
int video_w = 0;
|
int video_w = 0;
|
||||||
int video_h = 0;
|
int video_h = 0;
|
||||||
|
YCbCrMatrix video_mat;
|
||||||
|
|
||||||
wxSpinCtrl *source_x;
|
wxSpinCtrl *source_x;
|
||||||
wxSpinCtrl *source_y;
|
wxSpinCtrl *source_y;
|
||||||
wxSpinCtrl *dest_x;
|
wxSpinCtrl *dest_x;
|
||||||
wxSpinCtrl *dest_y;
|
wxSpinCtrl *dest_y;
|
||||||
|
wxComboBox *source_matrix;
|
||||||
|
wxComboBox *dest_matrix;
|
||||||
wxCheckBox *symmetrical;
|
wxCheckBox *symmetrical;
|
||||||
wxRadioBox *ar_mode;
|
wxRadioBox *ar_mode;
|
||||||
wxSpinCtrl *margin_ctrl[4];
|
wxSpinCtrl *margin_ctrl[4];
|
||||||
|
|
|
@ -134,9 +134,10 @@ bool UpdateVideoProperties(AssFile *file, const VideoProvider *new_provider, wxW
|
||||||
// Fallthrough to prompt if the AR changed
|
// Fallthrough to prompt if the AR changed
|
||||||
if (!ar_changed) {
|
if (!ar_changed) {
|
||||||
ResampleResolution(file, {
|
ResampleResolution(file, {
|
||||||
{0, 0, 0, 0},
|
{0, 0, 0, 0},
|
||||||
sx, sy, vx, vy,
|
sx, sy, vx, vy,
|
||||||
ResampleARMode::Stretch
|
ResampleARMode::Stretch,
|
||||||
|
YCbCrMatrix::rgb, YCbCrMatrix::rgb
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -147,9 +148,10 @@ bool UpdateVideoProperties(AssFile *file, const VideoProvider *new_provider, wxW
|
||||||
OPT_SET("Video/Last Script Resolution Mismatch Choice")->SetInt(res);
|
OPT_SET("Video/Last Script Resolution Mismatch Choice")->SetInt(res);
|
||||||
|
|
||||||
ResampleResolution(file, {
|
ResampleResolution(file, {
|
||||||
{0, 0, 0, 0},
|
{0, 0, 0, 0},
|
||||||
sx, sy, vx, vy,
|
sx, sy, vx, vy,
|
||||||
static_cast<ResampleARMode>(res - FIX_RESAMPLE)
|
static_cast<ResampleARMode>(res - FIX_RESAMPLE),
|
||||||
|
YCbCrMatrix::rgb, YCbCrMatrix::rgb
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,7 @@ public:
|
||||||
/// @return A string describing the source colorspace or "None" if it is
|
/// @return A string describing the source colorspace or "None" if it is
|
||||||
/// unknown or meaningless
|
/// unknown or meaningless
|
||||||
virtual std::string GetColorSpace() const = 0;
|
virtual std::string GetColorSpace() const = 0;
|
||||||
|
virtual std::string GetRealColorSpace() const { return GetColorSpace(); }
|
||||||
|
|
||||||
/// @brief Use this to set any post-loading warnings, such as "being loaded with unreliable seeking"
|
/// @brief Use this to set any post-loading warnings, such as "being loaded with unreliable seeking"
|
||||||
virtual std::string GetWarning() const { return ""; }
|
virtual std::string GetWarning() const { return ""; }
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
#include <libaegisub/of_type_adaptor.h>
|
||||||
#include <libaegisub/split.h>
|
#include <libaegisub/split.h>
|
||||||
#include <libaegisub/util.h>
|
#include <libaegisub/util.h>
|
||||||
|
#include <libaegisub/ycbcr_conv.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
@ -38,6 +39,30 @@ enum {
|
||||||
BOTTOM = 3
|
BOTTOM = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const std::string names[] = {
|
||||||
|
"None",
|
||||||
|
"TV.601", "PC.601",
|
||||||
|
"TV.709", "PC.709",
|
||||||
|
"TV.FCC", "PC.FCC",
|
||||||
|
"TV.240M", "PC.240M"
|
||||||
|
};
|
||||||
|
|
||||||
|
YCbCrMatrix MatrixFromString(std::string const& str) {
|
||||||
|
if (str.empty()) return YCbCrMatrix::tv_601;
|
||||||
|
auto pos = std::find(std::begin(names), std::end(names), str);
|
||||||
|
if (pos == std::end(names))
|
||||||
|
return YCbCrMatrix::rgb;
|
||||||
|
return static_cast<YCbCrMatrix>(std::distance(std::begin(names), pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MatrixToString(YCbCrMatrix mat) {
|
||||||
|
return names[static_cast<int>(mat)];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> MatrixNames() {
|
||||||
|
return {std::begin(names), std::end(names)};
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::string transform_drawing(std::string const& drawing, int shift_x, int shift_y, double scale_x, double scale_y) {
|
std::string transform_drawing(std::string const& drawing, int shift_x, int shift_y, double scale_x, double scale_y) {
|
||||||
bool is_x = true;
|
bool is_x = true;
|
||||||
|
@ -76,6 +101,8 @@ namespace {
|
||||||
double rx;
|
double rx;
|
||||||
double ry;
|
double ry;
|
||||||
double ar;
|
double ar;
|
||||||
|
agi::ycbcr_converter conv;
|
||||||
|
bool convert_colors;
|
||||||
};
|
};
|
||||||
|
|
||||||
void resample_tags(std::string const& name, AssOverrideParameter *cur, void *ud) {
|
void resample_tags(std::string const& name, AssOverrideParameter *cur, void *ud) {
|
||||||
|
@ -113,6 +140,11 @@ namespace {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case AssParameterClass::COLOR:
|
||||||
|
if (state->convert_colors)
|
||||||
|
cur->Set<std::string>(state->conv.rgb_to_rgb(agi::Color{cur->Get<std::string>()}).GetAssOverrideFormatted());
|
||||||
|
return;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -152,8 +184,42 @@ namespace {
|
||||||
style.scalex *= state->ar;
|
style.scalex *= state->ar;
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
style.Margin[i] = int((style.Margin[i] + state->margin[i]) * (i < 2 ? state->rx : state->ry) + 0.5);
|
style.Margin[i] = int((style.Margin[i] + state->margin[i]) * (i < 2 ? state->rx : state->ry) + 0.5);
|
||||||
|
if (state->convert_colors) {
|
||||||
|
style.primary = state->conv.rgb_to_rgb(style.primary);
|
||||||
|
style.secondary = state->conv.rgb_to_rgb(style.secondary);
|
||||||
|
style.outline = state->conv.rgb_to_rgb(style.outline);
|
||||||
|
style.shadow = state->conv.rgb_to_rgb(style.shadow);
|
||||||
|
}
|
||||||
style.UpdateData();
|
style.UpdateData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
agi::ycbcr_matrix matrix(YCbCrMatrix mat) {
|
||||||
|
switch (mat) {
|
||||||
|
case YCbCrMatrix::rgb: return agi::ycbcr_matrix::bt601;
|
||||||
|
case YCbCrMatrix::tv_601: case YCbCrMatrix::pc_601: return agi::ycbcr_matrix::bt601;
|
||||||
|
case YCbCrMatrix::tv_709: case YCbCrMatrix::pc_709: return agi::ycbcr_matrix::bt709;
|
||||||
|
case YCbCrMatrix::tv_fcc: case YCbCrMatrix::pc_fcc: return agi::ycbcr_matrix::fcc;
|
||||||
|
case YCbCrMatrix::tv_240m: case YCbCrMatrix::pc_240m: return agi::ycbcr_matrix::smpte_240m;
|
||||||
|
}
|
||||||
|
throw agi::InternalError("Invalid matrix", nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
agi::ycbcr_range range(YCbCrMatrix mat) {
|
||||||
|
switch (mat) {
|
||||||
|
case YCbCrMatrix::rgb:
|
||||||
|
case YCbCrMatrix::tv_601:
|
||||||
|
case YCbCrMatrix::tv_709:
|
||||||
|
case YCbCrMatrix::tv_fcc:
|
||||||
|
case YCbCrMatrix::tv_240m:
|
||||||
|
return agi::ycbcr_range::tv;
|
||||||
|
case YCbCrMatrix::pc_601:
|
||||||
|
case YCbCrMatrix::pc_709:
|
||||||
|
case YCbCrMatrix::pc_fcc:
|
||||||
|
case YCbCrMatrix::pc_240m:
|
||||||
|
return agi::ycbcr_range::pc;
|
||||||
|
}
|
||||||
|
throw agi::InternalError("Invalid matrix", nullptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResampleResolution(AssFile *ass, ResampleSettings settings) {
|
void ResampleResolution(AssFile *ass, ResampleSettings settings) {
|
||||||
|
@ -191,11 +257,23 @@ void ResampleResolution(AssFile *ass, ResampleSettings settings) {
|
||||||
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];
|
||||||
|
|
||||||
|
bool resample_colors =
|
||||||
|
settings.source_matrix != settings.dest_matrix &&
|
||||||
|
settings.source_matrix != YCbCrMatrix::rgb &&
|
||||||
|
settings.dest_matrix != YCbCrMatrix::rgb;
|
||||||
|
|
||||||
resample_state state = {
|
resample_state state = {
|
||||||
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),
|
||||||
horizontal_stretch
|
horizontal_stretch,
|
||||||
|
agi::ycbcr_converter{
|
||||||
|
matrix(settings.source_matrix),
|
||||||
|
range(settings.source_matrix),
|
||||||
|
matrix(settings.dest_matrix),
|
||||||
|
range(settings.dest_matrix),
|
||||||
|
},
|
||||||
|
resample_colors
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto& line : ass->Styles)
|
for (auto& line : ass->Styles)
|
||||||
|
@ -205,6 +283,7 @@ void ResampleResolution(AssFile *ass, ResampleSettings settings) {
|
||||||
|
|
||||||
ass->SetScriptInfo("PlayResX", std::to_string(settings.dest_x));
|
ass->SetScriptInfo("PlayResX", std::to_string(settings.dest_x));
|
||||||
ass->SetScriptInfo("PlayResY", std::to_string(settings.dest_y));
|
ass->SetScriptInfo("PlayResY", std::to_string(settings.dest_y));
|
||||||
|
ass->SetScriptInfo("YCbCr Matrix", MatrixToString(settings.dest_matrix));
|
||||||
|
|
||||||
ass->Commit(_("resolution resampling"), AssFile::COMMIT_SCRIPTINFO | AssFile::COMMIT_DIAG_FULL);
|
ass->Commit(_("resolution resampling"), AssFile::COMMIT_SCRIPTINFO | AssFile::COMMIT_DIAG_FULL);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,9 @@
|
||||||
//
|
//
|
||||||
// Aegisub Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class AssFile;
|
class AssFile;
|
||||||
|
|
||||||
enum class ResampleARMode {
|
enum class ResampleARMode {
|
||||||
|
@ -23,6 +26,22 @@ enum class ResampleARMode {
|
||||||
Manual
|
Manual
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class YCbCrMatrix : int {
|
||||||
|
rgb,
|
||||||
|
tv_601,
|
||||||
|
pc_601,
|
||||||
|
tv_709,
|
||||||
|
pc_709,
|
||||||
|
tv_fcc,
|
||||||
|
pc_fcc,
|
||||||
|
tv_240m,
|
||||||
|
pc_240m
|
||||||
|
};
|
||||||
|
|
||||||
|
YCbCrMatrix MatrixFromString(std::string const& str);
|
||||||
|
std::string MatrixToString(YCbCrMatrix mat);
|
||||||
|
std::vector<std::string> MatrixNames();
|
||||||
|
|
||||||
/// 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
|
||||||
|
@ -31,6 +50,8 @@ struct ResampleSettings {
|
||||||
int dest_x; ///< New X resolution
|
int dest_x; ///< New X resolution
|
||||||
int dest_y; ///< New Y resolution
|
int dest_y; ///< New Y resolution
|
||||||
ResampleARMode ar_mode; ///< What to do if the old AR and new AR don't match
|
ResampleARMode ar_mode; ///< What to do if the old AR and new AR don't match
|
||||||
|
YCbCrMatrix source_matrix;
|
||||||
|
YCbCrMatrix dest_matrix;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Resample the subtitles in the project
|
/// Resample the subtitles in the project
|
||||||
|
|
|
@ -79,7 +79,9 @@ class EnumBinder final : public wxValidator {
|
||||||
bool Validate(wxWindow *) override { return true; }
|
bool Validate(wxWindow *) override { return true; }
|
||||||
|
|
||||||
bool TransferFromWindow() override {
|
bool TransferFromWindow() override {
|
||||||
if (wxRadioBox *rb = dynamic_cast<wxRadioBox*>(GetWindow()))
|
if (auto rb = dynamic_cast<wxRadioBox*>(GetWindow()))
|
||||||
|
*value = static_cast<T>(rb->GetSelection());
|
||||||
|
else if (auto rb = dynamic_cast<wxComboBox*>(GetWindow()))
|
||||||
*value = static_cast<T>(rb->GetSelection());
|
*value = static_cast<T>(rb->GetSelection());
|
||||||
else
|
else
|
||||||
throw agi::InternalError("Control type not supported by EnumBinder", nullptr);
|
throw agi::InternalError("Control type not supported by EnumBinder", nullptr);
|
||||||
|
@ -87,8 +89,10 @@ class EnumBinder final : public wxValidator {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TransferToWindow() override {
|
bool TransferToWindow() override {
|
||||||
if (wxRadioBox *rb = dynamic_cast<wxRadioBox*>(GetWindow()))
|
if (auto rb = dynamic_cast<wxRadioBox*>(GetWindow()))
|
||||||
rb->SetSelection(static_cast<int>(*value));
|
rb->SetSelection(static_cast<int>(*value));
|
||||||
|
else if (auto cb = dynamic_cast<wxComboBox*>(GetWindow()))
|
||||||
|
cb->SetSelection(static_cast<int>(*value));
|
||||||
else
|
else
|
||||||
throw agi::InternalError("Control type not supported by EnumBinder", nullptr);
|
throw agi::InternalError("Control type not supported by EnumBinder", nullptr);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -65,6 +65,7 @@ class AvisynthVideoProvider: public VideoProvider {
|
||||||
std::vector<int> keyframes;
|
std::vector<int> keyframes;
|
||||||
std::string warning;
|
std::string warning;
|
||||||
std::string colorspace;
|
std::string colorspace;
|
||||||
|
std::string real_colorspace;
|
||||||
|
|
||||||
PClip RGB32Video;
|
PClip RGB32Video;
|
||||||
VideoInfo vi;
|
VideoInfo vi;
|
||||||
|
@ -85,6 +86,7 @@ public:
|
||||||
std::string GetWarning() const override { return warning; }
|
std::string GetWarning() const override { return warning; }
|
||||||
std::string GetDecoderName() const override { return decoder_name; }
|
std::string GetDecoderName() const override { return decoder_name; }
|
||||||
std::string GetColorSpace() const override { return colorspace; }
|
std::string GetColorSpace() const override { return colorspace; }
|
||||||
|
std::string GetRealColorSpace() const override { return real_colorspace; }
|
||||||
};
|
};
|
||||||
|
|
||||||
AvisynthVideoProvider::AvisynthVideoProvider(agi::fs::path const& filename, std::string const& colormatrix) {
|
AvisynthVideoProvider::AvisynthVideoProvider(agi::fs::path const& filename, std::string const& colormatrix) {
|
||||||
|
@ -176,22 +178,24 @@ file_exit:
|
||||||
throw VideoNotSupported("No usable video found");
|
throw VideoNotSupported("No usable video found");
|
||||||
|
|
||||||
vi = script.AsClip()->GetVideoInfo();
|
vi = script.AsClip()->GetVideoInfo();
|
||||||
if (!vi.IsRGB()) {
|
if (vi.IsRGB())
|
||||||
|
real_colorspace = colorspace = "None";
|
||||||
|
else {
|
||||||
/// @todo maybe read ColorMatrix hints for d2v files?
|
/// @todo maybe read ColorMatrix hints for d2v files?
|
||||||
AVSValue args[2] = { script, "Rec601" };
|
AVSValue args[2] = { script, "Rec601" };
|
||||||
bool force_bt601 = OPT_GET("Video/Force BT.601")->GetBool() || colormatrix == "TV.601";
|
bool force_bt601 = OPT_GET("Video/Force BT.601")->GetBool() || colormatrix == "TV.601";
|
||||||
bool bt709 = vi.width > 1024 || vi.height >= 600;
|
bool bt709 = vi.width > 1024 || vi.height >= 600;
|
||||||
if (bt709 && (!force_bt601 || colormatrix == "TV.709")) {
|
if (bt709 && (!force_bt601 || colormatrix == "TV.709")) {
|
||||||
args[1] = "Rec709";
|
args[1] = "Rec709";
|
||||||
colorspace = "TV.709";
|
real_colorspace = colorspace = "TV.709";
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
colorspace = "TV.601";
|
colorspace = "TV.601";
|
||||||
|
real_colorspace = bt709 ? "TV.709" : "TV.601";
|
||||||
|
}
|
||||||
const char *argnames[2] = { 0, "matrix" };
|
const char *argnames[2] = { 0, "matrix" };
|
||||||
script = avs.GetEnv()->Invoke("ConvertToRGB32", AVSValue(args, 2), argnames);
|
script = avs.GetEnv()->Invoke("ConvertToRGB32", AVSValue(args, 2), argnames);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
colorspace = "None";
|
|
||||||
|
|
||||||
RGB32Video = avs.GetEnv()->Invoke("Cache", script).AsClip();
|
RGB32Video = avs.GetEnv()->Invoke("Cache", script).AsClip();
|
||||||
vi = RGB32Video->GetVideoInfo();
|
vi = RGB32Video->GetVideoInfo();
|
||||||
|
|
|
@ -68,6 +68,7 @@ public:
|
||||||
std::string GetWarning() const override { return master->GetWarning(); }
|
std::string GetWarning() const override { return master->GetWarning(); }
|
||||||
std::string GetDecoderName() const override { return master->GetDecoderName(); }
|
std::string GetDecoderName() const override { return master->GetDecoderName(); }
|
||||||
std::string GetColorSpace() const override { return master->GetColorSpace(); }
|
std::string GetColorSpace() const override { return master->GetColorSpace(); }
|
||||||
|
std::string GetRealColorSpace() const override { return master->GetRealColorSpace(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<VideoFrame> VideoProviderCache::GetFrame(int n) {
|
std::shared_ptr<VideoFrame> VideoProviderCache::GetFrame(int n) {
|
||||||
|
|
|
@ -62,6 +62,7 @@ class FFmpegSourceVideoProvider final : public VideoProvider, FFmpegSourceProvid
|
||||||
std::vector<int> KeyFramesList; ///< list of keyframes
|
std::vector<int> KeyFramesList; ///< list of keyframes
|
||||||
agi::vfr::Framerate Timecodes; ///< vfr object
|
agi::vfr::Framerate Timecodes; ///< vfr object
|
||||||
std::string ColorSpace; ///< Colorspace name
|
std::string ColorSpace; ///< Colorspace name
|
||||||
|
std::string RealColorSpace; ///< Colorspace name
|
||||||
|
|
||||||
char FFMSErrMsg[1024]; ///< FFMS error message
|
char FFMSErrMsg[1024]; ///< FFMS error message
|
||||||
FFMS_ErrorInfo ErrInfo; ///< FFMS error codes/messages
|
FFMS_ErrorInfo ErrInfo; ///< FFMS error codes/messages
|
||||||
|
@ -79,6 +80,7 @@ public:
|
||||||
double GetDAR() const override { return DAR; }
|
double GetDAR() const override { return DAR; }
|
||||||
agi::vfr::Framerate GetFPS() const override { return Timecodes; }
|
agi::vfr::Framerate GetFPS() const override { return Timecodes; }
|
||||||
std::string GetColorSpace() const override { return ColorSpace; }
|
std::string GetColorSpace() const override { return ColorSpace; }
|
||||||
|
std::string GetRealColorSpace() const override { return RealColorSpace; }
|
||||||
std::vector<int> GetKeyFrames() const override { return KeyFramesList; };
|
std::vector<int> GetKeyFrames() const override { return KeyFramesList; };
|
||||||
std::string GetDecoderName() const override { return "FFmpegSource"; }
|
std::string GetDecoderName() const override { return "FFmpegSource"; }
|
||||||
bool WantsCaching() const override { return true; }
|
bool WantsCaching() const override { return true; }
|
||||||
|
@ -91,7 +93,6 @@ std::string colormatrix_description(int cs, int cr) {
|
||||||
switch (cs) {
|
switch (cs) {
|
||||||
case FFMS_CS_RGB:
|
case FFMS_CS_RGB:
|
||||||
return "None";
|
return "None";
|
||||||
break;
|
|
||||||
case FFMS_CS_BT709:
|
case FFMS_CS_BT709:
|
||||||
return str + ".709";
|
return str + ".709";
|
||||||
case FFMS_CS_FCC:
|
case FFMS_CS_FCC:
|
||||||
|
@ -227,7 +228,7 @@ void FFmpegSourceVideoProvider::LoadVideo(agi::fs::path const& filename, std::st
|
||||||
auto CS = TempFrame->ColorSpace;
|
auto CS = TempFrame->ColorSpace;
|
||||||
if (CS == FFMS_CS_UNSPECIFIED)
|
if (CS == FFMS_CS_UNSPECIFIED)
|
||||||
CS = Width > 1024 || Height >= 600 ? FFMS_CS_BT709 : FFMS_CS_BT470BG;
|
CS = Width > 1024 || Height >= 600 ? FFMS_CS_BT709 : FFMS_CS_BT470BG;
|
||||||
ColorSpace = colormatrix_description(CS, TempFrame->ColorRange);
|
RealColorSpace = ColorSpace = colormatrix_description(CS, TempFrame->ColorRange);
|
||||||
|
|
||||||
#if FFMS_VERSION >= ((2 << 24) | (17 << 16) | (1 << 8) | 0)
|
#if FFMS_VERSION >= ((2 << 24) | (17 << 16) | (1 << 8) | 0)
|
||||||
if (CS != FFMS_CS_RGB && CS != FFMS_CS_BT470BG && ColorSpace != colormatrix && (colormatrix == "TV.601" || OPT_GET("Video/Force BT.601")->GetBool())) {
|
if (CS != FFMS_CS_RGB && CS != FFMS_CS_BT470BG && ColorSpace != colormatrix && (colormatrix == "TV.601" || OPT_GET("Video/Force BT.601")->GetBool())) {
|
||||||
|
|
Loading…
Reference in a new issue